From 9df6448d85276030b7b08c8d8ac2d22fef56f65b Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 3 Jan 2023 17:36:26 +0300 Subject: [PATCH] Add Polyglot Support in Runtime Tests (#4016) `runtime-with-instruments` project sets `-Dgraalvm.locatorDisabled=true` that disables the discovery of available polyglot languages (installed with `gu`). On the other hand, enabling locator makes polyglot languages available, but also makes the program classes and the test classes loaded with different classloaders. This way we're unable to use `EnsoContext` in tests to observe internal context state (there is an exception when you try to cast to `EnsoContext`). The solution is to move tests with enabled polyglot support, but disabled `EnsoContext` introspection to a separate project. --- build.sbt | 49 +++++++++++++++++++ .../org/enso/polyglot/RuntimeOptions.java | 20 ++++++-- .../instrument/IdExecutionInstrument.java | 15 ++---- .../test/instrument/BuiltinTypesTest.scala | 17 +------ .../instrument/RuntimeAsyncCommandsTest.scala | 16 +----- .../test/instrument/RuntimeErrorsTest.scala | 16 +----- .../instrument/RuntimeInstrumentTest.scala | 16 +----- .../test/instrument/RuntimeServerTest.scala | 13 +---- .../RuntimeVisualizationsTest.scala | 31 +++--------- .../org/enso/interpreter/EnsoLanguage.java | 7 ++- .../instrument/IdExecutionService.java | 9 +--- .../interpreter/service/ExecutionService.java | 16 ++++-- .../instrument/execution/Timer.scala | 7 ++- 13 files changed, 104 insertions(+), 128 deletions(-) rename engine/{runtime-with-instruments => runtime-with-polyglot}/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala (98%) diff --git a/build.sbt b/build.sbt index 8ac3889026..c9e17999b8 100644 --- a/build.sbt +++ b/build.sbt @@ -285,6 +285,7 @@ lazy val enso = (project in file(".")) `runtime-instrument-repl-debugger`, `runtime-instrument-runtime-server`, `runtime-with-instruments`, + `runtime-with-polyglot`, `runtime-version-manager`, `runtime-version-manager-test`, editions, @@ -1143,6 +1144,7 @@ lazy val `polyglot-api` = project }, libraryDependencies ++= Seq( "org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided", + "org.graalvm.truffle" % "truffle-api" % graalVersion % "provided", "com.google.flatbuffers" % "flatbuffers-java" % flatbuffersVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test @@ -1569,6 +1571,53 @@ lazy val `runtime-with-instruments` = .dependsOn(`runtime-instrument-repl-debugger`) .dependsOn(`runtime-instrument-runtime-server`) +/* runtime-with-polyglot + * ~~~~~~~~~~~~~~~~~~~~~ + * A project that allows loading polyglot languages by not disabling + * the GraalVM locator explicitly with `-Dgraalvm.locatorDisabled=true` like the + * `runtime-with-instruments` project does. It means that the test classes and + * runtime classes are loaded using different classloaders, and the test code + * cannot access the `EnsoContext`. + */ + +lazy val `runtime-with-polyglot` = + (project in file("engine/runtime-with-polyglot")) + .configs(Benchmark) + .settings( + frgaalJavaCompilerSetting, + inConfig(Compile)(truffleRunOptionsSettings), + inConfig(Benchmark)(Defaults.testSettings), + commands += WithDebugCommand.withDebug, + Benchmark / javacOptions --= Seq( + "-source", + frgaalSourceLevel, + "--enable-preview" + ), + Test / javaOptions ++= { + // Note [Classpath Separation] + val runtimeClasspath = + (LocalProject("runtime") / Compile / fullClasspath).value + val runtimeInstrumentsClasspath = + (LocalProject("runtime-with-instruments") / Compile / fullClasspath).value + val appendClasspath = + (runtimeClasspath ++ runtimeInstrumentsClasspath) + .map(_.data) + .mkString(File.pathSeparator) + Seq( + s"-Dtruffle.class.path.append=$appendClasspath" + ) + }, + Test / fork := true, + Test / envVars ++= distributionEnvironmentOverrides ++ Map( + "ENSO_TEST_DISABLE_IR_CACHE" -> "false" + ), + libraryDependencies ++= Seq( + "org.scalatest" %% "scalatest" % scalatestVersion % Test + ) + ) + .dependsOn(runtime % "compile->compile;test->test;runtime->runtime") + .dependsOn(`runtime-with-instruments`) + /* Note [Unmanaged Classpath] * ~~~~~~~~~~~~~~~~~~~~~~~~~~ * As the definition of the core primitives in `core_definition` is achieved diff --git a/engine/polyglot-api/src/main/java/org/enso/polyglot/RuntimeOptions.java b/engine/polyglot-api/src/main/java/org/enso/polyglot/RuntimeOptions.java index 2cca1f6866..a1f92bf4d2 100644 --- a/engine/polyglot-api/src/main/java/org/enso/polyglot/RuntimeOptions.java +++ b/engine/polyglot-api/src/main/java/org/enso/polyglot/RuntimeOptions.java @@ -1,10 +1,9 @@ package org.enso.polyglot; import java.util.Arrays; -import java.util.logging.Level; -import org.graalvm.options.OptionDescriptor; -import org.graalvm.options.OptionDescriptors; -import org.graalvm.options.OptionKey; + +import org.graalvm.options.*; +import com.oracle.truffle.api.Option; /** Class representing runtime options supported by the Enso engine. */ public class RuntimeOptions { @@ -91,6 +90,16 @@ public class RuntimeOptions { OptionDescriptor.newBuilder(USE_GLOBAL_IR_CACHE_LOCATION_KEY, USE_GLOBAL_IR_CACHE_LOCATION) .build(); + public static final String ENABLE_EXECUTION_TIMER = optionName("enableExecutionTimer"); + + @Option( + help = "Enables timer that counts down the execution time of expressions.", + category = OptionCategory.INTERNAL) + public static final OptionKey ENABLE_EXECUTION_TIMER_KEY = new OptionKey<>(true); + + private static final OptionDescriptor ENABLE_EXECUTION_TIMER_DESCRIPTOR = + OptionDescriptor.newBuilder(ENABLE_EXECUTION_TIMER_KEY, ENABLE_EXECUTION_TIMER).build(); + public static final OptionDescriptors OPTION_DESCRIPTORS = OptionDescriptors.create( Arrays.asList( @@ -107,7 +116,8 @@ public class RuntimeOptions { INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION_DESCRIPTOR, DISABLE_IR_CACHES_DESCRIPTOR, WAIT_FOR_PENDING_SERIALIZATION_JOBS_DESCRIPTOR, - USE_GLOBAL_IR_CACHE_LOCATION_DESCRIPTOR)); + USE_GLOBAL_IR_CACHE_LOCATION_DESCRIPTOR, + ENABLE_EXECUTION_TIMER_DESCRIPTOR)); /** * Canonicalizes the option name by prefixing it with the language name. diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java index fa601a0f18..57f954e6e6 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java @@ -37,7 +37,7 @@ import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag; id = IdExecutionService.INSTRUMENT_ID, services = IdExecutionService.class) public class IdExecutionInstrument extends TruffleInstrument implements IdExecutionService { - private Timer timer; + private Env env; /** @@ -48,20 +48,9 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut @Override protected void onCreate(Env env) { env.registerService(this); - this.timer = new Timer.Nanosecond(); this.env = env; } - /** - * Override the default nanosecond timer with the specified {@code timer}. - * - * @param timer the timer to override with - */ - @Override - public void overrideTimer(Timer timer) { - this.timer = timer; - } - /** The listener class used by this instrument. */ private static class IdExecutionEventListener implements ExecutionEventListener { private final CallTarget entryCallTarget; @@ -305,6 +294,7 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut * @param cache the precomputed expression values. * @param methodCallsCache the storage tracking the executed method calls. * @param syncState the synchronization state of runtime updates. + * @param timer the execution timer. * @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. @@ -319,6 +309,7 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut RuntimeCache cache, MethodCallsCache methodCallsCache, UpdatesSynchronizationState syncState, + Timer timer, UUID nextExecutionItem, Consumer functionCallCallback, Consumer onComputedCallback, diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/BuiltinTypesTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/BuiltinTypesTest.scala index 671ce77748..5fc81765bf 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/BuiltinTypesTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/BuiltinTypesTest.scala @@ -2,9 +2,7 @@ package org.enso.interpreter.test.instrument import org.enso.distribution.FileSystem import org.enso.distribution.locking.ThreadSafeFileLockManager -import org.enso.interpreter.instrument.execution.Timer import org.enso.interpreter.runtime.`type`.ConstantsGen -import org.enso.interpreter.runtime.EnsoContext import org.enso.interpreter.test.Metadata import org.enso.pkg.{Package, PackageManager} import org.enso.polyglot._ @@ -24,12 +22,6 @@ class BuiltinTypesTest with Matchers with BeforeAndAfterEach { - // === Test Timer =========================================================== - - class TestTimer extends Timer { - override def getTime(): Long = 0 - } - // === Test Utilities ======================================================= var context: TestContext = _ @@ -56,6 +48,7 @@ class BuiltinTypesTest .option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true") .option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false") + .option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false") .option( RuntimeOptions.DISABLE_IR_CACHES, InstrumentTestContext.DISABLE_IR_CACHE @@ -77,14 +70,6 @@ class BuiltinTypesTest ) executionContext.context.initialize(LanguageInfo.ID) - val languageContext = executionContext.context - .getBindings(LanguageInfo.ID) - .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) - .asHostObject[EnsoContext] - languageContext.getLanguage.getIdExecutionService.ifPresent( - _.overrideTimer(new TestTimer) - ); - def writeMain(contents: String): File = Files.write(pkg.mainFile.toPath, contents.getBytes).toFile diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala index 851bcbd4f4..17016c569b 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeAsyncCommandsTest.scala @@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument import org.enso.distribution.FileSystem import org.enso.distribution.locking.ThreadSafeFileLockManager -import org.enso.interpreter.instrument.execution.Timer import org.enso.interpreter.test.Metadata import org.enso.pkg.{Package, PackageManager} import org.enso.polyglot._ @@ -23,12 +22,6 @@ class RuntimeAsyncCommandsTest with Matchers with BeforeAndAfterEach { - // === Test Timer =========================================================== - - class TestTimer extends Timer { - override def getTime(): Long = 0 - } - // === Test Utilities ======================================================= var context: TestContext = _ @@ -56,6 +49,7 @@ class RuntimeAsyncCommandsTest ) .option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false") + .option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false") .option( RuntimeOptions.DISABLE_IR_CACHES, InstrumentTestContext.DISABLE_IR_CACHE @@ -76,14 +70,6 @@ class RuntimeAsyncCommandsTest ) executionContext.context.initialize(LanguageInfo.ID) - val languageContext = executionContext.context - .getBindings(LanguageInfo.ID) - .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) - .asHostObject[org.enso.interpreter.runtime.EnsoContext] - languageContext.getLanguage.getIdExecutionService.ifPresent( - _.overrideTimer(new TestTimer) - ) - def writeMain(contents: String): File = Files.write(pkg.mainFile.toPath, contents.getBytes).toFile diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala index 4f868f851a..524cf13fa3 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeErrorsTest.scala @@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument import org.enso.distribution.FileSystem import org.enso.distribution.locking.ThreadSafeFileLockManager -import org.enso.interpreter.instrument.execution.Timer import org.enso.interpreter.runtime.`type`.ConstantsGen import org.enso.interpreter.test.Metadata import org.enso.pkg.{Package, PackageManager} @@ -26,12 +25,6 @@ class RuntimeErrorsTest with Matchers with BeforeAndAfterEach { - // === Test Timer =========================================================== - - class TestTimer extends Timer { - override def getTime(): Long = 0 - } - // === Test Utilities ======================================================= var context: TestContext = _ @@ -61,6 +54,7 @@ class RuntimeErrorsTest .option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true") .option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false") + .option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false") .option( RuntimeOptions.DISABLE_IR_CACHES, InstrumentTestContext.DISABLE_IR_CACHE @@ -81,14 +75,6 @@ class RuntimeErrorsTest ) executionContext.context.initialize(LanguageInfo.ID) - val languageContext = executionContext.context - .getBindings(LanguageInfo.ID) - .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) - .asHostObject[org.enso.interpreter.runtime.EnsoContext] - languageContext.getLanguage.getIdExecutionService.ifPresent( - _.overrideTimer(new TestTimer) - ) - def writeMain(contents: String): File = Files.write(pkg.mainFile.toPath, contents.getBytes).toFile diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeInstrumentTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeInstrumentTest.scala index 6fb79abe8f..8cb35d60ca 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeInstrumentTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeInstrumentTest.scala @@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument import org.enso.distribution.FileSystem import org.enso.distribution.locking.ThreadSafeFileLockManager -import org.enso.interpreter.instrument.execution.Timer import org.enso.interpreter.runtime.`type`.{Constants, ConstantsGen} import org.enso.interpreter.test.Metadata import org.enso.pkg.{Package, PackageManager} @@ -24,12 +23,6 @@ class RuntimeInstrumentTest with Matchers with BeforeAndAfterEach { - // === Test Timer =========================================================== - - class TestTimer extends Timer { - override def getTime(): Long = 0 - } - // === Test Utilities ======================================================= var context: TestContext = _ @@ -54,6 +47,7 @@ class RuntimeInstrumentTest .option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true") .option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false") + .option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false") .option( RuntimeOptions.DISABLE_IR_CACHES, InstrumentTestContext.DISABLE_IR_CACHE @@ -74,14 +68,6 @@ class RuntimeInstrumentTest ) executionContext.context.initialize(LanguageInfo.ID) - val languageContext = executionContext.context - .getBindings(LanguageInfo.ID) - .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) - .asHostObject[org.enso.interpreter.runtime.EnsoContext] - languageContext.getLanguage.getIdExecutionService.ifPresent( - _.overrideTimer(new TestTimer) - ); - def writeMain(contents: String): File = Files.write(pkg.mainFile.toPath, contents.getBytes).toFile diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala index 0250815507..7d9d023d4e 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala @@ -2,7 +2,6 @@ package org.enso.interpreter.test.instrument import org.enso.distribution.FileSystem import org.enso.distribution.locking.ThreadSafeFileLockManager -import org.enso.interpreter.instrument.execution.Timer import org.enso.interpreter.runtime.`type`.{ConstantsGen, Types} import org.enso.interpreter.runtime.EnsoContext import org.enso.interpreter.test.Metadata @@ -27,12 +26,6 @@ class RuntimeServerTest with Matchers with BeforeAndAfterEach { - // === Test Timer =========================================================== - - class TestTimer extends Timer { - override def getTime(): Long = 0 - } - // === Test Utilities ======================================================= var context: TestContext = _ @@ -59,6 +52,7 @@ class RuntimeServerTest .option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true") .option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false") + .option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false") .option( RuntimeOptions.DISABLE_IR_CACHES, InstrumentTestContext.DISABLE_IR_CACHE @@ -84,11 +78,6 @@ class RuntimeServerTest .getBindings(LanguageInfo.ID) .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) .asHostObject[EnsoContext] - val info = - languageContext.getEnvironment.getPublicLanguages.get(LanguageInfo.ID) - languageContext.getLanguage.getIdExecutionService.ifPresent( - _.overrideTimer(new TestTimer) - ) def writeMain(contents: String): File = Files.write(pkg.mainFile.toPath, contents.getBytes).toFile diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala b/engine/runtime-with-polyglot/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala similarity index 98% rename from engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala rename to engine/runtime-with-polyglot/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala index bbe5e18bf1..7b3efa9e3a 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala +++ b/engine/runtime-with-polyglot/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala @@ -1,10 +1,8 @@ package org.enso.interpreter.test.instrument -import org.enso.distribution.FileSystem +import org.apache.commons.io.FileUtils import org.enso.distribution.locking.ThreadSafeFileLockManager -import org.enso.interpreter.instrument.execution.Timer import org.enso.interpreter.runtime.`type`.ConstantsGen -import org.enso.interpreter.runtime.EnsoContext import org.enso.interpreter.test.Metadata import org.enso.pkg.{Package, PackageManager, QualifiedName} import org.enso.polyglot._ @@ -27,12 +25,6 @@ class RuntimeVisualizationsTest with Matchers with BeforeAndAfterEach { - // === Test Timer =========================================================== - - class TestTimer extends Timer { - override def getTime(): Long = 0 - } - // === Test Utilities ======================================================= var context: TestContext = _ @@ -40,7 +32,7 @@ class RuntimeVisualizationsTest class TestContext(packageName: String) extends InstrumentTestContext { val tmpDir: Path = Files.createTempDirectory("enso-test-packages") - sys.addShutdownHook(FileSystem.removeDirectoryIfExists(tmpDir)) + sys.addShutdownHook(FileUtils.deleteQuietly(tmpDir.toFile)) val lockManager = new ThreadSafeFileLockManager(tmpDir.resolve("locks")) val runtimeServerEmulator = new RuntimeServerEmulator(messageQueue, lockManager) @@ -59,6 +51,7 @@ class RuntimeVisualizationsTest .option(RuntimeOptions.INTERPRETER_SEQUENTIAL_COMMAND_EXECUTION, "true") .option(RuntimeOptions.ENABLE_PROJECT_SUGGESTIONS, "false") .option(RuntimeOptions.ENABLE_GLOBAL_SUGGESTIONS, "false") + .option(RuntimeOptions.ENABLE_EXECUTION_TIMER, "false") .option(RuntimeServerInfo.ENABLE_OPTION, "true") .option(RuntimeOptions.INTERACTIVE_MODE, "true") .option( @@ -76,14 +69,6 @@ class RuntimeVisualizationsTest ) executionContext.context.initialize(LanguageInfo.ID) - val languageContext = executionContext.context - .getBindings(LanguageInfo.ID) - .invokeMember(MethodNames.TopScope.LEAK_CONTEXT) - .asHostObject[EnsoContext] - languageContext.getLanguage.getIdExecutionService.ifPresent( - _.overrideTimer(new TestTimer) - ) - def writeMain(contents: String): File = Files.write(pkg.mainFile.toPath, contents.getBytes).toFile @@ -2160,10 +2145,6 @@ class RuntimeVisualizationsTest } it should "run internal IDE visualisation preprocessor catching error" in { - pending - // TODO [JD]: Disabled due to issue with context not allowing JS functions. - // https://www.pivotaltracker.com/story/show/184064564 - val contextId = UUID.randomUUID() val requestId = UUID.randomUUID() val visualisationId = UUID.randomUUID() @@ -2208,8 +2189,8 @@ class RuntimeVisualizationsTest context.send( Api.Request(requestId, Api.PushContextRequest(contextId, item1)) ) - val pushContextResponses = context.receiveNIgnorePendingExpressionUpdates(3) - pushContextResponses should contain allOf ( + val pushContextResponses = context.receiveNIgnoreStdLib(3) + pushContextResponses should contain theSameElementsAs Seq( Api.Response(requestId, Api.PushContextResponse(contextId)), TestMessages.error( contextId, @@ -2261,7 +2242,7 @@ class RuntimeVisualizationsTest data } val stringified = new String(data) - stringified shouldEqual """{ "kind": "Dataflow", "message": "The List is empty. (at Main.main(Enso_Test.Test.Main:6:5-32)"}""" + stringified shouldEqual """{"kind":"Dataflow","message":"The List is empty. (at Main.main(Enso_Test.Test.Main:6:5-32)"}""" } it should "attach method pointer visualisation without arguments" in { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java index 285df5619d..058acdfe5c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/EnsoLanguage.java @@ -30,6 +30,7 @@ import org.enso.interpreter.epb.EpbLanguage; import org.enso.interpreter.instrument.IdExecutionService; import org.enso.interpreter.instrument.NotificationHandler.Forwarder; import org.enso.interpreter.instrument.NotificationHandler.TextMode$; +import org.enso.interpreter.instrument.execution.Timer; import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ProgramRootNode; @@ -130,6 +131,10 @@ public final class EnsoLanguage extends TruffleLanguage { lockManager = new ThreadSafeFileLockManager(distributionManager.paths().locks()); } + boolean isExecutionTimerEnabled = + env.getOptions().get(RuntimeOptions.ENABLE_EXECUTION_TIMER_KEY); + Timer timer = isExecutionTimerEnabled ? new Timer.Nanosecond() : new Timer.Disabled(); + EnsoContext context = new EnsoContext( this, getLanguageHome(), env, notificationHandler, lockManager, distributionManager); @@ -140,7 +145,7 @@ public final class EnsoLanguage extends TruffleLanguage { env.lookup(idValueListenerInstrument, IdExecutionService.class)); env.registerService( new ExecutionService( - context, idExecutionInstrument, notificationHandler, connectedLockManager)); + context, idExecutionInstrument, notificationHandler, connectedLockManager, timer)); return context; } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java b/engine/runtime/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java index e25841b8be..e8aa87f8da 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java @@ -28,6 +28,7 @@ public interface IdExecutionService { * @param cache the precomputed expression values. * @param methodCallsCache the storage tracking the executed method calls. * @param syncState the synchronization state of runtime updates. + * @param timer the execution timer. * @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. @@ -41,19 +42,13 @@ public interface IdExecutionService { RuntimeCache cache, MethodCallsCache methodCallsCache, UpdatesSynchronizationState syncState, + Timer timer, UUID nextExecutionItem, Consumer functionCallCallback, Consumer onComputedCallback, Consumer onCachedCallback, Consumer 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; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java index a0c6caaf54..c25bf56379 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java @@ -18,6 +18,7 @@ import org.enso.interpreter.instrument.MethodCallsCache; import org.enso.interpreter.instrument.NotificationHandler; import org.enso.interpreter.instrument.RuntimeCache; import org.enso.interpreter.instrument.UpdatesSynchronizationState; +import org.enso.interpreter.instrument.execution.Timer; import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNodeGen; import org.enso.interpreter.runtime.EnsoContext; @@ -58,25 +59,30 @@ public class ExecutionService { private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID); private final ConnectedLockManager connectedLockManager; + private final Timer timer; + /** * Creates a new instance of this service. * * @param context the language context to use. * @param idExecutionInstrument optional instance of the {@link IdExecutionService} to use in the - * course of executions - * @param notificationForwarder a forwarder of notifications, used to communicate with the user + * course of executions. + * @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 - * to the language server, or null + * to the language server, or null. + * @param timer an execution timer. */ public ExecutionService( EnsoContext context, Optional idExecutionInstrument, NotificationHandler.Forwarder notificationForwarder, - ConnectedLockManager connectedLockManager) { + ConnectedLockManager connectedLockManager, + Timer timer) { this.idExecutionInstrument = idExecutionInstrument; this.context = context; this.notificationForwarder = notificationForwarder; this.connectedLockManager = connectedLockManager; + this.timer = timer; } /** @return the language context. */ @@ -159,6 +165,7 @@ public class ExecutionService { cache, methodCallsCache, syncState, + this.timer, nextExecutionItem, funCallCallback, onComputedCallback, @@ -289,6 +296,7 @@ public class ExecutionService { cache, methodCallsCache, syncState, + this.timer, nextExecutionItem, funCallCallback, onComputedCallback, diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/Timer.scala b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/Timer.scala index 49fd983457..6134423858 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/Timer.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/execution/Timer.scala @@ -7,7 +7,12 @@ trait Timer { object Timer { /** A nanosecond precision timer. */ - class Nanosecond extends Timer { + final class Nanosecond extends Timer { override def getTime(): Long = System.nanoTime() } + + /** A timer returns the same zero time. */ + final class Disabled extends Timer { + override def getTime(): Long = 0 + } }