From 2325347ebaecd21b270bc5ce8d477280c9d52fd5 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 9 Feb 2026 08:15:35 +0100 Subject: [PATCH 01/17] Enso supports evaluation with arguments --- .../org/enso/interpreter/EnsoLanguage.java | 10 ++- .../interpreter/node/ProgramRootNode.java | 60 ++++++++++----- .../interpreter/node/WithArgsRootNode.java | 76 +++++++++++++++++++ test/Base_Tests/src/Data/Polyglot_Spec.enso | 6 ++ 4 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java diff --git a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java index 594f65b19f46..f19b494d3e7e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java @@ -242,8 +242,14 @@ protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) { */ @Override protected CallTarget parse(ParsingRequest request) { - var root = ProgramRootNode.build(this, request.getSource()); - return root.getCallTarget(); + if (request.getArgumentNames().isEmpty()) { + var root = ProgramRootNode.build(this, request.getSource()); + return root.getCallTarget(); + } else { + var root = + ProgramRootNode.buildWithArgs(this, request.getSource(), request.getArgumentNames()); + return root.getCallTarget(); + } } /** diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java index 41392be091e3..6f6a04e2a6fa 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java @@ -9,6 +9,7 @@ import com.oracle.truffle.api.source.SourceSection; import java.io.File; import java.util.LinkedList; +import java.util.List; import org.enso.interpreter.EnsoLanguage; import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.Module; @@ -26,7 +27,7 @@ * result, this node handles the transformations and re-writes */ @NodeInfo(shortName = "ProgramRoot", description = "The root of an Enso program's execution") -public class ProgramRootNode extends RootNode { +public final class ProgramRootNode extends RootNode { private final Source sourceCode; private @CompilerDirectives.CompilationFinal Module module; @@ -38,12 +39,7 @@ public class ProgramRootNode extends RootNode { @Override @CompilerDirectives.TruffleBoundary public String getName() { - var segs = sourceCode.getName().split("\\."); - if (segs.length == 0) { - return "Unnamed"; - } else { - return segs[0]; - } + return findName(sourceCode); } @Override @@ -55,14 +51,29 @@ public SourceSection getSourceSection() { /** * Constructs the root node. * - * @param language the language instance in which this will execute + * @param language the language instance * @param sourceCode the code to compile and execute * @return a program root node */ - public static ProgramRootNode build(EnsoLanguage language, Source sourceCode) { + public static RootNode build(EnsoLanguage language, Source sourceCode) { return new ProgramRootNode(language, sourceCode); } + /** + * Creates root node with arguments. + * + * @param language the language instance + * @param sourceCode the code to compile and execute + * @param args additional arguments to expose + * @return a root node to use + */ + public static RootNode buildWithArgs( + EnsoLanguage language, Source sourceCode, List args) { + var name = findName(sourceCode); + var argNames = args.stream().skip(1).toList(); + return new WithArgsRootNode(language, argNames, sourceCode, name); + } + /** * Executes the static analysis passes before executing the resultant program. * @@ -74,15 +85,7 @@ public Object execute(VirtualFrame frame) { if (module == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); var ctx = EnsoContext.get(this); - if (sourceCode.getPath() != null) { - var src = ctx.getTruffleFile(new File(sourceCode.getPath())); - var pkg = ctx.getPackageOf(src).orElse(null); - var qualifiedName = findQualifiedNameInPackage(pkg, src, getName()); - module = new Module(qualifiedName, pkg, src); - } else { - var simpleName = QualifiedName.simpleName(getName()); - module = new Module(simpleName, null, sourceCode.getCharacters().toString()); - } + module = createModule(ctx, getName(), sourceCode); ctx.getPackageRepository().registerModuleCreatedInRuntime(module.asCompilerModule()); if (ctx.isStrictErrors()) { module.compileScope(ctx); @@ -92,6 +95,27 @@ public Object execute(VirtualFrame frame) { return module; } + static Module createModule(EnsoContext ctx, String name, Source code) { + if (code.getPath() != null) { + var src = ctx.getTruffleFile(new File(code.getPath())); + var pkg = ctx.getPackageOf(src).orElse(null); + var qualifiedName = findQualifiedNameInPackage(pkg, src, name); + return new Module(qualifiedName, pkg, src); + } else { + var simpleName = QualifiedName.simpleName(name); + return new Module(simpleName, null, code.getCharacters().toString()); + } + } + + private static String findName(Source src) { + var segs = src.getName().split("\\."); + if (segs.length == 0) { + return "Unnamed"; + } else { + return segs[0]; + } + } + private static QualifiedName findQualifiedNameInPackage( Package pkg, TruffleFile src, String srcName) { if (pkg != null) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java new file mode 100644 index 000000000000..925142013744 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java @@ -0,0 +1,76 @@ +package org.enso.interpreter.node; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; +import java.util.List; +import java.util.stream.Collectors; +import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.callable.function.Function; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.data.text.Text; + +final class WithArgsRootNode extends RootNode { + + private final List argNames; + private final Source src; + private final String name; + + public WithArgsRootNode( + TruffleLanguage language, List argNames, Source src, String name) { + super(language); + this.argNames = argNames; + this.src = src; + this.name = name; + } + + @CompilerDirectives.CompilationFinal private Function fn; + @CompilerDirectives.CompilationFinal private Type self; + @CompilerDirectives.CompilationFinal private Text code; + @Child private InvokeFunctionNode invokeNode; + + @Override + public Object execute(VirtualFrame frame) { + org.enso.interpreter.runtime.EnsoContext ctx = EnsoContext.get(this); + if (fn == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + final String lambdaCode = + """ + import Standard.Base.Runtime.Debug + import Standard.Base + + lambda code = + ${args}-> + Debug.eval code + """ + .replace("${args}", argNames.stream().collect(Collectors.joining("-> "))); + com.oracle.truffle.api.source.Source lambda = + Source.newBuilder(src).content(lambdaCode).build(); + org.enso.interpreter.runtime.Module module = ProgramRootNode.createModule(ctx, name, lambda); + org.enso.interpreter.runtime.scope.ModuleScope moduleScope = module.compileScope(ctx); + self = moduleScope.getAssociatedType(); + org.enso.interpreter.runtime.callable.function.Function lambdaFn = + moduleScope.getMethodForType(self, "lambda"); + fn = lambdaFn; + code = Text.create(src.getCharacters().toString()); + invokeNode = InvokeFunctionNode.buildWithArity(argNames.size() + 2); + } + java.lang.Object[] args = new Object[frame.getArguments().length + 1]; + args[0] = self; + args[1] = code; + System.arraycopy(frame.getArguments(), 1, args, 2, args.length - 2); + org.enso.interpreter.runtime.state.State state = ctx.currentState(); + java.lang.Object res = invokeNode.execute(fn, frame, state, args); + return res; + } + + @Override + @CompilerDirectives.TruffleBoundary + public SourceSection getSourceSection() { + return src.createSection(0, src.getLength()); + } +} diff --git a/test/Base_Tests/src/Data/Polyglot_Spec.enso b/test/Base_Tests/src/Data/Polyglot_Spec.enso index 86ba11c7ad32..52e79c062dec 100644 --- a/test/Base_Tests/src/Data/Polyglot_Spec.enso +++ b/test/Base_Tests/src/Data/Polyglot_Spec.enso @@ -39,6 +39,9 @@ add_specs suite_builder = suite_builder.group "Polyglot" group_builder-> group_builder.specify "Execute JavaScript function" pending=pending_js_missing <| js_plus 3 5 . should_equal 8 + group_builder.specify "Execute Enso function" <| + enso_plus 3 5 . should_equal 8 + group_builder.specify "Execute JavaScript with insufficient number of arguments" pending=pending_js_missing <| r = js_plus 3 r.is_nan . should_be_true @@ -67,6 +70,9 @@ foreign js js_meaning = """ foreign js js_plus = """ return (a, b) => a + b; +foreign enso enso_plus a b = """ + a + b + main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder From d0ffc5406f0736de7f92e487838c8ce5bb1e4980 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 9 Feb 2026 12:17:00 +0100 Subject: [PATCH 02/17] Using var --- .../org/enso/interpreter/node/WithArgsRootNode.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java index 925142013744..20c3f9e2a85d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java @@ -35,7 +35,7 @@ public WithArgsRootNode( @Override public Object execute(VirtualFrame frame) { - org.enso.interpreter.runtime.EnsoContext ctx = EnsoContext.get(this); + var ctx = EnsoContext.get(this); if (fn == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); final String lambdaCode = @@ -48,13 +48,11 @@ public Object execute(VirtualFrame frame) { Debug.eval code """ .replace("${args}", argNames.stream().collect(Collectors.joining("-> "))); - com.oracle.truffle.api.source.Source lambda = - Source.newBuilder(src).content(lambdaCode).build(); - org.enso.interpreter.runtime.Module module = ProgramRootNode.createModule(ctx, name, lambda); - org.enso.interpreter.runtime.scope.ModuleScope moduleScope = module.compileScope(ctx); + var lambda = Source.newBuilder(src).content(lambdaCode).build(); + var module = ProgramRootNode.createModule(ctx, name, lambda); + var moduleScope = module.compileScope(ctx); self = moduleScope.getAssociatedType(); - org.enso.interpreter.runtime.callable.function.Function lambdaFn = - moduleScope.getMethodForType(self, "lambda"); + var lambdaFn = moduleScope.getMethodForType(self, "lambda"); fn = lambdaFn; code = Text.create(src.getCharacters().toString()); invokeNode = InvokeFunctionNode.buildWithArity(argNames.size() + 2); From af327e3eb2f646c268b36bbcd8009f14eb2aecff Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 9 Feb 2026 12:50:23 +0100 Subject: [PATCH 03/17] Rename self argument --- .../org/enso/interpreter/node/ProgramRootNode.java | 3 ++- .../org/enso/interpreter/node/WithArgsRootNode.java | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java index 6f6a04e2a6fa..d1e83db347c5 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/ProgramRootNode.java @@ -70,7 +70,8 @@ public static RootNode build(EnsoLanguage language, Source sourceCode) { public static RootNode buildWithArgs( EnsoLanguage language, Source sourceCode, List args) { var name = findName(sourceCode); - var argNames = args.stream().skip(1).toList(); + var counter = new int[] {0}; + var argNames = args.stream().map(n -> n.equals("self") ? "self" + ++counter[0] : n).toList(); return new WithArgsRootNode(language, argNames, sourceCode, name); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java index 20c3f9e2a85d..80626c3b9b52 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java @@ -38,7 +38,7 @@ public Object execute(VirtualFrame frame) { var ctx = EnsoContext.get(this); if (fn == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - final String lambdaCode = + var lambdaCode = """ import Standard.Base.Runtime.Debug import Standard.Base @@ -57,12 +57,13 @@ public Object execute(VirtualFrame frame) { code = Text.create(src.getCharacters().toString()); invokeNode = InvokeFunctionNode.buildWithArity(argNames.size() + 2); } - java.lang.Object[] args = new Object[frame.getArguments().length + 1]; + var realArgs = frame.getArguments(); + var args = new Object[realArgs.length + 2]; args[0] = self; args[1] = code; - System.arraycopy(frame.getArguments(), 1, args, 2, args.length - 2); - org.enso.interpreter.runtime.state.State state = ctx.currentState(); - java.lang.Object res = invokeNode.execute(fn, frame, state, args); + System.arraycopy(realArgs, 0, args, 2, realArgs.length); + var state = ctx.currentState(); + var res = invokeNode.execute(fn, frame, state, args); return res; } From 35fc14def1f50359db2918e9346d4d8d57c8e1d4 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 9 Feb 2026 12:50:37 +0100 Subject: [PATCH 04/17] Insight test written in Enso --- .../test/instrument/InsightInEnsoTest.java | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java new file mode 100644 index 000000000000..faac56e27b8d --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -0,0 +1,194 @@ +package org.enso.interpreter.test.instrument; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.util.Map; +import java.util.function.Function; +import org.enso.common.MethodNames; +import org.enso.test.utils.ContextUtils; +import org.graalvm.polyglot.Language; +import org.graalvm.polyglot.Source; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +public class InsightInEnsoTest { + private static AutoCloseable insightHandle; + + @ClassRule public static final ContextUtils ctxRule = ContextUtils.newBuilder().build(); + + @BeforeClass + public static void initContext() { + var ctx = ctxRule.context(); + var engine = ctx.getEngine(); + Map langs = engine.getLanguages(); + assertNotNull("Enso found: " + langs, langs.get("enso")); + + @SuppressWarnings("unchecked") + var fn = + (Function) + engine.getInstruments().get("insight").lookup(Function.class); + assertNotNull(fn); + + Source insightScript; + try { + insightScript = + Source.newBuilder( + "enso", + """ + insight.on (ctx-> frame-> 0) (1) + """, + "trace.enso") + .build(); + } catch (IOException e) { + throw new AssertionError(e); + } + insightHandle = fn.apply(insightScript); + } + + @After + public void resetOut() { + ctxRule.resetOut(); + } + + @AfterClass + public static void dispose() throws Exception { + insightHandle.close(); + } + + @Test + public void computeFactorial() throws Exception { + var code = + Source.newBuilder( + "enso", + """ + import Standard.Base.Data.Numbers + fac n = + acc n v = if n <= 1 then v else + @Tail_Call acc n-1 n*v + + acc n 1 + """, + "factorial.enso") + .build(); + + var m = ctxRule.eval(code); + var fac = m.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "fac"); + var res = fac.execute(5); + assertEquals(120, res.asInt()); + + var msgs = ctxRule.getOut(); + assertContainsAll("Step one: " + msgs, msgs, "n=5", "v=1", "acc=function"); + assertContainsAll("Step two: " + msgs, msgs, "n=4", "v=5", "acc=function"); + assertContainsAll("3rd step: " + msgs, msgs, "n=3", "v=20", "acc=function"); + assertContainsAll("4th step: " + msgs, msgs, "n=2", "v=60", "acc=function"); + + assertNotEquals( + "Uninitialized variables (seen as JavaScript null) aren't there: " + msgs, + -1, + msgs.indexOf("null")); + } + + @Test + public void instantiateConstructor() throws Exception { + doInstantiateConstructor(false, false); + } + + @Test + public void instantiateAutoscopedConstructor() throws Exception { + doInstantiateConstructor(true, false); + } + + @Test + public void lazyInstantiateConstructor() throws Exception { + doInstantiateConstructor(false, true); + } + + @Test + public void lazyInstantiateAutoscopedConstructor() throws Exception { + doInstantiateConstructor(true, true); + } + + private void doInstantiateConstructor(boolean useAutoscoping, boolean lazy) throws Exception { + var code = + Source.newBuilder( + "enso", + """ + id x = x + init_first_switch_arg x = x + + type Complex + Number re im + + switch f=(init_first_switch_arg id) n:Complex = Complex.Number (f n.im) (f n.re) + switch_lazy f=(init_first_switch_arg id) (~n:Complex) = Complex.Number (f n.im) (f n.re) + + alloc1 a b = Complex.switch n=(Complex.Number a b) + alloc2 a b = Complex.switch n=(..Number a b) + alloc3 a b = Complex.switch_lazy n=(Complex.Number a b) + alloc4 a b = Complex.switch_lazy n=(..Number a b) + """, + "complex.enso") + .build(); + + var m = ctxRule.eval(code); + var alloc1 = m.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "alloc1"); + var alloc2 = m.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "alloc2"); + var alloc3 = m.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "alloc3"); + var alloc4 = m.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "alloc4"); + + var useAlloc = useAutoscoping ? (lazy ? alloc4 : alloc2) : (lazy ? alloc3 : alloc1); + var res = useAlloc.execute(3, 4); + assertEquals("Complex", res.getMetaObject().getMetaSimpleName()); + assertEquals(3, res.getMember("im").asInt()); + assertEquals(4, res.getMember("re").asInt()); + + var msgs = ctxRule.getOut(); + + var firstCons = msgs.indexOf("complex::complex.Complex::Number"); + var secondCons = msgs.lastIndexOf("complex::complex.Complex::Number"); + var switchInitCall = msgs.indexOf("complex::complex::init_first_switch_arg"); + + assertNotEquals(msgs, -1, switchInitCall); + assertNotEquals(msgs, -1, firstCons); + assertNotEquals(msgs, -1, secondCons); + assertTrue( + "First constructor call must be sooner than second:\n" + msgs, firstCons < secondCons); + + if (useAutoscoping || lazy) { + assertTrue( + "Switch call (" + + switchInitCall + + ") first and then both constructors (" + + firstCons + + "):\n" + + msgs, + switchInitCall < firstCons); + } else { + assertTrue("First constructor sooner than switch call:\n" + msgs, firstCons < switchInitCall); + assertTrue( + "Switch call sooner than second constructor:\n" + msgs, switchInitCall < secondCons); + } + } + + private void assertContainsAll(String msg, String text, String... expected) { + NEXT_LINE: + for (var line : text.split("\n")) { + for (var w : expected) { + if (line.indexOf(w) == -1) { + continue NEXT_LINE; + } + } + // found all expected + return; + } + fail(msg); + } +} From 370760b52f1e86b6cc9b7395edcaac96f0afc75f Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 06:00:14 +0100 Subject: [PATCH 05/17] First argument is name of action --- .../enso/interpreter/test/instrument/InsightInEnsoTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index faac56e27b8d..932669ef84b6 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -43,7 +43,7 @@ public static void initContext() { Source.newBuilder( "enso", """ - insight.on (ctx-> frame-> 0) (1) + insight.on "enter" (ctx-> frame-> 0) (1) """, "trace.enso") .build(); @@ -60,7 +60,9 @@ public void resetOut() { @AfterClass public static void dispose() throws Exception { - insightHandle.close(); + if (insightHandle != null) { + insightHandle.close(); + } } @Test From 5fbb729967305edd9b34abc694ece2b3c8e4f731 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 06:14:21 +0100 Subject: [PATCH 06/17] Observe loading of files test --- .../test/instrument/InsightInEnsoTest.java | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index 932669ef84b6..8278fc353498 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.util.Map; import java.util.function.Function; +import java.util.stream.Collectors; import org.enso.common.MethodNames; import org.enso.test.utils.ContextUtils; import org.graalvm.polyglot.Language; @@ -17,6 +18,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Test; public class InsightInEnsoTest { @@ -43,9 +45,10 @@ public static void initContext() { Source.newBuilder( "enso", """ - insight.on "enter" (ctx-> frame-> 0) (1) + insight.on "source" \\ev-> + Standard.Base.IO.println "Loading "+ev.name.to_text """, - "trace.enso") + "trace_sources.enso") .build(); } catch (IOException e) { throw new AssertionError(e); @@ -66,6 +69,37 @@ public static void dispose() throws Exception { } @Test + public void letInsightObserveLoadingOfEnsoFiles() { + var code = + """ + from Standard.Base import all + + value = 6 * 7 + """; + var value = ctxRule.evalModule(code, "Some_File.enso", "value"); + assertEquals(42, value.asInt()); + + var loading = + ctxRule + .getStdOut() + .lines() + .filter(l -> l.startsWith("Loading ")) + .collect(Collectors.joining("\n")); + + assertEquals( + "Loading Some_File.enso", + """ + Loading Some_File.enso + Loading IO.enso + Loading Text.enso + Loading Some_File + Loading Numbers.enso\ + """, + loading); + } + + @Test + @Ignore public void computeFactorial() throws Exception { var code = Source.newBuilder( @@ -99,21 +133,25 @@ public void computeFactorial() throws Exception { } @Test + @Ignore public void instantiateConstructor() throws Exception { doInstantiateConstructor(false, false); } @Test + @Ignore public void instantiateAutoscopedConstructor() throws Exception { doInstantiateConstructor(true, false); } @Test + @Ignore public void lazyInstantiateConstructor() throws Exception { doInstantiateConstructor(false, true); } @Test + @Ignore public void lazyInstantiateAutoscopedConstructor() throws Exception { doInstantiateConstructor(true, true); } From 486285d0aae951d6cc20ecc69d10289c3036e1cb Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 06:22:39 +0100 Subject: [PATCH 07/17] Use locally activated Insight scripts --- .../test/instrument/InsightInEnsoTest.java | 105 +++++++++--------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index 8278fc353498..dca260898eee 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -27,34 +27,7 @@ public class InsightInEnsoTest { @ClassRule public static final ContextUtils ctxRule = ContextUtils.newBuilder().build(); @BeforeClass - public static void initContext() { - var ctx = ctxRule.context(); - var engine = ctx.getEngine(); - Map langs = engine.getLanguages(); - assertNotNull("Enso found: " + langs, langs.get("enso")); - - @SuppressWarnings("unchecked") - var fn = - (Function) - engine.getInstruments().get("insight").lookup(Function.class); - assertNotNull(fn); - - Source insightScript; - try { - insightScript = - Source.newBuilder( - "enso", - """ - insight.on "source" \\ev-> - Standard.Base.IO.println "Loading "+ev.name.to_text - """, - "trace_sources.enso") - .build(); - } catch (IOException e) { - throw new AssertionError(e); - } - insightHandle = fn.apply(insightScript); - } + public static void initContext() {} @After public void resetOut() { @@ -69,33 +42,40 @@ public static void dispose() throws Exception { } @Test - public void letInsightObserveLoadingOfEnsoFiles() { - var code = + public void letInsightObserveLoadingOfEnsoFiles() throws Exception { + var insightCode = """ - from Standard.Base import all - - value = 6 * 7 + insight.on "source" \\ev-> + Standard.Base.IO.println "Loading "+ev.name.to_text """; - var value = ctxRule.evalModule(code, "Some_File.enso", "value"); - assertEquals(42, value.asInt()); - - var loading = - ctxRule - .getStdOut() - .lines() - .filter(l -> l.startsWith("Loading ")) - .collect(Collectors.joining("\n")); - - assertEquals( - "Loading Some_File.enso", - """ - Loading Some_File.enso - Loading IO.enso - Loading Text.enso - Loading Some_File - Loading Numbers.enso\ - """, - loading); + try (var _ = registerInsight(insightCode)) { + var code = + """ + from Standard.Base import all + + value = 6 * 7 + """; + var value = ctxRule.evalModule(code, "Some_File.enso", "value"); + assertEquals(42, value.asInt()); + + var loading = + ctxRule + .getStdOut() + .lines() + .filter(l -> l.startsWith("Loading ")) + .collect(Collectors.joining("\n")); + + assertEquals( + "Loading Some_File.enso", + """ + Loading Some_File.enso + Loading IO.enso + Loading Text.enso + Loading Some_File + Loading Numbers.enso\ + """, + loading); + } } @Test @@ -231,4 +211,23 @@ private void assertContainsAll(String msg, String text, String... expected) { } fail(msg); } + + private static AutoCloseable registerInsight(String insightCode) throws AssertionError { + var ctx = ctxRule.context(); + var engine = ctx.getEngine(); + Map langs = engine.getLanguages(); + assertNotNull("Enso found: " + langs, langs.get("enso")); + @SuppressWarnings("unchecked") + var fn = + (Function) + engine.getInstruments().get("insight").lookup(Function.class); + assertNotNull(fn); + Source insightScript; + try { + insightScript = Source.newBuilder("enso", insightCode, "trace_sources.enso").build(); + } catch (IOException e) { + throw new AssertionError(e); + } + return fn.apply(insightScript); + } } From df4108a146f35a4893ffddb144d4f3723635fda7 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 07:20:35 +0100 Subject: [PATCH 08/17] Snippets need imports support to be any useful --- .../test/instrument/InsightInEnsoTest.java | 50 +++++++++++-------- .../interpreter/node/WithArgsRootNode.java | 22 ++++++-- test/Base_Tests/src/Data/Polyglot_Spec.enso | 10 ++++ 3 files changed, 57 insertions(+), 25 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index dca260898eee..1d1acf34722c 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -14,40 +14,48 @@ import org.enso.test.utils.ContextUtils; import org.graalvm.polyglot.Language; import org.graalvm.polyglot.Source; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; public class InsightInEnsoTest { - private static AutoCloseable insightHandle; + @Rule public final ContextUtils ctxRule = ContextUtils.newBuilder().assertGC(false).build(); - @ClassRule public static final ContextUtils ctxRule = ContextUtils.newBuilder().build(); - - @BeforeClass - public static void initContext() {} - - @After - public void resetOut() { - ctxRule.resetOut(); + @Test + public void letInsightObserveLoadingOfEnsoFilesWithoutImport() throws Exception { + var insightCode = + """ + insight.on "source" \\ev-> + Standard.Base.IO.println "Loading "+ev.name.to_text + """; + assertLoading(insightCode); } - @AfterClass - public static void dispose() throws Exception { - if (insightHandle != null) { - insightHandle.close(); - } + @Test + public void letInsightObserveLoadingOfEnsoFilesWithImport() throws Exception { + var insightCode = + """ + import Standard.Base.IO + + insight.on "source" \\ev-> + IO.println "Loading "+ev.name.to_text + """; + assertLoading(insightCode); } @Test - public void letInsightObserveLoadingOfEnsoFiles() throws Exception { + public void letInsightObserveLoadingOfEnsoFilesWithFromImport() throws Exception { var insightCode = """ + from Standard.Base import IO + insight.on "source" \\ev-> - Standard.Base.IO.println "Loading "+ev.name.to_text + IO.println "Loading "+ev.name.to_text """; + assertLoading(insightCode); + } + + private void assertLoading(String insightCode) throws Exception { try (var _ = registerInsight(insightCode)) { var code = """ @@ -212,7 +220,7 @@ private void assertContainsAll(String msg, String text, String... expected) { fail(msg); } - private static AutoCloseable registerInsight(String insightCode) throws AssertionError { + private AutoCloseable registerInsight(String insightCode) throws AssertionError { var ctx = ctxRule.context(); var engine = ctx.getEngine(); Map langs = engine.getLanguages(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java index 80626c3b9b52..2ece394d31af 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/WithArgsRootNode.java @@ -7,6 +7,7 @@ import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import java.util.List; +import java.util.function.Predicate; import java.util.stream.Collectors; import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode; import org.enso.interpreter.runtime.EnsoContext; @@ -38,23 +39,36 @@ public Object execute(VirtualFrame frame) { var ctx = EnsoContext.get(this); if (fn == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); + var originalSrc = src.getCharacters().toString(); + + Predicate isImportStatement = + (line) -> line.startsWith("import ") || line.startsWith("from ") || line.trim().isEmpty(); + + var imports = + originalSrc.lines().takeWhile(isImportStatement).collect(Collectors.joining("\n")); + + var executableCode = + originalSrc.lines().dropWhile(isImportStatement).collect(Collectors.joining("\n")); + + var lambdaArgs = argNames.stream().collect(Collectors.joining("-> ")); var lambdaCode = """ - import Standard.Base.Runtime.Debug import Standard.Base + ${imports} lambda code = ${args}-> - Debug.eval code + Standard.Base.Runtime.Debug.eval code """ - .replace("${args}", argNames.stream().collect(Collectors.joining("-> "))); + .replace("${imports}", imports) + .replace("${args}", lambdaArgs); var lambda = Source.newBuilder(src).content(lambdaCode).build(); var module = ProgramRootNode.createModule(ctx, name, lambda); var moduleScope = module.compileScope(ctx); self = moduleScope.getAssociatedType(); var lambdaFn = moduleScope.getMethodForType(self, "lambda"); fn = lambdaFn; - code = Text.create(src.getCharacters().toString()); + code = Text.create(executableCode); invokeNode = InvokeFunctionNode.buildWithArity(argNames.size() + 2); } var realArgs = frame.getArguments(); diff --git a/test/Base_Tests/src/Data/Polyglot_Spec.enso b/test/Base_Tests/src/Data/Polyglot_Spec.enso index 52e79c062dec..909dcfb0fcad 100644 --- a/test/Base_Tests/src/Data/Polyglot_Spec.enso +++ b/test/Base_Tests/src/Data/Polyglot_Spec.enso @@ -42,6 +42,9 @@ add_specs suite_builder = suite_builder.group "Polyglot" group_builder-> group_builder.specify "Execute Enso function" <| enso_plus 3 5 . should_equal 8 + group_builder.specify "Execute Enso function that has import statement" <| + enso_plus_with_import 6 7 . should_equal 13 + group_builder.specify "Execute JavaScript with insufficient number of arguments" pending=pending_js_missing <| r = js_plus 3 r.is_nan . should_be_true @@ -73,6 +76,13 @@ foreign js js_plus = """ foreign enso enso_plus a b = """ a + b +foreign enso enso_plus_with_import a b = """ + import Standard.Base.Runtime.Ref.Ref + + ref = Ref.new a + ref.modify (+ b) + ref.get + main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder From c43d4e78b86767d71fc477fa667d2e1d1b8a82c6 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 13:47:06 +0100 Subject: [PATCH 09/17] InlineContext has to be able to provide own (interactive) Source to error reporter --- .../compiler/context/CompilerContext.java | 3 +- .../scala/org/enso/compiler/Compiler.scala | 19 +++++++++---- .../enso/compiler/context/InlineContext.scala | 11 ++++++-- .../test/instrument/InsightInEnsoTest.java | 28 ++++++++++++++++++- .../org/enso/interpreter/EnsoLanguage.java | 2 +- .../node/expression/debug/EvalNode.java | 4 +-- .../runtime/TruffleCompilerContext.java | 14 ++++++---- .../src/Data/Array_Polyglot_Spec.enso | 5 ++++ 8 files changed, 67 insertions(+), 19 deletions(-) diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/context/CompilerContext.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/context/CompilerContext.java index fc34f9c822f2..d258f2ede771 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/context/CompilerContext.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/context/CompilerContext.java @@ -58,10 +58,11 @@ public interface CompilerContext { * @param diagnostic an IR node representing diagnostic information * @param isOutputRedirected true if the output is not system's out. If true, no ANSI color escape * characters will be inside the returned string. + * @param source a special source for the module * @return exception with a message to display or to throw */ RuntimeException formatDiagnostic( - Module module, Diagnostic diagnostic, boolean isOutputRedirected); + Module module, Diagnostic diagnostic, boolean isOutputRedirected, Object source); // threads boolean isCreateThreadAllowed(); diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala index 57870a2ec6aa..2db91d45f90f 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Compiler.scala @@ -993,7 +993,7 @@ class Compiler( ) .diagnostics val module = inlineContext.getModule() - val hasErrors = reportDiagnostics(errors, module) + val hasErrors = reportDiagnostics(errors, module, inlineContext.src) hasErrors match { case error :: _ if inlineContext.compilerConfig.isStrictErrors => throw error @@ -1016,7 +1016,7 @@ class Compiler( List((module, errors)) } - val hasErrors = reportDiagnostics(diagnostics) + val hasErrors = reportDiagnostics(diagnostics, null) if (hasErrors.nonEmpty && config.isStrictErrors) { val count = diagnostics.map(_._2.collect { case e: Error => e }.length).sum @@ -1108,11 +1108,12 @@ class Compiler( * @return whether any errors were encountered. */ private def reportDiagnostics( - diagnostics: List[(Module, List[Diagnostic])] + diagnostics: List[(Module, List[Diagnostic])], + src: Object ): List[RuntimeException] = { diagnostics.flatMap { diags => if (diags._2.nonEmpty) { - reportDiagnostics(diags._2, diags._1) + reportDiagnostics(diags._2, diags._1, src) } else { List() } @@ -1128,13 +1129,19 @@ class Compiler( */ private def reportDiagnostics( diagnostics: List[Diagnostic], - compilerModule: CompilerContext.Module + compilerModule: CompilerContext.Module, + src: Object ): List[RuntimeException] = { val isOutputRedirected = config.outputRedirect.isDefined val exceptions = diagnostics .flatMap { diag => val formattedDiag = - context.formatDiagnostic(compilerModule, diag, isOutputRedirected) + context.formatDiagnostic( + compilerModule, + diag, + isOutputRedirected, + src + ) printDiagnostic(formattedDiag.getMessage) if (diag.isInstanceOf[Error] || config.treatWarningsAsErrors) { Some(formattedDiag) diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/InlineContext.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/InlineContext.scala index 2ae1db75f164..1785d8c33571 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/InlineContext.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/context/InlineContext.scala @@ -10,6 +10,7 @@ import org.enso.compiler.pass.PassConfiguration * * @param moduleContext the module in which the expression is being executed * @param compilerConfig the compiler configuration + * @param src inline context may need reference to special source * @param localScope the local scope in which the expression is being executed * @param isInTailPosition whether or not the inline expression occurs in tail * position ([[None]] indicates no information) @@ -20,6 +21,7 @@ import org.enso.compiler.pass.PassConfiguration case class InlineContext( private val moduleContext: ModuleContext, compilerConfig: CompilerConfig, + src: Object = null, localScope: Option[LocalScope] = None, isInTailPosition: Option[Boolean] = None, freshNameSupply: Option[FreshNameSupply] = None, @@ -52,14 +54,16 @@ object InlineContext { module: CompilerContext.Module, isInTailPosition: Option[Boolean], compilerConfig: CompilerConfig, - pkgRepo: Option[PackageRepository] + pkgRepo: Option[PackageRepository], + src: Object ): InlineContext = { InlineContext( localScope = Option(localScope), moduleContext = ModuleContext(module, compilerConfig), isInTailPosition = isInTailPosition, compilerConfig = compilerConfig, - pkgRepo = pkgRepo + pkgRepo = pkgRepo, + src = src ) } @@ -77,7 +81,8 @@ object InlineContext { freshNameSupply = moduleContext.freshNameSupply, passConfiguration = moduleContext.passConfiguration, compilerConfig = moduleContext.compilerConfig, - pkgRepo = moduleContext.pkgRepo + pkgRepo = moduleContext.pkgRepo, + src = null ) } } diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index 1d1acf34722c..57951a273148 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -87,8 +87,34 @@ private void assertLoading(String insightCode) throws Exception { } @Test - @Ignore public void computeFactorial() throws Exception { + var insightCode = + """ + from Standard.Base import True, Dictionary, IO, Polyglot, Meta + + when = Dictionary.empty + . insert "roots" True + . insert "rootNameFilter" ".*fac.*" + + log ctx frame = + IO.println ctx.name+" at "+ctx.source.name+":"+ctx.line.to_text+":" + IO.println (Meta.type_of frame) + # IO.println frame.to_text + # members = Polyglot.get_members frame + IO.println members.to_text + # IO.println members + # members . map \\p-> + # IO.println " "+p+"="+(Polyglot.get_member frame p) + + insight.on "enter" log when + """; + + try (var _ = registerInsight(insightCode)) { + assertComputeFactorial(); + } + } + + private void assertComputeFactorial() throws Exception { var code = Source.newBuilder( "enso", diff --git a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java index f19b494d3e7e..4932cd0864dc 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java @@ -305,7 +305,7 @@ protected ExecutableNode parse(InlineParsingRequest request) throws InlineParsin var inlineContext = new InlineContext( moduleContext, - redirectConfigWithStrictErrors, + redirectConfigWithStrictErrors, null, scala.Some.apply(localScope), scala.Some.apply(false), scala.Option.empty(), diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java index cddd3a142643..f43923fd61ef 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java @@ -68,15 +68,15 @@ RootCallTarget parseExpression(LocalScope scope, ModuleScope moduleScope, String LocalScope localScope = scope == LocalScope.empty() ? LocalScope.createEmpty() : scope.createChild(); var compiler = context.getCompiler(); + var src = Source.newBuilder(LanguageInfo.ID, expression, "").build(); InlineContext inlineContext = InlineContext.fromJava( localScope, moduleScope.getModule().asCompilerModule(), scala.Option.apply(getTailStatus() != TailStatus.NOT_TAIL), context.getCompilerConfig(), - scala.Option.apply(compiler.packageRepository())); + scala.Option.apply(compiler.packageRepository()), src); - var src = Source.newBuilder(LanguageInfo.ID, expression, "").build(); var tuppleOption = compiler.runInline(src.getCharacters(), inlineContext); if (tuppleOption.isEmpty()) { throw new RuntimeException("Invalid code passed to `eval`: " + expression); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java index f5ce0776c125..106f2173c4ab 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java @@ -348,7 +348,7 @@ private static boolean isLocationInsideModule( @Override public CompilationAbortedException formatDiagnostic( - CompilerContext.Module module, Diagnostic diagnostic, boolean isOutputRedirected) { + CompilerContext.Module module, Diagnostic diagnostic, boolean isOutputRedirected, Object src) { DiagnosticFormatter diagnosticFormatter; var m = org.enso.interpreter.runtime.Module.fromCompilerModule(module); if (module != null && diagnostic.location().isDefined()) { @@ -369,10 +369,14 @@ public CompilationAbortedException formatDiagnostic( } } Source fallbackSource; - try { - fallbackSource = m.getSource(); - } catch (IOException ex) { - fallbackSource = Source.newBuilder(LanguageInfo.ID, "", null).build(); + if (src instanceof Source s) { + fallbackSource = s; + } else { + try { + fallbackSource = m.getSource(); + } catch (IOException ex) { + fallbackSource = Source.newBuilder(LanguageInfo.ID, "", null).build(); + } } diagnosticFormatter = DiagnosticFormatter.create( diff --git a/test/Base_Tests/src/Data/Array_Polyglot_Spec.enso b/test/Base_Tests/src/Data/Array_Polyglot_Spec.enso index 0a53d21d29db..5e4ad8296044 100644 --- a/test/Base_Tests/src/Data/Array_Polyglot_Spec.enso +++ b/test/Base_Tests/src/Data/Array_Polyglot_Spec.enso @@ -33,6 +33,11 @@ add_specs suite_builder = names = classes.map (x -> x.name) names.should_equal ["java.lang.Object", "java.lang.Number", "java.lang.Integer"] + group_builder.specify "Type of JavaScript array" <| + js_arr = create_array [1, 2, 3] + js_arr . length . should_equal 3 + Meta.type_of js_arr . should_equal Array + group_builder.specify "Vector.from_array creates a copy" <| js_arr = create_array [1, 2, 3] enso_vector = Vector.from_array js_arr From ba56826b2d2fa6046b85be2f2a491c5b3310c49b Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 13:52:05 +0100 Subject: [PATCH 10/17] Yields expected self to be Array, but got (Error: (Compile_Error.Error 'unknown type_of for [n, acc]')) --- .../test/instrument/InsightInEnsoTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index 57951a273148..321277b6ee67 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -98,13 +98,14 @@ public void computeFactorial() throws Exception { log ctx frame = IO.println ctx.name+" at "+ctx.source.name+":"+ctx.line.to_text+":" - IO.println (Meta.type_of frame) + IO.println "Frame:"+(Meta.type_of frame).to_text # IO.println frame.to_text - # members = Polyglot.get_members frame - IO.println members.to_text + members = Polyglot.get_members frame + IO.println "Members: "+(Meta.type_of members).to_text + # IO.println members.to_text # IO.println members - # members . map \\p-> - # IO.println " "+p+"="+(Polyglot.get_member frame p) + members.map \\p-> + IO.println " "+p+"="+(Polyglot.get_member frame p) insight.on "enter" log when """; From 2a67ec9e85a4d0a2d4e6ff4b1caeab85291e444f Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 13:59:47 +0100 Subject: [PATCH 11/17] javafmt --- .../java/org/enso/interpreter/EnsoLanguage.java | 3 ++- .../node/expression/debug/EvalNode.java | 3 ++- .../runtime/TruffleCompilerContext.java | 17 ++++++++++------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java index 4932cd0864dc..871e35bbc77f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java @@ -305,7 +305,8 @@ protected ExecutableNode parse(InlineParsingRequest request) throws InlineParsin var inlineContext = new InlineContext( moduleContext, - redirectConfigWithStrictErrors, null, + redirectConfigWithStrictErrors, + null, scala.Some.apply(localScope), scala.Some.apply(false), scala.Option.empty(), diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java index f43923fd61ef..7e6895c2dfa7 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/debug/EvalNode.java @@ -75,7 +75,8 @@ RootCallTarget parseExpression(LocalScope scope, ModuleScope moduleScope, String moduleScope.getModule().asCompilerModule(), scala.Option.apply(getTailStatus() != TailStatus.NOT_TAIL), context.getCompilerConfig(), - scala.Option.apply(compiler.packageRepository()), src); + scala.Option.apply(compiler.packageRepository()), + src); var tuppleOption = compiler.runInline(src.getCharacters(), inlineContext); if (tuppleOption.isEmpty()) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java index 106f2173c4ab..7c22750c4979 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/TruffleCompilerContext.java @@ -348,7 +348,10 @@ private static boolean isLocationInsideModule( @Override public CompilationAbortedException formatDiagnostic( - CompilerContext.Module module, Diagnostic diagnostic, boolean isOutputRedirected, Object src) { + CompilerContext.Module module, + Diagnostic diagnostic, + boolean isOutputRedirected, + Object src) { DiagnosticFormatter diagnosticFormatter; var m = org.enso.interpreter.runtime.Module.fromCompilerModule(module); if (module != null && diagnostic.location().isDefined()) { @@ -370,13 +373,13 @@ public CompilationAbortedException formatDiagnostic( } Source fallbackSource; if (src instanceof Source s) { - fallbackSource = s; + fallbackSource = s; } else { - try { - fallbackSource = m.getSource(); - } catch (IOException ex) { - fallbackSource = Source.newBuilder(LanguageInfo.ID, "", null).build(); - } + try { + fallbackSource = m.getSource(); + } catch (IOException ex) { + fallbackSource = Source.newBuilder(LanguageInfo.ID, "", null).build(); + } } diagnosticFormatter = DiagnosticFormatter.create( From 88b27e17b6dc07ff4a7c903af5e8a19e94279815 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 14:00:44 +0100 Subject: [PATCH 12/17] TruffleObject with hasArrayElements is recognized as Array --- .../builtin/meta/TypeOfNodeTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java index 7f531d883713..c66287bd0dc2 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java @@ -3,7 +3,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; import java.util.ArrayList; import org.enso.interpreter.runtime.callable.UnresolvedConstructor; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; @@ -53,6 +57,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"}); + data.add(new Object[] {new PolyArray(), "Array"}); ctxRule.context().leave(); return data.toArray(new Object[0][]); } @@ -99,4 +104,27 @@ class ForeignObject implements TruffleObject {} assertTrue("It is meta object: " + symbolTypeValue, symbolTypeValue.isMetaObject()); assertEquals(expectedTypeName, symbolTypeValue.getMetaSimpleName()); } + + @ExportLibrary(InteropLibrary.class) + static final class PolyArray implements TruffleObject { + @ExportMessage + boolean hasArrayElements() { + return true; + } + + @ExportMessage + Object readArrayElement(long index) throws InvalidArrayIndexException { + throw InvalidArrayIndexException.create(index); + } + + @ExportMessage + long getArraySize() { + return 0L; + } + + @ExportMessage + boolean isArrayElementReadable(long index) { + return false; + } + } } From e11add4a58e04acbc2ade88862bdb6135daa31e1 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 16:52:42 +0100 Subject: [PATCH 13/17] Keep exception stack trace --- .../src/main/java/org/enso/test/utils/ContextUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/java/test-utils/src/main/java/org/enso/test/utils/ContextUtils.java b/lib/java/test-utils/src/main/java/org/enso/test/utils/ContextUtils.java index bdac8b7bd49b..6a463f32a444 100644 --- a/lib/java/test-utils/src/main/java/org/enso/test/utils/ContextUtils.java +++ b/lib/java/test-utils/src/main/java/org/enso/test/utils/ContextUtils.java @@ -546,7 +546,7 @@ public void evaluate() throws Throwable { try { base.evaluate(); } catch (Throwable e) { - throw new RuntimeException(e); + throw raise(RuntimeException.class, e); } return null; }); From 1fed43624d811362b89e646739aa89a19109f21a Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 17:20:48 +0100 Subject: [PATCH 14/17] Trace local variables in Enso --- .../test/instrument/InsightInEnsoTest.java | 84 +++++++++---------- .../runtime/scope/DebugLocalScope.java | 16 ++++ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index 321277b6ee67..81b7c124ed03 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.fail; import java.io.IOException; +import java.util.Arrays; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -88,6 +89,40 @@ private void assertLoading(String insightCode) throws Exception { @Test public void computeFactorial() throws Exception { + try (var _ = registerTraceOfLocalVariables()) { + var code = + Source.newBuilder( + "enso", + """ + import Standard.Base.Data.Numbers + fac n = + acc n v = if n <= 1 then v else + @Tail_Call acc n-1 n*v + + acc n 1 + """, + "factorial.enso") + .build(); + + var m = ctxRule.eval(code); + var fac = m.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "fac"); + var res = fac.execute(5); + assertEquals(120, res.asInt()); + + var msgs = ctxRule.getOut(); + assertContainsAll("Step one: " + msgs, msgs, "n=5", "v=1", "acc=factorial.fac.acc"); + assertContainsAll("Step two: " + msgs, msgs, "n=4", "v=5", "acc=factorial.fac.acc"); + assertContainsAll("3rd step: " + msgs, msgs, "n=3", "v=20", "acc=factorial.fac.acc"); + assertContainsAll("4th step: " + msgs, msgs, "n=2", "v=60", "acc=factorial.fac.acc"); + + assertNotEquals( + "Uninitialized variables (seen as Enso Nothing) are there: " + msgs, + -1, + msgs.indexOf("Nothing")); + } + } + + private AutoCloseable registerTraceOfLocalVariables() throws AssertionError { var insightCode = """ from Standard.Base import True, Dictionary, IO, Polyglot, Meta @@ -98,53 +133,14 @@ public void computeFactorial() throws Exception { log ctx frame = IO.println ctx.name+" at "+ctx.source.name+":"+ctx.line.to_text+":" - IO.println "Frame:"+(Meta.type_of frame).to_text - # IO.println frame.to_text members = Polyglot.get_members frame - IO.println "Members: "+(Meta.type_of members).to_text - # IO.println members.to_text - # IO.println members - members.map \\p-> - IO.println " "+p+"="+(Polyglot.get_member frame p) + line = members.fold "" \\sb -> \\p-> + sb + " " + p + "=" + (Polyglot.get_member frame p).to_text + IO.println line insight.on "enter" log when """; - - try (var _ = registerInsight(insightCode)) { - assertComputeFactorial(); - } - } - - private void assertComputeFactorial() throws Exception { - var code = - Source.newBuilder( - "enso", - """ - import Standard.Base.Data.Numbers - fac n = - acc n v = if n <= 1 then v else - @Tail_Call acc n-1 n*v - - acc n 1 - """, - "factorial.enso") - .build(); - - var m = ctxRule.eval(code); - var fac = m.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "fac"); - var res = fac.execute(5); - assertEquals(120, res.asInt()); - - var msgs = ctxRule.getOut(); - assertContainsAll("Step one: " + msgs, msgs, "n=5", "v=1", "acc=function"); - assertContainsAll("Step two: " + msgs, msgs, "n=4", "v=5", "acc=function"); - assertContainsAll("3rd step: " + msgs, msgs, "n=3", "v=20", "acc=function"); - assertContainsAll("4th step: " + msgs, msgs, "n=2", "v=60", "acc=function"); - - assertNotEquals( - "Uninitialized variables (seen as JavaScript null) aren't there: " + msgs, - -1, - msgs.indexOf("null")); + return registerInsight(insightCode); } @Test @@ -244,7 +240,7 @@ private void assertContainsAll(String msg, String text, String... expected) { // found all expected return; } - fail(msg); + fail(msg + " expecting " + Arrays.asList(expected)); } private AutoCloseable registerInsight(String insightCode) throws AssertionError { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java index b2b5bb9fb00a..ee42c432566a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/DebugLocalScope.java @@ -1,6 +1,7 @@ package org.enso.interpreter.runtime.scope; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnknownIdentifierException; @@ -8,6 +9,7 @@ import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.SourceSection; import java.util.ArrayList; import java.util.Comparator; @@ -21,9 +23,11 @@ import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.EnsoObject; +import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.atom.Atom; import org.enso.interpreter.runtime.data.atom.DebugAtomWrapper; import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; /** * This class serves as a basic support for debugging with Chrome inspector. Currently, only @@ -287,6 +291,7 @@ private MaterializedFrame getProperFrame(MaterializedFrame frame, FramePointer p /** Simple interop wrapper for a list of strings. */ @ExportLibrary(InteropLibrary.class) + @ExportLibrary(TypesLibrary.class) static final class ScopeMembers extends EnsoObject { private final List memberNames; @@ -325,5 +330,16 @@ public String toString() { public Object toDisplayString(boolean allowSideEffects) { return toString(); } + + @ExportMessage + Type getType(@Bind Node node) { + var ctx = EnsoContext.get(node); + return ctx.getBuiltins().vector(); + } + + @ExportMessage + boolean hasType() { + return true; + } } } From 5189b84ef30d166298c144f28172202be9b3f191 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 18 Feb 2026 17:39:04 +0100 Subject: [PATCH 15/17] All tests pass now --- .../test/instrument/InsightInEnsoTest.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java index 81b7c124ed03..1195f1b4436e 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/instrument/InsightInEnsoTest.java @@ -15,7 +15,6 @@ import org.enso.test.utils.ContextUtils; import org.graalvm.polyglot.Language; import org.graalvm.polyglot.Source; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -89,7 +88,7 @@ private void assertLoading(String insightCode) throws Exception { @Test public void computeFactorial() throws Exception { - try (var _ = registerTraceOfLocalVariables()) { + try (var _ = registerTraceOfLocalVariables("fac")) { var code = Source.newBuilder( "enso", @@ -122,14 +121,14 @@ public void computeFactorial() throws Exception { } } - private AutoCloseable registerTraceOfLocalVariables() throws AssertionError { + private AutoCloseable registerTraceOfLocalVariables(String method) throws AssertionError { var insightCode = """ from Standard.Base import True, Dictionary, IO, Polyglot, Meta when = Dictionary.empty . insert "roots" True - . insert "rootNameFilter" ".*fac.*" + . insert "rootNameFilter" ".*${method}.*" log ctx frame = IO.println ctx.name+" at "+ctx.source.name+":"+ctx.line.to_text+":" @@ -139,32 +138,37 @@ private AutoCloseable registerTraceOfLocalVariables() throws AssertionError { IO.println line insight.on "enter" log when - """; + """ + .replace("${method}", method); return registerInsight(insightCode); } @Test - @Ignore public void instantiateConstructor() throws Exception { - doInstantiateConstructor(false, false); + try (var _ = registerTraceOfLocalVariables("omplex")) { + doInstantiateConstructor(false, false); + } } @Test - @Ignore public void instantiateAutoscopedConstructor() throws Exception { - doInstantiateConstructor(true, false); + try (var _ = registerTraceOfLocalVariables("omplex")) { + doInstantiateConstructor(true, false); + } } @Test - @Ignore public void lazyInstantiateConstructor() throws Exception { - doInstantiateConstructor(false, true); + try (var _ = registerTraceOfLocalVariables("omplex")) { + doInstantiateConstructor(false, true); + } } @Test - @Ignore public void lazyInstantiateAutoscopedConstructor() throws Exception { - doInstantiateConstructor(true, true); + try (var _ = registerTraceOfLocalVariables("omplex")) { + doInstantiateConstructor(true, true); + } } private void doInstantiateConstructor(boolean useAutoscoping, boolean lazy) throws Exception { From eb07540387373705fd696398e17faa0f0e864e94 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 19 Feb 2026 11:10:42 +0100 Subject: [PATCH 16/17] Fixing compilation errors --- .../benchmarks/inline/InlineContextResourceFactory.java | 3 ++- .../java/org/enso/compiler/test/mock/MockCompilerContext.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/runtime-benchmarks/src/main/java/org/enso/compiler/benchmarks/inline/InlineContextResourceFactory.java b/engine/runtime-benchmarks/src/main/java/org/enso/compiler/benchmarks/inline/InlineContextResourceFactory.java index ed358d55ff11..d9d1ce4c7308 100644 --- a/engine/runtime-benchmarks/src/main/java/org/enso/compiler/benchmarks/inline/InlineContextResourceFactory.java +++ b/engine/runtime-benchmarks/src/main/java/org/enso/compiler/benchmarks/inline/InlineContextResourceFactory.java @@ -22,6 +22,7 @@ public InlineContext create() { moduleScope.getModule().asCompilerModule(), scala.Option.apply(false), ensoCtx.getCompilerConfig(), - scala.Option.apply(pkgRepository)); + scala.Option.apply(pkgRepository), + null); } } diff --git a/engine/runtime-compiler/src/test/java/org/enso/compiler/test/mock/MockCompilerContext.java b/engine/runtime-compiler/src/test/java/org/enso/compiler/test/mock/MockCompilerContext.java index faae95b4287e..3b0b533e9089 100644 --- a/engine/runtime-compiler/src/test/java/org/enso/compiler/test/mock/MockCompilerContext.java +++ b/engine/runtime-compiler/src/test/java/org/enso/compiler/test/mock/MockCompilerContext.java @@ -91,7 +91,7 @@ public Module findTopScopeModule(String name) { @Override public RuntimeException formatDiagnostic( - Module module, Diagnostic diagnostic, boolean isOutputRedirected) { + Module module, Diagnostic diagnostic, boolean isOutputRedirected, Object src) { return new DiagnosticException((MockModule) module, diagnostic, isOutputRedirected); } From d5b2d0e6d6f27da45e6e377ae4003d4cc4880dd6 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 19 Feb 2026 14:30:48 +0100 Subject: [PATCH 17/17] Recognize files with .enso extension --- .../src/main/java/org/enso/common/ContextInsightSetup.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java b/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java index 18c797db0e3e..86f26c537dbf 100644 --- a/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java +++ b/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java @@ -161,6 +161,9 @@ private void initialize() throws IOException { if (insightFile.getFileName().toString().endsWith(".py")) { language = "python"; } + if (insightFile.getFileName().toString().endsWith(".enso")) { + language = "enso"; + } var insightSrc = Source.newBuilder("epb", insightFile.toFile()) .content(language + ":0#" + insightCode)