Reducing memory leaks in runtime-integration-tests (#10793)

Inspired by the revert done in https://github.com/enso-org/enso/pull/10778, started looking into apparent memory leaks in `runtime-integration-tests`, written in Java.

Initial state:
![Screenshot from 2024-08-09 14-36-29](https://github.com/user-attachments/assets/39abd48f-503b-49d8-af97-da051352c70d)

After:
![Screenshot from 2024-08-12 16-33-40](https://github.com/user-attachments/assets/2bf4cc2d-7e0e-4d22-8810-c2e7e5c3b065)

# Important Notes
Some remaining issues:
- [ ] [TCK tests](https://github.com/enso-org/enso/tree/develop/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/tck) appear to have some memory leaks but we are essentially enabling them via simple inheritance
- [ ] [RuntimeManagementTest](https://github.com/enso-org/enso/blob/develop/engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/RuntimeManagementTest.scala) appears to be broken as it doesn't seem to shutdown properly created threads:
![Screenshot from 2024-08-12 16-29-09](https://github.com/user-attachments/assets/d90aca62-0562-4287-88b7-6d4719e5cf50)

Leaving this for now, as it will probably need to be taken care by initial authors of those tests, if possible. Plus this PR leaves tests in a much better state than before.
This commit is contained in:
Hubert Plociniczak 2024-08-13 10:01:31 +02:00 committed by GitHub
parent 31e589e362
commit ff7e31c237
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 362 additions and 204 deletions

View File

@ -105,6 +105,7 @@ trait PackageRepository {
context: CompilerContext context: CompilerContext
): Option[IRModule] ): Option[IRModule]
def shutdown(): Unit
} }
object PackageRepository { object PackageRepository {

View File

@ -17,8 +17,8 @@ import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source; import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.io.IOAccess; import org.graalvm.polyglot.io.IOAccess;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -45,7 +45,7 @@ public class ExecStrictCompilerTest {
assertNotNull("Enso language is supported", ctx.getEngine().getLanguages().get("enso")); assertNotNull("Enso language is supported", ctx.getEngine().getLanguages().get("enso"));
} }
@Before @After
public void cleanMessages() { public void cleanMessages() {
MESSAGES.reset(); MESSAGES.reset();
} }
@ -53,6 +53,7 @@ public class ExecStrictCompilerTest {
@AfterClass @AfterClass
public static void closeEnsoContext() { public static void closeEnsoContext() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -47,13 +47,14 @@ public class ExportedSymbolsTest {
type A_Type type A_Type
"""); """);
ProjectUtils.createProject("Proj", Set.of(mainSrcMod), projDir); ProjectUtils.createProject("Proj", Set.of(mainSrcMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.containsKey("A_Type"), is(true)); assertThat(mainExportedSymbols.containsKey("A_Type"), is(true));
assertThat( assertThat(
mainExportedSymbols.get("A_Type").get(0), instanceOf(BindingsMap.ResolvedType.class)); mainExportedSymbols.get("A_Type").get(0), instanceOf(BindingsMap.ResolvedType.class));
}
} }
@Test @Test
@ -70,11 +71,12 @@ public class ExportedSymbolsTest {
type B_Type type B_Type
"""); """);
ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir); ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(2)); assertThat(mainExportedSymbols.size(), is(2));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type"));
}
} }
@Test @Test
@ -91,11 +93,12 @@ public class ExportedSymbolsTest {
type B_Type type B_Type
"""); """);
ProjectUtils.createProject("Proj", Set.of(mainMod, bMod), projDir); ProjectUtils.createProject("Proj", Set.of(mainMod, bMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(2)); assertThat(mainExportedSymbols.size(), is(2));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type")); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Type", "B_Type"));
}
} }
@Test @Test
@ -111,11 +114,12 @@ public class ExportedSymbolsTest {
export project.A_Module.A_Type as Foo export project.A_Module.A_Type as Foo
"""); """);
ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir); ProjectUtils.createProject("Proj", Set.of(aMod, mainSrcMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Foo")); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Foo"));
}
} }
@Test @Test
@ -133,13 +137,16 @@ public class ExportedSymbolsTest {
import project.Synthetic_Module import project.Synthetic_Module
"""); """);
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var syntheticModExpSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Synthetic_Module"); var syntheticModExpSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Synthetic_Module");
assertThat( assertThat(
"Just a A_Module submodule should be exported", syntheticModExpSymbols.size(), is(1)); "Just a A_Module submodule should be exported", syntheticModExpSymbols.size(), is(1));
assertThat( assertThat(
"Just a A_Module submodule should be exported", syntheticModExpSymbols, hasKey("A_Module")); "Just a A_Module submodule should be exported",
syntheticModExpSymbols,
hasKey("A_Module"));
}
} }
@Test @Test
@ -156,14 +163,16 @@ public class ExportedSymbolsTest {
export project.A_Module.A_Module export project.A_Module.A_Module
"""); """);
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module")); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module"));
assertThat(mainExportedSymbols.get("A_Module").size(), is(1)); assertThat(mainExportedSymbols.get("A_Module").size(), is(1));
assertThat( assertThat(
mainExportedSymbols.get("A_Module").get(0), is(instanceOf(BindingsMap.ResolvedType.class))); mainExportedSymbols.get("A_Module").get(0),
is(instanceOf(BindingsMap.ResolvedType.class)));
}
} }
@Test @Test
@ -179,15 +188,16 @@ public class ExportedSymbolsTest {
export project.A_Module export project.A_Module
"""); """);
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module")); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("A_Module"));
assertThat(mainExportedSymbols.get("A_Module").size(), is(1)); assertThat(mainExportedSymbols.get("A_Module").size(), is(1));
assertThat( assertThat(
mainExportedSymbols.get("A_Module").get(0), mainExportedSymbols.get("A_Module").get(0),
is(instanceOf(BindingsMap.ResolvedModule.class))); is(instanceOf(BindingsMap.ResolvedModule.class)));
}
} }
@Test @Test
@ -205,15 +215,16 @@ public class ExportedSymbolsTest {
export project.Synthetic_Module export project.Synthetic_Module
"""); """);
ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir); ProjectUtils.createProject("Proj", Set.of(aMod, mainMod), projDir);
var ctx = createCtx(projDir); try (var ctx = createCtx(projDir)) {
compile(ctx); compile(ctx);
var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main"); var mainExportedSymbols = getExportedSymbolsFromModule(ctx, "local.Proj.Main");
assertThat(mainExportedSymbols.size(), is(1)); assertThat(mainExportedSymbols.size(), is(1));
assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Synthetic_Module")); assertThat(mainExportedSymbols.keySet(), containsInAnyOrder("Synthetic_Module"));
assertThat(mainExportedSymbols.get("Synthetic_Module").size(), is(1)); assertThat(mainExportedSymbols.get("Synthetic_Module").size(), is(1));
assertThat( assertThat(
mainExportedSymbols.get("Synthetic_Module").get(0), mainExportedSymbols.get("Synthetic_Module").get(0),
is(instanceOf(BindingsMap.ResolvedModule.class))); is(instanceOf(BindingsMap.ResolvedModule.class)));
}
} }
private static Context createCtx(Path projDir) { private static Context createCtx(Path projDir) {

View File

@ -103,6 +103,7 @@ main = 42
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
private final Symbol symbol; private final Symbol symbol;

View File

@ -74,7 +74,7 @@ public class SerdeCompilerTest {
var persisted = f.get(10, TimeUnit.SECONDS); var persisted = f.get(10, TimeUnit.SECONDS);
assertEquals("Fib_Test library has been fully persisted", true, persisted); assertEquals("Fib_Test library has been fully persisted", true, persisted);
} }
old = module.getIr(); old = module.getIr().duplicate(true, true, true, false);
ctx.leave(); ctx.leave();
} }
@ -103,7 +103,7 @@ public class SerdeCompilerTest {
var mainValue = ctx.asValue(main); var mainValue = ctx.asValue(main);
assertEquals(42, mainValue.execute().asInt()); assertEquals(42, mainValue.execute().asInt());
now = module.getIr(); now = module.getIr().duplicate(true, true, true, false);
ctx.leave(); ctx.leave();
} }

View File

@ -28,11 +28,12 @@ public class SerializationManagerTest {
private static final long COMPILE_TIMEOUT_SECONDS = 20; private static final long COMPILE_TIMEOUT_SECONDS = 20;
private final PackageManager<TruffleFile> packageManager; private PackageManager<TruffleFile> packageManager;
private final InterpreterContext interpreterContext; private InterpreterContext interpreterContext;
private final EnsoContext ensoContext; private EnsoContext ensoContext;
public SerializationManagerTest() { @Before
public void setup() {
packageManager = new PackageManager<>(new TruffleFileSystem()); packageManager = new PackageManager<>(new TruffleFileSystem());
interpreterContext = new InterpreterContext(x -> x); interpreterContext = new InterpreterContext(x -> x);
ensoContext = ensoContext =
@ -41,22 +42,20 @@ public class SerializationManagerTest {
.getBindings(LanguageInfo.ID) .getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject(); .asHostObject();
}
@Before
public void setup() {
interpreterContext.ctx().initialize(LanguageInfo.ID); interpreterContext.ctx().initialize(LanguageInfo.ID);
interpreterContext.ctx().enter(); interpreterContext.ctx().enter();
} }
@After @After
public void teardown() { public void teardown() {
interpreterContext.ctx().close(); interpreterContext.close();
ensoContext.shutdown();
ensoContext = null;
} }
private Path getLibraryPath(LibraryName libraryName) { private Path getLibraryPath(LibraryName libraryName) {
return Paths.get( return Paths.get(
interpreterContext.languageHome(), interpreterContext.languageHome().toFile().getAbsolutePath(),
"..", "..",
"lib", "lib",
libraryName.namespace(), libraryName.namespace(),

View File

@ -42,29 +42,29 @@ public class SerializerTest {
var pkgPath = new File(getClass().getClassLoader().getResource(testName).getPath()); var pkgPath = new File(getClass().getClassLoader().getResource(testName).getPath());
var pkg = PackageManager.Default().fromDirectory(pkgPath).get(); var pkg = PackageManager.Default().fromDirectory(pkgPath).get();
var ctx = ensoContextForPackage(testName, pkgPath); try (var ctx = ensoContextForPackage(testName, pkgPath)) {
var ensoContext = var ensoContext =
(EnsoContext) (EnsoContext)
ctx.getBindings(LanguageInfo.ID) ctx.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject(); .asHostObject();
var mainModuleOpt = ensoContext.getModuleForFile(pkg.mainFile()); var mainModuleOpt = ensoContext.getModuleForFile(pkg.mainFile());
assertEquals(mainModuleOpt.isPresent(), true); assertEquals(mainModuleOpt.isPresent(), true);
var compiler = ensoContext.getCompiler(); var compiler = ensoContext.getCompiler();
var module = mainModuleOpt.get().asCompilerModule(); var module = mainModuleOpt.get().asCompilerModule();
ctx.enter(); ctx.enter();
var result = compiler.run(module); var result = compiler.run(module);
assertEquals(result.compiledModules().exists(m -> m == module), true); assertEquals(result.compiledModules().exists(m -> m == module), true);
var useThreadPool = compiler.context().isCreateThreadAllowed(); var useThreadPool = compiler.context().isCreateThreadAllowed();
var future = compiler.context().serializeModule(compiler, module, true, useThreadPool); var future = compiler.context().serializeModule(compiler, module, true, useThreadPool);
var serialized = future.get(5, TimeUnit.SECONDS); var serialized = future.get(5, TimeUnit.SECONDS);
assertEquals(serialized, true); assertEquals(serialized, true);
var deserialized = compiler.context().deserializeModule(compiler, module); var deserialized = compiler.context().deserializeModule(compiler, module);
assertTrue("Deserialized", deserialized); assertTrue("Deserialized", deserialized);
compiler.context().shutdown(true); compiler.context().shutdown(true);
ctx.leave(); ctx.leave();
ctx.close(); }
} }
} }

View File

@ -35,6 +35,7 @@ public class VectorArraySignatureTest {
@AfterClass @AfterClass
public static void closeEnsoParser() throws Exception { public static void closeEnsoParser() throws Exception {
ensoCompiler.close(); ensoCompiler.close();
ensoCompiler = null;
} }
@Test @Test

View File

@ -19,8 +19,7 @@ public class IRDumpTest {
System.setProperty(IRDumper.SYSTEM_PROP, "true"); System.setProperty(IRDumper.SYSTEM_PROP, "true");
try (var ctx = ContextUtils.defaultContextBuilder().out(out).build()) { try (var ctx = ContextUtils.defaultContextBuilder().out(out).build()) {
// Dumping is done in the compiler, so it is enough just to compile the module // Dumping is done in the compiler, so it is enough just to compile the module
var moduleIr = ContextUtils.compileModule(ctx, """
ContextUtils.compileModule(ctx, """
main = 42 main = 42
""", "MyMainModule"); """, "MyMainModule");
assertThat( assertThat(
@ -39,6 +38,7 @@ public class IRDumpTest {
} catch (IOException e) { } catch (IOException e) {
// Ignore. The ir-dumps directory should be deleted eventually. // Ignore. The ir-dumps directory should be deleted eventually.
} }
out.reset();
} }
} }
} }

View File

@ -14,6 +14,7 @@ import org.enso.interpreter.runtime.EnsoContext;
import org.enso.test.utils.ContextUtils; import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source; import org.graalvm.polyglot.Source;
import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -30,6 +31,12 @@ public class ModuleCacheTest {
.build(); .build();
} }
@AfterClass
public static void disposeContext() {
ctx.close();
ctx = null;
}
@Test @Test
public void testCompareList() throws Exception { public void testCompareList() throws Exception {
var ensoCtx = var ensoCtx =

View File

@ -27,6 +27,7 @@ public class PolyglotCallTypeTest {
@AfterClass @AfterClass
public static void closeCtx() { public static void closeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -66,6 +66,7 @@ public class TypeOfNodeTest {
public static void disposeCtx() throws Exception { public static void disposeCtx() throws Exception {
if (ctx != null) { if (ctx != null) {
ctx.close(); ctx.close();
ctx = null;
} }
} }

View File

@ -42,6 +42,7 @@ public class ModuleSourcesTest {
public void cleanup() { public void cleanup() {
f.delete(); f.delete();
this.ctx.close(); this.ctx.close();
this.ctx = null;
} }
@Test @Test

View File

@ -50,6 +50,7 @@ public class ModuleTest {
public void cleanup() { public void cleanup() {
f.delete(); f.delete();
this.ctx.close(); this.ctx.close();
this.ctx = null;
} }
@Test @Test

View File

@ -31,6 +31,7 @@ public class AtomConstructorTest {
@AfterClass @AfterClass
public static void closeContext() { public static void closeContext() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -27,6 +27,7 @@ public class AtomInteropTest {
@After @After
public void disposeCtx() { public void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -12,8 +12,8 @@ import org.enso.common.MethodNames;
import org.enso.test.utils.ContextUtils; import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.PolyglotException;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -28,7 +28,7 @@ public class AutoscopedConstructorTest {
ctx = ContextUtils.createDefaultContext(out); ctx = ContextUtils.createDefaultContext(out);
} }
@Before @After
public void resetOut() { public void resetOut() {
out.reset(); out.reset();
} }
@ -36,6 +36,7 @@ public class AutoscopedConstructorTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -28,6 +28,7 @@ public class BigNumberTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -63,7 +63,9 @@ public class BinaryDispatchTest {
@AfterClass @AfterClass
public static void closeCtx() { public static void closeCtx() {
module = null;
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -89,6 +89,7 @@ public class BinaryOpFloatTest {
@AfterClass @AfterClass
public static void closeContext() { public static void closeContext() {
ctx.close(); ctx.close();
ctx = null;
} }
private final String operation; private final String operation;

View File

@ -108,6 +108,7 @@ public class BinaryOpIntegerTest {
@AfterClass @AfterClass
public static void closeContext() { public static void closeContext() {
ctx.close(); ctx.close();
ctx = null;
} }
private final String operation; private final String operation;

View File

@ -0,0 +1,27 @@
package org.enso.interpreter.test;
import java.io.OutputStream;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public class ContextTest {
protected static Context ctx;
@BeforeClass
public static void prepareCtx() {
ctx =
ContextUtils.defaultContextBuilder()
.out(OutputStream.nullOutputStream())
.err(OutputStream.nullOutputStream())
.build();
}
@AfterClass
public static void disposeCtx() {
ctx.close();
ctx = null;
}
}

View File

@ -10,8 +10,8 @@ import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value; import org.graalvm.polyglot.Value;
import org.hamcrest.MatcherAssert; import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -28,9 +28,10 @@ public class ConversionMethodTests {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Before @After
public void resetOutput() { public void resetOutput() {
out.reset(); out.reset();
} }

View File

@ -79,7 +79,9 @@ public class DebuggingEnsoTest {
@After @After
public void disposeContext() { public void disposeContext() {
context.close(); context.close();
context = null;
engine.close(); engine.close();
engine = null;
} }
private static void expectStackFrame( private static void expectStackFrame(

View File

@ -55,6 +55,7 @@ public class DiagnosticFormatterTest {
@After @After
public void closeCtx() throws IOException { public void closeCtx() throws IOException {
ctx.close(); ctx.close();
ctx = null;
output.close(); output.close();
} }

View File

@ -23,6 +23,7 @@ public class EqualsConversionsTest {
@AfterClass @AfterClass
public static void disposeContext() { public static void disposeContext() {
context.close(); context.close();
context = null;
} }
@Test @Test

View File

@ -61,6 +61,8 @@ public class EqualsTest {
@AfterClass @AfterClass
public static void disposeContext() { public static void disposeContext() {
context.close(); context.close();
context = null;
unwrappedValues = null;
} }
@DataPoints public static Object[] unwrappedValues; @DataPoints public static Object[] unwrappedValues;
@ -90,6 +92,8 @@ public class EqualsTest {
.toArray(new Object[] {}); .toArray(new Object[] {});
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError(e); throw new AssertionError(e);
} finally {
valGenerator.dispose();
} }
} }

View File

@ -29,6 +29,7 @@ public class FindExceptionMessageTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -27,6 +27,7 @@ public class ForeignMethodInvokeTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -53,6 +53,8 @@ public class HashCodeTest {
@AfterClass @AfterClass
public static void disposeContext() { public static void disposeContext() {
context.close(); context.close();
context = null;
unwrappedValues = null;
} }
/** /**

View File

@ -60,7 +60,9 @@ public class InsightForEnsoTest {
@After @After
public void disposeContext() throws Exception { public void disposeContext() throws Exception {
this.insightHandle.close(); this.insightHandle.close();
this.out.reset();
this.ctx.close(); this.ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -40,6 +40,7 @@ public class IntegerTest {
@AfterClass @AfterClass
public static void teardown() { public static void teardown() {
ctx.close(); ctx.close();
ctx = null;
} }
private static final EnsoBigInteger bigInt = private static final EnsoBigInteger bigInt =

View File

@ -12,8 +12,8 @@ import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value; import org.graalvm.polyglot.Value;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -30,9 +30,10 @@ public class JavaInteropTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Before @After
public void resetOutput() { public void resetOutput() {
out.reset(); out.reset();
} }

View File

@ -18,12 +18,13 @@ public class JsInteropTest {
@Before @Before
public void initContext() { public void initContext() {
ctx = ContextUtils.createDefaultContext(out); ctx = ContextUtils.createDefaultContext(out);
out.reset();
} }
@After @After
public void disposeCtx() { public void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
out.reset();
} }
@Test @Test

View File

@ -64,7 +64,15 @@ public class ListTest {
@After @After
public void disposeCtx() { public void disposeCtx() {
generator = null;
plusOne = null;
evenOnes = null;
taken = null;
init = null;
asVector = null;
asText = null;
this.ctx.close(); this.ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -53,7 +53,10 @@ public class MetaIsATest {
generator.dispose(); generator.dispose();
generator = null; generator = null;
} }
isACheck = null;
warningCheck = null;
ctx.close(); ctx.close();
ctx = null;
} }
/** /**

View File

@ -38,6 +38,7 @@ public class NonStrictModeTests {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
nonStrictCtx.close(); nonStrictCtx.close();
nonStrictCtx = null;
} }
@Before @Before

View File

@ -93,7 +93,9 @@ public class PolyglotErrorTest {
@After @After
public void disposeCtx() { public void disposeCtx() {
this.panic = null;
this.ctx.close(); this.ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -55,6 +55,8 @@ public class RootNamesTest {
public void disposeContext() throws Exception { public void disposeContext() throws Exception {
this.insightHandle.close(); this.insightHandle.close();
this.ctx.close(); this.ctx.close();
this.ctx = null;
this.out.reset();
} }
@Test @Test

View File

@ -6,36 +6,16 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.io.OutputStream;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import org.enso.common.MethodNames; import org.enso.common.MethodNames;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source; import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value; import org.graalvm.polyglot.Value;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
public class SignatureTest { public class SignatureTest extends ContextTest {
private static Context ctx;
@BeforeClass
public static void prepareCtx() {
ctx =
ContextUtils.defaultContextBuilder()
.out(OutputStream.nullOutputStream())
.err(OutputStream.nullOutputStream())
.build();
}
@AfterClass
public static void disposeCtx() {
ctx.close();
}
@Test @Test
public void wrongFunctionSignature() throws Exception { public void wrongFunctionSignature() throws Exception {

View File

@ -3,32 +3,12 @@ package org.enso.interpreter.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.OutputStream;
import java.net.URI; import java.net.URI;
import org.enso.common.MethodNames; import org.enso.common.MethodNames;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source; import org.graalvm.polyglot.Source;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
public class SymbolResolutionTest { public class SymbolResolutionTest extends ContextTest {
private static Context ctx;
@BeforeClass
public static void prepareCtx() {
ctx =
ContextUtils.defaultContextBuilder()
.out(OutputStream.nullOutputStream())
.err(OutputStream.nullOutputStream())
.build();
}
@AfterClass
public static void disposeCtx() {
ctx.close();
}
@Test @Test
public void resolvingLocalSymbol() throws Exception { public void resolvingLocalSymbol() throws Exception {

View File

@ -10,8 +10,8 @@ import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Source; import org.graalvm.polyglot.Source;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -34,7 +34,7 @@ public class TypeInferenceConsistencyTest {
.build(); .build();
} }
@Before @After
public void cleanMessages() { public void cleanMessages() {
output.reset(); output.reset();
} }
@ -46,6 +46,7 @@ public class TypeInferenceConsistencyTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -28,6 +28,7 @@ public class TypeMembersTest {
@After @After
public void disposeCtx() { public void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -47,12 +47,14 @@ public class VectorSortTest {
values.addAll(valuesGenerator.booleans()); values.addAll(valuesGenerator.booleans());
values.addAll(valuesGenerator.durations()); values.addAll(valuesGenerator.durations());
values.addAll(valuesGenerator.maps()); values.addAll(valuesGenerator.maps());
valuesGenerator.dispose();
} }
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
values.clear(); values.clear();
context.close(); context.close();
context = null;
} }
@DataPoints public static List<Value> values; @DataPoints public static List<Value> values;

View File

@ -27,6 +27,7 @@ public class VectorTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(); ctx.close();
ctx = null;
} }
@Test @Test

View File

@ -48,8 +48,11 @@ public class WarningsTest {
@AfterClass @AfterClass
public static void disposeContext() { public static void disposeContext() {
ensoContext = null; generator.dispose();
ctx.close(); ctx.close();
ctx = null;
ensoContext.shutdown();
ensoContext = null;
} }
@Test @Test

View File

@ -42,6 +42,7 @@ public class AssertionsTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(true); ctx.close(true);
ctx = null;
} }
@After @After

View File

@ -27,6 +27,7 @@ public class DisabledAssertionsTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(true); ctx.close(true);
ctx = null;
} }
@Test @Test

View File

@ -36,6 +36,7 @@ public class SuccessfulAssertionExpressionTest {
@AfterClass @AfterClass
public static void disposeCtx() { public static void disposeCtx() {
ctx.close(true); ctx.close(true);
ctx = null;
} }
@After @After

View File

@ -44,7 +44,9 @@ public class AvoidIdInstrumentationTagTest {
@After @After
public void disposeContext() { public void disposeContext() {
nodes = null;
context.close(); context.close();
context = null;
} }
@Test @Test

View File

@ -48,6 +48,7 @@ public class FunctionPointerTest {
@After @After
public void disposeContext() { public void disposeContext() {
context.close(); context.close();
context = null;
} }
@Test @Test

View File

@ -68,7 +68,9 @@ public class IncrementalUpdatesTest {
@After @After
public void teardownContext() { public void teardownContext() {
nodeCountingInstrument = null;
context.close(); context.close();
context = null;
} }
@Test @Test

View File

@ -62,7 +62,9 @@ public class WarningInstrumentationTest {
@After @After
public void disposeContext() { public void disposeContext() {
instrument = null;
context.close(); context.close();
context = null;
} }
@Test @Test

View File

@ -16,7 +16,8 @@ import java.util.UUID
class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues { class IRUtilsTest extends AnyWordSpecLike with Matchers with OptionValues {
private val ctx = new InterpreterContext() private val ctx = new InterpreterContext()
private val langCtx = ctx.ctx private val langCtx = ctx
.ctx()
.getBindings(LanguageInfo.ID) .getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]() .asHostObject[EnsoContext]()

View File

@ -71,13 +71,15 @@ trait ModifiedTest
.build() .build()
context.initialize(LanguageInfo.ID) context.initialize(LanguageInfo.ID)
val executionContext = new PolyglotContext(context) val executionContext = new PolyglotContext(context)
InterpreterException.rethrowPolyglot { val result = InterpreterException.rethrowPolyglot {
val topScope = executionContext.getTopScope val topScope = executionContext.getTopScope
val mainModuleScope = topScope.getModule(mainModule.toString) val mainModuleScope = topScope.getModule(mainModule.toString)
val assocCons = mainModuleScope.getAssociatedType val assocCons = mainModuleScope.getAssociatedType
val mainFun = mainModuleScope.getMethod(assocCons, "main").get val mainFun = mainModuleScope.getMethod(assocCons, "main").get
mainFun.execute() mainFun.execute()
} }
context.close()
result
} }
private def initialCopy(from: File, to: File): Unit = { private def initialCopy(from: File, to: File): Unit = {

View File

@ -16,7 +16,8 @@ import java.util.UUID
class SuggestionBuilderTest extends AnyWordSpecLike with Matchers { class SuggestionBuilderTest extends AnyWordSpecLike with Matchers {
private val ctx = new InterpreterContext() private val ctx = new InterpreterContext()
private val langCtx = ctx.ctx private val langCtx = ctx
.ctx()
.getBindings(LanguageInfo.ID) .getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]() .asHostObject[EnsoContext]()

View File

@ -130,7 +130,8 @@ class TypeSignaturesTest
with TypeMatchers { with TypeMatchers {
private val ctx = new InterpreterContext() private val ctx = new InterpreterContext()
private val langCtx = ctx.ctx private val langCtx = ctx
.ctx()
.getBindings(LanguageInfo.ID) .getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]() .asHostObject[EnsoContext]()

View File

@ -15,7 +15,6 @@ import org.enso.polyglot.debugger.{
} }
import org.enso.common.LanguageInfo import org.enso.common.LanguageInfo
import org.enso.common.RuntimeOptions import org.enso.common.RuntimeOptions
import org.enso.polyglot.{Function, PolyglotContext} import org.enso.polyglot.{Function, PolyglotContext}
import org.graalvm.polyglot.{Context, Value} import org.graalvm.polyglot.{Context, Value}
import org.scalatest.Assertions import org.scalatest.Assertions
@ -28,7 +27,7 @@ import java.io.{
PipedOutputStream, PipedOutputStream,
PrintStream PrintStream
} }
import java.nio.file.Paths import java.nio.file.{Path, Paths}
import java.util.UUID import java.util.UUID
import java.util.logging.Level import java.util.logging.Level
@ -104,43 +103,101 @@ class ReplaceableSessionManager extends SessionManager {
class InterpreterContext( class InterpreterContext(
contextModifiers: Context#Builder => Context#Builder = bldr => bldr contextModifiers: Context#Builder => Context#Builder = bldr => bldr
) { ) {
val output = new ByteArrayOutputStream() var _ctx: Context = _
val err = new ByteArrayOutputStream() var _err: ByteArrayOutputStream = _
val inOut = new PipedOutputStream() var _output: ByteArrayOutputStream = new ByteArrayOutputStream()
val inOutPrinter = new PrintStream(inOut, true) var _in: PipedInputStream = _
val in = new PipedInputStream(inOut) var _inOut: PipedOutputStream = _
val sessionManager = new ReplaceableSessionManager var _inOutPrinter: PrintStream = _
var _sessionManager: ReplaceableSessionManager = new ReplaceableSessionManager
var _languageHome: Path = _
val edition = "0.0.0-dev"
def ctx(): Context = {
if (_ctx == null) {
_output = new ByteArrayOutputStream()
_err = new ByteArrayOutputStream()
_inOut = new PipedOutputStream()
_inOutPrinter = new PrintStream(_inOut, true)
_in = new PipedInputStream(_inOut)
val languageHome = Paths _languageHome = Paths
.get("../../test/micro-distribution/component") .get("../../test/micro-distribution/component")
.toFile
.getAbsolutePath
val edition = "0.0.0-dev"
val ctx = contextModifiers( _ctx = contextModifiers(
Context Context
.newBuilder(LanguageInfo.ID) .newBuilder(LanguageInfo.ID)
.allowExperimentalOptions(true) .allowExperimentalOptions(true)
.allowAllAccess(true) .allowAllAccess(true)
.allowCreateThread(false) .allowCreateThread(false)
.out(output) .out(_output)
.err(err) .err(_err)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName()) .option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.option(RuntimeOptions.DISABLE_IR_CACHES, "true") .option(RuntimeOptions.DISABLE_IR_CACHES, "true")
.option(RuntimeOptions.STRICT_ERRORS, "false") .option(RuntimeOptions.STRICT_ERRORS, "false")
.environment("NO_COLOR", "true") .environment("NO_COLOR", "true")
.logHandler(System.err) .logHandler(System.err)
.in(in) .in(_in)
.option(RuntimeOptions.LANGUAGE_HOME_OVERRIDE, languageHome) .option(
.option(RuntimeOptions.EDITION_OVERRIDE, edition) RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
.option("engine.WarnInterpreterOnly", "false") _languageHome.toFile.getAbsolutePath
.serverTransport { (uri, peer) => )
if (uri.toString == DebugServerInfo.URI) { .option(RuntimeOptions.EDITION_OVERRIDE, edition)
new DebuggerSessionManagerEndpoint(sessionManager, peer) .option("engine.WarnInterpreterOnly", "false")
} else null .serverTransport { (uri, peer) =>
} if (uri.toString == DebugServerInfo.URI) {
).build() new DebuggerSessionManagerEndpoint(_sessionManager, peer)
lazy val executionContext = new PolyglotContext(ctx) } else null
}
).build()
}
_ctx
}
def err(): ByteArrayOutputStream = {
assert(_ctx != null)
_err
}
def output(): ByteArrayOutputStream = {
_output
}
def in(): PipedInputStream = {
assert(_ctx != null)
_in
}
def inOut(): PipedOutputStream = {
assert(_ctx != null)
_inOut;
}
def inOutPrinter(): PrintStream = {
assert(_ctx != null)
_inOutPrinter
}
def sessionManager(): ReplaceableSessionManager = {
_sessionManager
}
def languageHome(): Path = {
assert(_ctx != null)
_languageHome
}
def close(): Unit = {
if (_ctx != null) {
_ctx.close()
_ctx = null
}
_output.reset()
if (_err != null) _err.close()
if (_in != null) _in.close()
if (_inOut != null) _inOut.close()
}
lazy val executionContext = new PolyglotContext(ctx())
} }
trait InterpreterRunner { trait InterpreterRunner {
@ -154,7 +211,10 @@ trait InterpreterRunner {
def withLocationsInstrumenter( def withLocationsInstrumenter(
test: LocationsInstrumenter => Unit test: LocationsInstrumenter => Unit
)(implicit interpreterContext: InterpreterContext): Unit = { )(implicit interpreterContext: InterpreterContext): Unit = {
val instrument = interpreterContext.ctx.getEngine.getInstruments val instrument = interpreterContext
.ctx()
.getEngine
.getInstruments
.get(CodeLocationsTestInstrument.INSTRUMENT_ID) .get(CodeLocationsTestInstrument.INSTRUMENT_ID)
.lookup(classOf[CodeLocationsTestInstrument]) .lookup(classOf[CodeLocationsTestInstrument])
val instrumenter = LocationsInstrumenter(instrument) val instrumenter = LocationsInstrumenter(instrument)
@ -166,7 +226,10 @@ trait InterpreterRunner {
def withIdsInstrumenter( def withIdsInstrumenter(
test: IdsInstrumenter => Unit test: IdsInstrumenter => Unit
)(implicit interpreterContext: InterpreterContext): Unit = { )(implicit interpreterContext: InterpreterContext): Unit = {
val instrument = interpreterContext.ctx.getEngine.getInstruments val instrument = interpreterContext
.ctx()
.getEngine
.getInstruments
.get(CodeIdsTestInstrument.INSTRUMENT_ID) .get(CodeIdsTestInstrument.INSTRUMENT_ID)
.lookup(classOf[CodeIdsTestInstrument]) .lookup(classOf[CodeIdsTestInstrument])
val instrumenter = IdsInstrumenter(instrument) val instrumenter = IdsInstrumenter(instrument)
@ -185,7 +248,7 @@ trait InterpreterRunner {
def getMain( def getMain(
code: String code: String
)(implicit interpreterContext: InterpreterContext): MainMethod = { )(implicit interpreterContext: InterpreterContext): MainMethod = {
interpreterContext.output.reset() interpreterContext.output().reset()
val module = InterpreterException.rethrowPolyglot( val module = InterpreterException.rethrowPolyglot(
interpreterContext.executionContext.evalModule(code, "Test") interpreterContext.executionContext.evalModule(code, "Test")
) )
@ -206,52 +269,52 @@ trait InterpreterRunner {
def consumeErr(implicit def consumeErr(implicit
interpreterContext: InterpreterContext interpreterContext: InterpreterContext
): List[String] = { ): List[String] = {
val result = interpreterContext.err.toString val result = interpreterContext.err().toString
interpreterContext.err.reset() interpreterContext.err().reset()
result.linesIterator.toList result.linesIterator.toList
} }
def consumeErrBytes(implicit def consumeErrBytes(implicit
interpreterContext: InterpreterContext interpreterContext: InterpreterContext
): Array[Byte] = { ): Array[Byte] = {
val result = interpreterContext.err.toByteArray val result = interpreterContext.err().toByteArray
interpreterContext.err.reset() interpreterContext.err().reset()
result result
} }
def consumeOut(implicit def consumeOut(implicit
interpreterContext: InterpreterContext interpreterContext: InterpreterContext
): List[String] = { ): List[String] = {
val result = interpreterContext.output.toString val result = interpreterContext.output().toString
interpreterContext.output.reset() interpreterContext.output().reset()
result.linesIterator.toList result.linesIterator.toList
} }
def consumeOutBytes(implicit def consumeOutBytes(implicit
interpreterContext: InterpreterContext interpreterContext: InterpreterContext
): Array[Byte] = { ): Array[Byte] = {
val result = interpreterContext.output.toByteArray val result = interpreterContext.output().toByteArray
interpreterContext.output.reset() interpreterContext.output().reset()
result result
} }
def feedInput( def feedInput(
string: String string: String
)(implicit interpreterContext: InterpreterContext): Unit = { )(implicit interpreterContext: InterpreterContext): Unit = {
interpreterContext.inOutPrinter.println(string) interpreterContext.inOutPrinter().println(string)
} }
def feedBytes( def feedBytes(
input: Array[Byte] input: Array[Byte]
)(implicit interpreterContext: InterpreterContext): Unit = { )(implicit interpreterContext: InterpreterContext): Unit = {
interpreterContext.inOut.write(input) interpreterContext.inOut().write(input)
interpreterContext.inOut.flush() interpreterContext.inOut().flush()
} }
def setSessionManager( def setSessionManager(
manager: SessionManager manager: SessionManager
)(implicit interpreterContext: InterpreterContext): Unit = )(implicit interpreterContext: InterpreterContext): Unit =
interpreterContext.sessionManager.setSessionManager(manager) interpreterContext.sessionManager().setSessionManager(manager)
// For Enso raw text blocks inside scala multiline strings // For Enso raw text blocks inside scala multiline strings
val rawTQ = "\"\"\"" val rawTQ = "\"\"\""

View File

@ -36,7 +36,7 @@ class RuntimeServerTest
val out: ByteArrayOutputStream = new ByteArrayOutputStream() val out: ByteArrayOutputStream = new ByteArrayOutputStream()
val logOut: ByteArrayOutputStream = new ByteArrayOutputStream() val logOut: ByteArrayOutputStream = new ByteArrayOutputStream()
val context = protected val context =
Context Context
.newBuilder(LanguageInfo.ID) .newBuilder(LanguageInfo.ID)
.allowExperimentalOptions(true) .allowExperimentalOptions(true)

View File

@ -15,7 +15,8 @@ class RuntimeManagementTest extends InterpreterTest {
): Unit = { ): Unit = {
"Interrupt threads through Thread#interrupt()" in { "Interrupt threads through Thread#interrupt()" in {
val langCtx = interpreterContext.ctx val langCtx = interpreterContext
.ctx()
.getBindings(LanguageInfo.ID) .getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.asHostObject[EnsoContext]() .asHostObject[EnsoContext]()

View File

@ -290,6 +290,10 @@ public final class EnsoContext {
threadManager.shutdown(); threadManager.shutdown();
resourceManager.shutdown(); resourceManager.shutdown();
compiler.shutdown(shouldWaitForPendingSerializationJobs); compiler.shutdown(shouldWaitForPendingSerializationJobs);
packageRepository.shutdown();
guestJava = null;
topScope = null;
hostClassLoader.close();
} }
private boolean shouldAssertionsBeEnabled() { private boolean shouldAssertionsBeEnabled() {

View File

@ -17,7 +17,7 @@ import org.slf4j.LoggerFactory;
* the classes that are loaded via this class loader are first searched inside those archives. If * the classes that are loaded via this class loader are first searched inside those archives. If
* not found, delegates to parent class loaders. * not found, delegates to parent class loaders.
*/ */
final class HostClassLoader extends URLClassLoader { final class HostClassLoader extends URLClassLoader implements AutoCloseable {
private final Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<>(); private final Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<>();
private static final Logger logger = LoggerFactory.getLogger(HostClassLoader.class); private static final Logger logger = LoggerFactory.getLogger(HostClassLoader.class);
@ -84,4 +84,9 @@ final class HostClassLoader extends URLClassLoader {
return super.findResources(name); return super.findResources(name);
} }
} }
@Override
public void close() {
loadedClasses.clear();
}
} }

View File

@ -121,6 +121,7 @@ final class SerializationPool {
t.join(); t.join();
} }
context.logSerializationManager(Level.FINE, "Serialization manager has been shut down."); context.logSerializationManager(Level.FINE, "Serialization manager has been shut down.");
threads.clear();
} }
} }

View File

@ -610,6 +610,13 @@ private class DefaultPackageRepository(
} }
else Failure(PackageManager.PackageNotFound()) else Failure(PackageManager.PackageNotFound())
} }
override def shutdown(): Unit = {
loadedPackages.clear()
loadedModules.clear()
loadedComponents.clear()
loadedLibraryBindings.clear()
}
} }
private object DefaultPackageRepository { private object DefaultPackageRepository {