mirror of
https://github.com/enso-org/enso.git
synced 2024-11-29 16:13:55 +03:00
Register instruments/language in their own compilation units to fix the sbt build issues (#3509)
New plan to [fix the `sbt` build](https://www.pivotaltracker.com/n/projects/2539304/stories/182209126) and its annoying: ``` log.error( "Truffle Instrumentation is not up to date, " + "which will lead to runtime errors\n" + "Fixes have been applied to ensure consistent Instrumentation state, " + "but compilation has to be triggered again.\n" + "Please re-run the previous command.\n" + "(If this for some reason fails, " + s"please do a clean build of the $projectName project)" ) ``` When it is hard to fix `sbt` incremental compilation, let's restructure our project sources so that each `@TruffleInstrument` and `@TruffleLanguage` registration is in individual compilation unit. Each such unit is either going to be compiled or not going to be compiled as a batch - that will eliminate the `sbt` incremental compilation issues without addressing them in `sbt` itself. fa2cf6a33ec4a5b2e3370e1b22c2b5f712286a75 is the first step - it introduces `IdExecutionService` and moves all the `IdExecutionInstrument` API up to that interface. The rest of the `runtime` project then depends only on `IdExecutionService`. Such refactoring allows us to move the `IdExecutionInstrument` out of `runtime` project into independent compilation unit.
This commit is contained in:
parent
fd46e84e8d
commit
dc30e44b60
112
build.sbt
112
build.sbt
@ -260,6 +260,11 @@ lazy val enso = (project in file("."))
|
|||||||
searcher,
|
searcher,
|
||||||
launcher,
|
launcher,
|
||||||
downloader,
|
downloader,
|
||||||
|
`runtime-language-epb`,
|
||||||
|
`runtime-instrument-id-execution`,
|
||||||
|
`runtime-instrument-repl-debugger`,
|
||||||
|
`runtime-instrument-runtime-server`,
|
||||||
|
`runtime-with-instruments`,
|
||||||
`runtime-version-manager`,
|
`runtime-version-manager`,
|
||||||
`runtime-version-manager-test`,
|
`runtime-version-manager-test`,
|
||||||
editions,
|
editions,
|
||||||
@ -1156,6 +1161,30 @@ lazy val frgaalJavaCompilerSetting = Seq(
|
|||||||
Compile / javacOptions ++= Seq("-source", frgaalSourceLevel)
|
Compile / javacOptions ++= Seq("-source", frgaalSourceLevel)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
lazy val instrumentationSettings = frgaalJavaCompilerSetting ++ Seq(
|
||||||
|
version := ensoVersion,
|
||||||
|
commands += WithDebugCommand.withDebug,
|
||||||
|
Compile/logManager :=
|
||||||
|
sbt.internal.util.CustomLogManager.excludeMsg("Could not determine source for class ", Level.Warn),
|
||||||
|
Compile / javacOptions --= Seq("-source", frgaalSourceLevel),
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"org.graalvm.truffle" % "truffle-api" % graalVersion % "provided",
|
||||||
|
"org.graalvm.truffle" % "truffle-dsl-processor" % graalVersion % "provided",
|
||||||
|
),
|
||||||
|
(Compile / javacOptions) ++= Seq(
|
||||||
|
"-s",
|
||||||
|
(Compile / sourceManaged).value.getAbsolutePath,
|
||||||
|
"-Xlint:unchecked",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
lazy val `runtime-language-epb` = (project in file("engine/runtime-language-epb"))
|
||||||
|
.settings(
|
||||||
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
|
instrumentationSettings
|
||||||
|
)
|
||||||
|
|
||||||
lazy val runtime = (project in file("engine/runtime"))
|
lazy val runtime = (project in file("engine/runtime"))
|
||||||
.configs(Benchmark)
|
.configs(Benchmark)
|
||||||
.settings(
|
.settings(
|
||||||
@ -1164,7 +1193,6 @@ lazy val runtime = (project in file("engine/runtime"))
|
|||||||
sbt.internal.util.CustomLogManager.excludeMsg("Could not determine source for class ", Level.Warn),
|
sbt.internal.util.CustomLogManager.excludeMsg("Could not determine source for class ", Level.Warn),
|
||||||
version := ensoVersion,
|
version := ensoVersion,
|
||||||
commands += WithDebugCommand.withDebug,
|
commands += WithDebugCommand.withDebug,
|
||||||
cleanInstruments := FixInstrumentsGeneration.cleanInstruments.value,
|
|
||||||
inConfig(Compile)(truffleRunOptionsSettings),
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
inConfig(Benchmark)(Defaults.testSettings),
|
inConfig(Benchmark)(Defaults.testSettings),
|
||||||
inConfig(Benchmark)(
|
inConfig(Benchmark)(
|
||||||
@ -1197,14 +1225,9 @@ lazy val runtime = (project in file("engine/runtime"))
|
|||||||
"junit" % "junit" % "4.12" % Test,
|
"junit" % "junit" % "4.12" % Test,
|
||||||
"com.novocode" % "junit-interface" % "0.11" % Test exclude ("junit", "junit-dep")
|
"com.novocode" % "junit-interface" % "0.11" % Test exclude ("junit", "junit-dep")
|
||||||
),
|
),
|
||||||
// Note [Unmanaged Classpath]
|
|
||||||
Test / unmanagedClasspath += (baseDirectory.value / ".." / ".." / "app" / "gui" / "view" / "graph-editor" / "src" / "builtin" / "visualization" / "native" / "inc"),
|
|
||||||
Compile / compile / compileInputs := (Compile / compile / compileInputs)
|
Compile / compile / compileInputs := (Compile / compile / compileInputs)
|
||||||
.dependsOn(CopyTruffleJAR.preCompileTask)
|
.dependsOn(CopyTruffleJAR.preCompileTask)
|
||||||
.value,
|
.value,
|
||||||
Compile / compile := FixInstrumentsGeneration.patchedCompile
|
|
||||||
.dependsOn(FixInstrumentsGeneration.preCompileTask)
|
|
||||||
.value,
|
|
||||||
// Note [Classpath Separation]
|
// Note [Classpath Separation]
|
||||||
Test / javaOptions ++= Seq(
|
Test / javaOptions ++= Seq(
|
||||||
"-Dgraalvm.locatorDisabled=true",
|
"-Dgraalvm.locatorDisabled=true",
|
||||||
@ -1258,20 +1281,7 @@ lazy val runtime = (project in file("engine/runtime"))
|
|||||||
}.evaluated,
|
}.evaluated,
|
||||||
Benchmark / parallelExecution := false
|
Benchmark / parallelExecution := false
|
||||||
)
|
)
|
||||||
.settings(
|
.dependsOn(`runtime-language-epb`)
|
||||||
assembly / assemblyJarName := "runtime.jar",
|
|
||||||
assembly / test := {},
|
|
||||||
assembly / assemblyOutputPath := file("runtime.jar"),
|
|
||||||
assembly / assemblyMergeStrategy := {
|
|
||||||
case PathList("META-INF", file, xs @ _*) if file.endsWith(".DSA") =>
|
|
||||||
MergeStrategy.discard
|
|
||||||
case PathList("META-INF", file, xs @ _*) if file.endsWith(".SF") =>
|
|
||||||
MergeStrategy.discard
|
|
||||||
case PathList("META-INF", "MANIFEST.MF", xs @ _*) =>
|
|
||||||
MergeStrategy.discard
|
|
||||||
case _ => MergeStrategy.first
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.dependsOn(`edition-updater`)
|
.dependsOn(`edition-updater`)
|
||||||
.dependsOn(`interpreter-dsl`)
|
.dependsOn(`interpreter-dsl`)
|
||||||
.dependsOn(`library-manager`)
|
.dependsOn(`library-manager`)
|
||||||
@ -1288,6 +1298,66 @@ lazy val runtime = (project in file("engine/runtime"))
|
|||||||
.dependsOn(`docs-generator`)
|
.dependsOn(`docs-generator`)
|
||||||
.dependsOn(testkit % Test)
|
.dependsOn(testkit % Test)
|
||||||
|
|
||||||
|
lazy val `runtime-instrument-id-execution` = (project in file("engine/runtime-instrument-id-execution"))
|
||||||
|
.settings(
|
||||||
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
|
instrumentationSettings
|
||||||
|
)
|
||||||
|
.dependsOn(runtime)
|
||||||
|
|
||||||
|
lazy val `runtime-instrument-repl-debugger` = (project in file("engine/runtime-instrument-repl-debugger"))
|
||||||
|
.settings(
|
||||||
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
|
instrumentationSettings
|
||||||
|
)
|
||||||
|
.dependsOn(runtime)
|
||||||
|
|
||||||
|
lazy val `runtime-instrument-runtime-server` = (project in file("engine/runtime-instrument-runtime-server"))
|
||||||
|
.settings(
|
||||||
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
|
instrumentationSettings
|
||||||
|
)
|
||||||
|
.dependsOn(runtime)
|
||||||
|
|
||||||
|
lazy val `runtime-with-instruments` = (project in file("engine/runtime-with-instruments"))
|
||||||
|
.configs(Benchmark)
|
||||||
|
.settings(
|
||||||
|
inConfig(Compile)(truffleRunOptionsSettings),
|
||||||
|
inConfig(Benchmark)(Defaults.testSettings),
|
||||||
|
Benchmark / javacOptions --= Seq("-source", frgaalSourceLevel),
|
||||||
|
Test / javaOptions ++= Seq(
|
||||||
|
"-Dgraalvm.locatorDisabled=true",
|
||||||
|
s"--upgrade-module-path=${file("engine/runtime/build-cache/truffle-api.jar").absolutePath}"
|
||||||
|
),
|
||||||
|
Test / fork := true,
|
||||||
|
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
|
||||||
|
"ENSO_TEST_DISABLE_IR_CACHE" -> "false"
|
||||||
|
),
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||||
|
),
|
||||||
|
// Note [Unmanaged Classpath]
|
||||||
|
Test / unmanagedClasspath += (baseDirectory.value / ".." / ".." / "app" / "gui" / "view" / "graph-editor" / "src" / "builtin" / "visualization" / "native" / "inc"),
|
||||||
|
assembly / assemblyJarName := "runtime.jar",
|
||||||
|
assembly / test := {},
|
||||||
|
assembly / assemblyOutputPath := file("runtime.jar"),
|
||||||
|
assembly / assemblyMergeStrategy := {
|
||||||
|
case PathList("META-INF", file, xs @ _*) if file.endsWith(".DSA") =>
|
||||||
|
MergeStrategy.discard
|
||||||
|
case PathList("META-INF", file, xs @ _*) if file.endsWith(".SF") =>
|
||||||
|
MergeStrategy.discard
|
||||||
|
case PathList("META-INF", "MANIFEST.MF", xs @ _*) =>
|
||||||
|
MergeStrategy.discard
|
||||||
|
case PathList("META-INF", "services", xs @ _*) =>
|
||||||
|
MergeStrategy.concat
|
||||||
|
case _ => MergeStrategy.first
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.dependsOn(runtime % "compile->compile;test->test")
|
||||||
|
.dependsOn(`runtime-instrument-id-execution`)
|
||||||
|
.dependsOn(`runtime-instrument-repl-debugger`)
|
||||||
|
.dependsOn(`runtime-instrument-runtime-server`)
|
||||||
|
|
||||||
/* Note [Unmanaged Classpath]
|
/* Note [Unmanaged Classpath]
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
* As the definition of the core primitives in `core_definition` is achieved
|
* As the definition of the core primitives in `core_definition` is achieved
|
||||||
@ -1346,7 +1416,7 @@ lazy val `engine-runner` = project
|
|||||||
)
|
)
|
||||||
.settings(
|
.settings(
|
||||||
assembly := assembly
|
assembly := assembly
|
||||||
.dependsOn(runtime / assembly)
|
.dependsOn(`runtime-with-instruments` / assembly)
|
||||||
.value
|
.value
|
||||||
)
|
)
|
||||||
.dependsOn(`version-output`)
|
.dependsOn(`version-output`)
|
||||||
|
@ -85,9 +85,7 @@ dependency of `compileInputs` which runs _strictly before_ actual compilation.
|
|||||||
|
|
||||||
To check some invariants _after_ compilation, we can replace the original
|
To check some invariants _after_ compilation, we can replace the original
|
||||||
`Compile / compile` task with a custom one which does its post-compile checks
|
`Compile / compile` task with a custom one which does its post-compile checks
|
||||||
and returns the result of `(Compile / compile).value`. An example of such a
|
and returns the result of `(Compile / compile).value`.
|
||||||
'patched' compile task is implemented in
|
|
||||||
[`FixInstrumentsGeneration`](../../project/FixInstrumentsGeneration.scala).
|
|
||||||
|
|
||||||
## Helper Tasks
|
## Helper Tasks
|
||||||
|
|
||||||
@ -122,26 +120,17 @@ information is used by `enso --version`.
|
|||||||
Truffle annotation processor generates a file that registers instruments
|
Truffle annotation processor generates a file that registers instruments
|
||||||
provided by the runtime. Unfortunately, with incremental compilation, only the
|
provided by the runtime. Unfortunately, with incremental compilation, only the
|
||||||
changed instruments are recompiled and the annotation processor does not detect
|
changed instruments are recompiled and the annotation processor does not detect
|
||||||
this, so un-changed instruments get un-registered.
|
this, so un-changed instruments get overwritten.
|
||||||
|
|
||||||
To fix this, the pre-compile task defined in
|
In the past we had a pre-compile task (see
|
||||||
[`FixInstrumentsGeneration`](../../project/FixInstrumentsGeneration.scala)
|
[FixInstrumentsGeneration](https://github.com/enso-org/enso/blob/8ec2a92b770dea35e47fa9287dbdd1363aabc3c0/project/FixInstrumentsGeneration.scala))
|
||||||
detects changes to instruments and if only one of them should be recompiled, it
|
that detected changes to instruments and if only one of them was to be
|
||||||
forces recompilation of all of them, to ensure consistency.
|
recompiled, it forced recompilation of all of them, to ensure consistency. This
|
||||||
|
workaround helped to avoid later runtime issues but sometimes triggered a
|
||||||
For unclear reasons, if this task is attached to
|
cascade of recompilations, which weren't clear to the end user. Instead, to
|
||||||
`Compile / compile / compileInputs`, while it runs strictly _before_
|
avoid overwriting entries in META-INF files, individual services were moved to
|
||||||
compilation, the deleted class files are not always all recompiled. So instead,
|
separate subprojects and during assembly of uber jar we concatenate meta files
|
||||||
it is attached directly to `Compile / compile`. This technically could allow for
|
with the same service name.
|
||||||
a data race between this task and the actual compilation that happens in
|
|
||||||
`compileIncremental`, but in practice it seems to be a stable solution.
|
|
||||||
|
|
||||||
Sometimes it is unable to detect the need for recompilation before it takes
|
|
||||||
place. To help that, there is another task that replaces the default `compile`
|
|
||||||
task, which executes the default compilation task and after it, verifies the
|
|
||||||
consistency of instruments files. As it cannot restart compilation, to preserve
|
|
||||||
consistency it ensures the instruments will be recompiled the next time and
|
|
||||||
stops the current compilation task, asking the user to restart it.
|
|
||||||
|
|
||||||
### Flatbuffers Generation
|
### Flatbuffers Generation
|
||||||
|
|
||||||
|
@ -21,8 +21,7 @@ and other kinds of behavior analysis at runtime.
|
|||||||
## Naming Conventions
|
## Naming Conventions
|
||||||
|
|
||||||
Every Instrument must be implemented in Java and have name that ends with
|
Every Instrument must be implemented in Java and have name that ends with
|
||||||
`Instrument`. This requirement is to ensure that the [fix](#fixing-compilation)
|
`Instrument`.
|
||||||
described below works.
|
|
||||||
|
|
||||||
## Fixing Compilation
|
## Fixing Compilation
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ Unfortunately, when doing an incremental compilation, only the changed files are
|
|||||||
recompiled and the annotation processor 'forgets' about other instruments that
|
recompiled and the annotation processor 'forgets' about other instruments that
|
||||||
haven't been recompiled, leading to runtime errors about missing instruments.
|
haven't been recompiled, leading to runtime errors about missing instruments.
|
||||||
|
|
||||||
To fix that, we add the
|
To fix that, individual services have to be placed in separate subprojects
|
||||||
[`FixInstrumentsGeneration.scala`](../../project/FixInstrumentsGeneration.scala)
|
depending on `runtime` and aggregated under `runtime-with-instruments`. Later
|
||||||
task which detects changes to any of the instruments and forces recompilation of
|
the META-INF registrations are concatenated in the final uber jar.
|
||||||
all instruments in the project by removing their classfiles.
|
|
||||||
|
@ -31,11 +31,9 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
/** An instrument for getting values from AST-identified expressions. */
|
/** An instrument for getting values from AST-identified expressions. */
|
||||||
@TruffleInstrument.Registration(
|
@TruffleInstrument.Registration(
|
||||||
id = IdExecutionInstrument.INSTRUMENT_ID,
|
id = IdExecutionService.INSTRUMENT_ID,
|
||||||
services = IdExecutionInstrument.class)
|
services = IdExecutionService.class)
|
||||||
public class IdExecutionInstrument extends TruffleInstrument {
|
public class IdExecutionInstrument extends TruffleInstrument implements IdExecutionService {
|
||||||
public static final String INSTRUMENT_ID = "id-value-extractor";
|
|
||||||
|
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
private Env env;
|
private Env env;
|
||||||
|
|
||||||
@ -56,225 +54,11 @@ public class IdExecutionInstrument extends TruffleInstrument {
|
|||||||
*
|
*
|
||||||
* @param timer the timer to override with
|
* @param timer the timer to override with
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void overrideTimer(Timer timer) {
|
public void overrideTimer(Timer timer) {
|
||||||
this.timer = timer;
|
this.timer = timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A class for notifications about functions being called in the course of execution. */
|
|
||||||
public static class ExpressionCall {
|
|
||||||
private final UUID expressionId;
|
|
||||||
private final FunctionCallInstrumentationNode.FunctionCall call;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of this class.
|
|
||||||
*
|
|
||||||
* @param expressionId the expression id where function call was performed.
|
|
||||||
* @param call the actual function call data.
|
|
||||||
*/
|
|
||||||
public ExpressionCall(UUID expressionId, FunctionCallInstrumentationNode.FunctionCall call) {
|
|
||||||
this.expressionId = expressionId;
|
|
||||||
this.call = call;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the id of the node performing the function call. */
|
|
||||||
public UUID getExpressionId() {
|
|
||||||
return expressionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the function call metadata. */
|
|
||||||
public FunctionCallInstrumentationNode.FunctionCall getCall() {
|
|
||||||
return call;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A class for notifications about identified expressions' values being computed. */
|
|
||||||
public static class ExpressionValue {
|
|
||||||
private final UUID expressionId;
|
|
||||||
private final Object value;
|
|
||||||
private final String type;
|
|
||||||
private final String cachedType;
|
|
||||||
private final FunctionCallInfo callInfo;
|
|
||||||
private final FunctionCallInfo cachedCallInfo;
|
|
||||||
private final ProfilingInfo[] profilingInfo;
|
|
||||||
private final boolean wasCached;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance of this class.
|
|
||||||
*
|
|
||||||
* @param expressionId the id of the expression being computed.
|
|
||||||
* @param value the value returned by computing the expression.
|
|
||||||
* @param type the type of the returned value.
|
|
||||||
* @param cachedType the cached type of the value.
|
|
||||||
* @param callInfo the function call data.
|
|
||||||
* @param cachedCallInfo the cached call data.
|
|
||||||
* @param profilingInfo the profiling information associated with this node
|
|
||||||
* @param wasCached whether or not the value was obtained from the cache
|
|
||||||
*/
|
|
||||||
public ExpressionValue(
|
|
||||||
UUID expressionId,
|
|
||||||
Object value,
|
|
||||||
String type,
|
|
||||||
String cachedType,
|
|
||||||
FunctionCallInfo callInfo,
|
|
||||||
FunctionCallInfo cachedCallInfo,
|
|
||||||
ProfilingInfo[] profilingInfo,
|
|
||||||
boolean wasCached) {
|
|
||||||
this.expressionId = expressionId;
|
|
||||||
this.value = value;
|
|
||||||
this.type = type;
|
|
||||||
this.cachedType = cachedType;
|
|
||||||
this.callInfo = callInfo;
|
|
||||||
this.cachedCallInfo = cachedCallInfo;
|
|
||||||
this.profilingInfo = profilingInfo;
|
|
||||||
this.wasCached = wasCached;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String profilingInfo = Arrays.toString(this.profilingInfo);
|
|
||||||
return "ExpressionValue{"
|
|
||||||
+ "expressionId="
|
|
||||||
+ expressionId
|
|
||||||
+ ", value="
|
|
||||||
+ new MaskedString(value.toString()).applyMasking()
|
|
||||||
+ ", type='"
|
|
||||||
+ type
|
|
||||||
+ '\''
|
|
||||||
+ ", cachedType='"
|
|
||||||
+ cachedType
|
|
||||||
+ '\''
|
|
||||||
+ ", callInfo="
|
|
||||||
+ callInfo
|
|
||||||
+ ", cachedCallInfo="
|
|
||||||
+ cachedCallInfo
|
|
||||||
+ ", profilingInfo="
|
|
||||||
+ profilingInfo
|
|
||||||
+ ", wasCached="
|
|
||||||
+ wasCached
|
|
||||||
+ '}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the id of the expression computed. */
|
|
||||||
public UUID getExpressionId() {
|
|
||||||
return expressionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the type of the returned value. */
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the cached type of the value. */
|
|
||||||
public String getCachedType() {
|
|
||||||
return cachedType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the computed value of the expression. */
|
|
||||||
public Object getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the function call data. */
|
|
||||||
public FunctionCallInfo getCallInfo() {
|
|
||||||
return callInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the function call data previously associated with the expression. */
|
|
||||||
public FunctionCallInfo getCachedCallInfo() {
|
|
||||||
return cachedCallInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the profiling information associated with this expression */
|
|
||||||
public ProfilingInfo[] getProfilingInfo() {
|
|
||||||
return profilingInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return whether or not the expression result was obtained from the cache */
|
|
||||||
public boolean wasCached() {
|
|
||||||
return wasCached;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return {@code true} when the type differs from the cached value. */
|
|
||||||
public boolean isTypeChanged() {
|
|
||||||
return !Objects.equals(type, cachedType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return {@code true} when the function call differs from the cached value. */
|
|
||||||
public boolean isFunctionCallChanged() {
|
|
||||||
return !Objects.equals(callInfo, cachedCallInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Information about the function call. */
|
|
||||||
public static class FunctionCallInfo {
|
|
||||||
|
|
||||||
private final QualifiedName moduleName;
|
|
||||||
private final QualifiedName typeName;
|
|
||||||
private final String functionName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance of this class.
|
|
||||||
*
|
|
||||||
* @param call the function call.
|
|
||||||
*/
|
|
||||||
public FunctionCallInfo(FunctionCallInstrumentationNode.FunctionCall call) {
|
|
||||||
RootNode rootNode = call.getFunction().getCallTarget().getRootNode();
|
|
||||||
if (rootNode instanceof MethodRootNode) {
|
|
||||||
MethodRootNode methodNode = (MethodRootNode) rootNode;
|
|
||||||
moduleName = methodNode.getModuleScope().getModule().getName();
|
|
||||||
typeName = methodNode.getAtomConstructor().getQualifiedName();
|
|
||||||
functionName = methodNode.getMethodName();
|
|
||||||
} else if (rootNode instanceof EnsoRootNode) {
|
|
||||||
moduleName = ((EnsoRootNode) rootNode).getModuleScope().getModule().getName();
|
|
||||||
typeName = null;
|
|
||||||
functionName = rootNode.getName();
|
|
||||||
} else {
|
|
||||||
moduleName = null;
|
|
||||||
typeName = null;
|
|
||||||
functionName = rootNode.getName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
FunctionCallInfo that = (FunctionCallInfo) o;
|
|
||||||
return Objects.equals(moduleName, that.moduleName)
|
|
||||||
&& Objects.equals(typeName, that.typeName)
|
|
||||||
&& Objects.equals(functionName, that.functionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(moduleName, typeName, functionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return moduleName + "::" + typeName + "::" + functionName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the name of the module this function was defined in, or null if not available. */
|
|
||||||
public QualifiedName getModuleName() {
|
|
||||||
return moduleName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the name of the type this method was defined for, or null if not a method. */
|
|
||||||
public QualifiedName getTypeName() {
|
|
||||||
return typeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return the name of this function. */
|
|
||||||
public String getFunctionName() {
|
|
||||||
return functionName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The listener class used by this instrument. */
|
/** The listener class used by this instrument. */
|
||||||
private static class IdExecutionEventListener implements ExecutionEventListener {
|
private static class IdExecutionEventListener implements ExecutionEventListener {
|
||||||
private final CallTarget entryCallTarget;
|
private final CallTarget entryCallTarget;
|
||||||
@ -506,6 +290,7 @@ public class IdExecutionInstrument extends TruffleInstrument {
|
|||||||
* @param onExceptionalCallback the consumer of the exceptional events.
|
* @param onExceptionalCallback the consumer of the exceptional events.
|
||||||
* @return a reference to the attached event listener.
|
* @return a reference to the attached event listener.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public EventBinding<ExecutionEventListener> bind(
|
public EventBinding<ExecutionEventListener> bind(
|
||||||
CallTarget entryCallTarget,
|
CallTarget entryCallTarget,
|
||||||
LocationFilter locationFilter,
|
LocationFilter locationFilter,
|
@ -20,7 +20,6 @@ import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
|
|||||||
import org.enso.interpreter.node.expression.debug.CaptureResultScopeNode;
|
import org.enso.interpreter.node.expression.debug.CaptureResultScopeNode;
|
||||||
import org.enso.interpreter.node.expression.debug.EvalNode;
|
import org.enso.interpreter.node.expression.debug.EvalNode;
|
||||||
import org.enso.interpreter.runtime.Context;
|
import org.enso.interpreter.runtime.Context;
|
||||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
|
||||||
import org.enso.interpreter.runtime.callable.CallerInfo;
|
import org.enso.interpreter.runtime.callable.CallerInfo;
|
||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
import org.enso.interpreter.runtime.data.text.Text;
|
import org.enso.interpreter.runtime.data.text.Text;
|
||||||
@ -61,8 +60,8 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
|||||||
instrumenter.attachExecutionEventFactory(
|
instrumenter.attachExecutionEventFactory(
|
||||||
filter,
|
filter,
|
||||||
ctx ->
|
ctx ->
|
||||||
new ReplExecutionEventNode(
|
new ReplExecutionEventNodeImpl(
|
||||||
ctx, handler, env.getLogger(ReplExecutionEventNode.class)));
|
ctx, handler, env.getLogger(ReplExecutionEventNodeImpl.class)));
|
||||||
} else {
|
} else {
|
||||||
env.getLogger(ReplDebuggerInstrument.class)
|
env.getLogger(ReplDebuggerInstrument.class)
|
||||||
.warning("ReplDebuggerInstrument was initialized, " + "but no client connected");
|
.warning("ReplDebuggerInstrument was initialized, " + "but no client connected");
|
||||||
@ -85,7 +84,8 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The actual node that's installed as a probe on any node the instrument was launched for. */
|
/** The actual node that's installed as a probe on any node the instrument was launched for. */
|
||||||
public static class ReplExecutionEventNode extends ExecutionEventNode {
|
private static class ReplExecutionEventNodeImpl extends ExecutionEventNode
|
||||||
|
implements ReplExecutionEventNode {
|
||||||
private @Child EvalNode evalNode = EvalNode.buildWithResultScopeCapture();
|
private @Child EvalNode evalNode = EvalNode.buildWithResultScopeCapture();
|
||||||
private @Child ToJavaStringNode toJavaStringNode = ToJavaStringNode.build();
|
private @Child ToJavaStringNode toJavaStringNode = ToJavaStringNode.build();
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
|||||||
private DebuggerMessageHandler handler;
|
private DebuggerMessageHandler handler;
|
||||||
private TruffleLogger logger;
|
private TruffleLogger logger;
|
||||||
|
|
||||||
private ReplExecutionEventNode(
|
private ReplExecutionEventNodeImpl(
|
||||||
EventContext eventContext, DebuggerMessageHandler handler, TruffleLogger logger) {
|
EventContext eventContext, DebuggerMessageHandler handler, TruffleLogger logger) {
|
||||||
this.eventContext = eventContext;
|
this.eventContext = eventContext;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
@ -114,11 +114,7 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
|||||||
return currentFrame;
|
return currentFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Lists all the bindings available in the current execution scope.
|
|
||||||
*
|
|
||||||
* @return a map, where keys are variable names and values are current values of variables.
|
|
||||||
*/
|
|
||||||
public Map<String, Object> listBindings() {
|
public Map<String, Object> listBindings() {
|
||||||
Map<String, FramePointer> flatScope =
|
Map<String, FramePointer> flatScope =
|
||||||
nodeState.getLastScope().getLocalScope().flattenBindings();
|
nodeState.getLastScope().getLocalScope().flattenBindings();
|
||||||
@ -129,12 +125,7 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Evaluates an arbitrary expression in the current execution context.
|
|
||||||
*
|
|
||||||
* @param expression the expression to evaluate
|
|
||||||
* @return the result of evaluating the expression or an exception that caused failure
|
|
||||||
*/
|
|
||||||
public Either<Exception, Object> evaluate(String expression) {
|
public Either<Exception, Object> evaluate(String expression) {
|
||||||
ReplExecutionEventNodeState savedState = nodeState;
|
ReplExecutionEventNodeState savedState = nodeState;
|
||||||
try {
|
try {
|
||||||
@ -155,14 +146,11 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the String representation of the provided object as defined by Enso {@code to_text}
|
public Either<Exception, String> showObject(Object object) {
|
||||||
* operation.
|
|
||||||
*/
|
|
||||||
public Either<Exception, String> showObject(Object o) {
|
|
||||||
try {
|
try {
|
||||||
InteropLibrary interop = InteropLibrary.getUncached();
|
InteropLibrary interop = InteropLibrary.getUncached();
|
||||||
return new Right<>(interop.asString(interop.toDisplayString(o)));
|
return new Right<>(interop.asString(interop.toDisplayString(object)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return new Left<>(e);
|
return new Left<>(e);
|
||||||
}
|
}
|
||||||
@ -176,15 +164,7 @@ public class ReplDebuggerInstrument extends TruffleInstrument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Terminates this REPL session.
|
|
||||||
*
|
|
||||||
* <p>The last result of {@link #evaluate(String)} (or {@link Builtins#nothing()} if {@link
|
|
||||||
* #evaluate(String)} was not called before) will be returned from the instrumented node.
|
|
||||||
*
|
|
||||||
* <p>This function must always be called at the end of REPL session, as otherwise the program
|
|
||||||
* will never resume. It's forbidden to use this object after exit has been called.
|
|
||||||
*/
|
|
||||||
public void exit() {
|
public void exit() {
|
||||||
throw eventContext.createUnwind(nodeState.getLastReturn());
|
throw eventContext.createUnwind(nodeState.getLastReturn());
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ package org.enso.interpreter.epb;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import com.oracle.truffle.api.TruffleLanguage;
|
import com.oracle.truffle.api.TruffleLanguage;
|
||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
import org.enso.interpreter.Language;
|
|
||||||
import org.enso.interpreter.epb.runtime.GuardedTruffleContext;
|
import org.enso.interpreter.epb.runtime.GuardedTruffleContext;
|
||||||
|
|
||||||
/**
|
/**
|
@ -4,7 +4,6 @@ import com.oracle.truffle.api.dsl.NodeField;
|
|||||||
import com.oracle.truffle.api.dsl.Specialization;
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
import com.oracle.truffle.api.interop.*;
|
import com.oracle.truffle.api.interop.*;
|
||||||
import com.oracle.truffle.api.library.CachedLibrary;
|
import com.oracle.truffle.api.library.CachedLibrary;
|
||||||
import org.enso.interpreter.runtime.data.Array;
|
|
||||||
|
|
||||||
/** A node responsible for performing foreign JS calls. */
|
/** A node responsible for performing foreign JS calls. */
|
||||||
@NodeField(name = "foreignFunction", type = Object.class)
|
@NodeField(name = "foreignFunction", type = Object.class)
|
||||||
@ -34,7 +33,7 @@ public abstract class JsForeignNode extends ForeignFunctionCallNode {
|
|||||||
if (getArity() - 1 >= 0) System.arraycopy(arguments, 1, positionalArgs, 0, getArity() - 1);
|
if (getArity() - 1 >= 0) System.arraycopy(arguments, 1, positionalArgs, 0, getArity() - 1);
|
||||||
try {
|
try {
|
||||||
return interopLibrary.invokeMember(
|
return interopLibrary.invokeMember(
|
||||||
getForeignFunction(), "apply", arguments[0], new Array(positionalArgs));
|
getForeignFunction(), "apply", arguments[0], new ReadOnlyArray(positionalArgs));
|
||||||
} catch (UnsupportedMessageException
|
} catch (UnsupportedMessageException
|
||||||
| UnknownIdentifierException
|
| UnknownIdentifierException
|
||||||
| ArityException
|
| ArityException
|
@ -0,0 +1,96 @@
|
|||||||
|
package org.enso.interpreter.epb.node;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||||
|
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
|
||||||
|
import com.oracle.truffle.api.interop.TruffleObject;
|
||||||
|
import com.oracle.truffle.api.library.ExportLibrary;
|
||||||
|
import com.oracle.truffle.api.library.ExportMessage;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A primitive boxed array type to be used only in EPB.
|
||||||
|
*
|
||||||
|
* <p>{@link ReadOnlyArray} is essentially a stripped-down, read-only, version of {@link
|
||||||
|
* org.enso.interpreter.runtime.data.Array}, used for passing arguments. The latter cannot be used
|
||||||
|
* in EPB because EPB is a dependency of runtime.
|
||||||
|
*/
|
||||||
|
@ExportLibrary(InteropLibrary.class)
|
||||||
|
public class ReadOnlyArray implements TruffleObject {
|
||||||
|
|
||||||
|
private final Object[] items;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array
|
||||||
|
*
|
||||||
|
* @param items the element values
|
||||||
|
*/
|
||||||
|
public ReadOnlyArray(Object... items) {
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Marks the object as array-like for Polyglot APIs.
|
||||||
|
*
|
||||||
|
* @return {@code true}
|
||||||
|
*/
|
||||||
|
@ExportMessage
|
||||||
|
public boolean hasArrayElements() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles reading an element by index through the polyglot API.
|
||||||
|
*
|
||||||
|
* @param index the index to read
|
||||||
|
* @return the element value at the provided index
|
||||||
|
* @throws InvalidArrayIndexException when the index is out of bounds.
|
||||||
|
*/
|
||||||
|
@ExportMessage
|
||||||
|
public Object readArrayElement(long index) throws InvalidArrayIndexException {
|
||||||
|
if (index >= items.length || index < 0) {
|
||||||
|
throw InvalidArrayIndexException.create(index);
|
||||||
|
}
|
||||||
|
return items[(int) index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the size of this collection through the polyglot API.
|
||||||
|
*
|
||||||
|
* @return the size of this array
|
||||||
|
*/
|
||||||
|
@ExportMessage
|
||||||
|
long getArraySize() {
|
||||||
|
return items.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes an index validity check through the polyglot API.
|
||||||
|
*
|
||||||
|
* @param index the index to check
|
||||||
|
* @return {@code true} if the index is valid, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
@ExportMessage
|
||||||
|
boolean isArrayElementReadable(long index) {
|
||||||
|
return index < getArraySize() && index >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExportMessage
|
||||||
|
void writeArrayElement(long index, Object value) {
|
||||||
|
throw new UnsupportedOperationException("writing unsupoorted in PrimArray");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExportMessage
|
||||||
|
boolean isArrayElementModifiable(long index) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExportMessage
|
||||||
|
boolean isArrayElementInsertable(long index) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Arrays.toString(items);
|
||||||
|
}
|
||||||
|
}
|
@ -81,8 +81,9 @@ class RuntimeErrorsTest
|
|||||||
.getBindings(LanguageInfo.ID)
|
.getBindings(LanguageInfo.ID)
|
||||||
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
|
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
|
||||||
.asHostObject[org.enso.interpreter.runtime.Context]
|
.asHostObject[org.enso.interpreter.runtime.Context]
|
||||||
languageContext.getLanguage.getIdExecutionInstrument
|
languageContext.getLanguage.getIdExecutionService.ifPresent(
|
||||||
.overrideTimer(new TestTimer)
|
_.overrideTimer(new TestTimer)
|
||||||
|
);
|
||||||
|
|
||||||
def writeMain(contents: String): File =
|
def writeMain(contents: String): File =
|
||||||
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
@ -74,8 +74,9 @@ class RuntimeInstrumentTest
|
|||||||
.getBindings(LanguageInfo.ID)
|
.getBindings(LanguageInfo.ID)
|
||||||
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
|
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
|
||||||
.asHostObject[org.enso.interpreter.runtime.Context]
|
.asHostObject[org.enso.interpreter.runtime.Context]
|
||||||
languageContext.getLanguage.getIdExecutionInstrument
|
languageContext.getLanguage.getIdExecutionService.ifPresent(
|
||||||
.overrideTimer(new TestTimer)
|
_.overrideTimer(new TestTimer)
|
||||||
|
);
|
||||||
|
|
||||||
def writeMain(contents: String): File =
|
def writeMain(contents: String): File =
|
||||||
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
@ -82,8 +82,9 @@ class RuntimeServerTest
|
|||||||
.asHostObject[EnsoContext]
|
.asHostObject[EnsoContext]
|
||||||
val info =
|
val info =
|
||||||
languageContext.getEnvironment.getPublicLanguages.get(LanguageInfo.ID)
|
languageContext.getEnvironment.getPublicLanguages.get(LanguageInfo.ID)
|
||||||
languageContext.getLanguage.getIdExecutionInstrument
|
languageContext.getLanguage.getIdExecutionService.ifPresent(
|
||||||
.overrideTimer(new TestTimer)
|
_.overrideTimer(new TestTimer)
|
||||||
|
);
|
||||||
|
|
||||||
def writeMain(contents: String): File =
|
def writeMain(contents: String): File =
|
||||||
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
@ -81,8 +81,9 @@ class RuntimeVisualisationsTest
|
|||||||
.getBindings(LanguageInfo.ID)
|
.getBindings(LanguageInfo.ID)
|
||||||
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
|
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
|
||||||
.asHostObject[EnsoContext]
|
.asHostObject[EnsoContext]
|
||||||
languageContext.getLanguage.getIdExecutionInstrument
|
languageContext.getLanguage.getIdExecutionService.ifPresent(
|
||||||
.overrideTimer(new TestTimer)
|
_.overrideTimer(new TestTimer)
|
||||||
|
);
|
||||||
|
|
||||||
def writeMain(contents: String): File =
|
def writeMain(contents: String): File =
|
||||||
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
Files.write(pkg.mainFile.toPath, contents.getBytes).toFile
|
@ -15,7 +15,7 @@ import org.enso.distribution.Environment;
|
|||||||
import org.enso.distribution.locking.LockManager;
|
import org.enso.distribution.locking.LockManager;
|
||||||
import org.enso.distribution.locking.ThreadSafeFileLockManager;
|
import org.enso.distribution.locking.ThreadSafeFileLockManager;
|
||||||
import org.enso.interpreter.epb.EpbLanguage;
|
import org.enso.interpreter.epb.EpbLanguage;
|
||||||
import org.enso.interpreter.instrument.IdExecutionInstrument;
|
import org.enso.interpreter.instrument.IdExecutionService;
|
||||||
import org.enso.interpreter.instrument.NotificationHandler.Forwarder;
|
import org.enso.interpreter.instrument.NotificationHandler.Forwarder;
|
||||||
import org.enso.interpreter.instrument.NotificationHandler.TextMode$;
|
import org.enso.interpreter.instrument.NotificationHandler.TextMode$;
|
||||||
import org.enso.interpreter.node.ProgramRootNode;
|
import org.enso.interpreter.node.ProgramRootNode;
|
||||||
@ -29,6 +29,8 @@ import org.enso.polyglot.LanguageInfo;
|
|||||||
import org.enso.polyglot.RuntimeOptions;
|
import org.enso.polyglot.RuntimeOptions;
|
||||||
import org.graalvm.options.OptionDescriptors;
|
import org.graalvm.options.OptionDescriptors;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The root of the Enso implementation.
|
* The root of the Enso implementation.
|
||||||
*
|
*
|
||||||
@ -59,7 +61,7 @@ import org.graalvm.options.OptionDescriptors;
|
|||||||
IdentifiedTag.class
|
IdentifiedTag.class
|
||||||
})
|
})
|
||||||
public final class Language extends TruffleLanguage<Context> {
|
public final class Language extends TruffleLanguage<Context> {
|
||||||
private IdExecutionInstrument idExecutionInstrument;
|
private Optional<IdExecutionService> idExecutionInstrument = Optional.empty();
|
||||||
private static final LanguageReference<Language> REFERENCE =
|
private static final LanguageReference<Language> REFERENCE =
|
||||||
LanguageReference.create(Language.class);
|
LanguageReference.create(Language.class);
|
||||||
|
|
||||||
@ -108,12 +110,15 @@ public final class Language extends TruffleLanguage<Context> {
|
|||||||
Context context =
|
Context context =
|
||||||
new Context(
|
new Context(
|
||||||
this, getLanguageHome(), env, notificationHandler, lockManager, distributionManager);
|
this, getLanguageHome(), env, notificationHandler, lockManager, distributionManager);
|
||||||
InstrumentInfo idValueListenerInstrument =
|
idExecutionInstrument =
|
||||||
env.getInstruments().get(IdExecutionInstrument.INSTRUMENT_ID);
|
Optional.ofNullable(env.getInstruments().get(IdExecutionService.INSTRUMENT_ID))
|
||||||
idExecutionInstrument = env.lookup(idValueListenerInstrument, IdExecutionInstrument.class);
|
.map(
|
||||||
|
idValueListenerInstrument ->
|
||||||
|
env.lookup(idValueListenerInstrument, IdExecutionService.class));
|
||||||
env.registerService(
|
env.registerService(
|
||||||
new ExecutionService(
|
new ExecutionService(
|
||||||
context, idExecutionInstrument, notificationHandler, connectedLockManager));
|
context, idExecutionInstrument, notificationHandler, connectedLockManager));
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +184,7 @@ public final class Language extends TruffleLanguage<Context> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return a reference to the execution instrument */
|
/** @return a reference to the execution instrument */
|
||||||
public IdExecutionInstrument getIdExecutionInstrument() {
|
public Optional<IdExecutionService> getIdExecutionService() {
|
||||||
return idExecutionInstrument;
|
return idExecutionInstrument;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,271 @@
|
|||||||
|
package org.enso.interpreter.instrument;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.CallTarget;
|
||||||
|
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||||
|
import com.oracle.truffle.api.instrumentation.ExecutionEventListener;
|
||||||
|
import com.oracle.truffle.api.nodes.RootNode;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import org.enso.interpreter.instrument.execution.LocationFilter;
|
||||||
|
import org.enso.interpreter.instrument.execution.Timer;
|
||||||
|
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
|
||||||
|
import org.enso.interpreter.node.EnsoRootNode;
|
||||||
|
import org.enso.interpreter.node.MethodRootNode;
|
||||||
|
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||||
|
import org.enso.logger.masking.MaskedString;
|
||||||
|
import org.enso.pkg.QualifiedName;
|
||||||
|
|
||||||
|
public interface IdExecutionService {
|
||||||
|
public static final String INSTRUMENT_ID = "id-value-extractor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach a new listener to observe identified nodes within given function.
|
||||||
|
*
|
||||||
|
* @param entryCallTarget the call target being observed.
|
||||||
|
* @param locationFilter the location filter.
|
||||||
|
* @param cache the precomputed expression values.
|
||||||
|
* @param methodCallsCache the storage tracking the executed method calls.
|
||||||
|
* @param syncState the synchronization state of runtime updates.
|
||||||
|
* @param nextExecutionItem the next item scheduled for execution.
|
||||||
|
* @param functionCallCallback the consumer of function call events.
|
||||||
|
* @param onComputedCallback the consumer of the computed value events.
|
||||||
|
* @param onCachedCallback the consumer of the cached value events.
|
||||||
|
* @param onExceptionalCallback the consumer of the exceptional events.
|
||||||
|
* @return a reference to the attached event listener.
|
||||||
|
*/
|
||||||
|
public EventBinding<ExecutionEventListener> bind(
|
||||||
|
CallTarget entryCallTarget,
|
||||||
|
LocationFilter locationFilter,
|
||||||
|
RuntimeCache cache,
|
||||||
|
MethodCallsCache methodCallsCache,
|
||||||
|
UpdatesSynchronizationState syncState,
|
||||||
|
UUID nextExecutionItem,
|
||||||
|
Consumer<ExpressionCall> functionCallCallback,
|
||||||
|
Consumer<ExpressionValue> onComputedCallback,
|
||||||
|
Consumer<ExpressionValue> onCachedCallback,
|
||||||
|
Consumer<Exception> onExceptionalCallback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the default nanosecond timer with the specified {@code timer}.
|
||||||
|
*
|
||||||
|
* @param timer the timer to override with
|
||||||
|
*/
|
||||||
|
void overrideTimer(Timer timer);
|
||||||
|
|
||||||
|
/** A class for notifications about functions being called in the course of execution. */
|
||||||
|
public static class ExpressionCall {
|
||||||
|
private final UUID expressionId;
|
||||||
|
private final FunctionCallInstrumentationNode.FunctionCall call;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of this class.
|
||||||
|
*
|
||||||
|
* @param expressionId the expression id where function call was performed.
|
||||||
|
* @param call the actual function call data.
|
||||||
|
*/
|
||||||
|
public ExpressionCall(UUID expressionId, FunctionCallInstrumentationNode.FunctionCall call) {
|
||||||
|
this.expressionId = expressionId;
|
||||||
|
this.call = call;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the id of the node performing the function call. */
|
||||||
|
public UUID getExpressionId() {
|
||||||
|
return expressionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the function call metadata. */
|
||||||
|
public FunctionCallInstrumentationNode.FunctionCall getCall() {
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A class for notifications about identified expressions' values being computed. */
|
||||||
|
public static class ExpressionValue {
|
||||||
|
private final UUID expressionId;
|
||||||
|
private final Object value;
|
||||||
|
private final String type;
|
||||||
|
private final String cachedType;
|
||||||
|
private final FunctionCallInfo callInfo;
|
||||||
|
private final FunctionCallInfo cachedCallInfo;
|
||||||
|
private final ProfilingInfo[] profilingInfo;
|
||||||
|
private final boolean wasCached;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of this class.
|
||||||
|
*
|
||||||
|
* @param expressionId the id of the expression being computed.
|
||||||
|
* @param value the value returned by computing the expression.
|
||||||
|
* @param type the type of the returned value.
|
||||||
|
* @param cachedType the cached type of the value.
|
||||||
|
* @param callInfo the function call data.
|
||||||
|
* @param cachedCallInfo the cached call data.
|
||||||
|
* @param profilingInfo the profiling information associated with this node
|
||||||
|
* @param wasCached whether or not the value was obtained from the cache
|
||||||
|
*/
|
||||||
|
public ExpressionValue(
|
||||||
|
UUID expressionId,
|
||||||
|
Object value,
|
||||||
|
String type,
|
||||||
|
String cachedType,
|
||||||
|
FunctionCallInfo callInfo,
|
||||||
|
FunctionCallInfo cachedCallInfo,
|
||||||
|
ProfilingInfo[] profilingInfo,
|
||||||
|
boolean wasCached) {
|
||||||
|
this.expressionId = expressionId;
|
||||||
|
this.value = value;
|
||||||
|
this.type = type;
|
||||||
|
this.cachedType = cachedType;
|
||||||
|
this.callInfo = callInfo;
|
||||||
|
this.cachedCallInfo = cachedCallInfo;
|
||||||
|
this.profilingInfo = profilingInfo;
|
||||||
|
this.wasCached = wasCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String profilingInfo = Arrays.toString(this.profilingInfo);
|
||||||
|
return "ExpressionValue{"
|
||||||
|
+ "expressionId="
|
||||||
|
+ expressionId
|
||||||
|
+ ", value="
|
||||||
|
+ new MaskedString(value.toString()).applyMasking()
|
||||||
|
+ ", type='"
|
||||||
|
+ type
|
||||||
|
+ '\''
|
||||||
|
+ ", cachedType='"
|
||||||
|
+ cachedType
|
||||||
|
+ '\''
|
||||||
|
+ ", callInfo="
|
||||||
|
+ callInfo
|
||||||
|
+ ", cachedCallInfo="
|
||||||
|
+ cachedCallInfo
|
||||||
|
+ ", profilingInfo="
|
||||||
|
+ profilingInfo
|
||||||
|
+ ", wasCached="
|
||||||
|
+ wasCached
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the id of the expression computed. */
|
||||||
|
public UUID getExpressionId() {
|
||||||
|
return expressionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the type of the returned value. */
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the cached type of the value. */
|
||||||
|
public String getCachedType() {
|
||||||
|
return cachedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the computed value of the expression. */
|
||||||
|
public Object getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the function call data. */
|
||||||
|
public FunctionCallInfo getCallInfo() {
|
||||||
|
return callInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the function call data previously associated with the expression. */
|
||||||
|
public FunctionCallInfo getCachedCallInfo() {
|
||||||
|
return cachedCallInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the profiling information associated with this expression */
|
||||||
|
public ProfilingInfo[] getProfilingInfo() {
|
||||||
|
return profilingInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return whether or not the expression result was obtained from the cache */
|
||||||
|
public boolean wasCached() {
|
||||||
|
return wasCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {@code true} when the type differs from the cached value. */
|
||||||
|
public boolean isTypeChanged() {
|
||||||
|
return !Objects.equals(type, cachedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {@code true} when the function call differs from the cached value. */
|
||||||
|
public boolean isFunctionCallChanged() {
|
||||||
|
return !Objects.equals(callInfo, cachedCallInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Information about the function call. */
|
||||||
|
public static class FunctionCallInfo {
|
||||||
|
|
||||||
|
private final QualifiedName moduleName;
|
||||||
|
private final QualifiedName typeName;
|
||||||
|
private final String functionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of this class.
|
||||||
|
*
|
||||||
|
* @param call the function call.
|
||||||
|
*/
|
||||||
|
public FunctionCallInfo(FunctionCallInstrumentationNode.FunctionCall call) {
|
||||||
|
RootNode rootNode = call.getFunction().getCallTarget().getRootNode();
|
||||||
|
if (rootNode instanceof MethodRootNode) {
|
||||||
|
MethodRootNode methodNode = (MethodRootNode) rootNode;
|
||||||
|
moduleName = methodNode.getModuleScope().getModule().getName();
|
||||||
|
typeName = methodNode.getAtomConstructor().getQualifiedName();
|
||||||
|
functionName = methodNode.getMethodName();
|
||||||
|
} else if (rootNode instanceof EnsoRootNode) {
|
||||||
|
moduleName = ((EnsoRootNode) rootNode).getModuleScope().getModule().getName();
|
||||||
|
typeName = null;
|
||||||
|
functionName = rootNode.getName();
|
||||||
|
} else {
|
||||||
|
moduleName = null;
|
||||||
|
typeName = null;
|
||||||
|
functionName = rootNode.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FunctionCallInfo that = (FunctionCallInfo) o;
|
||||||
|
return Objects.equals(moduleName, that.moduleName)
|
||||||
|
&& Objects.equals(typeName, that.typeName)
|
||||||
|
&& Objects.equals(functionName, that.functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(moduleName, typeName, functionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return moduleName + "::" + typeName + "::" + functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the name of the module this function was defined in, or null if not available. */
|
||||||
|
public QualifiedName getModuleName() {
|
||||||
|
return moduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the name of the type this method was defined for, or null if not a method. */
|
||||||
|
public QualifiedName getTypeName() {
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the name of this function. */
|
||||||
|
public String getFunctionName() {
|
||||||
|
return functionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package org.enso.interpreter.instrument;
|
||||||
|
|
||||||
|
import scala.util.Either;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface ReplExecutionEventNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all the bindings available in the current execution scope.
|
||||||
|
*
|
||||||
|
* @return a map, where keys are variable names and values are current values of variables.
|
||||||
|
*/
|
||||||
|
Map<String, Object> listBindings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates an arbitrary expression in the current execution context.
|
||||||
|
*
|
||||||
|
* @param expression the expression to evaluate
|
||||||
|
* @return the result of evaluating the expression or an exception that caused failure
|
||||||
|
*/
|
||||||
|
Either<Exception, Object> evaluate(String expression);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the String representation of the provided object as defined by Enso {@code to_text}
|
||||||
|
* operation.
|
||||||
|
*
|
||||||
|
* @param object the object to show
|
||||||
|
* @return String representation of the provided object or a failure if it cannot be inferred
|
||||||
|
*/
|
||||||
|
Either<Exception, String> showObject(Object object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminates this REPL session.
|
||||||
|
*
|
||||||
|
* <p>The last result of {@link #evaluate(String)} (or {@link
|
||||||
|
* org.enso.interpreter.runtime.builtin.Builtins#nothing()} if {@link #evaluate(String)} was not
|
||||||
|
* called before) will be returned from the instrumented node.
|
||||||
|
*
|
||||||
|
* <p>This function must always be called at the end of REPL session, as otherwise the program
|
||||||
|
* will never resume. It's forbidden to use this object after exit has been called.
|
||||||
|
*/
|
||||||
|
void exit();
|
||||||
|
}
|
@ -11,7 +11,7 @@ public final class RuntimeCache {
|
|||||||
|
|
||||||
private final Map<UUID, SoftReference<Object>> cache = new HashMap<>();
|
private final Map<UUID, SoftReference<Object>> cache = new HashMap<>();
|
||||||
private final Map<UUID, String> types = new HashMap<>();
|
private final Map<UUID, String> types = new HashMap<>();
|
||||||
private final Map<UUID, IdExecutionInstrument.FunctionCallInfo> calls = new HashMap<>();
|
private final Map<UUID, IdExecutionService.FunctionCallInfo> calls = new HashMap<>();
|
||||||
private Map<UUID, Double> weights = new HashMap<>();
|
private Map<UUID, Double> weights = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,8 +73,8 @@ public final class RuntimeCache {
|
|||||||
* @param call the function call.
|
* @param call the function call.
|
||||||
* @return the function call that was previously associated with this expression.
|
* @return the function call that was previously associated with this expression.
|
||||||
*/
|
*/
|
||||||
public IdExecutionInstrument.FunctionCallInfo putCall(
|
public IdExecutionService.FunctionCallInfo putCall(
|
||||||
UUID key, IdExecutionInstrument.FunctionCallInfo call) {
|
UUID key, IdExecutionService.FunctionCallInfo call) {
|
||||||
if (call == null) {
|
if (call == null) {
|
||||||
return calls.remove(key);
|
return calls.remove(key);
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ public final class RuntimeCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return the cached function call associated with the expression. */
|
/** @return the cached function call associated with the expression. */
|
||||||
public IdExecutionInstrument.FunctionCallInfo getCall(UUID key) {
|
public IdExecutionService.FunctionCallInfo getCall(UUID key) {
|
||||||
return calls.get(key);
|
return calls.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** A primitve boxed array type for use in the runtime. */
|
/** A primitive boxed array type for use in the runtime. */
|
||||||
@ExportLibrary(InteropLibrary.class)
|
@ExportLibrary(InteropLibrary.class)
|
||||||
@ExportLibrary(MethodDispatchLibrary.class)
|
@ExportLibrary(MethodDispatchLibrary.class)
|
||||||
@Builtin(pkg = "mutable", stdlibName = "Standard.Base.Data.Array.Array")
|
@Builtin(pkg = "mutable", stdlibName = "Standard.Base.Data.Array.Array")
|
||||||
|
@ -18,7 +18,7 @@ import java.util.UUID;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.enso.compiler.context.ChangesetBuilder;
|
import org.enso.compiler.context.ChangesetBuilder;
|
||||||
import org.enso.interpreter.instrument.Endpoint;
|
import org.enso.interpreter.instrument.Endpoint;
|
||||||
import org.enso.interpreter.instrument.IdExecutionInstrument;
|
import org.enso.interpreter.instrument.IdExecutionService;
|
||||||
import org.enso.interpreter.instrument.MethodCallsCache;
|
import org.enso.interpreter.instrument.MethodCallsCache;
|
||||||
import org.enso.interpreter.instrument.NotificationHandler;
|
import org.enso.interpreter.instrument.NotificationHandler;
|
||||||
import org.enso.interpreter.instrument.RuntimeCache;
|
import org.enso.interpreter.instrument.RuntimeCache;
|
||||||
@ -54,7 +54,7 @@ import org.enso.text.editing.model;
|
|||||||
*/
|
*/
|
||||||
public class ExecutionService {
|
public class ExecutionService {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final IdExecutionInstrument idExecutionInstrument;
|
private final Optional<IdExecutionService> idExecutionInstrument;
|
||||||
private final NotificationHandler.Forwarder notificationForwarder;
|
private final NotificationHandler.Forwarder notificationForwarder;
|
||||||
private final InteropLibrary interopLibrary = InteropLibrary.getFactory().getUncached();
|
private final InteropLibrary interopLibrary = InteropLibrary.getFactory().getUncached();
|
||||||
private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID);
|
private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID);
|
||||||
@ -64,15 +64,15 @@ public class ExecutionService {
|
|||||||
* Creates a new instance of this service.
|
* Creates a new instance of this service.
|
||||||
*
|
*
|
||||||
* @param context the language context to use.
|
* @param context the language context to use.
|
||||||
* @param idExecutionInstrument an instance of the {@link IdExecutionInstrument} to use in the
|
* @param idExecutionInstrument optional instance of the {@link IdExecutionService} to use in the
|
||||||
* course of executions.
|
* course of executions
|
||||||
* @param notificationForwarder a forwarder of notifications, used to communicate with the user
|
* @param notificationForwarder a forwarder of notifications, used to communicate with the user
|
||||||
* @param connectedLockManager a connected lock manager (if it is in use) that should be connected
|
* @param connectedLockManager a connected lock manager (if it is in use) that should be connected
|
||||||
* to the language server, or null
|
* to the language server, or null
|
||||||
*/
|
*/
|
||||||
public ExecutionService(
|
public ExecutionService(
|
||||||
Context context,
|
Context context,
|
||||||
IdExecutionInstrument idExecutionInstrument,
|
Optional<IdExecutionService> idExecutionInstrument,
|
||||||
NotificationHandler.Forwarder notificationForwarder,
|
NotificationHandler.Forwarder notificationForwarder,
|
||||||
ConnectedLockManager connectedLockManager) {
|
ConnectedLockManager connectedLockManager) {
|
||||||
this.idExecutionInstrument = idExecutionInstrument;
|
this.idExecutionInstrument = idExecutionInstrument;
|
||||||
@ -142,9 +142,9 @@ public class ExecutionService {
|
|||||||
MethodCallsCache methodCallsCache,
|
MethodCallsCache methodCallsCache,
|
||||||
UpdatesSynchronizationState syncState,
|
UpdatesSynchronizationState syncState,
|
||||||
UUID nextExecutionItem,
|
UUID nextExecutionItem,
|
||||||
Consumer<IdExecutionInstrument.ExpressionCall> funCallCallback,
|
Consumer<IdExecutionService.ExpressionCall> funCallCallback,
|
||||||
Consumer<IdExecutionInstrument.ExpressionValue> onComputedCallback,
|
Consumer<IdExecutionService.ExpressionValue> onComputedCallback,
|
||||||
Consumer<IdExecutionInstrument.ExpressionValue> onCachedCallback,
|
Consumer<IdExecutionService.ExpressionValue> onCachedCallback,
|
||||||
Consumer<Exception> onExceptionalCallback)
|
Consumer<Exception> onExceptionalCallback)
|
||||||
throws ArityException, SourceNotFoundException, UnsupportedMessageException,
|
throws ArityException, SourceNotFoundException, UnsupportedMessageException,
|
||||||
UnsupportedTypeException {
|
UnsupportedTypeException {
|
||||||
@ -154,8 +154,10 @@ public class ExecutionService {
|
|||||||
}
|
}
|
||||||
LocationFilter locationFilter = LocationFilter.create(module.getIr(), src);
|
LocationFilter locationFilter = LocationFilter.create(module.getIr(), src);
|
||||||
|
|
||||||
EventBinding<ExecutionEventListener> listener =
|
Optional<EventBinding<ExecutionEventListener>> listener =
|
||||||
idExecutionInstrument.bind(
|
idExecutionInstrument.map(
|
||||||
|
service ->
|
||||||
|
service.bind(
|
||||||
call.getFunction().getCallTarget(),
|
call.getFunction().getCallTarget(),
|
||||||
locationFilter,
|
locationFilter,
|
||||||
cache,
|
cache,
|
||||||
@ -165,13 +167,13 @@ public class ExecutionService {
|
|||||||
funCallCallback,
|
funCallCallback,
|
||||||
onComputedCallback,
|
onComputedCallback,
|
||||||
onCachedCallback,
|
onCachedCallback,
|
||||||
onExceptionalCallback);
|
onExceptionalCallback));
|
||||||
Object p = context.getThreadManager().enter();
|
Object p = context.getThreadManager().enter();
|
||||||
try {
|
try {
|
||||||
interopLibrary.execute(call);
|
interopLibrary.execute(call);
|
||||||
} finally {
|
} finally {
|
||||||
context.getThreadManager().leave(p);
|
context.getThreadManager().leave(p);
|
||||||
listener.dispose();
|
listener.ifPresent(binding -> binding.dispose());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,9 +201,9 @@ public class ExecutionService {
|
|||||||
MethodCallsCache methodCallsCache,
|
MethodCallsCache methodCallsCache,
|
||||||
UpdatesSynchronizationState syncState,
|
UpdatesSynchronizationState syncState,
|
||||||
UUID nextExecutionItem,
|
UUID nextExecutionItem,
|
||||||
Consumer<IdExecutionInstrument.ExpressionCall> funCallCallback,
|
Consumer<IdExecutionService.ExpressionCall> funCallCallback,
|
||||||
Consumer<IdExecutionInstrument.ExpressionValue> onComputedCallback,
|
Consumer<IdExecutionService.ExpressionValue> onComputedCallback,
|
||||||
Consumer<IdExecutionInstrument.ExpressionValue> onCachedCallback,
|
Consumer<IdExecutionService.ExpressionValue> onCachedCallback,
|
||||||
Consumer<Exception> onExceptionalCallback)
|
Consumer<Exception> onExceptionalCallback)
|
||||||
throws ArityException, ConstructorNotFoundException, MethodNotFoundException,
|
throws ArityException, ConstructorNotFoundException, MethodNotFoundException,
|
||||||
ModuleNotFoundException, UnsupportedMessageException, UnsupportedTypeException {
|
ModuleNotFoundException, UnsupportedMessageException, UnsupportedTypeException {
|
||||||
|
@ -3,7 +3,6 @@ package org.enso.interpreter.instrument
|
|||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import com.oracle.truffle.api.TruffleStackTrace
|
import com.oracle.truffle.api.TruffleStackTrace
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.interpreter.instrument.ReplDebuggerInstrument.ReplExecutionEventNode
|
|
||||||
import org.enso.polyglot.debugger._
|
import org.enso.polyglot.debugger._
|
||||||
import org.graalvm.polyglot.io.MessageEndpoint
|
import org.graalvm.polyglot.io.MessageEndpoint
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import java.util.UUID
|
|||||||
|
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import com.oracle.truffle.api.exception.AbstractTruffleException
|
import com.oracle.truffle.api.exception.AbstractTruffleException
|
||||||
import org.enso.interpreter.instrument.IdExecutionInstrument.{
|
import org.enso.interpreter.instrument.IdExecutionService.{
|
||||||
ExpressionCall,
|
ExpressionCall,
|
||||||
ExpressionValue
|
ExpressionValue
|
||||||
}
|
}
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
import sbt.Keys._
|
|
||||||
import sbt._
|
|
||||||
|
|
||||||
object FixInstrumentsGeneration {
|
|
||||||
|
|
||||||
/** This task detects any changes in source files of Instruments and forces
|
|
||||||
* recompilation of all instruments on any change. This is to ensure that the
|
|
||||||
* Annotation Processor registers all of the instruments.
|
|
||||||
*
|
|
||||||
* Without that fix, incremental compilation would not register unchanged
|
|
||||||
* instruments, leading to runtime errors.
|
|
||||||
*
|
|
||||||
* It should be added as a dependency of Compile / compile / compileInputs.
|
|
||||||
*/
|
|
||||||
lazy val preCompileTask = Def.task {
|
|
||||||
val log = streams.value.log
|
|
||||||
val root = baseDirectory.value
|
|
||||||
val classFilesDirectory = (Compile / classDirectory).value
|
|
||||||
val FragileFiles(fragileSources, fragileClassFiles) =
|
|
||||||
getFragileFiles(root, classFilesDirectory)
|
|
||||||
|
|
||||||
val fragileSourcesStore =
|
|
||||||
streams.value.cacheStoreFactory.make("instruments_fixer")
|
|
||||||
|
|
||||||
Tracked.diffInputs(fragileSourcesStore, FileInfo.hash)(
|
|
||||||
fragileSources.toSet
|
|
||||||
) { sourcesDiff: ChangeReport[File] =>
|
|
||||||
if (sourcesDiff.modified.nonEmpty && sourcesDiff.unmodified.nonEmpty) {
|
|
||||||
val others =
|
|
||||||
if (sourcesDiff.modified.size >= 2)
|
|
||||||
s" and ${sourcesDiff.modified.size - 1} others"
|
|
||||||
else ""
|
|
||||||
val firstInstrument = sourcesDiff.modified.head
|
|
||||||
val sourcesMessage = firstInstrument.toString + others
|
|
||||||
log.warn(
|
|
||||||
s"Instruments sources ($sourcesMessage) have been changed.\n" +
|
|
||||||
s"Forcing recompilation of all instruments to maintain " +
|
|
||||||
s"consistency of generated services files."
|
|
||||||
)
|
|
||||||
|
|
||||||
fragileClassFiles.foreach(_.delete())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This task detects if just a subset of the Instruments has been recompiled
|
|
||||||
* (right now we did not find a way of detecting this before compilation). If
|
|
||||||
* the Instrumentation state is detected to be inconsistent, current
|
|
||||||
* compilation is aborted and classfiles are deleted to ensure that when
|
|
||||||
* re-run Instrumentation will be brought back to a consistent state.
|
|
||||||
*
|
|
||||||
* Without that fix, incremental compilation would not register unchanged
|
|
||||||
* instruments, leading to runtime errors.
|
|
||||||
*
|
|
||||||
* It should replace the default `Compile / compile` task in a project.
|
|
||||||
*/
|
|
||||||
lazy val patchedCompile = Def.task {
|
|
||||||
val compilationResult = (Compile / compile).value
|
|
||||||
|
|
||||||
val log = streams.value.log
|
|
||||||
val root = baseDirectory.value
|
|
||||||
val classFilesDirectory = (Compile / classDirectory).value
|
|
||||||
val FragileFiles(_, fragileClassFiles) =
|
|
||||||
getFragileFiles(root, classFilesDirectory)
|
|
||||||
|
|
||||||
val fragileClassFilesStore =
|
|
||||||
streams.value.cacheStoreFactory.make("instruments_classfiles")
|
|
||||||
|
|
||||||
Tracked.diffInputs(fragileClassFilesStore, FileInfo.lastModified)(
|
|
||||||
fragileClassFiles.toSet
|
|
||||||
) { sourcesDiff: ChangeReport[File] =>
|
|
||||||
if (sourcesDiff.modified.nonEmpty && sourcesDiff.unmodified.nonEmpty) {
|
|
||||||
fragileClassFiles.foreach(_.delete())
|
|
||||||
|
|
||||||
val projectName = name.value
|
|
||||||
log.error(
|
|
||||||
"Truffle Instrumentation is not up to date, " +
|
|
||||||
"which will lead to runtime errors\n" +
|
|
||||||
"Fixes have been applied to ensure consistent Instrumentation state, " +
|
|
||||||
"but compilation has to be triggered again.\n" +
|
|
||||||
"Please re-run the previous command.\n" +
|
|
||||||
"(If this for some reason fails, " +
|
|
||||||
s"please do a clean build of the $projectName project)"
|
|
||||||
)
|
|
||||||
|
|
||||||
throw new RuntimeException("Please re-run last command")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compilationResult
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Deletes the compiled instrumentation class files, forcing all of them to
|
|
||||||
* be recompiled.
|
|
||||||
*
|
|
||||||
* Since all instruments are recompiled at once, the service state should be
|
|
||||||
* consistent as all of them will be re-registered.
|
|
||||||
*/
|
|
||||||
def cleanInstruments = Def.task {
|
|
||||||
val log = streams.value.log
|
|
||||||
val root = baseDirectory.value
|
|
||||||
val classFilesDirectory = (Compile / classDirectory).value
|
|
||||||
val FragileFiles(_, fragileClassFiles) =
|
|
||||||
getFragileFiles(root, classFilesDirectory)
|
|
||||||
fragileClassFiles.foreach { file =>
|
|
||||||
if (file.exists()) {
|
|
||||||
log.info(s"[clean-instruments] Removing $file")
|
|
||||||
file.delete()
|
|
||||||
} else {
|
|
||||||
log.info(s"[clean-instruments] $file was already missing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.info(
|
|
||||||
"All fragile class files have been deleted. The next compilation " +
|
|
||||||
"should be forced to recompile all of them, preserving instrumentation " +
|
|
||||||
"configuration consistency."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private case class FragileFiles(sources: Seq[File], classFiles: Seq[File])
|
|
||||||
|
|
||||||
private def getFragileFiles(
|
|
||||||
root: File,
|
|
||||||
classFilesDirectory: File
|
|
||||||
): FragileFiles = {
|
|
||||||
val fragileSources =
|
|
||||||
(file(s"$root/src/main/java/") ** "*Instrument.java").get ++
|
|
||||||
Seq(
|
|
||||||
file(s"$root/src/main/java/org/enso/interpreter/Language.java"),
|
|
||||||
file(s"$root/src/main/java/org/enso/interpreter/epb/EpbLanguage.java")
|
|
||||||
)
|
|
||||||
val fragileClassFiles =
|
|
||||||
(classFilesDirectory ** "*Instrument.class").get ++
|
|
||||||
Seq(
|
|
||||||
file(s"$classFilesDirectory/org/enso/interpreter/Language.class"),
|
|
||||||
file(s"$classFilesDirectory/org/enso/interpreter/epb/EpbLanguage.class")
|
|
||||||
)
|
|
||||||
FragileFiles(fragileSources, fragileClassFiles)
|
|
||||||
}
|
|
||||||
}
|
|
@ -146,6 +146,8 @@ object GatherLicenses {
|
|||||||
val currentInputHash =
|
val currentInputHash =
|
||||||
ReportState.computeInputHash(distributionDescription)
|
ReportState.computeInputHash(distributionDescription)
|
||||||
if (currentInputHash != reviewState.inputHash) {
|
if (currentInputHash != reviewState.inputHash) {
|
||||||
|
log.info("Input hash computed from build.sbt: " + currentInputHash)
|
||||||
|
log.info("Input hash stored in metadata: " + reviewState.inputHash)
|
||||||
warnAndThrow(
|
warnAndThrow(
|
||||||
s"Report for the $name is not up to date - " +
|
s"Report for the $name is not up to date - " +
|
||||||
s"it seems that some dependencies were added or removed."
|
s"it seems that some dependencies were added or removed."
|
||||||
@ -180,6 +182,8 @@ object GatherLicenses {
|
|||||||
|
|
||||||
val currentOutputHash = ReportState.computeOutputHash(packageDestination)
|
val currentOutputHash = ReportState.computeOutputHash(packageDestination)
|
||||||
if (currentOutputHash != reportState.outputHash) {
|
if (currentOutputHash != reportState.outputHash) {
|
||||||
|
log.info("Output hash computed from build.sbt: " + currentOutputHash)
|
||||||
|
log.info("Output hash stored in metadata: " + reportState.outputHash)
|
||||||
log.error(
|
log.error(
|
||||||
s"Generated package at $packageDestination seems to be not up-to-date."
|
s"Generated package at $packageDestination seems to be not up-to-date."
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package src.main.scala.licenses.frontend
|
package src.main.scala.licenses.frontend
|
||||||
|
|
||||||
|
import com.typesafe.sbt.license.DepModuleInfo
|
||||||
import src.main.scala.licenses.DependencyInformation
|
import src.main.scala.licenses.DependencyInformation
|
||||||
|
|
||||||
/** Filters out irrelevant dependencies.
|
/** Filters out irrelevant dependencies.
|
||||||
@ -13,5 +14,10 @@ object DependencyFilter {
|
|||||||
/** Decides if the dependency should be kept for further processing.
|
/** Decides if the dependency should be kept for further processing.
|
||||||
*/
|
*/
|
||||||
def shouldKeep(dependencyInformation: DependencyInformation): Boolean =
|
def shouldKeep(dependencyInformation: DependencyInformation): Boolean =
|
||||||
dependencyInformation.moduleInfo.organization != "org.enso"
|
shouldKeep(dependencyInformation.moduleInfo)
|
||||||
|
|
||||||
|
/** Decides if the module should be kept for further processing.
|
||||||
|
*/
|
||||||
|
def shouldKeep(moduleInfo: DepModuleInfo): Boolean =
|
||||||
|
moduleInfo.organization != "org.enso"
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,8 @@ package src.main.scala.licenses.report
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
import sbt.{File, IO, Logger}
|
import sbt.{File, IO, Logger}
|
||||||
import src.main.scala.licenses.{
|
import src.main.scala.licenses.frontend.DependencyFilter
|
||||||
DistributionDescription,
|
import src.main.scala.licenses.{DistributionDescription, FilesHelper, PortablePath}
|
||||||
FilesHelper,
|
|
||||||
PortablePath
|
|
||||||
}
|
|
||||||
|
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
@ -87,7 +84,7 @@ object ReportState {
|
|||||||
digest.update(sbtComponent.name.getBytes)
|
digest.update(sbtComponent.name.getBytes)
|
||||||
val dependencies =
|
val dependencies =
|
||||||
sbtComponent.licenseReport.licenses.sortBy(_.module.toString)
|
sbtComponent.licenseReport.licenses.sortBy(_.module.toString)
|
||||||
for (dep <- dependencies) {
|
for (dep <- dependencies.filter(d => DependencyFilter.shouldKeep(d.module))) {
|
||||||
digest.update(dep.module.toString.getBytes)
|
digest.update(dep.module.toString.getBytes)
|
||||||
digest.update(dep.license.name.getBytes)
|
digest.update(dep.license.name.getBytes)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
E955B4896E24F176DC2DE1E947D0D320B7B688CC43E88BA0AD8BF3B75473356E
|
F584041D9C65EE3A64897906C912A288C33868416BDA1C24C9892C3C06E597CD
|
||||||
741A2D55D2B3911B0426A0472585A2966D5B9E381B6938EE1209A68E6F76C5AB
|
741A2D55D2B3911B0426A0472585A2966D5B9E381B6938EE1209A68E6F76C5AB
|
||||||
0
|
0
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
DDB2BFB46423B9AC369876C9DDAA6EA0F64FBB0B68C847C8D632F51F16E7D01F
|
807B8CAF24F78001A73CDFC31DDFA2CB129CDEA07FFCF907ED994FEC7EC4003C
|
||||||
FCC80E706112594F80CDC45199FDC1B0A90EF165DC5B5AB9171C5C185EC2B9E2
|
FCC80E706112594F80CDC45199FDC1B0A90EF165DC5B5AB9171C5C185EC2B9E2
|
||||||
0
|
0
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
FE0E365572AF01D432BC54F8586C13CF6FB175F31E6700795D4FE4F944AE6482
|
E8BCE903881AE1141DA4E2CAAC72D673680955BDA2AF64B822C62A5AF46DBFE8
|
||||||
0758086E9F188E70B4D212BD957A2DC12AB1D51A56E68F042BC94919B3F2E163
|
0758086E9F188E70B4D212BD957A2DC12AB1D51A56E68F042BC94919B3F2E163
|
||||||
0
|
0
|
||||||
|
Loading…
Reference in New Issue
Block a user