mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
Inline Execution (#8148)
close #8132 Update the `executionContext/executeExpression` request to execute expressions in the local scope.
This commit is contained in:
parent
876cbd7b5d
commit
565a858c5f
@ -3788,11 +3788,33 @@ interface ExecutionContextExecutionStatusNotification {
|
||||
|
||||
### `executionContext/executeExpression`
|
||||
|
||||
This message allows the client to execute an arbitrary expression on a given
|
||||
node. It behaves like oneshot
|
||||
This message allows the client to execute an arbitrary expression in a context
|
||||
of a given node. It behaves like putting a breakpoint after the expression with
|
||||
`expressionId` and executing the provided `expression`. All the local and global
|
||||
symbols that are available for the `expressionId` will be available when
|
||||
executing the `expression`. The result of the evaluation will be delivered as a
|
||||
visualization result on a binary connection. You can think of it as a oneshot
|
||||
[`executionContext/attachVisualization`](#executioncontextattachvisualization)
|
||||
visualization request, meaning that the visualization expression will be
|
||||
executed only once.
|
||||
visualization request, meaning that the expression will be executed once.
|
||||
|
||||
For example, given the current code:
|
||||
|
||||
```python
|
||||
main =
|
||||
operator1 = 42
|
||||
operator2 = operator1 + 1
|
||||
|
||||
fun1 x = x.to_text
|
||||
```
|
||||
|
||||
- You can execute an expression in the context of a function body. In this case,
|
||||
the `expressionId` should point to the body of a function. E.g. in the context
|
||||
of `main` available symbols are `operator1`, `operator2` and `fun1`.
|
||||
- Execute expression in the context of a local binding. E.g. in the context of
|
||||
`operator2 = operator1 + 1` available symbols are `operator1`, `operator2` and
|
||||
`fun1`.
|
||||
- Execute expression in the context of arbitrary expression. E.g. in the context
|
||||
of `operator1 + 1` available symbols are `operator1` and `fun1`.
|
||||
|
||||
- **Type:** Request
|
||||
- **Direction:** Client -> Server
|
||||
@ -3803,9 +3825,10 @@ executed only once.
|
||||
|
||||
```typescript
|
||||
interface ExecutionContextExecuteExpressionParameters {
|
||||
executionContextId: UUID;
|
||||
visualizationId: UUID;
|
||||
expressionId: UUID;
|
||||
visualizationConfig: VisualizationConfiguration;
|
||||
expression: string;
|
||||
}
|
||||
```
|
||||
|
||||
@ -3821,11 +3844,8 @@ type ExecutionContextExecuteExpressionResult = null;
|
||||
`executionContext/canModify` capability for this context.
|
||||
- [`ContextNotFoundError`](#contextnotfounderror) when context can not be found
|
||||
by provided id.
|
||||
- [`ModuleNotFoundError`](#modulenotfounderror) to signal that the module with
|
||||
the visualization cannot be found.
|
||||
- [`VisualizationExpressionError`](#visualizationexpressionerror) to signal that
|
||||
the expression specified in the `VisualizationConfiguration` cannot be
|
||||
evaluated.
|
||||
the provided expression cannot be evaluated.
|
||||
|
||||
### `executionContext/attachVisualization`
|
||||
|
||||
|
@ -40,9 +40,10 @@ class ExecuteExpressionHandler(
|
||||
) =>
|
||||
contextRegistry ! ContextRegistryProtocol.ExecuteExpression(
|
||||
clientId,
|
||||
params.executionContextId,
|
||||
params.visualizationId,
|
||||
params.expressionId,
|
||||
params.visualizationConfig
|
||||
params.expression
|
||||
)
|
||||
val cancellable =
|
||||
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||
|
@ -261,8 +261,13 @@ final class ContextRegistry(
|
||||
sender() ! AccessDenied
|
||||
}
|
||||
|
||||
case ExecuteExpression(clientId, visualizationId, expressionId, cfg) =>
|
||||
val contextId = cfg.executionContextId
|
||||
case ExecuteExpression(
|
||||
clientId,
|
||||
contextId,
|
||||
visualizationId,
|
||||
expressionId,
|
||||
expression
|
||||
) =>
|
||||
if (store.hasContext(clientId, contextId)) {
|
||||
store.getListener(contextId).foreach { listener =>
|
||||
listener ! RegisterOneshotVisualization(
|
||||
@ -272,17 +277,18 @@ final class ContextRegistry(
|
||||
)
|
||||
}
|
||||
val handler = context.actorOf(
|
||||
AttachVisualizationHandler.props(
|
||||
ExecuteExpressionHandler.props(
|
||||
runtimeFailureMapper,
|
||||
timeout,
|
||||
runtime
|
||||
)
|
||||
)
|
||||
handler.forward(
|
||||
Api.AttachVisualization(
|
||||
Api.ExecuteExpression(
|
||||
contextId,
|
||||
visualizationId,
|
||||
expressionId,
|
||||
cfg.toApi
|
||||
expression
|
||||
)
|
||||
)
|
||||
} else {
|
||||
|
@ -9,7 +9,7 @@ import org.enso.languageserver.filemanager.{FileSystemFailure, Path}
|
||||
import org.enso.languageserver.libraries.LibraryComponentGroup
|
||||
import org.enso.languageserver.runtime.ExecutionApi.ContextId
|
||||
import org.enso.languageserver.session.JsonSession
|
||||
import org.enso.logger.masking.ToLogString
|
||||
import org.enso.logger.masking.{MaskedString, ToLogString}
|
||||
import org.enso.text.editing.model
|
||||
|
||||
import java.util.UUID
|
||||
@ -422,14 +422,14 @@ object ContextRegistryProtocol {
|
||||
* @param clientId the requester id
|
||||
* @param visualizationId an identifier of a visualization
|
||||
* @param expressionId an identifier of an expression which is visualised
|
||||
* @param visualizationConfig a configuration object for properties of the
|
||||
* visualization
|
||||
* @param expression the expression to execute
|
||||
*/
|
||||
case class ExecuteExpression(
|
||||
clientId: ClientId,
|
||||
executionContextId: UUID,
|
||||
visualizationId: UUID,
|
||||
expressionId: UUID,
|
||||
visualizationConfig: VisualizationConfiguration
|
||||
expression: String
|
||||
) extends ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
@ -437,8 +437,8 @@ object ContextRegistryProtocol {
|
||||
"ExecuteExpression(" +
|
||||
s"clientId=$clientId," +
|
||||
s"visualizationId=$visualizationId," +
|
||||
s"expressionId=$expressionId,visualizationConfig=" +
|
||||
visualizationConfig.toLogString(shouldMask) +
|
||||
s"expressionId=$expressionId,expression=" +
|
||||
MaskedString(expression).toLogString(shouldMask) +
|
||||
")"
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,10 @@ object VisualizationApi {
|
||||
extends Method("executionContext/executeExpression") {
|
||||
|
||||
case class Params(
|
||||
executionContextId: UUID,
|
||||
visualizationId: UUID,
|
||||
expressionId: UUID,
|
||||
visualizationConfig: VisualizationConfiguration
|
||||
expression: String
|
||||
)
|
||||
|
||||
implicit val hasParams: HasParams.Aux[this.type, ExecuteExpression.Params] =
|
||||
|
@ -0,0 +1,81 @@
|
||||
package org.enso.languageserver.runtime.handler
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable, Props}
|
||||
import akka.pattern.pipe
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||
import org.enso.languageserver.runtime.{
|
||||
ContextRegistryProtocol,
|
||||
RuntimeFailureMapper
|
||||
}
|
||||
import org.enso.languageserver.util.UnhandledLogging
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
/** A request handler for execute expression commands.
|
||||
*
|
||||
* @param runtimeFailureMapper mapper for runtime failures
|
||||
* @param timeout request timeout
|
||||
* @param runtime reference to the runtime connector
|
||||
*/
|
||||
class ExecuteExpressionHandler(
|
||||
runtimeFailureMapper: RuntimeFailureMapper,
|
||||
timeout: FiniteDuration,
|
||||
runtime: ActorRef
|
||||
) extends Actor
|
||||
with LazyLogging
|
||||
with UnhandledLogging {
|
||||
|
||||
import context.dispatcher
|
||||
|
||||
override def receive: Receive = requestStage
|
||||
|
||||
private def requestStage: Receive = { case msg: Api.ExecuteExpression =>
|
||||
runtime ! Api.Request(UUID.randomUUID(), msg)
|
||||
val cancellable =
|
||||
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||
context.become(responseStage(sender(), cancellable))
|
||||
}
|
||||
|
||||
private def responseStage(
|
||||
replyTo: ActorRef,
|
||||
cancellable: Cancellable
|
||||
): Receive = {
|
||||
case RequestTimeout =>
|
||||
replyTo ! RequestTimeout
|
||||
context.stop(self)
|
||||
|
||||
case Api.Response(_, Api.VisualizationAttached()) =>
|
||||
replyTo ! ContextRegistryProtocol.VisualizationAttached
|
||||
cancellable.cancel()
|
||||
context.stop(self)
|
||||
|
||||
case Api.Response(_, error: Api.Error) =>
|
||||
runtimeFailureMapper.mapApiError(error).pipeTo(replyTo)
|
||||
cancellable.cancel()
|
||||
context.stop(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object ExecuteExpressionHandler {
|
||||
|
||||
/** Creates configuration object used to create a [[ExecuteExpressionHandler]].
|
||||
*
|
||||
* @param runtimeFailureMapper mapper for runtime failures
|
||||
* @param timeout request timeout
|
||||
* @param runtime reference to the runtime connector
|
||||
*/
|
||||
def props(
|
||||
runtimeFailureMapper: RuntimeFailureMapper,
|
||||
timeout: FiniteDuration,
|
||||
runtime: ActorRef
|
||||
): Props =
|
||||
Props(
|
||||
new ExecuteExpressionHandler(runtimeFailureMapper, timeout, runtime)
|
||||
)
|
||||
|
||||
}
|
@ -631,21 +631,21 @@ class ContextRegistryTest extends BaseServerTest {
|
||||
// attach visualization
|
||||
val visualizationId = UUID.randomUUID()
|
||||
val expressionId = UUID.randomUUID()
|
||||
val config =
|
||||
VisualizationConfiguration(contextId, "Test.Main", ".to_json.to_text")
|
||||
client.send(
|
||||
json.executionContextExecuteExpressionRequest(
|
||||
2,
|
||||
contextId,
|
||||
visualizationId,
|
||||
expressionId,
|
||||
config
|
||||
"expression"
|
||||
)
|
||||
)
|
||||
val requestId2 =
|
||||
runtimeConnectorProbe.receiveN(1).head match {
|
||||
case Api.Request(
|
||||
requestId,
|
||||
Api.AttachVisualization(
|
||||
Api.ExecuteExpression(
|
||||
`contextId`,
|
||||
`visualizationId`,
|
||||
`expressionId`,
|
||||
_
|
||||
@ -662,63 +662,6 @@ class ContextRegistryTest extends BaseServerTest {
|
||||
client.expectJson(json.ok(2))
|
||||
}
|
||||
|
||||
"return ModuleNotFound error when executing expression" in {
|
||||
val client = getInitialisedWsClient()
|
||||
|
||||
// create context
|
||||
client.send(json.executionContextCreateRequest(1))
|
||||
val (requestId, contextId) =
|
||||
runtimeConnectorProbe.receiveN(1).head match {
|
||||
case Api.Request(requestId, Api.CreateContextRequest(contextId)) =>
|
||||
(requestId, contextId)
|
||||
case msg =>
|
||||
fail(s"Unexpected message: $msg")
|
||||
}
|
||||
runtimeConnectorProbe.lastSender ! Api.Response(
|
||||
requestId,
|
||||
Api.CreateContextResponse(contextId)
|
||||
)
|
||||
client.expectJson(json.executionContextCreateResponse(1, contextId))
|
||||
|
||||
// attach visualization
|
||||
val visualizationId = UUID.randomUUID()
|
||||
val expressionId = UUID.randomUUID()
|
||||
val config =
|
||||
VisualizationConfiguration(contextId, "Test.Main", ".to_json.to_text")
|
||||
client.send(
|
||||
json.executionContextExecuteExpressionRequest(
|
||||
2,
|
||||
visualizationId,
|
||||
expressionId,
|
||||
config
|
||||
)
|
||||
)
|
||||
val requestId2 =
|
||||
runtimeConnectorProbe.receiveN(1).head match {
|
||||
case Api.Request(
|
||||
requestId,
|
||||
Api.AttachVisualization(
|
||||
`visualizationId`,
|
||||
`expressionId`,
|
||||
_
|
||||
)
|
||||
) =>
|
||||
requestId
|
||||
case msg =>
|
||||
fail(s"Unexpected message: $msg")
|
||||
}
|
||||
runtimeConnectorProbe.lastSender ! Api.Response(
|
||||
requestId2,
|
||||
Api.ModuleNotFound(config.visualizationModule)
|
||||
)
|
||||
client.expectJson(
|
||||
json.executionContextModuleNotFound(
|
||||
2,
|
||||
config.visualizationModule
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"successfully attach visualization" in {
|
||||
val client = getInitialisedWsClient()
|
||||
|
||||
|
@ -7,6 +7,8 @@ import org.enso.languageserver.runtime.{
|
||||
VisualizationExpression
|
||||
}
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
object ExecutionContextJsonMessages {
|
||||
|
||||
def localCall(expressionId: Api.ExpressionId) =
|
||||
@ -109,67 +111,23 @@ object ExecutionContextJsonMessages {
|
||||
|
||||
def executionContextExecuteExpressionRequest(
|
||||
reqId: Int,
|
||||
executionContextId: UUID,
|
||||
visualizationId: Api.VisualizationId,
|
||||
expressionId: Api.ExpressionId,
|
||||
configuration: VisualizationConfiguration
|
||||
expression: String
|
||||
) =
|
||||
configuration.expression match {
|
||||
case VisualizationExpression.Text(module, expression) =>
|
||||
json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "executionContext/executeExpression",
|
||||
"id": $reqId,
|
||||
"params": {
|
||||
"visualizationId": $visualizationId,
|
||||
"expressionId": $expressionId,
|
||||
"visualizationConfig": {
|
||||
"executionContextId": ${configuration.executionContextId},
|
||||
"visualizationModule": $module,
|
||||
"expression": $expression
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
case VisualizationExpression.ModuleMethod(methodPointer, Vector()) =>
|
||||
json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "executionContext/executeExpression",
|
||||
"id": $reqId,
|
||||
"params": {
|
||||
"visualizationId": $visualizationId,
|
||||
"expressionId": $expressionId,
|
||||
"visualizationConfig": {
|
||||
"executionContextId": ${configuration.executionContextId},
|
||||
"expression": {
|
||||
"module": ${methodPointer.module},
|
||||
"definedOnType": ${methodPointer.definedOnType},
|
||||
"name": ${methodPointer.name}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
case VisualizationExpression.ModuleMethod(methodPointer, arguments) =>
|
||||
json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "executionContext/executeExpression",
|
||||
"id": $reqId,
|
||||
"params": {
|
||||
"visualizationId": $visualizationId,
|
||||
"expressionId": $expressionId,
|
||||
"visualizationConfig": {
|
||||
"executionContextId": ${configuration.executionContextId},
|
||||
"expression": {
|
||||
"module": ${methodPointer.module},
|
||||
"definedOnType": ${methodPointer.definedOnType},
|
||||
"name": ${methodPointer.name}
|
||||
},
|
||||
"positionalArgumentsExpressions": $arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "executionContext/executeExpression",
|
||||
"id": $reqId,
|
||||
"params": {
|
||||
"executionContextId": $executionContextId,
|
||||
"visualizationId": $visualizationId,
|
||||
"expressionId": $expressionId,
|
||||
"expression": $expression
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
def executionContextAttachVisualizationRequest(
|
||||
reqId: Int,
|
||||
|
@ -0,0 +1,19 @@
|
||||
package org.enso.polyglot.debugger;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The result of executed oneshot visualization expression.
|
||||
*
|
||||
* @param result the execution result. {@code null} if the execution resulted in exception.
|
||||
* @param error the execution error. {@code null} if the execution was successful.
|
||||
* @param visualizationId the visualization id.
|
||||
* @param expressionId the id of expression that provides the execution scope.
|
||||
* @param expressionValue the value of the expression that provides the execution scope.
|
||||
*/
|
||||
public record ExecutedVisualization(
|
||||
Object result,
|
||||
Throwable error,
|
||||
UUID visualizationId,
|
||||
UUID expressionId,
|
||||
Object expressionValue) {}
|
@ -9,36 +9,60 @@ import java.util.UUID;
|
||||
public interface IdExecutionService {
|
||||
String INSTRUMENT_ID = "id-value-extractor";
|
||||
|
||||
public abstract class Info {
|
||||
|
||||
/** @return UUID of the node, never {@code null}. */
|
||||
public abstract UUID getId();
|
||||
|
||||
/** @return associated result or {@code null} if there is no associated result. */
|
||||
public abstract Object getResult();
|
||||
|
||||
/** @return {@code true} when the result is panic, {@code false} otherwise. */
|
||||
public abstract boolean isPanic();
|
||||
|
||||
/**
|
||||
* @return time (in nanoseconds) needed to compute the result or {@code -1} when not available.
|
||||
*/
|
||||
public abstract long getElapsedTime();
|
||||
|
||||
/**
|
||||
* Evaluates given code in the context of current UUID location.
|
||||
*
|
||||
* @param code the Enso code to evaluate.
|
||||
* @return result of the evaluation.
|
||||
*/
|
||||
public abstract Object eval(String code);
|
||||
}
|
||||
|
||||
public interface Callbacks {
|
||||
|
||||
/**
|
||||
* Finds out previously computed result for given id. If a result is returned, then the
|
||||
* execution of given node is skipped and the value is returned back.
|
||||
*
|
||||
* @param nodeId identification of the node to be computed
|
||||
* @param info info with UUID the node to be computed
|
||||
* @return {@code null} should the execution of the node be performed; any other value to skip
|
||||
* the execution and return the value as a result.
|
||||
*/
|
||||
Object findCachedResult(UUID nodeId);
|
||||
Object findCachedResult(Info info);
|
||||
|
||||
/**
|
||||
* Notifies when an execution of a node is over.
|
||||
*
|
||||
* @param nodeId identification of the node to be computed
|
||||
* @param result the just computed result
|
||||
* @param isPanic was the result a panic?
|
||||
* @param nanoElapsedTime how long it took to compute the result?
|
||||
* @param info info with node id, {@link Info#getResult()}, {@link Info#isPanic()} and {@link
|
||||
* Info#getElapsedTime()}
|
||||
*/
|
||||
void updateCachedResult(UUID nodeId, Object result, boolean isPanic, long nanoElapsedTime);
|
||||
void updateCachedResult(Info info);
|
||||
|
||||
/**
|
||||
* Notification when a returned value is a function.
|
||||
*
|
||||
* @param nodeId identification of the node to be computed
|
||||
* @param result info about function call
|
||||
* @param info with identification of the node and {@link Info#getResult()} info about function
|
||||
* call
|
||||
* @return {@code null} should the execution of the node be performed; any other value to skip
|
||||
* the execution and return the value as a result.
|
||||
*/
|
||||
Object onFunctionReturn(UUID nodeId, TruffleObject result);
|
||||
Object onFunctionReturn(Info info);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,6 +128,10 @@ object Runtime {
|
||||
value = classOf[Api.AttachVisualization],
|
||||
name = "attachVisualization"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Api.ExecuteExpression],
|
||||
name = "executeExpression"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Api.VisualizationAttached],
|
||||
name = "visualizationAttached"
|
||||
@ -1561,6 +1565,13 @@ object Runtime {
|
||||
*/
|
||||
final case class InitializedNotification() extends ApiResponse
|
||||
|
||||
final case class ExecuteExpression(
|
||||
contextId: ContextId,
|
||||
visualizationId: VisualizationId,
|
||||
expressionId: ExpressionId,
|
||||
expression: String
|
||||
) extends ApiRequest
|
||||
|
||||
/** A request sent from the client to the runtime server, to create a new
|
||||
* visualization for an expression identified by `expressionId`.
|
||||
*
|
||||
|
@ -67,7 +67,7 @@ class Compiler(
|
||||
private val importResolver: ImportResolver = new ImportResolver(this)
|
||||
private val irCachingEnabled = !context.isIrCachingDisabled
|
||||
private val useGlobalCacheLocations = context.isUseGlobalCacheLocations
|
||||
private val isInteractiveMode = context.isInteractiveMode()
|
||||
private val isInteractiveMode = context.isInteractiveMode
|
||||
private val output: PrintStream =
|
||||
if (config.outputRedirect.isDefined)
|
||||
new PrintStream(config.outputRedirect.get)
|
||||
@ -111,7 +111,7 @@ class Compiler(
|
||||
}
|
||||
|
||||
/** @return the package repository instance. */
|
||||
def getPackageRepository(): PackageRepository =
|
||||
def getPackageRepository: PackageRepository =
|
||||
context.getPackageRepository
|
||||
|
||||
/** Processes the provided language sources, registering any bindings in the
|
||||
@ -141,7 +141,7 @@ class Compiler(
|
||||
shouldCompileDependencies: Boolean,
|
||||
useGlobalCacheLocations: Boolean
|
||||
): Future[java.lang.Boolean] = {
|
||||
getPackageRepository().getMainProjectPackage match {
|
||||
getPackageRepository.getMainProjectPackage match {
|
||||
case None =>
|
||||
context.log(
|
||||
Level.SEVERE,
|
||||
@ -268,7 +268,7 @@ class Compiler(
|
||||
) {
|
||||
val importedModulesLoadedFromSource = importedModules
|
||||
.filter(isLoadedFromSource)
|
||||
.map(context.getModuleName(_))
|
||||
.map(context.getModuleName)
|
||||
context.log(
|
||||
Compiler.defaultLogLevel,
|
||||
"{0} imported module caches were invalided, forcing invalidation of {1}. [{2}]",
|
||||
@ -278,7 +278,7 @@ class Compiler(
|
||||
importedModulesLoadedFromSource.take(10).mkString("", ",", "...")
|
||||
)
|
||||
)
|
||||
context.updateModule(module, _.invalidateCache)
|
||||
context.updateModule(module, _.invalidateCache())
|
||||
parseModule(module)
|
||||
runImportsAndExportsResolution(module, generateCode)
|
||||
} else {
|
||||
@ -457,9 +457,9 @@ class Compiler(
|
||||
private def isModuleInRootPackage(module: Module): Boolean = {
|
||||
if (!context.isInteractive(module)) {
|
||||
val pkg = PackageRepositoryUtils
|
||||
.getPackageOf(getPackageRepository(), module.getSourceFile)
|
||||
.getPackageOf(getPackageRepository, module.getSourceFile)
|
||||
.toScala
|
||||
pkg.contains(getPackageRepository().getMainProjectPackage.get)
|
||||
pkg.contains(getPackageRepository.getMainProjectPackage.get)
|
||||
} else false
|
||||
}
|
||||
|
||||
@ -572,7 +572,7 @@ class Compiler(
|
||||
"Parsing module [{0}].",
|
||||
context.getModuleName(module)
|
||||
)
|
||||
context.updateModule(module, _.resetScope)
|
||||
context.updateModule(module, _.resetScope())
|
||||
|
||||
if (irCachingEnabled && !context.isInteractive(module)) {
|
||||
if (context.deserializeModule(this, module)) {
|
||||
@ -603,7 +603,7 @@ class Compiler(
|
||||
"Loading module [{0}] from source.",
|
||||
context.getModuleName(module)
|
||||
)
|
||||
context.updateModule(module, _.resetScope)
|
||||
context.updateModule(module, _.resetScope())
|
||||
|
||||
val moduleContext = ModuleContext(
|
||||
module = module,
|
||||
@ -694,10 +694,10 @@ class Compiler(
|
||||
.build()
|
||||
val tree = ensoCompiler.parse(source.getCharacters)
|
||||
|
||||
ensoCompiler.generateIRInline(tree).flatMap { ir =>
|
||||
ensoCompiler.generateIRInline(tree).map { ir =>
|
||||
val compilerOutput = runCompilerPhasesInline(ir, newContext)
|
||||
runErrorHandlingInline(compilerOutput, source, newContext)
|
||||
Some((newContext, compilerOutput, source))
|
||||
(newContext, compilerOutput, source)
|
||||
}
|
||||
}
|
||||
|
||||
@ -742,14 +742,6 @@ class Compiler(
|
||||
def parseInline(source: Source): Tree =
|
||||
ensoCompiler.parse(source.getCharacters())
|
||||
|
||||
/** Parses the metadata of the provided language sources.
|
||||
*
|
||||
* @param source the code to parse
|
||||
* @return the source metadata
|
||||
*/
|
||||
// def parseMeta(source: CharSequence): IDMap =
|
||||
// Parser().splitMeta(source.toString)._2
|
||||
|
||||
/** Enhances the provided IR with import/export statements for the provided list
|
||||
* of fully qualified names of modules. The statements are considered to be "synthetic" i.e. compiler-generated.
|
||||
* That way one can access modules using fully qualified names.
|
||||
@ -858,7 +850,7 @@ class Compiler(
|
||||
* for inline evaluation
|
||||
* @return the output result of the
|
||||
*/
|
||||
def runCompilerPhasesInline(
|
||||
private def runCompilerPhasesInline(
|
||||
ir: Expression,
|
||||
inlineContext: InlineContext
|
||||
): Expression = {
|
||||
@ -872,12 +864,12 @@ class Compiler(
|
||||
* @param source the original source code.
|
||||
* @param inlineContext the inline compilation context.
|
||||
*/
|
||||
def runErrorHandlingInline(
|
||||
private def runErrorHandlingInline(
|
||||
ir: Expression,
|
||||
source: Source,
|
||||
inlineContext: InlineContext
|
||||
): Unit =
|
||||
if (config.isStrictErrors) {
|
||||
if (inlineContext.compilerConfig.isStrictErrors) {
|
||||
val errors = GatherDiagnostics
|
||||
.runExpression(ir, inlineContext)
|
||||
.unsafeGetMetadata(
|
||||
@ -895,7 +887,7 @@ class Compiler(
|
||||
*
|
||||
* @param modules the modules to check against errors
|
||||
*/
|
||||
def runErrorHandling(
|
||||
private def runErrorHandling(
|
||||
modules: List[Module]
|
||||
): Unit = {
|
||||
if (config.isStrictErrors) {
|
||||
@ -921,7 +913,7 @@ class Compiler(
|
||||
* @param module the module for which to gather diagnostics
|
||||
* @return the diagnostics from the module
|
||||
*/
|
||||
def gatherDiagnostics(module: Module): List[Diagnostic] = {
|
||||
private def gatherDiagnostics(module: Module): List[Diagnostic] = {
|
||||
GatherDiagnostics
|
||||
.runModule(
|
||||
context.getIr(module),
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.enso.compiler.data
|
||||
|
||||
import org.enso.compiler.{PackageRepository}
|
||||
import org.enso.compiler.PackageRepository
|
||||
import org.enso.compiler.PackageRepository.ModuleMap
|
||||
import org.enso.compiler.context.CompilerContext.Module
|
||||
import org.enso.compiler.core.Implicits.AsMetadata
|
||||
@ -58,7 +58,7 @@ case class BindingsMap(
|
||||
override def restoreFromSerialization(
|
||||
compiler: Compiler
|
||||
): Option[BindingsMap] = {
|
||||
val packageRepository = compiler.getPackageRepository()
|
||||
val packageRepository = compiler.getPackageRepository
|
||||
this.toConcrete(packageRepository.getModuleMap)
|
||||
}
|
||||
|
||||
@ -1012,7 +1012,7 @@ object BindingsMap {
|
||||
override def restoreFromSerialization(
|
||||
compiler: Compiler
|
||||
): Option[Resolution] = {
|
||||
val moduleMap = compiler.getPackageRepository().getModuleMap
|
||||
val moduleMap = compiler.getPackageRepository.getModuleMap
|
||||
this.target.toConcrete(moduleMap).map(t => this.copy(target = t))
|
||||
}
|
||||
|
||||
|
@ -459,7 +459,7 @@ case object FullyQualifiedNames extends IRPass {
|
||||
override def restoreFromSerialization(
|
||||
compiler: CompilerContext
|
||||
): Option[PartiallyResolvedFQN] = {
|
||||
val packageRepository = compiler.getPackageRepository()
|
||||
val packageRepository = compiler.getPackageRepository
|
||||
moduleRef
|
||||
.toConcrete(packageRepository.getModuleMap)
|
||||
.map(ResolvedModule(_))
|
||||
|
@ -0,0 +1,50 @@
|
||||
package org.enso.interpreter.instrument.command;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.enso.interpreter.instrument.execution.RuntimeContext;
|
||||
import org.enso.interpreter.instrument.job.ExecuteExpressionJob;
|
||||
import org.enso.interpreter.instrument.job.ExecuteJob;
|
||||
import org.enso.polyglot.runtime.Runtime$Api$VisualizationAttached;
|
||||
import scala.Option;
|
||||
import scala.concurrent.ExecutionContext;
|
||||
import scala.concurrent.Future;
|
||||
import scala.runtime.BoxedUnit;
|
||||
|
||||
/** The command that handles the execute expression request. */
|
||||
public final class ExecuteExpressionCommand extends ContextCmd {
|
||||
|
||||
private final UUID contextId;
|
||||
private final UUID visualizationId;
|
||||
private final UUID expressionId;
|
||||
private final String expression;
|
||||
|
||||
/**
|
||||
* Create the {@link ExecuteExpressionCommand}.
|
||||
*
|
||||
* @param maybeRequestId the request id.
|
||||
* @param contextId the execution context id.
|
||||
* @param visualizationId the visualization id.
|
||||
* @param expressionId the expression providing the execution scope.
|
||||
* @param expression the expression to execute.
|
||||
*/
|
||||
public ExecuteExpressionCommand(
|
||||
Option<UUID> maybeRequestId,
|
||||
UUID contextId,
|
||||
UUID visualizationId,
|
||||
UUID expressionId,
|
||||
String expression) {
|
||||
super(contextId, maybeRequestId);
|
||||
this.contextId = contextId;
|
||||
this.visualizationId = visualizationId;
|
||||
this.expressionId = expressionId;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<BoxedUnit> executeCmd(RuntimeContext ctx, ExecutionContext ec) {
|
||||
reply(new Runtime$Api$VisualizationAttached(), ctx);
|
||||
return ctx.jobProcessor()
|
||||
.run(new ExecuteExpressionJob(contextId, visualizationId, expressionId, expression))
|
||||
.flatMap(executable -> ctx.jobProcessor().run(ExecuteJob.apply(executable)), ec);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.enso.interpreter.instrument.job;
|
||||
|
||||
import com.oracle.truffle.api.TruffleLogger;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import org.enso.interpreter.instrument.Visualization;
|
||||
import org.enso.interpreter.instrument.execution.Executable;
|
||||
import org.enso.interpreter.instrument.execution.RuntimeContext;
|
||||
import org.enso.interpreter.util.ScalaConversions;
|
||||
|
||||
/** The job that schedules the execution of the expression. */
|
||||
public class ExecuteExpressionJob extends Job<Executable> {
|
||||
|
||||
private final UUID contextId;
|
||||
private final UUID visualizationId;
|
||||
private final UUID expressionId;
|
||||
private final String expression;
|
||||
|
||||
/**
|
||||
* Create the {@link ExecuteExpressionJob}.
|
||||
*
|
||||
* @param contextId the execution context id.
|
||||
* @param visualizationId the visualization id.
|
||||
* @param expressionId the expression providing the execution scope.
|
||||
* @param expression the expression to execute.
|
||||
*/
|
||||
public ExecuteExpressionJob(
|
||||
UUID contextId, UUID visualizationId, UUID expressionId, String expression) {
|
||||
super(ScalaConversions.cons(contextId, ScalaConversions.nil()), false, false);
|
||||
this.contextId = contextId;
|
||||
this.visualizationId = visualizationId;
|
||||
this.expressionId = expressionId;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executable run(RuntimeContext ctx) {
|
||||
TruffleLogger logger = ctx.executionService().getLogger();
|
||||
long lockTimestamp = ctx.locking().acquireContextLock(contextId);
|
||||
|
||||
try {
|
||||
Visualization visualization =
|
||||
new Visualization.OneshotExpression(visualizationId, expressionId, contextId, expression);
|
||||
ctx.contextManager().upsertVisualization(contextId, visualization);
|
||||
|
||||
var stack = ctx.contextManager().getStack(contextId);
|
||||
return new Executable(contextId, stack);
|
||||
} finally {
|
||||
ctx.locking().releaseContextLock(contextId);
|
||||
logger.log(
|
||||
Level.FINEST,
|
||||
"Kept context lock [{0}] for {1} milliseconds.",
|
||||
new Object[] {
|
||||
this.getClass().getSimpleName(), System.currentTimeMillis() - lockTimestamp
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
package org.enso.interpreter.service;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.enso.polyglot.debugger.IdExecutionService;
|
||||
import org.enso.interpreter.instrument.MethodCallsCache;
|
||||
import org.enso.interpreter.instrument.RuntimeCache;
|
||||
import org.enso.interpreter.instrument.UpdatesSynchronizationState;
|
||||
import org.enso.interpreter.instrument.Visualization;
|
||||
import org.enso.interpreter.instrument.VisualizationHolder;
|
||||
import org.enso.interpreter.instrument.profiling.ExecutionTime;
|
||||
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
@ -19,12 +20,13 @@ import org.enso.interpreter.runtime.type.Constants;
|
||||
import org.enso.interpreter.service.ExecutionService.ExpressionCall;
|
||||
import org.enso.interpreter.service.ExecutionService.ExpressionValue;
|
||||
import org.enso.interpreter.service.ExecutionService.FunctionCallInfo;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import org.enso.polyglot.debugger.ExecutedVisualization;
|
||||
import org.enso.polyglot.debugger.IdExecutionService;
|
||||
import scala.collection.Iterator;
|
||||
|
||||
final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
||||
|
||||
private final VisualizationHolder visualizationHolder;
|
||||
private final UUID nextExecutionItem;
|
||||
private final RuntimeCache cache;
|
||||
private final MethodCallsCache methodCallsCache;
|
||||
@ -33,8 +35,10 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
||||
private final Consumer<ExpressionValue> onCachedCallback;
|
||||
private final Consumer<ExpressionValue> onComputedCallback;
|
||||
private final Consumer<ExpressionCall> functionCallCallback;
|
||||
private final Consumer<ExecutedVisualization> onExecutedVisualizationCallback;
|
||||
|
||||
/** Creates callbacks instance.
|
||||
/**
|
||||
* Creates callbacks instance.
|
||||
*
|
||||
* @param cache the precomputed expression values.
|
||||
* @param methodCallsCache the storage tracking the executed updateCachedResult calls.
|
||||
@ -45,11 +49,16 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
||||
* @param onCachedCallback the consumer of the cached value events.
|
||||
*/
|
||||
ExecutionCallbacks(
|
||||
UUID nextExecutionItem,
|
||||
RuntimeCache cache, MethodCallsCache methodCallsCache, UpdatesSynchronizationState syncState,
|
||||
Consumer<ExpressionValue> onCachedCallback, Consumer<ExpressionValue> onComputedCallback,
|
||||
Consumer<ExpressionCall> functionCallCallback
|
||||
) {
|
||||
VisualizationHolder visualizationHolder,
|
||||
UUID nextExecutionItem,
|
||||
RuntimeCache cache,
|
||||
MethodCallsCache methodCallsCache,
|
||||
UpdatesSynchronizationState syncState,
|
||||
Consumer<ExpressionValue> onCachedCallback,
|
||||
Consumer<ExpressionValue> onComputedCallback,
|
||||
Consumer<ExpressionCall> functionCallCallback,
|
||||
Consumer<ExecutedVisualization> onExecutedVisualizationCallback) {
|
||||
this.visualizationHolder = visualizationHolder;
|
||||
this.nextExecutionItem = nextExecutionItem;
|
||||
this.cache = cache;
|
||||
this.methodCallsCache = methodCallsCache;
|
||||
@ -57,46 +66,46 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
||||
this.onCachedCallback = onCachedCallback;
|
||||
this.onComputedCallback = onComputedCallback;
|
||||
this.functionCallCallback = functionCallCallback;
|
||||
this.onExecutedVisualizationCallback = onExecutedVisualizationCallback;
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public final Object findCachedResult(UUID nodeId) {
|
||||
// Add a flag to say it was cached.
|
||||
// An array of `ProfilingInfo` in the value update.
|
||||
Object result = cache.get(nodeId);
|
||||
@Override
|
||||
public Object findCachedResult(IdExecutionService.Info info) {
|
||||
UUID nodeId = info.getId();
|
||||
Object result = getCachedResult(nodeId);
|
||||
|
||||
if (result != null) {
|
||||
executeOneshotExpressions(nodeId, result, info);
|
||||
}
|
||||
|
||||
// When executing the call stack we need to capture the FunctionCall of the next (top) stack
|
||||
// item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be
|
||||
// able to continue the stack execution, and unwind later from the `onReturnValue` callback.
|
||||
if (result != null && !nodeId.equals(nextExecutionItem)) {
|
||||
var value = new ExpressionValue(
|
||||
nodeId,
|
||||
result,
|
||||
cache.getType(nodeId),
|
||||
typeOf(result),
|
||||
calls.get(nodeId),
|
||||
cache.getCall(nodeId),
|
||||
new ProfilingInfo[]{ExecutionTime.empty()},
|
||||
true
|
||||
);
|
||||
onCachedCallback.accept(value);
|
||||
callOnCachedCallback(nodeId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public final void updateCachedResult(UUID nodeId, Object result, boolean isPanic, long nanoTimeElapsed) {
|
||||
@Override
|
||||
public void updateCachedResult(IdExecutionService.Info info) {
|
||||
Object result = info.getResult();
|
||||
String resultType = typeOf(result);
|
||||
UUID nodeId = info.getId();
|
||||
String cachedType = cache.getType(nodeId);
|
||||
FunctionCallInfo call = functionCallInfoById(nodeId);
|
||||
FunctionCallInfo cachedCall = cache.getCall(nodeId);
|
||||
ProfilingInfo[] profilingInfo = new ProfilingInfo[]{new ExecutionTime(nanoTimeElapsed)};
|
||||
ProfilingInfo[] profilingInfo = new ProfilingInfo[] {new ExecutionTime(info.getElapsedTime())};
|
||||
|
||||
ExpressionValue expressionValue
|
||||
= new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false);
|
||||
ExpressionValue expressionValue =
|
||||
new ExpressionValue(
|
||||
nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false);
|
||||
syncState.setExpressionUnsync(nodeId);
|
||||
syncState.setVisualizationUnsync(nodeId);
|
||||
|
||||
boolean isPanic = info.isPanic();
|
||||
// Panics are not cached because a panic can be fixed by changing seemingly unrelated code,
|
||||
// like imports, and the invalidation mechanism can not always track those changes and
|
||||
// appropriately invalidate all dependent expressions.
|
||||
@ -106,7 +115,8 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
||||
}
|
||||
cache.putType(nodeId, resultType);
|
||||
|
||||
passExpressionValueToCallback(expressionValue);
|
||||
callOnComputedCallback(expressionValue);
|
||||
executeOneshotExpressions(nodeId, result, info);
|
||||
if (isPanic) {
|
||||
// We mark the node as executed so that it is not reported as not executed call after the
|
||||
// program execution is complete. If we clear the call from the cache instead, it will mess
|
||||
@ -116,8 +126,11 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public final Object onFunctionReturn(UUID nodeId, TruffleObject result) {
|
||||
var fnCall = (FunctionCallInstrumentationNode.FunctionCall) result;
|
||||
@Override
|
||||
public Object onFunctionReturn(IdExecutionService.Info info) {
|
||||
FunctionCallInstrumentationNode.FunctionCall fnCall =
|
||||
(FunctionCallInstrumentationNode.FunctionCall) info.getResult();
|
||||
UUID nodeId = info.getId();
|
||||
calls.put(nodeId, FunctionCallInfo.fromFunctionCall(fnCall));
|
||||
functionCallCallback.accept(new ExpressionCall(nodeId, fnCall));
|
||||
// Return cached value after capturing the enterable function call in `functionCallCallback`
|
||||
@ -130,10 +143,63 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks {
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private void passExpressionValueToCallback(ExpressionValue expressionValue) {
|
||||
private void callOnComputedCallback(ExpressionValue expressionValue) {
|
||||
onComputedCallback.accept(expressionValue);
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private void callOnCachedCallback(UUID nodeId, Object result) {
|
||||
ExpressionValue expressionValue =
|
||||
new ExpressionValue(
|
||||
nodeId,
|
||||
result,
|
||||
cache.getType(nodeId),
|
||||
typeOf(result),
|
||||
calls.get(nodeId),
|
||||
cache.getCall(nodeId),
|
||||
new ProfilingInfo[] {ExecutionTime.empty()},
|
||||
true);
|
||||
|
||||
onCachedCallback.accept(expressionValue);
|
||||
}
|
||||
|
||||
private void executeOneshotExpressions(UUID nodeId, Object result, IdExecutionService.Info info) {
|
||||
Iterator<Visualization> visualizations = findVisualizations(nodeId);
|
||||
while (visualizations.hasNext()) {
|
||||
Visualization visualization = visualizations.next();
|
||||
|
||||
if (visualization instanceof Visualization.OneshotExpression oneshotExpression) {
|
||||
Object visualizationResult = null;
|
||||
Throwable visualizationError = null;
|
||||
try {
|
||||
visualizationResult = info.eval(oneshotExpression.expression());
|
||||
} catch (Exception exception) {
|
||||
visualizationError = exception;
|
||||
}
|
||||
|
||||
ExecutedVisualization executedVisualization =
|
||||
new ExecutedVisualization(
|
||||
visualizationResult, visualizationError, visualization.id(), nodeId, result);
|
||||
callOnExecutedVisualizationCallback(executedVisualization);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private void callOnExecutedVisualizationCallback(ExecutedVisualization executedVisualization) {
|
||||
onExecutedVisualizationCallback.accept(executedVisualization);
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private Object getCachedResult(UUID nodeId) {
|
||||
return cache.get(nodeId);
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private Iterator<Visualization> findVisualizations(UUID nodeId) {
|
||||
return visualizationHolder.find(nodeId).iterator();
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private FunctionCallInfo functionCallInfoById(UUID nodeId) {
|
||||
return calls.get(nodeId);
|
||||
|
@ -1,62 +1,5 @@
|
||||
package org.enso.interpreter.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
|
||||
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 java.util.logging.Level;
|
||||
|
||||
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
|
||||
import org.enso.interpreter.node.MethodRootNode;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.data.Type;
|
||||
import org.enso.logger.masking.MaskedString;
|
||||
import org.enso.pkg.QualifiedName;
|
||||
|
||||
import org.enso.compiler.context.SimpleUpdate;
|
||||
import org.enso.interpreter.instrument.Endpoint;
|
||||
import org.enso.polyglot.debugger.IdExecutionService;
|
||||
import org.enso.interpreter.instrument.MethodCallsCache;
|
||||
import org.enso.interpreter.instrument.NotificationHandler;
|
||||
import org.enso.interpreter.instrument.RuntimeCache;
|
||||
import org.enso.interpreter.instrument.Timer;
|
||||
import org.enso.interpreter.instrument.UpdatesSynchronizationState;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNodeGen;
|
||||
import org.enso.interpreter.runtime.EnsoContext;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.Type;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
import org.enso.interpreter.runtime.state.State;
|
||||
import org.enso.interpreter.service.error.FailedToApplyEditsException;
|
||||
import org.enso.interpreter.service.error.MethodNotFoundException;
|
||||
import org.enso.interpreter.service.error.ModuleNotFoundException;
|
||||
import org.enso.interpreter.service.error.SourceNotFoundException;
|
||||
import org.enso.interpreter.service.error.TypeNotFoundException;
|
||||
import org.enso.lockmanager.client.ConnectedLockManager;
|
||||
import org.enso.polyglot.LanguageInfo;
|
||||
import org.enso.polyglot.MethodNames;
|
||||
import org.enso.text.editing.JavaEditorAdapter;
|
||||
import org.enso.text.editing.model;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleLogger;
|
||||
@ -72,6 +15,53 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.enso.compiler.context.SimpleUpdate;
|
||||
import org.enso.interpreter.instrument.Endpoint;
|
||||
import org.enso.interpreter.instrument.MethodCallsCache;
|
||||
import org.enso.interpreter.instrument.NotificationHandler;
|
||||
import org.enso.interpreter.instrument.RuntimeCache;
|
||||
import org.enso.interpreter.instrument.Timer;
|
||||
import org.enso.interpreter.instrument.UpdatesSynchronizationState;
|
||||
import org.enso.interpreter.instrument.VisualizationHolder;
|
||||
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
|
||||
import org.enso.interpreter.node.MethodRootNode;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNodeGen;
|
||||
import org.enso.interpreter.runtime.EnsoContext;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.data.Type;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
import org.enso.interpreter.runtime.state.State;
|
||||
import org.enso.interpreter.service.error.FailedToApplyEditsException;
|
||||
import org.enso.interpreter.service.error.MethodNotFoundException;
|
||||
import org.enso.interpreter.service.error.ModuleNotFoundException;
|
||||
import org.enso.interpreter.service.error.SourceNotFoundException;
|
||||
import org.enso.interpreter.service.error.TypeNotFoundException;
|
||||
import org.enso.lockmanager.client.ConnectedLockManager;
|
||||
import org.enso.logger.masking.MaskedString;
|
||||
import org.enso.pkg.QualifiedName;
|
||||
import org.enso.polyglot.LanguageInfo;
|
||||
import org.enso.polyglot.MethodNames;
|
||||
import org.enso.polyglot.debugger.ExecutedVisualization;
|
||||
import org.enso.polyglot.debugger.IdExecutionService;
|
||||
import org.enso.text.editing.JavaEditorAdapter;
|
||||
import org.enso.text.editing.model;
|
||||
|
||||
/**
|
||||
* A service allowing externally-triggered code execution, registered by an instance of the
|
||||
* language.
|
||||
@ -87,7 +77,6 @@ public final class ExecutionService {
|
||||
private final ExecuteRootNode execute = new ExecuteRootNode();
|
||||
private final CallRootNode call = new CallRootNode();
|
||||
private final InvokeMemberRootNode invoke = new InvokeMemberRootNode();
|
||||
|
||||
private final Timer timer;
|
||||
|
||||
/**
|
||||
@ -168,6 +157,7 @@ public final class ExecutionService {
|
||||
* @param onCachedCallback the consumer of the cached value events.
|
||||
*/
|
||||
public void execute(
|
||||
VisualizationHolder visualizationHolder,
|
||||
Module module,
|
||||
FunctionCallInstrumentationNode.FunctionCall call,
|
||||
RuntimeCache cache,
|
||||
@ -176,23 +166,32 @@ public final class ExecutionService {
|
||||
UUID nextExecutionItem,
|
||||
Consumer<ExecutionService.ExpressionCall> funCallCallback,
|
||||
Consumer<ExecutionService.ExpressionValue> onComputedCallback,
|
||||
Consumer<ExecutionService.ExpressionValue> onCachedCallback
|
||||
) throws ArityException, SourceNotFoundException, UnsupportedMessageException, UnsupportedTypeException {
|
||||
Consumer<ExecutionService.ExpressionValue> onCachedCallback,
|
||||
Consumer<ExecutedVisualization> onExecutedVisualizationCallback)
|
||||
throws ArityException,
|
||||
SourceNotFoundException,
|
||||
UnsupportedMessageException,
|
||||
UnsupportedTypeException {
|
||||
SourceSection src = call.getFunction().getSourceSection();
|
||||
if (src == null) {
|
||||
throw new SourceNotFoundException(call.getFunction().getName());
|
||||
}
|
||||
var callbacks = new ExecutionCallbacks(
|
||||
nextExecutionItem, cache, methodCallsCache, syncState,
|
||||
onCachedCallback, onComputedCallback, funCallCallback
|
||||
);
|
||||
var callbacks =
|
||||
new ExecutionCallbacks(
|
||||
visualizationHolder,
|
||||
nextExecutionItem,
|
||||
cache,
|
||||
methodCallsCache,
|
||||
syncState,
|
||||
onCachedCallback,
|
||||
onComputedCallback,
|
||||
funCallCallback,
|
||||
onExecutedVisualizationCallback);
|
||||
Optional<EventBinding<ExecutionEventNodeFactory>> eventNodeFactory =
|
||||
idExecutionInstrument.map(service -> service.bind(
|
||||
module,
|
||||
call.getFunction().getCallTarget(),
|
||||
callbacks,
|
||||
this.timer
|
||||
));
|
||||
idExecutionInstrument.map(
|
||||
service ->
|
||||
service.bind(
|
||||
module, call.getFunction().getCallTarget(), callbacks, this.timer));
|
||||
Object p = context.getThreadManager().enter();
|
||||
try {
|
||||
execute.getCallTarget().call(call);
|
||||
@ -221,22 +220,27 @@ public final class ExecutionService {
|
||||
String moduleName,
|
||||
String typeName,
|
||||
String methodName,
|
||||
VisualizationHolder visualizationHolder,
|
||||
RuntimeCache cache,
|
||||
MethodCallsCache methodCallsCache,
|
||||
UpdatesSynchronizationState syncState,
|
||||
UUID nextExecutionItem,
|
||||
Consumer<
|
||||
ExecutionService.ExpressionCall> funCallCallback,
|
||||
Consumer<ExecutionService.ExpressionCall> funCallCallback,
|
||||
Consumer<ExecutionService.ExpressionValue> onComputedCallback,
|
||||
Consumer<ExecutionService.ExpressionValue> onCachedCallback
|
||||
)
|
||||
throws ArityException, TypeNotFoundException, MethodNotFoundException,
|
||||
ModuleNotFoundException, UnsupportedMessageException, UnsupportedTypeException {
|
||||
Consumer<ExecutionService.ExpressionValue> onCachedCallback,
|
||||
Consumer<ExecutedVisualization> onExecutedVisualizationCallback)
|
||||
throws ArityException,
|
||||
TypeNotFoundException,
|
||||
MethodNotFoundException,
|
||||
ModuleNotFoundException,
|
||||
UnsupportedMessageException,
|
||||
UnsupportedTypeException {
|
||||
Module module =
|
||||
context.findModule(moduleName).orElseThrow(() -> new ModuleNotFoundException(moduleName));
|
||||
FunctionCallInstrumentationNode.FunctionCall call =
|
||||
prepareFunctionCall(module, typeName, methodName);
|
||||
execute(
|
||||
visualizationHolder,
|
||||
module,
|
||||
call,
|
||||
cache,
|
||||
@ -245,20 +249,18 @@ public final class ExecutionService {
|
||||
nextExecutionItem,
|
||||
funCallCallback,
|
||||
onComputedCallback,
|
||||
onCachedCallback
|
||||
);
|
||||
onCachedCallback,
|
||||
onExecutedVisualizationCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates an expression in the scope of the provided module.
|
||||
*
|
||||
* @param module the module providing a scope for the expression
|
||||
* @param expression the expression to evaluated
|
||||
* @param expression the expression to evaluate
|
||||
* @return a result of evaluation
|
||||
*/
|
||||
public Object evaluateExpression(Module module, String expression)
|
||||
throws UnsupportedMessageException, ArityException, UnknownIdentifierException,
|
||||
UnsupportedTypeException {
|
||||
public Object evaluateExpression(Module module, String expression) {
|
||||
Object p = context.getThreadManager().enter();
|
||||
try {
|
||||
return invoke.getCallTarget().call(module, expression);
|
||||
@ -290,11 +292,10 @@ public final class ExecutionService {
|
||||
* @param argument the argument applied to the function
|
||||
* @return the result of calling the function
|
||||
*/
|
||||
public Object callFunction(Object fn, Object argument)
|
||||
throws UnsupportedTypeException, ArityException, UnsupportedMessageException {
|
||||
public Object callFunction(Object fn, Object argument) {
|
||||
Object p = context.getThreadManager().enter();
|
||||
try {
|
||||
return call.getCallTarget().call(fn, new Object[] { argument });
|
||||
return call.getCallTarget().call(fn, new Object[] {argument});
|
||||
} finally {
|
||||
context.getThreadManager().leave(p);
|
||||
}
|
||||
@ -310,8 +311,11 @@ public final class ExecutionService {
|
||||
* @return the result of calling the function
|
||||
*/
|
||||
public Object callFunctionWithInstrument(
|
||||
RuntimeCache cache, Module module, Object function, Object... arguments)
|
||||
throws UnsupportedTypeException, ArityException, UnsupportedMessageException {
|
||||
VisualizationHolder visualizationHolder,
|
||||
RuntimeCache cache,
|
||||
Module module,
|
||||
Object function,
|
||||
Object... arguments) {
|
||||
UUID nextExecutionItem = null;
|
||||
CallTarget entryCallTarget =
|
||||
(function instanceof Function) ? ((Function) function).getCallTarget() : null;
|
||||
@ -322,18 +326,22 @@ public final class ExecutionService {
|
||||
(value) -> context.getLogger().finest("_ON_COMPUTED " + value.getExpressionId());
|
||||
Consumer<ExpressionValue> onCachedCallback =
|
||||
(value) -> context.getLogger().finest("_ON_CACHED_VALUE " + value.getExpressionId());
|
||||
Consumer<ExecutedVisualization> onExecutedVisualizationCallback = (value) -> {};
|
||||
|
||||
var callbacks = new ExecutionCallbacks(
|
||||
nextExecutionItem, cache, methodCallsCache, syncState,
|
||||
onCachedCallback, onComputedCallback, funCallCallback
|
||||
);
|
||||
var callbacks =
|
||||
new ExecutionCallbacks(
|
||||
visualizationHolder,
|
||||
nextExecutionItem,
|
||||
cache,
|
||||
methodCallsCache,
|
||||
syncState,
|
||||
onCachedCallback,
|
||||
onComputedCallback,
|
||||
funCallCallback,
|
||||
onExecutedVisualizationCallback);
|
||||
Optional<EventBinding<ExecutionEventNodeFactory>> eventNodeFactory =
|
||||
idExecutionInstrument.map(service -> service.bind(
|
||||
module,
|
||||
entryCallTarget,
|
||||
callbacks,
|
||||
this.timer
|
||||
));
|
||||
idExecutionInstrument.map(
|
||||
service -> service.bind(module, entryCallTarget, callbacks, this.timer));
|
||||
Object p = context.getThreadManager().enter();
|
||||
try {
|
||||
return call.getCallTarget().call(function, arguments);
|
||||
@ -451,12 +459,11 @@ public final class ExecutionService {
|
||||
var iop = InteropLibrary.getUncached();
|
||||
var p = context.getThreadManager().enter();
|
||||
try {
|
||||
// Invoking a member on an Atom that does not have a method `to_display_text` will not, contrary to what is
|
||||
// Invoking a member on an Atom that does not have a method `to_display_text` will not contrary to what is
|
||||
// expected from the documentation, throw an `UnsupportedMessageException`.
|
||||
// Instead it will crash with some internal assertion deep inside runtime. Hence the check.
|
||||
if (iop.isMemberInvocable(panic.getPayload(), "to_display_text")) {
|
||||
return iop.asString(
|
||||
iop.invokeMember(panic.getPayload(), "to_display_text"));
|
||||
return iop.asString(iop.invokeMember(panic.getPayload(), "to_display_text"));
|
||||
} else throw UnsupportedMessageException.create();
|
||||
} catch (UnsupportedMessageException
|
||||
| ArityException
|
||||
@ -530,11 +537,15 @@ public final class ExecutionService {
|
||||
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame) {
|
||||
var module = frame.getArguments()[0];
|
||||
var expression = frame.getArguments()[1];
|
||||
Object[] arguments = frame.getArguments();
|
||||
Object module = arguments[0];
|
||||
Object expression = arguments[1];
|
||||
try {
|
||||
return iop.invokeMember(module, MethodNames.Module.EVAL_EXPRESSION, expression);
|
||||
} catch (UnknownIdentifierException | UnsupportedTypeException | ArityException | UnsupportedMessageException ex) {
|
||||
} catch (UnknownIdentifierException
|
||||
| UnsupportedTypeException
|
||||
| ArityException
|
||||
| UnsupportedMessageException ex) {
|
||||
throw raise(RuntimeException.class, ex);
|
||||
}
|
||||
}
|
||||
@ -686,7 +697,8 @@ public final class ExecutionService {
|
||||
}
|
||||
|
||||
/** Points to the definition of a runtime function. */
|
||||
public record FunctionPointer(QualifiedName moduleName, QualifiedName typeName, String functionName) {
|
||||
public record FunctionPointer(
|
||||
QualifiedName moduleName, QualifiedName typeName, String functionName) {
|
||||
|
||||
public static FunctionPointer fromFunction(Function function) {
|
||||
RootNode rootNode = function.getCallTarget().getRootNode();
|
||||
@ -753,8 +765,8 @@ public final class ExecutionService {
|
||||
return false;
|
||||
}
|
||||
FunctionCallInfo that = (FunctionCallInfo) o;
|
||||
return Objects.equals(functionPointer, that.functionPointer) && Arrays.equals(
|
||||
notAppliedArguments, that.notAppliedArguments);
|
||||
return Objects.equals(functionPointer, that.functionPointer)
|
||||
&& Arrays.equals(notAppliedArguments, that.notAppliedArguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -768,14 +780,16 @@ public final class ExecutionService {
|
||||
*
|
||||
* @param call the function call.
|
||||
*/
|
||||
public static FunctionCallInfo fromFunctionCall(FunctionCallInstrumentationNode.FunctionCall call) {
|
||||
public static FunctionCallInfo fromFunctionCall(
|
||||
FunctionCallInstrumentationNode.FunctionCall call) {
|
||||
FunctionPointer functionPointer = FunctionPointer.fromFunction(call.getFunction());
|
||||
int[] notAppliedArguments = collectNotAppliedArguments(call);
|
||||
|
||||
return new FunctionCallInfo(functionPointer, notAppliedArguments);
|
||||
}
|
||||
|
||||
private static int[] collectNotAppliedArguments(FunctionCallInstrumentationNode.FunctionCall call) {
|
||||
private static int[] collectNotAppliedArguments(
|
||||
FunctionCallInstrumentationNode.FunctionCall call) {
|
||||
Object[] arguments = call.getArguments();
|
||||
int[] notAppliedArgs = new int[arguments.length];
|
||||
int notAppliedArgsSize = 0;
|
||||
|
@ -153,8 +153,9 @@ object CacheInvalidation {
|
||||
command: Command,
|
||||
indexes: Set[IndexSelector] = Set()
|
||||
): Unit =
|
||||
visualizations.foreach { visualization =>
|
||||
run(visualization.cache, command, indexes)
|
||||
visualizations.collect {
|
||||
case visualization: Visualization.AttachedVisualization =>
|
||||
run(visualization.cache, command, indexes)
|
||||
}
|
||||
|
||||
/** Run a cache invalidation instruction on an execution stack.
|
||||
|
@ -34,18 +34,6 @@ class ExecutionContextManager {
|
||||
contexts -= id
|
||||
}
|
||||
|
||||
/** Gets a context with a given id.
|
||||
*
|
||||
* @param id the context id.
|
||||
* @return the context with the given id, if exists.
|
||||
*/
|
||||
def get(id: ContextId): Option[ContextId] =
|
||||
synchronized {
|
||||
for {
|
||||
_ <- contexts.get(id)
|
||||
} yield id
|
||||
}
|
||||
|
||||
/** Gets a stack for a given context id.
|
||||
*
|
||||
* @param id the context id.
|
||||
@ -116,6 +104,16 @@ class ExecutionContextManager {
|
||||
state.visualizations.upsert(visualization)
|
||||
}
|
||||
|
||||
/** Gets a context with a given id.
|
||||
*
|
||||
* @param id the context id.
|
||||
* @return the context with the given id, if exists.
|
||||
*/
|
||||
def getVisualizationHolder(id: ContextId): VisualizationHolder =
|
||||
synchronized {
|
||||
contexts.get(id).map(_.visualizations).getOrElse(new VisualizationHolder)
|
||||
}
|
||||
|
||||
/** Get visualizations of all execution contexts. */
|
||||
def getAllVisualizations: Iterable[Visualization] =
|
||||
synchronized {
|
||||
|
@ -2,25 +2,47 @@ package org.enso.interpreter.instrument
|
||||
|
||||
import org.enso.interpreter.runtime.Module
|
||||
import org.enso.polyglot.runtime.Runtime.Api.{
|
||||
ContextId,
|
||||
ExpressionId,
|
||||
VisualizationConfiguration,
|
||||
VisualizationId
|
||||
}
|
||||
|
||||
/** An object containing visualization data.
|
||||
*
|
||||
* @param id the unique identifier of visualization
|
||||
* @param expressionId the identifier of expression that the visualization is
|
||||
* attached to
|
||||
* @param callback the callable expression used to generate visualization data
|
||||
*/
|
||||
case class Visualization(
|
||||
id: VisualizationId,
|
||||
expressionId: ExpressionId,
|
||||
cache: RuntimeCache,
|
||||
module: Module,
|
||||
config: VisualizationConfiguration,
|
||||
visualizationExpressionId: Option[ExpressionId],
|
||||
callback: AnyRef,
|
||||
arguments: Vector[AnyRef]
|
||||
)
|
||||
sealed trait Visualization {
|
||||
def id: VisualizationId
|
||||
def expressionId: ExpressionId
|
||||
}
|
||||
object Visualization {
|
||||
|
||||
/** An object containing visualization data.
|
||||
*
|
||||
* @param id the unique identifier of visualization
|
||||
* @param expressionId the identifier of expression that the visualization is
|
||||
* attached to
|
||||
* @param callback the callable expression used to generate visualization data
|
||||
*/
|
||||
case class AttachedVisualization(
|
||||
id: VisualizationId,
|
||||
expressionId: ExpressionId,
|
||||
cache: RuntimeCache,
|
||||
module: Module,
|
||||
config: VisualizationConfiguration,
|
||||
visualizationExpressionId: Option[ExpressionId],
|
||||
callback: AnyRef,
|
||||
arguments: Vector[AnyRef]
|
||||
) extends Visualization
|
||||
|
||||
/** An expression that will be executed in the local scope.
|
||||
*
|
||||
* @param id the unique identifier of visualization
|
||||
* @param expressionId the identifier of expression that provides the execution scope
|
||||
* @param executionContextId the identifier of the execution context
|
||||
* @param expression the expression to execute
|
||||
*/
|
||||
case class OneshotExpression(
|
||||
id: VisualizationId,
|
||||
expressionId: ExpressionId,
|
||||
executionContextId: ContextId,
|
||||
expression: String
|
||||
) extends Visualization
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import scala.collection.mutable
|
||||
|
||||
/** A mutable holder of all visualizations attached to an execution context.
|
||||
*/
|
||||
class VisualizationHolder() {
|
||||
class VisualizationHolder {
|
||||
|
||||
private val visualizationMap: mutable.Map[ExpressionId, List[Visualization]] =
|
||||
mutable.Map.empty.withDefaultValue(List.empty)
|
||||
@ -50,8 +50,14 @@ class VisualizationHolder() {
|
||||
* @param module the qualified module name
|
||||
* @return a list of matching visualization
|
||||
*/
|
||||
def findByModule(module: QualifiedName): Iterable[Visualization] =
|
||||
visualizationMap.values.flatten.filter(_.module.getName == module)
|
||||
def findByModule(
|
||||
module: QualifiedName
|
||||
): Iterable[Visualization.AttachedVisualization] =
|
||||
visualizationMap.values.flatten.collect {
|
||||
case visualization: Visualization.AttachedVisualization
|
||||
if visualization.module.getName == module =>
|
||||
visualization
|
||||
}
|
||||
|
||||
/** Returns a visualization with the provided id.
|
||||
*
|
||||
@ -69,6 +75,6 @@ class VisualizationHolder() {
|
||||
object VisualizationHolder {
|
||||
|
||||
/** Returns an empty visualization holder. */
|
||||
def empty = new VisualizationHolder()
|
||||
def empty = new VisualizationHolder
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,15 @@ object CommandFactory {
|
||||
case payload: Api.AttachVisualization =>
|
||||
new AttachVisualizationCmd(request.requestId, payload)
|
||||
|
||||
case payload: Api.ExecuteExpression =>
|
||||
new ExecuteExpressionCommand(
|
||||
request.requestId,
|
||||
payload.contextId,
|
||||
payload.visualizationId,
|
||||
payload.expressionId,
|
||||
payload.expression
|
||||
)
|
||||
|
||||
case payload: Api.DetachVisualization =>
|
||||
new DetachVisualizationCmd(request.requestId, payload)
|
||||
|
||||
|
@ -508,10 +508,14 @@ final class EnsureCompiledJob(
|
||||
|
||||
private def getCacheMetadata(
|
||||
visualization: Visualization
|
||||
): Option[CachePreferenceAnalysis.Metadata] = {
|
||||
val module = visualization.module
|
||||
module.getIr.getMetadata(CachePreferenceAnalysis)
|
||||
}
|
||||
): Option[CachePreferenceAnalysis.Metadata] =
|
||||
visualization match {
|
||||
case visualization: Visualization.AttachedVisualization =>
|
||||
val module = visualization.module
|
||||
module.getIr.getMetadata(CachePreferenceAnalysis)
|
||||
case _: Visualization.OneshotExpression =>
|
||||
None
|
||||
}
|
||||
|
||||
/** Get all project modules in the current compiler scope. */
|
||||
private def getProjectModulesInScope(implicit
|
||||
|
@ -2,11 +2,7 @@ package org.enso.interpreter.instrument.job
|
||||
|
||||
import cats.implicits._
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException
|
||||
import org.enso.interpreter.service.ExecutionService.{
|
||||
ExpressionCall,
|
||||
ExpressionValue,
|
||||
FunctionPointer
|
||||
}
|
||||
import org.enso.interpreter.instrument._
|
||||
import org.enso.interpreter.instrument.execution.{
|
||||
Completion,
|
||||
ErrorResolver,
|
||||
@ -14,7 +10,6 @@ import org.enso.interpreter.instrument.execution.{
|
||||
RuntimeContext
|
||||
}
|
||||
import org.enso.interpreter.instrument.profiling.ExecutionTime
|
||||
import org.enso.interpreter.instrument._
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode.FunctionCall
|
||||
import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode
|
||||
import org.enso.interpreter.runtime.`type`.{Types, TypesGen}
|
||||
@ -26,8 +21,14 @@ import org.enso.interpreter.runtime.error.{
|
||||
WarningsLibrary,
|
||||
WithWarnings
|
||||
}
|
||||
import org.enso.interpreter.service.ExecutionService.{
|
||||
ExpressionCall,
|
||||
ExpressionValue,
|
||||
FunctionPointer
|
||||
}
|
||||
import org.enso.interpreter.service.error._
|
||||
import org.enso.polyglot.LanguageInfo
|
||||
import org.enso.polyglot.debugger.ExecutedVisualization
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.polyglot.runtime.Runtime.Api.{ContextId, ExecutionResult}
|
||||
|
||||
@ -92,23 +93,60 @@ object ProgramExecutionSupport {
|
||||
cache,
|
||||
syncState
|
||||
) =>
|
||||
val onExecutedVisualizationCallback: Consumer[ExecutedVisualization] = {
|
||||
executedVisualization =>
|
||||
val visualizationResult =
|
||||
Either.cond(
|
||||
executedVisualization.error() eq null,
|
||||
executedVisualization.result(),
|
||||
executedVisualization.error()
|
||||
)
|
||||
sendVisualizationUpdate(
|
||||
visualizationResult,
|
||||
contextId,
|
||||
syncState,
|
||||
executedVisualization.visualizationId(),
|
||||
executedVisualization.expressionId(),
|
||||
executedVisualization.expressionValue()
|
||||
)
|
||||
}
|
||||
|
||||
ctx.executionService.execute(
|
||||
module.toString,
|
||||
cons.item,
|
||||
function,
|
||||
ctx.contextManager.getVisualizationHolder(contextId),
|
||||
cache,
|
||||
methodCallsCache,
|
||||
syncState,
|
||||
callStack.headOption.map(_.expressionId).orNull,
|
||||
callablesCallback,
|
||||
onComputedValueCallback,
|
||||
onCachedValueCallback
|
||||
onCachedValueCallback,
|
||||
onExecutedVisualizationCallback
|
||||
)
|
||||
case ExecutionFrame(
|
||||
ExecutionItem.CallData(expressionId, callData),
|
||||
cache,
|
||||
syncState
|
||||
) =>
|
||||
val onExecutedVisualizationCallback: Consumer[ExecutedVisualization] = {
|
||||
executedVisualization =>
|
||||
val visualizationResult =
|
||||
Either.cond(
|
||||
executedVisualization.error() eq null,
|
||||
executedVisualization.result(),
|
||||
executedVisualization.error()
|
||||
)
|
||||
sendVisualizationUpdate(
|
||||
visualizationResult,
|
||||
contextId,
|
||||
syncState,
|
||||
executedVisualization.visualizationId(),
|
||||
executedVisualization.expressionId(),
|
||||
executedVisualization.expressionValue()
|
||||
)
|
||||
}
|
||||
val module =
|
||||
ctx.executionService.getContext
|
||||
.findModuleByExpressionId(expressionId)
|
||||
@ -116,6 +154,7 @@ object ProgramExecutionSupport {
|
||||
new ModuleNotFoundForExpressionIdException(expressionId)
|
||||
)
|
||||
ctx.executionService.execute(
|
||||
ctx.contextManager.getVisualizationHolder(contextId),
|
||||
module,
|
||||
callData,
|
||||
cache,
|
||||
@ -124,7 +163,8 @@ object ProgramExecutionSupport {
|
||||
callStack.headOption.map(_.expressionId).orNull,
|
||||
callablesCallback,
|
||||
onComputedValueCallback,
|
||||
onCachedValueCallback
|
||||
onCachedValueCallback,
|
||||
onExecutedVisualizationCallback
|
||||
)
|
||||
}
|
||||
|
||||
@ -417,7 +457,6 @@ object ProgramExecutionSupport {
|
||||
* @param value the computed value
|
||||
* @param ctx the runtime context
|
||||
*/
|
||||
@com.oracle.truffle.api.CompilerDirectives.TruffleBoundary
|
||||
private def sendVisualizationUpdates(
|
||||
contextId: ContextId,
|
||||
syncState: UpdatesSynchronizationState,
|
||||
@ -429,70 +468,82 @@ object ProgramExecutionSupport {
|
||||
contextId,
|
||||
value.getExpressionId
|
||||
)
|
||||
visualizations.foreach { visualization =>
|
||||
sendVisualizationUpdate(
|
||||
contextId,
|
||||
syncState,
|
||||
visualization,
|
||||
value.getExpressionId,
|
||||
value.getValue
|
||||
)
|
||||
visualizations.collect {
|
||||
case visualization: Visualization.AttachedVisualization =>
|
||||
executeAndSendVisualizationUpdate(
|
||||
contextId,
|
||||
syncState,
|
||||
visualization,
|
||||
value.getExpressionId,
|
||||
value.getValue
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def executeVisualization(
|
||||
contextId: ContextId,
|
||||
visualization: Visualization.AttachedVisualization,
|
||||
expressionId: UUID,
|
||||
expressionValue: AnyRef
|
||||
)(implicit ctx: RuntimeContext): Either[Throwable, AnyRef] =
|
||||
Either
|
||||
.catchNonFatal {
|
||||
val logger = ctx.executionService.getLogger
|
||||
logger.log(
|
||||
Level.FINEST,
|
||||
"Executing visualization [{0}] on expression [{1}] of [{2}]...",
|
||||
Array[Object](
|
||||
visualization.id,
|
||||
expressionId,
|
||||
Try(TypeOfNode.getUncached.execute(expressionValue))
|
||||
.getOrElse(expressionValue.getClass)
|
||||
)
|
||||
)
|
||||
ctx.executionService.callFunctionWithInstrument(
|
||||
ctx.contextManager.getVisualizationHolder(contextId),
|
||||
visualization.cache,
|
||||
visualization.module,
|
||||
visualization.callback,
|
||||
expressionValue +: visualization.arguments: _*
|
||||
)
|
||||
}
|
||||
|
||||
/** Compute the visualization of the expression value and send an update.
|
||||
*
|
||||
* @param contextId an identifier of an execution context
|
||||
* @param visualization the visualization data
|
||||
* @param visualizationId the id of the visualization
|
||||
* @param expressionId the id of expression to visualise
|
||||
* @param expressionValue the value of expression to visualise
|
||||
* @param ctx the runtime context
|
||||
*/
|
||||
def sendVisualizationUpdate(
|
||||
private def sendVisualizationUpdate(
|
||||
visualizationResult: Either[Throwable, AnyRef],
|
||||
contextId: ContextId,
|
||||
syncState: UpdatesSynchronizationState,
|
||||
visualization: Visualization,
|
||||
visualizationId: UUID,
|
||||
expressionId: UUID,
|
||||
expressionValue: AnyRef
|
||||
)(implicit ctx: RuntimeContext): Unit = {
|
||||
val errorOrVisualizationData =
|
||||
Either
|
||||
.catchNonFatal {
|
||||
ctx.executionService.getLogger.log(
|
||||
Level.FINEST,
|
||||
"Executing visualization [{0}] on expression [{1}] of [{2}]...",
|
||||
Array[Object](
|
||||
visualization.config,
|
||||
expressionId,
|
||||
Try(TypeOfNode.getUncached.execute(expressionValue))
|
||||
.getOrElse(expressionValue.getClass)
|
||||
)
|
||||
)
|
||||
ctx.executionService.callFunctionWithInstrument(
|
||||
visualization.cache,
|
||||
visualization.module,
|
||||
visualization.callback,
|
||||
expressionValue +: visualization.arguments: _*
|
||||
)
|
||||
}
|
||||
.flatMap(visualizationResultToBytes)
|
||||
val result = errorOrVisualizationData match {
|
||||
val result = visualizationResultToBytes(visualizationResult) match {
|
||||
case Left(_: ThreadInterruptedException) =>
|
||||
Completion.Interrupted
|
||||
|
||||
case Left(error) =>
|
||||
val message =
|
||||
Option(error.getMessage).getOrElse(error.getClass.getSimpleName)
|
||||
val typeOfNode = Try(TypeOfNode.getUncached.execute(expressionValue))
|
||||
if (!typeOfNode.map(TypesGen.isPanicSentinel).getOrElse(false)) {
|
||||
if (!TypesGen.isPanicSentinel(expressionValue)) {
|
||||
val typeOfNode =
|
||||
Option(TypeOfNode.getUncached.execute(expressionValue))
|
||||
.getOrElse(expressionValue.getClass)
|
||||
ctx.executionService.getLogger.log(
|
||||
Level.WARNING,
|
||||
"Execution of visualization [{0}] on value [{1}] of [{2}] failed.",
|
||||
"Execution of visualization [{0}] on value [{1}] of [{2}] failed. {3}",
|
||||
Array[Object](
|
||||
visualization.config,
|
||||
visualizationId,
|
||||
expressionId,
|
||||
typeOfNode.getOrElse(expressionValue.getClass),
|
||||
typeOfNode,
|
||||
message,
|
||||
error
|
||||
)
|
||||
)
|
||||
@ -501,7 +552,7 @@ object ProgramExecutionSupport {
|
||||
Api.Response(
|
||||
Api.VisualizationEvaluationFailed(
|
||||
contextId,
|
||||
visualization.id,
|
||||
visualizationId,
|
||||
expressionId,
|
||||
message,
|
||||
getDiagnosticOutcome.lift(error)
|
||||
@ -520,7 +571,7 @@ object ProgramExecutionSupport {
|
||||
Api.Response(
|
||||
Api.VisualizationUpdate(
|
||||
Api.VisualizationContext(
|
||||
visualization.id,
|
||||
visualizationId,
|
||||
contextId,
|
||||
expressionId
|
||||
),
|
||||
@ -535,20 +586,56 @@ object ProgramExecutionSupport {
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute the visualization of the expression value and send an update.
|
||||
*
|
||||
* @param contextId an identifier of an execution context
|
||||
* @param visualization the visualization data
|
||||
* @param expressionId the id of expression to visualise
|
||||
* @param expressionValue the value of expression to visualise
|
||||
* @param ctx the runtime context
|
||||
*/
|
||||
def executeAndSendVisualizationUpdate(
|
||||
contextId: ContextId,
|
||||
syncState: UpdatesSynchronizationState,
|
||||
visualization: Visualization,
|
||||
expressionId: UUID,
|
||||
expressionValue: AnyRef
|
||||
)(implicit ctx: RuntimeContext): Unit =
|
||||
visualization match {
|
||||
case visualization: Visualization.AttachedVisualization =>
|
||||
val visualizationResult = executeVisualization(
|
||||
contextId,
|
||||
visualization,
|
||||
expressionId,
|
||||
expressionValue
|
||||
)
|
||||
sendVisualizationUpdate(
|
||||
visualizationResult,
|
||||
contextId,
|
||||
syncState,
|
||||
visualization.id,
|
||||
expressionId,
|
||||
expressionValue
|
||||
)
|
||||
case _: Visualization.OneshotExpression =>
|
||||
}
|
||||
|
||||
/** Convert the result of Enso visualization function to a byte array.
|
||||
*
|
||||
* @param value the result of Enso visualization function
|
||||
* @param visualizationResult the result of Enso visualization function
|
||||
* @return either a byte array representing the visualization result or an
|
||||
* error
|
||||
*/
|
||||
private def visualizationResultToBytes(
|
||||
value: AnyRef
|
||||
): Either[VisualizationException, Array[Byte]] = {
|
||||
Option(VisualizationResult.visualizationResultToBytes(value)).toRight(
|
||||
new VisualizationException(
|
||||
s"Cannot encode ${value.getClass} to byte array."
|
||||
visualizationResult: Either[Throwable, AnyRef]
|
||||
): Either[Throwable, Array[Byte]] = {
|
||||
visualizationResult.flatMap { value =>
|
||||
Option(VisualizationResult.visualizationResultToBytes(value)).toRight(
|
||||
new VisualizationException(
|
||||
s"Cannot encode ${value.getClass} to byte array."
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Extract the method call information from the provided expression value.
|
||||
|
@ -79,7 +79,7 @@ class UpsertVisualizationJob(
|
||||
|
||||
case Right(EvaluationResult(module, callable, arguments)) =>
|
||||
val visualization =
|
||||
UpsertVisualizationJob.updateVisualization(
|
||||
UpsertVisualizationJob.updateAttachedVisualization(
|
||||
visualizationId,
|
||||
expressionId,
|
||||
module,
|
||||
@ -96,7 +96,7 @@ class UpsertVisualizationJob(
|
||||
)
|
||||
cachedValue match {
|
||||
case Some(value) =>
|
||||
ProgramExecutionSupport.sendVisualizationUpdate(
|
||||
ProgramExecutionSupport.executeAndSendVisualizationUpdate(
|
||||
config.executionContextId,
|
||||
stack.headOption.get.syncState,
|
||||
visualization,
|
||||
@ -185,30 +185,39 @@ object UpsertVisualizationJob {
|
||||
*/
|
||||
def upsertVisualization(
|
||||
visualization: Visualization
|
||||
)(implicit ctx: RuntimeContext, logger: TruffleLogger): Unit = {
|
||||
val visualizationConfig = visualization.config
|
||||
val expressionId = visualization.expressionId
|
||||
val visualizationId = visualization.id
|
||||
val maybeCallable =
|
||||
evaluateVisualizationExpression(
|
||||
visualizationConfig.visualizationModule,
|
||||
visualizationConfig.expression
|
||||
)
|
||||
)(implicit ctx: RuntimeContext, logger: TruffleLogger): Unit =
|
||||
visualization match {
|
||||
case visualization: Visualization.AttachedVisualization =>
|
||||
val visualizationConfig = visualization.config
|
||||
val expressionId = visualization.expressionId
|
||||
val visualizationId = visualization.id
|
||||
val maybeCallable =
|
||||
evaluateVisualizationExpression(
|
||||
visualizationConfig.visualizationModule,
|
||||
visualizationConfig.expression
|
||||
)
|
||||
|
||||
maybeCallable.foreach { result =>
|
||||
updateAttachedVisualization(
|
||||
visualizationId,
|
||||
expressionId,
|
||||
result.module,
|
||||
visualizationConfig,
|
||||
result.callback,
|
||||
result.arguments
|
||||
)
|
||||
val stack =
|
||||
ctx.contextManager.getStack(visualizationConfig.executionContextId)
|
||||
requireVisualizationSynchronization(stack, expressionId)
|
||||
}
|
||||
|
||||
case visualization: Visualization.OneshotExpression =>
|
||||
ctx.contextManager.upsertVisualization(
|
||||
visualization.executionContextId,
|
||||
visualization
|
||||
)
|
||||
|
||||
maybeCallable.foreach { result =>
|
||||
updateVisualization(
|
||||
visualizationId,
|
||||
expressionId,
|
||||
result.module,
|
||||
visualizationConfig,
|
||||
result.callback,
|
||||
result.arguments
|
||||
)
|
||||
val stack =
|
||||
ctx.contextManager.getStack(visualizationConfig.executionContextId)
|
||||
requireVisualizationSynchronization(stack, expressionId)
|
||||
}
|
||||
}
|
||||
|
||||
/** Find module by name.
|
||||
*
|
||||
@ -462,7 +471,7 @@ object UpsertVisualizationJob {
|
||||
* @param ctx the runtime context
|
||||
* @return the re-evaluated visualization
|
||||
*/
|
||||
private def updateVisualization(
|
||||
private def updateAttachedVisualization(
|
||||
visualizationId: Api.VisualizationId,
|
||||
expressionId: Api.ExpressionId,
|
||||
module: Module,
|
||||
@ -472,16 +481,17 @@ object UpsertVisualizationJob {
|
||||
)(implicit ctx: RuntimeContext, logger: TruffleLogger): Visualization = {
|
||||
val visualizationExpressionId =
|
||||
findVisualizationExpressionId(module, visualizationConfig.expression)
|
||||
val visualization = Visualization(
|
||||
visualizationId,
|
||||
expressionId,
|
||||
new RuntimeCache(),
|
||||
module,
|
||||
visualizationConfig,
|
||||
visualizationExpressionId,
|
||||
callback,
|
||||
arguments
|
||||
)
|
||||
val visualization =
|
||||
Visualization.AttachedVisualization(
|
||||
visualizationId,
|
||||
expressionId,
|
||||
new RuntimeCache(),
|
||||
module,
|
||||
visualizationConfig,
|
||||
visualizationExpressionId,
|
||||
callback,
|
||||
arguments
|
||||
)
|
||||
val writeLockTimestamp = ctx.locking.acquireWriteCompilationLock()
|
||||
try {
|
||||
invalidateCaches(visualization)
|
||||
@ -575,15 +585,19 @@ object UpsertVisualizationJob {
|
||||
*
|
||||
* @param visualization the visualization to update
|
||||
*/
|
||||
private def setCacheWeights(visualization: Visualization): Unit = {
|
||||
visualization.module.getIr.getMetadata(CachePreferenceAnalysis).foreach {
|
||||
metadata =>
|
||||
CacheInvalidation.runVisualizations(
|
||||
Seq(visualization),
|
||||
CacheInvalidation.Command.SetMetadata(metadata)
|
||||
)
|
||||
private def setCacheWeights(visualization: Visualization): Unit =
|
||||
visualization match {
|
||||
case visualization: Visualization.AttachedVisualization =>
|
||||
visualization.module.getIr
|
||||
.getMetadata(CachePreferenceAnalysis)
|
||||
.foreach { metadata =>
|
||||
CacheInvalidation.runVisualizations(
|
||||
Seq(visualization),
|
||||
CacheInvalidation.Command.SetMetadata(metadata)
|
||||
)
|
||||
}
|
||||
case _: Visualization.OneshotExpression =>
|
||||
}
|
||||
}
|
||||
|
||||
/** Invalidate the first cached dependent node of the provided expression.
|
||||
*
|
||||
|
@ -1,8 +1,5 @@
|
||||
package org.enso.interpreter.instrument;
|
||||
|
||||
|
||||
import org.enso.polyglot.debugger.IdExecutionService;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.RootCallTarget;
|
||||
@ -10,6 +7,7 @@ import com.oracle.truffle.api.Truffle;
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.frame.FrameInstance;
|
||||
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||
import com.oracle.truffle.api.instrumentation.EventContext;
|
||||
@ -20,16 +18,18 @@ import com.oracle.truffle.api.instrumentation.StandardTags;
|
||||
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
|
||||
import com.oracle.truffle.api.interop.InteropException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.enso.interpreter.node.ClosureRootNode;
|
||||
import org.enso.interpreter.node.EnsoRootNode;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
import org.enso.interpreter.node.expression.debug.EvalNode;
|
||||
import org.enso.interpreter.runtime.EnsoContext;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.callable.CallerInfo;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.control.TailCallException;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
@ -39,8 +39,7 @@ import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.state.State;
|
||||
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import org.enso.polyglot.debugger.IdExecutionService;
|
||||
|
||||
/** An instrument for getting values from AST-identified expressions. */
|
||||
@TruffleInstrument.Registration(
|
||||
@ -68,6 +67,8 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
private final Callbacks callbacks;
|
||||
private final Timer timer;
|
||||
|
||||
private final EvalNode evalNode = EvalNode.build();
|
||||
|
||||
/**
|
||||
* Creates a new event node factory.
|
||||
*
|
||||
@ -75,11 +76,7 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
* @param callbacks communication with users
|
||||
* @param timer the timer for timing execution
|
||||
*/
|
||||
IdEventNodeFactory(
|
||||
CallTarget entryCallTarget,
|
||||
Callbacks callbacks,
|
||||
Timer timer
|
||||
) {
|
||||
IdEventNodeFactory(CallTarget entryCallTarget, Callbacks callbacks, Timer timer) {
|
||||
this.entryCallTarget = entryCallTarget;
|
||||
this.callbacks = callbacks;
|
||||
this.timer = timer;
|
||||
@ -90,9 +87,97 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
return new IdExecutionEventNode(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* The execution event node class used by this instrument.
|
||||
*/
|
||||
/** Implementation of {@link Info} for the instrumented {@link Node}. */
|
||||
private final class NodeInfo extends Info {
|
||||
|
||||
private final UUID nodeId;
|
||||
private final Object result;
|
||||
private final long elapsedTime;
|
||||
private final MaterializedFrame materializedFrame;
|
||||
private final EnsoRootNode ensoRootNode;
|
||||
|
||||
/**
|
||||
* Create a {@link NodeInfo} for the entered node.
|
||||
*
|
||||
* @param materializedFrame the execution frame
|
||||
* @param node the entered node
|
||||
*/
|
||||
public NodeInfo(
|
||||
MaterializedFrame materializedFrame,
|
||||
Node node) {
|
||||
super();
|
||||
|
||||
this.nodeId = getNodeId(node);
|
||||
this.result = null;
|
||||
this.elapsedTime = -1;
|
||||
this.materializedFrame = materializedFrame;
|
||||
this.ensoRootNode = (EnsoRootNode) node.getRootNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link NodeInfo} for the executed node.
|
||||
*
|
||||
* @param nodeId the id of the executed node
|
||||
* @param result the result of the node execution
|
||||
* @param elapsedTime the execution time
|
||||
* @param materializedFrame the execution frame
|
||||
* @param node the executed node
|
||||
*/
|
||||
public NodeInfo(
|
||||
UUID nodeId,
|
||||
Object result,
|
||||
long elapsedTime,
|
||||
MaterializedFrame materializedFrame,
|
||||
Node node) {
|
||||
super();
|
||||
|
||||
this.nodeId = nodeId;
|
||||
this.result = result;
|
||||
this.elapsedTime = elapsedTime;
|
||||
this.materializedFrame = materializedFrame;
|
||||
this.ensoRootNode = (EnsoRootNode) node.getRootNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPanic() {
|
||||
return result instanceof AbstractTruffleException && !(result instanceof DataflowError);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getElapsedTime() {
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String code) {
|
||||
CallerInfo callerInfo =
|
||||
new CallerInfo(
|
||||
materializedFrame, ensoRootNode.getLocalScope(), ensoRootNode.getModuleScope());
|
||||
|
||||
return evalNode.execute(callerInfo, State.create(EnsoContext.get(null)), Text.create(code));
|
||||
}
|
||||
|
||||
private static UUID getNodeId(Node node) {
|
||||
return switch (node) {
|
||||
case ExpressionNode n -> n.getId();
|
||||
case FunctionCallInstrumentationNode n -> n.getId();
|
||||
case null -> null;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** The execution event node class used by this instrument. */
|
||||
private class IdExecutionEventNode extends ExecutionEventNode {
|
||||
|
||||
private final EventContext context;
|
||||
@ -117,13 +202,10 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
if (!isTopFrame(entryCallTarget)) {
|
||||
return;
|
||||
}
|
||||
onEnterImpl();
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private void onEnterImpl() {
|
||||
UUID nodeId = getNodeId(context.getInstrumentedNode());
|
||||
var result = callbacks.findCachedResult(nodeId);
|
||||
Info info = new NodeInfo(frame.materialize(), context.getInstrumentedNode());
|
||||
Object result = callbacks.findCachedResult(info);
|
||||
|
||||
if (result != null) {
|
||||
throw context.createUnwind(result);
|
||||
}
|
||||
@ -131,12 +213,11 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a node (either a function call sentry or an identified
|
||||
* expression) finishes execution.
|
||||
* Triggered when a node (either a function call sentry or an identified expression) finishes
|
||||
* execution.
|
||||
*
|
||||
* @param frame the current execution frame.
|
||||
* @param result the result of executing the node this method was
|
||||
* triggered for.
|
||||
* @param result the result of executing the node this method was triggered for.
|
||||
*/
|
||||
@Override
|
||||
public void onReturnValue(VirtualFrame frame, Object result) {
|
||||
@ -146,15 +227,28 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
}
|
||||
Node node = context.getInstrumentedNode();
|
||||
|
||||
if (node instanceof FunctionCallInstrumentationNode
|
||||
&& result instanceof FunctionCallInstrumentationNode.FunctionCall functionCall) {
|
||||
UUID nodeId = ((FunctionCallInstrumentationNode) node).getId();
|
||||
var cachedResult = callbacks.onFunctionReturn(nodeId, functionCall);
|
||||
if (node instanceof FunctionCallInstrumentationNode functionCallInstrumentationNode
|
||||
&& result instanceof FunctionCallInstrumentationNode.FunctionCall) {
|
||||
Info info =
|
||||
new NodeInfo(
|
||||
functionCallInstrumentationNode.getId(),
|
||||
result,
|
||||
nanoTimeElapsed,
|
||||
frame.materialize(),
|
||||
node);
|
||||
Object cachedResult = callbacks.onFunctionReturn(info);
|
||||
if (cachedResult != null) {
|
||||
throw context.createUnwind(cachedResult);
|
||||
}
|
||||
} else if (node instanceof ExpressionNode) {
|
||||
onExpressionReturn(result, node, context, nanoTimeElapsed);
|
||||
} else if (node instanceof ExpressionNode expressionNode) {
|
||||
Info info =
|
||||
new NodeInfo(
|
||||
expressionNode.getId(), result, nanoTimeElapsed, frame.materialize(), node);
|
||||
callbacks.updateCachedResult(info);
|
||||
|
||||
if (info.isPanic()) {
|
||||
throw context.createUnwind(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,25 +263,13 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
}
|
||||
}
|
||||
|
||||
private void onExpressionReturn(Object result, Node node, EventContext context, long howLong) {
|
||||
boolean isPanic = result instanceof AbstractTruffleException && !(result instanceof DataflowError);
|
||||
UUID nodeId = ((ExpressionNode) node).getId();
|
||||
|
||||
callbacks.updateCachedResult(nodeId, result, isPanic, howLong);
|
||||
if (isPanic) {
|
||||
throw context.createUnwind(result);
|
||||
}
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private void onTailCallReturn(Throwable exception, State state) {
|
||||
try {
|
||||
TailCallException tailCallException = (TailCallException) exception;
|
||||
FunctionCallInstrumentationNode.FunctionCall functionCall
|
||||
= new FunctionCallInstrumentationNode.FunctionCall(
|
||||
tailCallException.getFunction(),
|
||||
state,
|
||||
tailCallException.getArguments());
|
||||
FunctionCallInstrumentationNode.FunctionCall functionCall =
|
||||
new FunctionCallInstrumentationNode.FunctionCall(
|
||||
tailCallException.getFunction(), state, tailCallException.getArguments());
|
||||
Object result = InteropLibrary.getFactory().getUncached().execute(functionCall);
|
||||
onReturnValue(null, result);
|
||||
} catch (InteropException e) {
|
||||
@ -196,47 +278,39 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we're not inside a recursive call, i.e. the
|
||||
* {@link #entryCallTarget} only appears in the stack trace once.
|
||||
* Checks if we're not inside a recursive call, i.e. the {@link #entryCallTarget} only appears
|
||||
* in the stack trace once.
|
||||
*
|
||||
* @return {@code true} if it's not a recursive call, {@code false}
|
||||
* otherwise.
|
||||
* @return {@code true} if it's not a recursive call, {@code false} otherwise.
|
||||
*/
|
||||
private boolean isTopFrame(CallTarget entryCallTarget) {
|
||||
Object result = Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Object>() {
|
||||
boolean seenFirst = false;
|
||||
Object result =
|
||||
Truffle.getRuntime()
|
||||
.iterateFrames(
|
||||
new FrameInstanceVisitor<Object>() {
|
||||
boolean seenFirst = false;
|
||||
|
||||
@Override
|
||||
public Object visitFrame(FrameInstance frameInstance) {
|
||||
CallTarget ct = frameInstance.getCallTarget();
|
||||
if (ct != entryCallTarget) {
|
||||
return null;
|
||||
}
|
||||
if (seenFirst) {
|
||||
return new Object();
|
||||
} else {
|
||||
seenFirst = true;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public Object visitFrame(FrameInstance frameInstance) {
|
||||
CallTarget ct = frameInstance.getCallTarget();
|
||||
if (ct != entryCallTarget) {
|
||||
return null;
|
||||
}
|
||||
if (seenFirst) {
|
||||
return new Object();
|
||||
} else {
|
||||
seenFirst = true;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result == null;
|
||||
}
|
||||
|
||||
private static UUID getNodeId(Node node) {
|
||||
return switch (node) {
|
||||
case ExpressionNode n -> n.getId();
|
||||
case FunctionCallInstrumentationNode n -> n.getId();
|
||||
case null -> null;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a new event node factory to observe identified nodes within given
|
||||
* function.
|
||||
* Attach a new event node factory to observe identified nodes within given function.
|
||||
*
|
||||
* @param mod module that contains the code
|
||||
* @param entryCallTarget the call target being observed.
|
||||
@ -246,19 +320,19 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
||||
*/
|
||||
@Override
|
||||
public EventBinding<ExecutionEventNodeFactory> bind(
|
||||
TruffleObject mod,
|
||||
CallTarget entryCallTarget,
|
||||
Callbacks callbacks,
|
||||
Object timer
|
||||
) {
|
||||
var module = (Module)mod;
|
||||
var builder = SourceSectionFilter.newBuilder()
|
||||
TruffleObject mod, CallTarget entryCallTarget, Callbacks callbacks, Object timer) {
|
||||
var module = (Module) mod;
|
||||
var builder =
|
||||
SourceSectionFilter.newBuilder()
|
||||
.tagIs(StandardTags.ExpressionTag.class, StandardTags.CallTag.class)
|
||||
.tagIs(IdentifiedTag.class)
|
||||
.tagIsNot(AvoidIdInstrumentationTag.class)
|
||||
.sourceIs(module::isModuleSource);
|
||||
|
||||
if (entryCallTarget instanceof RootCallTarget r && r.getRootNode() instanceof ClosureRootNode c && c.getSourceSection() instanceof SourceSection section && section != null) {
|
||||
if (entryCallTarget instanceof RootCallTarget r
|
||||
&& r.getRootNode() instanceof ClosureRootNode c
|
||||
&& c.getSourceSection() instanceof SourceSection section
|
||||
&& section != null) {
|
||||
final int firstFunctionLine = section.getStartLine();
|
||||
final int afterFunctionLine = section.getEndLine() + 1;
|
||||
builder.lineIn(SourceSectionFilter.IndexRange.between(firstFunctionLine, afterFunctionLine));
|
||||
|
@ -3513,4 +3513,365 @@ class RuntimeVisualizationsTest
|
||||
}
|
||||
new String(data1, StandardCharsets.UTF_8) shouldEqual "C"
|
||||
}
|
||||
|
||||
it should "execute expression in the scope of local expression cached" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val visualizationId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
val metadata = new Metadata
|
||||
|
||||
val idOp1 = metadata.addItem(23, 2)
|
||||
val idOp2 = metadata.addItem(42, 13)
|
||||
|
||||
val code =
|
||||
"""main =
|
||||
| operator1 = 42
|
||||
| operator2 = operator1 + 1
|
||||
| operator2
|
||||
|
|
||||
|fun1 x = x.to_text
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val contents = metadata.appendToCode(code)
|
||||
val mainFile = context.writeMain(contents)
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
// Open the new file
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer(moduleName, moduleName, "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||
)
|
||||
context.receiveNIgnorePendingExpressionUpdates(
|
||||
4
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER_BUILTIN),
|
||||
TestMessages.update(contextId, idOp2, ConstantsGen.INTEGER_BUILTIN),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
// execute expression
|
||||
context.send(
|
||||
Api.Request(
|
||||
requestId,
|
||||
Api.ExecuteExpression(
|
||||
contextId,
|
||||
visualizationId,
|
||||
idOp2,
|
||||
"fun1 operator1"
|
||||
)
|
||||
)
|
||||
)
|
||||
val executeExpressionResponses =
|
||||
context.receiveNIgnoreExpressionUpdates(3)
|
||||
executeExpressionResponses should contain allOf (
|
||||
Api.Response(requestId, Api.VisualizationAttached()),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
val Some(data) = executeExpressionResponses.collectFirst {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.VisualizationUpdate(
|
||||
Api.VisualizationContext(
|
||||
`visualizationId`,
|
||||
`contextId`,
|
||||
`idOp2`
|
||||
),
|
||||
data
|
||||
)
|
||||
) =>
|
||||
data
|
||||
}
|
||||
new String(data) shouldEqual "42"
|
||||
}
|
||||
|
||||
it should "execute expression in the scope of local expression not cached" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val visualizationId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
val metadata = new Metadata
|
||||
|
||||
val idOp1 = metadata.addItem(23, 2)
|
||||
val idOp2 = metadata.addItem(42, 13)
|
||||
val idRes = metadata.addItem(60, 9)
|
||||
|
||||
val code =
|
||||
"""main =
|
||||
| operator1 = 42
|
||||
| operator2 = operator1 + 1
|
||||
| operator2
|
||||
|
|
||||
|fun1 x = x.to_text
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val contents = metadata.appendToCode(code)
|
||||
val mainFile = context.writeMain(contents)
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
// Open the new file
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer(moduleName, moduleName, "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||
)
|
||||
context.receiveNIgnorePendingExpressionUpdates(
|
||||
5
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER_BUILTIN),
|
||||
TestMessages.update(contextId, idOp2, ConstantsGen.INTEGER_BUILTIN),
|
||||
TestMessages.update(contextId, idRes, ConstantsGen.INTEGER_BUILTIN),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
// execute expression
|
||||
context.send(
|
||||
Api.Request(
|
||||
requestId,
|
||||
Api.ExecuteExpression(
|
||||
contextId,
|
||||
visualizationId,
|
||||
idRes,
|
||||
"fun1 operator1"
|
||||
)
|
||||
)
|
||||
)
|
||||
val executeExpressionResponses =
|
||||
context.receiveNIgnoreExpressionUpdates(3)
|
||||
executeExpressionResponses should contain allOf (
|
||||
Api.Response(requestId, Api.VisualizationAttached()),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
val Some(data) = executeExpressionResponses.collectFirst {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.VisualizationUpdate(
|
||||
Api.VisualizationContext(
|
||||
`visualizationId`,
|
||||
`contextId`,
|
||||
`idRes`
|
||||
),
|
||||
data
|
||||
)
|
||||
) =>
|
||||
data
|
||||
}
|
||||
new String(data) shouldEqual "42"
|
||||
}
|
||||
|
||||
it should "execute expression in the scope of local binding" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val visualizationId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
val metadata = new Metadata
|
||||
|
||||
val idOp1 = metadata.addItem(23, 2)
|
||||
val idOp2 = metadata.addItem(42, 13)
|
||||
val idOp2Binding = metadata.addItem(30, 25)
|
||||
val idRes = metadata.addItem(60, 9)
|
||||
|
||||
val code =
|
||||
"""main =
|
||||
| operator1 = 42
|
||||
| operator2 = operator1 + 1
|
||||
| operator2
|
||||
|
|
||||
|fun1 x = x.to_text
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val contents = metadata.appendToCode(code)
|
||||
val mainFile = context.writeMain(contents)
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
// Open the new file
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer(moduleName, moduleName, "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||
)
|
||||
context.receiveNIgnorePendingExpressionUpdates(
|
||||
6
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER_BUILTIN),
|
||||
TestMessages.update(contextId, idOp2, ConstantsGen.INTEGER_BUILTIN),
|
||||
TestMessages
|
||||
.update(contextId, idOp2Binding, ConstantsGen.NOTHING_BUILTIN),
|
||||
TestMessages.update(contextId, idRes, ConstantsGen.INTEGER_BUILTIN),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
// execute expression
|
||||
context.send(
|
||||
Api.Request(
|
||||
requestId,
|
||||
Api.ExecuteExpression(
|
||||
contextId,
|
||||
visualizationId,
|
||||
idOp2Binding,
|
||||
"fun1 operator1+operator2"
|
||||
)
|
||||
)
|
||||
)
|
||||
val executeExpressionResponses =
|
||||
context.receiveNIgnoreExpressionUpdates(3)
|
||||
executeExpressionResponses should contain allOf (
|
||||
Api.Response(requestId, Api.VisualizationAttached()),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
val Some(data) = executeExpressionResponses.collectFirst {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.VisualizationUpdate(
|
||||
Api.VisualizationContext(
|
||||
`visualizationId`,
|
||||
`contextId`,
|
||||
`idOp2Binding`
|
||||
),
|
||||
data
|
||||
)
|
||||
) =>
|
||||
data
|
||||
}
|
||||
new String(data) shouldEqual "85"
|
||||
}
|
||||
|
||||
it should "execute expression in the scope of main method" in {
|
||||
val contextId = UUID.randomUUID()
|
||||
val requestId = UUID.randomUUID()
|
||||
val visualizationId = UUID.randomUUID()
|
||||
val moduleName = "Enso_Test.Test.Main"
|
||||
val metadata = new Metadata
|
||||
|
||||
val idOp1 = metadata.addItem(23, 2)
|
||||
val idOp2 = metadata.addItem(42, 13)
|
||||
val idMain = metadata.addItem(6, 63)
|
||||
|
||||
val code =
|
||||
"""main =
|
||||
| operator1 = 42
|
||||
| operator2 = operator1 + 1
|
||||
| operator2
|
||||
|
|
||||
|fun1 x = x.to_text
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val contents = metadata.appendToCode(code)
|
||||
val mainFile = context.writeMain(contents)
|
||||
|
||||
// create context
|
||||
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(requestId, Api.CreateContextResponse(contextId))
|
||||
)
|
||||
|
||||
// Open the new file
|
||||
context.send(
|
||||
Api.Request(requestId, Api.OpenFileRequest(mainFile, contents))
|
||||
)
|
||||
context.receive shouldEqual Some(
|
||||
Api.Response(Some(requestId), Api.OpenFileResponse)
|
||||
)
|
||||
|
||||
// push main
|
||||
val item1 = Api.StackItem.ExplicitCall(
|
||||
Api.MethodPointer(moduleName, moduleName, "main"),
|
||||
None,
|
||||
Vector()
|
||||
)
|
||||
context.send(
|
||||
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||
)
|
||||
context.receiveNIgnorePendingExpressionUpdates(
|
||||
5
|
||||
) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||
TestMessages.update(contextId, idOp1, ConstantsGen.INTEGER_BUILTIN),
|
||||
TestMessages.update(contextId, idOp2, ConstantsGen.INTEGER_BUILTIN),
|
||||
TestMessages.update(contextId, idMain, ConstantsGen.INTEGER_BUILTIN),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
// execute expression
|
||||
context.send(
|
||||
Api.Request(
|
||||
requestId,
|
||||
Api.ExecuteExpression(
|
||||
contextId,
|
||||
visualizationId,
|
||||
idMain,
|
||||
"fun1 operator1+operator2"
|
||||
)
|
||||
)
|
||||
)
|
||||
val executeExpressionResponses =
|
||||
context.receiveNIgnoreExpressionUpdates(3)
|
||||
executeExpressionResponses should contain allOf (
|
||||
Api.Response(requestId, Api.VisualizationAttached()),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
val Some(data) = executeExpressionResponses.collectFirst {
|
||||
case Api.Response(
|
||||
None,
|
||||
Api.VisualizationUpdate(
|
||||
Api.VisualizationContext(
|
||||
`visualizationId`,
|
||||
`contextId`,
|
||||
`idMain`
|
||||
),
|
||||
data
|
||||
)
|
||||
) =>
|
||||
data
|
||||
}
|
||||
new String(data) shouldEqual "85"
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ public final class EnsoLanguage extends TruffleLanguage<EnsoContext> {
|
||||
|
||||
/**
|
||||
* Parses the given Enso source code snippet in {@code request}.
|
||||
*
|
||||
* <p>
|
||||
* Inline parsing does not handle the following expressions:
|
||||
* <ul>
|
||||
* <li>Assignments</li>
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.enso.interpreter.node.expression.builtin.meta;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.enso.interpreter.instrument.Timer;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
import org.enso.interpreter.runtime.EnsoContext;
|
||||
@ -15,7 +13,6 @@ import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||
import com.oracle.truffle.api.interop.InteropException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
|
||||
final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
||||
|
||||
@ -61,33 +58,33 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
||||
// Callbacks
|
||||
//
|
||||
@Override
|
||||
public Object findCachedResult(UUID nodeId) {
|
||||
public Object findCachedResult(IdExecutionService.Info info) {
|
||||
try {
|
||||
if (onEnter != null) {
|
||||
var ret = InteropLibrary.getUncached().execute(onEnter, nodeId.toString());
|
||||
var ret = InteropLibrary.getUncached().execute(onEnter, info.getId().toString());
|
||||
ret = InteropLibrary.getUncached().isNull(ret) ? null : ret;
|
||||
return handle.isDisposed() ? null : ret;
|
||||
}
|
||||
} catch (InteropException ex) {
|
||||
return handle.isDisposed() ? null : ret; }
|
||||
} catch (InteropException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCachedResult(UUID nodeId, Object result, boolean isPanic, long nanoElapsedTime) {
|
||||
public void updateCachedResult(IdExecutionService.Info info) {
|
||||
try {
|
||||
if (onReturn != null) {
|
||||
InteropLibrary.getUncached().execute(onReturn, nodeId.toString(), result);
|
||||
InteropLibrary.getUncached().execute(onReturn, info.getId().toString(), info.getResult());
|
||||
}
|
||||
} catch (InteropException ex) {
|
||||
} catch (InteropException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onFunctionReturn(UUID nodeId, TruffleObject result) {
|
||||
public Object onFunctionReturn(IdExecutionService.Info info) {
|
||||
try {
|
||||
if (onCall != null && result instanceof FunctionCallInstrumentationNode.FunctionCall call) {
|
||||
var args = (Object[])call.getArguments().clone();
|
||||
if (onCall != null
|
||||
&& info.getResult() instanceof FunctionCallInstrumentationNode.FunctionCall call) {
|
||||
var args = (Object[]) call.getArguments().clone();
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (args[i] == null) {
|
||||
args[i] = EnsoContext.get(null).getBuiltins().nothing();
|
||||
@ -95,14 +92,14 @@ final class Instrumentor implements EnsoObject, IdExecutionService.Callbacks {
|
||||
}
|
||||
var ret = InteropLibrary.getUncached().execute(
|
||||
onCall,
|
||||
nodeId.toString(),
|
||||
info.getId().toString(),
|
||||
call.getFunction(),
|
||||
ArrayLikeHelpers.asVectorWithCheckAt(args)
|
||||
);
|
||||
ret = InteropLibrary.getUncached().isNull(ret) ? null : ret;
|
||||
return handle.isDisposed() ? null : ret;
|
||||
}
|
||||
} catch (InteropException ex) {
|
||||
} catch (InteropException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user