Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6738185
Verify context can be GCed after evaluation
JaroslavTulach Oct 29, 2025
d21d2fd
Cache EnsoMultiType instances per EnsoContext only
JaroslavTulach Oct 29, 2025
b9f1a97
Merge remote-tracking branch 'origin/develop' into wip/jtulach/Contex…
JaroslavTulach Oct 29, 2025
f2f638d
Let's make sure EnsoContext is garbage collected after each ContextUt…
JaroslavTulach Oct 29, 2025
c98bfc3
Separating the assertGC into own file
JaroslavTulach Oct 30, 2025
11f1a28
Avoid stack frame reference to ensoContext to allow GC of EnsoContext
JaroslavTulach Oct 30, 2025
7a1a95d
Dump .hprof file on out of memory errors
JaroslavTulach Oct 30, 2025
d8b80fe
Specify path to runtime-integration-tests/target/scala-2.13 directory
JaroslavTulach Oct 30, 2025
df08637
Try harder to process CleanableReference.QUEUE by creating new Context
JaroslavTulach Oct 30, 2025
0cc51c3
Eventually give up on GCing
JaroslavTulach Oct 30, 2025
1a63754
Don't hold EnsoContext via polyCtx
JaroslavTulach Oct 30, 2025
aa91c51
Allow to opt-out of assertGC check for ContextUtils
JaroslavTulach Oct 30, 2025
09277b0
Introducing topScope() getter for simple access to TopScope
JaroslavTulach Oct 30, 2025
7f4b17d
More tests that GC EnsoContext
JaroslavTulach Oct 30, 2025
35014cb
Clean up local variables to allow GC of the EnsoContext
JaroslavTulach Oct 30, 2025
fc3401b
assertGC is on by default when used as @Rule only
JaroslavTulach Oct 31, 2025
be9a9bd
Try harder to GC after five unsuccessful System.gc() calls
JaroslavTulach Oct 31, 2025
1addaa1
Clean Parameters when the test suite is over
JaroslavTulach Oct 31, 2025
e4a4ff2
Clean static state
JaroslavTulach Oct 31, 2025
23dcbef
Cleanup static values @AfterClass
JaroslavTulach Oct 31, 2025
d09e293
Cleanup EnsoMultiValues when no longer used
JaroslavTulach Oct 31, 2025
bff788a
Explicitly enter/leave context when computing @Parameterized.Parameters
JaroslavTulach Oct 31, 2025
235a5fb
Don't try to access context to assert GC in HelloWorldCacheTest
JaroslavTulach Oct 31, 2025
56c1653
Disable assertGC in these @Parametrized tests as parameters are leaki…
JaroslavTulach Oct 31, 2025
e700394
Clean all OtherJvmPool data when closing a channel
JaroslavTulach Oct 31, 2025
daeb088
Manually used ContextUtils don't check assertGC anymore. Removing the…
JaroslavTulach Oct 31, 2025
25314c5
= null no longer needed
JaroslavTulach Oct 31, 2025
31c4c39
Making anyIr per ModuleContext constant again
JaroslavTulach Oct 31, 2025
153dd0d
Even less = null assignments as assertGC is off for these cases now
JaroslavTulach Oct 31, 2025
1b2cef2
assertGC(false) in jvm-interop tests
JaroslavTulach Nov 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3023,7 +3023,9 @@ lazy val `runtime-integration-tests` =
"-Dtck.values=java-host,enso",
"-Dtck.language=enso",
"-Dtck.inlineVerifierInstrument=false",
"-Dpolyglot.engine.AllowExperimentalOptions=true"
"-Dpolyglot.engine.AllowExperimentalOptions=true",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:HeapDumpPath=" + (Compile / packageBin).value.getParentFile
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now there should be .hprof file in the engine/runtime-integration-tests/target directory when any of the tests yields OutOfMemoryException.

),
Test / javaOptions ++= testLogProviderOptions,
Test / moduleDependencies := {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.enso.compiler.pass.PassConfiguration
import org.enso.pkg.Package
import org.enso.pkg.QualifiedName
import org.enso.compiler.data.BindingsMap.ModuleReference
import org.enso.compiler.core.ir.Name

/** A type containing the information about the execution context for a module.
*
Expand All @@ -31,4 +32,14 @@ case class ModuleContext(
def getCharacters(): CharSequence = module.getCharacters()
def moduleReference(): ModuleReference.Concrete =
ModuleReference.Concrete(module)
val anyIr = Name.Qualified(
List(
Name.Literal("Standard", isMethod = false, identifiedLocation = null),
Name.Literal("Base", isMethod = false, identifiedLocation = null),
Name.Literal("Any", isMethod = false, identifiedLocation = null),
Name.Literal("Any", isMethod = false, identifiedLocation = null)
),
identifiedLocation = null
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ case object TypeSignatures extends IRPass {
): Module = {
val scopeMap = moduleContext.bindingsAnalysis()
resolveModule(
moduleContext,
ir,
scopeMap
.resolveQualifiedName(List("Standard", "Base", "Any", "Any"))
Expand Down Expand Up @@ -94,7 +95,11 @@ case object TypeSignatures extends IRPass {
* @param mod the module to resolve signatures in
* @return `mod`, with type signatures resolved
*/
private def resolveModule(mod: Module, canResolveAny: Boolean): Module = {
private def resolveModule(
moduleContext: ModuleContext,
mod: Module,
canResolveAny: Boolean
): Module = {
var lastSignature: Option[Type.Ascription] = None

val newBindings: List[Definition] = mod.bindings.flatMap {
Expand Down Expand Up @@ -155,7 +160,11 @@ case object TypeSignatures extends IRPass {
case None =>
// No explicit type signature *before* the method was provided.
// Reconstruct type signature from inlined types in arguments/return type, if present.
rebuildSignatureFromInlinedTypes(meth.body, canResolveAny)
rebuildSignatureFromInlinedTypes(
moduleContext,
meth.body,
canResolveAny
)
.filter(_.nonEmpty)
.foreach { inferred =>
val typeFun = Type.Function(
Expand Down Expand Up @@ -208,6 +217,7 @@ case object TypeSignatures extends IRPass {
}

private def rebuildSignatureFromInlinedTypes(
moduleContext: ModuleContext,
expr: Expression,
canResolveAny: Boolean
): Option[List[Expression]] = {
Expand All @@ -217,25 +227,37 @@ case object TypeSignatures extends IRPass {
case (defArg: DefinitionArgument.Specified) :: args
if defArg.name().isInstanceOf[Name.Self] =>
val bodyTypeArgs =
rebuildSignatureFromInlinedTypes(lambda.body, canResolveAny)
rebuildSignatureFromInlinedTypes(
moduleContext,
lambda.body,
canResolveAny
)
val argTypes =
args.flatMap(
_.getMetadata(this)
.map(_.signature)
.orElse(if (canResolveAny) Some(anyIr) else None)
.orElse(
if (canResolveAny) Some(moduleContext.anyIr) else None
)
)
if (argTypes.length == args.length)
bodyTypeArgs.map(b => argTypes ::: b)
else
None
case args =>
val bodyTypeArgs =
rebuildSignatureFromInlinedTypes(lambda.body, canResolveAny)
rebuildSignatureFromInlinedTypes(
moduleContext,
lambda.body,
canResolveAny
)
val argTypes =
args.flatMap(
_.getMetadata(this)
.map(_.signature)
.orElse(if (canResolveAny) Some(anyIr) else None)
.orElse(
if (canResolveAny) Some(moduleContext.anyIr) else None
)
)
if (argTypes.length == args.length)
bodyTypeArgs.map(b => argTypes ::: b)
Expand All @@ -252,16 +274,6 @@ case object TypeSignatures extends IRPass {
}
}

val anyIr = Name.Qualified(
List(
Name.Literal("Standard", isMethod = false, identifiedLocation = null),
Name.Literal("Base", isMethod = false, identifiedLocation = null),
Name.Literal("Any", isMethod = false, identifiedLocation = null),
Name.Literal("Any", isMethod = false, identifiedLocation = null)
),
identifiedLocation = null
)

/** Attaches {@link Signature} to each arguments of a function
* with ascribed type for correct resolution by {@link TypesNames}
* pass.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static void generateDocumentation(Path projDir, String projName, String code, Do
ProjectUtils.createProject(projName, code, projDir);
ProjectUtils.generateProjectDocs(
"api",
ContextUtils.newBuilder(),
ContextUtils.newBuilder().assertGC(false),
projDir,
(context) -> {
var enso = context.ensoContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.nio.file.Path;
import java.util.Set;
import org.enso.pkg.QualifiedName;
import org.enso.polyglot.PolyglotContext;
import org.enso.test.utils.ContextUtils;
import org.enso.test.utils.ModuleUtils;
import org.enso.test.utils.ProjectUtils;
Expand Down Expand Up @@ -113,9 +112,8 @@ public void noCycleDetectedWhenExportingSymbolsFromItself_1() throws IOException
var projDir = tempFolder.newFolder().toPath();
ProjectUtils.createProject("Proj", Set.of(mainMod), projDir);
try (var ctx = ContextUtils.newBuilder().withProjectRoot(projDir).build()) {
var polyCtx = new PolyglotContext(ctx.context());
try {
polyCtx.getTopScope().compile(true);
ctx.topScope().compile(true);
} catch (PolyglotException e) {
fail("Compilation error not expected. But got: " + e);
}
Expand All @@ -137,9 +135,8 @@ public void noCycleDetectedWhenExportingSymbolsFromItself_2() throws IOException
var projDir = tempFolder.newFolder().toPath();
ProjectUtils.createProject("Proj", Set.of(mainMod), projDir);
try (var ctx = ContextUtils.newBuilder().withProjectRoot(projDir).build()) {
var polyCtx = new PolyglotContext(ctx.context());
try {
polyCtx.getTopScope().compile(true);
ctx.topScope().compile(true);
} catch (PolyglotException e) {
fail("Compilation error not expected. But got: " + e);
}
Expand All @@ -151,9 +148,8 @@ public void noCycleDetectedWhenExportingSymbolsFromItself_2() throws IOException

private void expectProjectCompilationError(Path projDir, Matcher<String> errMsgMatcher) {
try (var ctx = ContextUtils.newBuilder().withProjectRoot(projDir).build()) {
var polyCtx = new PolyglotContext(ctx.context());
try {
polyCtx.getTopScope().compile(true);
ctx.topScope().compile(true);
fail("Expected compilation error");
} catch (PolyglotException e) {
assertThat(e.getMessage(), containsString("Compilation aborted"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public void loadingHelloWorldTwiceUsesCaching() throws Exception {
public void irCacheCannotBeEnabled_WhenPrivateCheckIsDisabled() {
try (var ctx =
ContextUtils.newBuilder()
.assertGC(false)
.withModifiedContext(
bldr ->
bldr.option(RuntimeOptions.DISABLE_PRIVATE_CHECK, "true")
Expand Down Expand Up @@ -108,16 +109,21 @@ public void runningAfterPrivateCheckWasDisabled_ShouldFail() throws Exception {
var mainMethod = mainMod.getMethod(assocMainModType, "main").get();
var res = mainMethod.execute();
assertThat("Eval with private check disabled is OK", res.asInt(), is(42));
polyCtx = null;
mainMod = null;
assocMainModType = null;
mainMethod = null;
res = null;
}

// Second run with private check ENABLED - should fail to compile
try (var privateCheckEnabledCtx = ctxInProj(projDir, false).build()) {
var polyCtx = new PolyglotContext(privateCheckEnabledCtx.context());
try {
polyCtx.getTopScope().compile(true);
privateCheckEnabledCtx.topScope().compile(true);
fail("Should result in compilation error");
} catch (PolyglotException e) {
assertThat(e.getMessage(), containsString("Cannot import private module"));
e = null;
}
}
}
Expand Down Expand Up @@ -149,6 +155,8 @@ private static String executeOnce(File src) throws Exception {
var code = Source.newBuilder("enso", src).build();
var res = ctx.evalModule(code, "main");
assertTrue("Result of IO.println is Nothing", res.isNull());
res = null;
code = null;
return ctx.getOut()
.lines()
.filter(l -> l.toUpperCase().contains("HELLO"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.enso.interpreter.runtime.data.Type;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Value;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Ignore;
Expand Down Expand Up @@ -46,6 +47,13 @@ public static void initTypeOf() {
""");
}

@AfterClass
public static void releaseReferences() {
typeOf = null;
normalType = null;
singletonType = null;
}

/** {@code allTypes(Text) == [Text, Any]} */
@Test
public void textChain() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

@RunWith(Parameterized.class)
public class TypeOfNodeMultiValueTest {
@ClassRule public static final ContextUtils ctxRule = ContextUtils.createDefault();
@ClassRule
public static final ContextUtils ctxRule = ContextUtils.newBuilder().assertGC(false).build();

private static RootCallTarget testTypesCall;

@Parameterized.Parameter(0)
Expand Down Expand Up @@ -61,6 +63,7 @@ public static void dispose() {

@Parameterized.Parameters
public static Object[][] allPossibleEnsoInterpreterValues() throws Exception {
ctxRule.context().enter();
var g = ValuesGenerator.create(ctxRule);
var typeOf =
ctxRule.evalModule(
Expand All @@ -74,6 +77,7 @@ public static Object[][] allPossibleEnsoInterpreterValues() throws Exception {
for (var polyValue : g.allValues()) {
registerValue(g, typeOf, polyValue, data);
}
ctxRule.context().leave();
return data.toArray(new Object[0][]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

@RunWith(Parameterized.class)
public class TypeOfNodeTest {
@ClassRule public static final ContextUtils ctxRule = ContextUtils.createDefault();
@ClassRule
public static final ContextUtils ctxRule = ContextUtils.newBuilder().assertGC(false).build();

@Parameterized.Parameter(0)
public Object value;
Expand All @@ -30,6 +31,7 @@ public class TypeOfNodeTest {

@Parameterized.Parameters
public static Object[][] allPossibleEnsoInterpreterValues() throws Exception {
ctxRule.context().enter();
var g = ValuesGenerator.create(ctxRule);
var typeOf =
ctxRule.evalModule(
Expand All @@ -51,6 +53,7 @@ public static Object[][] allPossibleEnsoInterpreterValues() throws Exception {
}
data.add(new Object[] {UnresolvedSymbol.build("unknown_name", null), "Function"});
data.add(new Object[] {UnresolvedConstructor.build(null, "Unknown_Name"), "Function"});
ctxRule.context().leave();
return data.toArray(new Object[0][]);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.enso.interpreter.runtime;

import static org.junit.Assert.assertEquals;

import org.enso.test.utils.ContextUtils;
import org.junit.Rule;
import org.junit.Test;

public class ContextGCTest {
@Rule public final ContextUtils ctx = ContextUtils.createDefault();

@Test
public void simpleEval() throws Exception {
var fourtyTwo =
ctx.evalModule(
"""
from Standard.Base import all

main = 6 * 7
""");
assertEquals(42, fourtyTwo.asInt());
}

@Test
public void multiValue() throws Exception {
var arr =
ctx.evalModule(
"""
from Standard.Base import all

type T

Integer.from (_:T) = 42
Text.from (_:T) = "Meaning"

main =
conv t -> Integer&Text = t
v = conv T
[v, v:Text, v:Integer]

""");
assertEquals(42, arr.getArrayElement(2).asInt());
assertEquals("Meaning", arr.getArrayElement(1).asString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ public void runFinalizersAtTheEnd() {
var fn = new FnCallback();

try (var ctx = ContextUtils.createDefault()) {
var ensoContext = ctx.ensoContext();

ensoContext.getResourceManager().register(obj, fn);
ctx.ensoContext().getResourceManager().register(obj, fn);
assertNull("Not invoked yet", fn.args);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.enso.interpreter.runtime.data.Type;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Value;
import org.junit.After;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -15,11 +16,21 @@
public class EnsoMultiValueInteropTest {
@ClassRule public static final ContextUtils ctxRule = ContextUtils.createDefault();

@Parameterized.Parameter(0)
public Object value;
private Object value;

public EnsoMultiValueInteropTest(Object[] data) {
this.value = data;
data[0] = null;
}

@After
public void releaseValue() {
value = null;
}

@Parameterized.Parameters
public static Object[][] allEnsoMultiValuePairs() throws Exception {
ctxRule.context().enter();
var typeOf =
ctxRule.evalModule(
"""
Expand All @@ -36,7 +47,8 @@ public static Object[][] allEnsoMultiValuePairs() throws Exception {
}
}
}
return data.toArray(new Object[0][]);
ctxRule.context().leave();
return data.stream().map(v -> new Object[] {v}).toArray(Object[][]::new);
}

private static void registerValue(
Expand Down
Loading
Loading