mirror of
https://github.com/enso-org/enso.git
synced 2025-01-03 14:04:44 +03:00
EpbLanguage re-uses other TruffleContext support to run tests with assertions enabled (#7882)
This commit is contained in:
parent
927df167d7
commit
4b65e44ef3
20
build.sbt
20
build.sbt
@ -1178,11 +1178,6 @@ val truffleRunOpts = Seq(
|
|||||||
"-Dpolyglot.compiler.BackgroundCompilation=false"
|
"-Dpolyglot.compiler.BackgroundCompilation=false"
|
||||||
)
|
)
|
||||||
|
|
||||||
val truffleRunOptionsNoAssertSettings = Seq(
|
|
||||||
fork := true,
|
|
||||||
javaOptions ++= benchOnlyOptions
|
|
||||||
)
|
|
||||||
|
|
||||||
val truffleRunOptionsSettings = Seq(
|
val truffleRunOptionsSettings = Seq(
|
||||||
fork := true,
|
fork := true,
|
||||||
javaOptions ++= "-ea" +: benchOnlyOptions
|
javaOptions ++= "-ea" +: benchOnlyOptions
|
||||||
@ -1378,11 +1373,20 @@ lazy val instrumentationSettings = frgaalJavaCompilerSetting ++ Seq(
|
|||||||
lazy val `runtime-language-epb` =
|
lazy val `runtime-language-epb` =
|
||||||
(project in file("engine/runtime-language-epb"))
|
(project in file("engine/runtime-language-epb"))
|
||||||
.settings(
|
.settings(
|
||||||
|
frgaalJavaCompilerSetting,
|
||||||
inConfig(Compile)(truffleRunOptionsSettings),
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
truffleDslSuppressWarnsSetting,
|
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"))
|
lazy val runtime = (project in file("engine/runtime"))
|
||||||
.configs(Benchmark)
|
.configs(Benchmark)
|
||||||
@ -1730,7 +1734,7 @@ lazy val `runtime-with-polyglot` =
|
|||||||
.configs(Benchmark)
|
.configs(Benchmark)
|
||||||
.settings(
|
.settings(
|
||||||
frgaalJavaCompilerSetting,
|
frgaalJavaCompilerSetting,
|
||||||
inConfig(Compile)(truffleRunOptionsNoAssertSettings),
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
inConfig(Benchmark)(Defaults.testSettings),
|
inConfig(Benchmark)(Defaults.testSettings),
|
||||||
commands += WithDebugCommand.withDebug,
|
commands += WithDebugCommand.withDebug,
|
||||||
Benchmark / javacOptions --= Seq(
|
Benchmark / javacOptions --= Seq(
|
||||||
|
@ -30,7 +30,7 @@ impl From<bool> for Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ide_ci::define_env_var! {
|
ide_ci::define_env_var! {
|
||||||
ENSO_JVM_OPTS, String;
|
JAVA_OPTS, String;
|
||||||
ENSO_BENCHMARK_TEST_DRY_RUN, Boolean;
|
ENSO_BENCHMARK_TEST_DRY_RUN, Boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ impl BuiltEnso {
|
|||||||
.arg(test_path)
|
.arg(test_path)
|
||||||
// This flag enables assertions in the JVM. Some of our stdlib tests had in the past
|
// 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.
|
// 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)
|
Ok(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,7 +22,6 @@ import org.enso.compiler.pass.analyse.{
|
|||||||
}
|
}
|
||||||
import org.enso.compiler.pass.lint.UnusedBindings
|
import org.enso.compiler.pass.lint.UnusedBindings
|
||||||
import org.enso.compiler.pass.optimise.LambdaConsolidate
|
import org.enso.compiler.pass.optimise.LambdaConsolidate
|
||||||
import org.enso.polyglot.ForeignLanguage
|
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
@ -161,7 +160,7 @@ case object GenerateMethodBodies extends IRPass {
|
|||||||
case lam @ Function.Lambda(_, body, _, _, _, _)
|
case lam @ Function.Lambda(_, body, _, _, _, _)
|
||||||
if findForeignDefinition(
|
if findForeignDefinition(
|
||||||
body,
|
body,
|
||||||
lang = Some(ForeignLanguage.JS)
|
lang = Some("js")
|
||||||
).isDefined =>
|
).isDefined =>
|
||||||
val thisArgs = chainedFunctionArgs.collect {
|
val thisArgs = chainedFunctionArgs.collect {
|
||||||
case (arg, idx) if arg.name.name == "this" =>
|
case (arg, idx) if arg.name.name == "this" =>
|
||||||
@ -308,7 +307,7 @@ case object GenerateMethodBodies extends IRPass {
|
|||||||
@tailrec
|
@tailrec
|
||||||
private def findForeignDefinition(
|
private def findForeignDefinition(
|
||||||
body: Expression,
|
body: Expression,
|
||||||
lang: Option[ForeignLanguage]
|
lang: Option[String]
|
||||||
): Option[Foreign.Definition] = {
|
): Option[Foreign.Definition] = {
|
||||||
body match {
|
body match {
|
||||||
case foreignDef: Foreign.Definition =>
|
case foreignDef: Foreign.Definition =>
|
||||||
|
@ -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.Cached;
|
||||||
import com.oracle.truffle.api.dsl.GenerateUncached;
|
import com.oracle.truffle.api.dsl.GenerateUncached;
|
||||||
@ -10,7 +10,7 @@ import com.oracle.truffle.api.strings.TruffleString;
|
|||||||
|
|
||||||
@GenerateUncached
|
@GenerateUncached
|
||||||
@ReportPolymorphism
|
@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
|
* Create a new node responsible for coercing primitive values to Enso primitives at the polyglot
|
@ -1,19 +1,18 @@
|
|||||||
package org.enso.interpreter.epb;
|
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.TruffleContext;
|
||||||
import com.oracle.truffle.api.TruffleLanguage;
|
import com.oracle.truffle.api.TruffleLanguage;
|
||||||
|
import com.oracle.truffle.api.TruffleLogger;
|
||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.logging.Level;
|
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
|
* A context for {@link EpbLanguage}. Provides access to both isolated Truffle contexts used in
|
||||||
* polyglot execution.
|
* polyglot execution.
|
||||||
*/
|
*/
|
||||||
public class EpbContext {
|
final class EpbContext {
|
||||||
|
|
||||||
private static final TruffleLanguage.ContextReference<EpbContext> REFERENCE =
|
private static final TruffleLanguage.ContextReference<EpbContext> REFERENCE =
|
||||||
TruffleLanguage.ContextReference.create(EpbLanguage.class);
|
TruffleLanguage.ContextReference.create(EpbLanguage.class);
|
||||||
@ -21,18 +20,19 @@ public class EpbContext {
|
|||||||
private static final String INNER_OPTION = "isEpbInner";
|
private static final String INNER_OPTION = "isEpbInner";
|
||||||
private final boolean isInner;
|
private final boolean isInner;
|
||||||
private final TruffleLanguage.Env env;
|
private final TruffleLanguage.Env env;
|
||||||
private @CompilerDirectives.CompilationFinal GuardedTruffleContext innerContext;
|
private @CompilationFinal TruffleContext innerContext;
|
||||||
private final GuardedTruffleContext currentContext;
|
private final ReentrantLock lock = new ReentrantLock();
|
||||||
|
private final TruffleLogger log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of this context.
|
* Creates a new instance of this context.
|
||||||
*
|
*
|
||||||
* @param env the current language environment.
|
* @param env the current language environment.
|
||||||
*/
|
*/
|
||||||
public EpbContext(TruffleLanguage.Env env) {
|
EpbContext(TruffleLanguage.Env env) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
isInner = env.getConfig().get(INNER_OPTION) != null;
|
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 (!isInner) {
|
||||||
if (innerContext == null) {
|
if (innerContext == null) {
|
||||||
innerContext =
|
innerContext =
|
||||||
new GuardedTruffleContext(
|
env.newInnerContextBuilder()
|
||||||
env.newInnerContextBuilder()
|
.initializeCreatorContext(true)
|
||||||
.initializeCreatorContext(true)
|
.inheritAllAccess(true)
|
||||||
.inheritAllAccess(true)
|
.config(INNER_OPTION, "yes")
|
||||||
.config(INNER_OPTION, "yes")
|
.build();
|
||||||
.build(),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
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<TruffleContext>)
|
|
||||||
(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.
|
* @param node the location of context access. Pass {@code null} if not in a node.
|
||||||
* @return the proper context instance for the current {@link
|
* @return the proper context instance for the current {@link
|
||||||
@ -106,30 +63,16 @@ public class EpbContext {
|
|||||||
return REFERENCE.get(node);
|
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. */
|
/** @return the language environment associated with this context. */
|
||||||
public TruffleLanguage.Env getEnv() {
|
public TruffleLanguage.Env getEnv() {
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TruffleContext getInnerContext() {
|
||||||
|
return innerContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(Level level, String msg, Object... args) {
|
||||||
|
this.log.log(level, msg, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,41 +3,17 @@ package org.enso.interpreter.epb;
|
|||||||
import com.oracle.truffle.api.CallTarget;
|
import com.oracle.truffle.api.CallTarget;
|
||||||
import com.oracle.truffle.api.TruffleLanguage;
|
import com.oracle.truffle.api.TruffleLanguage;
|
||||||
import java.util.function.Consumer;
|
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. */
|
||||||
* An internal language that serves as a bridge between Enso and other supported languages.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*/
|
|
||||||
@TruffleLanguage.Registration(
|
@TruffleLanguage.Registration(
|
||||||
id = ForeignLanguage.ID,
|
id = "epb",
|
||||||
name = "Enso Polyglot Bridge",
|
name = "Enso Polyglot Bridge",
|
||||||
characterMimeTypes = {EpbLanguage.MIME},
|
characterMimeTypes = {EpbLanguage.MIME},
|
||||||
internal = true,
|
internal = true,
|
||||||
defaultMimeType = EpbLanguage.MIME,
|
defaultMimeType = EpbLanguage.MIME,
|
||||||
contextPolicy = TruffleLanguage.ContextPolicy.SHARED,
|
contextPolicy = TruffleLanguage.ContextPolicy.SHARED,
|
||||||
services = Consumer.class)
|
services = Consumer.class)
|
||||||
public class EpbLanguage extends TruffleLanguage<EpbContext> {
|
public final class EpbLanguage extends TruffleLanguage<EpbContext> {
|
||||||
public static final String MIME = "application/epb";
|
public static final String MIME = "application/epb";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -55,9 +31,8 @@ public class EpbLanguage extends TruffleLanguage<EpbContext> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CallTarget parse(ParsingRequest request) {
|
protected CallTarget parse(ParsingRequest request) {
|
||||||
EpbParser.Result code = EpbParser.parse(request.getSource());
|
var node = ForeignEvalNode.parse(this, request.getSource(), request.getArgumentNames());
|
||||||
ForeignEvalNode foreignEvalNode = ForeignEvalNode.build(this, code, request.getArgumentNames());
|
return node.getCallTarget();
|
||||||
return foreignEvalNode.getCallTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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<String> arguments) {
|
||||||
|
super(language, new FrameDescriptor());
|
||||||
|
this.langAndCode = langAndCode;
|
||||||
|
this.argNames = arguments.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ForeignEvalNode parse(EpbLanguage epb, Source langAndCode, List<String> 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<CallTarget,ForeignFunctionCallNode> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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.interop.InteropException;
|
||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
|
|
||||||
/** An interface for nodes responsible for calling into foreign languages. */
|
/** 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.
|
* Executes the foreign call.
|
||||||
*
|
*
|
@ -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.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
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.
|
* language is not installed, or enabled, in the Truffle engine.
|
||||||
*/
|
*/
|
||||||
@ExportLibrary(InteropLibrary.class)
|
@ExportLibrary(InteropLibrary.class)
|
||||||
public class ForeignParsingException extends AbstractTruffleException {
|
class ForeignParsingException extends AbstractTruffleException {
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|
||||||
/**
|
/**
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,11 @@
|
|||||||
package org.enso.interpreter.epb.node;
|
package org.enso.interpreter.epb;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.ZoneId;
|
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.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.ArityException;
|
||||||
import com.oracle.truffle.api.interop.InteropException;
|
import com.oracle.truffle.api.interop.InteropException;
|
||||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
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.library.ExportMessage;
|
||||||
import com.oracle.truffle.api.source.Source;
|
import com.oracle.truffle.api.source.Source;
|
||||||
|
|
||||||
@NodeField(name = "foreignFunction", type = Object.class)
|
final class PyForeignNode extends GenericForeignNode {
|
||||||
public abstract class PyForeignNode extends ForeignFunctionCallNode {
|
|
||||||
@Child
|
|
||||||
private CoercePrimitiveNode coercePrimitiveNode = CoercePrimitiveNode.build();
|
|
||||||
@CompilerDirectives.CompilationFinal
|
@CompilerDirectives.CompilationFinal
|
||||||
private Object fnPythonDate;
|
private Object fnPythonDate;
|
||||||
@Child
|
@Child
|
||||||
@ -39,10 +33,72 @@ public abstract class PyForeignNode extends ForeignFunctionCallNode {
|
|||||||
private InteropLibrary nodePythonZone;
|
private InteropLibrary nodePythonZone;
|
||||||
@CompilerDirectives.CompilationFinal
|
@CompilerDirectives.CompilationFinal
|
||||||
private Object fnPythonCombine;
|
private Object fnPythonCombine;
|
||||||
|
@CompilerDirectives.CompilationFinal
|
||||||
|
private Object none;
|
||||||
@Child
|
@Child
|
||||||
private InteropLibrary nodePythonCombine;
|
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 {
|
private Object wrapPythonDate(LocalDate date) throws UnsupportedTypeException, ArityException, UnsupportedMessageException {
|
||||||
if (nodePythonDate == null) {
|
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)
|
@ExportLibrary(InteropLibrary.class)
|
||||||
static final class ZoneWrapper implements TruffleObject {
|
static final class ZoneWrapper implements TruffleObject {
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<String> arguments) {
|
|
||||||
return new ForeignEvalNode(language, code, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
ForeignEvalNode(EpbLanguage language, EpbParser.Result code, List<String> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.
|
|
||||||
*
|
|
||||||
* <p>{@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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<TruffleContext> 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.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>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();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
package org.enso.interpreter.test;
|
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.Context;
|
||||||
import org.graalvm.polyglot.Value;
|
import org.graalvm.polyglot.Value;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class ForeignMethodInvokeTest extends TestBase {
|
public class ForeignMethodInvokeTest extends TestBase {
|
||||||
@ -13,7 +16,7 @@ public class ForeignMethodInvokeTest extends TestBase {
|
|||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void prepareCtx() {
|
public static void prepareCtx() {
|
||||||
ctx = defaultContextBuilder("enso").build();
|
ctx = defaultContextBuilder("enso", "js").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
@ -44,4 +47,66 @@ public class ForeignMethodInvokeTest extends TestBase {
|
|||||||
e.getMessage().matches("Cannot parse foreign python method. Only available languages are .+"));
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
package org.enso.interpreter.test.instrument;
|
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.nio.file.Paths;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.enso.interpreter.test.MockLogHandler;
|
import org.enso.interpreter.test.MockLogHandler;
|
||||||
|
import org.enso.polyglot.MethodNames;
|
||||||
import org.enso.polyglot.RuntimeOptions;
|
import org.enso.polyglot.RuntimeOptions;
|
||||||
import org.graalvm.polyglot.Context;
|
import org.graalvm.polyglot.Context;
|
||||||
|
import org.graalvm.polyglot.Source;
|
||||||
import org.graalvm.polyglot.io.IOAccess;
|
import org.graalvm.polyglot.io.IOAccess;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class VerifyJavaScriptIsAvailableTest {
|
public class VerifyLanguageAvailabilityTest {
|
||||||
private static Context ctx;
|
private static Context ctx;
|
||||||
private static MockLogHandler handler;
|
private static MockLogHandler handler;
|
||||||
|
|
||||||
@ -44,16 +46,22 @@ public class VerifyJavaScriptIsAvailableTest {
|
|||||||
|
|
||||||
var args =
|
var args =
|
||||||
handler.assertMessage(
|
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("js", args[0]);
|
||||||
assertEquals(Boolean.TRUE, args[1]);
|
assertEquals("mul.mul", args[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void javaScriptIsPresent() {
|
public void javaScriptIsPresent() throws Exception {
|
||||||
var js = ctx.getEngine().getLanguages().get("js");
|
var js = ctx.getEngine().getLanguages().get("js");
|
||||||
assertNotNull("JavaScript is available", 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());
|
assertEquals(42, fourtyTwo.asInt());
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import org.enso.compiler.Compiler;
|
import org.enso.compiler.Compiler;
|
||||||
import org.enso.compiler.context.InlineContext;
|
import org.enso.compiler.context.InlineContext;
|
||||||
|
import org.enso.compiler.context.LocalScope;
|
||||||
import org.enso.compiler.context.ModuleContext;
|
import org.enso.compiler.context.ModuleContext;
|
||||||
import org.enso.compiler.data.CompilerConfig;
|
import org.enso.compiler.data.CompilerConfig;
|
||||||
import org.enso.compiler.exception.CompilationAbortedException;
|
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.node.ProgramRootNode;
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
import org.enso.interpreter.runtime.IrToTruffle;
|
import org.enso.interpreter.runtime.IrToTruffle;
|
||||||
import org.enso.compiler.context.LocalScope;
|
|
||||||
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
|
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
|
||||||
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
||||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
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.interpreter.util.FileDetector;
|
||||||
import org.enso.lockmanager.client.ConnectedLockManager;
|
import org.enso.lockmanager.client.ConnectedLockManager;
|
||||||
import org.enso.logger.masking.MaskingFactory;
|
import org.enso.logger.masking.MaskingFactory;
|
||||||
import org.enso.polyglot.ForeignLanguage;
|
|
||||||
import org.enso.polyglot.LanguageInfo;
|
import org.enso.polyglot.LanguageInfo;
|
||||||
import org.enso.polyglot.RuntimeOptions;
|
import org.enso.polyglot.RuntimeOptions;
|
||||||
import org.enso.syntax2.Line;
|
import org.enso.syntax2.Line;
|
||||||
@ -71,7 +70,7 @@ import com.oracle.truffle.api.nodes.RootNode;
|
|||||||
defaultMimeType = LanguageInfo.MIME_TYPE,
|
defaultMimeType = LanguageInfo.MIME_TYPE,
|
||||||
characterMimeTypes = {LanguageInfo.MIME_TYPE},
|
characterMimeTypes = {LanguageInfo.MIME_TYPE},
|
||||||
contextPolicy = TruffleLanguage.ContextPolicy.EXCLUSIVE,
|
contextPolicy = TruffleLanguage.ContextPolicy.EXCLUSIVE,
|
||||||
dependentLanguages = {ForeignLanguage.ID},
|
dependentLanguages = {"epb"},
|
||||||
fileTypeDetectors = FileDetector.class,
|
fileTypeDetectors = FileDetector.class,
|
||||||
services= { Timer.class, NotificationHandler.Forwarder.class, LockManager.class }
|
services= { Timer.class, NotificationHandler.Forwarder.class, LockManager.class }
|
||||||
)
|
)
|
||||||
|
@ -173,8 +173,15 @@ public class FunctionCallInstrumentationNode extends Node implements Instrumenta
|
|||||||
/** @return the source section of this node. */
|
/** @return the source section of this node. */
|
||||||
@Override
|
@Override
|
||||||
public SourceSection getSourceSection() {
|
public SourceSection getSourceSection() {
|
||||||
Node parent = getParent();
|
var parent = getParent();
|
||||||
return parent == null ? null : parent.getSourceSection();
|
while (parent != null) {
|
||||||
|
var ss = parent.getSourceSection();
|
||||||
|
if (ss != null) {
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
parent = parent.getParent();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the expression ID of this node. */
|
/** @return the expression ID of this node. */
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.enso.interpreter.node.expression.builtin.meta;
|
package org.enso.interpreter.node.expression.builtin.meta;
|
||||||
|
|
||||||
|
import org.enso.interpreter.EnsoLanguage;
|
||||||
import org.enso.interpreter.instrument.Timer;
|
import org.enso.interpreter.instrument.Timer;
|
||||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
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.interpreter.runtime.data.vector.ArrayLikeHelpers;
|
||||||
import org.enso.polyglot.debugger.IdExecutionService;
|
import org.enso.polyglot.debugger.IdExecutionService;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CallTarget;
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
|
import com.oracle.truffle.api.RootCallTarget;
|
||||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||||
import com.oracle.truffle.api.interop.InteropException;
|
import com.oracle.truffle.api.interop.InteropException;
|
||||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
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 {
|
final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
||||||
|
|
||||||
private final IdExecutionService service;
|
private final IdExecutionService service;
|
||||||
private final CallTarget target;
|
private final RootCallTarget target;
|
||||||
private final Module module;
|
private final Module module;
|
||||||
private final Object onEnter;
|
private final Object onEnter;
|
||||||
private final Object onReturn;
|
private final Object onReturn;
|
||||||
@ -25,7 +26,7 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
|||||||
private final Object onCall;
|
private final Object onCall;
|
||||||
private final EventBinding<?> handle;
|
private final EventBinding<?> handle;
|
||||||
|
|
||||||
Instrumentor(Module module, IdExecutionService service, CallTarget target) {
|
Instrumentor(Module module, IdExecutionService service, RootCallTarget target) {
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
@ -80,10 +81,12 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
|||||||
var result = onReturnExpr == null || !iop.isString(onReturnExpr) ?
|
var result = onReturnExpr == null || !iop.isString(onReturnExpr) ?
|
||||||
info.getResult()
|
info.getResult()
|
||||||
:
|
:
|
||||||
InstrumentorEvalNode.asSuspendedEval(onReturnExpr, info);
|
InstrumentorEvalNode.asSuspendedEval(EnsoLanguage.get(target.getRootNode()), onReturnExpr, info);
|
||||||
iop.execute(onReturn, info.getId().toString(), result);
|
iop.execute(onReturn, info.getId().toString(), result);
|
||||||
}
|
}
|
||||||
} catch (InteropException ignored) {
|
} catch (Throwable ignored) {
|
||||||
|
CompilerDirectives.transferToInterpreter();
|
||||||
|
ignored.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package org.enso.interpreter.node.expression.builtin.meta;
|
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.frame.VirtualFrame;
|
||||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||||
import com.oracle.truffle.api.nodes.RootNode;
|
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.EnsoLanguage;
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
import org.enso.interpreter.runtime.callable.Annotation;
|
import org.enso.interpreter.runtime.callable.Annotation;
|
||||||
@ -25,14 +27,22 @@ final class InstrumentorEvalNode extends RootNode {
|
|||||||
new boolean[] {true, true},
|
new boolean[] {true, true},
|
||||||
new CallArgumentInfo[0],
|
new CallArgumentInfo[0],
|
||||||
new Annotation[0]);
|
new Annotation[0]);
|
||||||
private static final RootCallTarget CALL = new InstrumentorEvalNode().getCallTarget();
|
private static Reference<InstrumentorEvalNode> last = new WeakReference<>(null);
|
||||||
|
|
||||||
private InstrumentorEvalNode() {
|
private InstrumentorEvalNode(EnsoLanguage language) {
|
||||||
super(EnsoLanguage.get(null));
|
super(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Function asSuspendedEval(Object expr, IdExecutionService.Info info) {
|
@TruffleBoundary
|
||||||
return new Function(CALL, null, SUSPENDED_EVAL, new Object[] {expr, info}, new Object[0]);
|
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
|
@Override
|
||||||
|
@ -53,7 +53,14 @@ public abstract class PowNode extends IntegerNode {
|
|||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Object doBigInteger(long self, EnsoBigInteger that) {
|
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
|
@Specialization
|
||||||
@ -69,10 +76,11 @@ public abstract class PowNode extends IntegerNode {
|
|||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) {
|
Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) {
|
||||||
if (that.getValue().signum() > 0) {
|
var thatValue = that.getValue();
|
||||||
return Math.pow(
|
if (thatValue.signum() > 0) {
|
||||||
BigIntegerOps.toDouble(self.getValue()), BigIntegerOps.toDouble(that.getValue()));
|
var selfValue = self.getValue();
|
||||||
} else if (that.getValue().signum() == 0) {
|
return Math.pow(BigIntegerOps.toDouble(selfValue), BigIntegerOps.toDouble(thatValue));
|
||||||
|
} else if (thatValue.signum() == 0) {
|
||||||
return 1.0D;
|
return 1.0D;
|
||||||
} else {
|
} else {
|
||||||
return 0.0D;
|
return 0.0D;
|
||||||
|
@ -39,7 +39,6 @@ import org.enso.librarymanager.resolved.LibraryRoot;
|
|||||||
import org.enso.pkg.Package;
|
import org.enso.pkg.Package;
|
||||||
import org.enso.pkg.PackageManager;
|
import org.enso.pkg.PackageManager;
|
||||||
import org.enso.pkg.QualifiedName;
|
import org.enso.pkg.QualifiedName;
|
||||||
import org.enso.polyglot.ForeignLanguage;
|
|
||||||
import org.enso.polyglot.LanguageInfo;
|
import org.enso.polyglot.LanguageInfo;
|
||||||
import org.enso.polyglot.RuntimeOptions;
|
import org.enso.polyglot.RuntimeOptions;
|
||||||
import org.enso.polyglot.debugger.IdExecutionService;
|
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;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.InstrumentInfo;
|
|
||||||
import com.oracle.truffle.api.ThreadLocalAction;
|
import com.oracle.truffle.api.ThreadLocalAction;
|
||||||
import com.oracle.truffle.api.Truffle;
|
import com.oracle.truffle.api.Truffle;
|
||||||
import com.oracle.truffle.api.TruffleFile;
|
import com.oracle.truffle.api.TruffleFile;
|
||||||
@ -210,7 +208,7 @@ public final class EnsoContext {
|
|||||||
|
|
||||||
var preinit = environment.getOptions().get(RuntimeOptions.PREINITIALIZE_KEY);
|
var preinit = environment.getOptions().get(RuntimeOptions.PREINITIALIZE_KEY);
|
||||||
if (preinit != null && preinit.length() > 0) {
|
if (preinit != null && preinit.length() > 0) {
|
||||||
var epb = environment.getInternalLanguages().get(ForeignLanguage.ID);
|
var epb = environment.getInternalLanguages().get("epb");
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
var run = (Consumer<String>) environment.lookup(epb, Consumer.class);
|
var run = (Consumer<String>) environment.lookup(epb, Consumer.class);
|
||||||
if (run != null) {
|
if (run != null) {
|
||||||
|
@ -37,11 +37,20 @@ final class Array implements EnsoObject {
|
|||||||
*
|
*
|
||||||
* @param items the element values
|
* @param items the element values
|
||||||
*/
|
*/
|
||||||
Array(Object... items) {
|
private Array(Object... items) {
|
||||||
assert noNulls(items);
|
|
||||||
this.items = 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) {
|
private static boolean noNulls(Object[] arr) {
|
||||||
for (Object o : arr) {
|
for (Object o : arr) {
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
|
@ -188,7 +188,7 @@ final class ArrayBuilder implements EnsoObject {
|
|||||||
if (arr instanceof double[] doubles) {
|
if (arr instanceof double[] doubles) {
|
||||||
yield Vector.fromDoubleArray(doubles);
|
yield Vector.fromDoubleArray(doubles);
|
||||||
}
|
}
|
||||||
yield Vector.fromInteropArray(new Array((Object[])arr));
|
yield Vector.fromInteropArray(Array.wrap((Object[])arr));
|
||||||
}
|
}
|
||||||
default -> throw UnknownIdentifierException.create(name);
|
default -> throw UnknownIdentifierException.create(name);
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@ package org.enso.interpreter.runtime.data.vector;
|
|||||||
import com.oracle.truffle.api.dsl.Cached;
|
import com.oracle.truffle.api.dsl.Cached;
|
||||||
import com.oracle.truffle.api.dsl.NeverDefault;
|
import com.oracle.truffle.api.dsl.NeverDefault;
|
||||||
import com.oracle.truffle.api.dsl.Specialization;
|
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.InteropLibrary;
|
||||||
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
|
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
|
||||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||||
@ -33,7 +34,10 @@ public abstract class ArrayLikeAtNode extends Node {
|
|||||||
try {
|
try {
|
||||||
return self.getItems()[Math.toIntExact(index)];
|
return self.getItems()[Math.toIntExact(index)];
|
||||||
} catch (ArithmeticException | IndexOutOfBoundsException ex) {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +64,7 @@ public final class ArrayLikeHelpers {
|
|||||||
* @return the array instance
|
* @return the array instance
|
||||||
*/
|
*/
|
||||||
public static EnsoObject allocate(long size) {
|
public static EnsoObject allocate(long size) {
|
||||||
var arr = new Object[Math.toIntExact(size)];
|
return Array.allocate(size);
|
||||||
return new Array(arr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Builtin.Method(
|
@Builtin.Method(
|
||||||
@ -107,7 +106,7 @@ public final class ArrayLikeHelpers {
|
|||||||
return Vector.fromDoubleArray(doubles);
|
return Vector.fromDoubleArray(doubles);
|
||||||
}
|
}
|
||||||
if (nonTrivialEnsoValue) {
|
if (nonTrivialEnsoValue) {
|
||||||
return Vector.fromInteropArray(new Array((Object[])res));
|
return Vector.fromInteropArray(Array.wrap((Object[])res));
|
||||||
} else {
|
} else {
|
||||||
return Vector.fromEnsoOnlyArray((Object[])res);
|
return Vector.fromEnsoOnlyArray((Object[])res);
|
||||||
}
|
}
|
||||||
@ -136,15 +135,15 @@ public final class ArrayLikeHelpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static EnsoObject wrapEnsoObjects(EnsoObject... arr) {
|
public static EnsoObject wrapEnsoObjects(EnsoObject... arr) {
|
||||||
return new Array((Object[]) arr);
|
return Array.wrap((Object[]) arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnsoObject wrapStrings(String... arr) {
|
public static EnsoObject wrapStrings(String... arr) {
|
||||||
return new Array((Object[]) arr);
|
return Array.wrap((Object[]) arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnsoObject wrapObjectsWithCheckAt(Object... arr) {
|
public static EnsoObject wrapObjectsWithCheckAt(Object... arr) {
|
||||||
return new Array((Object[]) arr);
|
return Array.wrap((Object[]) arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EnsoObject empty() {
|
public static EnsoObject empty() {
|
||||||
@ -152,7 +151,7 @@ public final class ArrayLikeHelpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static EnsoObject asVectorWithCheckAt(Object... arr) {
|
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) {
|
public static EnsoObject asVectorFromArray(Object storage) {
|
||||||
|
@ -187,7 +187,11 @@ public class DebugLocalScope implements EnsoObject {
|
|||||||
|
|
||||||
@ExportMessage
|
@ExportMessage
|
||||||
@TruffleBoundary
|
@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);
|
FramePointer framePtr = allBindings.get(member);
|
||||||
var value = getValue(frame, framePtr);
|
var value = getValue(frame, framePtr);
|
||||||
return value != null ? value : DataflowError.UNINITIALIZED;
|
return value != null ? value : DataflowError.UNINITIALIZED;
|
||||||
|
@ -62,7 +62,6 @@ import org.enso.compiler.pass.resolve.{
|
|||||||
TypeNames,
|
TypeNames,
|
||||||
TypeSignatures
|
TypeSignatures
|
||||||
}
|
}
|
||||||
import org.enso.polyglot.ForeignLanguage
|
|
||||||
import org.enso.interpreter.node.callable.argument.ReadArgumentNode
|
import org.enso.interpreter.node.callable.argument.ReadArgumentNode
|
||||||
import org.enso.interpreter.node.callable.argument.ReadArgumentCheckNode
|
import org.enso.interpreter.node.callable.argument.ReadArgumentCheckNode
|
||||||
import org.enso.interpreter.node.callable.function.{
|
import org.enso.interpreter.node.callable.function.{
|
||||||
@ -1897,7 +1896,7 @@ class IrToTruffle(
|
|||||||
val bodyExpr = body match {
|
val bodyExpr = body match {
|
||||||
case Foreign.Definition(lang, code, _, _, _) =>
|
case Foreign.Definition(lang, code, _, _, _) =>
|
||||||
buildForeignBody(
|
buildForeignBody(
|
||||||
ForeignLanguage.getBySyntacticTag(lang),
|
lang,
|
||||||
code,
|
code,
|
||||||
arguments.map(_.name.name),
|
arguments.map(_.name.name),
|
||||||
argSlotIdxs
|
argSlotIdxs
|
||||||
@ -1958,12 +1957,13 @@ class IrToTruffle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def buildForeignBody(
|
private def buildForeignBody(
|
||||||
language: ForeignLanguage,
|
language: String,
|
||||||
code: String,
|
code: String,
|
||||||
argumentNames: List[String],
|
argumentNames: List[String],
|
||||||
argumentSlotIdxs: List[Int]
|
argumentSlotIdxs: List[Int]
|
||||||
): RuntimeExpression = {
|
): RuntimeExpression = {
|
||||||
val src = language.buildSource(code, scopeName)
|
val src =
|
||||||
|
Source.newBuilder("epb", language + "#" + code, scopeName).build()
|
||||||
val foreignCt = context.parseInternal(src, argumentNames: _*)
|
val foreignCt = context.parseInternal(src, argumentNames: _*)
|
||||||
val argumentReaders = argumentSlotIdxs
|
val argumentReaders = argumentSlotIdxs
|
||||||
.map(slotIdx =>
|
.map(slotIdx =>
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package org.enso.benchmarks.processor;
|
package org.enso.benchmarks.processor;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -73,13 +70,12 @@ public class BenchProcessor extends AbstractProcessor {
|
|||||||
"import org.enso.benchmarks.Utils;");
|
"import org.enso.benchmarks.Utils;");
|
||||||
|
|
||||||
public BenchProcessor() {
|
public BenchProcessor() {
|
||||||
ensoDir = Utils.findRepoRootDir();
|
ensoDir = Utils.findRepoRootDir();
|
||||||
|
|
||||||
// Note that ensoHomeOverride does not have to exist, only its parent directory
|
// Note that ensoHomeOverride does not have to exist, only its parent directory
|
||||||
ensoHomeOverride = ensoDir.toPath().resolve("distribution").resolve("component").toFile();
|
ensoHomeOverride = ensoDir.toPath().resolve("distribution").resolve("component").toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SourceVersion getSupportedSourceVersion() {
|
public SourceVersion getSupportedSourceVersion() {
|
||||||
return SourceVersion.latest();
|
return SourceVersion.latest();
|
||||||
|
@ -38,7 +38,7 @@ public abstract class Builder {
|
|||||||
case BigIntegerType x -> new BigIntegerBuilder(size, problemAggregator);
|
case BigIntegerType x -> new BigIntegerBuilder(size, problemAggregator);
|
||||||
case null -> new InferredBuilder(size, problemAggregator);
|
case null -> new InferredBuilder(size, problemAggregator);
|
||||||
};
|
};
|
||||||
assert builder.getType().equals(type);
|
assert java.util.Objects.equals(builder.getType(), type);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,22 +27,23 @@ type Filter
|
|||||||
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
||||||
newLast = pair.value
|
newLast = pair.value
|
||||||
set_last newLast
|
set_last newLast
|
||||||
Filter.Head head.first pair.fill
|
[True, Filter.Head head.first pair.fill]
|
||||||
|
|
||||||
case self of
|
case self of
|
||||||
Filter.Empty ->
|
Filter.Empty ->
|
||||||
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
||||||
Filter.Head pair.value pair.fill
|
[True, Filter.Head pair.value pair.fill]
|
||||||
Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else self
|
Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else [False, Nothing]
|
||||||
|
|
||||||
type Primes
|
type Primes
|
||||||
Alg generator filter
|
Alg generator filter
|
||||||
|
|
||||||
Primes.next self = case self of
|
Primes.next self = case self of
|
||||||
Primes.Alg g f ->
|
Primes.Alg g f ->
|
||||||
filter = f.acceptAndAdd g.n
|
found_new = f.acceptAndAdd g.n
|
||||||
new_primes = Primes.Alg g.next filter
|
if found_new.first then Primes.Alg g.next found_new.last else
|
||||||
if Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes
|
new_primes = Primes.Alg g.next f
|
||||||
|
@Tail_Call new_primes.next
|
||||||
|
|
||||||
Primes.last_prime self = case self of
|
Primes.last_prime self = case self of
|
||||||
Primes.Alg g _ -> g.n - 1
|
Primes.Alg g _ -> g.n - 1
|
||||||
|
@ -33,7 +33,7 @@ foreign js filter_in_js = """
|
|||||||
while (filter != null) {
|
while (filter != null) {
|
||||||
if (filter.number) {
|
if (filter.number) {
|
||||||
if (n % filter.number === 0) {
|
if (n % filter.number === 0) {
|
||||||
return null;
|
return [false, null];
|
||||||
}
|
}
|
||||||
if (filter.number > sqrt) {
|
if (filter.number > sqrt) {
|
||||||
break;
|
break;
|
||||||
@ -44,7 +44,7 @@ foreign js filter_in_js = """
|
|||||||
var newFilter = new Filter(n);
|
var newFilter = new Filter(n);
|
||||||
this.last.next = newFilter;
|
this.last.next = newFilter;
|
||||||
this.last = newFilter;
|
this.last = newFilter;
|
||||||
return this;
|
return [true, this];
|
||||||
};
|
};
|
||||||
return new Filter(null);
|
return new Filter(null);
|
||||||
|
|
||||||
@ -65,9 +65,10 @@ type Primes
|
|||||||
|
|
||||||
Primes.next self = case self of
|
Primes.next self = case self of
|
||||||
Primes.Alg g f ->
|
Primes.Alg g f ->
|
||||||
filter = f.acceptAndAdd g.n
|
found_new = f.acceptAndAdd g.n
|
||||||
new_primes = Primes.Alg g.next filter
|
if found_new.first then Primes.Alg g.next found_new.last else
|
||||||
if Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes
|
new_primes = Primes.Alg g.next f
|
||||||
|
@Tail_Call new_primes.next
|
||||||
|
|
||||||
Primes.last_prime self = case self of
|
Primes.last_prime self = case self of
|
||||||
Primes.Alg g _ -> g.n - 1
|
Primes.Alg g _ -> g.n - 1
|
||||||
|
@ -27,22 +27,23 @@ type Filter
|
|||||||
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
||||||
newLast = pair.value
|
newLast = pair.value
|
||||||
set_last newLast
|
set_last newLast
|
||||||
Filter.Head head.first pair.fill
|
[True, Filter.Head head.first pair.fill]
|
||||||
|
|
||||||
case self of
|
case self of
|
||||||
Filter.Empty ->
|
Filter.Empty ->
|
||||||
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
||||||
Filter.Head pair.value pair.fill
|
[True, Filter.Head pair.value pair.fill]
|
||||||
Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else self
|
Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else [False, Nothing]
|
||||||
|
|
||||||
type Primes
|
type Primes
|
||||||
Alg generator filter
|
Alg generator filter
|
||||||
|
|
||||||
Primes.next self = case self of
|
Primes.next self = case self of
|
||||||
Primes.Alg g f ->
|
Primes.Alg g f ->
|
||||||
filter = f.acceptAndAdd g.n
|
found_new = f.acceptAndAdd g.n
|
||||||
new_primes = Primes.Alg g.next filter
|
if found_new.first then Primes.Alg g.next found_new.last else
|
||||||
if Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes
|
new_primes = Primes.Alg g.next f
|
||||||
|
@Tail_Call new_primes.next
|
||||||
|
|
||||||
Primes.last_prime self = case self of
|
Primes.last_prime self = case self of
|
||||||
Primes.Alg g _ -> g.n - 1
|
Primes.Alg g _ -> g.n - 1
|
||||||
|
@ -24,23 +24,24 @@ type Filter
|
|||||||
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
||||||
newLast = pair.value
|
newLast = pair.value
|
||||||
set_last newLast
|
set_last newLast
|
||||||
Filter.Head head.first pair.fill
|
[True, Filter.Head head.first pair.fill]
|
||||||
|
|
||||||
case self of
|
case self of
|
||||||
Filter.Empty ->
|
Filter.Empty ->
|
||||||
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
pair = Meta.atom_with_hole (h -> Filter.Item n h)
|
||||||
Filter.Head pair.value pair.fill
|
[True, Filter.Head pair.value pair.fill]
|
||||||
Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else self
|
Filter.Head _ _ -> if iterate self.first then appendN self self.set_last else
|
||||||
|
[False, Nothing]
|
||||||
|
|
||||||
type Primes
|
type Primes
|
||||||
Alg generator filter
|
Alg generator filter
|
||||||
|
|
||||||
Primes.next self = case self of
|
Primes.next self = case self of
|
||||||
Primes.Alg g f ->
|
Primes.Alg g f ->
|
||||||
filter = f.acceptAndAdd g.n
|
found_new = f.acceptAndAdd g.n
|
||||||
nf = if filter.is_nothing then f else filter
|
if found_new.first then Primes.Alg g.next found_new.last else
|
||||||
new_primes = Primes.Alg g.next nf
|
new_primes = Primes.Alg g.next f
|
||||||
if filter.is_nothing || Meta.is_same_object filter f then @Tail_Call new_primes.next else new_primes
|
@Tail_Call new_primes.next
|
||||||
|
|
||||||
Primes.last_prime self = case self of
|
Primes.last_prime self = case self of
|
||||||
Primes.Alg g _ -> g.n - 1
|
Primes.Alg g _ -> g.n - 1
|
||||||
|
@ -173,6 +173,14 @@ spec =
|
|||||||
_ -> False
|
_ -> False
|
||||||
num_double_match.should_be_true
|
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" <|
|
Test.specify "should make Python None values equal to Nothing" <|
|
||||||
py_null = make_null
|
py_null = make_null
|
||||||
py_null . should_equal Nothing
|
py_null . should_equal Nothing
|
||||||
|
Loading…
Reference in New Issue
Block a user