diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java index 38278a30fdfe..4c4f1149387b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java @@ -52,7 +52,6 @@ import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_TYPEDESC; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_XML; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; -import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; import static io.ballerina.runtime.api.types.semtype.Core.union; @@ -69,7 +68,6 @@ public final class Builder { private static final String[] EMPTY_STRING_ARR = new String[0]; private static final SemType VAL = SemType.from(VT_MASK); private static final SemType OBJECT = from(BT_OBJECT); - private static final SemType INHERENTLY_IMMUTABLE = SemType.from(VT_INHERENTLY_IMMUTABLE); private static final SemType INNER = getBasicTypeUnion(VAL.all() | from(BasicTypeCode.BT_UNDEF).all()); private static final SemType ANY = getBasicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); @@ -359,10 +357,6 @@ public static SemType getObjectType() { return OBJECT; } - public static SemType getInherentlyImmutable() { - return INHERENTLY_IMMUTABLE; - } - static SemType mappingRO() { return MAPPING_RO.get(); } 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 2cff96f94c5c..4d6634f89f76 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 @@ -22,7 +22,6 @@ import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; import io.ballerina.runtime.internal.types.semtype.ListAtomicType; import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; -import io.ballerina.runtime.internal.types.semtype.MutableSemType; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -49,17 +48,10 @@ public final class Context { public final Map functionMemo = new WeakHashMap<>(); private static final int MAX_CACHE_SIZE = 100; private final Map> typeCheckCacheMemo; - private Phase phase = Phase.INIT; - List typeResolutionPhases = new ArrayList<>(); - List typeCheckPhases = new ArrayList<>(); - private final boolean collectDiagnostic; - private int typeCheckDepth = 0; - private int typeResolutionDepth = 0; private Context(Env env) { this.env = env; this.typeCheckCacheMemo = createTypeCheckCacheMemo(); - this.collectDiagnostic = "true".equalsIgnoreCase(System.getenv("BAL_TYPE_CHECK_DIAGNOSTIC_ENABLE")); } private static Map> createTypeCheckCacheMemo() { @@ -98,111 +90,6 @@ public boolean memoSubtypeIsEmpty(Map memoTable, BddIsEmptyPredica return m.isEmpty().orElseGet(() -> memoSubTypeIsEmptyInner(isEmptyPredicate, bdd, m)); } - public void enterTypeResolutionPhase(MutableSemType type) throws InterruptedException { - switch (phase) { - case INIT -> { - typeResolutionDepth++; - env.enterTypeResolutionPhase(this, type); - phase = Phase.TYPE_RESOLUTION; - if (collectDiagnostic) { - typeResolutionPhases.add(new PhaseData()); - } - } - case TYPE_RESOLUTION -> { - typeResolutionDepth++; - } - case TYPE_CHECKING -> { - StringBuilder sb = new StringBuilder(); - sb.append("Cannot enter type resolution phase while in type checking phase\n"); - if (collectDiagnostic) { - appendPhaseDataToError(sb); - } - throw new IllegalStateException(sb.toString()); - } - } - } - - public void exitTypeResolutionPhaseAbruptly(Exception ex) { - env.exitTypeResolutionPhaseAbruptly(this, ex); - } - - public void enterTypeCheckingPhase(SemType t1, SemType t2) { - typeCheckDepth++; - switch (phase) { - case INIT -> { - env.enterTypeCheckingPhase(this, t1, t2); - if (collectDiagnostic) { - typeCheckPhases.add(new PhaseData()); - } - phase = Phase.TYPE_CHECKING; - } - case TYPE_RESOLUTION -> { - StringBuilder sb = new StringBuilder(); - sb.append("Cannot enter type checking phase while in type resolution phase\n"); - if (collectDiagnostic) { - appendPhaseDataToError(sb); - } - throw new IllegalStateException(sb.toString()); - } - case TYPE_CHECKING -> { - } - } - } - - public void exitTypeResolutionPhase() { - if (phase == Phase.TYPE_RESOLUTION) { - typeResolutionDepth--; - if (typeResolutionDepth == 0) { - env.exitTypeResolutionPhase(this); - phase = Phase.INIT; - if (collectDiagnostic) { - typeResolutionPhases.removeLast(); - } - } - } else { - throw new IllegalStateException("Cannot exit type resolution phase without entering it"); - } - } - - public void exitTypeCheckingPhase() { - switch (phase) { - case INIT -> { - StringBuilder sb = new StringBuilder(); - sb.append("Cannot exit type checking phase without entering it"); - if (collectDiagnostic) { - appendPhaseDataToError(sb); - } - throw new IllegalStateException(sb.toString()); - } - case TYPE_RESOLUTION -> { - StringBuilder sb = new StringBuilder(); - sb.append("Cannot exit type checking phase while in type resolution phase\n"); - if (collectDiagnostic) { - appendPhaseDataToError(sb); - } - throw new IllegalStateException(sb.toString()); - } - case TYPE_CHECKING -> { - env.exitTypeCheckingPhase(this); - typeCheckDepth--; - if (typeCheckDepth == 0) { - phase = Phase.INIT; - } - } - } - } - - private void appendPhaseDataToError(StringBuilder sb) { - sb.append("Type resolution phases:\n"); - for (PhaseData phaseData : typeResolutionPhases) { - sb.append(phaseData).append("\n"); - } - sb.append("Type checking phases:\n"); - for (PhaseData phaseData : typeCheckPhases) { - sb.append(phaseData).append("\n"); - } - } - private boolean memoSubTypeIsEmptyInner(BddIsEmptyPredicate isEmptyPredicate, Bdd bdd, BddMemo m) { // We are staring the type check with the assumption our type is empty (see: inductive type) m.isEmpty = BddMemo.Status.PROVISIONAL; @@ -244,7 +131,6 @@ private void resetMemoizedValues(int initStackDepth, boolean isEmpty, boolean is public ListAtomicType listAtomType(Atom atom) { if (atom instanceof RecAtom recAtom) { - assert this.env.getRecListAtomType(recAtom) != null; return this.env.getRecListAtomType(recAtom); } else { return (ListAtomicType) ((TypeAtom) atom).atomicType(); @@ -253,7 +139,6 @@ public ListAtomicType listAtomType(Atom atom) { public MappingAtomicType mappingAtomType(Atom atom) { if (atom instanceof RecAtom recAtom) { - assert this.env.getRecMappingAtomType(recAtom) != null; return this.env.getRecMappingAtomType(recAtom); } else { return (MappingAtomicType) ((TypeAtom) atom).atomicType(); @@ -262,7 +147,6 @@ public MappingAtomicType mappingAtomType(Atom atom) { public FunctionAtomicType functionAtomicType(Atom atom) { if (atom instanceof RecAtom recAtom) { - assert this.env.getRecFunctionAtomType(recAtom) != null; return this.env.getRecFunctionAtomType(recAtom); } else { return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); @@ -272,28 +156,4 @@ public FunctionAtomicType functionAtomicType(Atom atom) { public TypeCheckCache getTypeCheckCache(CacheableTypeDescriptor typeDescriptor) { return typeCheckCacheMemo.computeIfAbsent(typeDescriptor, TypeCheckCache::new); } - - public void registerAbruptTypeCheckEnd(Exception ex) { - env.registerAbruptTypeCheckEnd(this, ex); - } - - enum Phase { - INIT, TYPE_RESOLUTION, TYPE_CHECKING - } - - record PhaseData(StackTraceElement[] stackTrace) { - - PhaseData() { - this(Thread.currentThread().getStackTrace()); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - for (StackTraceElement element : stackTrace) { - builder.append("\tat ").append(element).append("\n"); - } - return builder.toString(); - } - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java index 370d01c3f66c..3eefea1b58e4 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java @@ -295,15 +295,7 @@ public static boolean isNever(SemType t) { } public static boolean isSubType(Context cx, SemType t1, SemType t2) { - try { - cx.enterTypeCheckingPhase(t1, t2); - boolean res = isEmpty(cx, diff(t1, t2)); - cx.exitTypeCheckingPhase(); - return res; - } catch (Exception e) { - cx.registerAbruptTypeCheckEnd(e); - throw e; - } + return isEmpty(cx, diff(t1, t2)); } public static boolean isSubtypeSimple(SemType t1, SemType t2) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/DebugSelfDiagnosticRunner.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/DebugSelfDiagnosticRunner.java deleted file mode 100644 index 35c567203be2..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/DebugSelfDiagnosticRunner.java +++ /dev/null @@ -1,268 +0,0 @@ -package io.ballerina.runtime.api.types.semtype; - -import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; -import io.ballerina.runtime.internal.types.semtype.ListAtomicType; -import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; -import io.ballerina.runtime.internal.types.semtype.MutableSemType; - -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; - -public class DebugSelfDiagnosticRunner implements TypeCheckSelfDiagnosticsRunner { - - private static final int MAX_TIMEOUT = 1000; - private static final String LOG_FILE_PATH = "/tmp/type_check_diagnostics.log"; - private final Queue pendingTypeResolutions = new ArrayDeque<>(); - private final Queue pendingTypeChecks = new ArrayDeque<>(); - private final Map uncaughtExceptions = new ConcurrentHashMap<>(); - private final Env env; - - DebugSelfDiagnosticRunner(Env env) { - try { - Files.write(Paths.get(LOG_FILE_PATH), "Type Check Diagnostics \n\n".getBytes(), StandardOpenOption.APPEND); - } catch (IOException ignored) { - } - this.env = env; - Thread houseKeeperThread = new Thread(new HouseKeeper()); - houseKeeperThread.setDaemon(true); - houseKeeperThread.start(); - } - - @Override - public void registerTypeResolutionStart(Context cx, MutableSemType type) { - synchronized (pendingTypeResolutions) { - pendingTypeResolutions.add(new TypeResolutionData(cx, type)); - } - Thread t = Thread.currentThread(); - t.setUncaughtExceptionHandler((thread, throwable) -> uncaughtExceptions.put(thread, throwable.getMessage())); - } - - private void validateRecAtomState() { - - List recMappingAtomsCopy = env.getRecMappingAtomsCopy(); - List recFunctionAtomsCopy = env.getRecFunctionAtomsCopy(); - List recListAtomsCopy = env.getRecListAtomsCopy(); - Thread validateThread = new Thread(() -> { - StringBuilder logBuilder = new StringBuilder(); - for (int i = 0; i < recMappingAtomsCopy.size(); i++) { - if (recMappingAtomsCopy.get(i) == null) { - logBuilder.append("Rec mapping atoms contain null values at ").append(i).append("\n"); - } - } - for (int i = 0; i < recFunctionAtomsCopy.size(); i++) { - if (recFunctionAtomsCopy.get(i) == null) { - logBuilder.append("Rec function atoms contain null values at ").append(i).append("\n"); - } - } - for (int i = 0; i < recListAtomsCopy.size(); i++) { - if (recListAtomsCopy.get(i) == null) { - logBuilder.append("Rec list atoms contain null values at ").append(i).append("\n"); - } - } - try { - Files.write(Paths.get(LOG_FILE_PATH), logBuilder.toString().getBytes(), StandardOpenOption.CREATE, - StandardOpenOption.APPEND); - } catch (IOException ignored) { - } - }); - validateThread.setDaemon(true); - validateThread.start(); - } - - @Override - public void registerTypeCheckStart(Context cx, SemType t1, SemType t2) { - synchronized (pendingTypeResolutions) { - pendingTypeResolutions.removeIf(data -> data.cx == cx); - } - synchronized (pendingTypeChecks) { - pendingTypeChecks.add(new TypeCheckData(cx, t1, t2)); - } - validateRecAtomState(); - Thread t = Thread.currentThread(); - t.setUncaughtExceptionHandler((thread, throwable) -> uncaughtExceptions.put(thread, throwable.getMessage())); - } - - @Override - public void registerTypeCheckEnd(Context cx) { - synchronized (pendingTypeChecks) { - pendingTypeChecks.removeIf(data -> data.cx == cx); - } - } - - @Override - public void registerAbruptTypeResolutionEnd(Context cx, Exception ex) { - StringBuilder logBuilder = new StringBuilder(); - logBuilder.append("Abrupt type resolution end:\n"); - logBuilder.append("Context: ").append(cx).append("\n"); - logBuilder.append("Exception: ").append(ex.getMessage()).append("\n"); - for (StackTraceElement element : ex.getStackTrace()) { - logBuilder.append("\tat ").append(element).append("\n"); - } - synchronized (pendingTypeChecks) { - logBuilder.append(pendingTypeChecksToString(pendingTypeChecks)); - } - synchronized (pendingTypeResolutions) { - logBuilder.append(pendingTypeResolutionsToString(pendingTypeResolutions)); - } - logBuilder.append("\n\n"); - try { - Files.write(Paths.get(LOG_FILE_PATH), logBuilder.toString().getBytes(), StandardOpenOption.CREATE, - StandardOpenOption.APPEND); - } catch (IOException ignored) { - } - } - - @Override - public void registerAbruptTypeCheckEnd(Context cx, Exception ex) { - StringBuilder logBuilder = new StringBuilder(); - logBuilder.append("Abrupt type check end:\n"); - logBuilder.append("Context: ").append(cx).append("\n"); - logBuilder.append("Exception: ").append(ex.getMessage()).append("\n"); - for (StackTraceElement element : ex.getStackTrace()) { - logBuilder.append("\tat ").append(element).append("\n"); - } - synchronized (pendingTypeChecks) { - logBuilder.append(pendingTypeChecksToString(pendingTypeChecks)); - } - synchronized (pendingTypeResolutions) { - logBuilder.append(pendingTypeResolutionsToString(pendingTypeResolutions)); - } - - logBuilder.append("\n\n"); - try { - Files.write(Paths.get(LOG_FILE_PATH), logBuilder.toString().getBytes(), StandardOpenOption.CREATE, - StandardOpenOption.APPEND); - } catch (IOException ignored) { - } - } - - @Override - public void registerTypeResolutionExit(Context cx) { - synchronized (pendingTypeResolutions) { - pendingTypeResolutions.removeIf(data -> data.cx == cx); - } - } - - private static String withIdentity(Object o) { - return o + "[" + System.identityHashCode(o) + "]"; - } - - private static String pendingTypeChecksToString(Collection typeChecks) { - StringBuilder logBuilder = new StringBuilder(); - logBuilder.append("Pending type checks:\n"); - typeChecks.forEach(data -> { - logBuilder.append(data.cx).append(" - ").append(withIdentity(data.t1)).append(" - ") - .append(withIdentity(data.t2)).append("\n"); - logBuilder.append("Thread state: ").append(data.t.getState()).append("\n"); - for (StackTraceElement element : data.t.getStackTrace()) { - logBuilder.append("\tat ").append(element).append("\n"); - } - logBuilder.append("Entry point\n"); - var typeCheckPhases = new ArrayList<>(data.cx.typeCheckPhases); - typeCheckPhases.forEach(each -> logBuilder.append(each.toString()).append("\n")); - }); - return logBuilder.toString(); - } - - private static String pendingTypeResolutionsToString(Collection typeResolutions) { - StringBuilder logBuilder = new StringBuilder(); - logBuilder.append("Pending type resolutions:\n"); - typeResolutions.forEach(data -> { - logBuilder.append(data.cx).append(" - ").append(withIdentity(data.type)).append("\n"); - logBuilder.append("Thread state: ").append(data.t.getState()).append("\n"); - for (StackTraceElement element : data.t.getStackTrace()) { - logBuilder.append("\tat ").append(element).append("\n"); - } - logBuilder.append("Entry point\n"); - List typeResolutionPhases = new ArrayList<>(data.cx.typeResolutionPhases); - typeResolutionPhases.forEach(each -> logBuilder.append(each.toString()).append("\n")); - }); - return logBuilder.toString(); - } - - private record TypeResolutionData(Context cx, Thread t, MutableSemType type, long startTime) { - - TypeResolutionData(Context cx, MutableSemType type) { - this(cx, Thread.currentThread(), type, System.nanoTime()); - } - } - - private record TypeCheckData(Context cx, Thread t, SemType t1, SemType t2, long startTime) { - - TypeCheckData(Context cx, SemType t1, SemType t2) { - this(cx, Thread.currentThread(), t1, t2, System.nanoTime()); - } - } - - private class HouseKeeper implements Runnable { - - @Override - public void run() { - while (true) { - List hangedTypeResolutions; - List hangedTypeChecks; - synchronized (pendingTypeResolutions) { - hangedTypeResolutions = pendingTypeResolutions.stream().filter(data -> { - long elapsedTime = System.nanoTime() - data.startTime(); - return elapsedTime > MAX_TIMEOUT; - }).toList(); - } - synchronized (pendingTypeChecks) { - hangedTypeChecks = pendingTypeChecks.stream().filter(data -> { - long elapsedTime = System.nanoTime() - data.startTime(); - return elapsedTime > MAX_TIMEOUT; - }).toList(); - } - StringBuilder logBuilder = new StringBuilder(); - if (!hangedTypeResolutions.isEmpty()) { - logBuilder.append(pendingTypeResolutionsToString(hangedTypeResolutions)); - } - if (!hangedTypeChecks.isEmpty()) { - logBuilder.append(pendingTypeChecksToString(hangedTypeChecks)); - } - uncaughtExceptions.forEach((thread, message) -> { - logBuilder.append("Uncaught exception in thread: ").append(thread).append("\n"); - logBuilder.append("Exception message: ").append(message).append("\n"); - for (StackTraceElement element : thread.getStackTrace()) { - logBuilder.append("\tat ").append(element).append("\n"); - } - }); - uncaughtExceptions.clear(); - ThreadMXBean tmx = ManagementFactory.getThreadMXBean(); - long[] ids = tmx.findDeadlockedThreads(); - if (ids != null) { - ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true); - logBuilder.append("The following threads are deadlocked:"); - for (ThreadInfo ti : infos) { - logBuilder.append(ti); - } - } - - logBuilder.append("\n\n"); - try { - Files.write(Paths.get(LOG_FILE_PATH), logBuilder.toString().getBytes(), - StandardOpenOption.CREATE, StandardOpenOption.APPEND); - } catch (IOException ignored) { - } - try { - Thread.sleep(5000); // Sleep for 5 seconds - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } - } - } - } -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java index 9edf4527b222..0b0017f05841 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java @@ -36,4 +36,19 @@ public abstract class Definition { */ public abstract SemType getSemType(Env env); + /** + * Register the container as the holder of this definition. Used to maintain concurrency invariants. + * + * @param container holder of the definition + * @see io.ballerina.runtime.internal.types.semtype.DefinitionContainer + */ + public void registerContainer(DefinitionContainer container) { + this.container = container; + } + + protected void notifyContainer() { + if (container != null) { + container.definitionUpdated(); + } + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java index eb2db9bb46d4..10b00639a035 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java @@ -22,18 +22,16 @@ import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; import io.ballerina.runtime.internal.types.semtype.ListAtomicType; import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; -import io.ballerina.runtime.internal.types.semtype.MutableSemType; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Supplier; @@ -67,13 +65,9 @@ public final class Env { private final ReadWriteLock recFunctionLock = new ReentrantReadWriteLock(); private final List recFunctionAtoms; - private final ReadWriteLock cellTypeCacheLock = new ReentrantReadWriteLock(); - private final Map cellTypeCache = new HashMap<>(); + private final Map cellTypeCache = new ConcurrentHashMap<>(); private final AtomicInteger distinctAtomCount = new AtomicInteger(0); - private final TypeCheckSelfDiagnosticsRunner selfDiagnosticsRunner; - - private final AtomicLong pendingTypeResolutions = new AtomicLong(0); private Env() { this.atomTable = new WeakHashMap<>(); @@ -82,12 +76,6 @@ private Env() { this.recFunctionAtoms = new ArrayList<>(); PredefinedTypeEnv.getInstance().initializeEnv(this); - String diagnosticEnable = System.getenv("BAL_TYPE_CHECK_DIAGNOSTIC_ENABLE"); - if ("true".equalsIgnoreCase(diagnosticEnable)) { - this.selfDiagnosticsRunner = new DebugSelfDiagnosticRunner(this); - } else { - this.selfDiagnosticsRunner = new NonOpSelfDiagnosticRunner(); - } } public static Env getInstance() { @@ -128,27 +116,7 @@ SemType getCachedCellType(SemType ty, CellAtomicType.CellMutability mut, Supplie if (ty.some() != 0) { return semTypeCreator.get(); } - try { - cellTypeCacheLock.readLock().lock(); - SemType cached = this.cellTypeCache.get(new CellSemTypeCacheKey(ty, mut)); - if (cached != null) { - return cached; - } - } finally { - cellTypeCacheLock.readLock().unlock(); - } - try { - cellTypeCacheLock.writeLock().lock(); - SemType cached = this.cellTypeCache.get(new CellSemTypeCacheKey(ty, mut)); - if (cached != null) { - return cached; - } - var result = semTypeCreator.get(); - this.cellTypeCache.put(new CellSemTypeCacheKey(ty, mut), result); - return result; - } finally { - cellTypeCacheLock.writeLock().unlock(); - } + return this.cellTypeCache.computeIfAbsent(new CellSemTypeCacheKey(ty, mut), k -> semTypeCreator.get()); } public RecAtom recListAtom() { @@ -289,104 +257,4 @@ public Optional atomicTypeByIndex(int index) { atomLock.readLock().unlock(); } } - - // When it comes to types there are 2 distinct stages, first we need to resolve types (ie turn type - // descriptor in to a semtype) and then do the type checking. In the compiler there is a clear temporal separation - // between these stages, but in runtime since we allow creating type dynamically we must allow them to interleave. - // As result, we have to treat both these stages of the type check. When a type is being used for type checking - // it is resolved (modifying the type after this point is undefined behaviour). To understand concurrency model for - // type checking we can break up type checking to 2 phases as type resolution and type checking. To allow - // concurrent type checking we need ensure fallowing invariants. - // 1. Phase 1 should be able to run in a non-blocking manner. Assume we are checking T1 < T2 and T3 < T4 - // concurrently with T1 depending on T3 and T4 depending on T2. If they are blocking we can have a deadlock. - // 2. Before starting phase 2 all the types involved in the type check must be resolved. In above example T3 which - // is needed for first type check is being resolved as a part of the second type check. - // Furthermore, ideally we shouldn't resolve the same type multiple times and both type checks should be able to - // run parallel as much as possible. - // Given each (strand) thread has its own context, it is easier to reason about concurrency using Context. First - // we require all phase changes to go via the context which will synchronize with other contexts via the shared Env. - // First we allow any number of context to enter phase 1 and run without blocking(property 1). When context - // need to move to phase 2 it must wait for all contexts in phase 1 to finish. To prevent starvation when a - // context has indicated that it needs to move to phase 2 we stop any new context from entering phase 1. When all - // the contexts have reached phase 2 again they all can continue in parallel. At the same time we can allow new - // context to enter phase 1. - - void enterTypeResolutionPhase(Context cx, MutableSemType t) throws InterruptedException { - pendingTypeResolutions.incrementAndGet(); - this.selfDiagnosticsRunner.registerTypeResolutionStart(cx, t); - } - - void exitTypeResolutionPhaseAbruptly(Context cx, Exception ex) { - try { - pendingTypeResolutions.decrementAndGet(); - releaseLock((ReentrantReadWriteLock) atomLock); - releaseLock((ReentrantReadWriteLock) recListLock); - releaseLock((ReentrantReadWriteLock) recMapLock); - releaseLock((ReentrantReadWriteLock) recFunctionLock); - } catch (Exception ignored) { - - } - this.selfDiagnosticsRunner.registerAbruptTypeResolutionEnd(cx, ex); - } - - private void releaseLock(ReentrantReadWriteLock lock) { - if (lock.writeLock().isHeldByCurrentThread()) { - lock.writeLock().unlock(); - } - if (lock.getReadHoldCount() > 0) { - lock.readLock().unlock(); - } - } - - void exitTypeResolutionPhase(Context cx) { - long res = pendingTypeResolutions.decrementAndGet(); - assert res >= 0; - this.selfDiagnosticsRunner.registerTypeResolutionExit(cx); - } - - void enterTypeCheckingPhase(Context cx, SemType t1, SemType t2) { - assert pendingTypeResolutions.get() >= 0; - while (pendingTypeResolutions.get() != 0) { - try { - Thread.sleep(10); - } catch (InterruptedException ignored) { - } - } - this.selfDiagnosticsRunner.registerTypeCheckStart(cx, t1, t2); - } - - void exitTypeCheckingPhase(Context cx) { - this.selfDiagnosticsRunner.registerTypeCheckEnd(cx); - } - - void registerAbruptTypeCheckEnd(Context context, Exception ex) { - this.selfDiagnosticsRunner.registerAbruptTypeCheckEnd(context, ex); - } - - List getRecListAtomsCopy() { - recListLock.readLock().lock(); - try { - return new ArrayList<>(this.recListAtoms); - } finally { - recListLock.readLock().unlock(); - } - } - - List getRecMappingAtomsCopy() { - recMapLock.readLock().lock(); - try { - return new ArrayList<>(this.recMappingAtoms); - } finally { - recMapLock.readLock().unlock(); - } - } - - List getRecFunctionAtomsCopy() { - recFunctionLock.readLock().lock(); - try { - return new ArrayList<>(this.recFunctionAtoms); - } finally { - recFunctionLock.readLock().unlock(); - } - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/NonOpSelfDiagnosticRunner.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/NonOpSelfDiagnosticRunner.java deleted file mode 100644 index 47aa4d078c60..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/NonOpSelfDiagnosticRunner.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.ballerina.runtime.api.types.semtype; - -import io.ballerina.runtime.internal.types.semtype.MutableSemType; - -public class NonOpSelfDiagnosticRunner implements TypeCheckSelfDiagnosticsRunner { - - @Override - public void registerTypeResolutionStart(Context cx, MutableSemType type) { - - } - - @Override - public void registerTypeCheckStart(Context cx, SemType t1, SemType t2) { - - } - - @Override - public void registerTypeCheckEnd(Context cx) { - - } - - @Override - public void registerAbruptTypeResolutionEnd(Context cx, Exception ex) { - - } - - @Override - public void registerAbruptTypeCheckEnd(Context cx, Exception ex) { - - } - - @Override - public void registerTypeResolutionExit(Context cx) { - - } -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java index 22c385d1a9e1..1545a36e572e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java @@ -566,19 +566,29 @@ SemType readonlyType() { // Due to some reason SpotBug thinks this method is overrideable if we don't put final here as well. final void initializeEnv(Env env) { assert initialized.get() : "PredefinedTypeEnv has not fully initialized, check concurrency issues"; - fillRecAtoms(env.recListAtoms, initializedRecListAtoms, initializedRecListAtoms.size()); - fillRecAtoms(env.recMappingAtoms, initializedRecMappingAtoms, initializedRecMappingAtoms.size()); + fillRecAtoms(env.recListAtoms, initializedRecListAtoms); + fillRecAtoms(env.recMappingAtoms, initializedRecMappingAtoms); initializedCellAtoms.forEach(each -> env.cellAtom(each.atomicType())); initializedListAtoms.forEach(each -> env.listAtom(each.atomicType())); } - private void fillRecAtoms(List envRecAtomList, List initializedRecAtoms, - int reservedAtomCount) { - for (int i = 0; i < reservedAtomCount; i++) { - envRecAtomList.add(initializedRecAtoms.get(i)); + private void fillRecAtoms(List envRecAtomList, List initializedRecAtoms) { + int count = reservedRecAtomCount(); + for (int i = 0; i < count; i++) { + if (i < initializedRecAtoms.size()) { + envRecAtomList.add(initializedRecAtoms.get(i)); + } else { + // This is mainly to help with bir serialization/deserialization logic. Given the number of such atoms + // will be small this shouldn't be a problem. + envRecAtomList.add(null); + } } } + private int reservedRecAtomCount() { + return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); + } + SemType cellSemTypeInner() { return cellSemTypeInner.get(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java index 3690198efa3c..d185c96b6c7f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java @@ -57,11 +57,10 @@ protected void setSome(int some, SubType[] subTypeData) { this.subTypeData = subTypeData; } - public static SemType tryInto(Context cx, Type type) { + public static SemType tryInto(Type type) { if (type instanceof MutableSemType mutableSemType) { - mutableSemType.updateInnerSemTypeIfNeeded(cx); + mutableSemType.updateInnerSemTypeIfNeeded(); } - return (SemType) type; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java index 2eceade31e8e..99bc4c9a51f5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java @@ -23,7 +23,7 @@ public static Optional acceptedTypeOf(Context cx, Type typeDesc) { if (typeDesc instanceof TypeWithAcceptedType typeWithAcceptedType) { return typeWithAcceptedType.acceptedTypeOf(cx); } - return Optional.of(SemType.tryInto(cx, typeDesc)); + return Optional.of(SemType.tryInto(typeDesc)); } public static Optional shapeOf(Context cx, Object object) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckSelfDiagnosticsRunner.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckSelfDiagnosticsRunner.java deleted file mode 100644 index aa3b65bc80f4..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckSelfDiagnosticsRunner.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.ballerina.runtime.api.types.semtype; - -import io.ballerina.runtime.internal.types.semtype.MutableSemType; - -interface TypeCheckSelfDiagnosticsRunner { - - void registerTypeResolutionStart(Context cx, MutableSemType type); - - void registerTypeCheckStart(Context cx, SemType t1, SemType t2); - - void registerTypeCheckEnd(Context cx); - - void registerAbruptTypeResolutionEnd(Context cx, Exception ex); - - void registerAbruptTypeCheckEnd(Context cx, Exception ex); - - void registerTypeResolutionExit(Context cx); -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java index 6386420a90ad..c23f563ff6d1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java @@ -67,11 +67,11 @@ default String informalStringValue(BLink parent) { * * @return {@code SemType} representing the value's basic type */ - default SemType widenedType(Context cx) { + default SemType widenedType() { // This is wrong since we are actually returning the actual (narrowed) type of the value. But since this is // used only as an optimization (to avoid recalculating singleton type) in the type checker this is better // than caching the widened types as well. - return SemType.tryInto(cx, getType()); + return SemType.tryInto(getType()); } default Optional inherentTypeOf(Context cx) { 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 ec998db10d0f..d1d5aeb91d80 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 @@ -124,10 +124,8 @@ public static Object checkCast(Object sourceVal, Type targetType) { return sourceVal; } Type sourceType = getType(sourceVal); - Context cx = context(); - if (Core.containsBasicType(SemType.tryInto(cx, sourceType), ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK) && - Core.containsBasicType(SemType.tryInto(cx, targetType), - ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK)) { + if (Core.containsBasicType(SemType.tryInto(sourceType), ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK) && + Core.containsBasicType(SemType.tryInto(targetType), ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK)) { // We need to maintain order for these? if (targetType instanceof BUnionType unionType) { for (Type memberType : unionType.getMemberTypes()) { @@ -260,13 +258,12 @@ public static boolean anyToJBoolean(Object sourceVal) { */ public static boolean checkIsType(Object sourceVal, Type targetType) { Type sourceType = getType(sourceVal); - Context cx = context(); - if (isSubType(cx, sourceType, targetType)) { + if (isSubType(sourceType, targetType)) { return true; } - SemType sourceSemType = SemType.tryInto(cx, sourceType); + SemType sourceSemType = SemType.tryInto(sourceType); return couldInherentTypeBeDifferent(sourceSemType) && - isSubTypeWithInherentType(cx, sourceVal, SemType.tryInto(cx, targetType)); + isSubTypeWithInherentType(cx, sourceVal, SemType.tryInto(targetType)); } /** @@ -341,8 +338,7 @@ private static SemType appendNumericConversionTypes(SemType semType) { * @return true if the two types are same; false otherwise */ public static boolean isSameType(Type sourceType, Type targetType) { - Context cx = context(); - return Core.isSameType(cx, SemType.tryInto(cx, sourceType), SemType.tryInto(cx, targetType)); + return Core.isSameType(context(), SemType.tryInto(sourceType), SemType.tryInto(targetType)); } public static Type getType(Object value) { @@ -428,8 +424,8 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { Context cx = context(); SemType lhsType = widenedType(cx, lhsValue); SemType rhsType = widenedType(cx, rhsValue); - if (isSimpleBasicSemType(cx, lhsType)) { - return isSimpleBasicValuesEqual(cx, lhsValue, rhsValue); + if (isSimpleBasicSemType(lhsType)) { + return isSimpleBasicValuesEqual(lhsValue, rhsValue); } Predicate basicTypePredicate = (basicType) -> Core.isSubType(cx, lhsType, basicType) && Core.isSubType(cx, rhsType, basicType); @@ -497,14 +493,15 @@ private static boolean isFunctionPointerEqual(Type lhsType, Type rhsType) { lhsType.getName().equals(rhsType.getName()) && rhsType.equals(lhsType); } - private static boolean isSimpleBasicValuesEqual(Context cx, Object v1, Object v2) { + private static boolean isSimpleBasicValuesEqual(Object v1, Object v2) { + Context cx = context(); SemType v1Ty = widenedType(cx, v1); - if (!isSimpleBasicSemType(cx, v1Ty)) { + if (!isSimpleBasicSemType(v1Ty)) { return false; } SemType v2Ty = widenedType(cx, v2); - if (!isSimpleBasicSemType(cx, v2Ty)) { + if (!isSimpleBasicSemType(v2Ty)) { return false; } @@ -534,8 +531,7 @@ public static TypedescValue getTypedesc(Object value) { if (type == null) { return null; } - Context cx = context(); - if (belongToSingleBasicTypeOrString(cx, type)) { + if (belongToSingleBasicTypeOrString(type)) { return new TypedescValueImpl(new BFiniteType(value.toString(), Set.of(value), 0)); } if (value instanceof BRefValue bRefValue) { @@ -567,12 +563,12 @@ public static Object getAnnotValue(TypedescValue typedescValue, BString annotTag * @return flag indicating the equivalence of the two types */ public static boolean checkIsType(Type sourceType, Type targetType) { - return isSubType(context(), sourceType, targetType); + return isSubType(sourceType, targetType); } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - return isSubType(context(), sourceType, targetType); + return isSubType(sourceType, targetType); } /** @@ -588,8 +584,7 @@ public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsV } public static boolean isNumericType(Type type) { - Context cx = context(); - return Core.isSubType(cx, SemType.tryInto(cx, type), NumericTypeHolder.NUMERIC_TYPE); + return Core.isSubType(context(), SemType.tryInto(type), NumericTypeHolder.NUMERIC_TYPE); } public static boolean isByteLiteral(long longValue) { @@ -601,28 +596,28 @@ public static boolean isByteLiteral(long longValue) { private static boolean isSubTypeWithInherentType(Context cx, Object sourceValue, SemType target) { return ShapeAnalyzer.inherentTypeOf(cx, sourceValue) .map(source -> !Core.isEmpty(cx, source) && Core.isSubType(cx, source, target)) + // OR else do the normal type check by taking the shape of .orElse(false); } - private static boolean isSubType(Context cx, Type source, Type target) { - if (source == target || (source.getTag() == target.getTag() && source.equals(target))) { - return true; - } + private static boolean isSubType(Type source, Type target) { if (source instanceof CacheableTypeDescriptor sourceCacheableType && target instanceof CacheableTypeDescriptor targetCacheableType) { - return isSubTypeWithCache(cx, sourceCacheableType, targetCacheableType); + return isSubTypeWithCache(sourceCacheableType, targetCacheableType); } - return isSubTypeInner(cx, source, target); + // This is really a workaround for Standard libraries that create record types that are not the "same". But + // with the same name and expect them to be same. + return isSubTypeInner(context(), source, target); } private static boolean isSubTypeInner(Context cx, Type source, Type target) { - SemType sourceSemType = SemType.tryInto(cx, source); - SemType targetSemType = SemType.tryInto(cx, target); + SemType sourceSemType = SemType.tryInto(source); + SemType targetSemType = SemType.tryInto(target); return Core.isSubType(cx, sourceSemType, targetSemType); } - private static boolean isSubTypeWithCache(Context cx, CacheableTypeDescriptor source, - CacheableTypeDescriptor target) { + private static boolean isSubTypeWithCache(CacheableTypeDescriptor source, CacheableTypeDescriptor target) { + Context cx = context(); if (!source.shouldCache() || !target.shouldCache()) { return isSubTypeInner(cx, source, target); } @@ -638,7 +633,7 @@ private static boolean isSubTypeWithCache(Context cx, CacheableTypeDescriptor so private static SemType widenedType(Context cx, Object value) { if (value instanceof BValue bValue) { - return bValue.widenedType(cx); + return bValue.widenedType(); } if (value == null) { return Builder.getNilType(); @@ -656,9 +651,8 @@ private static SemType widenedType(Context cx, Object value) { public static boolean isInherentlyImmutableType(Type sourceType) { // readonly part is there to match to old API - Context cx = context(); return - Core.isSubType(cx, SemType.tryInto(cx, sourceType), + Core.isSubType(context(), SemType.tryInto(sourceType), InherentlyImmutableTypeHolder.INHERENTLY_IMMUTABLE_TYPE) || sourceType instanceof ReadonlyType; } @@ -818,10 +812,11 @@ public static boolean isEqual(Object lhsValue, Object rhsValue, Set c return false; } - return checkValueEqual(context(), lhsValue, rhsValue, new HashSet<>(checkedValues)); + return checkValueEqual(lhsValue, rhsValue, new HashSet<>(checkedValues)); } - private static boolean checkValueEqual(Context cx, Object lhsValue, Object rhsValue, Set checkedValues) { + private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set checkedValues) { + Context cx = context(); SemType lhsShape = ShapeAnalyzer.inherentTypeOf(cx, lhsValue).orElseThrow(); SemType rhsShape = ShapeAnalyzer.inherentTypeOf(cx, rhsValue).orElseThrow(); Predicate belongToSameBasicType = (basicType) -> Core.containsBasicType(lhsShape, basicType) && @@ -981,7 +976,7 @@ private static boolean isHandleValueRefEqual(Object lhsValue, Object rhsValue) { * @return whether there's an implicit initial value or not. */ public static boolean hasFillerValue(Type type) { - return hasFillerValue(context(), type, new ArrayList<>()); + return hasFillerValue(type, new ArrayList<>()); } private enum FillerValueResult { @@ -1003,12 +998,12 @@ private static FillerValueResult hasFillerValueSemType(Context cx, SemType type) FillerValueResult.FALSE; } - private static boolean hasFillerValue(Context cx, Type type, List unanalyzedTypes) { + private static boolean hasFillerValue(Type type, List unanalyzedTypes) { if (type == null) { return true; } - FillerValueResult fastResult = hasFillerValueSemType(cx, SemType.tryInto(cx, type)); + FillerValueResult fastResult = hasFillerValueSemType(context(), SemType.tryInto(type)); if (fastResult != FillerValueResult.MAYBE) { return fastResult == FillerValueResult.TRUE; } @@ -1026,29 +1021,29 @@ private static boolean hasFillerValue(Context cx, Type type, List unanalyz case TypeTags.STREAM_TAG, TypeTags.MAP_TAG, TypeTags.ANY_TAG -> true; - case TypeTags.ARRAY_TAG -> checkFillerValue(cx, (BArrayType) type, unanalyzedTypes); + case TypeTags.ARRAY_TAG -> checkFillerValue((BArrayType) type, unanalyzedTypes); case TypeTags.FINITE_TYPE_TAG -> checkFillerValue((BFiniteType) type); case TypeTags.OBJECT_TYPE_TAG, - TypeTags.SERVICE_TAG -> checkFillerValue(cx, (BObjectType) type); - case TypeTags.RECORD_TYPE_TAG -> checkFillerValue(cx, (BRecordType) type, unanalyzedTypes); - case TypeTags.TUPLE_TAG -> checkFillerValue(cx, (BTupleType) type, unanalyzedTypes); + TypeTags.SERVICE_TAG -> checkFillerValue((BObjectType) type); + case TypeTags.RECORD_TYPE_TAG -> checkFillerValue((BRecordType) type, unanalyzedTypes); + case TypeTags.TUPLE_TAG -> checkFillerValue((BTupleType) type, unanalyzedTypes); case TypeTags.UNION_TAG -> checkFillerValue((BUnionType) type, unanalyzedTypes); case TypeTags.TYPE_REFERENCED_TYPE_TAG -> - hasFillerValue(cx, ((BTypeReferenceType) type).getReferredType(), unanalyzedTypes); + hasFillerValue(((BTypeReferenceType) type).getReferredType(), unanalyzedTypes); case TypeTags.INTERSECTION_TAG -> - hasFillerValue(cx, ((BIntersectionType) type).getEffectiveType(), unanalyzedTypes); + hasFillerValue(((BIntersectionType) type).getEffectiveType(), unanalyzedTypes); default -> false; }; } - private static boolean checkFillerValue(Context cx, BTupleType tupleType, List unAnalyzedTypes) { + private static boolean checkFillerValue(BTupleType tupleType, List unAnalyzedTypes) { if (unAnalyzedTypes.contains(tupleType)) { return true; } unAnalyzedTypes.add(tupleType); for (Type member : tupleType.getTupleTypes()) { - if (!hasFillerValue(cx, member, unAnalyzedTypes)) { + if (!hasFillerValue(member, unAnalyzedTypes)) { return false; } } @@ -1157,7 +1152,7 @@ private static boolean containsSameBasicType (Type nonFiniteType, Set fi return true; } - private static boolean checkFillerValue(Context cx, BRecordType type, List unAnalyzedTypes) { + private static boolean checkFillerValue(BRecordType type, List unAnalyzedTypes) { if (unAnalyzedTypes.contains(type)) { return true; } @@ -1174,11 +1169,11 @@ private static boolean checkFillerValue(Context cx, BRecordType type, List return true; } - private static boolean checkFillerValue(Context cx, BArrayType type, List unAnalyzedTypes) { - return type.getState() == ArrayState.OPEN || hasFillerValue(cx, type.getElementType(), unAnalyzedTypes); + private static boolean checkFillerValue(BArrayType type, List unAnalyzedTypes) { + return type.getState() == ArrayState.OPEN || hasFillerValue(type.getElementType(), unAnalyzedTypes); } - private static boolean checkFillerValue(Context cx, BObjectType type) { + private static boolean checkFillerValue(BObjectType type) { MethodType generatedInitMethod = type.getGeneratedInitMethod(); if (generatedInitMethod == null) { // abstract objects doesn't have a filler value. @@ -1254,12 +1249,14 @@ private static BError createTypeCastError(Object value, Type targetType, List ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); } - Predicate isIntSubType = (subType) -> Core.isSameType(cx, targetType, SemType.tryInto(cx, subType)); + Predicate isIntSubType = (subType) -> Core.isSameType(cx, targetType, SemType.tryInto(subType)); if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_32)) { return anyToSigned32(inputValue); } @@ -170,14 +171,14 @@ private static Object castValueToInt(Context cx, SemType targetType, Object inpu } public static Object castValues(Type targetType, Object inputValue) { - Context cx = TypeChecker.context(); - return castValuesInner(cx, SemType.tryInto(cx, targetType), inputValue, + return castValuesInner(SemType.tryInto(targetType), inputValue, () -> ErrorUtils.createTypeCastError(inputValue, targetType)); } - static Object castValuesInner(Context cx, SemType targetType, Object inputValue, Supplier errorSupplier) { + static Object castValuesInner(SemType targetType, Object inputValue, Supplier errorSupplier) { + Context cx = TypeChecker.context(); if (Core.isSubType(cx, targetType, Builder.getIntType())) { - return castValueToInt(cx, targetType, inputValue); + return castValueToInt(targetType, inputValue); } if (Core.isSubType(cx, targetType, Builder.getDecimalType())) { return anyToDecimalCast(inputValue, () -> @@ -423,11 +424,10 @@ private static Type getConvertibleStructuredTypeInUnion(Object inputValue, Strin public static Type getConvertibleFiniteType(Object inputValue, BFiniteType targetFiniteType, String varName, List errors, Set unresolvedValues, boolean allowNumericConversion) { - Context cx = TypeChecker.context(); // only the first matching type is returned. if (targetFiniteType.valueSpace.size() == 1) { Type valueType = getType(targetFiniteType.valueSpace.iterator().next()); - if (!belongToSingleBasicTypeOrString(cx, valueType) && valueType.getTag() != TypeTags.NULL_TAG) { + if (!belongToSingleBasicTypeOrString(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { return getConvertibleType(inputValue, valueType, varName, unresolvedValues, errors, allowNumericConversion); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java index 26cb9762de48..71dcdc374f48 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java @@ -25,7 +25,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.semtype.Builder; -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.internal.values.RefValue; @@ -95,7 +94,7 @@ public String toString() { // semtype. But some things could depend on this being a union type descriptor // as well (which has to be mutable) @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { SemType semType = Builder.getAnyDataType(); if (isReadOnly()) { semType = Core.intersect(semType, Builder.getReadonlyType()); 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..07834065c87d 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 @@ -227,8 +227,8 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType(Context cx) { - Env env = cx.env; + public SemType createSemType() { + Env env = Env.getInstance(); if (defn.isDefinitionReady()) { return defn.getSemType(env); } @@ -239,7 +239,7 @@ public SemType createSemType(Context cx) { ListDefinition ld = result.definition(); CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - return getSemTypePart(env, ld, size, tryInto(cx, getElementType()), mut); + return getSemTypePart(env, ld, size, tryInto(getElementType()), mut); } private SemType getSemTypePart(Env env, ListDefinition defn, int size, SemType elementType, @@ -266,7 +266,7 @@ protected boolean isDependentlyTypedInner(Set visited) { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } AbstractArrayValue value = (AbstractArrayValue) object; SemType cachedShape = value.shapeOf(); 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..44e8d987ccbf 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 @@ -29,6 +29,7 @@ import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.values.ErrorValue; import io.ballerina.runtime.internal.values.MapValueImpl; @@ -127,23 +128,23 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { SemType err; if (detailType == null || isTopType()) { err = Builder.getErrorType(); } else { - err = ErrorUtils.errorDetail(tryInto(cx, getDetailType())); + err = ErrorUtils.errorDetail(tryInto(getDetailType())); } - initializeDistinctIdSupplierIfNeeded(cx); + initializeDistinctIdSupplierIfNeeded(); return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); } - private void initializeDistinctIdSupplierIfNeeded(Context cx) { + private void initializeDistinctIdSupplierIfNeeded() { if (distinctIdSupplier == null) { synchronized (this) { if (distinctIdSupplier == null) { - distinctIdSupplier = new DistinctIdSupplier(cx.env, getTypeIdSet()); + distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, getTypeIdSet()); } } } @@ -162,14 +163,14 @@ private boolean isTopType() { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } BError errorValue = (BError) object; Object details = errorValue.getDetails(); if (!(details instanceof MapValueImpl errorDetails)) { return Optional.empty(); } - initializeDistinctIdSupplierIfNeeded(cx); + initializeDistinctIdSupplierIfNeeded(); // Should we actually pass the readonly shape supplier here? return BMapType.shapeOfInner(cx, shapeSupplier, errorDetails) .map(ErrorUtils::errorDetail) @@ -189,7 +190,7 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje @Override public Optional acceptedTypeOf(Context cx) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } @Override 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..bcef259ed050 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 @@ -210,7 +210,8 @@ public boolean equals(Object o) { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { + Context cx = TypeChecker.context(); return this.valueSpace.stream().map(each -> ShapeAnalyzer.inherentTypeOf(cx, each)) .map(Optional::orElseThrow) .reduce(Builder.getNeverType(), Core::union); 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..ecc2a44c1f98 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 @@ -26,7 +26,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.semtype.Builder; -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.internal.types.semtype.CellAtomicType; @@ -50,6 +49,8 @@ public class BFunctionType extends BAnnotatableType implements FunctionType { public Type retType; public long flags; public Parameter[] parameters; + private static final Env env = Env.getInstance(); + private static final SemType ISOLATED_TOP = createIsolatedTop(env); private final DefinitionContainer defn = new DefinitionContainer<>(); @@ -222,11 +223,10 @@ private static SemType createIsolatedTop(Env env) { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { if (isFunctionTop()) { - return getTopType(cx); + return getTopType(); } - Env env = cx.env; if (defn.isDefinitionReady()) { return defn.getSemType(env); } @@ -237,25 +237,25 @@ public SemType createSemType(Context cx) { FunctionDefinition fd = result.definition(); SemType[] params = new SemType[parameters.length]; for (int i = 0; i < parameters.length; i++) { - params[i] = getSemType(cx, parameters[i].type); + params[i] = getSemType(parameters[i].type); } SemType rest; if (restType instanceof BArrayType arrayType) { - rest = getSemType(cx, arrayType.getElementType()); + rest = getSemType(arrayType.getElementType()); } else { rest = Builder.getNeverType(); } - SemType returnType = resolveReturnType(cx); + SemType returnType = resolveReturnType(); ListDefinition paramListDefinition = new ListDefinition(); SemType paramType = paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, CellAtomicType.CellMutability.CELL_MUT_NONE); return fd.define(env, paramType, returnType, getQualifiers()); } - private SemType getTopType(Context cx) { + private SemType getTopType() { if (SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED)) { - return createIsolatedTop(cx.env); + return ISOLATED_TOP; } return Builder.getFunctionType(); } @@ -265,8 +265,8 @@ FunctionQualifiers getQualifiers() { SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); } - private SemType getSemType(Context cx, Type type) { - return tryInto(cx, type); + private SemType getSemType(Type type) { + return tryInto(type); } private boolean isFunctionTop() { @@ -294,17 +294,17 @@ private boolean isDependentlyTypeParameters(Set visited) { .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); } - private SemType resolveReturnType(Context cx) { + private SemType resolveReturnType() { if (retType == null) { return Builder.getNilType(); } MayBeDependentType retBType = (MayBeDependentType) retType; - SemType returnType = getSemType(cx, retType); + SemType returnType = getSemType(retType); ListDefinition ld = new ListDefinition(); SemType dependentlyTypedBit = retBType.isDependentlyTyped() ? Builder.getBooleanConst(true) : Builder.getBooleanType(); SemType[] innerType = new SemType[]{dependentlyTypedBit, returnType}; - return ld.defineListTypeWrapped(cx.env, innerType, 2, Builder.getNeverType(), + return ld.defineListTypeWrapped(env, innerType, 2, Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_NONE); } } 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..8839a92922fe 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 @@ -23,8 +23,8 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; 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.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.FutureUtils; import java.util.Objects; @@ -101,11 +101,11 @@ private String getConstraintString() { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { if (constraint == null) { return Builder.getFutureType(); } - return FutureUtils.futureContaining(cx.env, tryInto(cx, constraint)); + return FutureUtils.futureContaining(TypeChecker.context().env, tryInto(constraint)); } @Override 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..dc1991feb42c 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,6 +29,7 @@ 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.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; @@ -228,8 +229,8 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType(Context cx) { - return createSemTypeInner(cx, type -> SemType.tryInto(cx, type)); + public SemType createSemType() { + return createSemTypeInner(SemType::tryInto); } @Override @@ -238,28 +239,28 @@ protected boolean isDependentlyTypedInner(Set visited) { .anyMatch(type -> ((MayBeDependentType) type).isDependentlyTyped(visited)); } - private SemType createSemTypeInner(Context cx, Function semTypeFunction) { + private SemType createSemTypeInner(Function semTypeFunction) { if (constituentTypes.isEmpty()) { return Builder.getNeverType(); } SemType result = constituentTypes.stream().map(semTypeFunction).reduce(Core::intersect).orElseThrow(); - Optional distinctPart = distinctTypePart(cx, result); + Optional distinctPart = distinctTypePart(result); if (distinctPart.isPresent()) { result = Core.intersect(result, distinctPart.get()); } return result; } - private Optional distinctTypePart(Context cx, SemType result) { + private Optional distinctTypePart(SemType result) { if (Core.isSubtypeSimple(result, Builder.getErrorType())) { BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = - new DistinctIdSupplier(cx.env, effectiveErrorType.getTypeIdSet()); + new DistinctIdSupplier(TypeChecker.context().env, effectiveErrorType.getTypeIdSet()); return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(Core::intersect); } else if (Core.isSubtypeSimple(result, Builder.getObjectType())) { BObjectType effectiveObjectType = (BObjectType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = - new DistinctIdSupplier(cx.env, effectiveObjectType.getTypeIdSet()); + new DistinctIdSupplier(TypeChecker.context().env, effectiveObjectType.getTypeIdSet()); return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(Core::intersect); } return Optional.empty(); @@ -276,7 +277,7 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje @Override public Optional acceptedTypeOf(Context cx) { - return Optional.of(createSemTypeInner(cx, type -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); + return Optional.of(createSemTypeInner(type -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); } @Override 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..43d2058cd4d9 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 @@ -186,8 +186,8 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType(Context cx) { - Env env = cx.env; + public SemType createSemType() { + Env env = Env.getInstance(); if (defn.isDefinitionReady()) { return defn.getSemType(env); } @@ -198,7 +198,7 @@ public SemType createSemType(Context cx) { MappingDefinition md = result.definition(); CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - return createSemTypeInner(env, md, tryInto(cx, getConstrainedType()), mut); + return createSemTypeInner(env, md, tryInto(getConstrainedType()), mut); } @Override @@ -210,7 +210,7 @@ public void resetSemType() { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } MapValueImpl value = (MapValueImpl) object; SemType cachedShape = value.shapeOf(); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java index dbfcc2d6bd3a..5478813f2dc3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java @@ -22,7 +22,6 @@ import io.ballerina.runtime.api.types.NetworkObjectType; import io.ballerina.runtime.api.types.RemoteMethodType; import io.ballerina.runtime.api.types.ResourceMethodType; -import io.ballerina.runtime.api.types.semtype.Context; import java.util.ArrayList; import java.util.Arrays; @@ -85,16 +84,18 @@ public ResourceMethodType[] getResourceMethods() { } @Override - protected Collection allMethods(Context cx) { + protected Collection allMethods() { Stream methodStream = Arrays.stream(getMethods()) .filter(methodType -> !(SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.REMOTE) || SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.RESOURCE))) - .map(type -> MethodData.fromMethod(cx, type)); + .map(MethodData::fromMethod); Stream remoteMethodStream = Arrays.stream(getRemoteMethods()) - .map(type -> MethodData.fromRemoteMethod(cx, type)); - Stream resourceMethodStream = Arrays.stream(getResourceMethods()) - .map(method -> MethodData.fromResourceMethod(cx, (BResourceMethodType) method)); + .map(MethodData::fromRemoteMethod); + Stream resourceMethodStream = + Arrays.stream(getResourceMethods()) + .map(method -> MethodData.fromResourceMethod( + (BResourceMethodType) method)); return Stream.concat(methodStream, Stream.concat(remoteMethodStream, resourceMethodStream)).toList(); } } 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 150eab85f120..713a96f0d469 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 @@ -40,6 +40,7 @@ import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; import io.ballerina.runtime.internal.types.semtype.CellAtomicType; @@ -63,7 +64,7 @@ import java.util.Optional; import java.util.Set; import java.util.StringJoiner; -import java.util.function.BiFunction; +import java.util.function.Function; import static io.ballerina.runtime.api.types.TypeTags.SERVICE_TAG; @@ -88,7 +89,6 @@ 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 Lock typeResolutionLock = new ReentrantLock(); /** * Create a {@code BObjectType} which represents the user defined struct type. @@ -281,33 +281,25 @@ public TypeIdSet getTypeIdSet() { } @Override - public final SemType createSemType(Context cx) { - try { - // This is wrong (See {@code Env}). Instead this should be done similar to mapping and list definitions - // using rec atoms. - //typeResolutionLock.lock(); - Env env = cx.env; - initializeDistinctIdSupplierIfNeeded(env); - CellAtomicType.CellMutability mut = - SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY) ? - CellAtomicType.CellMutability.CELL_MUT_NONE : - CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType innerType; - if (defn.isDefinitionReady()) { + public final SemType createSemType() { + Env env = Env.getInstance(); + initializeDistinctIdSupplierIfNeeded(env); + CellAtomicType.CellMutability mut = + SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY) ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType innerType; + if (defn.isDefinitionReady()) { + innerType = defn.getSemType(env); + } else { + var result = defn.trySetDefinition(ObjectDefinition::new); + if (!result.updated()) { innerType = defn.getSemType(env); } else { - var result = defn.trySetDefinition(ObjectDefinition::new); - if (!result.updated()) { - innerType = defn.getSemType(env); - } else { - ObjectDefinition od = result.definition(); - innerType = semTypeInner(cx, od, mut, SemType::tryInto); - } + ObjectDefinition od = result.definition(); + innerType = semTypeInner(od, mut, SemType::tryInto); } - return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(innerType, Core::intersect); - } finally { - // typeResolutionLock.unlock(); } + return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(innerType, Core::intersect); } private static boolean skipField(Set seen, String name) { @@ -317,9 +309,9 @@ private static boolean skipField(Set seen, String name) { return !seen.add(name); } - private SemType semTypeInner(Context cx, ObjectDefinition od, CellAtomicType.CellMutability mut, - BiFunction semTypeSupplier) { - Env env = cx.env; + private SemType semTypeInner(ObjectDefinition od, CellAtomicType.CellMutability mut, + Function semTypeSupplier) { + Env env = Env.getInstance(); ObjectQualifiers qualifiers = getObjectQualifiers(); List members = new ArrayList<>(); Set seen = new HashSet<>(); @@ -331,10 +323,10 @@ private SemType semTypeInner(Context cx, ObjectDefinition od, CellAtomicType.Cel Field field = entry.getValue(); boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - members.add(new Member(name, semTypeSupplier.apply(cx, field.getFieldType()), Member.Kind.Field, + members.add(new Member(name, semTypeSupplier.apply(field.getFieldType()), Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } - for (MethodData method : allMethods(cx)) { + for (MethodData method : allMethods()) { String name = method.name(); if (skipField(seen, name)) { continue; @@ -363,7 +355,7 @@ private ObjectQualifiers getObjectQualifiers() { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } AbstractObjectValue abstractObjectValue = (AbstractObjectValue) object; SemType cachedShape = abstractObjectValue.shapeOf(); @@ -394,7 +386,7 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje @Override public final Optional acceptedTypeOf(Context cx) { - Env env = cx.env; + Env env = Env.getInstance(); initializeDistinctIdSupplierIfNeeded(cx.env); CellAtomicType.CellMutability mut = CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; SemType innerType; @@ -406,8 +398,7 @@ public final Optional acceptedTypeOf(Context cx) { innerType = acceptedTypeDefn.getSemType(env); } else { ObjectDefinition od = result.definition(); - innerType = semTypeInner(cx, od, mut, - ((context, type) -> ShapeAnalyzer.acceptedTypeOf(context, type).orElseThrow())); + innerType = semTypeInner(od, mut, (type -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); } } return Optional.of( @@ -446,7 +437,7 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObje members.add(new Member(name, fieldShape(cx, shapeSupplier, field, object, isImmutable), Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } - for (MethodData method : allMethods(cx)) { + for (MethodData method : allMethods()) { String name = method.name(); if (skipField(seen, name)) { continue; @@ -463,11 +454,11 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObje private static SemType fieldShape(Context cx, ShapeSupplier shapeSupplier, Field field, AbstractObjectValue objectValue, boolean isImmutable) { if (!isImmutable) { - return SemType.tryInto(cx, field.getFieldType()); + return SemType.tryInto(field.getFieldType()); } BString fieldName = StringUtils.fromString(field.getFieldName()); Optional shape = shapeSupplier.get(cx, objectValue.get(fieldName)); - assert shape.isPresent(); + assert !shape.isEmpty(); return shape.get(); } @@ -477,29 +468,29 @@ public void resetSemType() { super.resetSemType(); } - protected Collection allMethods(Context cx) { + protected Collection allMethods() { if (methodTypes == null) { return List.of(); } return Arrays.stream(methodTypes) - .map((type) -> MethodData.fromMethod(cx, type)).toList(); + .map(MethodData::fromMethod).toList(); } protected record MethodData(String name, long flags, SemType semType) { - static MethodData fromMethod(Context cx, MethodType method) { + static MethodData fromMethod(MethodType method) { return new MethodData(method.getName(), method.getFlags(), - tryInto(cx, method.getType())); + tryInto(method.getType())); } - static MethodData fromRemoteMethod(Context cx, MethodType method) { + static MethodData fromRemoteMethod(MethodType method) { // Remote methods need to be distinct with remote methods only there can be instance methods with the same // name return new MethodData("@remote_" + method.getName(), method.getFlags(), - tryInto(cx, method.getType())); + tryInto(method.getType())); } - static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { + static MethodData fromResourceMethod(BResourceMethodType method) { StringBuilder sb = new StringBuilder(); sb.append(method.getAccessor()); for (var each : method.getResourcePath()) { @@ -514,28 +505,28 @@ static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { if (part == null) { paramTypes.add(Builder.getAnyType()); } else { - paramTypes.add(tryInto(cx, part)); + paramTypes.add(tryInto(part)); } } for (Parameter paramType : innerFn.getParameters()) { - paramTypes.add(tryInto(cx, paramType.type)); + paramTypes.add(tryInto(paramType.type)); } SemType rest; Type restType = innerFn.getRestType(); if (restType instanceof BArrayType arrayType) { - rest = tryInto(cx, arrayType.getElementType()); + rest = tryInto(arrayType.getElementType()); } else { rest = Builder.getNeverType(); } SemType returnType; if (innerFn.getReturnType() != null) { - returnType = tryInto(cx, innerFn.getReturnType()); + returnType = tryInto(innerFn.getReturnType()); } else { returnType = Builder.getNilType(); } ListDefinition paramListDefinition = new ListDefinition(); - Env env = cx.env; + Env env = TypeChecker.context().env; SemType paramType = paramListDefinition.defineListTypeWrapped(env, paramTypes.toArray(SemType[]::new), paramTypes.size(), rest, CellAtomicType.CellMutability.CELL_MUT_NONE); FunctionDefinition fd = new FunctionDefinition(); 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..05b9229d68ac 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 @@ -21,7 +21,6 @@ import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; -import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Set; @@ -86,8 +85,8 @@ public int getParamIndex() { } @Override - public SemType createSemType(Context cx) { - return SemType.tryInto(cx, this.paramValueType); + public SemType createSemType() { + return SemType.tryInto(this.paramValueType); } @Override 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..f64e13db2304 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 @@ -243,8 +243,8 @@ public Map getDefaultValues() { } @Override - public SemType createSemType(Context cx) { - Env env = cx.env; + public SemType createSemType() { + Env env = Env.getInstance(); if (defn.isDefinitionReady()) { return defn.getSemType(env); } @@ -253,7 +253,7 @@ public SemType createSemType(Context cx) { return defn.getSemType(env); } MappingDefinition md = result.definition(); - return createSemTypeInner(md, env, mut(), (type) -> SemType.tryInto(cx, type)); + return createSemTypeInner(md, env, mut(), SemType::tryInto); } private CellMutability mut() { @@ -296,7 +296,7 @@ public boolean isDependentlyTypedInner(Set visited) { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } MapValueImpl value = (MapValueImpl) object; SemType cachedSemType = value.shapeOf(); @@ -336,7 +336,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueIm if (!takeFieldShape) { getFields().values().stream() .filter(field -> !handledFields.contains(field.getFieldName())) - .map(field -> fieldShapeWithoutValue(cx, field, field.getFieldName())) + .map(field -> fieldShapeWithoutValue(field, field.getFieldName())) .forEach(fields::add); } MappingDefinition.Field[] fieldsArray = fields.toArray(MappingDefinition.Field[]::new); @@ -344,22 +344,23 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueIm if (takeFieldShape) { rest = Builder.getNeverType(); } else { - rest = restFieldType != null ? SemType.tryInto(cx, restFieldType) : getNeverType(); + rest = restFieldType != null ? SemType.tryInto(restFieldType) : getNeverType(); } SemType shape = md.defineMappingTypeWrapped(env, fieldsArray, rest, mut()); value.resetReadonlyShapeDefinition(); return shape; } - private MappingDefinition.Field fieldShapeWithoutValue(Context cx, Field field, String fieldName) { + private MappingDefinition.Field fieldShapeWithoutValue(Field field, String fieldName) { boolean isOptional = fieldIsOptional(fieldName); boolean isReadonly = fieldIsReadonly(fieldName); - SemType fieldType = SemType.tryInto(cx, field.getFieldType()); + SemType fieldType = SemType.tryInto(field.getFieldType()); if (isReadonly && isOptional) { fieldType = Builder.getUndefType(); } - return new MappingDefinition.Field(field.getFieldName(), fieldType, + MappingDefinition.Field field1 = new MappingDefinition.Field(field.getFieldName(), fieldType, isReadonly, isOptional); + return field1; } @Override @@ -423,7 +424,7 @@ private MappingDefinition.Field fieldShape(Context cx, ShapeSupplier shapeSuppli optionalField = false; fieldType = shapeSupplier.get(cx, fieldValue).orElseThrow(); } else { - fieldType = SemType.tryInto(cx, fieldType(fieldName)); + fieldType = SemType.tryInto(fieldType(fieldName)); } return new MappingDefinition.Field(fieldName, fieldType, readonlyField, optionalField); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java index a99cd93e6b21..4c1f5ed85391 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java @@ -140,14 +140,12 @@ public boolean isAnydata() { @Override public boolean isPureType() { - return Core.isSubtypeSimple(this, Builder.getErrorType()) || isAnydata(); + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.getErrorType()) || isAnydata(); } @Override public boolean isReadOnly() { - if (Core.isSubtypeSimple(this, Builder.getInherentlyImmutable())) { - return true; - } Context cx = TypeChecker.context(); return Core.isSubType(cx, this, Builder.getReadonlyType()); } 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..1382a3e29943 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 @@ -25,9 +25,9 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.semtype.Builder; -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.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; import io.ballerina.runtime.internal.types.semtype.StreamDefinition; import io.ballerina.runtime.internal.values.StreamValue; @@ -145,11 +145,11 @@ public boolean equals(Object obj) { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { if (constraint == null) { return Builder.getStreamType(); } - Env env = cx.env; + Env env = TypeChecker.context().env; if (definition.isDefinitionReady()) { return definition.getSemType(env); } @@ -158,7 +158,7 @@ public SemType createSemType(Context cx) { return definition.getSemType(env); } StreamDefinition sd = result.definition(); - return sd.define(env, tryInto(cx, constraint), tryInto(cx, completionType)); + return sd.define(env, tryInto(constraint), tryInto(completionType)); } @Override 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..df743c54c344 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 @@ -28,6 +28,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.values.BTable; +import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.TableUtils; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TableValue; @@ -172,16 +173,17 @@ public boolean isAnydata() { } @Override - public SemType createSemType(Context cx) { - return createSemTypeWithConstraint(cx, tryInto(cx, constraint)); + public SemType createSemType() { + return createSemTypeWithConstraint(tryInto(constraint)); } - private SemType createSemTypeWithConstraint(Context cx, SemType constraintType) { + private SemType createSemTypeWithConstraint(SemType constraintType) { SemType semType; + Context cx = TypeChecker.context(); if (fieldNames.length > 0) { semType = TableUtils.tableContainingKeySpecifier(cx, constraintType, fieldNames); } else if (keyType != null) { - semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, tryInto(cx, keyType)); + semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, tryInto(keyType)); } else { semType = TableUtils.tableContaining(cx.env, constraintType); } @@ -195,7 +197,7 @@ private SemType createSemTypeWithConstraint(Context cx, SemType constraintType) @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } BTable table = (BTable) object; SemType cachedShape = table.shapeOf(); @@ -234,10 +236,10 @@ public Optional acceptedTypeOf(Context cx) { private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { SemType constraintType = Builder.getNeverType(); for (var value : table.values()) { - SemType valueShape = shapeSupplier.get(cx, value).orElse(SemType.tryInto(cx, constraint)); + SemType valueShape = shapeSupplier.get(cx, value).orElse(SemType.tryInto(constraint)); constraintType = Core.union(constraintType, valueShape); } - return createSemTypeWithConstraint(cx, constraintType); + return createSemTypeWithConstraint(constraintType); } @Override 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..ccda11d6f298 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 @@ -41,7 +41,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.BiFunction; +import java.util.function.Function; import java.util.stream.Collectors; import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; @@ -331,8 +331,8 @@ public String getAnnotationKey() { } @Override - public SemType createSemType(Context cx) { - Env env = cx.env; + public SemType createSemType() { + Env env = Env.getInstance(); if (defn.isDefinitionReady()) { return defn.getSemType(env); } @@ -341,26 +341,24 @@ public SemType createSemType(Context cx) { return defn.getSemType(env); } ListDefinition ld = result.definition(); - return createSemTypeInner(cx, ld, SemType::tryInto, mut()); + return createSemTypeInner(env, ld, SemType::tryInto, mut()); } private CellAtomicType.CellMutability mut() { return isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; } - private SemType createSemTypeInner(Context cx, ListDefinition ld, - BiFunction semTypeFunction, + private SemType createSemTypeInner(Env env, ListDefinition ld, Function semTypeFunction, CellAtomicType.CellMutability mut) { - Env env = cx.env; SemType[] memberTypes = new SemType[tupleTypes.size()]; for (int i = 0; i < tupleTypes.size(); i++) { - SemType memberType = semTypeFunction.apply(cx, tupleTypes.get(i)); + SemType memberType = semTypeFunction.apply(tupleTypes.get(i)); if (Core.isNever(memberType)) { return getNeverType(); } memberTypes[i] = memberType; } - SemType rest = restType != null ? semTypeFunction.apply(cx, restType) : getNeverType(); + SemType rest = restType != null ? semTypeFunction.apply(restType) : getNeverType(); return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } @@ -379,7 +377,7 @@ protected boolean isDependentlyTypedInner(Set visited) { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } AbstractArrayValue value = (AbstractArrayValue) object; SemType cachedShape = value.shapeOf(); @@ -412,8 +410,7 @@ public Optional acceptedTypeOf(Context cx) { return Optional.of(acceptedTypeDefn.getSemType(env)); } ListDefinition ld = result.definition(); - return Optional.of(createSemTypeInner(cx, ld, - (context, type) -> ShapeAnalyzer.acceptedTypeOf(context, type).orElseThrow(), + return Optional.of(createSemTypeInner(env, ld, (type) -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow(), CELL_MUT_UNLIMITED)); } 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 86d7bc34f729..1fdd10c91847 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 @@ -248,28 +248,23 @@ public Type getCachedImpliedType() { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { throw new IllegalStateException("Child that are used for type checking must implement this method"); } @Override - public void updateInnerSemTypeIfNeeded(Context cx) { - if (cachedSemType == null) { - try { - cx.enterTypeResolutionPhase(this); - cachedSemType = createSemType(cx); + public void updateInnerSemTypeIfNeeded() { + synchronized (this) { + if (cachedSemType == null) { + cachedSemType = createSemType(); setAll(cachedSemType.all()); setSome(cachedSemType.some(), cachedSemType.subTypeData()); - cx.exitTypeResolutionPhase(); - } catch (InterruptedException e) { - cx.exitTypeResolutionPhaseAbruptly(e); - throw new RuntimeException(e); } } } - protected SemType getSemType(Context cx) { - updateInnerSemTypeIfNeeded(cx); + protected SemType getSemType() { + updateInnerSemTypeIfNeeded(); return cachedSemType; } @@ -307,7 +302,7 @@ public final Optional cachedTypeCheckResult(Context cx, CacheableTypeDe } } - private void initializeCacheIfNeeded(Context cx) { + private synchronized void initializeCacheIfNeeded(Context cx) { typeCacheLock.readLock().lock(); boolean shouldInitialize = typeCheckCache == null; typeCacheLock.readLock().unlock(); 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..2e6384699111 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 @@ -132,9 +132,9 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { Type referredType = getReferredType(); - return tryInto(cx, referredType); + return tryInto(referredType); } @Override @@ -145,7 +145,7 @@ protected boolean isDependentlyTypedInner(Set visited) { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } Type referredType = getReferredType(); if (referredType instanceof TypeWithShape typeWithShape) { 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..da696b1bf335 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,6 +27,7 @@ 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.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.TypedescUtils; import io.ballerina.runtime.internal.values.TypedescValue; import io.ballerina.runtime.internal.values.TypedescValueImpl; @@ -94,11 +95,12 @@ public String toString() { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { if (constraint == null) { return Builder.getTypeDescType(); } - SemType constraint = tryInto(cx, getConstraint()); + SemType constraint = tryInto(getConstraint()); + Context cx = TypeChecker.context(); return TypedescUtils.typedescContaining(cx.env, constraint); } 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..bdd97fe67536 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 @@ -553,8 +553,8 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType(Context cx) { - return memberTypes.stream().map(type -> SemType.tryInto(cx, type)).reduce(Builder.getNeverType(), Core::union); + public SemType createSemType() { + return memberTypes.stream().map(SemType::tryInto).reduce(Builder.getNeverType(), Core::union); } @Override 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..45dd60c0ad10 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 @@ -156,16 +156,16 @@ public Optional getIntersectionType() { } @Override - public SemType createSemType(Context cx) { + public SemType createSemType() { SemType semType; if (constraint == null) { semType = pickTopType(); } else { SemType contraintSemtype; if (constraint instanceof ParameterizedType parameterizedType) { - contraintSemtype = tryInto(cx, parameterizedType.getParamValueType()); + contraintSemtype = tryInto(parameterizedType.getParamValueType()); } else { - contraintSemtype = tryInto(cx, constraint); + contraintSemtype = tryInto(constraint); } semType = XmlUtils.xmlSequence(contraintSemtype); } @@ -192,7 +192,7 @@ public void setIntersectionType(IntersectionType intersectionType) { public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { XmlValue xmlValue = (XmlValue) object; if (!isReadOnly(xmlValue)) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } return readonlyShapeOf(object); } @@ -209,7 +209,7 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje @Override public Optional acceptedTypeOf(Context cx) { - return Optional.of(getSemType(cx)); + return Optional.of(getSemType()); } private Optional readonlyShapeOf(Object object) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java index 66e443419554..2b4373a2de5e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java @@ -4,43 +4,63 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; +import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Supplier; /** - * Container used to maintain concurrent safety when creating {@code Definitions}. + * Container used to maintain concurrency invariants when creating a potentially recursive semtype. + * + * It maintains fallowing invariants. + * 1. When the type is being defined only the thread that is defining the type may proceed + * 2. After definition is completed any number of threads may proceed concurrently In order to achieve this container + * has three phases (init, defining, defined). At init phase (that is no definition has been set) any number of threads + * may proceed concurrently. When a thread sets a definition {@code setDefinition} container enters the defining phase. + * In that phase only that thread may continue in {@code getSemType} method (this is to allow for recursive type + * definitions). Container registers with the {@code Definition} using {@code registerContainer} method. When the + * {@code Definition} has been defined (ie. {@code Env} has an atom corresponding to the definition) it must notify the + * container using {@code definitionUpdated} method. At this point container moves to defined phase allowing concurrent + * access to {@code getSemType}. * * @param type of the definition * @since 2201.11.0 */ public class DefinitionContainer { + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); private volatile E definition; - private final ReentrantLock lock = new ReentrantLock(); + private final ReentrantLock recTypeLock = new ReentrantLock(); + private volatile boolean isDefining = false; public boolean isDefinitionReady() { try { - lock.lock(); + rwLock.readLock().lock(); return definition != null; } finally { - lock.unlock(); + rwLock.readLock().unlock(); } } - /** - * Get the semtype of the definition. Must call {@code isDefinitionReady} before calling this method. - * - * @param env {@code Env} in which type is defined at - * @return recursive semtype representing the type - */ public SemType getSemType(Env env) { - return definition.getSemType(env); + try { + rwLock.readLock().lock(); + // We don't need this check to be synchronized since {@code trySetDefinition} will hold the write lock until + // it completes, So isDefining should always be at a consistent state + if (isDefining) { + // This should prevent threads other than the defining thread to access the rec atom. + recTypeLock.lock(); + } + return definition.getSemType(env); + } finally { + rwLock.readLock().unlock(); + } } public DefinitionUpdateResult trySetDefinition(Supplier supplier) { try { - lock.lock(); + rwLock.writeLock().lock(); boolean updated; E newDefinition; if (this.definition != null) { @@ -49,23 +69,33 @@ public DefinitionUpdateResult trySetDefinition(Supplier supplier) { } else { updated = true; newDefinition = supplier.get(); + newDefinition.registerContainer(this); + this.recTypeLock.lock(); + isDefining = true; this.definition = newDefinition; } return new DefinitionUpdateResult<>(newDefinition, updated); } finally { - lock.unlock(); + rwLock.writeLock().unlock(); } } public void clear() { try { - lock.lock(); + rwLock.writeLock().lock(); + // This shouldn't happen because defining thread should hold the lock. + assert !isDefining; this.definition = null; } finally { - lock.unlock(); + rwLock.writeLock().unlock(); } } + public void definitionUpdated() { + recTypeLock.unlock(); + isDefining = false; + } + /** * Result of trying to update the definition. * diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java index 25215726ffe9..4878cbe1f717 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java @@ -27,9 +27,6 @@ import io.ballerina.runtime.api.types.semtype.RecAtom; import io.ballerina.runtime.api.types.semtype.SemType; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - /** * {@code Definition} used to create function subtypes. * @@ -39,21 +36,15 @@ public class FunctionDefinition extends Definition { private volatile RecAtom rec; private volatile SemType semType; - private final Lock lock = new ReentrantLock(); @Override public SemType getSemType(Env env) { - try { - this.lock.lock(); - if (this.semType != null) { - return this.semType; - } else { - RecAtom rec = env.recFunctionAtom(); - this.rec = rec; - return this.createSemType(rec); - } - } finally { - this.lock.unlock(); + if (this.semType != null) { + return this.semType; + } else { + RecAtom rec = env.recFunctionAtom(); + this.rec = rec; + return this.createSemType(rec); } } @@ -66,20 +57,17 @@ private SemType createSemType(Atom atom) { public SemType define(Env env, SemType args, SemType ret, FunctionQualifiers qualifiers) { FunctionAtomicType atomicType = new FunctionAtomicType(args, ret, qualifiers.toSemType(env)); - try { - lock.lock(); - RecAtom rec = this.rec; - Atom atom; - if (rec != null) { - atom = rec; - env.setRecFunctionAtomType(rec, atomicType); - } else { - atom = env.functionAtom(atomicType); - } - return this.createSemType(atom); - } finally { - lock.unlock(); + RecAtom rec = this.rec; + Atom atom; + if (rec != null) { + atom = rec; + env.setRecFunctionAtomType(rec, atomicType); + } else { + atom = env.functionAtom(atomicType); } + SemType semType = this.createSemType(atom); + notifyContainer(); + return semType; } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java index 43030d1383a2..58bd4674453e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java @@ -27,9 +27,6 @@ import io.ballerina.runtime.api.types.semtype.RecAtom; import io.ballerina.runtime.api.types.semtype.SemType; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; import static io.ballerina.runtime.api.types.semtype.Builder.getUndefType; @@ -46,23 +43,16 @@ public class ListDefinition extends Definition { private volatile RecAtom rec = null; private volatile SemType semType = null; - private final Lock lock = new ReentrantLock(); @Override public SemType getSemType(Env env) { - try { - this.lock.lock(); - SemType s = this.semType; - if (s == null) { - assert rec == null; - RecAtom rec = env.recListAtom(); - this.rec = rec; - return this.createSemType(env, rec); - } - return s; - } finally { - this.lock.unlock(); + SemType s = this.semType; + if (s == null) { + RecAtom rec = env.recListAtom(); + this.rec = rec; + return this.createSemType(env, rec); } + return s; } public SemType defineListTypeWrapped(Env env, SemType[] initial, int fixedLength, SemType rest, @@ -73,26 +63,23 @@ public SemType defineListTypeWrapped(Env env, SemType[] initial, int fixedLength } SemType restCell = Builder.getCellContaining(env, union(rest, getUndefType()), isNever(rest) ? CELL_MUT_NONE : mut); - return define(env, initialCells, fixedLength, restCell); + SemType semType = define(env, initialCells, fixedLength, restCell); + notifyContainer(); + return semType; } private SemType define(Env env, SemType[] initial, int fixedLength, SemType rest) { FixedLengthArray members = FixedLengthArray.normalized(initial, fixedLength); ListAtomicType atomicType = new ListAtomicType(members, rest); Atom atom; - try { - lock.lock(); - RecAtom rec = this.rec; - if (rec != null) { - atom = rec; - env.setRecListAtomType(rec, atomicType); - } else { - atom = env.listAtom(atomicType); - } - return this.createSemType(env, atom); - } finally { - lock.unlock(); + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecListAtomType(rec, atomicType); + } else { + atom = env.listAtom(atomicType); } + return this.createSemType(env, atom); } private SemType createSemType(Env env, Atom atom) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java index 415411845e0e..5e9ae332e55b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java @@ -30,8 +30,6 @@ import java.util.Arrays; import java.util.Comparator; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; @@ -48,21 +46,16 @@ public class MappingDefinition extends Definition { private volatile RecAtom rec = null; private volatile SemType semType = null; - private final Lock lock = new ReentrantLock(); @Override public SemType getSemType(Env env) { - try { - lock.lock(); - if (this.semType != null) { - return this.semType; - } - assert this.rec == null; + SemType s = this.semType; + if (s == null) { RecAtom rec = env.recMappingAtom(); this.rec = rec; return this.createSemType(env, rec); - } finally { - lock.unlock(); + } else { + return s; } } @@ -82,7 +75,9 @@ public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, C } SemType restCell = Builder.getCellContaining(env, union(rest, getUndefType()), isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); - return define(env, cellFields, restCell); + SemType semType = define(env, cellFields, restCell); + notifyContainer(); + return semType; } SemType define(Env env, BCellField[] cellFields, SemType rest) { @@ -91,19 +86,14 @@ SemType define(Env env, BCellField[] cellFields, SemType rest) { sortAndSplitFields(cellFields, names, types); MappingAtomicType atomicType = new MappingAtomicType(names, types, rest); Atom atom; - try { - lock.lock(); - RecAtom rec = this.rec; - if (rec != null) { - atom = rec; - env.setRecMappingAtomType(rec, atomicType); - } else { - atom = env.mappingAtom(atomicType); - } - return this.createSemType(env, atom); - } finally { - lock.unlock(); + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecMappingAtomType(rec, atomicType); + } else { + atom = env.mappingAtom(atomicType); } + return this.createSemType(env, atom); } private void sortAndSplitFields(BCellField[] fields, String[] names, SemType[] types) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java index e72a7c0e16ff..2059cb237766 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java @@ -18,7 +18,6 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.BType; @@ -30,9 +29,9 @@ */ public sealed interface MutableSemType permits BType { - SemType createSemType(Context cx); + SemType createSemType(); void resetSemType(); - void updateInnerSemTypeIfNeeded(Context cx); + void updateInnerSemTypeIfNeeded(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java index 4b04eda1f9b2..f843d04d43b2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java @@ -63,7 +63,9 @@ public SemType define(Env env, ObjectQualifiers qualifiers, List members SemType mappingType = mappingDefinition.define(env, Stream.concat(memberStream, qualifierStream) .map(field -> MappingDefinition.BCellField.from(env, field, mut)) .toArray(MappingDefinition.BCellField[]::new), restMemberType(env, mut, qualifiers.readonly())); - return objectContaining(mappingType); + SemType semType = objectContaining(mappingType); + notifyContainer(); + return semType; } private SemType objectContaining(SemType mappingType) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java index 1f4e9aab0a75..2023672f5848 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java @@ -50,7 +50,9 @@ public SemType define(Env env, SemType valueType, SemType completionType) { } SemType tuple = listDefinition.defineListTypeWrapped(env, new SemType[]{valueType, completionType}, 2, Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - return streamContaining(tuple); + SemType semType = streamContaining(tuple); + notifyContainer(); + return semType; } private SemType streamContaining(SemType tupleType) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java index e87581a4c04e..62ac70673626 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java @@ -118,7 +118,7 @@ public static boolean handleInherentTypeViolatingRecordUpdate( } private static boolean containsNilType(Type type) { - return Core.containsBasicType(SemType.tryInto(TypeChecker.context(), type), Builder.getNilType()); + return Core.containsBasicType(SemType.tryInto(type), Builder.getNilType()); } public static BError createOpNotSupportedError(Type type, String op) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java index cc4bf3b308c9..b610c8a18316 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java @@ -184,9 +184,9 @@ private static Object xmlSequenceHack(Object value, Type targetType) { } Context cx = TypeChecker.context(); List list = new ArrayList<>(); - SemType targetSemType = SemType.tryInto(cx, targetType); + SemType targetSemType = SemType.tryInto(targetType); for (BXml child : xmlSequence.getChildrenList()) { - SemType childType = SemType.tryInto(cx, child.getType()); + SemType childType = SemType.tryInto(child.getType()); boolean isReadonly = Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.getReadonlyType()); if (isReadonly) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java index d19ccb53fab3..6e116666a45b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java @@ -107,6 +107,6 @@ public String toString() { @Override public Optional inherentTypeOf(Context cx) { - return Optional.of(SemType.tryInto(cx, getType())); + return Optional.of(SemType.tryInto(getType())); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java index 460871ea7083..a00800f64b53 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java @@ -127,7 +127,7 @@ public boolean equals(Object o, Set visitedValues) { } @Override - public SemType widenedType(Context cx) { + public SemType widenedType() { return Builder.getRegexType(); }