diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java index bad069886c65..37778de8d4ae 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java @@ -11,15 +11,6 @@ */ public interface CacheableTypeDescriptor extends Type { - /** - * Check whether the type check result of this type descriptor should be cached. Can be used to avoid caching in - * cases where either directly doing the type check is cheaper or we can't determine if two instances of a type - * descriptor are equal without doing a type check. - * - * @return true if the type check result should be cached, false otherwise - */ - boolean shouldCache(); - /** * Check whether the type check result of this type descriptor is cached for the given type descriptor. * @@ -30,12 +21,12 @@ public interface CacheableTypeDescriptor extends Type { Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other); /** - * Cache the type check result of this type descriptor for the given type descriptor. Note that implementations of - * this method could choose to not cache the result if {@link #shouldCache()} returns false. In such cases, even - * after calling this method, {@link #cachedTypeCheckResult(Context, CacheableTypeDescriptor)} could return empty. + * Cache the type check result of this type descriptor for the given type descriptor. * * @param other Type descriptor to cache the result for * @param result Result of the type check */ void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result); + + TypeCheckCacheKey getLookupKey(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java index 363ba7cd90be..ab0c9b6c616c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java @@ -24,7 +24,6 @@ import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -46,24 +45,9 @@ public final class Context { public final Map listMemo = new WeakHashMap<>(); public final Map mappingMemo = new WeakHashMap<>(); public final Map functionMemo = new WeakHashMap<>(); - private static final int MAX_CACHE_SIZE = 100; - private final Map> typeCheckCacheMemo; private Context(Env env) { this.env = env; - this.typeCheckCacheMemo = createTypeCheckCacheMemo(); - } - - private static Map> createTypeCheckCacheMemo() { - // This is fine since this map is not going to get leaked out of the context and - // context is unique to a thread. So there will be no concurrent modifications - return new LinkedHashMap<>(MAX_CACHE_SIZE, 1f, true) { - @Override - protected boolean removeEldestEntry( - Map.Entry> eldest) { - return size() > MAX_CACHE_SIZE; - } - }; } public static Context from(Env env) { @@ -141,11 +125,4 @@ public FunctionAtomicType functionAtomicType(Atom atom) { return env.functionAtomType(atom); } - public TypeCheckCache getTypeCheckCache(CacheableTypeDescriptor typeDescriptor) { - return typeCheckCacheMemo.computeIfAbsent(typeDescriptor, TypeCheckCache::new); - } - - enum Phase { - INIT, TYPE_RESOLUTION, TYPE_CHECKING - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java index cf9bc820da2a..44239ddcf2e1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java @@ -1,41 +1,44 @@ package io.ballerina.runtime.api.types.semtype; -import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import java.util.Map; import java.util.Optional; import java.util.WeakHashMap; /** - * Generalized implementation of type check result cache. It is okay to access - * this from multiple threads but makes no - * guarantee about the consistency of the cache under parallel access. Given - * result don't change due to race conditions + * Generalized implementation of type check result cache. It is okay to access this from multiple threads but makes no + * guarantee about the consistency of the cache under parallel access. Given result don't change due to race conditions * this should eventually become consistent. * - * @param Type of the type descriptor which owns this cache * @since 2201.11.0 */ -public class TypeCheckCache { +public class TypeCheckCache { // Not synchronizing this should be fine since race conditions don't lead to inconsistent results. (i.e. results // of doing multiple type checks are agnostic to the order of execution). Data races shouldn't lead to tearing in // 64-bit JVMs. - private final Map cachedResults = new WeakHashMap<>(); - private final T owner; + private final Map cachedResults = new WeakHashMap<>(); + private final TypeCheckCacheKey ownerKey; - public TypeCheckCache(T owner) { - this.owner = owner; + public TypeCheckCache(CacheableTypeDescriptor owner) { + this.ownerKey = owner.getLookupKey(); } - public Optional cachedTypeCheckResult(T other) { - if (other.equals(owner)) { + public Optional cachedTypeCheckResult(CacheableTypeDescriptor other) { + TypeCheckCacheKey otherKey = other.getLookupKey(); + if (otherKey.equals(ownerKey)) { return Optional.of(true); } - return Optional.ofNullable(cachedResults.get(other)); + return Optional.ofNullable(cachedResults.get(otherKey)); } - public void cacheTypeCheckResult(T other, boolean result) { - cachedResults.put(other, result); + public void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result) { + TypeCheckCacheKey lookupKey = other.getLookupKey(); + // FIXME: this is just to prevent cache becoming too large. Revisit this after structured keys + if (lookupKey instanceof UniqueLookupKey) { + return; + } + cachedResults.put(lookupKey, result); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCacheFactory.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCacheFactory.java new file mode 100644 index 000000000000..735f0dab3730 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCacheFactory.java @@ -0,0 +1,45 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; + +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public final class TypeCheckCacheFactory { + + private static final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private static final Map cache = new WeakHashMap<>(); + + private TypeCheckCacheFactory() { + } + + public static TypeCheckCache getTypeCheckCache(CacheableTypeDescriptor owner) { + var key = owner.getLookupKey(); + if (key instanceof UniqueLookupKey) { + return new TypeCheckCache(owner); + } + lock.readLock().lock(); + try { + if (cache.containsKey(key)) { + return cache.get(key); + } + } finally { + lock.readLock().unlock(); + } + lock.writeLock().lock(); + try { + if (cache.containsKey(key)) { + return cache.get(key); + } + var typeCheckCache = new TypeCheckCache(owner); + cache.put(key, typeCheckCache); + return typeCheckCache; + } finally { + lock.writeLock().unlock(); + } + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCacheKey.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCacheKey.java new file mode 100644 index 000000000000..3473989c663d --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCacheKey.java @@ -0,0 +1,10 @@ +package io.ballerina.runtime.api.types.semtype; + +/** + * Marker type to represent the lookup key used in cache lookup operations involving {@CacheableTypeDescriptor}. + * + * @since 2201.12.0 + */ +public interface TypeCheckCacheKey { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index e8d1e846d6ae..aaea759950aa 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java @@ -639,13 +639,9 @@ private static boolean isSubTypeInner(Context cx, Type source, Type target) { private static boolean isSubTypeWithCache(Context cx, CacheableTypeDescriptor source, CacheableTypeDescriptor target) { - if (!source.shouldCache() || !target.shouldCache()) { - return isSubTypeInner(cx, source, target); - } Optional cachedResult = source.cachedTypeCheckResult(cx, target); logger.typeCheckCachedResult(cx, source, target, cachedResult); if (cachedResult.isPresent()) { - assert cachedResult.get() == isSubTypeInner(cx, source, target); return cachedResult.get(); } boolean result = isSubTypeInner(cx, source, target); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java index adb22bc0bdde..a9e066cdce0d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java @@ -29,6 +29,7 @@ import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.RefValue; import java.util.Optional; @@ -121,6 +122,11 @@ public void setImmutableType(IntersectionType immutableType) { this.immutableType = immutableType; } + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } + @Override public Optional getIntersectionType() { return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java index e3b56bd17d35..a610145895db 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java @@ -26,15 +26,20 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.ArrayValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.Collections; import java.util.Optional; import java.util.Set; @@ -69,6 +74,7 @@ public class BArrayType extends BType implements ArrayType, TypeWithShape { private int typeFlags; private final DefinitionContainer defn = new DefinitionContainer<>(); private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); + private Reference lookupKey; public BArrayType(Type elementType) { this(elementType, false); } @@ -263,6 +269,27 @@ protected boolean isDependentlyTypedInner(Set visited) { return elementType instanceof MayBeDependentType eType && eType.isDependentlyTyped(visited); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.ARRAY); + lookupKey = new WeakReference<>(structuredLookupKey); + if (size == -1) { + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{ + new StructuredLookupKey(StructuredLookupKey.Kind.REST, + new TypeCheckCacheKey[]{StructuredLookupKey.from(elementType)})}); + } else { + assert size >= 0; + var elementKey = StructuredLookupKey.from(elementType); + // TODO: think of a better way to represent this since size could be very large + structuredLookupKey.setChildren( + Collections.nCopies(size, elementKey).toArray(TypeCheckCacheKey[]::new)); + } + return structuredLookupKey; + } + @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java index f5a6c275ccda..5d5c35f9c740 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.function.Supplier; @@ -86,5 +87,10 @@ public int getTag() { public boolean isReadOnly() { return true; } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java index d69b43dc9b8d..c25d2bc2ab6f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.function.Supplier; @@ -91,5 +92,10 @@ public boolean isReadOnly() { public BType clone() { return super.clone(); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java index 441da73583d2..b638b205d5fa 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; @@ -93,5 +94,10 @@ public boolean isReadOnly() { public BType clone() { return super.clone(); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java index 9881bb8e706b..1f03212da0b9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java @@ -28,8 +28,11 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.internal.types.semtype.ErrorUtils; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import io.ballerina.runtime.internal.values.ErrorValue; import io.ballerina.runtime.internal.values.MapValueImpl; @@ -47,6 +50,8 @@ public class BErrorType extends BAnnotatableType implements ErrorType, TypeWithS public BTypeIdSet typeIdSet; private IntersectionType intersectionType = null; private volatile DistinctIdSupplier distinctIdSupplier; + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.ERROR, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); public BErrorType(String typeName, Module pkg, Type detailType) { super(typeName, pkg, ErrorValue.class); @@ -155,6 +160,12 @@ protected boolean isDependentlyTypedInner(Set visited) { mayBeDependentType.isDependentlyTyped(visited); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + // TODO: need to think how to handle distinct types + return lookupKey; + } + private boolean isTopType() { return detailType == PredefinedTypes.TYPE_DETAIL; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java index 8cb63b5cf11e..fd754baa2e05 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java @@ -26,7 +26,10 @@ import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import io.ballerina.runtime.internal.values.RefValue; import java.util.Iterator; @@ -47,6 +50,8 @@ public class BFiniteType extends BType implements FiniteType { public Set valueSpace; private int typeFlags; private String originalName; + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.FINITE, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); public BFiniteType(String typeName) { this(typeName, new LinkedHashSet<>(), 0); @@ -215,4 +220,9 @@ public SemType createSemType(Context cx) { .map(Optional::orElseThrow) .reduce(Builder.getNeverType(), Core::union); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return lookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java index 7d5aab69a070..9104511419b3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.function.Supplier; @@ -81,5 +82,10 @@ public int getTag() { public boolean isReadOnly() { return true; } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java index 3ada505c5f5f..81bb8bf4c020 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java @@ -29,11 +29,14 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import java.util.Arrays; import java.util.Objects; @@ -50,6 +53,8 @@ public class BFunctionType extends BAnnotatableType implements FunctionType { public Type retType; public long flags; public Parameter[] parameters; + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.FUNCTION, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); private final DefinitionContainer defn = new DefinitionContainer<>(); @@ -286,6 +291,12 @@ protected boolean isDependentlyTypedInner(Set visited) { isDependentlyTypeParameters(visited); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + // TODO: need to think how to handle dependently typed functions + return lookupKey; + } + private boolean isDependentlyTypeParameters(Set visited) { if (parameters == null) { return false; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java index 6bdd3d8f788e..bd0242e1fb41 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java @@ -25,8 +25,12 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.internal.types.semtype.FutureUtils; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.Objects; import java.util.Set; @@ -38,7 +42,7 @@ public class BFutureType extends BType implements FutureType { private final Type constraint; - + private Reference lookupKey; /** * Create a {@code {@link BFutureType}} which represents the future value. * @@ -112,4 +116,15 @@ public SemType createSemType(Context cx) { protected boolean isDependentlyTypedInner(Set visited) { return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.FUTURE); + lookupKey = new WeakReference<>(structuredLookupKey); + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{StructuredLookupKey.from(constraint)}); + return structuredLookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java index 9bd32efe9577..6cdba36017ac 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.RefValue; /** @@ -81,5 +82,10 @@ public boolean isReadOnly() { return true; } + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java index 0b7053f3c0d0..522f55139a1e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.function.Supplier; @@ -126,6 +127,11 @@ public boolean isReadOnly() { public BType clone() { return super.clone(); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } private static final class IntegerTypeCache { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java index dc8d99702e55..543aaaab553f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java @@ -29,9 +29,13 @@ import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -64,6 +68,7 @@ public class BIntersectionType extends BType implements IntersectionType, TypeWi private String cachedToString; private boolean resolving; + private Reference lookupKey; public BIntersectionType(Module pkg, Type[] constituentTypes, Type effectiveType, int typeFlags, boolean readonly) { @@ -238,6 +243,18 @@ protected boolean isDependentlyTypedInner(Set visited) { .anyMatch(type -> ((MayBeDependentType) type).isDependentlyTyped(visited)); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.INTERSECTION); + lookupKey = new WeakReference<>(structuredLookupKey); + structuredLookupKey.setChildren(constituentTypes.stream().map(StructuredLookupKey::from).toArray( + TypeCheckCacheKey[]::new)); + return structuredLookupKey; + } + private SemType createSemTypeInner(Context cx, Function semTypeFunction) { if (constituentTypes.isEmpty()) { return Builder.getNeverType(); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIteratorType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIteratorType.java index 5f4a3dde387a..651b71a84958 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIteratorType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIteratorType.java @@ -20,6 +20,9 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.IteratorType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import io.ballerina.runtime.internal.values.IteratorValue; /** @@ -29,6 +32,9 @@ */ public class BIteratorType extends BType implements IteratorType { + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.ITERATOR, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); + public BIteratorType(String typeName, Module pkg) { super(typeName, pkg, IteratorValue.class); } @@ -47,4 +53,9 @@ public V getEmptyValue() { public int getTag() { return TypeTags.ITERATOR_TAG; } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return lookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java index 7908a47b8fa5..3dd4830a9347 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java @@ -29,13 +29,17 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -63,6 +67,7 @@ public class BMapType extends BType implements MapType, TypeWithShape, Cloneable private IntersectionType intersectionType = null; private final DefinitionContainer defn = new DefinitionContainer<>(); private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); + private Reference lookupKey; public BMapType(Type constraint) { this(constraint, false); @@ -285,4 +290,15 @@ public BMapType clone() { protected boolean isDependentlyTypedInner(Set visited) { return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.MAP); + lookupKey = new WeakReference<>(structuredLookupKey); + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{StructuredLookupKey.from(constraint)}); + return structuredLookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java index 02e846517c71..cb2703e6b6b7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.function.Supplier; @@ -82,5 +83,10 @@ public boolean isNilable() { public boolean isReadOnly() { return true; } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java index 4f474ca11b23..355a21c739cf 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java @@ -37,6 +37,7 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; @@ -49,6 +50,8 @@ import io.ballerina.runtime.internal.types.semtype.Member; import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import io.ballerina.runtime.internal.types.semtype.ObjectQualifiers; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import io.ballerina.runtime.internal.utils.ValueUtils; import io.ballerina.runtime.internal.values.AbstractObjectValue; @@ -88,6 +91,8 @@ public class BObjectType extends BStructureType implements ObjectType, TypeWithS private final DefinitionContainer defn = new DefinitionContainer<>(); private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); private volatile DistinctIdSupplier distinctIdSupplier; + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.OBJECT, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); /** * Create a {@code BObjectType} which represents the user defined struct type. @@ -541,4 +546,10 @@ protected boolean isDependentlyTypedInner(Set visited) { return fields.values().stream().map(Field::getFieldType).filter(each -> each instanceof MayBeDependentType) .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + // TODO: need to think how to handle distinct, and names + return lookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java index 88a869dc6f53..3c269e4dcb56 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java @@ -23,7 +23,11 @@ import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.Set; /** @@ -35,6 +39,7 @@ public class BParameterizedType extends BType implements ParameterizedType { private final Type paramValueType; private final int paramIndex; + private Reference lookupKey; public BParameterizedType(Type paramValueType, int paramIndex) { super(null, null, null); @@ -94,4 +99,15 @@ public SemType createSemType(Context cx) { protected boolean isDependentlyTypedInner(Set visited) { return true; } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.TUPLE); + lookupKey = new WeakReference<>(structuredLookupKey); + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{StructuredLookupKey.from(paramValueType)}); + return structuredLookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java index c4366334b3fa..ea2623070df1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.RefValue; /** @@ -65,5 +66,10 @@ public boolean isNilable() { public boolean isReadOnly() { return true; } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java index 723b99da263c..c8829530cbf9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java @@ -35,6 +35,7 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BFunctionPointer; import io.ballerina.runtime.api.values.BMap; @@ -43,6 +44,8 @@ import io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability; import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import io.ballerina.runtime.internal.values.MapValue; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -77,6 +80,8 @@ public class BRecordType extends BStructureType implements RecordType, TypeWithS private final DefinitionContainer defn = new DefinitionContainer<>(); private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); private byte couldInhereTypeBeDifferentCache = 0; + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.FUNCTION, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); private final Map defaultValues = new LinkedHashMap<>(); @@ -293,6 +298,12 @@ public boolean isDependentlyTypedInner(Set visited) { .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + // TODO: we need to think about how we are going to handle names + return lookupKey; + } + @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java index 9c57d5cf47e3..6a3716f113dd 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java @@ -28,10 +28,14 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; import io.ballerina.runtime.internal.types.semtype.StreamDefinition; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.StreamValue; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.Objects; import java.util.Set; @@ -45,6 +49,7 @@ public class BStreamType extends BType implements StreamType { private final Type constraint; private final Type completionType; private final DefinitionContainer definition = new DefinitionContainer<>(); + private Reference lookupKey; /** * Creates a {@link BStreamType} which represents the stream type. @@ -168,4 +173,16 @@ protected boolean isDependentlyTypedInner(Set visited) { (completionType instanceof MayBeDependentType completionType && completionType.isDependentlyTyped(visited)); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.STREAM); + lookupKey = new WeakReference<>(structuredLookupKey); + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{StructuredLookupKey.from(constraint), + StructuredLookupKey.from(completionType)}); + return structuredLookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java index 7ddbd3bf0f7c..9c2c7e4d080e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.function.Supplier; @@ -107,5 +108,10 @@ public boolean isReadOnly() { public BType clone() { return super.clone(); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return null; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java index 45881c896dcb..468a7470a805 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java @@ -27,8 +27,11 @@ import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.api.values.BTable; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.types.semtype.TableUtils; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TableValue; import io.ballerina.runtime.internal.values.TableValueImpl; @@ -50,6 +53,8 @@ public class BTableType extends BType implements TableType, TypeWithShape { private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.TABLE, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); public BTableType(Type constraint, String[] fieldNames, boolean readonly) { this(constraint, readonly); @@ -241,13 +246,17 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable } @Override - public boolean shouldCache() { - // TODO: remove this once we have fixed equals - return false; + public boolean isAnonType() { + return true; } @Override protected boolean isDependentlyTypedInner(Set visited) { return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return this.lookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java index 52b7a9289e49..5d1c33d57c62 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java @@ -29,13 +29,17 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TupleValueImpl; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -43,6 +47,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; +import java.util.stream.Stream; import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; @@ -70,6 +75,7 @@ public class BTupleType extends BAnnotatableType implements TupleType, TypeWithS private String cachedToString; private final DefinitionContainer defn = new DefinitionContainer<>(); private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); + private Reference lookupKey; /** * Create a {@code BTupleType} which represents the tuple type. @@ -376,6 +382,22 @@ protected boolean isDependentlyTypedInner(Set visited) { .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.TUPLE); + lookupKey = new WeakReference<>(structuredLookupKey); + Stream children = tupleTypes.stream().map(StructuredLookupKey::from); + if (restType != null) { + children = Stream.concat(children, Stream.of(new StructuredLookupKey(StructuredLookupKey.Kind.REST, + new TypeCheckCacheKey[]{StructuredLookupKey.from(restType)}))); + } + structuredLookupKey.setChildren(children.toArray(StructuredLookupKey[]::new)); + return structuredLookupKey; + } + @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java index 6e6bbc6241c8..6ff845b2ad53 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java @@ -26,10 +26,14 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.TypeCheckCache; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheFactory; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeCheckLogger; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.MutableSemType; +import io.ballerina.runtime.internal.types.semtype.NamedLookupKey; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.HashSet; import java.util.Objects; @@ -58,7 +62,7 @@ public abstract non-sealed class BType extends SemType private Type cachedReferredType = null; private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; - private volatile TypeCheckCache typeCheckCache; + private volatile TypeCheckCache typeCheckCache; private final ReadWriteLock typeCacheLock = new ReentrantReadWriteLock(); protected BType(String typeName, Module pkg, Class valueClass) { @@ -288,14 +292,21 @@ public BType clone() { } } + public boolean isAnonType() { + return this.pkg == null || this.typeName == null || this.typeName.isEmpty() || this.typeName.contains("$anon"); + } + @Override - public boolean shouldCache() { - return this.pkg != null && this.typeName != null && !this.typeName.contains("$anon"); + public TypeCheckCacheKey getLookupKey() { + if (isAnonType()) { + return StructuredLookupKey.from(this); + } + return new NamedLookupKey(this.pkg, this.typeName); } @Override public final Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other) { - initializeCacheIfNeeded(cx); + initializeCacheIfNeeded(); typeCacheLock.readLock().lock(); try { return typeCheckCache.cachedTypeCheckResult(other); @@ -304,7 +315,7 @@ public final Optional cachedTypeCheckResult(Context cx, CacheableTypeDe } } - private void initializeCacheIfNeeded(Context cx) { + private void initializeCacheIfNeeded() { typeCacheLock.readLock().lock(); boolean shouldInitialize = typeCheckCache == null; typeCacheLock.readLock().unlock(); @@ -314,7 +325,7 @@ private void initializeCacheIfNeeded(Context cx) { try { typeCacheLock.writeLock().lock(); if (typeCheckCache == null) { - typeCheckCache = cx.getTypeCheckCache(this); + typeCheckCache = TypeCheckCacheFactory.getTypeCheckCache(this); } } finally { typeCacheLock.writeLock().unlock(); @@ -343,4 +354,6 @@ public final boolean isDependentlyTyped(Set visited) { protected boolean isDependentlyTypedInner(Set visited) { return false; } + + public abstract StructuredLookupKey getStructuredLookupKey(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java index c78429dd0124..f362545fb30b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java @@ -28,6 +28,7 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import java.util.Objects; import java.util.Optional; @@ -142,6 +143,11 @@ protected boolean isDependentlyTypedInner(Set visited) { return getReferredType() instanceof MayBeDependentType refType && refType.isDependentlyTyped(visited); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + return StructuredLookupKey.from(referredType); + } + @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java index fbda372b2985..29965c3dab15 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java @@ -27,10 +27,14 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.types.semtype.TypedescUtils; import io.ballerina.runtime.internal.values.TypedescValue; import io.ballerina.runtime.internal.values.TypedescValueImpl; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.Set; /** @@ -41,6 +45,7 @@ public class BTypedescType extends BType implements TypedescType { private final Type constraint; + private Reference lookupKey; public BTypedescType(String typeName, Module pkg) { super(typeName, pkg, Object.class); @@ -107,4 +112,15 @@ protected boolean isDependentlyTypedInner(Set visited) { return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.TYPE_DESC); + lookupKey = new WeakReference<>(structuredLookupKey); + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{StructuredLookupKey.from(constraint)}); + return structuredLookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java index 107d3eb2f0d0..8a0c7451b29c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java @@ -33,8 +33,11 @@ import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -65,6 +68,7 @@ public class BUnionType extends BType implements UnionType, SelectivelyImmutable private String cachedToString; private boolean resolving; public boolean resolvingReadonly; + private Reference lookupKey; private static final String INT_CLONEABLE = "__Cloneable"; private static final String CLONEABLE = "Cloneable"; @@ -564,6 +568,18 @@ protected boolean isDependentlyTypedInner(Set visited) { .anyMatch(type -> ((MayBeDependentType) type).isDependentlyTyped(visited)); } + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.UNION); + lookupKey = new WeakReference<>(structuredLookupKey); + structuredLookupKey.setChildren( + memberTypes.stream().map(StructuredLookupKey::from).toArray(StructuredLookupKey[]::new)); + return structuredLookupKey; + } + @Override public Optional acceptedTypeOf(Context cx) { return Optional.of(memberTypes.stream().map(each -> ShapeAnalyzer.acceptedTypeOf(cx, each).orElseThrow()) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlAttributesType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlAttributesType.java index 629be337797f..3461b0131ff0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlAttributesType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlAttributesType.java @@ -20,6 +20,9 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.XmlAttributesType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; +import io.ballerina.runtime.internal.types.semtype.UniqueLookupKey; /** * {@code BXMLAttributesType} represents the type of an xml-attribute-map in ballerina. @@ -28,6 +31,8 @@ */ public class BXmlAttributesType extends BType implements XmlAttributesType { + private final StructuredLookupKey lookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.XML_ATTRIBUTE, + new TypeCheckCacheKey[]{new UniqueLookupKey()}); /** * Create a {@code BXMLAttributesType} represents the type an xml-attribute-map in ballerina. * @@ -52,4 +57,9 @@ public V getEmptyValue() { public int getTag() { return TypeTags.XML_ATTRIBUTES_TAG; } + + @Override + public StructuredLookupKey getStructuredLookupKey() { + return lookupKey; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java index aba39af0a1d3..f31381a3c422 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java @@ -28,6 +28,8 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; +import io.ballerina.runtime.internal.types.semtype.StructuredLookupKey; import io.ballerina.runtime.internal.types.semtype.XmlUtils; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.XmlComment; @@ -37,6 +39,8 @@ import io.ballerina.runtime.internal.values.XmlText; import io.ballerina.runtime.internal.values.XmlValue; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.Optional; /** @@ -52,6 +56,7 @@ public class BXmlType extends BType implements XmlType, TypeWithShape { private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; + private Reference lookupKey; /** * Create a {@code BXMLType} which represents the boolean type. @@ -172,6 +177,21 @@ public SemType createSemType(Context cx) { return isReadOnly() ? Core.intersect(Builder.getReadonlyType(), semType) : semType; } + @Override + public StructuredLookupKey getStructuredLookupKey() { + if (lookupKey != null && lookupKey.get() != null) { + return lookupKey.get(); + } + StructuredLookupKey structuredLookupKey = new StructuredLookupKey(StructuredLookupKey.Kind.TUPLE); + lookupKey = new WeakReference<>(structuredLookupKey); + if (constraint != null) { + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{StructuredLookupKey.from(constraint)}); + } else { + structuredLookupKey.setChildren(new TypeCheckCacheKey[]{StructuredLookupKey.TOP}); + } + return structuredLookupKey; + } + private SemType pickTopType() { return switch (tag) { case TypeTags.XML_TAG -> Builder.getXmlType(); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/NamedLookupKey.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/NamedLookupKey.java new file mode 100644 index 000000000000..500c52aefe37 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/NamedLookupKey.java @@ -0,0 +1,13 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; + +public record NamedLookupKey(Module pkg, String name) implements TypeCheckCacheKey { + + public NamedLookupKey { + assert pkg != null; + assert name != null; + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeLookupKey.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeLookupKey.java new file mode 100644 index 000000000000..26d070160f7c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeLookupKey.java @@ -0,0 +1,8 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; + +public record SemTypeLookupKey(SemType semType) implements TypeCheckCacheKey { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StructuredLookupKey.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StructuredLookupKey.java new file mode 100644 index 000000000000..510bfbf13c9c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StructuredLookupKey.java @@ -0,0 +1,86 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; +import io.ballerina.runtime.internal.types.BType; + +import java.util.Arrays; +import java.util.Objects; + +public class StructuredLookupKey implements TypeCheckCacheKey { + + private final Kind kind; + private TypeCheckCacheKey[] children; + public static final StructuredLookupKey TOP = new StructuredLookupKey(Kind.TOP, new TypeCheckCacheKey[0]); + private int hash = -1; + private int bitSet = 0; + public static final int READONLY_BIT = 1; + + public StructuredLookupKey(Kind kind) { + this.kind = kind; + } + + public StructuredLookupKey(Kind kind, TypeCheckCacheKey[] children) { + this.kind = kind; + this.children = children; + } + + private static StructuredLookupKey updateBitSet(BType bType, StructuredLookupKey key) { + if (bType.isReadOnly()) { + key.bitSet |= READONLY_BIT; + } + return key; + } + + public static StructuredLookupKey from(Type type) { + if (type instanceof BType bType) { + if (bType.isAnonType()) { + return updateBitSet(bType, bType.getStructuredLookupKey()); + } + return new StructuredLookupKey(Kind.NAMED, new TypeCheckCacheKey[]{bType.getLookupKey()}); + } + assert type instanceof SemType; + return new StructuredLookupKey(Kind.SEMTYPE, new TypeCheckCacheKey[]{new SemTypeLookupKey((SemType) type)}); + } + + public void setChildren(TypeCheckCacheKey[] children) { + this.children = children; + } + + @Override + public int hashCode() { + if (hash == -1) { + // This is correct since you can't make un-named recursive types + hash = Objects.hash(kind, bitSet, Arrays.deepHashCode(children)); + } + return hash; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof StructuredLookupKey other)) { + return false; + } + return kind == other.kind && bitSet == other.bitSet && Arrays.deepEquals(children, other.children); + } + + public enum Kind { + NAMED, SEMTYPE, TUPLE, UNION, RECORD, REST, OBJECT, FINITE, FUNCTION, INTERSECTION, TABLE, STREAM, MAP, FUTURE, + ARRAY, ERROR, TYPE_DESC, ITERATOR, XML_ATTRIBUTE, TOP + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("(").append(kind).append(", ").append(bitSet).append(", ["); + for (int i = 0; i < children.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(children[i]); + } + sb.append("])"); + return sb.toString(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/UniqueLookupKey.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/UniqueLookupKey.java new file mode 100644 index 000000000000..8a641253b3ba --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/UniqueLookupKey.java @@ -0,0 +1,6 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.TypeCheckCacheKey; + +public class UniqueLookupKey implements TypeCheckCacheKey { +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal index f1edb6added6..94bcd55bbf7c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal @@ -14,7 +14,7 @@ // specific language governing permissions and limitations // under the License. -type Student record {| +type StudentOBC record {| int id; string? fname; float fee; @@ -22,63 +22,63 @@ type Student record {| boolean isUndergrad; |}; -type Person record {| +type PersonOBC record {| string firstName; string lastName; int age; |}; -type Customer record {| +type CustomerOBC record {| readonly int id; readonly string name; int noOfItems; |}; -type CustomerProfile record {| +type CustomerProfileOBC record {| string name; int age; int noOfItems; |}; -type Employee record {| +type EmployeeOBC record {| string name; - Address address; + AddressOBC address; map tokens; int[] noOfShifts; |}; -type Address record {| +type AddressOBC record {| int unitNo; string street; |}; -type PaymentInfo record {| +type PaymentInfoOBC record {| int custId; string modeOfPayment; |}; -type CustomerTable table key(id, name); +type CustomerTableOBC table key(id, name); -type CustomerValue record {| - Customer value; +type CustomerValueOBC record {| + CustomerOBC value; |}; -type PersonValue record {| - Person value; +type PersonValueOBC record {| + PersonOBC value; |}; -function getCustomer(record {| Customer value; |}? returnedVal) returns Customer? { - if (returnedVal is CustomerValue) { +function getCustomer(record {| CustomerOBC value; |}? returnedVal) returns CustomerOBC? { + if (returnedVal is CustomerValueOBC) { return returnedVal.value; } else { return (); } } -function getPersonValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) -returns PersonValue? { +function getPersonValue((record {| PersonOBC value; |}|error?)|(record {| PersonOBC value; |}?) returnedVal) +returns PersonValueOBC? { var result = returnedVal; - if (result is PersonValue) { + if (result is PersonValueOBC) { return result; } else { return (); @@ -88,14 +88,14 @@ returns PersonValue? { function testQueryExprWithOrderByClause() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; - Student s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentOBC s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentOBC s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentOBC s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; + StudentOBC s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4]; + StudentOBC[] studentList = [s1, s2, s3, s4]; - Student[] opStudentList = from var student in studentList + StudentOBC[] opStudentList = from var student in studentList order by student.fname descending, student.impact select student; @@ -110,14 +110,14 @@ function testQueryExprWithOrderByClause() returns boolean { function testQueryExprWithOrderByClause2() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: false}; - Student s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: false}; - Student s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentOBC s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentOBC s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: false}; + StudentOBC s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: false}; + StudentOBC s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4]; + StudentOBC[] studentList = [s1, s2, s3, s4]; - Student[] opStudentList = from var student in studentList + StudentOBC[] opStudentList = from var student in studentList order by student.isUndergrad ascending, student.fee select student; @@ -129,19 +129,19 @@ function testQueryExprWithOrderByClause2() returns boolean { return testPassed; } -function testQueryExprWithOrderByClause3() returns Customer[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 7, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; +function testQueryExprWithOrderByClause3() returns CustomerOBC[] { + CustomerOBC c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerOBC c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerOBC c3 = {id: 7, name: "James", noOfItems: 25}; + CustomerOBC c4 = {id: 0, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonOBC p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonOBC p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerOBC[] customerList = [c1, c2, c3, c4]; + PersonOBC[] personList = [p1, p2]; - Customer[] opCustomerList = from var customer in customerList + CustomerOBC[] opCustomerList = from var customer in customerList from var person in personList let string customerName = "Johns" where person.lastName == "James" @@ -158,18 +158,18 @@ function testQueryExprWithOrderByClause3() returns Customer[] { function testQueryExprWithOrderByClauseReturnTable() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerOBC c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerOBC c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerOBC c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerOBC c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonOBC p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonOBC p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerOBC[] customerList = [c1, c2, c3, c4]; + PersonOBC[] personList = [p1, p2]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList + CustomerTableOBC|error customerTable = table key(id, name) from var customer in customerList from var person in personList where person.firstName == "Frank" order by customer.noOfItems descending, customer.id @@ -180,9 +180,9 @@ function testQueryExprWithOrderByClauseReturnTable() returns boolean { noOfItems: customer.noOfItems }; - if (customerTable is CustomerTable) { + if (customerTable is CustomerTableOBC) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerOBC? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[2]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[3]; @@ -198,19 +198,19 @@ function testQueryExprWithOrderByClauseReturnTable() returns boolean { function testQueryExprWithOrderByClauseReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p2 = {firstName: "John", lastName: "David", age: 33}; - Person p3 = {firstName: "John", lastName: "Fonseka", age: 28}; - Person p4 = {firstName: "John", lastName: "Fonseka", age: 30}; - Person p5 = {firstName: "John", lastName: "Fonseka", age: 20}; + PersonOBC p1 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonOBC p2 = {firstName: "John", lastName: "David", age: 33}; + PersonOBC p3 = {firstName: "John", lastName: "Fonseka", age: 28}; + PersonOBC p4 = {firstName: "John", lastName: "Fonseka", age: 30}; + PersonOBC p5 = {firstName: "John", lastName: "Fonseka", age: 20}; - Customer c1 = {id: 1, name: "John", noOfItems: 25}; - Customer c2 = {id: 2, name: "Frank", noOfItems: 20}; + CustomerOBC c1 = {id: 1, name: "John", noOfItems: 25}; + CustomerOBC c2 = {id: 2, name: "Frank", noOfItems: 20}; - Person[] personList = [p1, p2, p3, p4, p5]; - Customer[] customerList = [c1, c2]; + PersonOBC[] personList = [p1, p2, p3, p4, p5]; + CustomerOBC[] customerList = [c1, c2]; - stream outputPersonStream = stream from var person in personList.toStream() + stream outputPersonStream = stream from var person in personList.toStream() from var customer in customerList let string newLastName = "Turin" let string newFirstName = "Johnas" @@ -223,7 +223,7 @@ function testQueryExprWithOrderByClauseReturnStream() returns boolean { age: person.age }; - record {| Person value; |}? person = getPersonValue(outputPersonStream.next()); + record {| PersonOBC value; |}? person = getPersonValue(outputPersonStream.next()); testPassed = testPassed && person?.value?.firstName == "Johnas" && person?.value?.lastName == "Turin" && person?.value?.age == 30; @@ -245,19 +245,19 @@ function testQueryExprWithOrderByClauseReturnStream() returns boolean { return testPassed; } -function testQueryExprWithOrderByClauseAndJoin() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; +function testQueryExprWithOrderByClauseAndJoin() returns CustomerProfileOBC[] { + CustomerOBC c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerOBC c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerOBC c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerOBC c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonOBC p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonOBC p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerOBC[] customerList = [c1, c2, c3, c4]; + PersonOBC[] personList = [p1, p2]; - CustomerProfile[] customerProfileList = from var customer in customerList + CustomerProfileOBC[] customerProfileList = from var customer in customerList join var person in personList on customer.name equals person.lastName order by customer.noOfItems @@ -273,18 +273,18 @@ function testQueryExprWithOrderByClauseAndJoin() returns CustomerProfile[] { function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, + EmployeeOBC e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:1, two:2, three:3}, + EmployeeOBC e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:1, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, + EmployeeOBC e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, + EmployeeOBC e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee[] empList = [e1, e2, e3, e4]; + EmployeeOBC[] empList = [e1, e2, e3, e4]; - Employee[] opEmpList = from var emp in empList + EmployeeOBC[] opEmpList = from var emp in empList order by emp.address.unitNo descending, emp.address.street.toLowerAscii() select emp; @@ -299,20 +299,20 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() retur function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction2() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, + EmployeeOBC e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:2, three:3}, + EmployeeOBC e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:111, two:2, three:3}, + EmployeeOBC e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:111, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, + EmployeeOBC e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, + EmployeeOBC e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, noOfShifts: [3, 2, 3]}; - Employee[] empList = [e1, e2, e3, e4, e5]; + EmployeeOBC[] empList = [e1, e2, e3, e4, e5]; - Employee[] opEmpList = from var emp in empList + EmployeeOBC[] opEmpList = from var emp in empList order by emp.name, emp.tokens["one"] descending, emp.noOfShifts[0] descending select emp; @@ -325,19 +325,19 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction2() retu return testPassed; } -function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction3() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; +function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction3() returns CustomerProfileOBC[] { + CustomerOBC c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerOBC c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerOBC c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerOBC c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonOBC p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonOBC p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerOBC[] customerList = [c1, c2, c3, c4]; + PersonOBC[] personList = [p1, p2]; - CustomerProfile[] customerProfileList = from var customer in customerList + CustomerProfileOBC[] customerProfileList = from var customer in customerList join var person in personList on customer.name equals person.lastName order by incrementCount(0), customer.noOfItems descending @@ -353,24 +353,24 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction3() retu function testQueryExprWithOrderByClauseHavingNaNNilValues() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, + EmployeeOBC e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:(), three:3}, + EmployeeOBC e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:(), three:3}, noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:11, two:(0.0/0.0), + EmployeeOBC e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:11, two:(0.0/0.0), three:3}, noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:3}, + EmployeeOBC e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:3}, noOfShifts: [1, 2, 3]}; - Employee e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:()}, + EmployeeOBC e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:()}, noOfShifts: [1, 2, 3]}; - Employee e6 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, + EmployeeOBC e6 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:(0.0/0.0)}, noOfShifts: [1, 2, 3]}; - Employee e7 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:55}, + EmployeeOBC e7 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:55}, noOfShifts: [1, 2, 3]}; - Employee[] empList = [e1, e2, e3, e4, e5, e6, e7]; + EmployeeOBC[] empList = [e1, e2, e3, e4, e5, e6, e7]; - Employee[] opEmpList = from var emp in empList + EmployeeOBC[] opEmpList = from var emp in empList order by emp.tokens["two"] descending, emp.tokens["three"] ascending select emp; @@ -386,13 +386,13 @@ function testQueryExprWithOrderByClauseHavingNaNNilValues() returns boolean { } function testQueryExprWithOrderByClauseReturnString() returns string { - Person p1 = {firstName: "Amy", lastName: "Melina", age: 34}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Melina", lastName: "Kodel", age: 72}; - Person p4 = {firstName: "Terrence", lastName: "Lewis", age: 19}; - Person p5 = {firstName: "Meghan", lastName: "Markle", age: 55}; + PersonOBC p1 = {firstName: "Amy", lastName: "Melina", age: 34}; + PersonOBC p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonOBC p3 = {firstName: "Melina", lastName: "Kodel", age: 72}; + PersonOBC p4 = {firstName: "Terrence", lastName: "Lewis", age: 19}; + PersonOBC p5 = {firstName: "Meghan", lastName: "Markle", age: 55}; - Person[] personList = [p1, p2, p3, p4, p5]; + PersonOBC[] personList = [p1, p2, p3, p4, p5]; string outputNameString = from var person in personList order by person.age descending @@ -426,21 +426,21 @@ function testQueryExprWithOrderByClauseReturnXML() returns xml { return authors; } -function testQueryExprWithOrderByClauseAndInnerQueries() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; +function testQueryExprWithOrderByClauseAndInnerQueries() returns CustomerProfileOBC[] { + CustomerOBC c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerOBC c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerOBC c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerOBC c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerOBC c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50}; + PersonOBC p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; + PersonOBC p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonOBC p3 = {firstName: "Zeth", lastName: "James", age: 50}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerOBC[] customerList = [c1, c2, c3, c4, c5]; + PersonOBC[] personList = [p1, p2, p3]; - CustomerProfile[] customerProfileList = from var customer in (stream from var c in customerList + CustomerProfileOBC[] customerProfileList = from var customer in (stream from var c in customerList order by c.id descending limit 4 select c) join var person in (from var p in personList order by p.firstName descending limit 2 select p) on customer.name equals person.lastName @@ -455,32 +455,32 @@ function testQueryExprWithOrderByClauseAndInnerQueries() returns CustomerProfile return customerProfileList; } -function testQueryExprWithOrderByClauseAndInnerQueries2() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; - Customer c6 = {id: 3, name: "Melina", noOfItems: 20}; - - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50}; - - PaymentInfo i1 = {custId: 1, modeOfPayment: "cash"}; - PaymentInfo i2 = {custId: 9, modeOfPayment: "debit card"}; - PaymentInfo i3 = {custId: 1, modeOfPayment: "creadit card"}; - PaymentInfo i4 = {custId: 5, modeOfPayment: "cash"}; - PaymentInfo i5 = {custId: 9, modeOfPayment: "cash"}; - PaymentInfo i6 = {custId: 0, modeOfPayment: "cash"}; - PaymentInfo i7 = {custId: 2, modeOfPayment: "cash"}; - PaymentInfo i8 = {custId: 3, modeOfPayment: "cash"}; - - Customer[] customerList = [c1, c2, c3, c4, c5, c6]; - Person[] personList = [p1, p2, p3]; - PaymentInfo[] paymentList = [i1, i2, i3, i4, i5, i6, i7, i8]; - - CustomerProfile[] customerProfileList = from var customer in (stream from var c in customerList +function testQueryExprWithOrderByClauseAndInnerQueries2() returns CustomerProfileOBC[] { + CustomerOBC c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerOBC c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerOBC c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerOBC c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerOBC c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerOBC c6 = {id: 3, name: "Melina", noOfItems: 20}; + + PersonOBC p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; + PersonOBC p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonOBC p3 = {firstName: "Zeth", lastName: "James", age: 50}; + + PaymentInfoOBC i1 = {custId: 1, modeOfPayment: "cash"}; + PaymentInfoOBC i2 = {custId: 9, modeOfPayment: "debit card"}; + PaymentInfoOBC i3 = {custId: 1, modeOfPayment: "creadit card"}; + PaymentInfoOBC i4 = {custId: 5, modeOfPayment: "cash"}; + PaymentInfoOBC i5 = {custId: 9, modeOfPayment: "cash"}; + PaymentInfoOBC i6 = {custId: 0, modeOfPayment: "cash"}; + PaymentInfoOBC i7 = {custId: 2, modeOfPayment: "cash"}; + PaymentInfoOBC i8 = {custId: 3, modeOfPayment: "cash"}; + + CustomerOBC[] customerList = [c1, c2, c3, c4, c5, c6]; + PersonOBC[] personList = [p1, p2, p3]; + PaymentInfoOBC[] paymentList = [i1, i2, i3, i4, i5, i6, i7, i8]; + + CustomerProfileOBC[] customerProfileList = from var customer in (stream from var c in customerList order by c.id descending limit 4 select c) join var person in (from var p in personList order by p.firstName descending limit 2 select p) on customer.name equals person.lastName