mirror of
https://github.com/enso-org/enso.git
synced 2024-11-29 16:57:44 +03:00
Upgrade to GraalVM 22.3.0 (#3663)
Upgrading to GraalVM 22.3.0. # Important Notes - Removed all deprecated `FrameSlot`, and replaced them with frame indexes - integers. - Add more information to `AliasAnalysis` so that it also gathers these indexes. - Add quick build mode option to `native-image` as default for non-release builds - `graaljs` and `native-image` should now be downloaded via `gu` automatically, as dependencies. - Remove `engine-runner-native` project - native image is now build straight from `engine-runner`. - We used to have `engine-runner-native` without `sqldf` in classpath as a workaround for an internal native image bug. - Fixed chrome inspector integration, such that it shows values of local variables both for current stack frame and caller stack frames. - There are still many issues with the debugging in general, for example, when there is a polyglot value among local variables, a `NullPointerException` is thrown and no values are displayed. - Removed some deprecated `native-image` options - Remove some deprecated Truffle API method calls.
This commit is contained in:
parent
deb670785c
commit
402ebb2f8e
2
.github/workflows/formatting.yml
vendored
2
.github/workflows/formatting.yml
vendored
@ -8,7 +8,7 @@ on:
|
||||
|
||||
env:
|
||||
# Please ensure that this is in sync with graalVersion in build.sbt
|
||||
graalVersion: 21.3.0
|
||||
graalVersion: 22.3.0
|
||||
# Please ensure that this is in sync with javaVersion in build.sbt
|
||||
javaVersion: 11
|
||||
# Please ensure that this is in sync with project/build.properties
|
||||
|
@ -6,7 +6,7 @@ on:
|
||||
|
||||
env:
|
||||
# Please ensure that this is in sync with graalVersion in build.sbt
|
||||
graalVersion: 21.3.0
|
||||
graalVersion: 22.3.0
|
||||
# Please ensure that this is in sync with javaVersion in build.sbt
|
||||
javaVersion: 11
|
||||
# Please ensure that this is in sync with project/build.properties
|
||||
|
@ -444,6 +444,8 @@
|
||||
- [Accept Array-like object seamlessly in builtins][3817]
|
||||
- [Initialize Builtins at Native Image build time][3821]
|
||||
- [Split Atom suggestion entry to Type and Constructor][3835]
|
||||
- [Update to GraalVM 22.3.0][3663]
|
||||
- [Connecting IGV 4 Enso with Engine sources][3810]
|
||||
- [Add the `Self` keyword referring to current type][3844]
|
||||
- [Support VCS for projects in Language Server][3851]
|
||||
- [Support multiple exports of the same module][3897]
|
||||
@ -509,6 +511,8 @@
|
||||
[3821]: https://github.com/enso-org/enso/pull/3821
|
||||
[3828]: https://github.com/enso-org/enso/pull/3828
|
||||
[3835]: https://github.com/enso-org/enso/pull/3835
|
||||
[3663]: https://github.com/enso-org/enso/pull/3663
|
||||
[3810]: https://github.com/enso-org/enso/pull/3810
|
||||
[3844]: https://github.com/enso-org/enso/pull/3844
|
||||
[3851]: https://github.com/enso-org/enso/pull/3851
|
||||
[3897]: https://github.com/enso-org/enso/pull/3897
|
||||
|
119
build.sbt
119
build.sbt
@ -7,6 +7,7 @@ import sbt.addCompilerPlugin
|
||||
import sbt.complete.DefaultParsers._
|
||||
import sbt.complete.Parser
|
||||
import sbt.nio.file.FileTreeView
|
||||
import sbt.internal.util.ManagedLogger
|
||||
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
|
||||
import src.main.scala.licenses.{
|
||||
DistributionDescription,
|
||||
@ -20,7 +21,7 @@ import java.io.File
|
||||
// ============================================================================
|
||||
|
||||
val scalacVersion = "2.13.8"
|
||||
val graalVersion = "21.3.0"
|
||||
val graalVersion = "22.3.0"
|
||||
val javaVersion = "11"
|
||||
val defaultDevEnsoVersion = "0.0.0-dev"
|
||||
val ensoVersion = sys.env.getOrElse(
|
||||
@ -983,6 +984,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager"))
|
||||
initializeAtRuntime = Seq("scala.util.Random")
|
||||
)
|
||||
.dependsOn(VerifyReflectionSetup.run)
|
||||
.dependsOn(installNativeImage)
|
||||
.dependsOn(assembly)
|
||||
.value,
|
||||
buildNativeImage := NativeImage
|
||||
@ -1303,6 +1305,79 @@ lazy val `runtime-language-epb` =
|
||||
instrumentationSettings
|
||||
)
|
||||
|
||||
/**
|
||||
* Runs gu (GraalVM updater) command with given `args`.
|
||||
* For example `runGu(Seq("install", "js"))`.
|
||||
* @param logger Logger for the `gu` command.
|
||||
* @param args Arguments for the `gu` command.
|
||||
*/
|
||||
def runGu(logger: ManagedLogger, args: Seq[String]): String = {
|
||||
val javaHome = new File(
|
||||
System.getProperty("java.home")
|
||||
)
|
||||
val os = {
|
||||
if (Platform.isLinux) DistributionPackage.OS.Linux
|
||||
else if (Platform.isMacOS) DistributionPackage.OS.MacOS
|
||||
else if (Platform.isWindows) DistributionPackage.OS.Windows
|
||||
else throw new RuntimeException("Unknown platform")
|
||||
}
|
||||
packageBuilder.gu(
|
||||
logger,
|
||||
os,
|
||||
javaHome,
|
||||
args:_*
|
||||
)
|
||||
}
|
||||
|
||||
def installedGuComponents(logger: ManagedLogger): Seq[String] = {
|
||||
val componentList = runGu(
|
||||
logger,
|
||||
Seq("list")
|
||||
)
|
||||
val components = componentList
|
||||
.linesIterator
|
||||
.drop(2)
|
||||
.map { line =>
|
||||
line
|
||||
.split(" ")
|
||||
.head
|
||||
}
|
||||
.toList
|
||||
logger.debug(s"Installed GU components = $components")
|
||||
if (!components.contains("graalvm")) {
|
||||
throw new RuntimeException(s"graalvm components is not in $components")
|
||||
}
|
||||
components
|
||||
}
|
||||
|
||||
lazy val installGraalJs = taskKey[Unit]("Install graaljs GraalVM component")
|
||||
ThisBuild / installGraalJs := {
|
||||
val logger = streams.value.log
|
||||
if (!installedGuComponents(logger).contains("js")) {
|
||||
logger.info("Installing js GraalVM component")
|
||||
runGu(
|
||||
logger,
|
||||
Seq("install", "js")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
lazy val installNativeImage = taskKey[Unit]("Install native-image GraalVM component")
|
||||
ThisBuild / installNativeImage := {
|
||||
val logger = streams.value.log
|
||||
if (!installedGuComponents(logger).contains("native-image")) {
|
||||
logger.info("Installing native-image GraalVM component")
|
||||
runGu(
|
||||
logger,
|
||||
Seq("install", "native-image")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ThisBuild / installNativeImage := {
|
||||
(ThisBuild / installNativeImage).result.value
|
||||
}
|
||||
|
||||
lazy val runtime = (project in file("engine/runtime"))
|
||||
.configs(Benchmark)
|
||||
.settings(
|
||||
@ -1381,6 +1456,7 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
.settings(
|
||||
(Compile / compile) := (Compile / compile)
|
||||
.dependsOn(Def.task { (Compile / sourceManaged).value.mkdirs })
|
||||
.dependsOn(installGraalJs)
|
||||
.value
|
||||
)
|
||||
.settings(
|
||||
@ -1555,34 +1631,8 @@ lazy val `engine-runner` = project
|
||||
.settings(
|
||||
assembly := assembly
|
||||
.dependsOn(`runtime-with-instruments` / assembly)
|
||||
.value
|
||||
)
|
||||
.dependsOn(`version-output`)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(cli)
|
||||
.dependsOn(`library-manager`)
|
||||
.dependsOn(`language-server`)
|
||||
.dependsOn(`polyglot-api`)
|
||||
.dependsOn(`logging-service`)
|
||||
|
||||
// A workaround for https://github.com/oracle/graal/issues/4200 until we upgrade to GraalVM 22.x.
|
||||
// sqlite-jdbc jar is problematic and had to be exluded for the purposes of building a native
|
||||
// image of the runner.
|
||||
lazy val `engine-runner-native` = project
|
||||
.in(file("engine/runner-native"))
|
||||
.settings(
|
||||
assembly / assemblyExcludedJars := {
|
||||
val cp = (assembly / fullClasspath).value
|
||||
(assembly / assemblyExcludedJars).value ++
|
||||
cp.filter(_.data.getName.startsWith("sqlite-jdbc"))
|
||||
},
|
||||
assembly / mainClass := (`engine-runner` / assembly / mainClass).value,
|
||||
assembly / assemblyMergeStrategy := (`engine-runner` / assembly / assemblyMergeStrategy).value,
|
||||
assembly / assemblyJarName := "runner-native.jar",
|
||||
assembly / assemblyOutputPath := file("runner-native.jar"),
|
||||
assembly := assembly
|
||||
.dependsOn(`engine-runner` / assembly)
|
||||
.value,
|
||||
|
||||
rebuildNativeImage := NativeImage
|
||||
.buildNativeImage(
|
||||
"runner",
|
||||
@ -1590,7 +1640,6 @@ lazy val `engine-runner-native` = project
|
||||
additionalOptions = Seq(
|
||||
"-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog",
|
||||
"-H:IncludeResources=.*Main.enso$",
|
||||
"--allow-incomplete-classpath",
|
||||
"--macro:truffle",
|
||||
"--language:js",
|
||||
// "-g",
|
||||
@ -1606,9 +1655,10 @@ lazy val `engine-runner-native` = project
|
||||
"io.methvin.watchservice.jna.CarbonAPI"
|
||||
)
|
||||
)
|
||||
.dependsOn(installNativeImage)
|
||||
.dependsOn(assembly)
|
||||
.dependsOn(VerifyReflectionSetup.run)
|
||||
.value,
|
||||
|
||||
buildNativeImage := NativeImage
|
||||
.incrementalNativeImageBuild(
|
||||
rebuildNativeImage,
|
||||
@ -1616,7 +1666,13 @@ lazy val `engine-runner-native` = project
|
||||
)
|
||||
.value
|
||||
)
|
||||
.dependsOn(`engine-runner`)
|
||||
.dependsOn(`version-output`)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(cli)
|
||||
.dependsOn(`library-manager`)
|
||||
.dependsOn(`language-server`)
|
||||
.dependsOn(`polyglot-api`)
|
||||
.dependsOn(`logging-service`)
|
||||
|
||||
lazy val launcher = project
|
||||
.in(file("engine/launcher"))
|
||||
@ -1646,6 +1702,7 @@ lazy val launcher = project
|
||||
"org.enso.loggingservice.WSLoggerManager$"
|
||||
)
|
||||
)
|
||||
.dependsOn(installNativeImage)
|
||||
.dependsOn(assembly)
|
||||
.dependsOn(VerifyReflectionSetup.run)
|
||||
.value,
|
||||
|
@ -269,7 +269,7 @@ impl RunContext {
|
||||
ret.packages.engine = Some(self.paths.engine.clone());
|
||||
}
|
||||
if build_native_runner {
|
||||
tasks.push("engine-runner-native/buildNativeImage");
|
||||
tasks.push("engine-runner/buildNativeImage");
|
||||
}
|
||||
|
||||
if TARGET_OS != OS::Windows {
|
||||
|
12
build/ci_utils/src/cache/goodie/graalvm.rs
vendored
12
build/ci_utils/src/cache/goodie/graalvm.rs
vendored
@ -134,7 +134,7 @@ mod tests {
|
||||
#[ignore]
|
||||
async fn test_is_enabled() -> Result {
|
||||
setup_logging()?;
|
||||
let graal_version = Version::parse("21.3.0").unwrap();
|
||||
let graal_version = Version::parse("22.3.0").unwrap();
|
||||
let java_version = java::LanguageVersion(11);
|
||||
let os = TARGET_OS;
|
||||
let arch = Arch::X86_64;
|
||||
@ -152,14 +152,14 @@ mod tests {
|
||||
/// Check that we correctly recognize both the GraalVM version and the Java version.
|
||||
#[test]
|
||||
fn version_recognize() {
|
||||
let version_string = r"openjdk 11.0.13 2021-10-19
|
||||
OpenJDK Runtime Environment GraalVM CE 21.3.0 (build 11.0.13+7-jvmci-21.3-b05)
|
||||
OpenJDK 64-Bit Server VM GraalVM CE 21.3.0 (build 11.0.13+7-jvmci-21.3-b05, mixed mode, sharing)";
|
||||
let version_string = r"openjdk 11.0.17 2022-10-18
|
||||
OpenJDK Runtime Environment GraalVM CE 22.3.0 (build 11.0.17+8-jvmci-22.3-b08)
|
||||
OpenJDK 64-Bit Server VM GraalVM CE 22.3.0 (build 11.0.17+8-jvmci-22.3-b08, mixed mode, sharing)";
|
||||
|
||||
let found_graal = graal_version_from_version_string(version_string).unwrap();
|
||||
assert_eq!(found_graal, Version::new(21, 3, 0));
|
||||
assert_eq!(found_graal, Version::new(22, 3, 0));
|
||||
|
||||
let found_java = Java.parse_version(version_string).unwrap();
|
||||
assert_eq!(found_java, Version::new(11, 0, 13));
|
||||
assert_eq!(found_java, Version::new(11, 0, 17));
|
||||
}
|
||||
}
|
||||
|
@ -529,6 +529,12 @@ enso --run test/Geo_Tests
|
||||
enso --run test/Table_Tests
|
||||
```
|
||||
|
||||
Or to run just a single test (e.g., `Duration_Spec.enso`):
|
||||
|
||||
```bash
|
||||
enso --in-project test/Tests --run test/Tests/src/Data/Time/Duration_Spec.enso
|
||||
```
|
||||
|
||||
The Database tests will by default only test the SQLite backend, to test other
|
||||
backends see
|
||||
[`test/Table_Tests/src/Database/README.md`](../test/Table_Tests/src/Database/README.md)
|
||||
|
@ -206,7 +206,7 @@ state. Limitations are currently mostly due to
|
||||
of stdlib components. To generate the Native Image for runner simply execute
|
||||
|
||||
```
|
||||
sbt> engine-runner-native/buildNativeImage
|
||||
sbt> engine-runner/buildNativeImage
|
||||
```
|
||||
|
||||
and execute the binary on a sample factorial test program
|
||||
|
@ -35,8 +35,9 @@ to perform the following tasks:
|
||||
configuration. This is both a version number and (if it is changed), the
|
||||
associated version of Java.
|
||||
- Change the expected GraalVM version in the
|
||||
[`scala.yml`](../../.github/workflows/scala.yml) and
|
||||
[`release.yml`](../../.github/workflows/release.yml) workflows.
|
||||
[`release-publish-edition`](../../.github/workflows/release-publish-edition.yml)
|
||||
workflow.
|
||||
- Change the base image in the [`Dockerfile`](../../tools/ci/docker/Dockerfile)
|
||||
to contain the correct GraalVM version.
|
||||
- Just to be sure, search for the version regex in all the files in the repo.
|
||||
- Ensure that all deprecations have been handled.
|
||||
|
@ -1,7 +1,8 @@
|
||||
package org.enso.languageserver.io
|
||||
|
||||
import java.io.OutputStream
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary
|
||||
|
||||
import java.io.OutputStream
|
||||
import org.enso.languageserver.io.ObservableOutputStream.OutputObserver
|
||||
|
||||
/** An observable output stream of bytes. It accepts output bytes
|
||||
@ -15,11 +16,13 @@ class ObservableOutputStream extends OutputStream {
|
||||
private var observers = Set.empty[OutputObserver]
|
||||
|
||||
/** @inheritdoc */
|
||||
@TruffleBoundary
|
||||
override def write(byte: Int): Unit = lock.synchronized {
|
||||
notify(Array[Byte](byte.toByte))
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
@TruffleBoundary
|
||||
override def write(bytes: Array[Byte]): Unit = lock.synchronized {
|
||||
if (bytes.length > 0) {
|
||||
notify(bytes)
|
||||
@ -27,6 +30,7 @@ class ObservableOutputStream extends OutputStream {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
@TruffleBoundary
|
||||
override def write(bytes: Array[Byte], off: Int, len: Int): Unit =
|
||||
lock.synchronized {
|
||||
if (len > 0) {
|
||||
@ -52,6 +56,7 @@ class ObservableOutputStream extends OutputStream {
|
||||
observers -= observer
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
protected def notify(output: Array[Byte]): Unit = {
|
||||
observers foreach { _.update(output) }
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
||||
}
|
||||
|
||||
private Object getValue(MaterializedFrame frame, FramePointer ptr) {
|
||||
return getProperFrame(frame, ptr).getValue(ptr.getFrameSlot());
|
||||
return getProperFrame(frame, ptr).getValue(ptr.getFrameSlotIdx());
|
||||
}
|
||||
|
||||
private MaterializedFrame getProperFrame(MaterializedFrame frame, FramePointer ptr) {
|
||||
|
@ -39,7 +39,12 @@ public class EpbContext {
|
||||
if (!isInner) {
|
||||
innerContext =
|
||||
new GuardedTruffleContext(
|
||||
env.newContextBuilder().config(INNER_OPTION, "yes").build(), true);
|
||||
env.newInnerContextBuilder()
|
||||
.initializeCreatorContext(true)
|
||||
.inheritAllAccess(true)
|
||||
.config(INNER_OPTION, "yes")
|
||||
.build(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,8 @@ public class EpbLanguage extends TruffleLanguage<EpbContext> {
|
||||
@Override
|
||||
protected CallTarget parse(ParsingRequest request) {
|
||||
EpbParser.Result code = EpbParser.parse(request.getSource());
|
||||
return Truffle.getRuntime()
|
||||
.createCallTarget(ForeignEvalNode.build(this, code, request.getArgumentNames()));
|
||||
ForeignEvalNode foreignEvalNode = ForeignEvalNode.build(this, code, request.getArgumentNames());
|
||||
return foreignEvalNode.getCallTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,10 +1,12 @@
|
||||
package org.enso.interpreter.epb.node;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
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.TruffleObject;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.strings.TruffleString;
|
||||
|
||||
@GenerateUncached
|
||||
@ReportPolymorphism
|
||||
@ -74,6 +76,12 @@ public abstract class CoercePrimitiveNode extends Node {
|
||||
return s;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
String doTruffleString(
|
||||
TruffleString truffleString, @Cached TruffleString.ToJavaStringNode toJavaStringNode) {
|
||||
return toJavaStringNode.execute(truffleString);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Object doNonPrimitive(TruffleObject value) {
|
||||
return value;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.interpreter.epb.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
@ -49,6 +50,7 @@ public class ForeignParsingException extends AbstractTruffleException {
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
@TruffleBoundary
|
||||
String toDisplayString(boolean hasSideEffects) {
|
||||
return "ForeignParsingException: '" + message + "'";
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package org.enso.interpreter;
|
||||
|
||||
import com.oracle.truffle.api.*;
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.TruffleLogger;
|
||||
import com.oracle.truffle.api.Option;
|
||||
import com.oracle.truffle.api.debug.DebuggerTags;
|
||||
import com.oracle.truffle.api.instrumentation.ProvidedTags;
|
||||
import com.oracle.truffle.api.instrumentation.StandardTags;
|
||||
@ -59,6 +62,7 @@ import org.graalvm.options.OptionType;
|
||||
StandardTags.ExpressionTag.class,
|
||||
StandardTags.StatementTag.class,
|
||||
StandardTags.RootTag.class,
|
||||
StandardTags.RootBodyTag.class,
|
||||
StandardTags.TryBlockTag.class,
|
||||
IdentifiedTag.class,
|
||||
AvoidIdInstrumentationTag.class,
|
||||
@ -167,7 +171,7 @@ public final class Language extends TruffleLanguage<Context> {
|
||||
@Override
|
||||
protected CallTarget parse(ParsingRequest request) {
|
||||
RootNode root = ProgramRootNode.build(this, request.getSource());
|
||||
return Truffle.getRuntime().createCallTarget(root);
|
||||
return root.getCallTarget();
|
||||
}
|
||||
|
||||
@Option(
|
||||
@ -189,7 +193,7 @@ public final class Language extends TruffleLanguage<Context> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top scope of the requested contenxt.
|
||||
* Returns the top scope of the requested context.
|
||||
*
|
||||
* @param context the context holding the top scope
|
||||
* @return the language's top scope
|
||||
|
@ -3,7 +3,6 @@ package org.enso.interpreter.node;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.dsl.ReportPolymorphism;
|
||||
import com.oracle.truffle.api.frame.FrameSlot;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
|
||||
|
@ -2,12 +2,10 @@ package org.enso.interpreter.node;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.ReportPolymorphism;
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.scope.LocalScope;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.enso.interpreter.node;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameSlot;
|
||||
import com.oracle.truffle.api.frame.FrameSlotKind;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.source.Source;
|
||||
|
@ -1,25 +1,31 @@
|
||||
package org.enso.interpreter.node;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.NodeField;
|
||||
import com.oracle.truffle.api.frame.Frame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
|
||||
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
|
||||
import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.instrumentation.StandardTags;
|
||||
import com.oracle.truffle.api.instrumentation.Tag;
|
||||
import com.oracle.truffle.api.interop.NodeLibrary;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.UUID;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.enso.interpreter.runtime.tag.Patchable;
|
||||
import org.enso.interpreter.runtime.scope.DebugLocalScope;
|
||||
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||
import org.enso.interpreter.runtime.tag.Patchable;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/**
|
||||
* A base class for all Enso expressions.
|
||||
@ -32,12 +38,22 @@ import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
||||
* executeGeneric} method for various scenarios in order to improve performance.
|
||||
*/
|
||||
@NodeInfo(shortName = "EnsoExpression", description = "The base node for all enso expressions.")
|
||||
@ExportLibrary(NodeLibrary.class)
|
||||
@GenerateWrapper
|
||||
public abstract class ExpressionNode extends BaseNode implements InstrumentableNode {
|
||||
private @CompilerDirectives.CompilationFinal int sourceStartIndex;
|
||||
private @CompilerDirectives.CompilationFinal int sourceLength;
|
||||
private @CompilerDirectives.CompilationFinal UUID id = null;
|
||||
|
||||
public static boolean isWrapper(ExpressionNode node) {
|
||||
return node instanceof ExpressionNodeWrapper;
|
||||
}
|
||||
|
||||
public static ExpressionNode unwrapDelegate(ExpressionNode wrapperNode) {
|
||||
assert isWrapper(wrapperNode);
|
||||
return ((ExpressionNodeWrapper) wrapperNode).getDelegateNode();
|
||||
}
|
||||
|
||||
/** Creates a new instance of this node. */
|
||||
public ExpressionNode() {
|
||||
sourceLength = EnsoRootNode.NO_SOURCE;
|
||||
@ -63,8 +79,12 @@ public abstract class ExpressionNode extends BaseNode implements InstrumentableN
|
||||
*/
|
||||
@Override
|
||||
public SourceSection getSourceSection() {
|
||||
if (this instanceof ExpressionNodeWrapper wrapper) {
|
||||
return wrapper.getDelegateNode().getSourceSection();
|
||||
} else {
|
||||
return EnsoRootNode.findSourceSection(getRootNode(), sourceStartIndex, sourceLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID associated with this node.
|
||||
@ -188,4 +208,19 @@ public abstract class ExpressionNode extends BaseNode implements InstrumentableN
|
||||
public WrapperNode createWrapper(ProbeNode probe) {
|
||||
return new ExpressionNodeWrapper(this, probe);
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasScope(Frame frame) {
|
||||
return isInstrumentable();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
Object getScope(Frame frame, boolean onEnter) {
|
||||
RootNode rootNode = getRootNode();
|
||||
if (!isInstrumentable() || rootNode == null) {
|
||||
return null;
|
||||
} else {
|
||||
return DebugLocalScope.createFromFrame((EnsoRootNode) rootNode, frame.materialize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.enso.interpreter.node.callable;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
|
@ -66,7 +66,9 @@ public abstract class CaptureCallerInfoNode extends Node {
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
ScopeInfo buildUncachedScopeInfo() {
|
||||
RootCallTarget ct = (RootCallTarget) Truffle.getRuntime().getCurrentFrame().getCallTarget();
|
||||
RootCallTarget ct =
|
||||
Truffle.getRuntime()
|
||||
.iterateFrames(frameInstance -> (RootCallTarget) frameInstance.getCallTarget());
|
||||
EnsoRootNode rootNode = (EnsoRootNode) ct.getRootNode();
|
||||
return new ScopeInfo(rootNode.getLocalScope(), rootNode.getModuleScope());
|
||||
}
|
||||
|
@ -6,9 +6,7 @@ import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.GenerateUncached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.FrameSlot;
|
||||
import com.oracle.truffle.api.frame.FrameSlotKind;
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.LoopNode;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
@ -105,22 +103,23 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
* {@link RepeatingNode}.
|
||||
*/
|
||||
public static final class RepeatedCallNode extends Node implements RepeatingNode {
|
||||
private final FrameSlot resultSlot;
|
||||
private final FrameSlot functionSlot;
|
||||
private final FrameSlot argsSlot;
|
||||
private final FrameSlot stateSlot;
|
||||
private final FrameSlot callerInfoSlot;
|
||||
private final int resultSlotIdx;
|
||||
private final int functionSlotIdx;
|
||||
private final int argsSlotIdx;
|
||||
private final int stateSlotIdx;
|
||||
private final int callerInfoSlotIdx;
|
||||
private final FrameDescriptor descriptor;
|
||||
@Child private ExecuteCallNode dispatchNode;
|
||||
|
||||
/** Creates a new node used for repeating a call. */
|
||||
public RepeatedCallNode() {
|
||||
descriptor = new FrameDescriptor();
|
||||
functionSlot = descriptor.findOrAddFrameSlot("<TCO Function>", FrameSlotKind.Object);
|
||||
resultSlot = descriptor.findOrAddFrameSlot("<TCO Result>", FrameSlotKind.Object);
|
||||
argsSlot = descriptor.findOrAddFrameSlot("<TCO Arguments>", FrameSlotKind.Object);
|
||||
stateSlot = descriptor.findOrAddFrameSlot("<TCO State>", FrameSlotKind.Object);
|
||||
callerInfoSlot = descriptor.findOrAddFrameSlot("<TCO Caller Info>", FrameSlotKind.Object);
|
||||
var descrBuilder = FrameDescriptor.newBuilder();
|
||||
functionSlotIdx = descrBuilder.addSlot(FrameSlotKind.Object, "<TCO Function>", null);
|
||||
resultSlotIdx = descrBuilder.addSlot(FrameSlotKind.Object, "<TCO Result>", null);
|
||||
argsSlotIdx = descrBuilder.addSlot(FrameSlotKind.Object, "<TCO Arguments>", null);
|
||||
stateSlotIdx = descrBuilder.addSlot(FrameSlotKind.Object, "<TCO State>", null);
|
||||
callerInfoSlotIdx = descrBuilder.addSlot(FrameSlotKind.Object, "<TCO Caller Info>", null);
|
||||
descriptor = descrBuilder.build();
|
||||
dispatchNode = ExecuteCallNodeGen.create();
|
||||
}
|
||||
|
||||
@ -138,13 +137,13 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
*/
|
||||
private void setNextCall(
|
||||
VirtualFrame frame, Function function, CallerInfo callerInfo, Object[] arguments) {
|
||||
frame.setObject(functionSlot, function);
|
||||
frame.setObject(callerInfoSlot, callerInfo);
|
||||
frame.setObject(argsSlot, arguments);
|
||||
frame.setObject(functionSlotIdx, function);
|
||||
frame.setObject(callerInfoSlotIdx, callerInfo);
|
||||
frame.setObject(argsSlotIdx, arguments);
|
||||
}
|
||||
|
||||
private void setState(VirtualFrame frame, State state) {
|
||||
frame.setObject(stateSlot, state);
|
||||
frame.setObject(stateSlotIdx, state);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,12 +153,12 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
* @return the result of execution in {@code frame}
|
||||
*/
|
||||
public Object getResult(VirtualFrame frame) {
|
||||
return FrameUtil.getObjectSafe(frame, resultSlot);
|
||||
return frame.getObject(resultSlotIdx);
|
||||
}
|
||||
|
||||
private CallerInfo getCallerInfo(VirtualFrame frame) {
|
||||
CallerInfo result = (CallerInfo) FrameUtil.getObjectSafe(frame, callerInfoSlot);
|
||||
frame.setObject(callerInfoSlot, null);
|
||||
CallerInfo result = (CallerInfo) frame.getObject(callerInfoSlotIdx);
|
||||
frame.setObject(callerInfoSlotIdx, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -170,8 +169,8 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
* @return the function to be executed next in the loop
|
||||
*/
|
||||
public Function getNextFunction(VirtualFrame frame) {
|
||||
Object result = FrameUtil.getObjectSafe(frame, functionSlot);
|
||||
frame.setObject(functionSlot, null);
|
||||
Object result = frame.getObject(functionSlotIdx);
|
||||
frame.setObject(functionSlotIdx, null);
|
||||
return (Function) result;
|
||||
}
|
||||
|
||||
@ -182,7 +181,7 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
* @return the state to pass to the next function
|
||||
*/
|
||||
public Object getNextState(VirtualFrame frame) {
|
||||
return FrameUtil.getObjectSafe(frame, stateSlot);
|
||||
return frame.getObject(stateSlotIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -192,8 +191,8 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
* @return the arguments to be applied to the next function
|
||||
*/
|
||||
public Object[] getNextArgs(VirtualFrame frame) {
|
||||
Object[] result = (Object[]) FrameUtil.getObjectSafe(frame, argsSlot);
|
||||
frame.setObject(argsSlot, null);
|
||||
Object[] result = (Object[]) frame.getObject(argsSlotIdx);
|
||||
frame.setObject(argsSlotIdx, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -212,7 +211,7 @@ public abstract class LoopingCallOptimiserNode extends CallOptimiserNode {
|
||||
Object[] arguments = getNextArgs(frame);
|
||||
CallerInfo callerInfo = getCallerInfo(frame);
|
||||
frame.setObject(
|
||||
resultSlot, dispatchNode.executeCall(function, callerInfo, state, arguments));
|
||||
resultSlotIdx, dispatchNode.executeCall(function, callerInfo, state, arguments));
|
||||
return false;
|
||||
} catch (TailCallException e) {
|
||||
setNextCall(frame, e.getFunction(), e.getCallerInfo(), e.getArguments());
|
||||
|
@ -53,18 +53,32 @@ public class BlockNode extends ExpressionNode {
|
||||
return returnExpr.executeGeneric(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap all the statements inside this block node in {@link StatementNode}. Care is taken not for
|
||||
* wrapping expression twice.
|
||||
*
|
||||
* @return This BlockNode with all the statements wrapped.
|
||||
*/
|
||||
@Override
|
||||
public InstrumentableNode materializeInstrumentableNodes(
|
||||
Set<Class<? extends Tag>> materializedTags) {
|
||||
if (materializedTags.contains(StandardTags.StatementTag.class)) {
|
||||
for (int i = 0; i < statements.length; i++) {
|
||||
if (!isNodeWrapped(statements[i])) {
|
||||
statements[i] = insert(StatementNode.wrap(statements[i]));
|
||||
}
|
||||
this.returnExpr = insert(StatementNode.wrap(returnExpr));
|
||||
}
|
||||
if (!isNodeWrapped(returnExpr)) {
|
||||
returnExpr = insert(StatementNode.wrap(returnExpr));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private static boolean isNodeWrapped(ExpressionNode node) {
|
||||
return node instanceof StatementNode || ExpressionNode.isWrapper(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTag(Class<? extends Tag> tag) {
|
||||
return super.hasTag(tag)
|
||||
|
@ -3,7 +3,6 @@ package org.enso.interpreter.node.callable.thunk;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.NodeChild;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
|
@ -3,7 +3,6 @@ package org.enso.interpreter.node.controlflow.caseexpr;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.NodeChild;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.enso.interpreter.node.controlflow.permission;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
|
@ -1,7 +1,9 @@
|
||||
package org.enso.interpreter.node.expression.builtin.meta;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
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.dsl.AcceptsError;
|
||||
@ -20,12 +22,30 @@ public abstract class IsSameObjectNode extends Node {
|
||||
|
||||
public abstract boolean execute(@AcceptsError Object left, @AcceptsError Object right);
|
||||
|
||||
@Specialization(limit = "3")
|
||||
boolean doExecute(
|
||||
Object left,
|
||||
Object right,
|
||||
@CachedLibrary("left") InteropLibrary leftInterop,
|
||||
@CachedLibrary("right") InteropLibrary rightInteropp) {
|
||||
return (left == right) || leftInterop.isIdentical(left, right, rightInteropp);
|
||||
/**
|
||||
* Shortcut specialization for meta objects.
|
||||
*
|
||||
* <p>Note: Do not remove, as this is a workaround for an unexpected behavior of HostObject
|
||||
* interop in GraalVM 22.3.0, where isIdentical(ArrayList.class, arrayListObject.getClass()) would
|
||||
* return false.
|
||||
*
|
||||
* @return True if the qualified names of the meta objects are same.
|
||||
*/
|
||||
@Specialization(guards = {"interop.isMetaObject(metaLeft)", "interop.isMetaObject(metaRight)"})
|
||||
boolean isSameMetaObjects(
|
||||
Object metaLeft, Object metaRight, @CachedLibrary(limit = "2") InteropLibrary interop) {
|
||||
try {
|
||||
Object metaLeftName = interop.getMetaQualifiedName(metaLeft);
|
||||
Object metaRightName = interop.getMetaQualifiedName(metaRight);
|
||||
return isIdenticalObjects(metaLeftName, metaRightName, interop);
|
||||
} catch (UnsupportedMessageException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Fallback
|
||||
boolean isIdenticalObjects(
|
||||
Object left, Object right, @CachedLibrary(limit = "2") InteropLibrary interop) {
|
||||
return (left == right) || interop.isIdentical(left, right, interop);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package org.enso.interpreter.node.expression.builtin.special;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
|
||||
@BuiltinMethod(type = "Special", name = "<join_thread>")
|
||||
public class JoinThreadNode extends Node {
|
||||
@TruffleBoundary
|
||||
public Object execute(Object self) {
|
||||
try {
|
||||
((Thread) self).join();
|
||||
|
@ -79,7 +79,7 @@ public abstract class EvalNode extends BaseNode {
|
||||
ClosureRootNode framedNode =
|
||||
ClosureRootNode.build(
|
||||
context.getLanguage(), localScope, moduleScope, expr, null, "<eval>", false, false);
|
||||
return Truffle.getRuntime().createCallTarget(framedNode);
|
||||
return framedNode.getCallTarget();
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
|
@ -1,9 +1,8 @@
|
||||
package org.enso.interpreter.node.scope;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.NodeChild;
|
||||
import com.oracle.truffle.api.dsl.NodeField;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.FrameSlot;
|
||||
import com.oracle.truffle.api.frame.FrameSlotKind;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
@ -13,21 +12,25 @@ import org.enso.interpreter.runtime.Context;
|
||||
/** This node represents an assignment to a variable in a given scope. */
|
||||
@NodeInfo(shortName = "=", description = "Assigns expression result to a variable.")
|
||||
@NodeChild(value = "rhsNode", type = ExpressionNode.class)
|
||||
@NodeField(name = "frameSlot", type = FrameSlot.class)
|
||||
public abstract class AssignmentNode extends ExpressionNode {
|
||||
|
||||
AssignmentNode() {}
|
||||
private final int frameSlotIdx;
|
||||
|
||||
AssignmentNode(int frameSlotIdx) {
|
||||
this.frameSlotIdx = frameSlotIdx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of this node.
|
||||
*
|
||||
* @param expression the expression being assigned
|
||||
* @param slot the slot to which {@code expression} is being assigned
|
||||
* @param frameSlotIdx the slot index to which {@code expression} is being assigned
|
||||
* @return a node representing an assignment
|
||||
*/
|
||||
public static AssignmentNode build(ExpressionNode expression, FrameSlot slot) {
|
||||
return AssignmentNodeGen.create(expression, slot);
|
||||
public static AssignmentNode build(ExpressionNode expression, int frameSlotIdx) {
|
||||
return AssignmentNodeGen.create(frameSlotIdx, expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a long value into the provided frame.
|
||||
*
|
||||
@ -37,8 +40,8 @@ public abstract class AssignmentNode extends ExpressionNode {
|
||||
*/
|
||||
@Specialization(guards = "isLongOrIllegal(frame)")
|
||||
protected Object writeLong(VirtualFrame frame, long value) {
|
||||
frame.getFrameDescriptor().setFrameSlotKind(getFrameSlot(), FrameSlotKind.Long);
|
||||
frame.setLong(getFrameSlot(), value);
|
||||
frame.getFrameDescriptor().setSlotKind(frameSlotIdx, FrameSlotKind.Long);
|
||||
frame.setLong(frameSlotIdx, value);
|
||||
|
||||
return Context.get(this).getNothing();
|
||||
}
|
||||
@ -50,23 +53,16 @@ public abstract class AssignmentNode extends ExpressionNode {
|
||||
* @param value the value to write
|
||||
* @return the unit type
|
||||
*/
|
||||
@Specialization
|
||||
@Fallback
|
||||
protected Object writeObject(VirtualFrame frame, Object value) {
|
||||
frame.getFrameDescriptor().setFrameSlotKind(getFrameSlot(), FrameSlotKind.Object);
|
||||
frame.setObject(getFrameSlot(), value);
|
||||
frame.getFrameDescriptor().setSlotKind(frameSlotIdx, FrameSlotKind.Object);
|
||||
frame.setObject(frameSlotIdx, value);
|
||||
|
||||
return Context.get(this).getNothing();
|
||||
}
|
||||
|
||||
boolean isLongOrIllegal(VirtualFrame frame) {
|
||||
FrameSlotKind kind = frame.getFrameDescriptor().getFrameSlotKind(getFrameSlot());
|
||||
FrameSlotKind kind = frame.getFrameDescriptor().getSlotKind(frameSlotIdx);
|
||||
return kind == FrameSlotKind.Long || kind == FrameSlotKind.Illegal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current frame slot
|
||||
*
|
||||
* @return the frame slot being written to
|
||||
*/
|
||||
public abstract FrameSlot getFrameSlot();
|
||||
}
|
||||
|
@ -12,7 +12,12 @@ import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.scope.FramePointer;
|
||||
|
||||
/** Reads from a local target (variable or call target). */
|
||||
/**
|
||||
* Reads from a local target (variable or call target).
|
||||
*
|
||||
* <p>Note that local in this context does not necessarily mean that the variable is in the given
|
||||
* {@link Frame}. The {@code framePointer} field may point to the parent frame.
|
||||
*/
|
||||
@NodeInfo(shortName = "readVar", description = "Access local variable value.")
|
||||
@NodeField(name = "framePointer", type = FramePointer.class)
|
||||
public abstract class ReadLocalVariableNode extends ExpressionNode {
|
||||
@ -41,9 +46,9 @@ public abstract class ReadLocalVariableNode extends ExpressionNode {
|
||||
@Specialization(rewriteOn = FrameSlotTypeException.class)
|
||||
protected long readLong(VirtualFrame frame) throws FrameSlotTypeException {
|
||||
if (getFramePointer().getParentLevel() == 0)
|
||||
return frame.getLong(getFramePointer().getFrameSlot());
|
||||
return frame.getLong(getFramePointer().getFrameSlotIdx());
|
||||
MaterializedFrame currentFrame = getProperFrame(frame);
|
||||
return currentFrame.getLong(getFramePointer().getFrameSlot());
|
||||
return currentFrame.getLong(getFramePointer().getFrameSlotIdx());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,17 +62,17 @@ public abstract class ReadLocalVariableNode extends ExpressionNode {
|
||||
@Specialization(rewriteOn = FrameSlotTypeException.class)
|
||||
protected Object readGeneric(VirtualFrame frame) throws FrameSlotTypeException {
|
||||
if (getFramePointer().getParentLevel() == 0)
|
||||
return frame.getObject(getFramePointer().getFrameSlot());
|
||||
return frame.getObject(getFramePointer().getFrameSlotIdx());
|
||||
MaterializedFrame currentFrame = getProperFrame(frame);
|
||||
return currentFrame.getObject(getFramePointer().getFrameSlot());
|
||||
return currentFrame.getObject(getFramePointer().getFrameSlotIdx());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected Object readGenericValue(VirtualFrame frame) {
|
||||
if (getFramePointer().getParentLevel() == 0)
|
||||
return frame.getValue(getFramePointer().getFrameSlot());
|
||||
return frame.getValue(getFramePointer().getFrameSlotIdx());
|
||||
MaterializedFrame currentFrame = getProperFrame(frame);
|
||||
return currentFrame.getValue(getFramePointer().getFrameSlot());
|
||||
return currentFrame.getValue(getFramePointer().getFrameSlotIdx());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.interpreter.runtime.callable;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
@ -52,7 +53,7 @@ public final class UnresolvedSymbol implements TruffleObject {
|
||||
* is returned. This is useful for certain subtyping relations, such as "any constructor is a
|
||||
* subtype of Any" or "Nat is a subtype of Int, is a subtype of Number".
|
||||
*
|
||||
* @param constructors the constructors hierarchy for which this symbol should be resolved
|
||||
* @param type the type for which this symbol should be resolved
|
||||
* @return the resolved function definition, or null if not found
|
||||
*/
|
||||
public Function resolveFor(Type type) {
|
||||
@ -73,6 +74,7 @@ public final class UnresolvedSymbol implements TruffleObject {
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
@TruffleBoundary
|
||||
String toDisplayString(boolean allowSideEffects) {
|
||||
return this.toString();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.interpreter.runtime.callable.atom;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.RootCallTarget;
|
||||
import com.oracle.truffle.api.Truffle;
|
||||
import com.oracle.truffle.api.interop.ArityException;
|
||||
@ -145,13 +146,13 @@ public final class AtomConstructor implements TruffleObject {
|
||||
type.getName() + "." + name,
|
||||
null,
|
||||
false);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
|
||||
RootCallTarget callTarget = rootNode.getCallTarget();
|
||||
return new Function(callTarget, null, new FunctionSchema(args));
|
||||
}
|
||||
|
||||
private void generateQualifiedAccessor() {
|
||||
QualifiedAccessorNode node = new QualifiedAccessorNode(null, this);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
RootCallTarget callTarget = node.getCallTarget();
|
||||
Function function =
|
||||
new Function(
|
||||
callTarget,
|
||||
@ -249,12 +250,13 @@ public final class AtomConstructor implements TruffleObject {
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
@TruffleBoundary
|
||||
String toDisplayString(boolean allowSideEffects) {
|
||||
return "Constructor<" + name + ">";
|
||||
}
|
||||
|
||||
/** @return the fully qualified name of this constructor. */
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
@TruffleBoundary
|
||||
public QualifiedName getQualifiedName() {
|
||||
return type.getQualifiedName().createChild(getName());
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ public final class Function implements TruffleObject {
|
||||
* @return a Function object with specified behavior and arguments
|
||||
*/
|
||||
public static Function fromBuiltinRootNode(BuiltinRootNode node, ArgumentDefinition... args) {
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
RootCallTarget callTarget = node.getCallTarget();
|
||||
FunctionSchema schema = new FunctionSchema(args);
|
||||
return new Function(callTarget, null, schema);
|
||||
}
|
||||
@ -102,7 +102,7 @@ public final class Function implements TruffleObject {
|
||||
*/
|
||||
public static Function fromBuiltinRootNodeWithCallerFrameAccess(
|
||||
BuiltinRootNode node, ArgumentDefinition... args) {
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
RootCallTarget callTarget = node.getCallTarget();
|
||||
FunctionSchema schema = new FunctionSchema(FunctionSchema.CallerFrameAccess.FULL, args);
|
||||
return new Function(callTarget, null, schema);
|
||||
}
|
||||
|
@ -66,10 +66,9 @@ public final class Type implements TruffleObject {
|
||||
|
||||
private void generateQualifiedAccessor() {
|
||||
var node = new ConstantNode(null, this);
|
||||
var callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
var function =
|
||||
new Function(
|
||||
callTarget,
|
||||
node.getCallTarget(),
|
||||
null,
|
||||
new FunctionSchema(
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE)));
|
||||
@ -161,10 +160,9 @@ public final class Type implements TruffleObject {
|
||||
}
|
||||
roots.forEach(
|
||||
(name, node) -> {
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
var f =
|
||||
new Function(
|
||||
callTarget,
|
||||
node.getCallTarget(),
|
||||
null,
|
||||
new FunctionSchema(
|
||||
new ArgumentDefinition(
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.interpreter.runtime.error;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.TruffleStackTrace;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
@ -82,6 +83,7 @@ public class DataflowError extends AbstractTruffleException {
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
@TruffleBoundary
|
||||
public String toDisplayString(
|
||||
boolean allowSideEffects,
|
||||
@CachedLibrary(limit = "3") InteropLibrary displays,
|
||||
|
@ -0,0 +1,279 @@
|
||||
package org.enso.interpreter.runtime.scope;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
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.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.EnsoRootNode;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
/**
|
||||
* This class serves as a basic support for debugging with Chrome inspector. Currently, only
|
||||
* function scopes are supported.
|
||||
*
|
||||
* <p>Some of the features that remain to be implemented are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Module scopes. How to display imports in chrome devtools? Get inspiration from Python?
|
||||
* <li>Evaluation of an arbitrary expression
|
||||
* </ul>
|
||||
*/
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
public class DebugLocalScope implements TruffleObject {
|
||||
private final EnsoRootNode rootNode;
|
||||
|
||||
/** All the bindings, including the parent scopes. */
|
||||
private final Map<String, FramePointer> allBindings;
|
||||
|
||||
/**
|
||||
* The inner lists represent particular scope in a scope hierarchy. For example, for the following
|
||||
* snippet:
|
||||
*
|
||||
* <pre>
|
||||
* func =
|
||||
* x = 1
|
||||
* inner_func =
|
||||
* y = 2
|
||||
* y
|
||||
* inner_func
|
||||
* </pre>
|
||||
*
|
||||
* the value of this field (for `inner_func` scope) would be {@code [['x'], ['y']]}
|
||||
*/
|
||||
private final List<List<String>> bindingsByLevels;
|
||||
|
||||
/** Index of the current scope into {@link #bindingsByLevels} list. */
|
||||
private final int bindingsByLevelsIdx;
|
||||
|
||||
private final MaterializedFrame frame;
|
||||
|
||||
private DebugLocalScope(
|
||||
EnsoRootNode rootNode,
|
||||
MaterializedFrame frame,
|
||||
List<List<String>> bindingsByLevels,
|
||||
int bindingsByLevelsIdx) {
|
||||
assert bindingsByLevels != null;
|
||||
this.rootNode = rootNode;
|
||||
this.frame = frame;
|
||||
this.allBindings = rootNode.getLocalScope().flattenBindings();
|
||||
this.bindingsByLevels = bindingsByLevels;
|
||||
this.bindingsByLevelsIdx = bindingsByLevelsIdx;
|
||||
assert !this.bindingsByLevels.isEmpty();
|
||||
assert 0 <= this.bindingsByLevelsIdx && this.bindingsByLevelsIdx < this.bindingsByLevels.size();
|
||||
}
|
||||
|
||||
public static DebugLocalScope createFromFrame(EnsoRootNode rootNode, MaterializedFrame frame) {
|
||||
return new DebugLocalScope(
|
||||
rootNode, frame, gatherBindingsByLevels(rootNode.getLocalScope().flattenBindings()), 0);
|
||||
}
|
||||
|
||||
private static DebugLocalScope createParent(DebugLocalScope childScope) {
|
||||
return new DebugLocalScope(
|
||||
childScope.rootNode,
|
||||
childScope.frame,
|
||||
childScope.bindingsByLevels,
|
||||
childScope.bindingsByLevelsIdx + 1);
|
||||
}
|
||||
|
||||
private static List<List<String>> gatherBindingsByLevels(Map<String, FramePointer> bindings) {
|
||||
int maxParentLevel =
|
||||
bindings.values().stream()
|
||||
.max(Comparator.comparingInt(FramePointer::getParentLevel))
|
||||
.orElseThrow()
|
||||
.getParentLevel();
|
||||
|
||||
// Get all binding names for a particular parent level
|
||||
List<List<String>> bindingsByLevels = new ArrayList<>(maxParentLevel + 1);
|
||||
for (int level = 0; level < maxParentLevel + 1; level++) {
|
||||
final int finalLevel = level;
|
||||
List<String> levelBindings =
|
||||
bindings.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().getParentLevel() == finalLevel)
|
||||
.map(Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
bindingsByLevels.add(levelBindings);
|
||||
}
|
||||
return bindingsByLevels;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasLanguage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
Class<? extends TruffleLanguage<?>> getLanguage() {
|
||||
return Language.class;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isScope() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasMembers() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns the members from the current local scope and all the parent scopes. */
|
||||
@ExportMessage
|
||||
ScopeMembers getMembers(boolean includeInternal) {
|
||||
List<String> members = new ArrayList<>();
|
||||
bindingsByLevels.stream().skip(bindingsByLevelsIdx).forEach(members::addAll);
|
||||
return new ScopeMembers(members);
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isMemberModifiable(String memberName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isMemberInsertable(String memberName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isMemberInvocable(String memberName) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasMemberReadSideEffects(String member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasMemberWriteSideEffects(String member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isMemberReadable(String memberName) {
|
||||
// When a value in a frame is null, it means that the corresponding
|
||||
// AssignmentNode was not run yet, and the slot kind of the
|
||||
// FrameDescriptor would be Illegal.
|
||||
return allBindings.containsKey(memberName)
|
||||
&& getValue(frame, allBindings.get(memberName)) != null;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
Object readMember(String member) {
|
||||
FramePointer framePtr = allBindings.get(member);
|
||||
return getValue(frame, framePtr);
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
void writeMember(String member, Object value) throws UnsupportedMessageException {
|
||||
throw UnsupportedMessageException.create();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
Object invokeMember(String member, Object[] args) throws UnsupportedMessageException {
|
||||
throw UnsupportedMessageException.create();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasScopeParent() {
|
||||
return bindingsByLevelsIdx < bindingsByLevels.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent scope. ModuleScopes are not supported yet.
|
||||
*
|
||||
* @return Parent scope (outer method).
|
||||
* @throws UnsupportedMessageException if there is no parent scope.
|
||||
*/
|
||||
@ExportMessage
|
||||
Object getScopeParent() throws UnsupportedMessageException {
|
||||
if (!hasScopeParent()) {
|
||||
throw UnsupportedMessageException.create();
|
||||
} else {
|
||||
return createParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasSourceLocation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
SourceSection getSourceLocation() {
|
||||
return rootNode.getSourceSection();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
@TruffleBoundary
|
||||
String toDisplayString(boolean allowSideEffects) {
|
||||
return rootNode.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"DebugLocalScope{rootNode = '%s', bindingsByLevels = %s, idx = %d}",
|
||||
rootNode.toString(), bindingsByLevels.toString(), bindingsByLevelsIdx);
|
||||
}
|
||||
|
||||
private Object getValue(MaterializedFrame frame, FramePointer ptr) {
|
||||
return getProperFrame(frame, ptr).getValue(ptr.getFrameSlotIdx());
|
||||
}
|
||||
|
||||
private MaterializedFrame getProperFrame(MaterializedFrame frame, FramePointer ptr) {
|
||||
MaterializedFrame currentFrame = frame;
|
||||
for (int i = 0; i < ptr.getParentLevel(); i++) {
|
||||
currentFrame = Function.ArgumentsHelper.getLocalScope(currentFrame.getArguments());
|
||||
}
|
||||
return currentFrame;
|
||||
}
|
||||
|
||||
/** Simple interop wrapper for a list of strings. */
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
static final class ScopeMembers implements TruffleObject {
|
||||
private final List<String> memberNames;
|
||||
|
||||
ScopeMembers(List<String> memberNames) {
|
||||
this.memberNames = memberNames;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasArrayElements() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
long getArraySize() {
|
||||
return memberNames.size();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isArrayElementReadable(long index) {
|
||||
return 0 <= index && index < memberNames.size();
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
String readArrayElement(long index) {
|
||||
return memberNames.get((int) index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return memberNames.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +1,21 @@
|
||||
package org.enso.interpreter.runtime.scope;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameSlot;
|
||||
|
||||
/**
|
||||
* A representation of a pointer into a stack frame at a given number of levels above the current.
|
||||
*/
|
||||
public class FramePointer {
|
||||
private final int parentLevel;
|
||||
private final FrameSlot frameSlot;
|
||||
private final int frameSlotIdx;
|
||||
|
||||
/**
|
||||
* A representation of a frame slot at a given level above the current frame.
|
||||
*
|
||||
* @param parentLevel the number of parents to move from the current frame to get here
|
||||
* @param frameSlot the slot in the n-th parent frame
|
||||
* @param frameSlotIdx the index of the slot in the n-th parent frame
|
||||
*/
|
||||
public FramePointer(int parentLevel, FrameSlot frameSlot) {
|
||||
public FramePointer(int parentLevel, int frameSlotIdx) {
|
||||
this.parentLevel = parentLevel;
|
||||
this.frameSlot = frameSlot;
|
||||
this.frameSlotIdx = frameSlotIdx;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -30,11 +28,11 @@ public class FramePointer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the frame slot.
|
||||
* Gets the index of the frame slot.
|
||||
*
|
||||
* @return the frame slot represented by this {@code FramePointer}
|
||||
* @return the frame slot index represented by this {@code FramePointer}
|
||||
*/
|
||||
public FrameSlot getFrameSlot() {
|
||||
return frameSlot;
|
||||
public int getFrameSlotIdx() {
|
||||
return frameSlotIdx;
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class SerializationManager(compiler: Compiler) {
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingDeque[Runnable](),
|
||||
(runnable: Runnable) => {
|
||||
env.createThread(runnable)
|
||||
env.createSystemThread(runnable)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.enso.compiler.codegen
|
||||
|
||||
import com.oracle.truffle.api.Truffle
|
||||
import com.oracle.truffle.api.frame.FrameSlot
|
||||
import com.oracle.truffle.api.source.{Source, SourceSection}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Import
|
||||
@ -244,12 +242,13 @@ class IrToTruffle(
|
||||
"No occurrence on an argument definition."
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||
val slot = localScope.createVarSlot(occInfo.id)
|
||||
val slotIdx = localScope.getVarSlotIdx(occInfo.id)
|
||||
argDefs(idx) = arg
|
||||
val readArg =
|
||||
ReadArgumentNode.build(idx, arg.getDefaultValue.orElse(null))
|
||||
val assignmentArg = AssignmentNode.build(readArg, slot)
|
||||
val argRead = ReadLocalVariableNode.build(new FramePointer(0, slot))
|
||||
val assignmentArg = AssignmentNode.build(readArg, slotIdx)
|
||||
val argRead =
|
||||
ReadLocalVariableNode.build(new FramePointer(0, slotIdx))
|
||||
argumentExpressions.append((assignmentArg, argRead))
|
||||
}
|
||||
|
||||
@ -398,7 +397,7 @@ class IrToTruffle(
|
||||
cons,
|
||||
methodDef.methodName.name
|
||||
)
|
||||
val callTarget = Truffle.getRuntime.createCallTarget(rootNode)
|
||||
val callTarget = rootNode.getCallTarget
|
||||
val arguments = bodyBuilder.args()
|
||||
Right(
|
||||
Some(
|
||||
@ -479,7 +478,7 @@ class IrToTruffle(
|
||||
toType,
|
||||
methodDef.methodName.name
|
||||
)
|
||||
val callTarget = Truffle.getRuntime.createCallTarget(rootNode)
|
||||
val callTarget = rootNode.getCallTarget
|
||||
val arguments = bodyBuilder.args()
|
||||
new RuntimeFunction(
|
||||
callTarget,
|
||||
@ -604,9 +603,8 @@ class IrToTruffle(
|
||||
private def generateEnsoProjectMethod(): Unit = {
|
||||
val name = BindingsMap.Generated.ensoProjectMethodName
|
||||
val pkg = context.getPackageOf(moduleScope.getModule.getSourceFile)
|
||||
val body = Truffle.getRuntime.createCallTarget(
|
||||
new EnsoProjectNode(language, context, pkg)
|
||||
)
|
||||
val ensoProjectNode = new EnsoProjectNode(language, context, pkg)
|
||||
val body = ensoProjectNode.getCallTarget
|
||||
val schema = new FunctionSchema(
|
||||
new ArgumentDefinition(
|
||||
0,
|
||||
@ -621,9 +619,7 @@ class IrToTruffle(
|
||||
private def generateReExportBindings(module: IR.Module): Unit = {
|
||||
def mkConsGetter(constructor: AtomConstructor): RuntimeFunction = {
|
||||
new RuntimeFunction(
|
||||
Truffle.getRuntime.createCallTarget(
|
||||
new QualifiedAccessorNode(language, constructor)
|
||||
),
|
||||
new QualifiedAccessorNode(language, constructor).getCallTarget,
|
||||
null,
|
||||
new FunctionSchema(
|
||||
new ArgumentDefinition(
|
||||
@ -637,9 +633,7 @@ class IrToTruffle(
|
||||
|
||||
def mkTypeGetter(tp: Type): RuntimeFunction = {
|
||||
new RuntimeFunction(
|
||||
Truffle.getRuntime.createCallTarget(
|
||||
new ConstantNode(language, tp)
|
||||
),
|
||||
new ConstantNode(language, tp).getCallTarget,
|
||||
null,
|
||||
new FunctionSchema(
|
||||
new ArgumentDefinition(
|
||||
@ -838,7 +832,7 @@ class IrToTruffle(
|
||||
false
|
||||
)
|
||||
|
||||
val callTarget = Truffle.getRuntime.createCallTarget(defaultRootNode)
|
||||
val callTarget = defaultRootNode.getCallTarget
|
||||
setLocation(CreateThunkNode.build(callTarget), block.location)
|
||||
} else {
|
||||
val statementExprs = block.expressions.map(this.run).toArray
|
||||
@ -1213,10 +1207,10 @@ class IrToTruffle(
|
||||
|
||||
currentVarName = binding.name.name
|
||||
|
||||
val slot = scope.createVarSlot(occInfo.id)
|
||||
val slotIdx = scope.getVarSlotIdx(occInfo.id)
|
||||
|
||||
setLocation(
|
||||
AssignmentNode.build(this.run(binding.expression, true), slot),
|
||||
AssignmentNode.build(this.run(binding.expression, true), slotIdx),
|
||||
binding.location
|
||||
)
|
||||
}
|
||||
@ -1275,10 +1269,10 @@ class IrToTruffle(
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||
|
||||
val slot = scope.getFramePointer(useInfo.id)
|
||||
val framePointer = scope.getFramePointer(useInfo.id)
|
||||
val global = name.getMetadata(GlobalNames)
|
||||
if (slot.isDefined) {
|
||||
ReadLocalVariableNode.build(slot.get)
|
||||
if (framePointer.isDefined) {
|
||||
ReadLocalVariableNode.build(framePointer.get)
|
||||
} else if (global.isDefined) {
|
||||
val resolution = global.get.target
|
||||
nodeForResolution(resolution)
|
||||
@ -1475,7 +1469,7 @@ class IrToTruffle(
|
||||
def bodyNode(): RuntimeExpression = bodyN
|
||||
|
||||
private def computeBodyNode(): RuntimeExpression = {
|
||||
val (argSlots, _, argExpressions) = slots
|
||||
val (argSlotIdxs, _, argExpressions) = slots
|
||||
|
||||
val bodyExpr = body match {
|
||||
case IR.Foreign.Definition(lang, code, _, _, _) =>
|
||||
@ -1483,7 +1477,7 @@ class IrToTruffle(
|
||||
lang,
|
||||
code,
|
||||
arguments.map(_.name.name),
|
||||
argSlots
|
||||
argSlotIdxs
|
||||
)
|
||||
case _ => ExpressionProcessor.this.run(body)
|
||||
}
|
||||
@ -1496,7 +1490,7 @@ class IrToTruffle(
|
||||
}
|
||||
|
||||
private def computeSlots(): (
|
||||
List[FrameSlot],
|
||||
List[Int],
|
||||
Array[ArgumentDefinition],
|
||||
ArrayBuffer[RuntimeExpression]
|
||||
) = {
|
||||
@ -1516,10 +1510,10 @@ class IrToTruffle(
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||
|
||||
val slot = scope.createVarSlot(occInfo.id)
|
||||
val slotIdx = scope.getVarSlotIdx(occInfo.id)
|
||||
val readArg =
|
||||
ReadArgumentNode.build(idx, arg.getDefaultValue.orElse(null))
|
||||
val assignArg = AssignmentNode.build(readArg, slot)
|
||||
val assignArg = AssignmentNode.build(readArg, slotIdx)
|
||||
|
||||
argExpressions.append(assignArg)
|
||||
|
||||
@ -1534,7 +1528,7 @@ class IrToTruffle(
|
||||
s"A duplicate argument name, $argName, was found during codegen."
|
||||
)
|
||||
} else seenArgNames.add(argName)
|
||||
slot
|
||||
slotIdx
|
||||
}
|
||||
(argSlots, argDefinitions, argExpressions)
|
||||
}
|
||||
@ -1544,13 +1538,15 @@ class IrToTruffle(
|
||||
language: EpbParser.ForeignLanguage,
|
||||
code: String,
|
||||
argumentNames: List[String],
|
||||
argumentSlots: List[FrameSlot]
|
||||
argumentSlotIdxs: List[Int]
|
||||
): RuntimeExpression = {
|
||||
val src = EpbParser.buildSource(language, code, scopeName)
|
||||
val foreignCt = context.getEnvironment
|
||||
.parseInternal(src, argumentNames: _*)
|
||||
val argumentReaders = argumentSlots
|
||||
.map(slot => ReadLocalVariableNode.build(new FramePointer(0, slot)))
|
||||
val argumentReaders = argumentSlotIdxs
|
||||
.map(slotIdx =>
|
||||
ReadLocalVariableNode.build(new FramePointer(0, slotIdx))
|
||||
)
|
||||
.toArray[RuntimeExpression]
|
||||
ForeignMethodCallNode.build(argumentReaders, foreignCt)
|
||||
}
|
||||
@ -1580,7 +1576,7 @@ class IrToTruffle(
|
||||
false,
|
||||
binding
|
||||
)
|
||||
val callTarget = Truffle.getRuntime.createCallTarget(fnRootNode)
|
||||
val callTarget = fnRootNode.getCallTarget
|
||||
|
||||
val expr = CreateFunctionNode.build(callTarget, bodyBuilder.args())
|
||||
|
||||
@ -1599,14 +1595,14 @@ class IrToTruffle(
|
||||
* 1. Argument Conversion: Arguments are converted into their definitions so
|
||||
* as to provide a compact representation of all known information about
|
||||
* that argument.
|
||||
* 2. Frame Conversion: A variable slot is created in the function's local
|
||||
* 2. Frame Conversion: A variable framePointer is created in the function's local
|
||||
* frame to contain the value of the function argument.
|
||||
* 3. Read Provision: A `ReadArgumentNode` is generated to allow that
|
||||
* function argument to be treated purely as a local variable access. See
|
||||
* Note [Handling Argument Defaults] for more information on how this
|
||||
* works.
|
||||
* 4. Value Assignment: A `AssignmentNode` is created to connect the
|
||||
* argument value to the frame slot created in Step 2.
|
||||
* argument value to the framePointer created in Step 2.
|
||||
* 5. Body Rewriting: The expression representing the argument is written
|
||||
* into the function body, thus allowing it to be read simply.
|
||||
*/
|
||||
@ -1729,14 +1725,14 @@ class IrToTruffle(
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Scope.Child]
|
||||
|
||||
val shouldSuspend = value match {
|
||||
val shouldCreateClosureRootNode = value match {
|
||||
case _: IR.Name => false
|
||||
case _: IR.Literal.Text => false
|
||||
case _: IR.Literal.Number => false
|
||||
case _ => true
|
||||
}
|
||||
|
||||
val childScope = if (shouldSuspend) {
|
||||
val childScope = if (shouldCreateClosureRootNode) {
|
||||
scope.createChild(scopeInfo.scope)
|
||||
} else {
|
||||
// Note [Scope Flattening]
|
||||
@ -1745,7 +1741,7 @@ class IrToTruffle(
|
||||
val argumentExpression =
|
||||
new ExpressionProcessor(childScope, scopeName).run(value)
|
||||
|
||||
val result = if (!shouldSuspend) {
|
||||
val result = if (!shouldCreateClosureRootNode) {
|
||||
argumentExpression
|
||||
} else {
|
||||
argumentExpression.setTailStatus(getTailStatus(value))
|
||||
@ -1757,8 +1753,7 @@ class IrToTruffle(
|
||||
.map(loc => source.createSection(loc.start, loc.length))
|
||||
.orNull
|
||||
|
||||
val callTarget = Truffle.getRuntime.createCallTarget(
|
||||
ClosureRootNode.build(
|
||||
val closureRootNode = ClosureRootNode.build(
|
||||
language,
|
||||
childScope,
|
||||
moduleScope,
|
||||
@ -1768,7 +1763,7 @@ class IrToTruffle(
|
||||
true,
|
||||
false
|
||||
)
|
||||
)
|
||||
val callTarget = closureRootNode.getCallTarget
|
||||
|
||||
CreateThunkNode.build(callTarget)
|
||||
}
|
||||
@ -1848,7 +1843,7 @@ class IrToTruffle(
|
||||
)
|
||||
|
||||
CreateThunkNode.build(
|
||||
Truffle.getRuntime.createCallTarget(defaultRootNode)
|
||||
defaultRootNode.getCallTarget
|
||||
)
|
||||
} else {
|
||||
defaultExpression
|
||||
|
@ -366,6 +366,7 @@ case object AliasAnalysis extends IRPass {
|
||||
)
|
||||
|
||||
parentScope.add(occurrence)
|
||||
parentScope.addDefinition(occurrence)
|
||||
|
||||
binding
|
||||
.copy(
|
||||
@ -414,9 +415,10 @@ case object AliasAnalysis extends IRPass {
|
||||
}
|
||||
|
||||
val labelId = graph.nextId()
|
||||
parentScope.add(
|
||||
val definition =
|
||||
Occurrence.Def(labelId, label.name, label.getId, label.getExternalId)
|
||||
)
|
||||
parentScope.add(definition)
|
||||
parentScope.addDefinition(definition)
|
||||
|
||||
member
|
||||
.copy(
|
||||
@ -452,7 +454,7 @@ case object AliasAnalysis extends IRPass {
|
||||
): List[IR.DefinitionArgument] = {
|
||||
args.map {
|
||||
case arg @ IR.DefinitionArgument.Specified(
|
||||
IR.Name.Self(_, true, _, _),
|
||||
selfName @ IR.Name.Self(_, true, _, _),
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
@ -460,9 +462,19 @@ case object AliasAnalysis extends IRPass {
|
||||
_,
|
||||
_
|
||||
) =>
|
||||
// Synthetic `self` must not be added to the scope
|
||||
// Synthetic `self` must not be added to the scope, but it has to be added as a
|
||||
// definition for frame index metadata
|
||||
val occurrenceId = graph.nextId()
|
||||
arg.updateMetadata(this -->> Info.Occurrence(graph, occurrenceId))
|
||||
val definition = Graph.Occurrence.Def(
|
||||
occurrenceId,
|
||||
selfName.name,
|
||||
arg.getId,
|
||||
arg.getExternalId
|
||||
)
|
||||
scope.addDefinition(definition)
|
||||
arg.updateMetadata(
|
||||
this -->> Info.Occurrence(graph, occurrenceId)
|
||||
)
|
||||
case arg @ IR.DefinitionArgument.Specified(
|
||||
name,
|
||||
_,
|
||||
@ -480,10 +492,15 @@ case object AliasAnalysis extends IRPass {
|
||||
)
|
||||
|
||||
val occurrenceId = graph.nextId()
|
||||
scope.add(
|
||||
Graph.Occurrence
|
||||
.Def(occurrenceId, name.name, arg.getId, arg.getExternalId, susp)
|
||||
val definition = Graph.Occurrence.Def(
|
||||
occurrenceId,
|
||||
name.name,
|
||||
arg.getId,
|
||||
arg.getExternalId,
|
||||
susp
|
||||
)
|
||||
scope.add(definition)
|
||||
scope.addDefinition(definition)
|
||||
|
||||
arg
|
||||
.copy(defaultValue = newDefault)
|
||||
@ -605,7 +622,7 @@ case object AliasAnalysis extends IRPass {
|
||||
* @param isConstructorNameInPatternContext whether or not the name is
|
||||
* constructor name occurring in a pattern context
|
||||
* @param graph the graph in which the analysis is taking place
|
||||
* @param parentScope the scope in which `name` is delcared
|
||||
* @param parentScope the scope in which `name` is declared
|
||||
* @return `name`, with alias analysis information attached
|
||||
*/
|
||||
def analyseName(
|
||||
@ -618,9 +635,10 @@ case object AliasAnalysis extends IRPass {
|
||||
val occurrenceId = graph.nextId()
|
||||
|
||||
if (isInPatternContext && !isConstructorNameInPatternContext) {
|
||||
val occurrence =
|
||||
val definition =
|
||||
Occurrence.Def(occurrenceId, name.name, name.getId, name.getExternalId)
|
||||
parentScope.add(occurrence)
|
||||
parentScope.add(definition)
|
||||
parentScope.addDefinition(definition)
|
||||
} else {
|
||||
val occurrence =
|
||||
Occurrence.Use(occurrenceId, name.name, name.getId, name.getExternalId)
|
||||
@ -1145,11 +1163,15 @@ case object AliasAnalysis extends IRPass {
|
||||
*
|
||||
* @param childScopes all scopes that are _direct_ children of `this`
|
||||
* @param occurrences all symbol occurrences in `this` scope
|
||||
* @param allDefinitions all definitions in this scope, including synthetic ones.
|
||||
* Note that there may not be a link for all these definitions.
|
||||
*/
|
||||
sealed class Scope(
|
||||
var childScopes: List[Scope] = List(),
|
||||
var occurrences: Set[Occurrence] = Set()
|
||||
var occurrences: Set[Occurrence] = Set(),
|
||||
var allDefinitions: List[Occurrence.Def] = List()
|
||||
) extends Serializable {
|
||||
|
||||
var parent: Option[Scope] = None
|
||||
|
||||
/** Counts the number of scopes from this scope to the root.
|
||||
@ -1189,7 +1211,8 @@ case object AliasAnalysis extends IRPass {
|
||||
this.childScopes.foreach(scope =>
|
||||
childScopeCopies += scope.deepCopy(mapping)
|
||||
)
|
||||
val newScope = new Scope(childScopeCopies.toList, occurrences)
|
||||
val newScope =
|
||||
new Scope(childScopeCopies.toList, occurrences, allDefinitions)
|
||||
mapping.put(this, newScope)
|
||||
newScope
|
||||
}
|
||||
@ -1235,6 +1258,15 @@ case object AliasAnalysis extends IRPass {
|
||||
occurrences += occurrence
|
||||
}
|
||||
|
||||
/** Adds a definition, including a definition with synthetic name, without
|
||||
* any links.
|
||||
*
|
||||
* @param definition The definition to add.
|
||||
*/
|
||||
def addDefinition(definition: Occurrence.Def): Unit = {
|
||||
allDefinitions = allDefinitions ++ List(definition)
|
||||
}
|
||||
|
||||
/** Finds an occurrence for the provided ID in the current scope, if it
|
||||
* exists.
|
||||
*
|
||||
|
@ -1,14 +1,18 @@
|
||||
package org.enso.interpreter.runtime.scope
|
||||
|
||||
import com.oracle.truffle.api.frame.{FrameDescriptor, FrameSlot}
|
||||
import com.oracle.truffle.api.frame.{FrameDescriptor, FrameSlotKind}
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph
|
||||
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{
|
||||
Id,
|
||||
Occurrence,
|
||||
Scope => AliasScope
|
||||
}
|
||||
import org.enso.compiler.pass.analyse.{AliasAnalysis, DataflowAnalysis}
|
||||
import org.enso.interpreter.runtime.scope.LocalScope.{
|
||||
internalSlots,
|
||||
monadicStateSlotName
|
||||
}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.jdk.CollectionConverters._
|
||||
|
||||
/** A representation of an Enso local scope.
|
||||
@ -28,8 +32,9 @@ import scala.jdk.CollectionConverters._
|
||||
* @param dataflowInfo information on the dataflow analysis for this scope
|
||||
* @param flattenToParent whether or not the frame should be flattened into its
|
||||
* parent
|
||||
* @param frameSlots a mapping from symbol definition identifiers to slots in
|
||||
* the Enso frame
|
||||
* @param parentFrameSlotIdxs Mapping of occurence identifiers to frame slot indexes
|
||||
* from the whole parent hierarchy, i.e., this parameter should contain all the
|
||||
* indexes for all the parents.
|
||||
*/
|
||||
class LocalScope(
|
||||
final val parentScope: Option[LocalScope],
|
||||
@ -37,11 +42,17 @@ class LocalScope(
|
||||
final val scope: AliasAnalysis.Graph.Scope,
|
||||
final val dataflowInfo: DataflowAnalysis.Metadata,
|
||||
final val flattenToParent: Boolean = false,
|
||||
final val frameSlots: mutable.Map[Graph.Id, FrameSlot] = mutable.Map()
|
||||
private val parentFrameSlotIdxs: Map[Graph.Id, Int] = Map()
|
||||
) {
|
||||
lazy val frameDescriptor: FrameDescriptor = buildFrameDescriptor()
|
||||
private lazy val localFrameSlotIdxs: Map[Graph.Id, Int] =
|
||||
gatherLocalFrameSlotIdxs()
|
||||
|
||||
/** A descriptor for this frame. */
|
||||
val frameDescriptor: FrameDescriptor = new FrameDescriptor()
|
||||
/** All frame slot indexes, including local and all the parents.
|
||||
* Useful for quick searching for [[FramePointer]] of parent scopes.
|
||||
*/
|
||||
private lazy val allFrameSlotIdxs: Map[Graph.Id, Int] =
|
||||
parentFrameSlotIdxs ++ localFrameSlotIdxs
|
||||
|
||||
/** Creates a new child with a new aliasing scope.
|
||||
*
|
||||
@ -66,32 +77,50 @@ class LocalScope(
|
||||
childScope,
|
||||
dataflowInfo,
|
||||
flattenToParent,
|
||||
frameSlots
|
||||
allFrameSlotIdxs
|
||||
)
|
||||
}
|
||||
|
||||
/** Creates a frame slot for a given identifier.
|
||||
*
|
||||
* @param id the identifier of a variable definition occurrence from alias
|
||||
* analysis
|
||||
* @return a new frame slot for `id`
|
||||
/** Returns frame slot index to a monadic state, which is considered an internal slot.
|
||||
* @return
|
||||
*/
|
||||
def createVarSlot(id: Graph.Id): FrameSlot = {
|
||||
val slot = frameDescriptor.addFrameSlot(aliasingGraph.idToSymbol(id))
|
||||
frameSlots(id) = slot
|
||||
slot
|
||||
def monadicStateSlotIdx: Int = {
|
||||
internalSlots.zipWithIndex
|
||||
.find { case ((_, name), _) => name == monadicStateSlotName }
|
||||
.map(_._2)
|
||||
.getOrElse(
|
||||
throw new IllegalStateException(
|
||||
s"$monadicStateSlotName slot should be present in every frame descriptor"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Obtains the frame pointer for a given identifier.
|
||||
/** Get a frame slot index for a given identifier.
|
||||
*
|
||||
* The identifier must be present in the local scope.
|
||||
*
|
||||
* @param id the identifier of a variable definition occurrence from alias
|
||||
* analysis.
|
||||
* @return the frame slot index for `id`.
|
||||
*/
|
||||
def getVarSlotIdx(id: Graph.Id): Int = {
|
||||
assert(localFrameSlotIdxs.contains(id))
|
||||
localFrameSlotIdxs(id)
|
||||
}
|
||||
|
||||
/** Obtains the frame pointer for a given identifier from the current scope, or from
|
||||
* any parent scopes.
|
||||
*
|
||||
* @param id the identifier of a variable usage occurrence from alias
|
||||
* analysis
|
||||
* @return the frame pointer for `id`, if it exists
|
||||
*/
|
||||
def getFramePointer(id: Graph.Id): Option[FramePointer] = {
|
||||
aliasingGraph.defLinkFor(id).flatMap { link =>
|
||||
val slot = frameSlots.get(link.target)
|
||||
slot.map(
|
||||
aliasingGraph
|
||||
.defLinkFor(id)
|
||||
.flatMap { link =>
|
||||
val slotIdx = allFrameSlotIdxs.get(link.target)
|
||||
slotIdx.map(
|
||||
new FramePointer(
|
||||
if (flattenToParent) link.scopeCount - 1 else link.scopeCount,
|
||||
_
|
||||
@ -108,6 +137,50 @@ class LocalScope(
|
||||
def flattenBindings: java.util.Map[String, FramePointer] =
|
||||
flattenBindingsWithLevel(0).asJava
|
||||
|
||||
private def addInternalSlots(
|
||||
descriptorBuilder: FrameDescriptor.Builder
|
||||
): Unit = {
|
||||
for ((slotKind, name) <- internalSlots) {
|
||||
descriptorBuilder.addSlot(slotKind, name, null)
|
||||
}
|
||||
}
|
||||
|
||||
/** Builds a [[FrameDescriptor]] from the alias analysis scope metadata for the local scope.
|
||||
* See [[AliasAnalysis.Graph.Scope.allDefinitions]].
|
||||
*
|
||||
* @return [[FrameDescriptor]] built from the variable definitions in the local scope.
|
||||
*/
|
||||
private def buildFrameDescriptor(): FrameDescriptor = {
|
||||
val descriptorBuilder = FrameDescriptor.newBuilder()
|
||||
addInternalSlots(descriptorBuilder)
|
||||
for (definition <- scope.allDefinitions) {
|
||||
val returnedFrameIdx =
|
||||
descriptorBuilder.addSlot(
|
||||
FrameSlotKind.Illegal,
|
||||
definition.symbol,
|
||||
null
|
||||
)
|
||||
assert(localFrameSlotIdxs(definition.id) == returnedFrameIdx)
|
||||
}
|
||||
val frameDescriptor = descriptorBuilder.build()
|
||||
assert(
|
||||
internalSlots.length + localFrameSlotIdxs.size == frameDescriptor.getNumberOfSlots
|
||||
)
|
||||
frameDescriptor
|
||||
}
|
||||
|
||||
/** Gather local variables from the alias scope information.
|
||||
* Does not include any variables from the parent scopes.
|
||||
* @return Mapping of local variable identifiers to their
|
||||
* indexes in the frame. Takes into account all the
|
||||
* internal slots, that are prepended to every frame.
|
||||
*/
|
||||
private def gatherLocalFrameSlotIdxs(): Map[Id, Int] = {
|
||||
scope.allDefinitions.zipWithIndex.map { case (definition, i) =>
|
||||
definition.id -> (i + internalSlots.size)
|
||||
}.toMap
|
||||
}
|
||||
|
||||
/** Flatten bindings from a given set of levels, accounting for shadowing.
|
||||
*
|
||||
* @param level the level to which to flatten
|
||||
@ -124,12 +197,16 @@ class LocalScope(
|
||||
case x: Occurrence.Def =>
|
||||
parentResult += x.symbol -> new FramePointer(
|
||||
level,
|
||||
frameSlots(x.id)
|
||||
allFrameSlotIdxs(x.id)
|
||||
)
|
||||
case _ =>
|
||||
}
|
||||
parentResult
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
s"LocalScope(${frameDescriptor.toString})"
|
||||
}
|
||||
}
|
||||
object LocalScope {
|
||||
|
||||
@ -147,4 +224,14 @@ object LocalScope {
|
||||
DataflowAnalysis.DependencyInfo()
|
||||
)
|
||||
}
|
||||
|
||||
private val monadicStateSlotName = "<<monadic_state>>"
|
||||
|
||||
/** Internal slots are prepended at the beginning of every [[FrameDescriptor]].
|
||||
* Every tuple of the list denotes frame slot kind and its name.
|
||||
* Note that `info` for a frame slot is not used by Enso.
|
||||
*/
|
||||
def internalSlots: List[(FrameSlotKind, String)] = List(
|
||||
(FrameSlotKind.Object, monadicStateSlotName)
|
||||
)
|
||||
}
|
||||
|
@ -1,12 +1,17 @@
|
||||
package org.enso.interpreter.test;
|
||||
|
||||
import com.oracle.truffle.api.debug.DebugException;
|
||||
import com.oracle.truffle.api.debug.DebugStackFrame;
|
||||
import com.oracle.truffle.api.debug.DebugValue;
|
||||
import com.oracle.truffle.api.debug.Debugger;
|
||||
import com.oracle.truffle.api.debug.DebuggerSession;
|
||||
import com.oracle.truffle.api.debug.SuspendedEvent;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@ -15,27 +20,74 @@ import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Engine;
|
||||
import org.graalvm.polyglot.Language;
|
||||
import org.graalvm.polyglot.Source;
|
||||
import org.graalvm.polyglot.Value;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DebuggingEnsoTest {
|
||||
@Test
|
||||
public void evaluation() throws Exception {
|
||||
Engine eng = Engine.newBuilder()
|
||||
private Context context;
|
||||
private Engine engine;
|
||||
private Debugger debugger;
|
||||
|
||||
@Before
|
||||
public void initContext() {
|
||||
engine = Engine.newBuilder()
|
||||
.allowExperimentalOptions(true)
|
||||
.option(
|
||||
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
|
||||
Paths.get("../../test/micro-distribution/component").toFile().getAbsolutePath()
|
||||
).build();
|
||||
Context ctx = Context.newBuilder()
|
||||
.engine(eng)
|
||||
.allowIO(true)
|
||||
.build();
|
||||
final Map<String, Language> langs = ctx.getEngine().getLanguages();
|
||||
org.junit.Assert.assertNotNull("Enso found: " + langs, langs.get("enso"));
|
||||
|
||||
context = Context.newBuilder()
|
||||
.engine(engine)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowIO(true)
|
||||
.allowAllAccess(true)
|
||||
.build();
|
||||
|
||||
debugger = Debugger.find(engine);
|
||||
|
||||
Map<String, Language> langs = engine.getLanguages();
|
||||
Assert.assertNotNull("Enso found: " + langs, langs.get("enso"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void disposeContext() {
|
||||
context.close();
|
||||
engine.close();
|
||||
}
|
||||
|
||||
private static void expectStackFrame(DebugStackFrame actualFrame, Map<String, String> expectedValues) {
|
||||
Map<String, String> actualValues = new HashMap<>();
|
||||
for (DebugValue declaredValue : actualFrame.getScope().getDeclaredValues()) {
|
||||
actualValues.put(
|
||||
declaredValue.getName(),
|
||||
declaredValue.toDisplayString()
|
||||
);
|
||||
}
|
||||
String errMessage = String.format("Expected values in stack: %s, instead got: %s",
|
||||
expectedValues, actualValues);
|
||||
Assert.assertEquals(errMessage, expectedValues, actualValues);
|
||||
}
|
||||
|
||||
private static List<DebugStackFrame> getStackFramesFromEvent(SuspendedEvent event) {
|
||||
List<DebugStackFrame> stackFrames = new ArrayList<>();
|
||||
event.getStackFrames().forEach(stackFrames::add);
|
||||
return stackFrames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps through recursive evaluation of factorial with an accumulator, and for each step,
|
||||
* checks the value of the `accumulator` variable.
|
||||
*/
|
||||
@Test
|
||||
public void recursiveFactorialCall() throws Exception {
|
||||
final URI facUri = new URI("memory://fac.enso");
|
||||
final Source facSrc = Source.newBuilder("enso", """
|
||||
fac : Number -> Number
|
||||
@ -50,11 +102,10 @@ public class DebuggingEnsoTest {
|
||||
.uri(facUri)
|
||||
.buildLiteral();
|
||||
|
||||
var module = ctx.eval(facSrc);
|
||||
var module = context.eval(facSrc);
|
||||
var facFn = module.invokeMember("eval_expression", "fac");
|
||||
final var dbg = Debugger.find(eng);
|
||||
final var values = new TreeSet<Integer>();
|
||||
try (var session = dbg.startSession((event) -> {
|
||||
try (var session = debugger.startSession((event) -> {
|
||||
final DebugValue accumulatorValue = findDebugValue(event, "accumulator");
|
||||
if (accumulatorValue != null) {
|
||||
final int accumulator = accumulatorValue.asInt();
|
||||
@ -69,7 +120,60 @@ public class DebuggingEnsoTest {
|
||||
assertEquals("Accumulator gets following values one by one", Set.of(1, 5, 20, 60, 120), values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the debugger correctly displays the values of variables in
|
||||
* stack frames, including the stack frame of the caller method.
|
||||
*/
|
||||
@Test
|
||||
public void callerVariablesAreVisibleOnPreviousStackFrame() {
|
||||
URI fooUri = URI.create("memory://tmp.enso");
|
||||
Source fooSource = Source.newBuilder("enso", """
|
||||
bar arg_bar =
|
||||
loc_bar = arg_bar + 1
|
||||
loc_bar
|
||||
|
||||
foo x =
|
||||
loc_foo = 1
|
||||
bar loc_foo
|
||||
""", "tmp.enso")
|
||||
.uri(fooUri)
|
||||
.buildLiteral();
|
||||
|
||||
Value module = context.eval(fooSource);
|
||||
Value fooFunc = module.invokeMember("eval_expression", "foo");
|
||||
|
||||
try (DebuggerSession session = debugger.startSession((SuspendedEvent event) -> {
|
||||
// TODO[PM]: This is a workaround for proper breakpoints, which do not work atm.
|
||||
switch (event.getSourceSection().getCharacters().toString().strip()) {
|
||||
// In method "foo"
|
||||
case "bar loc_foo" -> {
|
||||
List<DebugStackFrame> stackFrames = getStackFramesFromEvent(event);
|
||||
Assert.assertEquals(1, stackFrames.size());
|
||||
expectStackFrame(stackFrames.get(0), Map.of("x", "42", "loc_foo", "1"));
|
||||
}
|
||||
// In method "bar" called from "foo"
|
||||
case "loc_bar" -> {
|
||||
List<DebugStackFrame> stackFrames = getStackFramesFromEvent(event);
|
||||
|
||||
Assert.assertEquals(2, stackFrames.size());
|
||||
Assert.assertTrue(stackFrames.get(1).getName().contains("foo"));
|
||||
Assert.assertTrue(stackFrames.get(0).getName().contains("bar"));
|
||||
|
||||
expectStackFrame(stackFrames.get(1), Map.of("x", "42", "loc_foo", "1"));
|
||||
expectStackFrame(stackFrames.get(0), Map.of("arg_bar", "1", "loc_bar", "2"));
|
||||
}
|
||||
}
|
||||
event.getSession().suspendNextExecution();
|
||||
})) {
|
||||
session.suspendNextExecution();
|
||||
fooFunc.execute(42);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO[PM]: Re-enable (https://www.pivotaltracker.com/story/show/183854585)
|
||||
@Test
|
||||
@Ignore
|
||||
public void unsafeRecursiveAtom() throws Exception {
|
||||
Engine eng = Engine.newBuilder()
|
||||
.allowExperimentalOptions(true)
|
||||
|
@ -894,6 +894,21 @@ class AliasAnalysisTest extends CompilerTest {
|
||||
// No link between self.x and self
|
||||
graphLinks shouldEqual Set(Link(valueUseId, 1, valueDefId))
|
||||
}
|
||||
|
||||
"add self as a definition" in {
|
||||
lambda.arguments.length shouldEqual 2
|
||||
lambda.arguments(0).name shouldBe a[IR.Name.Self]
|
||||
val lambdaScope = lambda
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Scope.Child]
|
||||
.scope
|
||||
|
||||
lambdaScope.allDefinitions.length shouldBe 2
|
||||
val defSymbols = lambdaScope.allDefinitions
|
||||
.map(definition => definition.symbol)
|
||||
defSymbols should equal(List("self", "x"))
|
||||
}
|
||||
}
|
||||
|
||||
"Alias analysis on conversion methods" should {
|
||||
@ -1109,6 +1124,19 @@ class AliasAnalysisTest extends CompilerTest {
|
||||
rootScope.childScopes should contain(fallbackBranchScope)
|
||||
}
|
||||
|
||||
"cons branch scope should have argument definitions" in {
|
||||
val consBranchScope = caseExpr.branches.head
|
||||
.getMetadata(AliasAnalysis)
|
||||
.get
|
||||
.unsafeAs[Info.Scope.Child]
|
||||
.scope
|
||||
consBranchScope.allDefinitions.length shouldBe 2
|
||||
|
||||
val defSymbols = consBranchScope.allDefinitions
|
||||
.map(definition => definition.symbol)
|
||||
defSymbols should equal(List("a", "b"))
|
||||
}
|
||||
|
||||
"correctly link to pattern variables" in {
|
||||
val consBranch = caseExpr.branches.head
|
||||
val pattern = consBranch.pattern.asInstanceOf[Pattern.Constructor]
|
||||
|
@ -99,6 +99,15 @@ class SuspendedArgumentsTest extends InterpreterTest {
|
||||
eval(code).call(1) shouldEqual 1
|
||||
}
|
||||
|
||||
"work properly with multiple defaulted arguments" in {
|
||||
val code =
|
||||
"""from Standard.Base import all
|
||||
|
|
||||
|main = a -> (~b = Panic.throw 1) -> (~c = Panic.throw 2) -> a
|
||||
|""".stripMargin
|
||||
eval(code).call(1) shouldEqual 1
|
||||
}
|
||||
|
||||
"allow passing suspended functions" in {
|
||||
val code =
|
||||
"""main =
|
||||
|
@ -506,31 +506,44 @@ object DistributionPackage {
|
||||
* @param os the system type
|
||||
* @param graalDir the directory with a GraalVM distribution
|
||||
* @param arguments the command arguments
|
||||
* @return Stdout from the `gu` command.
|
||||
*/
|
||||
def gu(
|
||||
log: ManagedLogger,
|
||||
os: OS,
|
||||
graalDir: File,
|
||||
arguments: String*
|
||||
): Unit = {
|
||||
): String = {
|
||||
val shallowFile = graalDir / "bin" / "gu"
|
||||
val deepFile = graalDir / "Contents" / "Home" / "bin" / "gu"
|
||||
val executableFile = os match {
|
||||
case OS.Linux =>
|
||||
graalDir / "bin" / "gu"
|
||||
shallowFile
|
||||
case OS.MacOS =>
|
||||
graalDir / "Contents" / "Home" / "bin" / "gu"
|
||||
if (deepFile.exists) {
|
||||
deepFile
|
||||
} else {
|
||||
shallowFile
|
||||
}
|
||||
case OS.Windows =>
|
||||
graalDir / "bin" / "gu.cmd"
|
||||
}
|
||||
val javaHomeFile = executableFile.getParentFile.getParentFile
|
||||
val javaHome = javaHomeFile.toPath.toAbsolutePath
|
||||
val command =
|
||||
executableFile.toPath.toAbsolutePath.toString +: arguments
|
||||
val exitCode = Process(
|
||||
|
||||
log.debug(s"Running $command in $graalDir with JAVA_HOME=${javaHome.toString}")
|
||||
|
||||
try {
|
||||
Process(
|
||||
command,
|
||||
Some(graalDir),
|
||||
("JAVA_HOME", javaHomeFile.toPath.toAbsolutePath.toString),
|
||||
("GRAALVM_HOME", javaHomeFile.toPath.toAbsolutePath.toString)
|
||||
).!
|
||||
if (exitCode != 0) {
|
||||
("JAVA_HOME", javaHome.toString),
|
||||
("GRAALVM_HOME", javaHome.toString)
|
||||
).!!
|
||||
} catch {
|
||||
case _: RuntimeException =>
|
||||
throw new RuntimeException(
|
||||
s"Failed to run '${command.mkString(" ")}'"
|
||||
)
|
||||
|
@ -30,8 +30,6 @@ object NativeImage {
|
||||
* @param artifactName name of the artifact to create
|
||||
* @param staticOnLinux specifies whether to link statically (applies only
|
||||
* on Linux)
|
||||
* @param initializeAtBuildtime specifies if classes should be initialized at
|
||||
* build time by default
|
||||
* @param additionalOptions additional options for the Native Image build
|
||||
* tool
|
||||
* @param memoryLimitMegabytes a memory limit for the build tool, in
|
||||
@ -45,7 +43,6 @@ object NativeImage {
|
||||
def buildNativeImage(
|
||||
artifactName: String,
|
||||
staticOnLinux: Boolean,
|
||||
initializeAtBuildtime: Boolean = true,
|
||||
additionalOptions: Seq[String] = Seq.empty,
|
||||
memoryLimitMegabytes: Option[Int] = Some(15608),
|
||||
initializeAtRuntime: Seq[String] = Seq.empty,
|
||||
@ -100,6 +97,9 @@ object NativeImage {
|
||||
Seq()
|
||||
}
|
||||
|
||||
val quickBuildOption =
|
||||
if (BuildInfo.isReleaseMode) Seq() else Seq("-Ob")
|
||||
|
||||
val memoryLimitOptions =
|
||||
memoryLimitMegabytes.map(megs => s"-J-Xmx${megs}M").toSeq
|
||||
|
||||
@ -110,15 +110,14 @@ object NativeImage {
|
||||
Seq(s"--initialize-at-run-time=$classes")
|
||||
}
|
||||
|
||||
val initializeAtBuildtimeOptions =
|
||||
if (initializeAtBuildtime) Seq("--initialize-at-build-time") else Seq()
|
||||
|
||||
var cmd =
|
||||
Seq(nativeImagePath) ++
|
||||
quickBuildOption ++
|
||||
debugParameters ++ staticParameters ++ configs ++
|
||||
Seq("--no-fallback", "--no-server") ++
|
||||
initializeAtBuildtimeOptions ++
|
||||
memoryLimitOptions ++ initializeAtRuntimeOptions ++
|
||||
Seq("--initialize-at-build-time=") ++
|
||||
initializeAtRuntimeOptions ++
|
||||
memoryLimitOptions ++
|
||||
additionalOptions;
|
||||
|
||||
if (mainClass.isEmpty) {
|
||||
|
@ -49,7 +49,7 @@ spec =
|
||||
{
|
||||
"headers": {
|
||||
"Content-Length": "0",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -63,7 +63,7 @@ spec =
|
||||
{
|
||||
"headers": {
|
||||
"Content-Length": "0",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -78,7 +78,7 @@ spec =
|
||||
{
|
||||
"headers": {
|
||||
"Content-Length": "0",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -99,7 +99,7 @@ spec =
|
||||
{
|
||||
"headers": {
|
||||
"Content-Length": "0",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -118,7 +118,7 @@ spec =
|
||||
{
|
||||
"headers": {
|
||||
"Content-Length": "0",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -138,7 +138,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "12",
|
||||
"Content-Type": "text/plain",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -158,7 +158,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "7",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -178,7 +178,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "7",
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -208,7 +208,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "13",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -231,7 +231,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "13",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -254,7 +254,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "12",
|
||||
"Content-Type": "application/octet-stream",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -274,7 +274,7 @@ spec =
|
||||
{
|
||||
"headers": {
|
||||
"Content-Length": "0",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -290,7 +290,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "13",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
@ -315,7 +315,7 @@ spec =
|
||||
"headers": {
|
||||
"Content-Length": "16",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Java-http-client/11.0.13"
|
||||
"User-Agent": "Java-http-client/11.0.17"
|
||||
},
|
||||
"origin": "127.0.0.1",
|
||||
"url": "",
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM ghcr.io/graalvm/graalvm-ce:java11-21.3.0
|
||||
FROM ghcr.io/graalvm/graalvm-ce:java11-22.3.0
|
||||
|
||||
USER root
|
||||
|
||||
|
@ -66,7 +66,7 @@ and then launch it with special `--dump-graphs` option:
|
||||
enso$ ./built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/bin/enso --dump-graphs --run yourprogram.enso
|
||||
```
|
||||
|
||||
When executed on [GraalVM 21.3.0](http://graalvm.org) these options instruct the
|
||||
When executed on [GraalVM 22.3.0](http://graalvm.org) these options instruct the
|
||||
_Graal/Truffle compiler_ to dump files into `graal_dumps/_sometimestamp_`
|
||||
directory. Generating these files takes a while - make sure `yourprogram.enso`
|
||||
runs long enough for the system to warmup, compile the code and run at _full
|
||||
|
Loading…
Reference in New Issue
Block a user