From 054225e730481ba00af638686cb4ac1320e6b43d Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Tue, 6 Jun 2023 13:40:57 +0200 Subject: [PATCH 01/15] Forward port of former Espresso support to most recent Enso@GraalVM 22.3 --- build.sbt | 17 ++++++- .../enso/languageserver/boot/MainModule.scala | 3 ++ .../org/enso/runner/ContextFactory.scala | 11 +++++ .../org/enso/interpreter/EnsoLanguage.java | 3 +- .../enso/interpreter/node/ExpressionNode.java | 13 +++++- .../node/callable/function/StatementNode.java | 7 ++- .../builtin/interop/java/LookupClassNode.java | 2 + .../literal/PatchableLiteralNode.java | 5 +-- .../enso/interpreter/runtime/EnsoContext.java | 45 ++++++++++++------- .../runtime/scope/DebugLocalScope.java | 11 ++++- .../org/enso/compiler/PackageRepository.scala | 19 +++++++- 11 files changed, 105 insertions(+), 31 deletions(-) diff --git a/build.sbt b/build.sbt index 74461909aa9e..b924e74aa72c 100644 --- a/build.sbt +++ b/build.sbt @@ -1602,10 +1602,12 @@ lazy val `engine-runner` = project staticOnLinux = false, additionalOptions = Seq( "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", + "-Dorg.graalvm.launcher.home=" + System.getProperty("java.home"), "-H:IncludeResources=.*Main.enso$", "--macro:truffle", "--language:js", - // "-g", + "--language:java", + "-g", // "-H:+DashboardAll", // "-H:DashboardDump=runner.bgv" "-Dnic=nic" @@ -1617,7 +1619,18 @@ lazy val `engine-runner` = project "org.enso.loggingservice.WSLoggerManager$", "io.methvin.watchservice.jna.CarbonAPI", "org.enso.syntax2.Parser", - "zio.internal.ZScheduler$$anon$4" + "zio.internal.ZScheduler$$anon$4", + "org.enso.runner.Main$", + "akka.http.scaladsl.model.StatusCode$", + "akka.http.scaladsl.marshalling.Marshaller$", + "akka.http.scaladsl.marshalling.ToResponseMarshallable$", + "akka.http.scaladsl.marshalling.PredefinedToResponseMarshallers$", + "akka.http.scaladsl.model.HttpEntity$", + "akka.http.scaladsl.model.HttpCharset$", + "akka.http.scaladsl.model.HttpCharsets$", + "akka.http.impl.engine.rendering.RenderSupport$", + "akka.http.scaladsl.model.MediaTypes$", + "akka.http.scaladsl.model.ContentTypes$" ) ) .dependsOn(installNativeImage) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala index 07c9d7e45bb0..e528223ce50a 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala @@ -281,6 +281,9 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) { .allowAllAccess(true) .allowHostAccess(new HostAccessFactory().allWithTypeMapping()) .allowExperimentalOptions(true) + .option("java.ExposeNativeJavaVM", "true") + .option("java.Polyglot", "true") + .option("java.UseBindingsLoader", "true") .option(RuntimeServerInfo.ENABLE_OPTION, "true") .option(RuntimeOptions.INTERACTIVE_MODE, "true") .option(RuntimeOptions.PROJECT_ROOT, serverConfig.contentRootPath) diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index 0faff393e031..75b2fdb903cb 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -49,11 +49,22 @@ class ContextFactory { executionEnvironment.foreach { name => options.put("enso.ExecutionEnvironment", name) } + var javaHome = System.getenv("JAVA_HOME"); + if (javaHome == null) { + javaHome = System.getProperty("java.home"); + } + if (javaHome == null) { + throw new IllegalStateException("Specify JAVA_HOME environment property"); + } val context = Context .newBuilder() .allowExperimentalOptions(true) .allowAllAccess(true) .allowHostAccess(new HostAccessFactory().allWithTypeMapping()) + .option("java.ExposeNativeJavaVM", "true") + .option("java.Polyglot", "true") + .option("java.UseBindingsLoader", "true") + .option("java.JavaHome", javaHome) .option(RuntimeOptions.PROJECT_ROOT, projectRoot) .option(RuntimeOptions.STRICT_ERRORS, strictErrors.toString) .option(RuntimeOptions.WAIT_FOR_PENDING_SERIALIZATION_JOBS, "true") 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 f299fb948785..9c29669b5034 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java @@ -281,10 +281,11 @@ protected ExecutableNode parse(InlineParsingRequest request) throws Exception { if (exprNode.isDefined()) { var language = EnsoLanguage.get(exprNode.get()); + var exprNodeReal = exprNode.get(); return new ExecutableNode(language) { @Override public Object execute(VirtualFrame frame) { - return exprNode.get().executeGeneric(frame); + return exprNodeReal.executeGeneric(frame); } }; } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/ExpressionNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/ExpressionNode.java index 623eabffd713..5e347e39619f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/ExpressionNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/ExpressionNode.java @@ -79,10 +79,19 @@ public void setSourceLocation(int sourceStartIndex, int sourceLength) { */ @Override public SourceSection getSourceSection() { + var bounds = getSourceSectionBounds(); + return bounds == null ? null : EnsoRootNode.findSourceSection(getRootNode(), bounds[0], bounds[1]); + } + + public int[] getSourceSectionBounds() { if (this instanceof ExpressionNodeWrapper wrapper) { - return wrapper.getDelegateNode().getSourceSection(); + return wrapper.getDelegateNode().getSourceSectionBounds(); } else { - return EnsoRootNode.findSourceSection(getRootNode(), sourceStartIndex, sourceLength); + if (sourceStartIndex == EnsoRootNode.NO_SOURCE && sourceLength == EnsoRootNode.NO_SOURCE) { + return null; + } else { + return new int[] { sourceStartIndex, sourceLength }; + } } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/StatementNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/StatementNode.java index 7ac125605c2e..72930da7f0f1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/StatementNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/StatementNode.java @@ -5,7 +5,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.instrumentation.Tag; -import com.oracle.truffle.api.source.SourceSection; import org.enso.interpreter.node.ClosureRootNode; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.runtime.EnsoContext; @@ -31,13 +30,13 @@ static StatementNode wrap(ExpressionNode node) { } @Override - public SourceSection getSourceSection() { - return node.getSourceSection(); + public int[] getSourceSectionBounds() { + return node.getSourceSectionBounds(); } @Override public boolean isInstrumentable() { - return getSourceSection() != null && node.isInstrumentable(); + return getSourceSectionBounds() != null && node.isInstrumentable(); } @Override diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/LookupClassNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/LookupClassNode.java index 5688edb9a56c..60e84837fbb4 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/LookupClassNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/LookupClassNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.interop.java; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; @@ -18,6 +19,7 @@ static LookupClassNode build() { } @Specialization + @CompilerDirectives.TruffleBoundary Object doExecute(Object name, @Cached("build()") ExpectStringNode expectStringNode) { return EnsoContext.get(this).lookupJavaClass(expectStringNode.execute(name)); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/literal/PatchableLiteralNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/literal/PatchableLiteralNode.java index 931d5750de15..2143f4219e13 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/literal/PatchableLiteralNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/literal/PatchableLiteralNode.java @@ -3,7 +3,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.source.SourceSection; import java.util.function.Predicate; import org.enso.compiler.core.IR; import org.enso.compiler.core.IR$Literal$Number; @@ -51,8 +50,8 @@ public boolean test(IR.Expression ir) { } @Override - public SourceSection getSourceSection() { - return node.getSourceSection(); + public int[] getSourceSectionBounds() { + return node.getSourceSectionBounds(); } @Override 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 dec1c0cdadec..1c3148bd2f40 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 @@ -1,23 +1,30 @@ package org.enso.interpreter.runtime; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleFile; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnknownIdentifierException; import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.object.Shape; +import com.oracle.truffle.api.source.Source; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; -import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Deque; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicLong; - +import java.util.logging.Level; +import java.util.stream.StreamSupport; import org.enso.compiler.Compiler; import org.enso.compiler.PackageRepository; import org.enso.compiler.data.CompilerConfig; @@ -27,6 +34,7 @@ import org.enso.interpreter.EnsoLanguage; import org.enso.interpreter.OptionsHelper; import org.enso.interpreter.instrument.NotificationHandler; +import org.enso.interpreter.runtime.Module; import org.enso.interpreter.runtime.builtin.Builtins; import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.scope.TopLevelScope; @@ -42,17 +50,6 @@ import org.enso.polyglot.RuntimeOptions; import org.enso.polyglot.RuntimeServerInfo; import org.graalvm.options.OptionKey; - -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.TruffleFile; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.TruffleLogger; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.object.Shape; -import java.util.concurrent.ExecutorService; -import java.util.stream.StreamSupport; - import scala.jdk.javaapi.OptionConverters; /** @@ -365,6 +362,14 @@ public Optional findModuleByExpressionId(UUID expressionId) { */ @TruffleBoundary public Object lookupJavaClass(String className) { + var isHosted = "hosted".equals(System.getenv("ENSO_JAVA")); + Object java; + if (!isHosted) { + var src = Source.newBuilder("java", "", "getbindings.java").build(); + java = environment.parsePublic(src).call(); + } else { + java = null; + } List items = Arrays.asList(className.split("\\.")); for (int i = items.size() - 1; i >= 0; i--) { String pkgName = String.join(".", items.subList(0, i)); @@ -372,13 +377,19 @@ public Object lookupJavaClass(String className) { List nestedClassPart = i < items.size() - 1 ? items.subList(i + 1, items.size()) : List.of(); try { - Object hostSymbol = environment.lookupHostSymbol(pkgName + "." + curClassName); + Object hostSymbol; + if (isHosted) { + hostSymbol = environment.lookupHostSymbol(pkgName + "." + curClassName); + } else { + hostSymbol = InteropLibrary.getUncached().readMember(java, pkgName + "." + curClassName); + } if (nestedClassPart.isEmpty()) { return hostSymbol; } else { return getNestedClass(hostSymbol, nestedClassPart); } - } catch (RuntimeException ignored) { + } catch (RuntimeException | UnsupportedMessageException | UnknownIdentifierException ex) { + logger.log(Level.WARNING, null, ex); } } return null; 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 e228c99072ec..b84ce5036208 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 @@ -80,11 +80,13 @@ private DebugLocalScope( && this.bindingsByLevelsIdx < this.bindingsByLevels.size()); } + @TruffleBoundary public static DebugLocalScope createFromFrame(EnsoRootNode rootNode, MaterializedFrame frame) { return new DebugLocalScope( rootNode, frame, gatherBindingsByLevels(rootNode.getLocalScope().flattenBindings()), 0); } + @TruffleBoundary private static DebugLocalScope createParent(DebugLocalScope childScope) { return new DebugLocalScope( childScope.rootNode, @@ -140,6 +142,7 @@ boolean hasMembers() { /** Returns the members from the current local scope and all the parent scopes. */ @ExportMessage + @TruffleBoundary ScopeMembers getMembers(boolean includeInternal) { List members = new ArrayList<>(); bindingsByLevels.stream().skip(bindingsByLevelsIdx).forEach(members::addAll); @@ -147,6 +150,7 @@ ScopeMembers getMembers(boolean includeInternal) { } @ExportMessage + @TruffleBoundary boolean isMemberModifiable(String memberName) { return allBindings.containsKey(memberName); } @@ -173,6 +177,7 @@ boolean hasMemberWriteSideEffects(String member) { } @ExportMessage + @TruffleBoundary boolean isMemberReadable(String memberName) { // When a value in a frame is null, it means that the corresponding // AssignmentNode was not run yet, and the slot kind of the @@ -182,7 +187,8 @@ boolean isMemberReadable(String memberName) { } @ExportMessage - Object readMember(String member) { + @TruffleBoundary + Object readMember(String member, @CachedLibrary("this") InteropLibrary interop) { FramePointer framePtr = allBindings.get(member); if (framePtr == null) { return null; @@ -192,6 +198,7 @@ Object readMember(String member) { } @ExportMessage + @TruffleBoundary void writeMember(String member, Object value) throws UnknownIdentifierException { if (!allBindings.containsKey(member)) { throw UnknownIdentifierException.create(member); @@ -231,6 +238,7 @@ boolean hasSourceLocation() { } @ExportMessage + @TruffleBoundary SourceSection getSourceLocation() { return rootNode.getSourceSection(); } @@ -242,6 +250,7 @@ String toDisplayString(boolean allowSideEffects) { } @Override + @TruffleBoundary public String toString() { return String.format( "DebugLocalScope{rootNode = '%s', bindingsByLevels = %s, idx = %d}", diff --git a/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala b/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala index 99ea89f033eb..443877dbda9b 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/PackageRepository.scala @@ -324,7 +324,24 @@ object PackageRepository { isLibrary: Boolean ): Unit = { val extensions = pkg.listPolyglotExtensions("java") - extensions.foreach(context.getEnvironment.addToHostClassPath) + if ("hosted".equals(System.getenv("ENSO_JAVA"))) { + extensions.foreach(context.getEnvironment.addToHostClassPath) + } else { + val env = context.getEnvironment(); + val src = com.oracle.truffle.api.source.Source + .newBuilder("java", "", "getbindings.java") + .build(); + val espresso = env.parsePublic(src).call(); + def register(path: TruffleFile) = { + val iop = com.oracle.truffle.api.interop.InteropLibrary.getUncached() + try { + iop.invokeMember(espresso, "addPath", path.toString()) + } catch { + case t: Throwable => t.printStackTrace() + } + } + extensions.foreach(register) + } val (regularModules, syntheticModulesMetadata) = pkg .listSources() From 89458ec2f2861c3c06649eb7c6efe4a3e91e6091 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 07:34:23 +0200 Subject: [PATCH 02/15] Exclude target directories used by sbt from VSCode watching --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f71ca980586f..9f707a88f579 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,8 @@ "vue.complete.casing.tags": "pascal", "auto-snippets.snippets": [ { "language": "vue", "snippet": "Vue single-file component" } - ] + ], + "files.watcherExclude": { + "**/target": true + } } From 64029efbadf51f043b13e718278c190dd902a0ba Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 07:42:19 +0200 Subject: [PATCH 03/15] Use ENSO_JAVA=espresso to turn on Espresso support --- CHANGELOG.md | 2 + docs/infrastructure/native-image.md | 29 ++++++++-- .../enso/interpreter/runtime/EnsoContext.java | 55 +++++++++++-------- .../runtime/scope/DebugLocalScope.java | 5 +- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0215c818501..a0a5fcff4542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -923,6 +923,7 @@ - [Warning.get_all returns only unique warnings][6372] - [Reimplement `enso_project` as a proper builtin][6352] - [Limit number of reported warnings per value][6577] +- [Experimental support for Espresso Java interpreter][6966] - [Suggestions are updated only when the type of the expression changes][6755] - [Add project creation time to project metadata][6780] - [Upgrade GraalVM to 22.3.1 JDK17][6750] @@ -1064,6 +1065,7 @@ [6372]: https://github.com/enso-org/enso/pull/6372 [6352]: https://github.com/enso-org/enso/pull/6352 [6577]: https://github.com/enso-org/enso/pull/6577 +[6966]: https://github.com/enso-org/enso/pull/6966 [6750]: https://github.com/enso-org/enso/pull/6750 [6755]: https://github.com/enso-org/enso/pull/6755 [6780]: https://github.com/enso-org/enso/pull/6780 diff --git a/docs/infrastructure/native-image.md b/docs/infrastructure/native-image.md index 8ae0c49a5a8b..2ea348475dde 100644 --- a/docs/infrastructure/native-image.md +++ b/docs/infrastructure/native-image.md @@ -201,9 +201,7 @@ safely. ### Engine runner Configuration The Native Image generation for the Engine Runner is currently in a preview -state. Limitations are currently mostly due to -[Java interop](https://www.pivotaltracker.com/story/show/183260380) and loading -of stdlib components. To generate the Native Image for runner simply execute +state. To generate the Native Image for runner simply execute ``` sbt> engine-runner/buildNativeImage @@ -217,4 +215,27 @@ and execute the binary on a sample factorial test program The task that generates the Native Image, along with all the necessary configuration, reside in a separate project due to a bug in the currently used -GraalVM version. +GraalVM version. As September 2023 it can execute all Enso code, but cannot invoke `IO.println` +or other library functions that require [polyglot java import](../../docs/polyglot/java.md), +but read on... + +### Engine with Espresso + +Since [PR-6966](https://github.com/enso-org/enso/pull/6966) there is an experimental +support for including [Espresso Java interpreter](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/) +to allow use of some library functions (like `IO.println`) in the _Native Image_ built +runner. + +The support can be enabled by setting environment variable `ENSO_JAVA=espresso` and +making sure Espresso is installed in GraalVM executing the Enso engine - e.g. by +running `graalvm/bin/gu install espresso`. Then execute: + +```bash +$ cat >hello.enso +import Standard.Base.IO + +main = IO.println <| "Hello World!" +$ ENSO_JAVA=espresso ./enso-x.y.z-dev/bin/enso --run hello.enso +``` +Unless you see a warning containing _"No language for id java found."_ your code +has just successfully been executed by [Espresso](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/)! 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 a73912da1af2..957c1ba7beb6 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 @@ -1,17 +1,5 @@ package org.enso.interpreter.runtime; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.TruffleFile; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.TruffleLogger; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnknownIdentifierException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.object.Shape; -import com.oracle.truffle.api.source.Source; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; @@ -26,7 +14,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; -import java.util.stream.StreamSupport; + import org.enso.compiler.Compiler; import org.enso.compiler.PackageRepository; import org.enso.compiler.PackageRepositoryUtils; @@ -66,6 +54,7 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.Shape; +import com.oracle.truffle.api.source.Source; import scala.jdk.javaapi.OptionConverters; @@ -405,14 +394,6 @@ public Optional findModuleByExpressionId(UUID expressionId) { */ @TruffleBoundary public Object lookupJavaClass(String className) { - var isHosted = "hosted".equals(System.getenv("ENSO_JAVA")); - Object java; - if (!isHosted) { - var src = Source.newBuilder("java", "", "getbindings.java").build(); - java = environment.parsePublic(src).call(); - } else { - java = null; - } List items = Arrays.asList(className.split("\\.")); for (int i = items.size() - 1; i >= 0; i--) { String pkgName = String.join(".", items.subList(0, i)); @@ -421,10 +402,10 @@ public Object lookupJavaClass(String className) { i < items.size() - 1 ? items.subList(i + 1, items.size()) : List.of(); try { Object hostSymbol; - if (isHosted) { + if (findGuestJava() == null) { hostSymbol = environment.lookupHostSymbol(pkgName + "." + curClassName); } else { - hostSymbol = InteropLibrary.getUncached().readMember(java, pkgName + "." + curClassName); + hostSymbol = InteropLibrary.getUncached().readMember(findGuestJava(), pkgName + "." + curClassName); } if (nestedClassPart.isEmpty()) { return hostSymbol; @@ -438,6 +419,34 @@ public Object lookupJavaClass(String className) { return null; } + private Object guestJava = this; + + @TruffleBoundary + private Object findGuestJava() throws IllegalStateException { + if (guestJava != this) { + return guestJava; + } + guestJava = null; + var envJava = System.getenv("ENSO_JAVA"); + if ("espresso".equals(envJava)) { + var src = Source.newBuilder("java", "", "getbindings.java").build(); + try { + guestJava = environment.parsePublic(src).call(); + } catch (Exception ex) { + if (ex.getMessage().contains("No language for id java found.")) { + logger.log(Level.SEVERE, "Environment variable ENSO_JAVA=" + envJava + ", but " + ex.getMessage()); + logger.log(Level.SEVERE, "Use " + System.getProperty("java.home") + "/bin/gu install espresso"); + logger.log(Level.SEVERE, "Continuing in regular Java mode"); + } else { + var ise = new IllegalStateException(ex.getMessage()); + ise.setStackTrace(ex.getStackTrace()); + throw ise; + } + } + } + return guestJava; + } + /** * Finds the package the provided module belongs to. * 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 7f1e47b50ab8..570007eeb9bb 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 @@ -6,25 +6,22 @@ import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnknownIdentifierException; 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.source.SourceSection; - import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; - import org.enso.interpreter.EnsoLanguage; import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.error.DataflowError; -import com.oracle.truffle.api.library.CachedLibrary; - /** * This class serves as a basic support for debugging with Chrome inspector. Currently, only * function scopes are supported. From 9be90fbcf3311cde2f55f40c187c41a89343b244 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 08:41:04 +0200 Subject: [PATCH 04/15] Conditionally support Espresso in the language server mode --- .../org/enso/languageserver/boot/MainModule.scala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala index 1ee48ac55e7f..c677ef49fc14 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala @@ -50,6 +50,7 @@ import org.enso.logger.akka.AkkaConverter import org.enso.polyglot.{HostAccessFactory, RuntimeOptions, RuntimeServerInfo} import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo} import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator} +import org.graalvm.polyglot.Engine import org.graalvm.polyglot.Context import org.graalvm.polyglot.io.MessageEndpoint import org.slf4j.event.Level @@ -286,14 +287,11 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { val stdInSink = new ObservableOutputStream val stdIn = new ObservablePipedInputStream(stdInSink) - val context = Context + val builder = Context .newBuilder() .allowAllAccess(true) .allowHostAccess(new HostAccessFactory().allWithTypeMapping()) .allowExperimentalOptions(true) - .option("java.ExposeNativeJavaVM", "true") - .option("java.Polyglot", "true") - .option("java.UseBindingsLoader", "true") .option(RuntimeServerInfo.ENABLE_OPTION, "true") .option(RuntimeOptions.INTERACTIVE_MODE, "true") .option(RuntimeOptions.PROJECT_ROOT, serverConfig.contentRootPath) @@ -322,7 +320,14 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { connection } else null }) - .build() + if (Engine.newBuilder().allowExperimentalOptions(true).build.getLanguages().containsKey("java")) { + builder + .option("java.ExposeNativeJavaVM", "true") + .option("java.Polyglot", "true") + .option("java.UseBindingsLoader", "true") + } + + val context = builder.build() log.trace("Created Runtime context [{}].", context) system.eventStream.setLogLevel(AkkaConverter.toAkka(logLevel)) From f3705eaf84b67de403a770518e12d141a491ffef Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 10:13:13 +0200 Subject: [PATCH 05/15] Support addToHostClassPath in Espresso mode --- .../interop/java/AddToClassPathNode.java | 9 +-- .../enso/interpreter/runtime/EnsoContext.java | 73 +++++++++++-------- .../runtime/DefaultPackageRepository.scala | 2 +- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java index 7442bde16576..551c3f8877d5 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java @@ -25,10 +25,9 @@ static AddToClassPathNode build() { @CompilerDirectives.TruffleBoundary @Specialization Object doExecute(Object path, @Cached ExpectStringNode expectStringNode) { - EnsoContext context = EnsoContext.get(this); - context - .getEnvironment() - .addToHostClassPath(context.getTruffleFile(new File(expectStringNode.execute(path)))); - return context.getBuiltins().nothing(); + var ctx = EnsoContext.get(this); + var file = ctx.getTruffleFile(new File(expectStringNode.execute(path))); + ctx.addToHostClassPath(file); + return ctx.getBuiltins().nothing(); } } 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 957c1ba7beb6..3c630f634de5 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 @@ -49,6 +49,7 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.Env; 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.UnknownIdentifierException; import com.oracle.truffle.api.interop.UnsupportedMessageException; @@ -383,6 +384,27 @@ public Optional findModuleByExpressionId(UUID expressionId) { .findFirst(); } + /** + * Modifies the classpath to use to lookup {@code polyglot java} imports. + * @param file the file to register + */ + @TruffleBoundary + public void addToHostClassPath(TruffleFile file) { + if (findGuestJava() == null) { + environment.addToHostClassPath(file); + } else { + try { + var path = new File(file.toUri()).getAbsoluteFile(); + if (!path.exists()) { + throw new IllegalStateException("File not found " + path); + } + InteropLibrary.getUncached().invokeMember(findGuestJava(), "addPath", path.getPath()); + } catch (InteropException ex) { + throw new IllegalStateException(ex); + } + } + } + /** * Tries to lookup a Java class (host symbol in Truffle terminology) by its fully qualified name. * This method also tries to lookup inner classes. More specifically, if the provided name @@ -401,24 +423,29 @@ public Object lookupJavaClass(String className) { List nestedClassPart = i < items.size() - 1 ? items.subList(i + 1, items.size()) : List.of(); try { - Object hostSymbol; - if (findGuestJava() == null) { - hostSymbol = environment.lookupHostSymbol(pkgName + "." + curClassName); - } else { - hostSymbol = InteropLibrary.getUncached().readMember(findGuestJava(), pkgName + "." + curClassName); - } + var hostSymbol = lookupHostSymbol(pkgName, curClassName); if (nestedClassPart.isEmpty()) { return hostSymbol; } else { - return getNestedClass(hostSymbol, nestedClassPart); + var fullInnerClassName = curClassName + "$" + String.join("$", nestedClassPart); + return lookupHostSymbol(pkgName, fullInnerClassName); } - } catch (RuntimeException | UnsupportedMessageException | UnknownIdentifierException ex) { + } catch (RuntimeException | InteropException ex) { logger.log(Level.WARNING, null, ex); } } return null; } + private Object lookupHostSymbol(String pkgName, String curClassName) + throws UnknownIdentifierException, UnsupportedMessageException { + if (findGuestJava() == null) { + return environment.lookupHostSymbol(pkgName + "." + curClassName); + } else { + return InteropLibrary.getUncached().readMember(findGuestJava(), pkgName + "." + curClassName); + } + } + private Object guestJava = this; @TruffleBoundary @@ -428,10 +455,14 @@ private Object findGuestJava() throws IllegalStateException { } guestJava = null; var envJava = System.getenv("ENSO_JAVA"); + if (envJava == null) { + return guestJava; + } if ("espresso".equals(envJava)) { var src = Source.newBuilder("java", "", "getbindings.java").build(); try { guestJava = environment.parsePublic(src).call(); + logger.log(Level.SEVERE, "Using experimental Espresso support!"); } catch (Exception ex) { if (ex.getMessage().contains("No language for id java found.")) { logger.log(Level.SEVERE, "Environment variable ENSO_JAVA=" + envJava + ", but " + ex.getMessage()); @@ -443,6 +474,8 @@ private Object findGuestJava() throws IllegalStateException { throw ise; } } + } else { + throw new IllegalStateException("Unsupported value of ENSO_JAVA environment variable: " + envJava); } return guestJava; } @@ -641,30 +674,6 @@ public NotificationHandler getNotificationHandler() { return notificationHandler; } - private Object getNestedClass(Object hostClass, List nestedClassName) { - Object nestedClass = hostClass; - var interop = InteropLibrary.getUncached(); - for (String name : nestedClassName) { - if (interop.isMemberReadable(nestedClass, name)) { - Object member; - try { - member = interop.readMember(nestedClass, name); - } catch (UnsupportedMessageException | UnknownIdentifierException e) { - throw new IllegalStateException(e); - } - assert member != null; - if (interop.isMetaObject(member)) { - nestedClass = member; - } else { - return null; - } - } else { - return null; - } - } - return nestedClass; - } - private T getOption(OptionKey key) { var options = getEnvironment().getOptions(); var safely = false; diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala index 413a8b9bd57e..9fceeb762e4c 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala @@ -181,7 +181,7 @@ private class DefaultPackageRepository( isLibrary: Boolean ): Unit = { val extensions = pkg.listPolyglotExtensions("java") - extensions.foreach(context.getEnvironment.addToHostClassPath) + extensions.foreach(context.addToHostClassPath) val (regularModules, syntheticModulesMetadata) = pkg .listSources() From 03541547abc1b8b08d93e7ae043a72f331aea946 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 10:30:17 +0200 Subject: [PATCH 06/15] Applying Scala formatting --- .../scala/org/enso/languageserver/boot/MainModule.scala | 9 ++++++++- .../src/main/scala/org/enso/runner/ContextFactory.scala | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala index c677ef49fc14..27747c55c334 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala @@ -320,7 +320,14 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { connection } else null }) - if (Engine.newBuilder().allowExperimentalOptions(true).build.getLanguages().containsKey("java")) { + if ( + Engine + .newBuilder() + .allowExperimentalOptions(true) + .build + .getLanguages() + .containsKey("java") + ) { builder .option("java.ExposeNativeJavaVM", "true") .option("java.Polyglot", "true") diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index 6fd4cf008892..a0ac233840f1 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -64,8 +64,10 @@ class ContextFactory { .newBuilder() .allowExperimentalOptions(true) .allowAllAccess(true) - .allowHostAccess(new HostAccessFactory() - .allWithTypeMapping()) + .allowHostAccess( + new HostAccessFactory() + .allWithTypeMapping() + ) .option(RuntimeOptions.PROJECT_ROOT, projectRoot) .option(RuntimeOptions.STRICT_ERRORS, strictErrors.toString) .option(RuntimeOptions.WAIT_FOR_PENDING_SERIALIZATION_JOBS, "true") From 3c5bbe2cd31e48f2176b2bfd4aebac8b17ea38f1 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 11:10:39 +0200 Subject: [PATCH 07/15] Enable language:java in engine-runner/buildNativeImage only if Espresso is installed in the JVM --- build.sbt | 23 ++++++++++++--- docs/infrastructure/native-image.md | 46 ++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/build.sbt b/build.sbt index 1b89a6620548..94627bf91616 100644 --- a/build.sbt +++ b/build.sbt @@ -1696,16 +1696,31 @@ lazy val `engine-runner` = project staticOnLinux = false, additionalOptions = Seq( "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", - "-Dorg.graalvm.launcher.home=" + System.getProperty("java.home"), "-H:IncludeResources=.*Main.enso$", "--macro:truffle", "--language:js", - "--language:java", - "-g", + // "-g", // "-H:+DashboardAll", // "-H:DashboardDump=runner.bgv" "-Dnic=nic" - ), + ) ++ (if ( + org.graalvm.polyglot.Engine + .create() + .getLanguages() + .containsKey("java") + ) { + System.out.println( + "Building engine `runner` image with experimental Espresso support!" + ) + Seq( + "-Dorg.graalvm.launcher.home=" + System.getProperty( + "java.home" + ), + "--language:java" + ) + } else { + Seq() + }), mainClass = Option("org.enso.runner.Main"), cp = Option("runtime.jar"), initializeAtRuntime = Seq( diff --git a/docs/infrastructure/native-image.md b/docs/infrastructure/native-image.md index 2ea348475dde..09c92c81beca 100644 --- a/docs/infrastructure/native-image.md +++ b/docs/infrastructure/native-image.md @@ -215,20 +215,21 @@ and execute the binary on a sample factorial test program The task that generates the Native Image, along with all the necessary configuration, reside in a separate project due to a bug in the currently used -GraalVM version. As September 2023 it can execute all Enso code, but cannot invoke `IO.println` -or other library functions that require [polyglot java import](../../docs/polyglot/java.md), -but read on... +GraalVM version. As September 2023 it can execute all Enso code, but cannot +invoke `IO.println` or other library functions that require +[polyglot java import](../../docs/polyglot/java.md), but read on... ### Engine with Espresso -Since [PR-6966](https://github.com/enso-org/enso/pull/6966) there is an experimental -support for including [Espresso Java interpreter](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/) -to allow use of some library functions (like `IO.println`) in the _Native Image_ built -runner. +Since [PR-6966](https://github.com/enso-org/enso/pull/6966) there is an +experimental support for including +[Espresso Java interpreter](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/) +to allow use of some library functions (like `IO.println`) in the _Native Image_ +built runner. -The support can be enabled by setting environment variable `ENSO_JAVA=espresso` and -making sure Espresso is installed in GraalVM executing the Enso engine - e.g. by -running `graalvm/bin/gu install espresso`. Then execute: +The support can be enabled by setting environment variable `ENSO_JAVA=espresso` +and making sure Espresso is installed in GraalVM executing the Enso engine - +e.g. by running `graalvm/bin/gu install espresso`. Then execute: ```bash $ cat >hello.enso @@ -237,5 +238,28 @@ import Standard.Base.IO main = IO.println <| "Hello World!" $ ENSO_JAVA=espresso ./enso-x.y.z-dev/bin/enso --run hello.enso ``` + Unless you see a warning containing _"No language for id java found."_ your code -has just successfully been executed by [Espresso](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/)! +has just successfully been executed by +[Espresso](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/)! + +Espresso support works also with +[native image support](#engine-runner-configuration). Just make sure Espresso is +installed in your GraalVM (via `gu install espresso`) and then rebuild the +`runner` executable: + +``` +enso$ rm runner +enso$ sbt --java-home /graalvm +sbt> engine-runner/buildNativeImage +``` + +as suggested in the [native image support](#engine-runner-configuration). The +build script detects presence of Espresso and automatically adds +`--language:java` when creating the image. Then you can use + +``` +$ ENSO_JAVA=espresso ./runner --run hello.enso +``` + +to execute native image `runner` build of Enso together with Espresso. From bec8fde708cbc2a48b1b28bfde5eadb4afb9207d Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 11:22:30 +0200 Subject: [PATCH 08/15] .allowExperimentalOptions when checking for Espresso --- .../src/main/scala/org/enso/runner/ContextFactory.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index a0ac233840f1..e5d0e6f39047 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -110,7 +110,14 @@ class ContextFactory { if (graalpy.exists()) { builder.option("python.Executable", graalpy.getAbsolutePath()); } - if (Engine.create().getLanguages().containsKey("java")) { + if ( + Engine + .newBuilder() + .allowExperimentalOptions(true) + .build() + .getLanguages() + .containsKey("java") + ) { builder .option("java.ExposeNativeJavaVM", "true") .option("java.Polyglot", "true") From 2f5c569f2da8c0832697c11881142cb1445d1615 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 15:28:18 +0200 Subject: [PATCH 09/15] Improving ability of Enso+Espresso to execute runEngineDistribution --run test/Tests --- docs/infrastructure/native-image.md | 16 ++++++++---- .../callable/resolver/HostMethodCallNode.java | 25 ++++++++++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/docs/infrastructure/native-image.md b/docs/infrastructure/native-image.md index 09c92c81beca..541bb54853f9 100644 --- a/docs/infrastructure/native-image.md +++ b/docs/infrastructure/native-image.md @@ -203,13 +203,13 @@ safely. The Native Image generation for the Engine Runner is currently in a preview state. To generate the Native Image for runner simply execute -``` +```bash sbt> engine-runner/buildNativeImage ``` and execute the binary on a sample factorial test program -``` +```bash > runner --run engine/runner-native/src/test/resources/Factorial.enso 6 ``` @@ -236,19 +236,25 @@ $ cat >hello.enso import Standard.Base.IO main = IO.println <| "Hello World!" + $ ENSO_JAVA=espresso ./enso-x.y.z-dev/bin/enso --run hello.enso ``` Unless you see a warning containing _"No language for id java found."_ your code has just successfully been executed by -[Espresso](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/)! +[Espresso](https://www.graalvm.org/jdk17/reference-manual/java-on-truffle/)! To +debug just add `JAVA_OPTS` environment variable set to your IDE favorite value: + +```bash +$ JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,address=5005 ENSO_JAVA=espresso enso --run hello.enso +``` Espresso support works also with [native image support](#engine-runner-configuration). Just make sure Espresso is installed in your GraalVM (via `gu install espresso`) and then rebuild the `runner` executable: -``` +```bash enso$ rm runner enso$ sbt --java-home /graalvm sbt> engine-runner/buildNativeImage @@ -258,7 +264,7 @@ as suggested in the [native image support](#engine-runner-configuration). The build script detects presence of Espresso and automatically adds `--language:java` when creating the image. Then you can use -``` +```bash $ ENSO_JAVA=espresso ./runner --run hello.enso ``` diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java index 6fae9a16a84e..95b1f142ed08 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.callable.resolver; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateUncached; @@ -213,25 +214,25 @@ Object resolveHostMethod( Object[] args, @Shared("interop") @CachedLibrary(limit = "LIB_LIMIT") InteropLibrary members, @Shared("hostValueToEnsoNode") @Cached HostValueToEnsoNode hostValueToEnsoNode) { + var ctx = EnsoContext.get(this); try { return hostValueToEnsoNode.execute(members.invokeMember(self, symbol, args)); } catch (UnsupportedMessageException | UnknownIdentifierException e) { - throw new IllegalStateException( - "Impossible to reach here. The member is checked to be invocable."); + CompilerDirectives.transferToInterpreter(); + var err = ctx.getBuiltins().error().makeNotInvokable(self); + throw new PanicException(err, e, this); } catch (ArityException e) { - throw new PanicException( - EnsoContext.get(this) - .getBuiltins() + var err = + ctx.getBuiltins() .error() - .makeArityError(e.getExpectedMinArity(), e.getExpectedMaxArity(), e.getActualArity()), - this); + .makeArityError(e.getExpectedMinArity(), e.getExpectedMaxArity(), e.getActualArity()); + throw new PanicException(err, this); } catch (UnsupportedTypeException e) { - throw new PanicException( - EnsoContext.get(this) - .getBuiltins() + var err = + ctx.getBuiltins() .error() - .makeUnsupportedArgumentsError(e.getSuppliedValues(), e.getMessage()), - this); + .makeUnsupportedArgumentsError(e.getSuppliedValues(), e.getMessage()); + throw new PanicException(err, this); } } From 860909035d0a875efe347ee10310fbe93b5e6e92 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 16:04:22 +0200 Subject: [PATCH 10/15] Espresso requires additional thread to execute finalization and handle event queues --- .../src/main/scala/org/enso/languageserver/boot/MainModule.scala | 1 + .../runner/src/main/scala/org/enso/runner/ContextFactory.scala | 1 + 2 files changed, 2 insertions(+) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala index 27747c55c334..0bfe094dc5f5 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala @@ -332,6 +332,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { .option("java.ExposeNativeJavaVM", "true") .option("java.Polyglot", "true") .option("java.UseBindingsLoader", "true") + .allowCreateThread(true) } val context = builder.build() diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index e5d0e6f39047..3078fb03ffbe 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -123,6 +123,7 @@ class ContextFactory { .option("java.Polyglot", "true") .option("java.UseBindingsLoader", "true") .option("java.JavaHome", javaHome) + .allowCreateThread(true) } new PolyglotContext(builder.build) } From 513215f5d084439a3b4ce2bdfdee80a9fbf9169d Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Sat, 16 Sep 2023 16:05:43 +0200 Subject: [PATCH 11/15] Expect unexpected and report just PanicException --- .../node/callable/resolver/HostMethodCallNode.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java index 95b1f142ed08..32de29eb3025 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/resolver/HostMethodCallNode.java @@ -269,8 +269,10 @@ Object resolveHostConstructor( try { return hostValueToEnsoNode.execute(instances.instantiate(self, args)); } catch (UnsupportedMessageException e) { - throw new IllegalStateException( - "Impossible to reach here. The member is checked to be instantiable."); + CompilerDirectives.transferToInterpreter(); + var ctx = EnsoContext.get(this); + var err = ctx.getBuiltins().error().makeNotInvokable(self); + throw new PanicException(err, e, this); } catch (ArityException e) { throw new PanicException( EnsoContext.get(this) From 2e393ddde6b95c840e55ab7a7a79eaf77fd16870 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 18 Sep 2023 09:07:49 +0200 Subject: [PATCH 12/15] Removing useless import --- test/Tests/src/Data/Time/Time_Zone_Spec.enso | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Tests/src/Data/Time/Time_Zone_Spec.enso b/test/Tests/src/Data/Time/Time_Zone_Spec.enso index 9190ed5313dd..b05fa31c235c 100644 --- a/test/Tests/src/Data/Time/Time_Zone_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Zone_Spec.enso @@ -6,7 +6,6 @@ import Standard.Test.Extensions polyglot java import java.time.ZoneId polyglot java import java.time.ZoneOffset -polyglot java import java.time.ZoneRegion spec = Test.group "Zone" <| From b3332c4ed1aef714c3ae24ee1e4c4a0e35aff9ba Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Tue, 19 Sep 2023 06:20:59 +0200 Subject: [PATCH 13/15] Renaming the method to addToClassPath --- .../expression/builtin/interop/java/AddToClassPathNode.java | 2 +- .../main/java/org/enso/interpreter/runtime/EnsoContext.java | 4 ++-- .../enso/interpreter/runtime/DefaultPackageRepository.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java index 551c3f8877d5..4d757a703502 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/interop/java/AddToClassPathNode.java @@ -27,7 +27,7 @@ static AddToClassPathNode build() { Object doExecute(Object path, @Cached ExpectStringNode expectStringNode) { var ctx = EnsoContext.get(this); var file = ctx.getTruffleFile(new File(expectStringNode.execute(path))); - ctx.addToHostClassPath(file); + ctx.addToClassPath(file); return ctx.getBuiltins().nothing(); } } 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 3c630f634de5..ee751f06578f 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 @@ -63,7 +63,7 @@ * The language context is the internal state of the language that is associated with each thread in * a running Enso program. */ -public class EnsoContext { +public final class EnsoContext { private static final TruffleLanguage.ContextReference REFERENCE = TruffleLanguage.ContextReference.create(EnsoLanguage.class); @@ -389,7 +389,7 @@ public Optional findModuleByExpressionId(UUID expressionId) { * @param file the file to register */ @TruffleBoundary - public void addToHostClassPath(TruffleFile file) { + public void addToClassPath(TruffleFile file) { if (findGuestJava() == null) { environment.addToHostClassPath(file); } else { diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala index 9fceeb762e4c..5d2b2a9277ad 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala @@ -181,7 +181,7 @@ private class DefaultPackageRepository( isLibrary: Boolean ): Unit = { val extensions = pkg.listPolyglotExtensions("java") - extensions.foreach(context.addToHostClassPath) + extensions.foreach(context.addToClassPath) val (regularModules, syntheticModulesMetadata) = pkg .listSources() From fd23ea394ece33b8b4081168737406cf6c21c0f4 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Tue, 19 Sep 2023 08:24:51 +0200 Subject: [PATCH 14/15] Warn just before building the native image with Espresso support --- build.sbt | 3 --- project/NativeImage.scala | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 94627bf91616..9868000eaf0e 100644 --- a/build.sbt +++ b/build.sbt @@ -1709,9 +1709,6 @@ lazy val `engine-runner` = project .getLanguages() .containsKey("java") ) { - System.out.println( - "Building engine `runner` image with experimental Espresso support!" - ) Seq( "-Dorg.graalvm.launcher.home=" + System.getProperty( "java.home" diff --git a/project/NativeImage.scala b/project/NativeImage.scala index d4a64e31aee3..e1801d1d0d06 100644 --- a/project/NativeImage.scala +++ b/project/NativeImage.scala @@ -101,6 +101,12 @@ object NativeImage { "because Native Image component was not found." ) } + if (additionalOptions.contains("--language:java")) { + log.warn( + s"Building ${artifactName} image with experimental Espresso support!" + ) + + } val debugParameters = if (includeDebugInfo) Seq("-H:GenerateDebugInfo=1") else Seq() From 814848320eabf8ad091adb21c8dda6ee0e2f3c52 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Tue, 19 Sep 2023 08:38:17 +0200 Subject: [PATCH 15/15] Hint proper usage of the environment variable --- .../src/main/java/org/enso/interpreter/runtime/EnsoContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ee751f06578f..50c271dfb6ff 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 @@ -475,7 +475,7 @@ private Object findGuestJava() throws IllegalStateException { } } } else { - throw new IllegalStateException("Unsupported value of ENSO_JAVA environment variable: " + envJava); + throw new IllegalStateException("Specify ENSO_JAVA=espresso to use Espresso. Was: " + envJava); } return guestJava; }