From f4a29a1c06aaa5b7d5699b78a4d1cdfef1273a17 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Tue, 27 Aug 2024 14:56:56 +0200 Subject: [PATCH 01/10] Benchmark measuring startup with import of all libraries --- test/Benchmarks/src/Startup/Import_World.enso | 9 +++++++++ test/Benchmarks/src/Startup/Startup.enso | 8 ++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/Benchmarks/src/Startup/Import_World.enso diff --git a/test/Benchmarks/src/Startup/Import_World.enso b/test/Benchmarks/src/Startup/Import_World.enso new file mode 100644 index 000000000000..4fd46858d42e --- /dev/null +++ b/test/Benchmarks/src/Startup/Import_World.enso @@ -0,0 +1,9 @@ +from Standard.Base import all +from Standard.Table import all +from Standard.Database import all +from Standard.AWS import all +from Standard.Google_Api import all +from Standard.Snowflake import all +import Standard.Visualization + +main = IO.println "Hello World" diff --git a/test/Benchmarks/src/Startup/Startup.enso b/test/Benchmarks/src/Startup/Startup.enso index 36947a0587ea..6d2ab0d5fdc9 100644 --- a/test/Benchmarks/src/Startup/Startup.enso +++ b/test/Benchmarks/src/Startup/Startup.enso @@ -6,7 +6,7 @@ polyglot java import java.lang.System as Java_System polyglot java import java.io.File as Java_File type Data - Value ~enso_bin:File ~empty_world:File ~hello_world:File + Value ~enso_bin:File ~empty_world:File ~hello_world:File ~import_world:File bench_empty self = self.startup [ "--run", self.empty_world.to_text ] @@ -14,6 +14,9 @@ type Data bench_hello self = self.startup [ "--run", self.hello_world.to_text ] + bench_import self = + self.startup [ "--run", self.import_world.to_text ] + startup self args = exe = self.enso_bin result = Process.run exe.path args @@ -34,11 +37,12 @@ collect_benches = Bench.build builder-> data = - Data.Value enso_bin (find_sibling "Empty_World.enso") (find_sibling "Hello_World.enso") + Data.Value enso_bin (find_sibling "Empty_World.enso") (find_sibling "Hello_World.enso") (find_sibling "Import_World.enso") builder.group "Startup" options group_builder-> group_builder.specify "empty_startup" data.bench_empty group_builder.specify "hello_world_startup" data.bench_hello + group_builder.specify "import_world_startup" data.bench_import find_sibling name = f = enso_project.root / "src" / "Startup" / name From f93ee2166b258d031d75bb1d3af1a18145d0be98 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Tue, 27 Aug 2024 15:32:10 +0200 Subject: [PATCH 02/10] Delay loading classes in registerPolyglotSymbol by using Suppliers --- .../runtime/scope/ModuleScope.java | 19 +++++++++++-------- .../interpreter/runtime/IrToTruffle.scala | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index f5de1e783971..ea2c6c511f92 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -21,7 +21,7 @@ public final class ModuleScope implements EnsoObject { private final Type associatedType; private final Module module; - private final Map polyglotSymbols; + private final Map> polyglotSymbols; private final Map types; private final Map>> methods; @@ -43,7 +43,7 @@ public final class ModuleScope implements EnsoObject { public ModuleScope( Module module, Type associatedType, - Map polyglotSymbols, + Map> polyglotSymbols, Map types, Map>> methods, Map> conversions, @@ -257,10 +257,13 @@ public List getConversions() { } /** + * Finds a polyglot symbol. + * + * @param symbolName name of the symbol to search for * @return the polyglot symbol imported into this scope. */ public Object getPolyglotSymbol(String symbolName) { - return polyglotSymbols.get(symbolName); + return polyglotSymbols.get(symbolName).get(); } @ExportMessage @@ -283,7 +286,7 @@ public static class Builder { @CompilerDirectives.CompilationFinal private ModuleScope moduleScope = null; private final Module module; private final Type associatedType; - private final Map polyglotSymbols; + private final Map> polyglotSymbols; private final Map types; private final Map>> methods; private final Map> conversions; @@ -315,7 +318,7 @@ public Builder(Module module, Map types) { public Builder( Module module, Type associatedType, - Map polyglotSymbols, + Map> polyglotSymbols, Map types, Map>> methods, Map> conversions, @@ -409,11 +412,11 @@ public void registerConversionMethod(Type toType, Type fromType, Function functi * Registers a new symbol in the polyglot namespace. * * @param name the name of the symbol - * @param sym the value being exposed + * @param symbolFactory the value being exposed */ - public void registerPolyglotSymbol(String name, Object sym) { + public void registerPolyglotSymbol(String name, Supplier symbolFactory) { assert moduleScope == null; - polyglotSymbols.put(name, sym); + polyglotSymbols.put(name, new CachingSupplier<>(symbolFactory)); } /** diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index 808e8cc47f7e..bfa0703e66e9 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -233,16 +233,18 @@ class IrToTruffle( private def registerPolyglotImports(module: Module): Unit = module.imports.foreach { case poly @ imports.Polyglot(i: imports.Polyglot.Java, _, _, _) => - var hostSymbol = context.lookupJavaClass(i.getJavaName) - if (hostSymbol == null) { - val err = Text.create( - s"Incorrect polyglot java import: ${i.getJavaName}" - ) - hostSymbol = DataflowError.withDefaultTrace(err, null) - } this.scopeBuilder.registerPolyglotSymbol( poly.getVisibleName, - hostSymbol + () => { + var hostSymbol = context.lookupJavaClass(i.getJavaName) + if (hostSymbol == null) { + val err = Text.create( + s"Incorrect polyglot java import: ${i.getJavaName}" + ) + hostSymbol = DataflowError.withDefaultTrace(err, null) + } + hostSymbol + } ) case _: Import.Module => case _: Error => From 0fba66b2ac17b7ef0748963948e36cb1708ff143 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 28 Aug 2024 15:31:09 +0200 Subject: [PATCH 03/10] getPolyglotSymbol may return null --- .../java/org/enso/interpreter/runtime/scope/ModuleScope.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index ea2c6c511f92..1ccc2c8220c2 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -260,10 +260,11 @@ public List getConversions() { * Finds a polyglot symbol. * * @param symbolName name of the symbol to search for - * @return the polyglot symbol imported into this scope. + * @return the polyglot symbol imported into this scope or {@code null} if it cannot be found */ public Object getPolyglotSymbol(String symbolName) { - return polyglotSymbols.get(symbolName).get(); + var supplier = polyglotSymbols.get(symbolName); + return supplier == null ? null : supplier.get(); } @ExportMessage From 6ee9c8f80feb3caa8ec26b2d735451efd22c91cd Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 28 Aug 2024 22:01:09 +0200 Subject: [PATCH 04/10] getPolyglotSymbolSupplier returns a supplier --- .../argument/ReadArgumentCheckNode.java | 18 ++++---- .../caseexpr/ObjectEqualityBranchNode.java | 12 +++-- .../constant/ConstantObjectNode.java | 2 +- .../expression/constant/LazyObjectNode.java | 44 +++++++++++++++++++ .../enso/interpreter/runtime/EnsoContext.java | 5 ++- .../runtime/scope/ModuleScope.java | 20 +++++---- .../runtime/util/CachingSupplier.java | 37 ++++++++++++++-- .../interpreter/runtime/IrToTruffle.scala | 43 ++++++++---------- 8 files changed, 129 insertions(+), 52 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java index d61186db70f6..743e0eda3a96 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java @@ -17,6 +17,7 @@ import com.oracle.truffle.api.nodes.RootNode; import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import org.enso.interpreter.EnsoLanguage; @@ -162,8 +163,9 @@ public static ReadArgumentCheckNode build(EnsoContext ctx, String comment, Type return ReadArgumentCheckNodeFactory.TypeCheckNodeGen.create(comment, expectedType); } - public static ReadArgumentCheckNode meta(String comment, Object metaObject) { - return ReadArgumentCheckNodeFactory.MetaCheckNodeGen.create(comment, metaObject); + public static ReadArgumentCheckNode meta( + String comment, Supplier metaObjectSupplier) { + return ReadArgumentCheckNodeFactory.MetaCheckNodeGen.create(comment, metaObjectSupplier); } public static boolean isWrappedThunk(Function fn) { @@ -475,12 +477,12 @@ String expectedTypeMessage() { } abstract static class MetaCheckNode extends ReadArgumentCheckNode { - private final Object expectedMeta; + private final Supplier expectedSupplier; @CompilerDirectives.CompilationFinal private String expectedTypeMessage; - MetaCheckNode(String name, Object expectedMeta) { + MetaCheckNode(String name, Supplier expectedMetaSupplier) { super(name); - this.expectedMeta = expectedMeta; + this.expectedSupplier = expectedMetaSupplier; } @Override @@ -493,7 +495,7 @@ Object verifyMetaObject(VirtualFrame frame, Object v, @Cached IsValueOfTypeNode if (isAllFitValue(v)) { return v; } - if (isA.execute(expectedMeta, v)) { + if (isA.execute(expectedSupplier.get(), v)) { return v; } else { return null; @@ -508,9 +510,9 @@ String expectedTypeMessage() { CompilerDirectives.transferToInterpreterAndInvalidate(); var iop = InteropLibrary.getUncached(); try { - expectedTypeMessage = iop.asString(iop.getMetaQualifiedName(expectedMeta)); + expectedTypeMessage = iop.asString(iop.getMetaQualifiedName(expectedSupplier.get())); } catch (UnsupportedMessageException ex) { - expectedTypeMessage = expectedMeta.toString(); + expectedTypeMessage = expectedSupplier.get().toString(); } return expectedTypeMessage; } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/ObjectEqualityBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/ObjectEqualityBranchNode.java index b1d7cf9c1445..37b4d4f50df3 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/ObjectEqualityBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/ObjectEqualityBranchNode.java @@ -3,25 +3,29 @@ import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.profiles.CountingConditionProfile; +import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.expression.builtin.meta.IsSameObjectNode; public class ObjectEqualityBranchNode extends BranchNode { - private final Object expected; + private @Child ExpressionNode expected; private @Child IsSameObjectNode isSameObject = IsSameObjectNode.build(); private final CountingConditionProfile profile = CountingConditionProfile.create(); - private ObjectEqualityBranchNode(RootCallTarget branch, Object expected, boolean terminalBranch) { + private ObjectEqualityBranchNode( + RootCallTarget branch, ExpressionNode expected, boolean terminalBranch) { super(branch, terminalBranch); this.expected = expected; } - public static BranchNode build(RootCallTarget branch, Object expected, boolean terminalBranch) { + public static BranchNode build( + RootCallTarget branch, ExpressionNode expected, boolean terminalBranch) { return new ObjectEqualityBranchNode(branch, expected, terminalBranch); } @Override public void execute(VirtualFrame frame, Object state, Object target) { - if (profile.profile(isSameObject.execute(target, expected))) { + var exp = expected.executeGeneric(frame); + if (profile.profile(isSameObject.execute(target, exp))) { accept(frame, state, new Object[0]); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/ConstantObjectNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/ConstantObjectNode.java index bbf9c8441e74..bf0e9ed7d84f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/ConstantObjectNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/ConstantObjectNode.java @@ -7,7 +7,7 @@ /** Represents a compile-time constant. */ @NodeInfo(shortName = "const", description = "Represents an arbitrary compile-time constant.") -public class ConstantObjectNode extends ExpressionNode { +public final class ConstantObjectNode extends ExpressionNode { private final Object object; private ConstantObjectNode(Object object) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java new file mode 100644 index 000000000000..956a6e92ac33 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java @@ -0,0 +1,44 @@ +package org.enso.interpreter.node.expression.constant; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.nodes.NodeInfo; +import java.util.function.Supplier; +import org.enso.interpreter.node.ExpressionNode; +import org.enso.interpreter.runtime.data.text.Text; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.util.CachingSupplier; + +@NodeInfo( + shortName = "lazy", + description = "Represents an arbitrary compile-time constant computed lazily.") +public final class LazyObjectNode extends ExpressionNode { + + private final String error; + private final Supplier supply; + + private LazyObjectNode(String error, Supplier supply) { + this.error = error; + this.supply = supply; + } + + /** + * Creates a node that returns lazily computed value. + * + * @param errorMessage the error message to show when the value is {@code null} + * @param supplier computes the value lazily. Can return {@code null} and then the {@code + * errorMessage} error is created + */ + public static ExpressionNode build(String errorMessage, Supplier supplier) { + return new LazyObjectNode(errorMessage, new CachingSupplier<>(supplier)); + } + + @Override + public Object executeGeneric(VirtualFrame frame) { + var result = supply.get(); + if (result instanceof TruffleObject) { + return result; + } + return DataflowError.withDefaultTrace(Text.create(error), this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java index be434fb65b59..3e8fb3b4bc54 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java @@ -13,6 +13,7 @@ import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnknownIdentifierException; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.io.TruffleProcessBuilder; @@ -556,7 +557,7 @@ public boolean isColorTerminalOutput() { * @return If the java class is found, return it, otherwise return null. */ @TruffleBoundary - public Object lookupJavaClass(String className) { + public TruffleObject lookupJavaClass(String className) { var binaryName = new StringBuilder(className); var collectedExceptions = new ArrayList(); for (; ; ) { @@ -564,7 +565,7 @@ public Object lookupJavaClass(String className) { try { var hostSymbol = lookupHostSymbol(fqn); if (hostSymbol != null) { - return hostSymbol; + return (TruffleObject) hostSymbol; } } catch (ClassNotFoundException | RuntimeException | InteropException ex) { collectedExceptions.add(ex); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index 1ccc2c8220c2..89160dd0bfad 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -1,6 +1,7 @@ package org.enso.interpreter.runtime.scope; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; import java.util.*; @@ -21,7 +22,7 @@ public final class ModuleScope implements EnsoObject { private final Type associatedType; private final Module module; - private final Map> polyglotSymbols; + private final Map> polyglotSymbols; private final Map types; private final Map>> methods; @@ -43,7 +44,7 @@ public final class ModuleScope implements EnsoObject { public ModuleScope( Module module, Type associatedType, - Map> polyglotSymbols, + Map> polyglotSymbols, Map types, Map>> methods, Map> conversions, @@ -257,14 +258,15 @@ public List getConversions() { } /** - * Finds a polyglot symbol. + * Finds a polyglot symbol supplier. The supplier will then load the provided {@code symbolName} + * when its {@link Supplier#get()} method is called. * * @param symbolName name of the symbol to search for - * @return the polyglot symbol imported into this scope or {@code null} if it cannot be found + * @return non-{@code null} supplier of a polyglot symbol imported into this scope */ - public Object getPolyglotSymbol(String symbolName) { + public Supplier getPolyglotSymbolSupplier(String symbolName) { var supplier = polyglotSymbols.get(symbolName); - return supplier == null ? null : supplier.get(); + return supplier != null ? supplier : CachingSupplier.nullSupplier(); } @ExportMessage @@ -287,7 +289,7 @@ public static class Builder { @CompilerDirectives.CompilationFinal private ModuleScope moduleScope = null; private final Module module; private final Type associatedType; - private final Map> polyglotSymbols; + private final Map> polyglotSymbols; private final Map types; private final Map>> methods; private final Map> conversions; @@ -319,7 +321,7 @@ public Builder(Module module, Map types) { public Builder( Module module, Type associatedType, - Map> polyglotSymbols, + Map> polyglotSymbols, Map types, Map>> methods, Map> conversions, @@ -415,7 +417,7 @@ public void registerConversionMethod(Type toType, Type fromType, Function functi * @param name the name of the symbol * @param symbolFactory the value being exposed */ - public void registerPolyglotSymbol(String name, Supplier symbolFactory) { + public void registerPolyglotSymbol(String name, Supplier symbolFactory) { assert moduleScope == null; polyglotSymbols.put(name, new CachingSupplier<>(symbolFactory)); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java index 8526c3b75fe1..de1094aaf6f9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java @@ -1,10 +1,15 @@ package org.enso.interpreter.runtime.util; +import com.oracle.truffle.api.CompilerDirectives; import java.util.function.Supplier; public final class CachingSupplier implements Supplier { + @SuppressWarnings("unchecked") + private static final Supplier EMPTY = new CachingSupplier(null); + private final Supplier supply; - private T memo; + @CompilerDirectives.CompilationFinal private boolean memoComputed; + @CompilerDirectives.CompilationFinal private T memo; public CachingSupplier(Supplier supply) { this.supply = supply; @@ -17,9 +22,33 @@ public CachingSupplier(T memo) { @Override public T get() { - if (memo == null) { - memo = supply.get(); + synchronized (this) { + if (memoComputed) { + return memo; + } + CompilerDirectives.transferToInterpreterAndInvalidate(); + if (supply == null) { + memoComputed = true; + return memo; + } + } + var v = supply.get(); + synchronized (this) { + if (!memoComputed) { + memo = v; + } + return memo; } - return memo; + } + + /** + * Returns a supplier that always returns {@code null} when its {@link Supplier#get()} method is + * called. + * + * @return non-{@code null} instance of supplier + */ + @SuppressWarnings("unchecked") + public static Supplier nullSupplier() { + return EMPTY; } } diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index bfa0703e66e9..2373b08d0d40 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -904,7 +904,7 @@ class IrToTruffle( comment, asScope( mod.unsafeAsModule().asInstanceOf[TruffleCompilerContext.Module] - ).getPolyglotSymbol(symbol.name) + ).getPolyglotSymbolSupplier(symbol.name) ) case _ => null } @@ -1518,7 +1518,7 @@ class IrToTruffle( Right( ObjectEqualityBranchNode.build( branchCodeNode.getCallTarget, - asAssociatedType(mod.unsafeAsModule()), + LiteralNode.build(asAssociatedType(mod.unsafeAsModule())), branch.terminalBranch ) ) @@ -1566,7 +1566,7 @@ class IrToTruffle( } else { ObjectEqualityBranchNode.build( branchCodeNode.getCallTarget, - tpe, + LiteralNode.build(tpe), branch.terminalBranch ) } @@ -1577,13 +1577,14 @@ class IrToTruffle( ) ) => val polyglotSymbol = - asScope(mod.unsafeAsModule()).getPolyglotSymbol(symbol.name) + asScope(mod.unsafeAsModule()) + .getPolyglotSymbolSupplier(symbol.name) Either.cond( polyglotSymbol != null, ObjectEqualityBranchNode .build( branchCodeNode.getCallTarget, - polyglotSymbol, + LazyObjectNode.build(symbol.name, polyglotSymbol), branch.terminalBranch ), BadPatternMatch.NonVisiblePolyglotSymbol(symbol.name) @@ -1595,7 +1596,8 @@ class IrToTruffle( ) => val mod = typ.module val polyClass = asScope(mod.unsafeAsModule()) - .getPolyglotSymbol(typ.symbol.name) + .getPolyglotSymbolSupplier(typ.symbol.name) + .get() val polyValueOrError = if (polyClass == null) @@ -1629,7 +1631,7 @@ class IrToTruffle( ObjectEqualityBranchNode .build( branchCodeNode.getCallTarget, - polyValue, + ConstantObjectNode.build(polyValue), branch.terminalBranch ) }) @@ -1753,7 +1755,9 @@ class IrToTruffle( ) ) => val polySymbol = - asScope(mod.unsafeAsModule()).getPolyglotSymbol(symbol.name) + asScope(mod.unsafeAsModule()) + .getPolyglotSymbolSupplier(symbol.name) + .get() if (polySymbol != null) { val argOfType = List( new DefinitionArgument.Specified( @@ -2021,23 +2025,14 @@ class IrToTruffle( ) case BindingsMap.ResolvedPolyglotSymbol(module, symbol) => val s = - asScope(module.unsafeAsModule()).getPolyglotSymbol(symbol.name) - if (s == null) { - throw new CompilerError( - s"No polyglot symbol for ${symbol.name}" - ) - } - ConstantObjectNode.build(s) + asScope(module.unsafeAsModule()) + .getPolyglotSymbolSupplier(symbol.name) + LazyObjectNode.build(symbol.name, s) case BindingsMap.ResolvedPolyglotField(symbol, name) => val s = - asScope(symbol.module.unsafeAsModule()).getPolyglotSymbol(name) - if (s == null) { - throw new CompilerError( - s"No polyglot field for ${name}" - ) - } - - ConstantObjectNode.build(s) + asScope(symbol.module.unsafeAsModule()) + .getPolyglotSymbolSupplier(name) + LazyObjectNode.build(name, s) case BindingsMap.ResolvedModuleMethod(_, method) => throw new CompilerError( s"Impossible here, module method ${method.name} should be caught when translating application" @@ -2151,7 +2146,7 @@ class IrToTruffle( * @return the Nothing builtin */ private def processEmpty(): RuntimeExpression = { - ConstantObjectNode.build(context.getBuiltins.nothing()) + LiteralNode.build(context.getBuiltins.nothing()) } /** Processes function arguments, generates arguments reads and creates From e2c7bd39b81dc820d8b5abc594097c40030d7be5 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 29 Aug 2024 06:56:09 +0200 Subject: [PATCH 05/10] Using CachingSupplier in PE directly. Avoiding double wrapping. --- .../argument/ReadArgumentCheckNode.java | 8 +++++--- .../expression/constant/LazyObjectNode.java | 6 +++--- .../interpreter/runtime/builtin/Builtins.java | 8 ++++---- .../interpreter/runtime/scope/ModuleScope.java | 6 +++--- .../runtime/util/CachingSupplier.java | 18 ++++++++++++++++-- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java index 743e0eda3a96..b353b105e5bb 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/argument/ReadArgumentCheckNode.java @@ -46,6 +46,7 @@ import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.library.dispatch.TypeOfNode; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; +import org.enso.interpreter.runtime.util.CachingSupplier; import org.graalvm.collections.Pair; public abstract class ReadArgumentCheckNode extends Node { @@ -165,7 +166,8 @@ public static ReadArgumentCheckNode build(EnsoContext ctx, String comment, Type public static ReadArgumentCheckNode meta( String comment, Supplier metaObjectSupplier) { - return ReadArgumentCheckNodeFactory.MetaCheckNodeGen.create(comment, metaObjectSupplier); + var cachingSupplier = CachingSupplier.wrap(metaObjectSupplier); + return ReadArgumentCheckNodeFactory.MetaCheckNodeGen.create(comment, cachingSupplier); } public static boolean isWrappedThunk(Function fn) { @@ -477,10 +479,10 @@ String expectedTypeMessage() { } abstract static class MetaCheckNode extends ReadArgumentCheckNode { - private final Supplier expectedSupplier; + private final CachingSupplier expectedSupplier; @CompilerDirectives.CompilationFinal private String expectedTypeMessage; - MetaCheckNode(String name, Supplier expectedMetaSupplier) { + MetaCheckNode(String name, CachingSupplier expectedMetaSupplier) { super(name); this.expectedSupplier = expectedMetaSupplier; } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java index 956a6e92ac33..0c7dda52705d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/constant/LazyObjectNode.java @@ -15,11 +15,11 @@ public final class LazyObjectNode extends ExpressionNode { private final String error; - private final Supplier supply; + private final CachingSupplier supply; private LazyObjectNode(String error, Supplier supply) { this.error = error; - this.supply = supply; + this.supply = CachingSupplier.wrap(supply); } /** @@ -30,7 +30,7 @@ private LazyObjectNode(String error, Supplier supply) { * errorMessage} error is created */ public static ExpressionNode build(String errorMessage, Supplier supplier) { - return new LazyObjectNode(errorMessage, new CachingSupplier<>(supplier)); + return new LazyObjectNode(errorMessage, supplier); } @Override diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java index 7224e103e624..746908d52d3a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Builtins.java @@ -244,7 +244,7 @@ private Map>> registerBuiltinM constr -> { Map> atomNodes = getOrUpdate(builtinMethodNodes, constr.getName()); - atomNodes.put(builtinMethodName, new CachingSupplier<>(() -> meta.toMethod())); + atomNodes.put(builtinMethodName, CachingSupplier.wrap(() -> meta.toMethod())); Map atomNodesMeta = getOrUpdate(builtinMetaMethods, constr.getName()); @@ -253,7 +253,7 @@ private Map>> registerBuiltinM () -> { Map> atomNodes = getOrUpdate(builtinMethodNodes, builtinMethodOwner); - atomNodes.put(builtinMethodName, new CachingSupplier<>(() -> meta.toMethod())); + atomNodes.put(builtinMethodName, CachingSupplier.wrap(() -> meta.toMethod())); Map atomNodesMeta = getOrUpdate(builtinMetaMethods, builtinMethodOwner); @@ -420,12 +420,12 @@ private Map>> readBuiltinMetho constr -> { Map> atomNodes = getOrUpdate(methodNodes, constr.getName()); - atomNodes.put(builtinMethodName, new CachingSupplier<>(builtin)); + atomNodes.put(builtinMethodName, CachingSupplier.forValue(builtin)); }, () -> { Map> atomNodes = getOrUpdate(methodNodes, builtinMethodOwner); - atomNodes.put(builtinMethodName, new CachingSupplier<>(builtin)); + atomNodes.put(builtinMethodName, CachingSupplier.forValue(builtin)); }); }); return methodNodes; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index 89160dd0bfad..d704546caf51 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -370,7 +370,7 @@ public void registerMethod(Type type, String method, Function function) { if (methodMap.containsKey(method) && !type.isBuiltin()) { throw new RedefinedMethodException(type.getName(), method); } else { - methodMap.put(method, new CachingSupplier<>(function)); + methodMap.put(method, CachingSupplier.forValue(function)); } } @@ -390,7 +390,7 @@ public void registerMethod(Type type, String method, Supplier supply) if (methodMap.containsKey(method) && !type.isBuiltin()) { throw new RedefinedMethodException(type.getName(), method); } else { - methodMap.put(method, new CachingSupplier<>(supply)); + methodMap.put(method, CachingSupplier.wrap(supply)); } } @@ -419,7 +419,7 @@ public void registerConversionMethod(Type toType, Type fromType, Function functi */ public void registerPolyglotSymbol(String name, Supplier symbolFactory) { assert moduleScope == null; - polyglotSymbols.put(name, new CachingSupplier<>(symbolFactory)); + polyglotSymbols.put(name, CachingSupplier.wrap(symbolFactory)); } /** diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java index de1094aaf6f9..8f98edf45b9d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java @@ -11,13 +11,26 @@ public final class CachingSupplier implements Supplier { @CompilerDirectives.CompilationFinal private boolean memoComputed; @CompilerDirectives.CompilationFinal private T memo; - public CachingSupplier(Supplier supply) { + private CachingSupplier(Supplier supply) { this.supply = supply; } - public CachingSupplier(T memo) { + private CachingSupplier(T memo) { this.supply = null; this.memo = memo; + this.memoComputed = true; + } + + public static CachingSupplier wrap(Supplier supply) { + if (supply instanceof CachingSupplier cs) { + return cs; + } else { + return new CachingSupplier<>(supply); + } + } + + public static CachingSupplier forValue(V value) { + return new CachingSupplier<>(value); } @Override @@ -45,6 +58,7 @@ public T get() { * Returns a supplier that always returns {@code null} when its {@link Supplier#get()} method is * called. * + * @param type of value returned by the supplier * @return non-{@code null} instance of supplier */ @SuppressWarnings("unchecked") From a73192960618293533a6aa0bacadf2a5181946d3 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 29 Aug 2024 08:22:30 +0200 Subject: [PATCH 06/10] Mark memoComputed when it is computed --- .../java/org/enso/interpreter/runtime/util/CachingSupplier.java | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java index 8f98edf45b9d..4f5f17d287c6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/CachingSupplier.java @@ -49,6 +49,7 @@ public T get() { synchronized (this) { if (!memoComputed) { memo = v; + memoComputed = true; } return memo; } From 5aa0bfdd577efe8eade6a8618e6eb29a7678c461 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 29 Aug 2024 09:26:54 +0200 Subject: [PATCH 07/10] Ensure standardized type for argument of makeSyntaxError and makeCompileError --- .../enso/interpreter/node/MethodRootNode.java | 3 +- .../enso/interpreter/runtime/EnsoContext.java | 4 +- .../interpreter/runtime/builtin/Error.java | 12 +++--- .../interpreter/runtime/IrToTruffle.scala | 37 ++++++++----------- 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java index 7477056e24ce..90674085e6c7 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/MethodRootNode.java @@ -14,7 +14,6 @@ import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.atom.AtomConstructor; -import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.scope.ModuleScope; @@ -214,7 +213,7 @@ final ExpressionNode replaceItself() { return newNode; } catch (CompilerError abnormalException) { var ctx = EnsoContext.get(this); - var msg = Text.create(abnormalException.getMessage()); + var msg = abnormalException.getMessage(); var load = ctx.getBuiltins().error().makeCompileError(msg); throw new PanicException(load, this); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java index 3e8fb3b4bc54..733c7ab3a76b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java @@ -52,7 +52,6 @@ import org.enso.interpreter.instrument.NotificationHandler; import org.enso.interpreter.runtime.builtin.Builtins; import org.enso.interpreter.runtime.data.Type; -import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.scope.TopLevelScope; import org.enso.interpreter.runtime.state.ExecutionEnvironment; @@ -972,8 +971,7 @@ public PanicException raiseAssertionPanic(Node node, String message, Throwable e if (message != null) { msg = msg + sep + message; } - var txt = Text.create(msg); - var err = getBuiltins().error().makeAssertionError(txt); + var err = getBuiltins().error().makeAssertionError(msg); throw new PanicException(err, e, node); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java index 75419e8904cd..639d3a35bba1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java @@ -110,16 +110,16 @@ public Error(Builtins builtins, EnsoContext context) { mapError = builtins.getBuiltinType(MapError.class); } - public Atom makeSyntaxError(Object message) { - return syntaxError.newInstance(message); + public Atom makeSyntaxError(String message) { + return syntaxError.newInstance(Text.create(message)); } - public Atom makeCompileError(Object message) { - return compileError.newInstance(message); + public Atom makeCompileError(String message) { + return compileError.newInstance(Text.create(message)); } - public Atom makeAssertionError(Text text) { - return assertionError.newInstance(text); + public Atom makeAssertionError(String text) { + return assertionError.newInstance(Text.create(text)); } public Atom makeIndexOutOfBounds(long index, long length) { diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index 2373b08d0d40..d694c1181005 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -102,7 +102,6 @@ import org.enso.interpreter.runtime.callable.{ Annotation => RuntimeAnnotation } import org.enso.interpreter.runtime.data.Type -import org.enso.interpreter.runtime.data.text.Text import org.enso.interpreter.runtime.scope.{ImportExportScope, ModuleScope} import org.enso.interpreter.{Constants, EnsoLanguage} @@ -238,9 +237,9 @@ class IrToTruffle( () => { var hostSymbol = context.lookupJavaClass(i.getJavaName) if (hostSymbol == null) { - val err = Text.create( + val msg = s"Incorrect polyglot java import: ${i.getJavaName}" - ) + val err = context.getBuiltins.error.makeCompileError(msg) hostSymbol = DataflowError.withDefaultTrace(err, null) } hostSymbol @@ -1393,9 +1392,7 @@ class IrToTruffle( context.getBuiltins .error() .makeSyntaxError( - Text.create( - "Type operators are not currently supported at runtime" - ) + "Type operators are not currently supported at runtime" ) ), value.location @@ -1441,7 +1438,7 @@ class IrToTruffle( val error = context.getBuiltins .error() - .makeCompileError(Text.create(message)) + .makeCompileError(message) setLocation(ErrorNode.build(error), caseExpr.location) } @@ -2088,47 +2085,47 @@ class IrToTruffle( case err: errors.Syntax => context.getBuiltins .error() - .makeSyntaxError(Text.create(err.message(fileLocationFromSection))) + .makeSyntaxError(err.message(fileLocationFromSection)) case err: errors.Redefined.Binding => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Redefined.Method => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Redefined.MethodClashWithAtom => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Redefined.Conversion => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Redefined.Type => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Redefined.SelfArg => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Redefined.Arg => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Unexpected.TypeSignature => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Resolution => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case err: errors.Conversion => context.getBuiltins .error() - .makeCompileError(Text.create(err.message(fileLocationFromSection))) + .makeCompileError(err.message(fileLocationFromSection)) case _: errors.Pattern => throw new CompilerError( "Impossible here, should be handled in the pattern match." @@ -2359,9 +2356,7 @@ class IrToTruffle( context.getBuiltins .error() .makeSyntaxError( - Text.create( - "Typeset literals are not yet supported at runtime" - ) + "Typeset literals are not yet supported at runtime" ) ), application.location From 6c00db4eed5e24fb6b65b287197a78ac3329d8e7 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 29 Aug 2024 10:23:48 +0200 Subject: [PATCH 08/10] Giving DataflowError the same getExceptionMessage as PanicException has --- .../runtime/error/DataflowError.java | 24 +++++++++++++++++++ .../runtime/error/PanicException.java | 9 +++++++ 2 files changed, 33 insertions(+) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java index 181f4449ed38..c87e494e00b0 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java @@ -1,17 +1,25 @@ package org.enso.interpreter.runtime.error; +import static org.enso.interpreter.runtime.error.PanicException.handleExceptionMessage; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; +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 java.util.Objects; +import org.enso.interpreter.node.callable.IndirectInvokeMethodNode; +import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNode; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers; @@ -26,6 +34,7 @@ */ @ExportLibrary(InteropLibrary.class) @ExportLibrary(TypesLibrary.class) +@ImportStatic(PanicException.class) public final class DataflowError extends AbstractTruffleException implements EnsoObject { /** Signals (local) values that haven't yet been initialized */ public static final DataflowError UNINITIALIZED = new DataflowError(null, (Node) null); @@ -141,6 +150,21 @@ boolean isException() { return true; } + @ExportMessage + boolean hasExceptionMessage() { + return true; + } + + @ExportMessage + Object getExceptionMessage( + @Cached IndirectInvokeMethodNode payloads, + @Cached(value = "toDisplayText(payloads)", allowUncached = true) + UnresolvedSymbol toDisplayText, + @CachedLibrary(limit = "3") InteropLibrary strings, + @Cached TypeToDisplayTextNode typeToDisplayTextNode) { + return handleExceptionMessage(payload, payloads, toDisplayText, strings, typeToDisplayTextNode); + } + @ExportMessage boolean hasExceptionStackTrace() { return ownTrace; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicException.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicException.java index 2493789d0066..39f80e13bc27 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicException.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicException.java @@ -128,6 +128,15 @@ Object getExceptionMessage( UnresolvedSymbol toDisplayText, @CachedLibrary(limit = "3") InteropLibrary strings, @Cached TypeToDisplayTextNode typeToDisplayTextNode) { + return handleExceptionMessage(payload, payloads, toDisplayText, strings, typeToDisplayTextNode); + } + + static Object handleExceptionMessage( + Object payload, + IndirectInvokeMethodNode payloads, + UnresolvedSymbol toDisplayText, + InteropLibrary strings, + TypeToDisplayTextNode typeToDisplayTextNode) { var ctx = EnsoContext.get(payloads); var text = payloads.execute( From 7fa566296be32347f82bb787a6468eb8a62cd31e Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 29 Aug 2024 10:25:00 +0200 Subject: [PATCH 09/10] Unifying handling of missing polyglot symbol --- .../java/org/enso/compiler/ExecCompilerTest.java | 9 ++++----- .../org/enso/interpreter/runtime/EnsoContext.java | 5 +++-- .../org/enso/interpreter/runtime/builtin/Error.java | 13 +++++++++++++ .../enso/interpreter/runtime/scope/ModuleScope.java | 8 +++++++- .../org/enso/interpreter/runtime/IrToTruffle.scala | 9 +-------- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java index bc9f0cf7fbf0..592ec81905d1 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/ExecCompilerTest.java @@ -408,16 +408,15 @@ public void testDoubledRandom() throws Exception { polyglot java import java.util.Random run seed = - operator1 = Random.new_generator seed + Random.new_generator seed """); var run = module.invokeMember("eval_expression", "run"); try { var err = run.execute(1L); - fail("Not expecting any result: " + err); + assertTrue("Returned value represents exception: " + err, err.isException()); + throw err.throwException(); } catch (PolyglotException ex) { - assertEquals( - "Compile error: Compiler Internal Error: No polyglot symbol for Random.", - ex.getMessage()); + assertEquals("Compile error: No polyglot symbol for Random.", ex.getMessage()); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java index 733c7ab3a76b..72704cc4513c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/EnsoContext.java @@ -52,6 +52,7 @@ import org.enso.interpreter.instrument.NotificationHandler; import org.enso.interpreter.runtime.builtin.Builtins; import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.scope.TopLevelScope; import org.enso.interpreter.runtime.state.ExecutionEnvironment; @@ -553,7 +554,7 @@ public boolean isColorTerminalOutput() { * is looked up by iterating the members of the outer class via Truffle's interop protocol. * * @param className Fully qualified class name, can also be nested static inner class. - * @return If the java class is found, return it, otherwise return null. + * @return If the java class is found, return it, otherwise return {@link DataflowError}. */ @TruffleBoundary public TruffleObject lookupJavaClass(String className) { @@ -581,7 +582,7 @@ public TruffleObject lookupJavaClass(String className) { level = Level.FINE; logger.log(Level.FINE, null, ex); } - return null; + return getBuiltins().error().makeMissingPolyglotImportError(className); } private Object lookupHostSymbol(String fqn) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java index 639d3a35bba1..ace957b9e26d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java @@ -38,6 +38,7 @@ import org.enso.interpreter.runtime.data.atom.Atom; import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers; +import org.enso.interpreter.runtime.error.DataflowError; /** Container for builtin Error types */ public final class Error { @@ -350,4 +351,16 @@ public Atom makeNumberParseError(String message) { public Atom makeMapError(long index, Object innerError) { return mapError.newInstance(index, innerError); } + + /** + * Creates error on missing polyglot java import class. + * + * @param className the name of the class that is missing + * @return data flow error representing the missing value + */ + public DataflowError makeMissingPolyglotImportError(String className) { + var msg = "No polyglot symbol for " + className; + var err = makeCompileError(msg); + return DataflowError.withDefaultTrace(err, null); + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java index d704546caf51..7e6f85284688 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java @@ -8,6 +8,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import org.enso.compiler.context.CompilerContext; +import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.Module; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.EnsoObject; @@ -266,7 +267,12 @@ public List getConversions() { */ public Supplier getPolyglotSymbolSupplier(String symbolName) { var supplier = polyglotSymbols.get(symbolName); - return supplier != null ? supplier : CachingSupplier.nullSupplier(); + if (supplier != null) { + return supplier; + } + var ctx = EnsoContext.get(null); + var err = ctx.getBuiltins().error().makeMissingPolyglotImportError(symbolName); + return CachingSupplier.forValue(err); } @ExportMessage diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala index d694c1181005..b7103a4e70e7 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala @@ -111,7 +111,6 @@ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.jdk.CollectionConverters._ import scala.jdk.OptionConverters._ -import org.enso.interpreter.runtime.error.DataflowError /** This is an implementation of a codegeneration pass that lowers the Enso * [[IR]] into the truffle structures that are actually executed. @@ -235,13 +234,7 @@ class IrToTruffle( this.scopeBuilder.registerPolyglotSymbol( poly.getVisibleName, () => { - var hostSymbol = context.lookupJavaClass(i.getJavaName) - if (hostSymbol == null) { - val msg = - s"Incorrect polyglot java import: ${i.getJavaName}" - val err = context.getBuiltins.error.makeCompileError(msg) - hostSymbol = DataflowError.withDefaultTrace(err, null) - } + val hostSymbol = context.lookupJavaClass(i.getJavaName) hostSymbol } ) From 53eece8f3a30231a9f095714ec2b3120b517a2ba Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 29 Aug 2024 10:54:36 +0200 Subject: [PATCH 10/10] Adjusting the test to the fact that DataflowError cannot have Warning attached --- .../enso/interpreter/test/WarningsTest.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WarningsTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WarningsTest.java index 3dd2def91dfd..61aead7772ee 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WarningsTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WarningsTest.java @@ -148,16 +148,27 @@ private void assertWarningsForAType(Value v) { assertEquals("Types without and with warnings are the same", type, warningType); assertTrue("It is an exception. Type: " + type, warning2.isException()); try { - warning2.throwException(); + throw warning2.throwException(); } catch (PolyglotException ex) { if (ex.getMessage() == null) { assertEquals(generator.typeError(), type); assertEquals(generator.typeError(), warningType); } else { - assertThat( - "Warning found for " + type, - ex.getMessage(), - AllOf.allOf(containsString("warn:once"), containsString("warn:twice"))); + try { + assertThat( + "Warning found for " + type, + ex.getMessage(), + AllOf.allOf(containsString("warn:once"), containsString("warn:twice"))); + } catch (AssertionError err) { + if (type != null && v.equals(warning1) && v.equals(warning2)) { + assertEquals( + "Cannot attach warnings to Error - check it is an error", + "Standard.Base.Error.Error", + type.getMetaQualifiedName()); + return; + } + throw err; + } } } }