From 4b65e44ef3be32f9f0f1d2f94c5faa90476a3a32 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 15 Dec 2023 13:31:32 +0100 Subject: [PATCH] EpbLanguage re-uses other TruffleContext support to run tests with assertions enabled (#7882) --- build.sbt | 20 +- build/build/src/enso.rs | 4 +- .../org/enso/polyglot/ForeignLanguage.java | 47 - .../pass/desugar/GenerateMethodBodies.scala | 5 +- .../epb/{node => }/CoercePrimitiveNode.java | 4 +- .../org/enso/interpreter/epb/EpbContext.java | 101 +- .../org/enso/interpreter/epb/EpbLanguage.java | 35 +- .../org/enso/interpreter/epb/EpbParser.java | 40 - .../interpreter/epb/ExceptionForeignNode.java | 14 + .../enso/interpreter/epb/ForeignEvalNode.java | 107 ++ .../{node => }/ForeignFunctionCallNode.java | 4 +- .../ForeignParsingException.java | 4 +- .../interpreter/epb/GenericForeignNode.java | 22 + .../enso/interpreter/epb/JsForeignNode.java | 95 ++ .../epb/{node => }/PyForeignNode.java | 107 +- .../epb/node/ContextRewrapExceptionNode.java | 55 - .../epb/node/ContextRewrapNode.java | 123 -- .../interpreter/epb/node/ForeignEvalNode.java | 191 --- .../interpreter/epb/node/JsForeignNode.java | 43 - .../interpreter/epb/node/RForeignNode.java | 22 - .../interpreter/epb/node/ReadOnlyArray.java | 96 -- .../epb/runtime/GuardedTruffleContext.java | 85 -- .../epb/runtime/PolyglotExceptionProxy.java | 61 - .../epb/runtime/PolyglotProxy.java | 1167 ----------------- .../epb/EpbLanguageDependenciesTest.java | 49 + .../interpreter/epb/ForeignEvalNodeTest.java | 29 + .../test/ForeignMethodInvokeTest.java | 69 +- ...va => VerifyLanguageAvailabilityTest.java} | 24 +- .../org/enso/interpreter/EnsoLanguage.java | 5 +- .../FunctionCallInstrumentationNode.java | 11 +- .../expression/builtin/meta/Instrumentor.java | 13 +- .../builtin/meta/InstrumentorEvalNode.java | 22 +- .../builtin/number/integer/PowNode.java | 18 +- .../enso/interpreter/runtime/EnsoContext.java | 4 +- .../runtime/data/vector/Array.java | 13 +- .../runtime/data/vector/ArrayBuilder.java | 2 +- .../runtime/data/vector/ArrayLikeAtNode.java | 6 +- .../runtime/data/vector/ArrayLikeHelpers.java | 13 +- .../runtime/scope/DebugLocalScope.java | 6 +- .../interpreter/runtime/IrToTruffle.scala | 8 +- .../benchmarks/processor/BenchProcessor.java | 10 +- .../table/data/column/builder/Builder.java | 2 +- test/Benchmarks/src/Sieve/Sieve_Ascribed.enso | 13 +- .../src/Sieve/Sieve_Java_Script.enso | 11 +- test/Benchmarks/src/Sieve/Sieve_Original.enso | 13 +- .../src/Sieve/Sieve_Without_Types.enso | 15 +- .../src/Semantic/Python_Interop_Spec.enso | 8 + 47 files changed, 635 insertions(+), 2181 deletions(-) delete mode 100644 engine/polyglot-api/src/main/java/org/enso/polyglot/ForeignLanguage.java rename engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/{node => }/CoercePrimitiveNode.java (95%) delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbParser.java create mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ExceptionForeignNode.java create mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignEvalNode.java rename engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/{node => }/ForeignFunctionCallNode.java (83%) rename engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/{runtime => }/ForeignParsingException.java (94%) create mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/GenericForeignNode.java create mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/JsForeignNode.java rename engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/{node => }/PyForeignNode.java (83%) delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapExceptionNode.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapNode.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ForeignEvalNode.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/JsForeignNode.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/RForeignNode.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/GuardedTruffleContext.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotExceptionProxy.java delete mode 100644 engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java create mode 100644 engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/EpbLanguageDependenciesTest.java create mode 100644 engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/ForeignEvalNodeTest.java rename engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/instrument/{VerifyJavaScriptIsAvailableTest.java => VerifyLanguageAvailabilityTest.java} (77%) diff --git a/build.sbt b/build.sbt index 2e6bae28ff2..829e7dd5642 100644 --- a/build.sbt +++ b/build.sbt @@ -1178,11 +1178,6 @@ val truffleRunOpts = Seq( "-Dpolyglot.compiler.BackgroundCompilation=false" ) -val truffleRunOptionsNoAssertSettings = Seq( - fork := true, - javaOptions ++= benchOnlyOptions -) - val truffleRunOptionsSettings = Seq( fork := true, javaOptions ++= "-ea" +: benchOnlyOptions @@ -1378,11 +1373,20 @@ lazy val instrumentationSettings = frgaalJavaCompilerSetting ++ Seq( lazy val `runtime-language-epb` = (project in file("engine/runtime-language-epb")) .settings( + frgaalJavaCompilerSetting, inConfig(Compile)(truffleRunOptionsSettings), truffleDslSuppressWarnsSetting, - instrumentationSettings + commands += WithDebugCommand.withDebug, + fork := true, + Test / javaOptions ++= Seq(), + instrumentationSettings, + libraryDependencies ++= Seq( + "junit" % "junit" % junitVersion % Test, + "com.github.sbt" % "junit-interface" % junitIfVersion % Test, + "org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided", + "org.graalvm.truffle" % "truffle-dsl-processor" % graalMavenPackagesVersion % "provided" + ) ) - .dependsOn(`polyglot-api`) lazy val runtime = (project in file("engine/runtime")) .configs(Benchmark) @@ -1730,7 +1734,7 @@ lazy val `runtime-with-polyglot` = .configs(Benchmark) .settings( frgaalJavaCompilerSetting, - inConfig(Compile)(truffleRunOptionsNoAssertSettings), + inConfig(Compile)(truffleRunOptionsSettings), inConfig(Benchmark)(Defaults.testSettings), commands += WithDebugCommand.withDebug, Benchmark / javacOptions --= Seq( diff --git a/build/build/src/enso.rs b/build/build/src/enso.rs index 40d2e27721c..b6c9a0b150f 100644 --- a/build/build/src/enso.rs +++ b/build/build/src/enso.rs @@ -30,7 +30,7 @@ impl From for Boolean { } ide_ci::define_env_var! { - ENSO_JVM_OPTS, String; + JAVA_OPTS, String; ENSO_BENCHMARK_TEST_DRY_RUN, Boolean; } @@ -88,7 +88,7 @@ impl BuiltEnso { .arg(test_path) // This flag enables assertions in the JVM. Some of our stdlib tests had in the past // failed on Graal/Truffle assertions, so we want to have them triggered. - .set_env(ENSO_JVM_OPTS, &ide_ci::programs::java::Option::EnableAssertions.as_ref())?; + .set_env(JAVA_OPTS, &ide_ci::programs::java::Option::EnableAssertions.as_ref())?; Ok(command) } diff --git a/engine/polyglot-api/src/main/java/org/enso/polyglot/ForeignLanguage.java b/engine/polyglot-api/src/main/java/org/enso/polyglot/ForeignLanguage.java deleted file mode 100644 index 857ded4a7e9..00000000000 --- a/engine/polyglot-api/src/main/java/org/enso/polyglot/ForeignLanguage.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.enso.polyglot; - -import com.oracle.truffle.api.source.Source; -import java.util.Arrays; - -/** Lists all the languages supported in polyglot eval. */ -public enum ForeignLanguage { - JS("js", "js"), - PY("python", "python"), - R("R", "r"); - - public static final String ID = "epb"; - - private final String truffleId; - private final String syntacticTag; - - ForeignLanguage(String truffleId, String syntacticTag) { - this.truffleId = truffleId; - this.syntacticTag = syntacticTag; - } - - /** @return a Truffle language ID associated with this language */ - public String getTruffleId() { - return truffleId; - } - - /** - * Transforms an Enso-side syntactic language tag into a recognized language object. - * - * @param tag the tag to parse - * @return a corresponding language value, or null if the language is not recognized - */ - public static ForeignLanguage getBySyntacticTag(String tag) { - return Arrays.stream(values()).filter(l -> l.syntacticTag.equals(tag)).findFirst().orElse(null); - } - - /** - * Builds a new source instance that can later be parsed by this class. - * - * @param foreignSource the foreign source to evaluate - * @param name the name of the source - * @return a source instance, parsable by the EPB language - */ - public Source buildSource(String foreignSource, String name) { - return Source.newBuilder(ID, this + "#" + foreignSource, name).build(); - } -} diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/GenerateMethodBodies.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/GenerateMethodBodies.scala index 6592cc3c6ab..24150a39199 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/GenerateMethodBodies.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/desugar/GenerateMethodBodies.scala @@ -22,7 +22,6 @@ import org.enso.compiler.pass.analyse.{ } import org.enso.compiler.pass.lint.UnusedBindings import org.enso.compiler.pass.optimise.LambdaConsolidate -import org.enso.polyglot.ForeignLanguage import scala.annotation.tailrec @@ -161,7 +160,7 @@ case object GenerateMethodBodies extends IRPass { case lam @ Function.Lambda(_, body, _, _, _, _) if findForeignDefinition( body, - lang = Some(ForeignLanguage.JS) + lang = Some("js") ).isDefined => val thisArgs = chainedFunctionArgs.collect { case (arg, idx) if arg.name.name == "this" => @@ -308,7 +307,7 @@ case object GenerateMethodBodies extends IRPass { @tailrec private def findForeignDefinition( body: Expression, - lang: Option[ForeignLanguage] + lang: Option[String] ): Option[Foreign.Definition] = { body match { case foreignDef: Foreign.Definition => diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/CoercePrimitiveNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/CoercePrimitiveNode.java similarity index 95% rename from engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/CoercePrimitiveNode.java rename to engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/CoercePrimitiveNode.java index ed0d4996c20..9af420365a7 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/CoercePrimitiveNode.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/CoercePrimitiveNode.java @@ -1,4 +1,4 @@ -package org.enso.interpreter.epb.node; +package org.enso.interpreter.epb; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateUncached; @@ -10,7 +10,7 @@ import com.oracle.truffle.api.strings.TruffleString; @GenerateUncached @ReportPolymorphism -public abstract class CoercePrimitiveNode extends Node { +abstract class CoercePrimitiveNode extends Node { /** * Create a new node responsible for coercing primitive values to Enso primitives at the polyglot diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java index 79c97d70a76..b6ab74548dc 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java @@ -1,19 +1,18 @@ package org.enso.interpreter.epb; -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.nodes.Node; -import java.util.concurrent.CountDownLatch; -import java.util.function.Consumer; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; -import org.enso.interpreter.epb.runtime.GuardedTruffleContext; /** * A context for {@link EpbLanguage}. Provides access to both isolated Truffle contexts used in * polyglot execution. */ -public class EpbContext { +final class EpbContext { private static final TruffleLanguage.ContextReference REFERENCE = TruffleLanguage.ContextReference.create(EpbLanguage.class); @@ -21,18 +20,19 @@ public class EpbContext { private static final String INNER_OPTION = "isEpbInner"; private final boolean isInner; private final TruffleLanguage.Env env; - private @CompilerDirectives.CompilationFinal GuardedTruffleContext innerContext; - private final GuardedTruffleContext currentContext; + private @CompilationFinal TruffleContext innerContext; + private final ReentrantLock lock = new ReentrantLock(); + private final TruffleLogger log; /** * Creates a new instance of this context. * * @param env the current language environment. */ - public EpbContext(TruffleLanguage.Env env) { + EpbContext(TruffleLanguage.Env env) { this.env = env; isInner = env.getConfig().get(INNER_OPTION) != null; - currentContext = new GuardedTruffleContext(env.getContext(), isInner); + this.log = env.getLogger(EpbContext.class); } /** @@ -45,58 +45,15 @@ public class EpbContext { if (!isInner) { if (innerContext == null) { innerContext = - new GuardedTruffleContext( - env.newInnerContextBuilder() - .initializeCreatorContext(true) - .inheritAllAccess(true) - .config(INNER_OPTION, "yes") - .build(), - true); + env.newInnerContextBuilder() + .initializeCreatorContext(true) + .inheritAllAccess(true) + .config(INNER_OPTION, "yes") + .build(); } - initializeLanguages(env, innerContext, preInitializeLanguages); } } - private static void initializeLanguages( - TruffleLanguage.Env environment, GuardedTruffleContext innerContext, String langs) { - if (langs == null || langs.isEmpty()) { - return; - } - var log = environment.getLogger(EpbContext.class); - log.log(Level.FINE, "Initializing languages {0}", langs); - var cdl = new CountDownLatch(1); - var run = - (Consumer) - (context) -> { - var lock = innerContext.enter(null); - try { - log.log(Level.FINEST, "Entering initialization thread"); - cdl.countDown(); - for (var l : langs.split(",")) { - log.log(Level.FINEST, "Initializing language {0}", l); - long then = System.currentTimeMillis(); - var res = context.initializeInternal(null, l); - long took = System.currentTimeMillis() - then; - log.log( - Level.FINE, - "Done initializing language {0} with {1} in {2} ms", - new Object[] {l, res, took}); - } - } finally { - innerContext.leave(null, lock); - } - }; - var init = innerContext.createThread(environment, run); - log.log(Level.FINEST, "Starting initialization thread"); - init.start(); - try { - cdl.await(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - log.log(Level.FINEST, "Initializing on background"); - } - /** * @param node the location of context access. Pass {@code null} if not in a node. * @return the proper context instance for the current {@link @@ -106,30 +63,16 @@ public class EpbContext { return REFERENCE.get(node); } - /** - * Checks if this context corresponds to the inner Truffle context. - * - * @return true if run in the inner Truffle context, false otherwise. - */ - public boolean isInner() { - return isInner; - } - - /** - * @return the inner Truffle context handle if called from the outer context, or null if called in - * the inner context. - */ - public GuardedTruffleContext getInnerContext() { - return innerContext; - } - - /** @return returns the currently entered Truffle context handle. */ - public GuardedTruffleContext getCurrentContext() { - return currentContext; - } - /** @return the language environment associated with this context. */ public TruffleLanguage.Env getEnv() { return env; } + + public TruffleContext getInnerContext() { + return innerContext; + } + + public void log(Level level, String msg, Object... args) { + this.log.log(level, msg, args); + } } diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbLanguage.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbLanguage.java index a5306ed7460..608dc355e63 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbLanguage.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbLanguage.java @@ -3,41 +3,17 @@ package org.enso.interpreter.epb; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.TruffleLanguage; import java.util.function.Consumer; -import org.enso.interpreter.epb.node.ContextRewrapNode; -import org.enso.interpreter.epb.node.ForeignEvalNode; -import org.enso.polyglot.ForeignLanguage; -/** - * An internal language that serves as a bridge between Enso and other supported languages. - * - *

Truffle places a lot of emphasis on safety guarantees, which also means that single-threaded - * languages cannot easily be called from multiple threads. We circumvent this by using two separate - * {@link com.oracle.truffle.api.TruffleContext}s, one (often referred to as "outer") is allowed to - * run Enso, Host Java, and possibly other thread-ready languages. Languages that cannot safely run - * in a multithreaded environment are relegated to the other context (referred to as "inner"). The - * inner context provides a GIL capability, ensuring that access to the single-threaded languages is - * serialized. - * - *

This imposes certain limitations on data interchange between the contexts. In particular, it - * is impossible to execute origin language's code when executing in the other context. Therefore - * outer context values need to be specially wrapped before being passed (e.g. as arguments) to the - * inner context, and inner context values need rewrapping for use in the outer context. See {@link - * org.enso.interpreter.epb.runtime.PolyglotProxy} and {@link ContextRewrapNode} for details of how - * and when this wrapping is done. - * - *

With the structure outlined above, EPB is the only language that is initialized in both inner - * and outer contexts and thus it is very minimal. Its only role is to manage both contexts and - * provide context-switching facilities. - */ +/** An internal language that serves as a bridge between Enso and other supported languages. */ @TruffleLanguage.Registration( - id = ForeignLanguage.ID, + id = "epb", name = "Enso Polyglot Bridge", characterMimeTypes = {EpbLanguage.MIME}, internal = true, defaultMimeType = EpbLanguage.MIME, contextPolicy = TruffleLanguage.ContextPolicy.SHARED, services = Consumer.class) -public class EpbLanguage extends TruffleLanguage { +public final class EpbLanguage extends TruffleLanguage { public static final String MIME = "application/epb"; @Override @@ -55,9 +31,8 @@ public class EpbLanguage extends TruffleLanguage { @Override protected CallTarget parse(ParsingRequest request) { - EpbParser.Result code = EpbParser.parse(request.getSource()); - ForeignEvalNode foreignEvalNode = ForeignEvalNode.build(this, code, request.getArgumentNames()); - return foreignEvalNode.getCallTarget(); + var node = ForeignEvalNode.parse(this, request.getSource(), request.getArgumentNames()); + return node.getCallTarget(); } @Override diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbParser.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbParser.java deleted file mode 100644 index 21c4ca625f4..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbParser.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.enso.interpreter.epb; - -import com.oracle.truffle.api.source.Source; -import org.enso.polyglot.ForeignLanguage; - -/** A class containing helpers for creating and parsing EPB code */ -public class EpbParser { - /** A parsing result. */ - public static class Result { - private final ForeignLanguage language; - private final String foreignSource; - - private Result(ForeignLanguage language, String foreignSource) { - this.language = language; - this.foreignSource = foreignSource; - } - - /** @return the foreign language code to eval */ - public String getForeignSource() { - return foreignSource; - } - - /** @return the foreign language in which the source is written */ - public ForeignLanguage getLanguage() { - return language; - } - } - - /** - * Parses an EPB source - * - * @param source the source to parse - * @return the result of parsing - */ - public static Result parse(Source source) { - String src = source.getCharacters().toString(); - String[] langAndCode = src.split("#", 2); - return new Result(ForeignLanguage.valueOf(langAndCode[0]), langAndCode[1]); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ExceptionForeignNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ExceptionForeignNode.java new file mode 100644 index 00000000000..50e4e96efd0 --- /dev/null +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ExceptionForeignNode.java @@ -0,0 +1,14 @@ +package org.enso.interpreter.epb; + +final class ExceptionForeignNode extends ForeignFunctionCallNode { + private final ForeignParsingException ex; + + ExceptionForeignNode(ForeignParsingException ex) { + this.ex = ex; + } + + @Override + public Object execute(Object[] arguments) { + throw ex; + } +} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignEvalNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignEvalNode.java new file mode 100644 index 00000000000..078451d6c79 --- /dev/null +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignEvalNode.java @@ -0,0 +1,107 @@ +package org.enso.interpreter.epb; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.stream.Collectors; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropException; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; + +final class ForeignEvalNode extends RootNode { + private final Source langAndCode; + private @Child ForeignFunctionCallNode foreign; + private final String[] argNames; + + private ForeignEvalNode(EpbLanguage language, Source langAndCode, List arguments) { + super(language, new FrameDescriptor()); + this.langAndCode = langAndCode; + this.argNames = arguments.toArray(new String[0]); + } + + static ForeignEvalNode parse(EpbLanguage epb, Source langAndCode, List args) { + var node = new ForeignEvalNode(epb, langAndCode, args); + return node; + } + + private String truffleId(Source langAndCode) { + var seq = langAndCode.getCharacters(); + return seq.subSequence(0, splitAt(seq)).toString().toLowerCase(); + } + + private int splitAt(CharSequence seq) { + var at = 0; + while (at < seq.length()) { + if (seq.charAt(at) == '#') { + return at; + } + at++; + } + throw new ForeignParsingException("No # found", this); + } + + private String foreignSource(Source langAndCode) { + var seq = langAndCode.getCharacters(); + return seq.toString().substring(splitAt(seq) + 1); + } + + @Override + public Object execute(VirtualFrame frame) { + if (foreign == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + var id = truffleId(langAndCode); + var context = EpbContext.get(this); + var installedLanguages = context.getEnv().getInternalLanguages(); + var node = switch (installedLanguages.containsKey(id) ? 1 : 0) { + case 0 -> { + var ex = new ForeignParsingException(id, installedLanguages.keySet(), this); + yield new ExceptionForeignNode(ex); + } + default -> { + context.log(Level.FINE, "Parsing foreign script {1} - language {0}", id, langAndCode.getName()); + yield switch (id) { + case "js" -> parseJs(); + case "python" -> parseGeneric("python", PyForeignNode::new); + default -> parseGeneric(id, GenericForeignNode::new); + }; + } + }; + foreign = insert(node); + } + try { + var toRet = foreign.execute(frame.getArguments()); + return toRet; + } catch (InteropException ex) { + throw new ForeignParsingException(ex.getMessage(), this); + } + } + + private ForeignFunctionCallNode parseJs() { + var context = EpbContext.get(this); + var inner = context.getInnerContext(); + var code = foreignSource(langAndCode); + var args = Arrays.stream(argNames).skip(1).collect(Collectors.joining(",")); + var wrappedSrc + = "var poly_enso_eval=function(" + + args + + "){\n" + + code + + "\n};poly_enso_eval"; + Source source = Source.newBuilder("js", wrappedSrc, "").build(); + var fn = inner.evalPublic(this, source); + return JsForeignNode.build(fn); + } + + private ForeignFunctionCallNode parseGeneric(String language, Function nodeFactory) { + var ctx = EpbContext.get(this); + Source source = Source.newBuilder(language, foreignSource(langAndCode), langAndCode.getName()).build(); + CallTarget ct = ctx.getEnv().parsePublic(source, argNames); + return nodeFactory.apply(ct); + } +} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ForeignFunctionCallNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignFunctionCallNode.java similarity index 83% rename from engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ForeignFunctionCallNode.java rename to engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignFunctionCallNode.java index 5bac79aa02c..14fca257a39 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ForeignFunctionCallNode.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignFunctionCallNode.java @@ -1,10 +1,10 @@ -package org.enso.interpreter.epb.node; +package org.enso.interpreter.epb; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.nodes.Node; /** An interface for nodes responsible for calling into foreign languages. */ -public abstract class ForeignFunctionCallNode extends Node { +abstract class ForeignFunctionCallNode extends Node { /** * Executes the foreign call. * diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/ForeignParsingException.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignParsingException.java similarity index 94% rename from engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/ForeignParsingException.java rename to engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignParsingException.java index dfa1800b6a2..f3d1e3bb42d 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/ForeignParsingException.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/ForeignParsingException.java @@ -1,4 +1,4 @@ -package org.enso.interpreter.epb.runtime; +package org.enso.interpreter.epb; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.exception.AbstractTruffleException; @@ -13,7 +13,7 @@ import java.util.Set; * language is not installed, or enabled, in the Truffle engine. */ @ExportLibrary(InteropLibrary.class) -public class ForeignParsingException extends AbstractTruffleException { +class ForeignParsingException extends AbstractTruffleException { private final String message; /** diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/GenericForeignNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/GenericForeignNode.java new file mode 100644 index 00000000000..42fdccf76f8 --- /dev/null +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/GenericForeignNode.java @@ -0,0 +1,22 @@ +package org.enso.interpreter.epb; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.interop.InteropException; +import com.oracle.truffle.api.nodes.DirectCallNode; + +class GenericForeignNode extends ForeignFunctionCallNode { + private @Child DirectCallNode callNode; + private @Child CoercePrimitiveNode coerceNode; + + GenericForeignNode(CallTarget ct) { + callNode = DirectCallNode.create(ct); + coerceNode = CoercePrimitiveNode.build(); + } + + @Override + public Object execute(Object[] arguments) throws InteropException { + var rawResult = callNode.call(arguments); + var res = coerceNode.execute(rawResult); + return res; + } +} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/JsForeignNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/JsForeignNode.java new file mode 100644 index 00000000000..5634e306087 --- /dev/null +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/JsForeignNode.java @@ -0,0 +1,95 @@ +package org.enso.interpreter.epb; + +import com.oracle.truffle.api.dsl.NodeField; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import java.util.Arrays; + +/** A node responsible for performing foreign JS calls. */ +@NodeField(name = "foreignFunction", type = Object.class) +abstract class JsForeignNode extends ForeignFunctionCallNode { + + private @Child CoercePrimitiveNode coercePrimitiveNode = CoercePrimitiveNode.build(); + + abstract Object getForeignFunction(); + + /** + * Creates a new instance of this node. + * + * @param jsFunction the parsed JS object (required to be {@link + * InteropLibrary#isExecutable(Object)}) + * @return a node able to call the JS function with given arguments + */ + static JsForeignNode build(Object jsFunction) { + return JsForeignNodeGen.create(jsFunction); + } + + @Specialization + Object doExecute(Object[] arguments, @CachedLibrary("foreignFunction") InteropLibrary iop) + throws InteropException { + var args = new ArgumentsArray(arguments); + var self = arguments[0]; + var raw = iop.invokeMember(getForeignFunction(), "apply", self, args); + return coercePrimitiveNode.execute(raw); + } + + @ExportLibrary(InteropLibrary.class) + static final class ArgumentsArray implements TruffleObject { + private static final int OFFSET = 1; + private final Object[] items; + + public ArgumentsArray(Object... items) { + this.items = items; + } + + @ExportMessage + public boolean hasArrayElements() { + return true; + } + + @ExportMessage + public Object readArrayElement(long index) throws InvalidArrayIndexException { + if (index >= getArraySize() || index < 0) { + throw InvalidArrayIndexException.create(index); + } + return items[OFFSET + (int) index]; + } + + @ExportMessage + long getArraySize() { + return items.length - OFFSET; + } + + @ExportMessage + boolean isArrayElementReadable(long index) { + return index < getArraySize() && index >= 0; + } + + @ExportMessage + void writeArrayElement(long index, Object value) throws UnsupportedMessageException { + throw UnsupportedMessageException.create(); + } + + @ExportMessage + boolean isArrayElementModifiable(long index) { + return false; + } + + @ExportMessage + boolean isArrayElementInsertable(long index) { + return false; + } + + @Override + public String toString() { + return Arrays.toString(items); + } + } +} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/PyForeignNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/PyForeignNode.java similarity index 83% rename from engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/PyForeignNode.java rename to engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/PyForeignNode.java index b479cf5b710..81e2a1f7c20 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/PyForeignNode.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/PyForeignNode.java @@ -1,14 +1,11 @@ -package org.enso.interpreter.epb.node; +package org.enso.interpreter.epb; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; -import org.enso.interpreter.epb.EpbContext; - +import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.dsl.NodeField; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; @@ -21,10 +18,7 @@ import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.source.Source; -@NodeField(name = "foreignFunction", type = Object.class) -public abstract class PyForeignNode extends ForeignFunctionCallNode { - @Child - private CoercePrimitiveNode coercePrimitiveNode = CoercePrimitiveNode.build(); +final class PyForeignNode extends GenericForeignNode { @CompilerDirectives.CompilationFinal private Object fnPythonDate; @Child @@ -39,10 +33,72 @@ public abstract class PyForeignNode extends ForeignFunctionCallNode { private InteropLibrary nodePythonZone; @CompilerDirectives.CompilationFinal private Object fnPythonCombine; + @CompilerDirectives.CompilationFinal + private Object none; @Child private InteropLibrary nodePythonCombine; + @Child + private InteropLibrary iop = InteropLibrary.getFactory().createDispatched(3); - abstract Object getForeignFunction(); + PyForeignNode(CallTarget ct) { + super(ct); + } + + @Override + public Object execute(Object[] arguments) throws InteropException { + // initialize venv by importing site + none(); + + for (int i = 0; i < arguments.length; i++) { + var javaTime = iop.isTime(arguments[i]) ? iop.asTime(arguments[i]) : null; + var time = javaTime != null ? wrapPythonTime(javaTime) : null; + var javaDate = iop.isDate(arguments[i]) ? iop.asDate(arguments[i]) : null; + var date = javaDate != null ? wrapPythonDate(javaDate) : null; + var zone = iop.isTimeZone(arguments[i]) ? wrapPythonZone(iop.asTimeZone(arguments[i]), javaTime, javaDate) : null; + if (date != null && time != null) { + arguments[i] = combinePythonDateTimeZone(date, time, zone); + } else if (date != null) { + arguments[i] = date; + } else if (time != null) { + arguments[i] = time; + } else if (zone != null) { + arguments[i] = zone; + } + // Enso's Text type should be converted to TruffleString before sent to python, because + // python does not tend to use InteropLibrary, so any Text object would be treated as + // a foreign object, rather than 'str' type. + if (iop.isString(arguments[i])) { + arguments[i] = iop.asTruffleString(arguments[i]); + } + } + var res = super.execute(arguments); + if (iop.hasMetaObject(res)) { + var meta = iop.getMetaObject(res); + var name = iop.getMetaQualifiedName(meta); + if ("module".equals(name)) { + return none(); + } + } + return res; + } + + private Object none() throws UnsupportedTypeException, ArityException, UnsupportedMessageException { + if (none == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + var ctx = EpbContext.get(this); + var src = Source.newBuilder("python", """ + import site + def nothing(): + return None + nothing + """, "nothing.py").build(); + var nothingFn = ctx.getEnv().parsePublic(src).call(); + assert InteropLibrary.getUncached().isExecutable(nothingFn); + none = InteropLibrary.getUncached().execute(nothingFn); + assert InteropLibrary.getUncached().isNull(none); + } + return none; + } private Object wrapPythonDate(LocalDate date) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { if (nodePythonDate == null) { @@ -129,37 +185,6 @@ public abstract class PyForeignNode extends ForeignFunctionCallNode { } } - @Specialization - public Object doExecute( - Object[] arguments, - @CachedLibrary("foreignFunction") InteropLibrary interopLibrary, - @CachedLibrary(limit = "3") InteropLibrary iop - ) throws InteropException { - for (int i = 0; i < arguments.length; i++) { - var javaTime = iop.isTime(arguments[i]) ? iop.asTime(arguments[i]) : null; - var time = javaTime != null ? wrapPythonTime(javaTime) : null; - var javaDate = iop.isDate(arguments[i]) ? iop.asDate(arguments[i]) : null; - var date = javaDate != null ? wrapPythonDate(javaDate) : null; - var zone = iop.isTimeZone(arguments[i]) ? wrapPythonZone(iop.asTimeZone(arguments[i]), javaTime, javaDate) : null; - if (date != null && time != null) { - arguments[i] = combinePythonDateTimeZone(date, time, zone); - } else if (date != null) { - arguments[i] = date; - } else if (time != null) { - arguments[i] = time; - } else if (zone != null) { - arguments[i] = zone; - } - // Enso's Text type should be converted to TruffleString before sent to python, because - // python does not tend to use InteropLibrary, so any Text object would be treated as - // a foreign object, rather than 'str' type. - if (iop.isString(arguments[i])) { - arguments[i] = iop.asTruffleString(arguments[i]); - } - } - return coercePrimitiveNode.execute(interopLibrary.execute(getForeignFunction(), arguments)); - } - @ExportLibrary(InteropLibrary.class) static final class ZoneWrapper implements TruffleObject { diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapExceptionNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapExceptionNode.java deleted file mode 100644 index 7cff5fc6018..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapExceptionNode.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.enso.interpreter.epb.node; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.GenerateUncached; -import com.oracle.truffle.api.dsl.ReportPolymorphism; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.exception.AbstractTruffleException; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.epb.runtime.GuardedTruffleContext; -import org.enso.interpreter.epb.runtime.PolyglotExceptionProxy; - -@GenerateUncached -@ReportPolymorphism -public abstract class ContextRewrapExceptionNode extends Node { - - /** - * Create a new context rewrap exception node. - * - * @return a new context rewrap exception node - */ - public static ContextRewrapExceptionNode build() { - return ContextRewrapExceptionNodeGen.create(); - } - - /** - * Wraps a value originating from {@code origin} into a value valid in {@code target}. This method - * is allowed to use interop library on {@code value} and therefore must be called with {@code - * origin} entered. - * - * @param value the value to wrap - * @param origin the context the value originates in (and is currently entered) - * @param target the context in which the value will be accessed in the future - * @return a context-switch-safe wrapper for the value - */ - public abstract AbstractTruffleException execute( - AbstractTruffleException value, GuardedTruffleContext origin, GuardedTruffleContext target); - - @Specialization(guards = "proxy.getOrigin() == target") - AbstractTruffleException doUnwrapProxy( - PolyglotExceptionProxy proxy, GuardedTruffleContext origin, GuardedTruffleContext target) { - return proxy.getOriginal(); - } - - @Specialization(guards = "proxy.getTarget() == target") - AbstractTruffleException doAlreadyProxied( - PolyglotExceptionProxy proxy, GuardedTruffleContext origin, GuardedTruffleContext target) { - return proxy; - } - - @Fallback - AbstractTruffleException doWrapProxy( - AbstractTruffleException o, GuardedTruffleContext origin, GuardedTruffleContext target) { - return new PolyglotExceptionProxy(o, origin, target); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapNode.java deleted file mode 100644 index d93dc8e26f5..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ContextRewrapNode.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.enso.interpreter.epb.node; - -import com.oracle.truffle.api.dsl.Cached.Exclusive; -import com.oracle.truffle.api.dsl.Cached.Shared; -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.GenerateUncached; -import com.oracle.truffle.api.dsl.ReportPolymorphism; -import com.oracle.truffle.api.dsl.Specialization; -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.nodes.Node; -import org.enso.interpreter.epb.runtime.GuardedTruffleContext; -import org.enso.interpreter.epb.runtime.PolyglotProxy; - -@GenerateUncached -@ReportPolymorphism -public abstract class ContextRewrapNode extends Node { - - /** - * Create a new context rewrap node. - * - * @return a new context rewrap node. - */ - static ContextRewrapNode build() { - return ContextRewrapNodeGen.create(); - } - - /** - * Wraps a value originating from {@code origin} into a value valid in {@code target}. This method - * is allowed to use interop library on {@code value} and therefore must be called with {@code - * origin} entered. - * - * @param value the value to wrap - * @param origin the context the value originates in (and is currently entered) - * @param target the context in which the value will be accessed in the future - * @return a context-switch-safe wrapper for the value - */ - public abstract Object execute( - Object value, GuardedTruffleContext origin, GuardedTruffleContext target); - - @Specialization - double doDouble(double d, GuardedTruffleContext origin, GuardedTruffleContext target) { - return d; - } - - @Specialization - double doFloat(float d, GuardedTruffleContext origin, GuardedTruffleContext target) { - return d; - } - - @Specialization - long doLong(long i, GuardedTruffleContext origin, GuardedTruffleContext target) { - return i; - } - - @Specialization - long doInt(int i, GuardedTruffleContext origin, GuardedTruffleContext target) { - return i; - } - - @Specialization - boolean doBoolean(boolean b, GuardedTruffleContext origin, GuardedTruffleContext target) { - return b; - } - - @Specialization(guards = "proxy.getOrigin() == target") - Object doUnwrapProxy( - PolyglotProxy proxy, GuardedTruffleContext origin, GuardedTruffleContext target) { - return proxy.getDelegate(); - } - - @Specialization(guards = "proxy.getTarget() == target") - Object doAlreadyProxied( - PolyglotProxy proxy, GuardedTruffleContext origin, GuardedTruffleContext target) { - return proxy; - } - - @Specialization(guards = "bools.isBoolean(b)") - boolean doWrappedBoolean( - Object b, - GuardedTruffleContext origin, - GuardedTruffleContext target, - @Exclusive @CachedLibrary(limit = "5") InteropLibrary bools) { - try { - return bools.asBoolean(b); - } catch (UnsupportedMessageException e) { - throw new IllegalStateException("Impossible, `b` is checked to be a boolean"); - } - } - - @Specialization(guards = {"numbers.isNumber(l)", "numbers.fitsInLong(l)"}) - long doWrappedLong( - Object l, - GuardedTruffleContext origin, - GuardedTruffleContext target, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary numbers) { - try { - return numbers.asLong(l); - } catch (UnsupportedMessageException e) { - throw new IllegalStateException("Impossible, `l` is checked to be a long"); - } - } - - @Specialization( - guards = {"numbers.isNumber(d)", "!numbers.fitsInLong(d)", "numbers.fitsInDouble(d)"}) - double doWrappedDouble( - Object d, - GuardedTruffleContext origin, - GuardedTruffleContext target, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary numbers) { - try { - return numbers.asDouble(d); - } catch (UnsupportedMessageException e) { - throw new IllegalStateException("Impossible, `l` is checked to be a double"); - } - } - - @Fallback - Object doWrapProxy(Object o, GuardedTruffleContext origin, GuardedTruffleContext target) { - return new PolyglotProxy(o, origin, target); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ForeignEvalNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ForeignEvalNode.java deleted file mode 100644 index 5709c915b7f..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ForeignEvalNode.java +++ /dev/null @@ -1,191 +0,0 @@ -package org.enso.interpreter.epb.node; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.enso.interpreter.epb.EpbContext; -import org.enso.interpreter.epb.EpbLanguage; -import org.enso.interpreter.epb.EpbParser; -import org.enso.interpreter.epb.runtime.ForeignParsingException; -import org.enso.interpreter.epb.runtime.GuardedTruffleContext; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.exception.AbstractTruffleException; -import com.oracle.truffle.api.frame.FrameDescriptor; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.interop.InteropException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.Source; - -public class ForeignEvalNode extends RootNode { - private final EpbParser.Result code; - private @Child ForeignFunctionCallNode foreign; - private @Child ContextRewrapNode rewrapNode = ContextRewrapNode.build(); - - private @Child ContextRewrapExceptionNode rewrapExceptionNode = - ContextRewrapExceptionNode.build(); - private @CompilationFinal ForeignParsingException parseException; - private final String[] argNames; - - /** - * Creates a new instance of this node - * - * @param language the current language instance - * @param code the result of parsing EPB code - * @param arguments argument names allowed in the function body - * @return an instance of this node - */ - public static ForeignEvalNode build( - EpbLanguage language, EpbParser.Result code, List arguments) { - return new ForeignEvalNode(language, code, arguments); - } - - ForeignEvalNode(EpbLanguage language, EpbParser.Result code, List arguments) { - super(language, new FrameDescriptor()); - this.code = code; - argNames = arguments.toArray(new String[0]); - } - - public Object execute(VirtualFrame frame) { - ensureParsed(); - if (foreign != null) { - try { - return foreign.execute(frame.getArguments()); - } catch (InteropException ex) { - throw new ForeignParsingException(ex.getMessage(), this); - } - } else { - CompilerDirectives.transferToInterpreter(); - throw parseException; - } - } - - private void ensureParsed() { - if (foreign == null && parseException == null) { - lockAndParse(); - } - } - - @CompilerDirectives.TruffleBoundary - private void lockAndParse() throws IllegalStateException { - getLock().lock(); - try { - if (foreign == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - var foreignLang = code.getLanguage(); - String truffleLangId = foreignLang.getTruffleId(); - var context = EpbContext.get(this); - var installedLanguages = context.getEnv().getInternalLanguages(); - if (!installedLanguages.containsKey(truffleLangId)) { - this.parseException = - new ForeignParsingException(truffleLangId, installedLanguages.keySet(), this); - } else { - switch (foreignLang) { - case JS: - parseJs(); - break; - case PY: - parsePy(); - break; - case R: - parseR(); - break; - default: - throw new IllegalStateException("Unsupported language resulted from EPB parsing"); - } - } - } - } finally { - getLock().unlock(); - } - } - - private void parseJs() { - EpbContext context = EpbContext.get(this); - GuardedTruffleContext outer = context.getCurrentContext(); - GuardedTruffleContext inner = context.getInnerContext(); - Object p = inner.enter(this); - try { - String args = Arrays.stream(argNames).skip(1).collect(Collectors.joining(",")); - String wrappedSrc = - "var poly_enso_eval=function(" - + args - + "){\n" - + code.getForeignSource() - + "\n};poly_enso_eval"; - Source source = Source.newBuilder(code.getLanguage().getTruffleId(), wrappedSrc, "").build(); - - // After calling inner.enter, operating in a different, isolated truffle instance so need to - // call one with the correct semantics. - CallTarget ct = EpbContext.get(this).getEnv().parsePublic(source); - Object fn = rewrapNode.execute(ct.call(), inner, outer); - foreign = insert(JsForeignNode.build(fn, argNames.length)); - } catch (Throwable e) { - if (InteropLibrary.getUncached().isException(e)) { - throw rewrapExceptionNode.execute((AbstractTruffleException) e, inner, outer); - } else { - throw e; - } - } finally { - inner.leave(this, p); - } - } - - private void parsePy() { - try { - String args = Arrays.stream(argNames).collect(Collectors.joining(",")); - String head = """ - import site - import polyglot - @polyglot.export_value - def polyglot_enso_python_eval(""" - + args - + "):\n"; - String indentLines = - code.getForeignSource().lines().map(l -> " " + l).collect(Collectors.joining("\n")); - Source source = - Source.newBuilder(code.getLanguage().getTruffleId(), head + indentLines, "").build(); - EpbContext context = EpbContext.get(this); - CallTarget ct = context.getEnv().parsePublic(source); - ct.call(); - Object fn = context.getEnv().importSymbol("polyglot_enso_python_eval"); - foreign = insert(PyForeignNodeGen.create(fn)); - } catch (Throwable e) { - if (InteropLibrary.getUncached().isException(e)) { - throw (AbstractTruffleException) e; - } else { - throw e; - } - } - } - - private void parseR() { - EpbContext context = EpbContext.get(this); - GuardedTruffleContext outer = context.getCurrentContext(); - GuardedTruffleContext inner = context.getInnerContext(); - Object p = inner.enter(this); - try { - String args = String.join(",", argNames); - String wrappedSrc = "function(" + args + "){\n" + code.getForeignSource() + "\n}"; - Source source = Source.newBuilder(code.getLanguage().getTruffleId(), wrappedSrc, "").build(); - - // After calling inner.enter, operating in a different, isolated truffle instance so need to - // call one with the correct semantics. - CallTarget ct = EpbContext.get(this).getEnv().parsePublic(source); - Object fn = rewrapNode.execute(ct.call(), inner, outer); - foreign = insert(RForeignNodeGen.create(fn)); - } catch (Throwable e) { - if (InteropLibrary.getUncached().isException(e)) { - throw rewrapExceptionNode.execute((AbstractTruffleException) e, inner, outer); - } else { - throw e; - } - } finally { - inner.leave(this, p); - } - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/JsForeignNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/JsForeignNode.java deleted file mode 100644 index 2117210c105..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/JsForeignNode.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.enso.interpreter.epb.node; - -import com.oracle.truffle.api.dsl.NodeField; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.library.CachedLibrary; - -/** A node responsible for performing foreign JS calls. */ -@NodeField(name = "foreignFunction", type = Object.class) -@NodeField(name = "arity", type = int.class) -public abstract class JsForeignNode extends ForeignFunctionCallNode { - - private @Child CoercePrimitiveNode coercePrimitiveNode = CoercePrimitiveNode.build(); - - abstract Object getForeignFunction(); - - abstract int getArity(); - - /** - * Creates a new instance of this node. - * - * @param argumentsCount the number of arguments the function expects (including {@code this}) - * @param jsFunction the parsed JS object (required to be {@link - * InteropLibrary#isExecutable(Object)}) - * @return a node able to call the JS function with given arguments - */ - public static JsForeignNode build(Object jsFunction, int argumentsCount) { - return JsForeignNodeGen.create(jsFunction, argumentsCount); - } - - @Specialization - Object doExecute( - Object[] arguments, @CachedLibrary("foreignFunction") InteropLibrary interopLibrary) - throws InteropException { - int newLength = getArity() - 1; - Object[] positionalArgs = new Object[newLength]; - System.arraycopy(arguments, 1, positionalArgs, 0, newLength); - return coercePrimitiveNode.execute( - interopLibrary.invokeMember( - getForeignFunction(), "apply", arguments[0], new ReadOnlyArray(positionalArgs))); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/RForeignNode.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/RForeignNode.java deleted file mode 100644 index eb2eb4e1c90..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/RForeignNode.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.enso.interpreter.epb.node; - -import com.oracle.truffle.api.dsl.NodeField; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.library.CachedLibrary; - -@NodeField(name = "foreignFunction", type = Object.class) -public abstract class RForeignNode extends ForeignFunctionCallNode { - - private @Child CoercePrimitiveNode coercePrimitiveNode = CoercePrimitiveNode.build(); - - abstract Object getForeignFunction(); - - @Specialization - public Object doExecute( - Object[] arguments, @CachedLibrary("foreignFunction") InteropLibrary interopLibrary) - throws InteropException { - return coercePrimitiveNode.execute(interopLibrary.execute(getForeignFunction(), arguments)); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java deleted file mode 100644 index 770642c33bb..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/node/ReadOnlyArray.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.enso.interpreter.epb.node; - -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; -import java.util.Arrays; - -/** - * A primitive boxed array type to be used only in EPB. - * - *

{@link ReadOnlyArray} is essentially a stripped-down, read-only, version of {@link - * org.enso.interpreter.runtime.data.vector.Array}, used for passing arguments. The latter cannot be - * used in EPB because EPB is a dependency of runtime. - */ -@ExportLibrary(InteropLibrary.class) -public final class ReadOnlyArray implements TruffleObject { - - private final Object[] items; - - /** - * Creates a new array - * - * @param items the element values - */ - public ReadOnlyArray(Object... items) { - this.items = items; - } - /** - * Marks the object as array-like for Polyglot APIs. - * - * @return {@code true} - */ - @ExportMessage - public boolean hasArrayElements() { - return true; - } - - /** - * Handles reading an element by index through the polyglot API. - * - * @param index the index to read - * @return the element value at the provided index - * @throws InvalidArrayIndexException when the index is out of bounds. - */ - @ExportMessage - public Object readArrayElement(long index) throws InvalidArrayIndexException { - if (index >= items.length || index < 0) { - throw InvalidArrayIndexException.create(index); - } - return items[(int) index]; - } - - /** - * Exposes the size of this collection through the polyglot API. - * - * @return the size of this array - */ - @ExportMessage - long getArraySize() { - return items.length; - } - - /** - * Exposes an index validity check through the polyglot API. - * - * @param index the index to check - * @return {@code true} if the index is valid, {@code false} otherwise. - */ - @ExportMessage - boolean isArrayElementReadable(long index) { - return index < getArraySize() && index >= 0; - } - - @ExportMessage - void writeArrayElement(long index, Object value) throws UnsupportedMessageException { - throw UnsupportedMessageException.create(); - } - - @ExportMessage - boolean isArrayElementModifiable(long index) { - return false; - } - - @ExportMessage - boolean isArrayElementInsertable(long index) { - return false; - } - - @Override - public String toString() { - return Arrays.toString(items); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/GuardedTruffleContext.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/GuardedTruffleContext.java deleted file mode 100644 index 7d5a85e9312..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/GuardedTruffleContext.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.enso.interpreter.epb.runtime; - -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.TruffleContext; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.nodes.Node; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Consumer; - -/** Wraps a {@link TruffleContext} by providing an optional GIL functionality. */ -public class GuardedTruffleContext { - private final TruffleContext context; - private final Lock lock; - - /** - * Creates a new instance of this wrapper. - * - * @param context the Truffle context to wrap - * @param isSingleThreaded whether or not the context should be accessed through a GIL. - */ - public GuardedTruffleContext(TruffleContext context, boolean isSingleThreaded) { - this.context = context; - if (isSingleThreaded) { - this.lock = new ReentrantLock(); - } else { - this.lock = null; - } - } - - /** - * Spawns new thread with associated {@code context} - * - * @param env environment to spawn the thread in - * @param run code to execute in given TruffleContext - */ - public Thread createThread(TruffleLanguage.Env env, Consumer run) { - return env.newTruffleThreadBuilder(() -> run.accept(context)).context(context).build(); - } - - /** - * Enters this context. If this wrapper is single threaded and the context is in use, this method - * will block indefinitely until the context becomes available. - * - *

Any code following a call to this method should be executed in a try/finally block, with - * {@link #leave(Node, Object)} being called in the finally block. It is crucial that this context - * is always left as soon as guest code execution finishes. - * - *

The token returned from this method may not be stored or used for any purpose other than - * leaving the context. - * - * @param node the node to enter this context for - * @return a context restoration token that must be passed to {@link #leave(Node, Object)} - */ - public Object enter(Node node) { - if (lock != null) { - lock(); - } - return context.enter(node); - } - - @CompilerDirectives.TruffleBoundary - private void lock() { - lock.lock(); - } - - /** - * Leaves the context and unlocks it if this wrapper is GILed. - * - * @param node the node to leave this context for (this must correspond to the same node used in - * the call that provided the {@code prev} token - * @param prev the token obtained from the call to {@link #enter(Node)} - */ - public void leave(Node node, Object prev) { - context.leave(node, prev); - if (lock != null) { - unlock(); - } - } - - @CompilerDirectives.TruffleBoundary - private void unlock() { - lock.unlock(); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotExceptionProxy.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotExceptionProxy.java deleted file mode 100644 index 5a7eb965865..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotExceptionProxy.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.enso.interpreter.epb.runtime; - -import com.oracle.truffle.api.exception.AbstractTruffleException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; - -/** - * A wrapper for exceptions that cross the polyglot boundary. - * - *

It is responsible for proxying messages across the polyglot boundary specifically for the case - * of exceptions. Without this, exceptions (as non-linear control flow) would bubble up into Enso - * without their context. This would allow them to be caught, but Enso would have no means of - * executing code on the guest-language exception. Instead, we wrap the exception into this proxy, - * which holds onto the foreign exception, as well as the two contexts necessary for mediating calls - * between Enso and the foreign language. - * - *

This is _separate_ to the {@link PolyglotProxy} as we did not want to make that proxy into an - * {@link AbstractTruffleException}. This means that we have more control over when foreign objects - * are represented as exceptions. - */ -@ExportLibrary(value = InteropLibrary.class, delegateTo = "delegate") -public class PolyglotExceptionProxy extends AbstractTruffleException { - final PolyglotProxy delegate; - final AbstractTruffleException original; - - public PolyglotExceptionProxy( - AbstractTruffleException prototype, - GuardedTruffleContext origin, - GuardedTruffleContext target) { - super(prototype); - this.original = prototype; - this.delegate = new PolyglotProxy(prototype, origin, target); - } - - @ExportMessage - boolean isException() { - return true; - } - - @ExportMessage - RuntimeException throwException() { - throw this; - } - - public PolyglotProxy getDelegate() { - return delegate; - } - - public AbstractTruffleException getOriginal() { - return original; - } - - public GuardedTruffleContext getOrigin() { - return delegate.getOrigin(); - } - - public GuardedTruffleContext getTarget() { - return delegate.getTarget(); - } -} diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java deleted file mode 100644 index 656696a5164..00000000000 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/runtime/PolyglotProxy.java +++ /dev/null @@ -1,1167 +0,0 @@ -package org.enso.interpreter.epb.runtime; - -import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.Cached.Exclusive; -import com.oracle.truffle.api.dsl.Cached.Shared; -import com.oracle.truffle.api.exception.AbstractTruffleException; -import com.oracle.truffle.api.interop.ArityException; -import com.oracle.truffle.api.interop.ExceptionType; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.interop.UnknownIdentifierException; -import com.oracle.truffle.api.interop.UnknownKeyException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.interop.UnsupportedTypeException; -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.profiles.BranchProfile; -import java.math.BigInteger; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; -import org.enso.interpreter.epb.node.ContextRewrapExceptionNode; -import org.enso.interpreter.epb.node.ContextRewrapNode; - -/** - * Wraps a polyglot value that is to be shared between Truffle contexts. See {@link - * org.enso.interpreter.epb.EpbLanguage} for the explanation of this system. - */ -@ExportLibrary(InteropLibrary.class) -public final class PolyglotProxy implements TruffleObject { - final Object delegate; - private final GuardedTruffleContext origin; - private final GuardedTruffleContext target; - - /** - * Wraps a value for inter-context use. - * - * @param delegate the wrapped value - * @param origin the context in which {@code delegate} is a valid value - * @param target the context in which {@code delegate} will be accessed through this wrapper - */ - public PolyglotProxy( - Object delegate, GuardedTruffleContext origin, GuardedTruffleContext target) { - this.delegate = delegate; - this.origin = origin; - this.target = target; - } - - /** @return the wrapped value */ - public Object getDelegate() { - return delegate; - } - - /** @return the context in which the wrapped value originates */ - public GuardedTruffleContext getOrigin() { - return origin; - } - - /** @return the context in which this wrapper is supposed to be used */ - public GuardedTruffleContext getTarget() { - return target; - } - - Object enterOrigin(InteropLibrary n) { - if (n.isAdoptable()) { - return origin.enter(n); - } else { - return origin.enter(null); - } - } - - void leaveOrigin(InteropLibrary node, Object prev) { - if (node.isAdoptable()) { - origin.leave(node, prev); - } else { - origin.leave(null, prev); - } - } - - @ExportMessage - public boolean isNull( - @CachedLibrary("this.delegate") InteropLibrary nulls, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return nulls.isNull(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean hasMembers( - @CachedLibrary("this.delegate") InteropLibrary members, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return members.hasMembers(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object getMembers( - boolean includeInternal, - @CachedLibrary("this.delegate") InteropLibrary members, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapNode contextRewrapNode, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return contextRewrapNode.execute( - members.getMembers(this.delegate, includeInternal), origin, target); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean isMemberInvocable( - String member, - @CachedLibrary("this.delegate") InteropLibrary members, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return members.isMemberInvocable(this.delegate, member); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object invokeMember( - String member, - Object[] arguments, - @CachedLibrary("this.delegate") InteropLibrary members, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached ContextRewrapNode contextRewrapNode, - @Exclusive @Cached BranchProfile profile) - throws ArityException, UnknownIdentifierException, UnsupportedMessageException, - UnsupportedTypeException { - Object[] wrappedArgs = new Object[arguments.length]; - for (int i = 0; i < arguments.length; i++) { - wrappedArgs[i] = contextRewrapNode.execute(arguments[i], target, origin); - } - Object p = enterOrigin(node); - try { - return contextRewrapNode.execute( - members.invokeMember(this.delegate, member, wrappedArgs), origin, target); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean isMemberReadable( - String member, - @CachedLibrary("this.delegate") InteropLibrary members, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return members.isMemberReadable(this.delegate, member); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object readMember( - String member, - @CachedLibrary("this.delegate") InteropLibrary members, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapNode contextRewrapNode, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnknownIdentifierException, UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return contextRewrapNode.execute(members.readMember(this.delegate, member), origin, target); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean isExecutable( - @CachedLibrary("this.delegate") InteropLibrary functions, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return functions.isExecutable(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object execute( - Object[] arguments, - @CachedLibrary("this.delegate") InteropLibrary functions, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapNode contextRewrapNode, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException, ArityException, UnsupportedTypeException { - Object[] wrappedArgs = new Object[arguments.length]; - for (int i = 0; i < arguments.length; i++) { - wrappedArgs[i] = contextRewrapNode.execute(arguments[i], target, origin); - } - Object p = enterOrigin(node); - try { - return contextRewrapNode.execute( - functions.execute(this.delegate, wrappedArgs), origin, target); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean hasArrayElements( - @CachedLibrary("this.delegate") InteropLibrary arrays, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return arrays.hasArrayElements(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public long getArraySize( - @CachedLibrary("this.delegate") InteropLibrary arrays, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return arrays.getArraySize(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean isArrayElementReadable( - long idx, - @CachedLibrary("this.delegate") InteropLibrary arrays, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return arrays.isArrayElementReadable(this.delegate, idx); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object readArrayElement( - long index, - @CachedLibrary("this.delegate") InteropLibrary arrays, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapNode contextRewrapNode, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws InvalidArrayIndexException, UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return contextRewrapNode.execute( - arrays.readArrayElement(this.delegate, index), origin, target); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean hasHashEntries( - @CachedLibrary("this.delegate") InteropLibrary hashMaps, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return hashMaps.hasHashEntries(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public long getHashSize( - @CachedLibrary("this.delegate") InteropLibrary hashes, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return hashes.getHashSize(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean isHashEntryReadable( - Object key, - @CachedLibrary("this.delegate") InteropLibrary hashes, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return hashes.isHashEntryReadable(this.delegate, key); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object readHashValue( - Object key, - @CachedLibrary("this.delegate") InteropLibrary hashes, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException, UnknownKeyException { - Object p = enterOrigin(node); - try { - return hashes.readHashValue(this.delegate, key); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object getHashEntriesIterator( - @CachedLibrary("this.delegate") InteropLibrary hashes, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return hashes.getHashEntriesIterator(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean isString( - @CachedLibrary("this.delegate") InteropLibrary strings, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return strings.isString(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public String asString( - @CachedLibrary("this.delegate") InteropLibrary strings, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return strings.asString(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean isNumber( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.isNumber(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean fitsInByte( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.fitsInByte(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean fitsInShort( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.fitsInShort(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean fitsInInt( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.fitsInInt(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean fitsInBigInteger( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.fitsInBigInteger(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean fitsInLong( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.fitsInLong(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean fitsInFloat( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.fitsInFloat(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public boolean fitsInDouble( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return numbers.fitsInDouble(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public byte asByte( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return numbers.asByte(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public short asShort( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return numbers.asShort(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public int asInt( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return numbers.asInt(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public BigInteger asBigInteger( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return numbers.asBigInteger(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public long asLong( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return numbers.asLong(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public float asFloat( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return numbers.asFloat(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public double asDouble( - @CachedLibrary("this.delegate") InteropLibrary numbers, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return numbers.asDouble(this.delegate); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - public Object toDisplayString( - boolean allowSideEffects, - @CachedLibrary("this.delegate") InteropLibrary displays, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode, - @Exclusive @Cached BranchProfile profile) { - Object p = enterOrigin(node); - try { - return displays.toDisplayString(this.delegate, allowSideEffects); - } catch (Throwable e) { - profile.enter(); - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - boolean isException( - @CachedLibrary("this") InteropLibrary node, - @CachedLibrary("this.delegate") InteropLibrary errors) { - Object p = enterOrigin(node); - try { - return errors.isException(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - RuntimeException throwException( - @CachedLibrary("this.delegate") InteropLibrary delegate, - @CachedLibrary("this") InteropLibrary node, - @Shared("interop") @CachedLibrary(limit = "5") InteropLibrary errors, - @Exclusive @Cached ContextRewrapExceptionNode contextRewrapExceptionNode) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - throw delegate.throwException(delegate); - } catch (Throwable e) { - if (errors.isException(e)) { - // `isException` means this must be AbstractTruffleException - //noinspection ConstantConditions - throw contextRewrapExceptionNode.execute((AbstractTruffleException) e, origin, target); - } else { - throw e; - } - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - ExceptionType getExceptionType( - @CachedLibrary("this") InteropLibrary node, - @CachedLibrary("this.delegate") InteropLibrary errors) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return errors.getExceptionType(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - int getExceptionExitStatus( - @CachedLibrary("this") InteropLibrary node, - @CachedLibrary("this.delegate") InteropLibrary errors) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return errors.getExceptionExitStatus(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - boolean isExceptionIncompleteSource( - @CachedLibrary("this.delegate") InteropLibrary errors, - @CachedLibrary("this") InteropLibrary node) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return errors.isExceptionIncompleteSource(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - boolean hasExceptionMessage( - @CachedLibrary("this.delegate") InteropLibrary errors, - @CachedLibrary("this") InteropLibrary node) { - Object p = enterOrigin(node); - try { - return errors.hasExceptionMessage(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - Object getExceptionMessage( - @CachedLibrary("this.delegate") InteropLibrary errors, - @CachedLibrary("this") InteropLibrary node, - @Exclusive @Cached ContextRewrapNode contextRewrapNode) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return contextRewrapNode.execute(errors.getExceptionMessage(delegate), origin, target); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - boolean isDate( - @CachedLibrary("this.delegate") InteropLibrary datum, - @CachedLibrary("this") InteropLibrary node) { - Object p = enterOrigin(node); - try { - return datum.isDate(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - LocalDate asDate( - @CachedLibrary("this.delegate") InteropLibrary datume, - @CachedLibrary("this") InteropLibrary node) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return datume.asDate(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - boolean isTime( - @CachedLibrary("this.delegate") InteropLibrary times, - @CachedLibrary("this") InteropLibrary node) { - Object p = enterOrigin(node); - try { - return times.isTime(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - LocalTime asTime( - @CachedLibrary("this.delegate") InteropLibrary times, - @CachedLibrary("this") InteropLibrary node) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return times.asTime(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - boolean isTimeZone( - @CachedLibrary("this.delegate") InteropLibrary times, - @CachedLibrary("this") InteropLibrary node) { - Object p = enterOrigin(node); - try { - return times.isTimeZone(delegate); - } finally { - leaveOrigin(node, p); - } - } - - @ExportMessage - ZoneId asTimeZone( - @CachedLibrary("this.delegate") InteropLibrary times, - @CachedLibrary("this") InteropLibrary node) - throws UnsupportedMessageException { - Object p = enterOrigin(node); - try { - return times.asTimeZone(delegate); - } finally { - leaveOrigin(node, p); - } - } -} diff --git a/engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/EpbLanguageDependenciesTest.java b/engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/EpbLanguageDependenciesTest.java new file mode 100644 index 00000000000..cedca482d1d --- /dev/null +++ b/engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/EpbLanguageDependenciesTest.java @@ -0,0 +1,49 @@ +import static org.junit.Assert.fail; + +import org.junit.Test; + +public class EpbLanguageDependenciesTest { + public EpbLanguageDependenciesTest() {} + + @Test(expected = ClassNotFoundException.class) + public void avoidFlatbuffers() throws Exception { + var c = Class.forName("com.google.flatbuffers.Constants"); + fail("No class should be found: " + c); + } + + @Test(expected = ClassNotFoundException.class) + public void avoidJackson() throws Exception { + var c = Class.forName("com.fasterxml.jackson.core.JsonParser"); + fail("No class should be found: " + c); + } + + @Test(expected = ClassNotFoundException.class) + public void avoidCirce() throws Exception { + var c = Class.forName("io.circe.Codec"); + fail("No class should be found: " + c); + } + + @Test(expected = ClassNotFoundException.class) + public void avoidApacheCommons() throws Exception { + var c = Class.forName("org.apache.commons.io.Charsets"); + fail("No class should be found: " + c); + } + + @Test(expected = ClassNotFoundException.class) + public void avoidCats() throws Exception { + var c = Class.forName("cats.Align"); + fail("No class should be found: " + c); + } + + @Test(expected = ClassNotFoundException.class) + public void avoidSlf4j() throws Exception { + var c = Class.forName("org.slf4j.Logger"); + fail("No class should be found: " + c); + } + + @Test(expected = ClassNotFoundException.class) + public void avoidSnakeyaml() throws Exception { + var c = Class.forName("org.yaml.snakeyaml.Yaml"); + fail("No class should be found: " + c); + } +} diff --git a/engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/ForeignEvalNodeTest.java b/engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/ForeignEvalNodeTest.java new file mode 100644 index 00000000000..545c9602a8e --- /dev/null +++ b/engine/runtime-language-epb/src/test/java/org/enso/interpreter/epb/ForeignEvalNodeTest.java @@ -0,0 +1,29 @@ +package org.enso.interpreter.epb; + +import java.util.Collections; + +import org.junit.Test; +import static org.junit.Assert.*; + +import com.oracle.truffle.api.source.Source; + +public class ForeignEvalNodeTest { + + public ForeignEvalNodeTest() { + } + + @Test + public void sourceWithoutHash() throws Exception { + var src = Source.newBuilder("epb", """ + nonsensecontent + """, "simple.test").build(); + + var node = ForeignEvalNode.parse(null, src, Collections.emptyList()); + try { + var res = node.execute(null); + fail("Unexpected result: " + res); + } catch (ForeignParsingException e) { + assertEquals("No # found", e.getMessage()); + } + } +} diff --git a/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java b/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java index 32ea800719f..5f67a744e5d 100644 --- a/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java +++ b/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/ForeignMethodInvokeTest.java @@ -1,11 +1,14 @@ package org.enso.interpreter.test; -import static org.junit.Assert.assertTrue; +import java.util.concurrent.Executors; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; import org.junit.AfterClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; public class ForeignMethodInvokeTest extends TestBase { @@ -13,7 +16,7 @@ public class ForeignMethodInvokeTest extends TestBase { @BeforeClass public static void prepareCtx() { - ctx = defaultContextBuilder("enso").build(); + ctx = defaultContextBuilder("enso", "js").build(); } @AfterClass @@ -44,4 +47,66 @@ public class ForeignMethodInvokeTest extends TestBase { e.getMessage().matches("Cannot parse foreign python method. Only available languages are .+")); } } + + @Test + public void testInteropWithJavaScript() throws Exception { + var source = """ + from Standard.Base import all + + foreign js js_array t = \"\"\" + return [1, 2, t] + + third t = js_array t + """; + + var module = ctx.eval("enso", source); + var third = module.invokeMember("eval_expression", "third"); + var res = third.execute(13); + assertTrue("It is an array", res.hasArrayElements()); + assertEquals(3, res.getArraySize()); + assertEquals(1, res.getArrayElement(0).asInt()); + assertEquals(2, res.getArrayElement(1).asInt()); + assertEquals(13, res.getArrayElement(2).asInt()); + + var res2 = Executors.newSingleThreadExecutor().submit(() -> { + return third.execute(12); + }).get(); + + assertTrue("It is an array2", res2.hasArrayElements()); + assertEquals(12, res2.getArrayElement(2).asInt()); + } + + @Ignore + @Test + public void testParallelInteropWithJavaScript() throws Exception { + var source = """ + from Standard.Base import all + + polyglot java import java.lang.Thread + + foreign js js_array t f = \"\"\" + f(300) + return [1, 2, t] + + third t = js_array t (delay-> Thread.sleep delay) + """; + + var module = ctx.eval("enso", source); + var third = module.invokeMember("eval_expression", "third"); + + var future = Executors.newSingleThreadExecutor().submit(() -> { + return third.execute(12); + }); + var res = third.execute(13); + assertTrue("It is an array", res.hasArrayElements()); + assertEquals(3, res.getArraySize()); + assertEquals(1, res.getArrayElement(0).asInt()); + assertEquals(2, res.getArrayElement(1).asInt()); + assertEquals(13, res.getArrayElement(2).asInt()); + + var res2 = future.get(); + + assertTrue("It is an array2", res2.hasArrayElements()); + assertEquals(12, res2.getArrayElement(2).asInt()); + } } diff --git a/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/instrument/VerifyJavaScriptIsAvailableTest.java b/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/instrument/VerifyLanguageAvailabilityTest.java similarity index 77% rename from engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/instrument/VerifyJavaScriptIsAvailableTest.java rename to engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/instrument/VerifyLanguageAvailabilityTest.java index 8a294fcbaab..010d13cdfc8 100644 --- a/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/instrument/VerifyJavaScriptIsAvailableTest.java +++ b/engine/runtime-with-polyglot/src/test/java/org/enso/interpreter/test/instrument/VerifyLanguageAvailabilityTest.java @@ -1,19 +1,21 @@ package org.enso.interpreter.test.instrument; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - import java.nio.file.Paths; import java.util.logging.Level; + import org.enso.interpreter.test.MockLogHandler; +import org.enso.polyglot.MethodNames; import org.enso.polyglot.RuntimeOptions; import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Source; import org.graalvm.polyglot.io.IOAccess; import org.junit.AfterClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import org.junit.BeforeClass; import org.junit.Test; -public class VerifyJavaScriptIsAvailableTest { +public class VerifyLanguageAvailabilityTest { private static Context ctx; private static MockLogHandler handler; @@ -44,16 +46,22 @@ public class VerifyJavaScriptIsAvailableTest { var args = handler.assertMessage( - "epb.org.enso.interpreter.epb.EpbContext", "Done initializing language"); + "epb.org.enso.interpreter.epb.EpbContext", "Parsing foreign script"); assertEquals("js", args[0]); - assertEquals(Boolean.TRUE, args[1]); + assertEquals("mul.mul", args[1]); } @Test - public void javaScriptIsPresent() { + public void javaScriptIsPresent() throws Exception { var js = ctx.getEngine().getLanguages().get("js"); assertNotNull("JavaScript is available", js); - var fourtyTwo = ctx.eval("js", "6 * 7"); + var src = Source.newBuilder("enso", """ + foreign js mul a b = \"\"\" + return a * b + + run = mul 6 7 + """, "mul.enso").build(); + var fourtyTwo = ctx.eval(src).invokeMember(MethodNames.Module.EVAL_EXPRESSION, "run"); assertEquals(42, fourtyTwo.asInt()); } 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 bad08d407b3..766e5a720aa 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java @@ -7,6 +7,7 @@ import java.util.Objects; import org.enso.compiler.Compiler; import org.enso.compiler.context.InlineContext; +import org.enso.compiler.context.LocalScope; import org.enso.compiler.context.ModuleContext; import org.enso.compiler.data.CompilerConfig; import org.enso.compiler.exception.CompilationAbortedException; @@ -24,7 +25,6 @@ import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ProgramRootNode; import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.IrToTruffle; -import org.enso.compiler.context.LocalScope; import org.enso.interpreter.runtime.state.ExecutionEnvironment; import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag; import org.enso.interpreter.runtime.tag.IdentifiedTag; @@ -32,7 +32,6 @@ import org.enso.interpreter.runtime.tag.Patchable; import org.enso.interpreter.util.FileDetector; import org.enso.lockmanager.client.ConnectedLockManager; import org.enso.logger.masking.MaskingFactory; -import org.enso.polyglot.ForeignLanguage; import org.enso.polyglot.LanguageInfo; import org.enso.polyglot.RuntimeOptions; import org.enso.syntax2.Line; @@ -71,7 +70,7 @@ import com.oracle.truffle.api.nodes.RootNode; defaultMimeType = LanguageInfo.MIME_TYPE, characterMimeTypes = {LanguageInfo.MIME_TYPE}, contextPolicy = TruffleLanguage.ContextPolicy.EXCLUSIVE, - dependentLanguages = {ForeignLanguage.ID}, + dependentLanguages = {"epb"}, fileTypeDetectors = FileDetector.class, services= { Timer.class, NotificationHandler.Forwarder.class, LockManager.class } ) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java index 02aba4178c0..b3ca4b4198a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/FunctionCallInstrumentationNode.java @@ -173,8 +173,15 @@ public class FunctionCallInstrumentationNode extends Node implements Instrumenta /** @return the source section of this node. */ @Override public SourceSection getSourceSection() { - Node parent = getParent(); - return parent == null ? null : parent.getSourceSection(); + var parent = getParent(); + while (parent != null) { + var ss = parent.getSourceSection(); + if (ss != null) { + return ss; + } + parent = parent.getParent(); + } + return null; } /** @return the expression ID of this node. */ diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/Instrumentor.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/Instrumentor.java index c5fed517978..3a3da0a898b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/Instrumentor.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/Instrumentor.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.meta; +import org.enso.interpreter.EnsoLanguage; import org.enso.interpreter.instrument.Timer; import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; import org.enso.interpreter.runtime.EnsoContext; @@ -8,8 +9,8 @@ import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers; import org.enso.polyglot.debugger.IdExecutionService; -import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.instrumentation.EventBinding; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; @@ -17,7 +18,7 @@ import com.oracle.truffle.api.interop.InteropLibrary; final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks { private final IdExecutionService service; - private final CallTarget target; + private final RootCallTarget target; private final Module module; private final Object onEnter; private final Object onReturn; @@ -25,7 +26,7 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks { private final Object onCall; private final EventBinding handle; - Instrumentor(Module module, IdExecutionService service, CallTarget target) { + Instrumentor(Module module, IdExecutionService service, RootCallTarget target) { this.module = module; this.service = service; this.target = target; @@ -80,10 +81,12 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks { var result = onReturnExpr == null || !iop.isString(onReturnExpr) ? info.getResult() : - InstrumentorEvalNode.asSuspendedEval(onReturnExpr, info); + InstrumentorEvalNode.asSuspendedEval(EnsoLanguage.get(target.getRootNode()), onReturnExpr, info); iop.execute(onReturn, info.getId().toString(), result); } - } catch (InteropException ignored) { + } catch (Throwable ignored) { + CompilerDirectives.transferToInterpreter(); + ignored.printStackTrace(); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/InstrumentorEvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/InstrumentorEvalNode.java index d05fe743f1a..584f82b35bb 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/InstrumentorEvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/InstrumentorEvalNode.java @@ -1,10 +1,12 @@ package org.enso.interpreter.node.expression.builtin.meta; -import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.RootNode; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import org.enso.interpreter.EnsoLanguage; import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.callable.Annotation; @@ -25,14 +27,22 @@ final class InstrumentorEvalNode extends RootNode { new boolean[] {true, true}, new CallArgumentInfo[0], new Annotation[0]); - private static final RootCallTarget CALL = new InstrumentorEvalNode().getCallTarget(); + private static Reference last = new WeakReference<>(null); - private InstrumentorEvalNode() { - super(EnsoLanguage.get(null)); + private InstrumentorEvalNode(EnsoLanguage language) { + super(language); } - static Function asSuspendedEval(Object expr, IdExecutionService.Info info) { - return new Function(CALL, null, SUSPENDED_EVAL, new Object[] {expr, info}, new Object[0]); + @TruffleBoundary + static Function asSuspendedEval( + EnsoLanguage language, Object expr, IdExecutionService.Info info) { + var node = last.get(); + if (node == null || node.getLanguage(EnsoLanguage.class) != language) { + node = new InstrumentorEvalNode(language); + last = new WeakReference<>(node); + } + var call = node.getCallTarget(); + return new Function(call, null, SUSPENDED_EVAL, new Object[] {expr, info}, new Object[0]); } @Override diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java index 2c27a7f59de..92f1eeea685 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java @@ -53,7 +53,14 @@ public abstract class PowNode extends IntegerNode { @Specialization Object doBigInteger(long self, EnsoBigInteger that) { - return doBigInteger(toBigInteger(self), that); + var thatValue = that.getValue(); + if (thatValue.signum() > 0) { + return Math.pow((double) self, BigIntegerOps.toDouble(thatValue)); + } else if (thatValue.signum() == 0) { + return 1.0D; + } else { + return 0.0D; + } } @Specialization @@ -69,10 +76,11 @@ public abstract class PowNode extends IntegerNode { @Specialization Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - if (that.getValue().signum() > 0) { - return Math.pow( - BigIntegerOps.toDouble(self.getValue()), BigIntegerOps.toDouble(that.getValue())); - } else if (that.getValue().signum() == 0) { + var thatValue = that.getValue(); + if (thatValue.signum() > 0) { + var selfValue = self.getValue(); + return Math.pow(BigIntegerOps.toDouble(selfValue), BigIntegerOps.toDouble(thatValue)); + } else if (thatValue.signum() == 0) { return 1.0D; } else { return 0.0D; 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 4634130a602..b5107a957e2 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 @@ -39,7 +39,6 @@ import org.enso.librarymanager.resolved.LibraryRoot; import org.enso.pkg.Package; import org.enso.pkg.PackageManager; import org.enso.pkg.QualifiedName; -import org.enso.polyglot.ForeignLanguage; import org.enso.polyglot.LanguageInfo; import org.enso.polyglot.RuntimeOptions; import org.enso.polyglot.debugger.IdExecutionService; @@ -50,7 +49,6 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.InstrumentInfo; import com.oracle.truffle.api.ThreadLocalAction; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleFile; @@ -210,7 +208,7 @@ public final class EnsoContext { var preinit = environment.getOptions().get(RuntimeOptions.PREINITIALIZE_KEY); if (preinit != null && preinit.length() > 0) { - var epb = environment.getInternalLanguages().get(ForeignLanguage.ID); + var epb = environment.getInternalLanguages().get("epb"); @SuppressWarnings("unchecked") var run = (Consumer) environment.lookup(epb, Consumer.class); if (run != null) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/Array.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/Array.java index 0867c544c21..e0b4cd91c6b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/Array.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/Array.java @@ -37,11 +37,20 @@ final class Array implements EnsoObject { * * @param items the element values */ - Array(Object... items) { - assert noNulls(items); + private Array(Object... items) { this.items = items; } + static Array wrap(Object... items) { + assert noNulls(items); + return new Array(items); + } + + static Array allocate(long size) { + var arr = new Object[Math.toIntExact(size)]; + return new Array(arr); + } + private static boolean noNulls(Object[] arr) { for (Object o : arr) { if (o == null) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java index e801896d0e8..0359034968b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java @@ -188,7 +188,7 @@ final class ArrayBuilder implements EnsoObject { if (arr instanceof double[] doubles) { yield Vector.fromDoubleArray(doubles); } - yield Vector.fromInteropArray(new Array((Object[])arr)); + yield Vector.fromInteropArray(Array.wrap((Object[])arr)); } default -> throw UnknownIdentifierException.create(name); }; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeAtNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeAtNode.java index c85f32a903f..5c097d24d4f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeAtNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeAtNode.java @@ -3,6 +3,7 @@ package org.enso.interpreter.runtime.data.vector; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.UnsupportedMessageException; @@ -33,7 +34,10 @@ public abstract class ArrayLikeAtNode extends Node { try { return self.getItems()[Math.toIntExact(index)]; } catch (ArithmeticException | IndexOutOfBoundsException ex) { - throw InvalidArrayIndexException.create(index, ex); + var cause = + new AbstractTruffleException( + ex.getMessage(), ex, AbstractTruffleException.UNLIMITED_STACK_TRACE, this) {}; + throw InvalidArrayIndexException.create(index, cause); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java index c89405cf6e8..216c6eb9e75 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java @@ -64,8 +64,7 @@ public final class ArrayLikeHelpers { * @return the array instance */ public static EnsoObject allocate(long size) { - var arr = new Object[Math.toIntExact(size)]; - return new Array(arr); + return Array.allocate(size); } @Builtin.Method( @@ -107,7 +106,7 @@ public final class ArrayLikeHelpers { return Vector.fromDoubleArray(doubles); } if (nonTrivialEnsoValue) { - return Vector.fromInteropArray(new Array((Object[])res)); + return Vector.fromInteropArray(Array.wrap((Object[])res)); } else { return Vector.fromEnsoOnlyArray((Object[])res); } @@ -136,15 +135,15 @@ public final class ArrayLikeHelpers { } public static EnsoObject wrapEnsoObjects(EnsoObject... arr) { - return new Array((Object[]) arr); + return Array.wrap((Object[]) arr); } public static EnsoObject wrapStrings(String... arr) { - return new Array((Object[]) arr); + return Array.wrap((Object[]) arr); } public static EnsoObject wrapObjectsWithCheckAt(Object... arr) { - return new Array((Object[]) arr); + return Array.wrap((Object[]) arr); } public static EnsoObject empty() { @@ -152,7 +151,7 @@ public final class ArrayLikeHelpers { } public static EnsoObject asVectorWithCheckAt(Object... arr) { - return Vector.fromInteropArray(new Array((Object[]) arr)); + return Vector.fromInteropArray(Array.wrap((Object[]) arr)); } public static EnsoObject asVectorFromArray(Object storage) { 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 4eb5cad9cd5..d1b1359d954 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 @@ -187,7 +187,11 @@ public class DebugLocalScope implements EnsoObject { @ExportMessage @TruffleBoundary - Object readMember(String member, @CachedLibrary("this") InteropLibrary interop) { + Object readMember(String member, @CachedLibrary("this") InteropLibrary interop) + throws UnknownIdentifierException { + if (!allBindings.containsKey(member)) { + throw UnknownIdentifierException.create(member); + } FramePointer framePtr = allBindings.get(member); var value = getValue(frame, framePtr); return value != null ? value : DataflowError.UNINITIALIZED; 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 8d428363bea..0b2bbb45771 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 @@ -62,7 +62,6 @@ import org.enso.compiler.pass.resolve.{ TypeNames, TypeSignatures } -import org.enso.polyglot.ForeignLanguage import org.enso.interpreter.node.callable.argument.ReadArgumentNode import org.enso.interpreter.node.callable.argument.ReadArgumentCheckNode import org.enso.interpreter.node.callable.function.{ @@ -1897,7 +1896,7 @@ class IrToTruffle( val bodyExpr = body match { case Foreign.Definition(lang, code, _, _, _) => buildForeignBody( - ForeignLanguage.getBySyntacticTag(lang), + lang, code, arguments.map(_.name.name), argSlotIdxs @@ -1958,12 +1957,13 @@ class IrToTruffle( } private def buildForeignBody( - language: ForeignLanguage, + language: String, code: String, argumentNames: List[String], argumentSlotIdxs: List[Int] ): RuntimeExpression = { - val src = language.buildSource(code, scopeName) + val src = + Source.newBuilder("epb", language + "#" + code, scopeName).build() val foreignCt = context.parseInternal(src, argumentNames: _*) val argumentReaders = argumentSlotIdxs .map(slotIdx => diff --git a/lib/scala/bench-processor/src/main/java/org/enso/benchmarks/processor/BenchProcessor.java b/lib/scala/bench-processor/src/main/java/org/enso/benchmarks/processor/BenchProcessor.java index 8b4962c36fa..9db7b0990a9 100644 --- a/lib/scala/bench-processor/src/main/java/org/enso/benchmarks/processor/BenchProcessor.java +++ b/lib/scala/bench-processor/src/main/java/org/enso/benchmarks/processor/BenchProcessor.java @@ -1,11 +1,8 @@ package org.enso.benchmarks.processor; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintWriter; -import java.net.URISyntaxException; -import java.nio.file.Files; import java.util.List; import java.util.Set; import java.util.logging.Level; @@ -73,13 +70,12 @@ public class BenchProcessor extends AbstractProcessor { "import org.enso.benchmarks.Utils;"); public BenchProcessor() { - ensoDir = Utils.findRepoRootDir(); + ensoDir = Utils.findRepoRootDir(); - // Note that ensoHomeOverride does not have to exist, only its parent directory - ensoHomeOverride = ensoDir.toPath().resolve("distribution").resolve("component").toFile(); + // Note that ensoHomeOverride does not have to exist, only its parent directory + ensoHomeOverride = ensoDir.toPath().resolve("distribution").resolve("component").toFile(); } - @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java index a2697367241..94ec567ec61 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java @@ -38,7 +38,7 @@ public abstract class Builder { case BigIntegerType x -> new BigIntegerBuilder(size, problemAggregator); case null -> new InferredBuilder(size, problemAggregator); }; - assert builder.getType().equals(type); + assert java.util.Objects.equals(builder.getType(), type); return builder; } diff --git a/test/Benchmarks/src/Sieve/Sieve_Ascribed.enso b/test/Benchmarks/src/Sieve/Sieve_Ascribed.enso index 8cfd292bc92..90526f97762 100644 --- a/test/Benchmarks/src/Sieve/Sieve_Ascribed.enso +++ b/test/Benchmarks/src/Sieve/Sieve_Ascribed.enso @@ -27,22 +27,23 @@ type Filter pair = Meta.atom_with_hole (h -> Filter.Item n h) newLast = pair.value set_last newLast - Filter.Head head.first pair.fill + [True, Filter.Head head.first pair.fill] case self of Filter.Empty -> pair = Meta.atom_with_hole (h -> Filter.Item n h) - Filter.Head pair.value pair.fill - Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else self + [True, Filter.Head pair.value pair.fill] + Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else [False, Nothing] type Primes Alg generator filter Primes.next self = case self of Primes.Alg g f -> - filter = f.acceptAndAdd g.n - new_primes = Primes.Alg g.next filter - if Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes + found_new = f.acceptAndAdd g.n + if found_new.first then Primes.Alg g.next found_new.last else + new_primes = Primes.Alg g.next f + @Tail_Call new_primes.next Primes.last_prime self = case self of Primes.Alg g _ -> g.n - 1 diff --git a/test/Benchmarks/src/Sieve/Sieve_Java_Script.enso b/test/Benchmarks/src/Sieve/Sieve_Java_Script.enso index f6790b95855..53e77745bf7 100644 --- a/test/Benchmarks/src/Sieve/Sieve_Java_Script.enso +++ b/test/Benchmarks/src/Sieve/Sieve_Java_Script.enso @@ -33,7 +33,7 @@ foreign js filter_in_js = """ while (filter != null) { if (filter.number) { if (n % filter.number === 0) { - return null; + return [false, null]; } if (filter.number > sqrt) { break; @@ -44,7 +44,7 @@ foreign js filter_in_js = """ var newFilter = new Filter(n); this.last.next = newFilter; this.last = newFilter; - return this; + return [true, this]; }; return new Filter(null); @@ -65,9 +65,10 @@ type Primes Primes.next self = case self of Primes.Alg g f -> - filter = f.acceptAndAdd g.n - new_primes = Primes.Alg g.next filter - if Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes + found_new = f.acceptAndAdd g.n + if found_new.first then Primes.Alg g.next found_new.last else + new_primes = Primes.Alg g.next f + @Tail_Call new_primes.next Primes.last_prime self = case self of Primes.Alg g _ -> g.n - 1 diff --git a/test/Benchmarks/src/Sieve/Sieve_Original.enso b/test/Benchmarks/src/Sieve/Sieve_Original.enso index 239ee7cd9c5..a4d6c4b69a4 100644 --- a/test/Benchmarks/src/Sieve/Sieve_Original.enso +++ b/test/Benchmarks/src/Sieve/Sieve_Original.enso @@ -27,22 +27,23 @@ type Filter pair = Meta.atom_with_hole (h -> Filter.Item n h) newLast = pair.value set_last newLast - Filter.Head head.first pair.fill + [True, Filter.Head head.first pair.fill] case self of Filter.Empty -> pair = Meta.atom_with_hole (h -> Filter.Item n h) - Filter.Head pair.value pair.fill - Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else self + [True, Filter.Head pair.value pair.fill] + Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else [False, Nothing] type Primes Alg generator filter Primes.next self = case self of Primes.Alg g f -> - filter = f.acceptAndAdd g.n - new_primes = Primes.Alg g.next filter - if Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes + found_new = f.acceptAndAdd g.n + if found_new.first then Primes.Alg g.next found_new.last else + new_primes = Primes.Alg g.next f + @Tail_Call new_primes.next Primes.last_prime self = case self of Primes.Alg g _ -> g.n - 1 diff --git a/test/Benchmarks/src/Sieve/Sieve_Without_Types.enso b/test/Benchmarks/src/Sieve/Sieve_Without_Types.enso index 0341ad81b44..b399d4716f1 100644 --- a/test/Benchmarks/src/Sieve/Sieve_Without_Types.enso +++ b/test/Benchmarks/src/Sieve/Sieve_Without_Types.enso @@ -24,23 +24,24 @@ type Filter pair = Meta.atom_with_hole (h -> Filter.Item n h) newLast = pair.value set_last newLast - Filter.Head head.first pair.fill + [True, Filter.Head head.first pair.fill] case self of Filter.Empty -> pair = Meta.atom_with_hole (h -> Filter.Item n h) - Filter.Head pair.value pair.fill - Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else self + [True, Filter.Head pair.value pair.fill] + Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else + [False, Nothing] type Primes Alg generator filter Primes.next self = case self of Primes.Alg g f -> - filter = f.acceptAndAdd g.n - nf = if filter.is_nothing then f else filter - new_primes = Primes.Alg g.next nf - if filter.is_nothing || Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes + found_new = f.acceptAndAdd g.n + if found_new.first then Primes.Alg g.next found_new.last else + new_primes = Primes.Alg g.next f + @Tail_Call new_primes.next Primes.last_prime self = case self of Primes.Alg g _ -> g.n - 1 diff --git a/test/Tests/src/Semantic/Python_Interop_Spec.enso b/test/Tests/src/Semantic/Python_Interop_Spec.enso index 114dc36db4e..2ce00a086ba 100644 --- a/test/Tests/src/Semantic/Python_Interop_Spec.enso +++ b/test/Tests/src/Semantic/Python_Interop_Spec.enso @@ -173,6 +173,14 @@ spec = _ -> False num_double_match.should_be_true + Test.specify "should make Python number values equal to Enso ints" <| + py_10 = make_int + py_10 . should_equal 10 + + Test.specify "should make Python number values equal to Enso doubles" <| + py_d = make_double + py_d . should_equal 10.5 + Test.specify "should make Python None values equal to Nothing" <| py_null = make_null py_null . should_equal Nothing