mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 18:15:21 +03:00
Enable caching in visualization functions (#3618)
PR allows to attach metod pointers as a visualization expressions. This way it allows to attach a runtime instrument that enables caching of intermediate expressions.
# Important Notes
ℹ️ API is backward compatible.
To attach the visualization with caching support, the same `executionContext/attachVisualisation` method is used, but `VisualisationConfig` message should contain the message pointer.
While `VisualisationConfiguration` message has changed, the language server accepts both new and old formats to keep visualisations working in IDE.
#### Old format
```json
{
"executionContextId": "UUID",
"visualisationModule": "local.Unnamed.Main",
"expression": "x -> x.to_text"
}
```
#### New format
```json
{
"executionContextId": "UUID",
"expression": {
"module": "local.Unnamed.Main",
"definedOnType": "local.Unnamed.Main",
"name": "encode"
}
}
```
This commit is contained in:
parent
bd3b778721
commit
98d30bccf3
@ -302,6 +302,7 @@
|
|||||||
- [Explicit `self`][3569]
|
- [Explicit `self`][3569]
|
||||||
- [Added benchmarking tool for the language server][3578]
|
- [Added benchmarking tool for the language server][3578]
|
||||||
- [Support module imports using a qualified name][3608]
|
- [Support module imports using a qualified name][3608]
|
||||||
|
- [Enable caching in visualisation functions][3618]
|
||||||
- [Update Scala compiler and libraries][3631]
|
- [Update Scala compiler and libraries][3631]
|
||||||
- [Support importing module methods][3633]
|
- [Support importing module methods][3633]
|
||||||
|
|
||||||
@ -338,7 +339,8 @@
|
|||||||
[3562]: https://github.com/enso-org/enso/pull/3562
|
[3562]: https://github.com/enso-org/enso/pull/3562
|
||||||
[3538]: https://github.com/enso-org/enso/pull/3538
|
[3538]: https://github.com/enso-org/enso/pull/3538
|
||||||
[3538]: https://github.com/enso-org/enso/pull/3569
|
[3538]: https://github.com/enso-org/enso/pull/3569
|
||||||
[3578]: https://github.com/enso-org/enso/pull/3578
|
[3618]: https://github.com/enso-org/enso/pull/3618
|
||||||
|
[3608]: https://github.com/enso-org/enso/pull/3608
|
||||||
[3608]: https://github.com/enso-org/enso/pull/3608
|
[3608]: https://github.com/enso-org/enso/pull/3608
|
||||||
[3631]: https://github.com/enso-org/enso/pull/3631
|
[3631]: https://github.com/enso-org/enso/pull/3631
|
||||||
[3633]: https://github.com/enso-org/enso/pull/3633
|
[3633]: https://github.com/enso-org/enso/pull/3633
|
||||||
|
@ -27,6 +27,7 @@ transport formats, please look [here](./protocol-architecture).
|
|||||||
- [`ExpressionUpdate`](#expressionupdate)
|
- [`ExpressionUpdate`](#expressionupdate)
|
||||||
- [`ExpressionUpdatePayload`](#expressionupdatepayload)
|
- [`ExpressionUpdatePayload`](#expressionupdatepayload)
|
||||||
- [`VisualisationConfiguration`](#visualisationconfiguration)
|
- [`VisualisationConfiguration`](#visualisationconfiguration)
|
||||||
|
- [`VisualisationExpression`](#visualisationexpression)
|
||||||
- [`SuggestionEntryArgument`](#suggestionentryargument)
|
- [`SuggestionEntryArgument`](#suggestionentryargument)
|
||||||
- [`SuggestionEntry`](#suggestionentry)
|
- [`SuggestionEntry`](#suggestionentry)
|
||||||
- [`SuggestionEntryType`](#suggestionentrytype)
|
- [`SuggestionEntryType`](#suggestionentrytype)
|
||||||
@ -378,19 +379,17 @@ A configuration object for properties of the visualisation.
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface VisualisationConfiguration {
|
interface VisualisationConfiguration {
|
||||||
/**
|
/** An execution context of the visualisation. */
|
||||||
* An execution context of the visualisation.
|
|
||||||
*/
|
|
||||||
executionContextId: UUID;
|
executionContextId: UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A qualified name of the module containing the expression which creates
|
* A qualified name of the module containing the expression which creates
|
||||||
* visualisation.
|
* visualisation.
|
||||||
*/
|
*/
|
||||||
visualisationModule: String;
|
visualisationModule?: String;
|
||||||
/**
|
|
||||||
* The expression that creates a visualisation.
|
/** An expression that creates a visualisation. */
|
||||||
*/
|
expression: String | MethodPointer;
|
||||||
expression: String;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ final class ContextRegistry(
|
|||||||
Api.AttachVisualisation(
|
Api.AttachVisualisation(
|
||||||
visualisationId,
|
visualisationId,
|
||||||
expressionId,
|
expressionId,
|
||||||
convertVisualisationConfig(cfg)
|
cfg.toApi
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -263,7 +263,7 @@ final class ContextRegistry(
|
|||||||
Api.AttachVisualisation(
|
Api.AttachVisualisation(
|
||||||
visualisationId,
|
visualisationId,
|
||||||
expressionId,
|
expressionId,
|
||||||
convertVisualisationConfig(cfg)
|
cfg.toApi
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -301,25 +301,14 @@ final class ContextRegistry(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val configuration = convertVisualisationConfig(cfg)
|
|
||||||
|
|
||||||
handler.forward(
|
handler.forward(
|
||||||
Api.ModifyVisualisation(visualisationId, configuration)
|
Api.ModifyVisualisation(visualisationId, cfg.toApi)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
sender() ! AccessDenied
|
sender() ! AccessDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def convertVisualisationConfig(
|
|
||||||
config: VisualisationConfiguration
|
|
||||||
): Api.VisualisationConfiguration =
|
|
||||||
Api.VisualisationConfiguration(
|
|
||||||
executionContextId = config.executionContextId,
|
|
||||||
visualisationModule = config.visualisationModule,
|
|
||||||
expression = config.expression
|
|
||||||
)
|
|
||||||
|
|
||||||
private def getRuntimeStackItem(
|
private def getRuntimeStackItem(
|
||||||
stackItem: StackItem
|
stackItem: StackItem
|
||||||
): Api.StackItem =
|
): Api.StackItem =
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
package org.enso.languageserver.runtime
|
package org.enso.languageserver.runtime
|
||||||
|
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
|
|
||||||
/** An object pointing to a method definition.
|
/** An object pointing to a method definition.
|
||||||
*
|
*
|
||||||
* @param module the module of the method file
|
* @param module the module of the method file
|
||||||
* @param definedOnType method type
|
* @param definedOnType method type
|
||||||
* @param name method name
|
* @param name method name
|
||||||
*/
|
*/
|
||||||
case class MethodPointer(module: String, definedOnType: String, name: String)
|
case class MethodPointer(module: String, definedOnType: String, name: String) {
|
||||||
|
|
||||||
|
/** Convert to corresponding [[Api]] message. */
|
||||||
|
def toApi: Api.MethodPointer =
|
||||||
|
Api.MethodPointer(module, definedOnType, name)
|
||||||
|
}
|
||||||
|
@ -1,27 +1,201 @@
|
|||||||
package org.enso.languageserver.runtime
|
package org.enso.languageserver.runtime
|
||||||
|
|
||||||
import java.util.UUID
|
import io.circe.generic.auto._
|
||||||
|
import io.circe.syntax._
|
||||||
|
import io.circe.{Decoder, Encoder, Json}
|
||||||
|
import org.enso.logger.masking.ToLogString
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
|
|
||||||
import org.enso.logger.masking.{MaskedString, ToLogString}
|
import java.util.UUID
|
||||||
|
|
||||||
/** A configuration object for properties of the visualisation.
|
/** A configuration object for properties of the visualisation.
|
||||||
*
|
*
|
||||||
* @param executionContextId an execution context of the visualisation
|
* @param executionContextId an execution context of the visualisation
|
||||||
* @param visualisationModule a qualified name of the module containing
|
* @param expression an expression that creates a visualisation
|
||||||
* the expression which creates visualisation
|
|
||||||
* @param expression the expression that creates a visualisation
|
|
||||||
*/
|
*/
|
||||||
case class VisualisationConfiguration(
|
case class VisualisationConfiguration(
|
||||||
executionContextId: UUID,
|
executionContextId: UUID,
|
||||||
visualisationModule: String,
|
expression: VisualisationExpression
|
||||||
expression: String
|
|
||||||
) extends ToLogString {
|
) extends ToLogString {
|
||||||
|
|
||||||
|
/** A qualified module name containing the expression. */
|
||||||
|
def visualisationModule: String =
|
||||||
|
expression.module
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def toLogString(shouldMask: Boolean): String =
|
override def toLogString(shouldMask: Boolean): String =
|
||||||
"VisualisationConfiguration(" +
|
s"VisualisationConfiguration(" +
|
||||||
s"executionContextId=$executionContextId," +
|
s"executionContextId=$executionContextId," +
|
||||||
s"visualisationModule=$visualisationModule,expression=" +
|
s"expression=${expression.toLogString(shouldMask)})"
|
||||||
MaskedString(expression).toLogString(shouldMask) +
|
|
||||||
")"
|
/** Convert to corresponding [[Api]] message. */
|
||||||
|
def toApi: Api.VisualisationConfiguration =
|
||||||
|
Api.VisualisationConfiguration(
|
||||||
|
executionContextId = executionContextId,
|
||||||
|
expression = expression.toApi
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
object VisualisationConfiguration {
|
||||||
|
|
||||||
|
/** Create a visualisation configuration.
|
||||||
|
*
|
||||||
|
* @param contextId an execution context of the visualisation
|
||||||
|
* @param module a qualified module name containing the visualisation
|
||||||
|
* @param expression a visualisation expression
|
||||||
|
* @return an instance of [[VisualisationConfiguration]]
|
||||||
|
*/
|
||||||
|
def apply(
|
||||||
|
contextId: UUID,
|
||||||
|
module: String,
|
||||||
|
expression: String
|
||||||
|
): VisualisationConfiguration =
|
||||||
|
new VisualisationConfiguration(
|
||||||
|
contextId,
|
||||||
|
VisualisationExpression.Text(module, expression)
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Create a visualisation configuration.
|
||||||
|
*
|
||||||
|
* @param contextId an execution context of the visualisation
|
||||||
|
* @param expression a visualisation expression
|
||||||
|
* @return an instance of [[VisualisationConfiguration]]
|
||||||
|
*/
|
||||||
|
def apply(
|
||||||
|
contextId: UUID,
|
||||||
|
expression: MethodPointer
|
||||||
|
): VisualisationConfiguration =
|
||||||
|
new VisualisationConfiguration(
|
||||||
|
contextId,
|
||||||
|
VisualisationExpression.ModuleMethod(expression)
|
||||||
|
)
|
||||||
|
|
||||||
|
private object CodecField {
|
||||||
|
|
||||||
|
val Expression = "expression"
|
||||||
|
|
||||||
|
val ExecutionContextId = "executionContextId"
|
||||||
|
|
||||||
|
val VisualisationModule = "visualisationModule"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Json decoder that supports both old and new formats. */
|
||||||
|
implicit val decoder: Decoder[VisualisationConfiguration] =
|
||||||
|
Decoder.instance { cursor =>
|
||||||
|
cursor.downField(CodecField.Expression).as[String] match {
|
||||||
|
case Left(_) =>
|
||||||
|
for {
|
||||||
|
contextId <- cursor
|
||||||
|
.downField(CodecField.ExecutionContextId)
|
||||||
|
.as[UUID]
|
||||||
|
expression <- cursor
|
||||||
|
.downField(CodecField.Expression)
|
||||||
|
.as[MethodPointer]
|
||||||
|
} yield VisualisationConfiguration(contextId, expression)
|
||||||
|
|
||||||
|
case Right(expression) =>
|
||||||
|
for {
|
||||||
|
contextId <- cursor
|
||||||
|
.downField(CodecField.ExecutionContextId)
|
||||||
|
.as[UUID]
|
||||||
|
visualisationModule <- cursor
|
||||||
|
.downField(CodecField.VisualisationModule)
|
||||||
|
.as[String]
|
||||||
|
} yield VisualisationConfiguration(
|
||||||
|
contextId,
|
||||||
|
visualisationModule,
|
||||||
|
expression
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A visualisation expression. */
|
||||||
|
sealed trait VisualisationExpression extends ToLogString {
|
||||||
|
|
||||||
|
/** A qualified module name. */
|
||||||
|
def module: String
|
||||||
|
|
||||||
|
/** Convert to corresponding [[Api]] message. */
|
||||||
|
def toApi: Api.VisualisationExpression
|
||||||
|
}
|
||||||
|
object VisualisationExpression {
|
||||||
|
|
||||||
|
/** Visualization expression represented as a text.
|
||||||
|
*
|
||||||
|
* @param module a qualified module name containing the expression
|
||||||
|
* @param expression an expression that creates a visualization
|
||||||
|
*/
|
||||||
|
case class Text(module: String, expression: String)
|
||||||
|
extends VisualisationExpression {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toApi: Api.VisualisationExpression =
|
||||||
|
Api.VisualisationExpression.Text(module, expression)
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toLogString(shouldMask: Boolean): String =
|
||||||
|
s"Text(module=$module" +
|
||||||
|
s",expression=" +
|
||||||
|
(if (shouldMask) STUB else expression) +
|
||||||
|
")"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Visualization expression represented as a module method.
|
||||||
|
*
|
||||||
|
* @param methodPointer a pointer to a method definition
|
||||||
|
*/
|
||||||
|
case class ModuleMethod(methodPointer: MethodPointer)
|
||||||
|
extends VisualisationExpression {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override val module: String = methodPointer.module
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toApi: Api.VisualisationExpression =
|
||||||
|
Api.VisualisationExpression.ModuleMethod(methodPointer.toApi)
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toLogString(shouldMask: Boolean): String =
|
||||||
|
s"ModuleMethod(methodPointer=$methodPointer)"
|
||||||
|
}
|
||||||
|
|
||||||
|
private object CodecField {
|
||||||
|
|
||||||
|
val Type = "type"
|
||||||
|
}
|
||||||
|
|
||||||
|
private object PayloadType {
|
||||||
|
|
||||||
|
val Text = "Text"
|
||||||
|
|
||||||
|
val ModuleMethod = "ModuleMethod"
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val encoder: Encoder[VisualisationExpression] =
|
||||||
|
Encoder.instance[VisualisationExpression] {
|
||||||
|
case text: VisualisationExpression.Text =>
|
||||||
|
Encoder[VisualisationExpression.Text]
|
||||||
|
.apply(text)
|
||||||
|
.deepMerge(Json.obj(CodecField.Type -> PayloadType.Text.asJson))
|
||||||
|
|
||||||
|
case moduleMethod: VisualisationExpression.ModuleMethod =>
|
||||||
|
Encoder[VisualisationExpression.ModuleMethod]
|
||||||
|
.apply(moduleMethod)
|
||||||
|
.deepMerge(
|
||||||
|
Json.obj(CodecField.Type -> PayloadType.ModuleMethod.asJson)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val decoder: Decoder[VisualisationExpression] =
|
||||||
|
Decoder.instance { cursor =>
|
||||||
|
cursor.downField(CodecField.Type).as[String].flatMap {
|
||||||
|
case PayloadType.Text =>
|
||||||
|
Decoder[VisualisationExpression.Text].tryDecode(cursor)
|
||||||
|
|
||||||
|
case PayloadType.ModuleMethod =>
|
||||||
|
Decoder[VisualisationExpression.ModuleMethod].tryDecode(cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ package org.enso.languageserver.websocket.json
|
|||||||
|
|
||||||
import org.enso.polyglot.runtime.Runtime.Api
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
import io.circe.literal._
|
import io.circe.literal._
|
||||||
import org.enso.languageserver.runtime.VisualisationConfiguration
|
import org.enso.languageserver.runtime.{
|
||||||
|
VisualisationConfiguration,
|
||||||
|
VisualisationExpression
|
||||||
|
}
|
||||||
|
|
||||||
object ExecutionContextJsonMessages {
|
object ExecutionContextJsonMessages {
|
||||||
|
|
||||||
@ -110,7 +113,25 @@ object ExecutionContextJsonMessages {
|
|||||||
expressionId: Api.ExpressionId,
|
expressionId: Api.ExpressionId,
|
||||||
configuration: VisualisationConfiguration
|
configuration: VisualisationConfiguration
|
||||||
) =
|
) =
|
||||||
json"""
|
configuration.expression match {
|
||||||
|
case VisualisationExpression.Text(module, expression) =>
|
||||||
|
json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "executionContext/executeExpression",
|
||||||
|
"id": $reqId,
|
||||||
|
"params": {
|
||||||
|
"visualisationId": $visualisationId,
|
||||||
|
"expressionId": $expressionId,
|
||||||
|
"visualisationConfig": {
|
||||||
|
"executionContextId": ${configuration.executionContextId},
|
||||||
|
"visualisationModule": $module,
|
||||||
|
"expression": $expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
case VisualisationExpression.ModuleMethod(methodPointer) =>
|
||||||
|
json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"method": "executionContext/executeExpression",
|
"method": "executionContext/executeExpression",
|
||||||
"id": $reqId,
|
"id": $reqId,
|
||||||
@ -119,20 +140,26 @@ object ExecutionContextJsonMessages {
|
|||||||
"expressionId": $expressionId,
|
"expressionId": $expressionId,
|
||||||
"visualisationConfig": {
|
"visualisationConfig": {
|
||||||
"executionContextId": ${configuration.executionContextId},
|
"executionContextId": ${configuration.executionContextId},
|
||||||
"visualisationModule": ${configuration.visualisationModule},
|
"expression": {
|
||||||
"expression": ${configuration.expression}
|
"module": ${methodPointer.module},
|
||||||
|
"definedOnType": ${methodPointer.definedOnType},
|
||||||
|
"name": ${methodPointer.name}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
def executionContextAttachVisualisationRequest(
|
def executionContextAttachVisualisationRequest(
|
||||||
reqId: Int,
|
reqId: Int,
|
||||||
visualisationId: Api.VisualisationId,
|
visualisationId: Api.VisualisationId,
|
||||||
expressionId: Api.ExpressionId,
|
expressionId: Api.ExpressionId,
|
||||||
configuration: VisualisationConfiguration
|
configuration: VisualisationConfiguration
|
||||||
) =
|
) = {
|
||||||
json"""
|
configuration.expression match {
|
||||||
|
case VisualisationExpression.Text(module, expression) =>
|
||||||
|
json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"method": "executionContext/attachVisualisation",
|
"method": "executionContext/attachVisualisation",
|
||||||
"id": $reqId,
|
"id": $reqId,
|
||||||
@ -141,12 +168,33 @@ object ExecutionContextJsonMessages {
|
|||||||
"expressionId": $expressionId,
|
"expressionId": $expressionId,
|
||||||
"visualisationConfig": {
|
"visualisationConfig": {
|
||||||
"executionContextId": ${configuration.executionContextId},
|
"executionContextId": ${configuration.executionContextId},
|
||||||
"visualisationModule": ${configuration.visualisationModule},
|
"visualisationModule": $module,
|
||||||
"expression": ${configuration.expression}
|
"expression": $expression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
case VisualisationExpression.ModuleMethod(methodPointer) =>
|
||||||
|
json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "executionContext/attachVisualisation",
|
||||||
|
"id": $reqId,
|
||||||
|
"params": {
|
||||||
|
"visualisationId": $visualisationId,
|
||||||
|
"expressionId": $expressionId,
|
||||||
|
"visualisationConfig": {
|
||||||
|
"executionContextId": ${configuration.executionContextId},
|
||||||
|
"expression": {
|
||||||
|
"module": ${methodPointer.module},
|
||||||
|
"definedOnType": ${methodPointer.definedOnType},
|
||||||
|
"name": ${methodPointer.name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def executionContextModuleNotFound(
|
def executionContextModuleNotFound(
|
||||||
reqId: Int,
|
reqId: Int,
|
||||||
@ -215,8 +263,10 @@ object ExecutionContextJsonMessages {
|
|||||||
reqId: Int,
|
reqId: Int,
|
||||||
visualisationId: Api.VisualisationId,
|
visualisationId: Api.VisualisationId,
|
||||||
configuration: VisualisationConfiguration
|
configuration: VisualisationConfiguration
|
||||||
) =
|
) = {
|
||||||
json"""
|
configuration.expression match {
|
||||||
|
case VisualisationExpression.Text(module, expression) =>
|
||||||
|
json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"method": "executionContext/modifyVisualisation",
|
"method": "executionContext/modifyVisualisation",
|
||||||
"id": $reqId,
|
"id": $reqId,
|
||||||
@ -224,12 +274,32 @@ object ExecutionContextJsonMessages {
|
|||||||
"visualisationId": $visualisationId,
|
"visualisationId": $visualisationId,
|
||||||
"visualisationConfig": {
|
"visualisationConfig": {
|
||||||
"executionContextId": ${configuration.executionContextId},
|
"executionContextId": ${configuration.executionContextId},
|
||||||
"visualisationModule": ${configuration.visualisationModule},
|
"visualisationModule": $module,
|
||||||
"expression": ${configuration.expression}
|
"expression": $expression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
case VisualisationExpression.ModuleMethod(methodPointer) =>
|
||||||
|
json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "executionContext/modifyVisualisation",
|
||||||
|
"id": $reqId,
|
||||||
|
"params": {
|
||||||
|
"visualisationId": $visualisationId,
|
||||||
|
"visualisationConfig": {
|
||||||
|
"executionContextId": ${configuration.executionContextId},
|
||||||
|
"expression": {
|
||||||
|
"module": ${methodPointer.module},
|
||||||
|
"definedOnType": ${methodPointer.definedOnType},
|
||||||
|
"name": ${methodPointer.name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def executionContextGetComponentGroupsRequest(
|
def executionContextGetComponentGroupsRequest(
|
||||||
reqId: Int,
|
reqId: Int,
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package org.enso.languageserver.websocket.json
|
package org.enso.languageserver.websocket.json
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import io.circe.literal._
|
import io.circe.literal._
|
||||||
import org.enso.languageserver.runtime.VisualisationConfiguration
|
import org.enso.languageserver.runtime.{
|
||||||
|
MethodPointer,
|
||||||
|
VisualisationConfiguration
|
||||||
|
}
|
||||||
import org.enso.polyglot.runtime.Runtime.Api
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
import org.enso.text.editing.model
|
import org.enso.text.editing.model
|
||||||
|
|
||||||
@ -37,7 +39,60 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
config
|
config
|
||||||
)
|
)
|
||||||
) =>
|
) =>
|
||||||
config.expression shouldBe visualisationConfig.expression
|
config.expression shouldBe visualisationConfig.expression.toApi
|
||||||
|
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
||||||
|
config.executionContextId shouldBe visualisationConfig.executionContextId
|
||||||
|
requestId
|
||||||
|
|
||||||
|
case msg =>
|
||||||
|
fail(s"Unexpected message: $msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimeConnectorProbe.lastSender ! Api.Response(
|
||||||
|
requestId,
|
||||||
|
Api.VisualisationAttached()
|
||||||
|
)
|
||||||
|
client.expectJson(ExecutionContextJsonMessages.ok(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
"allow attaching method pointer as a visualisation expression" in {
|
||||||
|
val visualisationId = UUID.randomUUID()
|
||||||
|
val expressionId = UUID.randomUUID()
|
||||||
|
|
||||||
|
val client = getInitialisedWsClient()
|
||||||
|
val contextId = createExecutionContext(client)
|
||||||
|
val visualisationModule = "Foo.Bar"
|
||||||
|
val visualisationMethod = "baz"
|
||||||
|
val visualisationConfig =
|
||||||
|
VisualisationConfiguration(
|
||||||
|
contextId,
|
||||||
|
MethodPointer(
|
||||||
|
visualisationModule,
|
||||||
|
visualisationModule,
|
||||||
|
visualisationMethod
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
client.send(
|
||||||
|
ExecutionContextJsonMessages.executionContextAttachVisualisationRequest(
|
||||||
|
1,
|
||||||
|
visualisationId,
|
||||||
|
expressionId,
|
||||||
|
visualisationConfig
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val requestId =
|
||||||
|
runtimeConnectorProbe.receiveN(1).head match {
|
||||||
|
case Api.Request(
|
||||||
|
requestId,
|
||||||
|
Api.AttachVisualisation(
|
||||||
|
`visualisationId`,
|
||||||
|
`expressionId`,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
config.expression shouldBe visualisationConfig.expression.toApi
|
||||||
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
||||||
config.executionContextId shouldBe visualisationConfig.executionContextId
|
config.executionContextId shouldBe visualisationConfig.executionContextId
|
||||||
requestId
|
requestId
|
||||||
@ -106,7 +161,7 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
config
|
config
|
||||||
)
|
)
|
||||||
) =>
|
) =>
|
||||||
config.expression shouldBe visualisationConfig.expression
|
config.expression shouldBe visualisationConfig.expression.toApi
|
||||||
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
||||||
config.executionContextId shouldBe visualisationConfig.executionContextId
|
config.executionContextId shouldBe visualisationConfig.executionContextId
|
||||||
requestId
|
requestId
|
||||||
@ -155,7 +210,7 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
config
|
config
|
||||||
)
|
)
|
||||||
) =>
|
) =>
|
||||||
config.expression shouldBe visualisationConfig.expression
|
config.expression shouldBe visualisationConfig.expression.toApi
|
||||||
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
||||||
config.executionContextId shouldBe visualisationConfig.executionContextId
|
config.executionContextId shouldBe visualisationConfig.executionContextId
|
||||||
requestId
|
requestId
|
||||||
@ -220,17 +275,14 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
val expressionId = UUID.randomUUID()
|
val expressionId = UUID.randomUUID()
|
||||||
val client = getInitialisedWsClient()
|
val client = getInitialisedWsClient()
|
||||||
val contextId = createExecutionContext(client)
|
val contextId = createExecutionContext(client)
|
||||||
client.send(json"""
|
client.send(
|
||||||
{ "jsonrpc": "2.0",
|
ExecutionContextJsonMessages.executionContextDetachVisualisationRequest(
|
||||||
"method": "executionContext/detachVisualisation",
|
1,
|
||||||
"id": 1,
|
contextId,
|
||||||
"params": {
|
visualisationId,
|
||||||
"contextId": $contextId,
|
expressionId
|
||||||
"visualisationId": $visualisationId,
|
)
|
||||||
"expressionId": $expressionId
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
val requestId =
|
val requestId =
|
||||||
runtimeConnectorProbe.receiveN(1).head match {
|
runtimeConnectorProbe.receiveN(1).head match {
|
||||||
case Api.Request(
|
case Api.Request(
|
||||||
@ -259,17 +311,14 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
val expressionId = UUID.randomUUID()
|
val expressionId = UUID.randomUUID()
|
||||||
val contextId = UUID.randomUUID()
|
val contextId = UUID.randomUUID()
|
||||||
val client = getInitialisedWsClient()
|
val client = getInitialisedWsClient()
|
||||||
client.send(json"""
|
client.send(
|
||||||
{ "jsonrpc": "2.0",
|
ExecutionContextJsonMessages.executionContextDetachVisualisationRequest(
|
||||||
"method": "executionContext/detachVisualisation",
|
1,
|
||||||
"id": 1,
|
contextId,
|
||||||
"params": {
|
visualisationId,
|
||||||
"contextId": $contextId,
|
expressionId
|
||||||
"visualisationId": $visualisationId,
|
)
|
||||||
"expressionId": $expressionId
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
client.expectJson(json"""
|
client.expectJson(json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"id" : 1,
|
"id" : 1,
|
||||||
@ -291,20 +340,13 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
val contextId = createExecutionContext(client)
|
val contextId = createExecutionContext(client)
|
||||||
val visualisationConfig =
|
val visualisationConfig =
|
||||||
VisualisationConfiguration(contextId, "Foo.Bar.baz", "a=x+y")
|
VisualisationConfiguration(contextId, "Foo.Bar.baz", "a=x+y")
|
||||||
client.send(json"""
|
client.send(
|
||||||
{ "jsonrpc": "2.0",
|
ExecutionContextJsonMessages.executionContextModifyVisualisationRequest(
|
||||||
"method": "executionContext/modifyVisualisation",
|
1,
|
||||||
"id": 1,
|
visualisationId,
|
||||||
"params": {
|
visualisationConfig
|
||||||
"visualisationId": $visualisationId,
|
)
|
||||||
"visualisationConfig": {
|
)
|
||||||
"executionContextId": $contextId,
|
|
||||||
"visualisationModule": ${visualisationConfig.visualisationModule},
|
|
||||||
"expression": ${visualisationConfig.expression}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
|
|
||||||
val requestId =
|
val requestId =
|
||||||
runtimeConnectorProbe.receiveN(1).head match {
|
runtimeConnectorProbe.receiveN(1).head match {
|
||||||
@ -312,7 +354,7 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
requestId,
|
requestId,
|
||||||
Api.ModifyVisualisation(`visualisationId`, config)
|
Api.ModifyVisualisation(`visualisationId`, config)
|
||||||
) =>
|
) =>
|
||||||
config.expression shouldBe visualisationConfig.expression
|
config.expression shouldBe visualisationConfig.expression.toApi
|
||||||
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
config.visualisationModule shouldBe visualisationConfig.visualisationModule
|
||||||
config.executionContextId shouldBe visualisationConfig.executionContextId
|
config.executionContextId shouldBe visualisationConfig.executionContextId
|
||||||
requestId
|
requestId
|
||||||
@ -334,20 +376,14 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
val client = getInitialisedWsClient()
|
val client = getInitialisedWsClient()
|
||||||
val visualisationConfig =
|
val visualisationConfig =
|
||||||
VisualisationConfiguration(contextId, "Foo.Bar.baz", "a=x+y")
|
VisualisationConfiguration(contextId, "Foo.Bar.baz", "a=x+y")
|
||||||
client.send(json"""
|
|
||||||
{ "jsonrpc": "2.0",
|
client.send(
|
||||||
"method": "executionContext/modifyVisualisation",
|
ExecutionContextJsonMessages.executionContextModifyVisualisationRequest(
|
||||||
"id": 1,
|
1,
|
||||||
"params": {
|
visualisationId,
|
||||||
"visualisationId": $visualisationId,
|
visualisationConfig
|
||||||
"visualisationConfig": {
|
)
|
||||||
"executionContextId": $contextId,
|
)
|
||||||
"visualisationModule": ${visualisationConfig.visualisationModule},
|
|
||||||
"expression": ${visualisationConfig.expression}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
client.expectJson(json"""
|
client.expectJson(json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"id" : 1,
|
"id" : 1,
|
||||||
@ -358,7 +394,6 @@ class VisualisationOperationsTest extends BaseServerTest {
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def createExecutionContext(client: WsTestClient): UUID = {
|
private def createExecutionContext(client: WsTestClient): UUID = {
|
||||||
|
@ -477,26 +477,76 @@ object Runtime {
|
|||||||
expressionId: ExpressionId
|
expressionId: ExpressionId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** A visualization expression. */
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||||
|
@JsonSubTypes(
|
||||||
|
Array(
|
||||||
|
new JsonSubTypes.Type(
|
||||||
|
value = classOf[VisualisationExpression.Text],
|
||||||
|
name = "visualisationExpressionText"
|
||||||
|
),
|
||||||
|
new JsonSubTypes.Type(
|
||||||
|
value = classOf[VisualisationExpression.ModuleMethod],
|
||||||
|
name = "visualisationExpressionModuleMethod"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sealed trait VisualisationExpression extends ToLogString {
|
||||||
|
def module: String
|
||||||
|
}
|
||||||
|
object VisualisationExpression {
|
||||||
|
|
||||||
|
/** Visualization expression represented as a text.
|
||||||
|
*
|
||||||
|
* @param module a qualified module name containing the expression
|
||||||
|
* @param expression an expression that creates a visualization
|
||||||
|
*/
|
||||||
|
case class Text(module: String, expression: String)
|
||||||
|
extends VisualisationExpression {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toLogString(shouldMask: Boolean): String =
|
||||||
|
s"Text(module=$module" +
|
||||||
|
s",expression=" +
|
||||||
|
(if (shouldMask) STUB else expression) +
|
||||||
|
")"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Visualization expression represented as a module method.
|
||||||
|
*
|
||||||
|
* @param methodPointer a pointer to a method definition
|
||||||
|
*/
|
||||||
|
case class ModuleMethod(methodPointer: MethodPointer)
|
||||||
|
extends VisualisationExpression {
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override val module: String = methodPointer.module
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def toLogString(shouldMask: Boolean): String =
|
||||||
|
s"ModuleMethod(methodPointer=$methodPointer)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** A configuration object for properties of the visualisation.
|
/** A configuration object for properties of the visualisation.
|
||||||
*
|
*
|
||||||
* @param executionContextId an execution context of the visualisation
|
* @param executionContextId an execution context of the visualisation
|
||||||
* @param visualisationModule a qualified name of the module containing
|
|
||||||
* the expression which creates visualisation
|
|
||||||
* @param expression the expression that creates a visualisation
|
* @param expression the expression that creates a visualisation
|
||||||
*/
|
*/
|
||||||
case class VisualisationConfiguration(
|
case class VisualisationConfiguration(
|
||||||
executionContextId: ContextId,
|
executionContextId: ContextId,
|
||||||
visualisationModule: String,
|
expression: VisualisationExpression
|
||||||
expression: String
|
|
||||||
) extends ToLogString {
|
) extends ToLogString {
|
||||||
|
|
||||||
|
/** A qualified module name containing the expression. */
|
||||||
|
def visualisationModule: String =
|
||||||
|
expression.module
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def toLogString(shouldMask: Boolean): String =
|
override def toLogString(shouldMask: Boolean): String =
|
||||||
s"VisualisationConfiguration(" +
|
s"VisualisationConfiguration(" +
|
||||||
s"executionContextId=$executionContextId," +
|
s"executionContextId=$executionContextId," +
|
||||||
s"visualisationModule=$visualisationModule,expression=" +
|
s"expression=${expression.toLogString(shouldMask)})"
|
||||||
(if (shouldMask) STUB else expression) +
|
|
||||||
")"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An operation applied to the suggestion argument. */
|
/** An operation applied to the suggestion argument. */
|
||||||
|
@ -330,4 +330,5 @@ public class IdExecutionInstrument extends TruffleInstrument implements IdExecut
|
|||||||
onExceptionalCallback,
|
onExceptionalCallback,
|
||||||
timer));
|
timer));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -414,7 +414,60 @@ class RuntimeServerTest
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
it should "push method with default arguments" in {
|
it should "push method with default arguments on top of the stack" in {
|
||||||
|
val contextId = UUID.randomUUID()
|
||||||
|
val requestId = UUID.randomUUID()
|
||||||
|
val moduleName = "Enso_Test.Test.Main"
|
||||||
|
|
||||||
|
val metadata = new Metadata
|
||||||
|
val idFoo = metadata.addItem(35, 6)
|
||||||
|
|
||||||
|
val code =
|
||||||
|
"""import Standard.Base.IO
|
||||||
|
|
|
||||||
|
|foo x=0 = x + 42
|
||||||
|
|
|
||||||
|
|main =
|
||||||
|
| IO.println foo
|
||||||
|
|""".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 file
|
||||||
|
context.send(
|
||||||
|
Api.Request(Api.OpenFileNotification(mainFile, contents))
|
||||||
|
)
|
||||||
|
context.receiveNone shouldEqual None
|
||||||
|
|
||||||
|
// push main
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
requestId,
|
||||||
|
Api.PushContextRequest(
|
||||||
|
contextId,
|
||||||
|
Api.StackItem.ExplicitCall(
|
||||||
|
Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "foo"),
|
||||||
|
None,
|
||||||
|
Vector()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
context.receiveNIgnoreStdLib(3) should contain theSameElementsAs Seq(
|
||||||
|
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||||
|
TestMessages.update(contextId, idFoo, ConstantsGen.INTEGER),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
context.consumeOut shouldEqual List()
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "push method with default arguments on the stack" in {
|
||||||
val contextId = UUID.randomUUID()
|
val contextId = UUID.randomUUID()
|
||||||
val requestId = UUID.randomUUID()
|
val requestId = UUID.randomUUID()
|
||||||
val moduleName = "Enso_Test.Test.Main"
|
val moduleName = "Enso_Test.Test.Main"
|
||||||
@ -1013,7 +1066,9 @@ class RuntimeServerTest
|
|||||||
contextId,
|
contextId,
|
||||||
mainFoo,
|
mainFoo,
|
||||||
ConstantsGen.INTEGER,
|
ConstantsGen.INTEGER,
|
||||||
Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "foo")
|
Api
|
||||||
|
.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "foo"),
|
||||||
|
fromCache = true
|
||||||
),
|
),
|
||||||
context.executionComplete(contextId)
|
context.executionComplete(contextId)
|
||||||
)
|
)
|
||||||
|
@ -23,7 +23,7 @@ import java.util.UUID
|
|||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
|
|
||||||
@scala.annotation.nowarn("msg=multiarg infix syntax")
|
@scala.annotation.nowarn("msg=multiarg infix syntax")
|
||||||
class RuntimeVisualisationsTest
|
class RuntimeVisualizationsTest
|
||||||
extends AnyFlatSpec
|
extends AnyFlatSpec
|
||||||
with Matchers
|
with Matchers
|
||||||
with BeforeAndAfterEach {
|
with BeforeAndAfterEach {
|
||||||
@ -234,13 +234,43 @@ class RuntimeVisualisationsTest
|
|||||||
|
|
||||||
object Visualisation {
|
object Visualisation {
|
||||||
|
|
||||||
|
val metadata = new Metadata
|
||||||
|
|
||||||
val code =
|
val code =
|
||||||
"""
|
metadata.appendToCode(
|
||||||
|encode = x -> x.to_text
|
"""
|
||||||
|
|
|encode x = x.to_text
|
||||||
|incAndEncode = x -> encode x+1
|
|
|
||||||
|
|
|incAndEncode x =
|
||||||
|""".stripMargin
|
| y = x + 1
|
||||||
|
| encode y
|
||||||
|
|
|
||||||
|
|""".stripMargin.linesIterator.mkString("\n")
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object AnnotatedVisualisation {
|
||||||
|
|
||||||
|
val metadata = new Metadata
|
||||||
|
val idIncY = metadata.addItem(50, 5)
|
||||||
|
val idIncRes = metadata.addItem(66, 8)
|
||||||
|
val idIncMethod = metadata.addItem(25, 58)
|
||||||
|
|
||||||
|
val code =
|
||||||
|
metadata.appendToCode(
|
||||||
|
"""import Standard.Base.IO
|
||||||
|
|
|
||||||
|
|incAndEncode x =
|
||||||
|
| y = x + 1
|
||||||
|
| res = encode y
|
||||||
|
| res
|
||||||
|
|
|
||||||
|
|encode x =
|
||||||
|
| IO.println "encoding..."
|
||||||
|
| x.to_text
|
||||||
|
|""".stripMargin.linesIterator.mkString("\n")
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,8 +341,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMainRes,
|
idMainRes,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> encode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> encode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -423,8 +455,10 @@ class RuntimeVisualisationsTest
|
|||||||
context.Main.idMainX,
|
context.Main.idMainX,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> encode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> encode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -552,8 +586,10 @@ class RuntimeVisualisationsTest
|
|||||||
context.Main.idMainX,
|
context.Main.idMainX,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> encode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> encode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -675,8 +711,10 @@ class RuntimeVisualisationsTest
|
|||||||
context.Main.idMainZ,
|
context.Main.idMainZ,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"encode"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"encode"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -796,8 +834,10 @@ class RuntimeVisualisationsTest
|
|||||||
context.Main.idMainX,
|
context.Main.idMainX,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> encode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> encode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -832,8 +872,10 @@ class RuntimeVisualisationsTest
|
|||||||
visualisationId,
|
visualisationId,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> incAndEncode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> incAndEncode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -899,8 +941,10 @@ class RuntimeVisualisationsTest
|
|||||||
context.Main.idMainX,
|
context.Main.idMainX,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> encode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> encode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1052,8 +1096,10 @@ class RuntimeVisualisationsTest
|
|||||||
context.Main.idMainX,
|
context.Main.idMainX,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"encode"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"encode"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1157,8 +1203,10 @@ class RuntimeVisualisationsTest
|
|||||||
context.Main.idMainX,
|
context.Main.idMainX,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> encode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> encode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1193,8 +1241,10 @@ class RuntimeVisualisationsTest
|
|||||||
visualisationId,
|
visualisationId,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> incAndEncode x"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"x -> incAndEncode x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1282,8 +1332,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Test.Undefined",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> x"
|
"Test.Undefined",
|
||||||
|
"x -> x"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1342,8 +1394,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Standard.Visualization.Id",
|
Api.VisualisationExpression.Text(
|
||||||
"x -> x.default_visualization.to_text"
|
"Standard.Visualization.Id",
|
||||||
|
"x -> x.default_visualization.to_text"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1429,8 +1483,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Main",
|
Api.VisualisationExpression.Text(
|
||||||
"Main.does_not_exist"
|
"Enso_Test.Test.Main",
|
||||||
|
"Main.does_not_exist"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1503,8 +1559,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
moduleName,
|
Api.VisualisationExpression.Text(
|
||||||
"x -> x.visualise_me"
|
moduleName,
|
||||||
|
"x -> x.visualise_me"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1608,8 +1666,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
"Enso_Test.Test.Visualisation",
|
Api.VisualisationExpression.Text(
|
||||||
"inc_and_encode"
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"inc_and_encode"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1720,8 +1780,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
moduleName,
|
Api.VisualisationExpression.Text(
|
||||||
"x -> x.catch_primitive _.to_text"
|
moduleName,
|
||||||
|
"x -> x.catch_primitive _.to_text"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1806,8 +1868,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
moduleName,
|
Api.VisualisationExpression.Text(
|
||||||
"x -> Panic.catch_primitive x caught_panic-> caught_panic.payload.to_text"
|
moduleName,
|
||||||
|
"x -> Panic.catch_primitive x caught_panic-> caught_panic.payload.to_text"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1920,8 +1984,10 @@ class RuntimeVisualisationsTest
|
|||||||
idMain,
|
idMain,
|
||||||
Api.VisualisationConfiguration(
|
Api.VisualisationConfiguration(
|
||||||
contextId,
|
contextId,
|
||||||
visualisationModule,
|
Api.VisualisationExpression.Text(
|
||||||
visualisationCode
|
visualisationModule,
|
||||||
|
visualisationCode
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1948,4 +2014,284 @@ class RuntimeVisualisationsTest
|
|||||||
val stringified = new String(data)
|
val stringified = new String(data)
|
||||||
stringified shouldEqual """{ "kind": "Dataflow", "message": "The List is empty."}"""
|
stringified shouldEqual """{ "kind": "Dataflow", "message": "The List is empty."}"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "attach method pointer visualisation" in {
|
||||||
|
val idMainRes = context.Main.metadata.addItem(99, 1)
|
||||||
|
val contents = context.Main.code
|
||||||
|
val mainFile = context.writeMain(context.Main.code)
|
||||||
|
val visualisationFile =
|
||||||
|
context.writeInSrcDir("Visualisation", context.Visualisation.code)
|
||||||
|
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
Api.OpenFileNotification(
|
||||||
|
visualisationFile,
|
||||||
|
context.Visualisation.code
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val contextId = UUID.randomUUID()
|
||||||
|
val requestId = UUID.randomUUID()
|
||||||
|
val visualisationId = UUID.randomUUID()
|
||||||
|
|
||||||
|
// 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(Api.OpenFileNotification(mainFile, contents))
|
||||||
|
)
|
||||||
|
context.receiveNone shouldEqual None
|
||||||
|
|
||||||
|
// push main
|
||||||
|
val item1 = Api.StackItem.ExplicitCall(
|
||||||
|
Api.MethodPointer("Enso_Test.Test.Main", "Enso_Test.Test.Main", "main"),
|
||||||
|
None,
|
||||||
|
Vector()
|
||||||
|
)
|
||||||
|
context.send(
|
||||||
|
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||||
|
)
|
||||||
|
context.receiveNIgnoreStdLib(6) should contain theSameElementsAs Seq(
|
||||||
|
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||||
|
context.Main.Update.mainX(contextId),
|
||||||
|
context.Main.Update.mainY(contextId),
|
||||||
|
context.Main.Update.mainZ(contextId),
|
||||||
|
TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
|
||||||
|
// attach visualisation
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
requestId,
|
||||||
|
Api.AttachVisualisation(
|
||||||
|
visualisationId,
|
||||||
|
idMainRes,
|
||||||
|
Api.VisualisationConfiguration(
|
||||||
|
contextId,
|
||||||
|
Api.VisualisationExpression.ModuleMethod(
|
||||||
|
Api.MethodPointer(
|
||||||
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"incAndEncode"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val attachVisualisationResponses = context.receiveN(3)
|
||||||
|
attachVisualisationResponses should contain allOf (
|
||||||
|
Api.Response(requestId, Api.VisualisationAttached()),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
val Some(data) = attachVisualisationResponses.collectFirst {
|
||||||
|
case Api.Response(
|
||||||
|
None,
|
||||||
|
Api.VisualisationUpdate(
|
||||||
|
Api.VisualisationContext(
|
||||||
|
`visualisationId`,
|
||||||
|
`contextId`,
|
||||||
|
`idMainRes`
|
||||||
|
),
|
||||||
|
data
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
data
|
||||||
|
}
|
||||||
|
data.sameElements("51".getBytes) shouldBe true
|
||||||
|
|
||||||
|
// recompute
|
||||||
|
context.send(
|
||||||
|
Api.Request(requestId, Api.RecomputeContextRequest(contextId, None))
|
||||||
|
)
|
||||||
|
|
||||||
|
val recomputeResponses = context.receiveN(3)
|
||||||
|
recomputeResponses should contain allOf (
|
||||||
|
Api.Response(requestId, Api.RecomputeContextResponse(contextId)),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
val Some(data2) = recomputeResponses.collectFirst {
|
||||||
|
case Api.Response(
|
||||||
|
None,
|
||||||
|
Api.VisualisationUpdate(
|
||||||
|
Api.VisualisationContext(
|
||||||
|
`visualisationId`,
|
||||||
|
`contextId`,
|
||||||
|
`idMainRes`
|
||||||
|
),
|
||||||
|
data
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
data
|
||||||
|
}
|
||||||
|
data2.sameElements("51".getBytes) shouldBe true
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "cache intermediate visualization expressions" in {
|
||||||
|
val idMainRes = context.Main.metadata.addItem(99, 1)
|
||||||
|
val contents = context.Main.code
|
||||||
|
val mainFile = context.writeMain(context.Main.code)
|
||||||
|
val moduleName = "Enso_Test.Test.Main"
|
||||||
|
val visualisationFile =
|
||||||
|
context.writeInSrcDir(
|
||||||
|
"Visualisation",
|
||||||
|
context.AnnotatedVisualisation.code
|
||||||
|
)
|
||||||
|
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
Api.OpenFileNotification(
|
||||||
|
visualisationFile,
|
||||||
|
context.AnnotatedVisualisation.code
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val contextId = UUID.randomUUID()
|
||||||
|
val requestId = UUID.randomUUID()
|
||||||
|
val visualisationId = UUID.randomUUID()
|
||||||
|
|
||||||
|
// 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(Api.OpenFileNotification(mainFile, contents))
|
||||||
|
)
|
||||||
|
context.receiveNone shouldEqual None
|
||||||
|
|
||||||
|
// push main
|
||||||
|
val item1 = Api.StackItem.ExplicitCall(
|
||||||
|
Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"),
|
||||||
|
None,
|
||||||
|
Vector()
|
||||||
|
)
|
||||||
|
context.send(
|
||||||
|
Api.Request(requestId, Api.PushContextRequest(contextId, item1))
|
||||||
|
)
|
||||||
|
context.receiveNIgnoreStdLib(6) should contain theSameElementsAs Seq(
|
||||||
|
Api.Response(requestId, Api.PushContextResponse(contextId)),
|
||||||
|
context.Main.Update.mainX(contextId),
|
||||||
|
context.Main.Update.mainY(contextId),
|
||||||
|
context.Main.Update.mainZ(contextId),
|
||||||
|
TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
context.consumeOut shouldEqual List()
|
||||||
|
|
||||||
|
// attach visualisation
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
requestId,
|
||||||
|
Api.AttachVisualisation(
|
||||||
|
visualisationId,
|
||||||
|
idMainRes,
|
||||||
|
Api.VisualisationConfiguration(
|
||||||
|
contextId,
|
||||||
|
Api.VisualisationExpression.ModuleMethod(
|
||||||
|
Api.MethodPointer(
|
||||||
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"Enso_Test.Test.Visualisation",
|
||||||
|
"incAndEncode"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val attachVisualisationResponses = context.receiveN(3)
|
||||||
|
attachVisualisationResponses should contain allOf (
|
||||||
|
Api.Response(requestId, Api.VisualisationAttached()),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
val Some(data) = attachVisualisationResponses.collectFirst {
|
||||||
|
case Api.Response(
|
||||||
|
None,
|
||||||
|
Api.VisualisationUpdate(
|
||||||
|
Api.VisualisationContext(
|
||||||
|
`visualisationId`,
|
||||||
|
`contextId`,
|
||||||
|
`idMainRes`
|
||||||
|
),
|
||||||
|
data
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
data
|
||||||
|
}
|
||||||
|
data.sameElements("51".getBytes) shouldBe true
|
||||||
|
context.consumeOut shouldEqual List("encoding...")
|
||||||
|
|
||||||
|
// recompute
|
||||||
|
context.send(
|
||||||
|
Api.Request(requestId, Api.RecomputeContextRequest(contextId, None))
|
||||||
|
)
|
||||||
|
|
||||||
|
val recomputeResponses = context.receiveN(3)
|
||||||
|
recomputeResponses should contain allOf (
|
||||||
|
Api.Response(requestId, Api.RecomputeContextResponse(contextId)),
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
val Some(data2) = recomputeResponses.collectFirst {
|
||||||
|
case Api.Response(
|
||||||
|
None,
|
||||||
|
Api.VisualisationUpdate(
|
||||||
|
Api.VisualisationContext(
|
||||||
|
`visualisationId`,
|
||||||
|
`contextId`,
|
||||||
|
`idMainRes`
|
||||||
|
),
|
||||||
|
data
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
data
|
||||||
|
}
|
||||||
|
data2.sameElements("51".getBytes) shouldBe true
|
||||||
|
context.consumeOut shouldEqual List()
|
||||||
|
|
||||||
|
// Modify the visualization file
|
||||||
|
context.send(
|
||||||
|
Api.Request(
|
||||||
|
Api.EditFileNotification(
|
||||||
|
visualisationFile,
|
||||||
|
Seq(
|
||||||
|
TextEdit(
|
||||||
|
model.Range(model.Position(3, 12), model.Position(3, 13)),
|
||||||
|
"2"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
execute = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val editFileResponse = context.receiveN(2)
|
||||||
|
editFileResponse should contain(
|
||||||
|
context.executionComplete(contextId)
|
||||||
|
)
|
||||||
|
val Some(data3) = editFileResponse.collectFirst {
|
||||||
|
case Api.Response(
|
||||||
|
None,
|
||||||
|
Api.VisualisationUpdate(
|
||||||
|
Api.VisualisationContext(
|
||||||
|
`visualisationId`,
|
||||||
|
`contextId`,
|
||||||
|
`idMainRes`
|
||||||
|
),
|
||||||
|
data
|
||||||
|
)
|
||||||
|
) =>
|
||||||
|
data
|
||||||
|
}
|
||||||
|
data3.sameElements("52".getBytes) shouldBe true
|
||||||
|
context.consumeOut shouldEqual List("encoding...")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import com.oracle.truffle.api.source.SourceSection;
|
|||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,8 +93,13 @@ public class FunctionCallInstrumentationNode extends Node implements Instrumenta
|
|||||||
FunctionCall functionCall,
|
FunctionCall functionCall,
|
||||||
Object[] arguments,
|
Object[] arguments,
|
||||||
@Cached InteropApplicationNode interopApplicationNode) {
|
@Cached InteropApplicationNode interopApplicationNode) {
|
||||||
|
Object[] callArguments =
|
||||||
|
Arrays.copyOf(
|
||||||
|
functionCall.getArguments(), functionCall.getArguments().length + arguments.length);
|
||||||
|
System.arraycopy(
|
||||||
|
arguments, 0, callArguments, functionCall.getArguments().length, arguments.length);
|
||||||
return interopApplicationNode.execute(
|
return interopApplicationNode.execute(
|
||||||
functionCall.function, functionCall.state, functionCall.arguments);
|
functionCall.function, functionCall.state, callArguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.enso.interpreter.service;
|
package org.enso.interpreter.service;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.CallTarget;
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import com.oracle.truffle.api.TruffleLogger;
|
import com.oracle.truffle.api.TruffleLogger;
|
||||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
import com.oracle.truffle.api.instrumentation.EventBinding;
|
||||||
@ -48,6 +49,8 @@ import java.util.function.Consumer;
|
|||||||
* language.
|
* language.
|
||||||
*/
|
*/
|
||||||
public class ExecutionService {
|
public class ExecutionService {
|
||||||
|
|
||||||
|
private static final String MAIN_METHOD = "main";
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final Optional<IdExecutionService> idExecutionInstrument;
|
private final Optional<IdExecutionService> idExecutionInstrument;
|
||||||
private final NotificationHandler.Forwarder notificationForwarder;
|
private final NotificationHandler.Forwarder notificationForwarder;
|
||||||
@ -86,7 +89,7 @@ public class ExecutionService {
|
|||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FunctionCallInstrumentationNode.FunctionCall prepareFunctionCall(
|
public FunctionCallInstrumentationNode.FunctionCall prepareFunctionCall(
|
||||||
Module module, String consName, String methodName)
|
Module module, String consName, String methodName)
|
||||||
throws ConstructorNotFoundException, MethodNotFoundException {
|
throws ConstructorNotFoundException, MethodNotFoundException {
|
||||||
ModuleScope scope = module.compileScope(context);
|
ModuleScope scope = module.compileScope(context);
|
||||||
@ -99,8 +102,9 @@ public class ExecutionService {
|
|||||||
if (function == null) {
|
if (function == null) {
|
||||||
throw new MethodNotFoundException(module.getName().toString(), atomConstructor, methodName);
|
throw new MethodNotFoundException(module.getName().toString(), atomConstructor, methodName);
|
||||||
}
|
}
|
||||||
return new FunctionCallInstrumentationNode.FunctionCall(
|
Object[] arguments =
|
||||||
function, EmptyMap.create(), new Object[] {});
|
MAIN_METHOD.equals(methodName) ? new Object[] {} : new Object[] {atomConstructor};
|
||||||
|
return new FunctionCallInstrumentationNode.FunctionCall(function, EmptyMap.create(), arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initializeLanguageServerConnection(Endpoint endpoint) {
|
public void initializeLanguageServerConnection(Endpoint endpoint) {
|
||||||
@ -252,6 +256,55 @@ public class ExecutionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls a function with the given argument and attaching an execution instrument.
|
||||||
|
*
|
||||||
|
* @param module the module providing scope for the function
|
||||||
|
* @param function the function object
|
||||||
|
* @param argument the argument applied to the function
|
||||||
|
* @param cache the runtime cache
|
||||||
|
* @return the result of calling the function
|
||||||
|
*/
|
||||||
|
public Object callFunctionWithInstrument(
|
||||||
|
Module module, Object function, Object argument, RuntimeCache cache)
|
||||||
|
throws UnsupportedTypeException, ArityException, UnsupportedMessageException {
|
||||||
|
UUID nextExecutionItem = null;
|
||||||
|
CallTarget entryCallTarget =
|
||||||
|
(function instanceof Function) ? ((Function) function).getCallTarget() : null;
|
||||||
|
MethodCallsCache methodCallsCache = new MethodCallsCache();
|
||||||
|
UpdatesSynchronizationState syncState = new UpdatesSynchronizationState();
|
||||||
|
Consumer<IdExecutionService.ExpressionCall> funCallCallback =
|
||||||
|
(value) -> context.getLogger().finest("ON_CACHED_CALL " + value.getExpressionId());
|
||||||
|
Consumer<IdExecutionService.ExpressionValue> onComputedCallback =
|
||||||
|
(value) -> context.getLogger().finest("ON_COMPUTED " + value.getExpressionId());
|
||||||
|
Consumer<IdExecutionService.ExpressionValue> onCachedCallback =
|
||||||
|
(value) -> context.getLogger().finest("ON_CACHED_VALUE " + value.getExpressionId());
|
||||||
|
Consumer<Exception> onExceptionalCallback =
|
||||||
|
(value) -> context.getLogger().finest("ON_ERROR " + value);
|
||||||
|
|
||||||
|
Optional<EventBinding<ExecutionEventListener>> listener =
|
||||||
|
idExecutionInstrument.map(
|
||||||
|
service ->
|
||||||
|
service.bind(
|
||||||
|
module,
|
||||||
|
entryCallTarget,
|
||||||
|
cache,
|
||||||
|
methodCallsCache,
|
||||||
|
syncState,
|
||||||
|
nextExecutionItem,
|
||||||
|
funCallCallback,
|
||||||
|
onComputedCallback,
|
||||||
|
onCachedCallback,
|
||||||
|
onExceptionalCallback));
|
||||||
|
Object p = context.getThreadManager().enter();
|
||||||
|
try {
|
||||||
|
return interopLibrary.execute(function, argument);
|
||||||
|
} finally {
|
||||||
|
context.getThreadManager().leave(p);
|
||||||
|
listener.ifPresent(EventBinding::dispose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a module at a given path to use a literal source.
|
* Sets a module at a given path to use a literal source.
|
||||||
*
|
*
|
||||||
|
@ -29,7 +29,7 @@ object CacheInvalidation {
|
|||||||
sealed trait IndexSelector
|
sealed trait IndexSelector
|
||||||
object IndexSelector {
|
object IndexSelector {
|
||||||
|
|
||||||
/** Invalidate value from indexes. */
|
/** Invalidate value from all indexes. */
|
||||||
case object All extends IndexSelector
|
case object All extends IndexSelector
|
||||||
|
|
||||||
/** Invalidate the types index. */
|
/** Invalidate the types index. */
|
||||||
@ -118,6 +118,38 @@ object CacheInvalidation {
|
|||||||
): Unit =
|
): Unit =
|
||||||
instructions.foreach(run(stack, _))
|
instructions.foreach(run(stack, _))
|
||||||
|
|
||||||
|
/** Run a sequence of invalidation instructions on all visualisations.
|
||||||
|
*
|
||||||
|
* @param visualisations the list of available visualisations
|
||||||
|
* @param instructions the list of cache invalidation instructions
|
||||||
|
*/
|
||||||
|
def runAllVisualisations(
|
||||||
|
visualisations: Iterable[Visualisation],
|
||||||
|
instructions: Iterable[CacheInvalidation]
|
||||||
|
): Unit =
|
||||||
|
instructions.foreach { instruction =>
|
||||||
|
runVisualisations(
|
||||||
|
visualisations,
|
||||||
|
instruction.command,
|
||||||
|
instruction.indexes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run cache invalidation of a multiple visualisations
|
||||||
|
*
|
||||||
|
* @param visualisations visualisations cache should be invalidated
|
||||||
|
* @param command the invalidation instruction
|
||||||
|
* @param indexes the list of indexes to invalidate
|
||||||
|
*/
|
||||||
|
def runVisualisations(
|
||||||
|
visualisations: Iterable[Visualisation],
|
||||||
|
command: Command,
|
||||||
|
indexes: Set[IndexSelector] = Set()
|
||||||
|
): Unit =
|
||||||
|
visualisations.foreach { visualisation =>
|
||||||
|
run(visualisation.cache, command, indexes)
|
||||||
|
}
|
||||||
|
|
||||||
/** Run a cache invalidation instruction on an execution stack.
|
/** Run a cache invalidation instruction on an execution stack.
|
||||||
*
|
*
|
||||||
* @param stack the runtime stack
|
* @param stack the runtime stack
|
||||||
@ -148,6 +180,36 @@ object CacheInvalidation {
|
|||||||
frames.foreach(frame => run(frame.cache, frame.syncState, command, indexes))
|
frames.foreach(frame => run(frame.cache, frame.syncState, command, indexes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Run cache invalidation of a single instrument frame.
|
||||||
|
*
|
||||||
|
* @param cache the cache to invalidate
|
||||||
|
* @param command the invalidation instruction
|
||||||
|
* @param indexes the list of indexes to invalidate
|
||||||
|
*/
|
||||||
|
private def run(
|
||||||
|
cache: RuntimeCache,
|
||||||
|
command: Command,
|
||||||
|
indexes: Set[IndexSelector]
|
||||||
|
): Unit =
|
||||||
|
command match {
|
||||||
|
case Command.InvalidateAll =>
|
||||||
|
cache.clear()
|
||||||
|
indexes.foreach(clearIndex(_, cache))
|
||||||
|
case Command.InvalidateKeys(keys) =>
|
||||||
|
keys.foreach { key =>
|
||||||
|
cache.remove(key)
|
||||||
|
indexes.foreach(clearIndexKey(key, _, cache))
|
||||||
|
}
|
||||||
|
case Command.InvalidateStale(scope) =>
|
||||||
|
val staleKeys = cache.getKeys.asScala.diff(scope.toSet)
|
||||||
|
staleKeys.foreach { key =>
|
||||||
|
cache.remove(key)
|
||||||
|
indexes.foreach(clearIndexKey(key, _, cache))
|
||||||
|
}
|
||||||
|
case Command.SetMetadata(metadata) =>
|
||||||
|
cache.setWeights(metadata.asJavaWeights)
|
||||||
|
}
|
||||||
|
|
||||||
/** Run cache invalidation of a single instrument frame.
|
/** Run cache invalidation of a single instrument frame.
|
||||||
*
|
*
|
||||||
* @param cache the cache to invalidate
|
* @param cache the cache to invalidate
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.enso.interpreter.instrument
|
package org.enso.interpreter.instrument
|
||||||
|
|
||||||
|
import org.enso.pkg.QualifiedName
|
||||||
import org.enso.polyglot.runtime.Runtime.Api.{
|
import org.enso.polyglot.runtime.Runtime.Api.{
|
||||||
ContextId,
|
ContextId,
|
||||||
ExpressionId,
|
ExpressionId,
|
||||||
@ -7,7 +8,7 @@ import org.enso.polyglot.runtime.Runtime.Api.{
|
|||||||
VisualisationId
|
VisualisationId
|
||||||
}
|
}
|
||||||
|
|
||||||
import scala.collection.mutable.Stack
|
import scala.collection.mutable
|
||||||
|
|
||||||
/** Storage for active execution contexts.
|
/** Storage for active execution contexts.
|
||||||
*/
|
*/
|
||||||
@ -50,16 +51,17 @@ class ExecutionContextManager {
|
|||||||
* @param id the context id.
|
* @param id the context id.
|
||||||
* @return the stack.
|
* @return the stack.
|
||||||
*/
|
*/
|
||||||
def getStack(id: ContextId): Stack[InstrumentFrame] =
|
def getStack(id: ContextId): mutable.Stack[InstrumentFrame] =
|
||||||
synchronized {
|
synchronized {
|
||||||
contexts(id).stack
|
contexts(id).stack
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets all execution contexts.
|
/** Gets all execution contexts.
|
||||||
*
|
*
|
||||||
* @return all currently available execution contexsts.
|
* @return all currently available execution contexts.
|
||||||
*/
|
*/
|
||||||
def getAll: collection.MapView[ContextId, Stack[InstrumentFrame]] =
|
def getAllContexts
|
||||||
|
: collection.MapView[ContextId, mutable.Stack[InstrumentFrame]] =
|
||||||
synchronized {
|
synchronized {
|
||||||
contexts.view.mapValues(_.stack)
|
contexts.view.mapValues(_.stack)
|
||||||
}
|
}
|
||||||
@ -114,6 +116,22 @@ class ExecutionContextManager {
|
|||||||
state.visualisations.upsert(visualisation)
|
state.visualisations.upsert(visualisation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get visualizations of all execution contexts. */
|
||||||
|
def getAllVisualisations: Iterable[Visualisation] =
|
||||||
|
synchronized {
|
||||||
|
contexts.values.flatMap(_.visualisations.getAll)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get visualisations defined in the module.
|
||||||
|
*
|
||||||
|
* @param module the qualified module name
|
||||||
|
* @return the list of matching visualisations
|
||||||
|
*/
|
||||||
|
def getVisualisations(module: QualifiedName): Iterable[Visualisation] =
|
||||||
|
synchronized {
|
||||||
|
contexts.values.flatMap(_.visualisations.findByModule(module))
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns a visualisation with the provided id.
|
/** Returns a visualisation with the provided id.
|
||||||
*
|
*
|
||||||
* @param contextId the identifier of the execution context
|
* @param contextId the identifier of the execution context
|
||||||
@ -148,6 +166,25 @@ class ExecutionContextManager {
|
|||||||
} yield visualisation
|
} yield visualisation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get all visualisations invalidated by the provided list of expressions.
|
||||||
|
*
|
||||||
|
* @param module the module containing the visualisations
|
||||||
|
* @param invalidatedExpressions the list of invalidated expressions
|
||||||
|
* @return a list of matching visualisation
|
||||||
|
*/
|
||||||
|
def getInvalidatedVisualisations(
|
||||||
|
module: QualifiedName,
|
||||||
|
invalidatedExpressions: Set[ExpressionId]
|
||||||
|
): Iterable[Visualisation] = {
|
||||||
|
for {
|
||||||
|
state <- contexts.values
|
||||||
|
visualisation <- state.visualisations.findByModule(module)
|
||||||
|
if visualisation.visualisationExpressionId.exists(
|
||||||
|
invalidatedExpressions.contains
|
||||||
|
)
|
||||||
|
} yield visualisation
|
||||||
|
}
|
||||||
|
|
||||||
/** Removes a visualisation from the holder.
|
/** Removes a visualisation from the holder.
|
||||||
*
|
*
|
||||||
* @param contextId the identifier of the execution context
|
* @param contextId the identifier of the execution context
|
||||||
|
@ -37,6 +37,11 @@ case class InstrumentFrame(
|
|||||||
|
|
||||||
case object InstrumentFrame {
|
case object InstrumentFrame {
|
||||||
|
|
||||||
|
/** Create an instrument frame.
|
||||||
|
*
|
||||||
|
* @param item the stack item
|
||||||
|
* @return an instance of [[InstrumentFrame]]
|
||||||
|
*/
|
||||||
def apply(item: StackItem): InstrumentFrame =
|
def apply(item: StackItem): InstrumentFrame =
|
||||||
new InstrumentFrame(item, new RuntimeCache, new UpdatesSynchronizationState)
|
new InstrumentFrame(item, new RuntimeCache, new UpdatesSynchronizationState)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package org.enso.interpreter.instrument
|
package org.enso.interpreter.instrument
|
||||||
|
|
||||||
import org.enso.polyglot.runtime.Runtime.Api.{ExpressionId, VisualisationId}
|
import org.enso.interpreter.runtime.Module
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api.{
|
||||||
|
ExpressionId,
|
||||||
|
VisualisationConfiguration,
|
||||||
|
VisualisationId
|
||||||
|
}
|
||||||
|
|
||||||
/** An object containing visualisation data.
|
/** An object containing visualisation data.
|
||||||
*
|
*
|
||||||
@ -12,5 +17,9 @@ import org.enso.polyglot.runtime.Runtime.Api.{ExpressionId, VisualisationId}
|
|||||||
case class Visualisation(
|
case class Visualisation(
|
||||||
id: VisualisationId,
|
id: VisualisationId,
|
||||||
expressionId: ExpressionId,
|
expressionId: ExpressionId,
|
||||||
|
cache: RuntimeCache,
|
||||||
|
module: Module,
|
||||||
|
config: VisualisationConfiguration,
|
||||||
|
visualisationExpressionId: Option[ExpressionId],
|
||||||
callback: AnyRef
|
callback: AnyRef
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package org.enso.interpreter.instrument
|
package org.enso.interpreter.instrument
|
||||||
|
|
||||||
|
import org.enso.pkg.QualifiedName
|
||||||
import org.enso.polyglot.runtime.Runtime.Api.{ExpressionId, VisualisationId}
|
import org.enso.polyglot.runtime.Runtime.Api.{ExpressionId, VisualisationId}
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
/** A mutable holder of all visualisations attached to an execution context.
|
/** A mutable holder of all visualisations attached to an execution context.
|
||||||
*/
|
*/
|
||||||
class VisualisationHolder() {
|
class VisualisationHolder() {
|
||||||
|
|
||||||
private var visualisationMap: Map[ExpressionId, List[Visualisation]] =
|
private val visualisationMap: mutable.Map[ExpressionId, List[Visualisation]] =
|
||||||
Map.empty.withDefaultValue(List.empty)
|
mutable.Map.empty.withDefaultValue(List.empty)
|
||||||
|
|
||||||
/** Upserts a visualisation.
|
/** Upserts a visualisation.
|
||||||
*
|
*
|
||||||
@ -15,8 +18,8 @@ class VisualisationHolder() {
|
|||||||
*/
|
*/
|
||||||
def upsert(visualisation: Visualisation): Unit = {
|
def upsert(visualisation: Visualisation): Unit = {
|
||||||
val visualisations = visualisationMap(visualisation.expressionId)
|
val visualisations = visualisationMap(visualisation.expressionId)
|
||||||
val removed = visualisations.filterNot(_.id == visualisation.id)
|
val rest = visualisations.filterNot(_.id == visualisation.id)
|
||||||
visualisationMap += (visualisation.expressionId -> (visualisation :: removed))
|
visualisationMap.update(visualisation.expressionId, visualisation :: rest)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes a visualisation from the holder.
|
/** Removes a visualisation from the holder.
|
||||||
@ -30,8 +33,8 @@ class VisualisationHolder() {
|
|||||||
expressionId: ExpressionId
|
expressionId: ExpressionId
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val visualisations = visualisationMap(expressionId)
|
val visualisations = visualisationMap(expressionId)
|
||||||
val removed = visualisations.filterNot(_.id == visualisationId)
|
val rest = visualisations.filterNot(_.id == visualisationId)
|
||||||
visualisationMap += (expressionId -> removed)
|
visualisationMap.update(expressionId, rest)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Finds all visualisations attached to an expression.
|
/** Finds all visualisations attached to an expression.
|
||||||
@ -42,6 +45,14 @@ class VisualisationHolder() {
|
|||||||
def find(expressionId: ExpressionId): List[Visualisation] =
|
def find(expressionId: ExpressionId): List[Visualisation] =
|
||||||
visualisationMap(expressionId)
|
visualisationMap(expressionId)
|
||||||
|
|
||||||
|
/** Finds all visualisations in a given module.
|
||||||
|
*
|
||||||
|
* @param module the qualified module name
|
||||||
|
* @return a list of matching visualisation
|
||||||
|
*/
|
||||||
|
def findByModule(module: QualifiedName): Iterable[Visualisation] =
|
||||||
|
visualisationMap.values.flatten.filter(_.module.getName == module)
|
||||||
|
|
||||||
/** Returns a visualisation with the provided id.
|
/** Returns a visualisation with the provided id.
|
||||||
*
|
*
|
||||||
* @param visualisationId the identifier of visualisation
|
* @param visualisationId the identifier of visualisation
|
||||||
@ -50,12 +61,14 @@ class VisualisationHolder() {
|
|||||||
def getById(visualisationId: VisualisationId): Option[Visualisation] =
|
def getById(visualisationId: VisualisationId): Option[Visualisation] =
|
||||||
visualisationMap.values.flatten.find(_.id == visualisationId)
|
visualisationMap.values.flatten.find(_.id == visualisationId)
|
||||||
|
|
||||||
|
/** @return all available visualisations. */
|
||||||
|
def getAll: Iterable[Visualisation] =
|
||||||
|
visualisationMap.values.flatten
|
||||||
}
|
}
|
||||||
|
|
||||||
object VisualisationHolder {
|
object VisualisationHolder {
|
||||||
|
|
||||||
/** Returns an empty holder.
|
/** Returns an empty visualisation holder. */
|
||||||
*/
|
|
||||||
def empty = new VisualisationHolder()
|
def empty = new VisualisationHolder()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,10 +49,10 @@ class AttachVisualisationCmd(
|
|||||||
ctx.jobProcessor.run(
|
ctx.jobProcessor.run(
|
||||||
new UpsertVisualisationJob(
|
new UpsertVisualisationJob(
|
||||||
maybeRequestId,
|
maybeRequestId,
|
||||||
|
Api.VisualisationAttached(),
|
||||||
request.visualisationId,
|
request.visualisationId,
|
||||||
request.expressionId,
|
request.expressionId,
|
||||||
request.visualisationConfig,
|
request.visualisationConfig
|
||||||
Api.VisualisationAttached()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class EditFileCmd(request: Api.EditFileNotification) extends Command(None) {
|
|||||||
private def executeJobs(implicit
|
private def executeJobs(implicit
|
||||||
ctx: RuntimeContext
|
ctx: RuntimeContext
|
||||||
): Iterable[ExecuteJob] = {
|
): Iterable[ExecuteJob] = {
|
||||||
ctx.contextManager.getAll
|
ctx.contextManager.getAllContexts
|
||||||
.collect {
|
.collect {
|
||||||
case (contextId, stack) if stack.nonEmpty =>
|
case (contextId, stack) if stack.nonEmpty =>
|
||||||
new ExecuteJob(contextId, stack.toList)
|
new ExecuteJob(contextId, stack.toList)
|
||||||
|
@ -60,10 +60,10 @@ class ModifyVisualisationCmd(
|
|||||||
ctx.jobProcessor.run(
|
ctx.jobProcessor.run(
|
||||||
new UpsertVisualisationJob(
|
new UpsertVisualisationJob(
|
||||||
maybeRequestId,
|
maybeRequestId,
|
||||||
|
Api.VisualisationModified(),
|
||||||
request.visualisationId,
|
request.visualisationId,
|
||||||
visualisation.expressionId,
|
visualisation.expressionId,
|
||||||
request.visualisationConfig,
|
request.visualisationConfig
|
||||||
Api.VisualisationModified()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
maybeFutureExecutable flatMap {
|
maybeFutureExecutable flatMap {
|
||||||
|
@ -39,7 +39,7 @@ class RenameProjectCmd(
|
|||||||
request.oldName,
|
request.oldName,
|
||||||
request.newName
|
request.newName
|
||||||
)
|
)
|
||||||
ctx.contextManager.getAll.values
|
ctx.contextManager.getAllContexts.values
|
||||||
.foreach(updateMethodPointers(request.newName, _))
|
.foreach(updateMethodPointers(request.newName, _))
|
||||||
reply(Api.ProjectRenamed(request.namespace, request.newName))
|
reply(Api.ProjectRenamed(request.namespace, request.newName))
|
||||||
logger.log(
|
logger.log(
|
||||||
|
@ -47,7 +47,7 @@ class SetExpressionValueCmd(request: Api.SetExpressionValueNotification)
|
|||||||
private def executeJobs(implicit
|
private def executeJobs(implicit
|
||||||
ctx: RuntimeContext
|
ctx: RuntimeContext
|
||||||
): Iterable[ExecuteJob] = {
|
): Iterable[ExecuteJob] = {
|
||||||
ctx.contextManager.getAll
|
ctx.contextManager.getAllContexts
|
||||||
.collect {
|
.collect {
|
||||||
case (contextId, stack) if stack.nonEmpty =>
|
case (contextId, stack) if stack.nonEmpty =>
|
||||||
new ExecuteJob(contextId, stack.toList)
|
new ExecuteJob(contextId, stack.toList)
|
||||||
|
@ -12,10 +12,16 @@ import org.enso.interpreter.instrument.execution.{
|
|||||||
LocationResolver,
|
LocationResolver,
|
||||||
RuntimeContext
|
RuntimeContext
|
||||||
}
|
}
|
||||||
import org.enso.interpreter.instrument.{CacheInvalidation, InstrumentFrame}
|
import org.enso.interpreter.instrument.{
|
||||||
|
CacheInvalidation,
|
||||||
|
InstrumentFrame,
|
||||||
|
Visualisation
|
||||||
|
}
|
||||||
import org.enso.interpreter.runtime.Module
|
import org.enso.interpreter.runtime.Module
|
||||||
import org.enso.interpreter.service.error.ModuleNotFoundForFileException
|
import org.enso.interpreter.service.error.ModuleNotFoundForFileException
|
||||||
|
import org.enso.pkg.QualifiedName
|
||||||
import org.enso.polyglot.runtime.Runtime.Api
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api.StackItem
|
||||||
import org.enso.text.buffer.Rope
|
import org.enso.text.buffer.Rope
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -39,17 +45,7 @@ final class EnsureCompiledJob(protected val files: Iterable[File])
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
val compilationResult = ensureCompiledFiles(files)
|
val compilationResult = ensureCompiledFiles(files)
|
||||||
ctx.contextManager.getAll.values.foreach { stack =>
|
setCacheWeights()
|
||||||
getCacheMetadata(stack).foreach { metadata =>
|
|
||||||
CacheInvalidation.run(
|
|
||||||
stack,
|
|
||||||
CacheInvalidation(
|
|
||||||
CacheInvalidation.StackSelector.Top,
|
|
||||||
CacheInvalidation.Command.SetMetadata(metadata)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compilationResult
|
compilationResult
|
||||||
} finally {
|
} finally {
|
||||||
ctx.locking.releaseWriteCompilationLock()
|
ctx.locking.releaseWriteCompilationLock()
|
||||||
@ -89,12 +85,7 @@ final class EnsureCompiledJob(protected val files: Iterable[File])
|
|||||||
applyEdits(new File(module.getPath)).map { changeset =>
|
applyEdits(new File(module.getPath)).map { changeset =>
|
||||||
compile(module)
|
compile(module)
|
||||||
.map { compilerResult =>
|
.map { compilerResult =>
|
||||||
val cacheInvalidationCommands =
|
invalidateCaches(module, changeset)
|
||||||
buildCacheInvalidationCommands(
|
|
||||||
changeset,
|
|
||||||
module.getSource.getCharacters
|
|
||||||
)
|
|
||||||
runInvalidationCommands(cacheInvalidationCommands)
|
|
||||||
ctx.jobProcessor.runBackground(
|
ctx.jobProcessor.runBackground(
|
||||||
AnalyzeModuleInScopeJob(
|
AnalyzeModuleInScopeJob(
|
||||||
module.getName,
|
module.getName,
|
||||||
@ -301,18 +292,44 @@ final class EnsureCompiledJob(protected val files: Iterable[File])
|
|||||||
|
|
||||||
/** Run the invalidation commands.
|
/** Run the invalidation commands.
|
||||||
*
|
*
|
||||||
* @param invalidationCommands the invalidation command to run
|
* @param module the compiled module
|
||||||
|
* @param changeset the changeset containing the list of invalidated expressions
|
||||||
* @param ctx the runtime context
|
* @param ctx the runtime context
|
||||||
*/
|
*/
|
||||||
private def runInvalidationCommands(
|
private def invalidateCaches(
|
||||||
invalidationCommands: Iterable[CacheInvalidation]
|
module: Module,
|
||||||
|
changeset: Changeset[_]
|
||||||
)(implicit ctx: RuntimeContext): Unit = {
|
)(implicit ctx: RuntimeContext): Unit = {
|
||||||
ctx.contextManager.getAll.values
|
val invalidationCommands =
|
||||||
|
buildCacheInvalidationCommands(
|
||||||
|
changeset,
|
||||||
|
module.getSource.getCharacters
|
||||||
|
)
|
||||||
|
ctx.contextManager.getAllContexts.values
|
||||||
.foreach { stack =>
|
.foreach { stack =>
|
||||||
if (stack.nonEmpty) {
|
if (stack.nonEmpty && isStackInModule(module.getName, stack)) {
|
||||||
CacheInvalidation.runAll(stack, invalidationCommands)
|
CacheInvalidation.runAll(stack, invalidationCommands)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CacheInvalidation.runAllVisualisations(
|
||||||
|
ctx.contextManager.getVisualisations(module.getName),
|
||||||
|
invalidationCommands
|
||||||
|
)
|
||||||
|
|
||||||
|
val invalidatedVisualisations =
|
||||||
|
ctx.contextManager.getInvalidatedVisualisations(
|
||||||
|
module.getName,
|
||||||
|
changeset.invalidated
|
||||||
|
)
|
||||||
|
invalidatedVisualisations.foreach { visualisation =>
|
||||||
|
UpsertVisualisationJob.upsertVisualisation(visualisation)
|
||||||
|
}
|
||||||
|
if (invalidatedVisualisations.nonEmpty) {
|
||||||
|
ctx.executionService.getLogger.log(
|
||||||
|
Level.FINE,
|
||||||
|
s"Invalidated visualisations [${invalidatedVisualisations.map(_.id)}]"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send notification about the compilation status.
|
/** Send notification about the compilation status.
|
||||||
@ -325,7 +342,7 @@ final class EnsureCompiledJob(protected val files: Iterable[File])
|
|||||||
diagnostics: Seq[Api.ExecutionResult.Diagnostic]
|
diagnostics: Seq[Api.ExecutionResult.Diagnostic]
|
||||||
)(implicit ctx: RuntimeContext): Unit =
|
)(implicit ctx: RuntimeContext): Unit =
|
||||||
if (diagnostics.nonEmpty) {
|
if (diagnostics.nonEmpty) {
|
||||||
ctx.contextManager.getAll.keys.foreach { contextId =>
|
ctx.contextManager.getAllContexts.keys.foreach { contextId =>
|
||||||
ctx.endpoint.sendToClient(
|
ctx.endpoint.sendToClient(
|
||||||
Api.Response(Api.ExecutionUpdate(contextId, diagnostics))
|
Api.Response(Api.ExecutionUpdate(contextId, diagnostics))
|
||||||
)
|
)
|
||||||
@ -340,7 +357,7 @@ final class EnsureCompiledJob(protected val files: Iterable[File])
|
|||||||
private def sendFailureUpdate(
|
private def sendFailureUpdate(
|
||||||
failure: Api.ExecutionResult.Failure
|
failure: Api.ExecutionResult.Failure
|
||||||
)(implicit ctx: RuntimeContext): Unit =
|
)(implicit ctx: RuntimeContext): Unit =
|
||||||
ctx.contextManager.getAll.keys.foreach { contextId =>
|
ctx.contextManager.getAllContexts.keys.foreach { contextId =>
|
||||||
ctx.endpoint.sendToClient(
|
ctx.endpoint.sendToClient(
|
||||||
Api.Response(Api.ExecutionFailed(contextId, failure))
|
Api.Response(Api.ExecutionFailed(contextId, failure))
|
||||||
)
|
)
|
||||||
@ -354,6 +371,27 @@ final class EnsureCompiledJob(protected val files: Iterable[File])
|
|||||||
else
|
else
|
||||||
CompilationStatus.Success
|
CompilationStatus.Success
|
||||||
|
|
||||||
|
private def setCacheWeights()(implicit ctx: RuntimeContext): Unit = {
|
||||||
|
ctx.contextManager.getAllContexts.values.foreach { stack =>
|
||||||
|
getCacheMetadata(stack).foreach { metadata =>
|
||||||
|
CacheInvalidation.run(
|
||||||
|
stack,
|
||||||
|
CacheInvalidation(
|
||||||
|
CacheInvalidation.StackSelector.Top,
|
||||||
|
CacheInvalidation.Command.SetMetadata(metadata)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val visualisations = ctx.contextManager.getAllVisualisations
|
||||||
|
visualisations.flatMap(getCacheMetadata).foreach { metadata =>
|
||||||
|
CacheInvalidation.runVisualisations(
|
||||||
|
visualisations,
|
||||||
|
CacheInvalidation.Command.SetMetadata(metadata)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def getCacheMetadata(
|
private def getCacheMetadata(
|
||||||
stack: Iterable[InstrumentFrame]
|
stack: Iterable[InstrumentFrame]
|
||||||
)(implicit ctx: RuntimeContext): Option[CachePreferenceAnalysis.Metadata] =
|
)(implicit ctx: RuntimeContext): Option[CachePreferenceAnalysis.Metadata] =
|
||||||
@ -370,12 +408,37 @@ final class EnsureCompiledJob(protected val files: Iterable[File])
|
|||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def getCacheMetadata(
|
||||||
|
visualisation: Visualisation
|
||||||
|
): Option[CachePreferenceAnalysis.Metadata] = {
|
||||||
|
val module = visualisation.module
|
||||||
|
module.getIr.getMetadata(CachePreferenceAnalysis)
|
||||||
|
}
|
||||||
|
|
||||||
/** Get all modules in the current compiler scope. */
|
/** Get all modules in the current compiler scope. */
|
||||||
private def getModulesInScope(implicit
|
private def getModulesInScope(implicit
|
||||||
ctx: RuntimeContext
|
ctx: RuntimeContext
|
||||||
): Iterable[Module] =
|
): Iterable[Module] =
|
||||||
ctx.executionService.getContext.getTopScope.getModules.asScala
|
ctx.executionService.getContext.getTopScope.getModules.asScala
|
||||||
|
|
||||||
|
/** Check if stack belongs to the provided module.
|
||||||
|
*
|
||||||
|
* @param module the qualified module name
|
||||||
|
* @param stack the execution stack
|
||||||
|
*/
|
||||||
|
private def isStackInModule(
|
||||||
|
module: QualifiedName,
|
||||||
|
stack: Iterable[InstrumentFrame]
|
||||||
|
): Boolean =
|
||||||
|
stack.headOption match {
|
||||||
|
case Some(
|
||||||
|
InstrumentFrame(StackItem.ExplicitCall(methodPointer, _, _), _, _)
|
||||||
|
) =>
|
||||||
|
methodPointer.module == module.toString
|
||||||
|
case _ =>
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object EnsureCompiledJob {
|
object EnsureCompiledJob {
|
||||||
|
@ -429,12 +429,14 @@ object ProgramExecutionSupport {
|
|||||||
Either
|
Either
|
||||||
.catchNonFatal {
|
.catchNonFatal {
|
||||||
ctx.executionService.getLogger.log(
|
ctx.executionService.getLogger.log(
|
||||||
Level.FINEST,
|
Level.FINE,
|
||||||
s"Executing visualisation ${visualisation.expressionId}"
|
s"Executing visualisation ${visualisation.expressionId}"
|
||||||
)
|
)
|
||||||
ctx.executionService.callFunction(
|
ctx.executionService.callFunctionWithInstrument(
|
||||||
|
visualisation.module,
|
||||||
visualisation.callback,
|
visualisation.callback,
|
||||||
expressionValue
|
expressionValue,
|
||||||
|
visualisation.cache
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.flatMap {
|
.flatMap {
|
||||||
|
@ -1,42 +1,45 @@
|
|||||||
package org.enso.interpreter.instrument.job
|
package org.enso.interpreter.instrument.job
|
||||||
|
|
||||||
import java.util.logging.Level
|
|
||||||
|
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import org.enso.interpreter.instrument.{InstrumentFrame, Visualisation}
|
import org.enso.compiler.core.IR
|
||||||
|
import org.enso.compiler.pass.analyse.CachePreferenceAnalysis
|
||||||
import org.enso.interpreter.instrument.execution.{Executable, RuntimeContext}
|
import org.enso.interpreter.instrument.execution.{Executable, RuntimeContext}
|
||||||
import org.enso.interpreter.instrument.job.UpsertVisualisationJob.{
|
import org.enso.interpreter.instrument.job.UpsertVisualisationJob.{
|
||||||
EvalFailure,
|
|
||||||
EvaluationFailed,
|
EvaluationFailed,
|
||||||
MaxEvaluationRetryCount,
|
EvaluationResult,
|
||||||
ModuleNotFound
|
ModuleNotFound
|
||||||
}
|
}
|
||||||
|
import org.enso.interpreter.instrument.{
|
||||||
|
CacheInvalidation,
|
||||||
|
InstrumentFrame,
|
||||||
|
RuntimeCache,
|
||||||
|
Visualisation
|
||||||
|
}
|
||||||
import org.enso.interpreter.runtime.Module
|
import org.enso.interpreter.runtime.Module
|
||||||
import org.enso.interpreter.runtime.control.ThreadInterruptedException
|
import org.enso.interpreter.runtime.control.ThreadInterruptedException
|
||||||
import org.enso.polyglot.runtime.Runtime.Api.{
|
import org.enso.pkg.QualifiedName
|
||||||
ExpressionId,
|
import org.enso.polyglot.runtime.Runtime.Api._
|
||||||
RequestId,
|
|
||||||
VisualisationId
|
|
||||||
}
|
|
||||||
import org.enso.polyglot.runtime.Runtime.{Api, ApiResponse}
|
import org.enso.polyglot.runtime.Runtime.{Api, ApiResponse}
|
||||||
|
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
/** A job that upserts a visualisation.
|
/** A job that upserts a visualisation.
|
||||||
*
|
*
|
||||||
* @param requestId maybe a request id
|
* @param requestId maybe a request id
|
||||||
|
* @param response a response used to reply to a client
|
||||||
* @param visualisationId an identifier of visualisation
|
* @param visualisationId an identifier of visualisation
|
||||||
* @param expressionId an identifier of expression
|
* @param expressionId an identifier of expression
|
||||||
* @param config a visualisation config
|
* @param config a visualisation config
|
||||||
* @param response a response used to reply to a client
|
|
||||||
*/
|
*/
|
||||||
class UpsertVisualisationJob(
|
class UpsertVisualisationJob(
|
||||||
requestId: Option[RequestId],
|
requestId: Option[RequestId],
|
||||||
|
response: ApiResponse,
|
||||||
visualisationId: VisualisationId,
|
visualisationId: VisualisationId,
|
||||||
expressionId: ExpressionId,
|
expressionId: ExpressionId,
|
||||||
config: Api.VisualisationConfiguration,
|
config: Api.VisualisationConfiguration
|
||||||
response: ApiResponse
|
|
||||||
) extends Job[Option[Executable]](
|
) extends Job[Option[Executable]](
|
||||||
List(config.executionContextId),
|
List(config.executionContextId),
|
||||||
true,
|
false,
|
||||||
false
|
false
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -46,24 +49,36 @@ class UpsertVisualisationJob(
|
|||||||
ctx.locking.acquireWriteCompilationLock()
|
ctx.locking.acquireWriteCompilationLock()
|
||||||
try {
|
try {
|
||||||
val maybeCallable =
|
val maybeCallable =
|
||||||
evaluateExpression(config.visualisationModule, config.expression)
|
UpsertVisualisationJob.evaluateVisualisationExpression(
|
||||||
|
config.expression
|
||||||
|
)
|
||||||
|
|
||||||
maybeCallable match {
|
maybeCallable match {
|
||||||
case Left(ModuleNotFound) =>
|
case Left(ModuleNotFound(moduleName)) =>
|
||||||
replyWithModuleNotFoundError()
|
replyWithModuleNotFoundError(moduleName)
|
||||||
None
|
None
|
||||||
|
|
||||||
case Left(EvaluationFailed(message, result)) =>
|
case Left(EvaluationFailed(message, result)) =>
|
||||||
replyWithExpressionFailedError(message, result)
|
replyWithExpressionFailedError(message, result)
|
||||||
None
|
None
|
||||||
|
|
||||||
case Right(callable) =>
|
case Right(EvaluationResult(module, callable)) =>
|
||||||
val visualisation = updateVisualisation(callable)
|
val visualisation =
|
||||||
|
UpsertVisualisationJob.updateVisualisation(
|
||||||
|
visualisationId,
|
||||||
|
expressionId,
|
||||||
|
module,
|
||||||
|
config,
|
||||||
|
callable
|
||||||
|
)
|
||||||
ctx.endpoint.sendToClient(Api.Response(requestId, response))
|
ctx.endpoint.sendToClient(Api.Response(requestId, response))
|
||||||
val stack = ctx.contextManager.getStack(config.executionContextId)
|
val stack = ctx.contextManager.getStack(config.executionContextId)
|
||||||
val cachedValue = stack.headOption
|
val cachedValue = stack.headOption
|
||||||
.flatMap(frame => Option(frame.cache.get(expressionId)))
|
.flatMap(frame => Option(frame.cache.get(expressionId)))
|
||||||
requireVisualisationSynchronization(stack, expressionId)
|
UpsertVisualisationJob.requireVisualisationSynchronization(
|
||||||
|
stack,
|
||||||
|
expressionId
|
||||||
|
)
|
||||||
cachedValue match {
|
cachedValue match {
|
||||||
case Some(value) =>
|
case Some(value) =>
|
||||||
ProgramExecutionSupport.sendVisualisationUpdate(
|
ProgramExecutionSupport.sendVisualisationUpdate(
|
||||||
@ -84,28 +99,6 @@ class UpsertVisualisationJob(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def requireVisualisationSynchronization(
|
|
||||||
stack: Iterable[InstrumentFrame],
|
|
||||||
expressionId: ExpressionId
|
|
||||||
): Unit = {
|
|
||||||
stack.foreach(_.syncState.setVisualisationUnsync(expressionId))
|
|
||||||
}
|
|
||||||
|
|
||||||
private def updateVisualisation(
|
|
||||||
callable: AnyRef
|
|
||||||
)(implicit ctx: RuntimeContext): Visualisation = {
|
|
||||||
val visualisation = Visualisation(
|
|
||||||
visualisationId,
|
|
||||||
expressionId,
|
|
||||||
callable
|
|
||||||
)
|
|
||||||
ctx.contextManager.upsertVisualisation(
|
|
||||||
config.executionContextId,
|
|
||||||
visualisation
|
|
||||||
)
|
|
||||||
visualisation
|
|
||||||
}
|
|
||||||
|
|
||||||
private def replyWithExpressionFailedError(
|
private def replyWithExpressionFailedError(
|
||||||
message: String,
|
message: String,
|
||||||
executionResult: Option[Api.ExecutionResult.Diagnostic]
|
executionResult: Option[Api.ExecutionResult.Diagnostic]
|
||||||
@ -118,39 +111,115 @@ class UpsertVisualisationJob(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def replyWithModuleNotFoundError()(implicit
|
private def replyWithModuleNotFoundError(module: String)(implicit
|
||||||
ctx: RuntimeContext
|
ctx: RuntimeContext
|
||||||
): Unit = {
|
): Unit = {
|
||||||
ctx.endpoint.sendToClient(
|
ctx.endpoint.sendToClient(
|
||||||
Api.Response(
|
Api.Response(requestId, Api.ModuleNotFound(module))
|
||||||
requestId,
|
|
||||||
Api.ModuleNotFound(config.visualisationModule)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object UpsertVisualisationJob {
|
||||||
|
|
||||||
|
/** The number of times to retry the expression evaluation. */
|
||||||
|
val MaxEvaluationRetryCount: Int = 5
|
||||||
|
|
||||||
|
/** Base trait for evaluation failures.
|
||||||
|
*/
|
||||||
|
sealed trait EvaluationFailure
|
||||||
|
|
||||||
|
/** Signals that a module cannot be found.
|
||||||
|
*
|
||||||
|
* @param moduleName the module name
|
||||||
|
*/
|
||||||
|
case class ModuleNotFound(moduleName: String) extends EvaluationFailure
|
||||||
|
|
||||||
|
/** Signals that an evaluation of an expression failed.
|
||||||
|
*
|
||||||
|
* @param message the textual reason of a failure
|
||||||
|
* @param failure the error description
|
||||||
|
*/
|
||||||
|
case class EvaluationFailed(
|
||||||
|
message: String,
|
||||||
|
failure: Option[Api.ExecutionResult.Diagnostic]
|
||||||
|
) extends EvaluationFailure
|
||||||
|
|
||||||
|
case class EvaluationResult(module: Module, callback: AnyRef)
|
||||||
|
|
||||||
|
/** Upsert the provided visualisation.
|
||||||
|
*
|
||||||
|
* @param visualisation the visualisation to update
|
||||||
|
*/
|
||||||
|
def upsertVisualisation(
|
||||||
|
visualisation: Visualisation
|
||||||
|
)(implicit ctx: RuntimeContext): Unit = {
|
||||||
|
val visualisationConfig = visualisation.config
|
||||||
|
val expressionId = visualisation.expressionId
|
||||||
|
val visualisationId = visualisation.id
|
||||||
|
val maybeCallable =
|
||||||
|
evaluateVisualisationExpression(visualisation.config.expression)
|
||||||
|
|
||||||
|
maybeCallable.foreach { result =>
|
||||||
|
updateVisualisation(
|
||||||
|
visualisationId,
|
||||||
|
expressionId,
|
||||||
|
result.module,
|
||||||
|
visualisationConfig,
|
||||||
|
result.callback
|
||||||
|
)
|
||||||
|
val stack =
|
||||||
|
ctx.contextManager.getStack(visualisationConfig.executionContextId)
|
||||||
|
requireVisualisationSynchronization(stack, expressionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find module by name.
|
||||||
|
*
|
||||||
|
* @param moduleName the module name
|
||||||
|
* @return either the requested module or an error
|
||||||
|
*/
|
||||||
private def findModule(
|
private def findModule(
|
||||||
moduleName: String
|
moduleName: String
|
||||||
)(implicit ctx: RuntimeContext): Either[EvalFailure, Module] = {
|
)(implicit ctx: RuntimeContext): Either[EvaluationFailure, Module] = {
|
||||||
val context = ctx.executionService.getContext
|
val context = ctx.executionService.getContext
|
||||||
// TODO [RW] more specific error when the module cannot be installed (#1861)
|
|
||||||
context.ensureModuleIsLoaded(moduleName)
|
context.ensureModuleIsLoaded(moduleName)
|
||||||
val maybeModule = context.findModule(moduleName)
|
val maybeModule = context.findModule(moduleName)
|
||||||
|
|
||||||
if (maybeModule.isPresent) Right(maybeModule.get())
|
if (maybeModule.isPresent) Right(maybeModule.get())
|
||||||
else Left(ModuleNotFound)
|
else Left(ModuleNotFound(moduleName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Evaluate the visualisation expression in a given module.
|
||||||
|
*
|
||||||
|
* @param module the module where to evaluate the expression
|
||||||
|
* @param expression the visualisation expression
|
||||||
|
* @param retryCount the number of attempted retries
|
||||||
|
* @return either the evaluation result or an evaluation failure
|
||||||
|
*/
|
||||||
private def evaluateModuleExpression(
|
private def evaluateModuleExpression(
|
||||||
module: Module,
|
module: Module,
|
||||||
expression: String,
|
expression: Api.VisualisationExpression,
|
||||||
retryCount: Int = 0
|
retryCount: Int = 0
|
||||||
)(implicit
|
)(implicit
|
||||||
ctx: RuntimeContext
|
ctx: RuntimeContext
|
||||||
): Either[EvalFailure, AnyRef] =
|
): Either[EvaluationFailure, EvaluationResult] =
|
||||||
Either
|
Either
|
||||||
.catchNonFatal {
|
.catchNonFatal {
|
||||||
ctx.executionService.evaluateExpression(module, expression)
|
val callback = expression match {
|
||||||
|
case Api.VisualisationExpression.Text(_, expression) =>
|
||||||
|
ctx.executionService.evaluateExpression(module, expression)
|
||||||
|
case Api.VisualisationExpression.ModuleMethod(
|
||||||
|
Api.MethodPointer(_, definedOnType, name)
|
||||||
|
) =>
|
||||||
|
ctx.executionService.prepareFunctionCall(
|
||||||
|
module,
|
||||||
|
QualifiedName.fromString(definedOnType).item,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
EvaluationResult(module, callback)
|
||||||
}
|
}
|
||||||
.leftFlatMap {
|
.leftFlatMap {
|
||||||
case _: ThreadInterruptedException
|
case _: ThreadInterruptedException
|
||||||
@ -193,38 +262,110 @@ class UpsertVisualisationJob(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def evaluateExpression(
|
/** Evaluate the visualisation expression.
|
||||||
moduleName: String,
|
*
|
||||||
expression: String
|
* @param expression the visualisation expression to evaluate
|
||||||
)(implicit ctx: RuntimeContext): Either[EvalFailure, AnyRef] =
|
* @return either the evaluation result or an evaluation error
|
||||||
|
*/
|
||||||
|
private def evaluateVisualisationExpression(
|
||||||
|
expression: Api.VisualisationExpression
|
||||||
|
)(implicit
|
||||||
|
ctx: RuntimeContext
|
||||||
|
): Either[EvaluationFailure, EvaluationResult] = {
|
||||||
for {
|
for {
|
||||||
module <- findModule(moduleName)
|
module <- findModule(expression.module)
|
||||||
expression <- evaluateModuleExpression(module, expression)
|
expression <- evaluateModuleExpression(module, expression)
|
||||||
} yield expression
|
} yield expression
|
||||||
|
}
|
||||||
|
|
||||||
}
|
/** Update the visualisation state.
|
||||||
|
|
||||||
object UpsertVisualisationJob {
|
|
||||||
|
|
||||||
/** The number of times to retry the expression evaluation. */
|
|
||||||
val MaxEvaluationRetryCount: Int = 5
|
|
||||||
|
|
||||||
/** Base trait for evaluation failures.
|
|
||||||
*/
|
|
||||||
sealed trait EvalFailure
|
|
||||||
|
|
||||||
/** Signals that a module cannot be found.
|
|
||||||
*/
|
|
||||||
case object ModuleNotFound extends EvalFailure
|
|
||||||
|
|
||||||
/** Signals that an evaluation of an expression failed.
|
|
||||||
*
|
*
|
||||||
* @param message the textual reason of a failure
|
* @param visualisationId the visualisation identifier
|
||||||
* @param failure the error description
|
* @param expressionId the expression to which the visualisation is applied
|
||||||
|
* @param module the module containing the visualisation
|
||||||
|
* @param visualisationConfig the visualisation configuration
|
||||||
|
* @param callback the visualisation callback function
|
||||||
|
* @param ctx the runtime context
|
||||||
|
* @return the re-evaluated visualisation
|
||||||
*/
|
*/
|
||||||
case class EvaluationFailed(
|
private def updateVisualisation(
|
||||||
message: String,
|
visualisationId: VisualisationId,
|
||||||
failure: Option[Api.ExecutionResult.Diagnostic]
|
expressionId: ExpressionId,
|
||||||
) extends EvalFailure
|
module: Module,
|
||||||
|
visualisationConfig: VisualisationConfiguration,
|
||||||
|
callback: AnyRef
|
||||||
|
)(implicit ctx: RuntimeContext): Visualisation = {
|
||||||
|
val visualisationExpressionId =
|
||||||
|
findVisualisationExpressionId(module, visualisationConfig.expression)
|
||||||
|
val visualisation = Visualisation(
|
||||||
|
visualisationId,
|
||||||
|
expressionId,
|
||||||
|
new RuntimeCache(),
|
||||||
|
module,
|
||||||
|
visualisationConfig,
|
||||||
|
visualisationExpressionId,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
setCacheWeights(visualisation)
|
||||||
|
ctx.contextManager.upsertVisualisation(
|
||||||
|
visualisationConfig.executionContextId,
|
||||||
|
visualisation
|
||||||
|
)
|
||||||
|
visualisation
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find the expressionId of visualisation function.
|
||||||
|
*
|
||||||
|
* @param module the module environment
|
||||||
|
* @param visualisationExpression the visualisation expression
|
||||||
|
* @return the expression id of required visualisation function
|
||||||
|
*/
|
||||||
|
private def findVisualisationExpressionId(
|
||||||
|
module: Module,
|
||||||
|
visualisationExpression: VisualisationExpression
|
||||||
|
): Option[ExpressionId] =
|
||||||
|
visualisationExpression match {
|
||||||
|
case VisualisationExpression.ModuleMethod(methodPointer) =>
|
||||||
|
module.getIr.bindings
|
||||||
|
.collect { case method: IR.Module.Scope.Definition.Method =>
|
||||||
|
val methodReference = method.methodReference
|
||||||
|
val methodReferenceName = methodReference.methodName.name
|
||||||
|
val methodReferenceTypeOpt = methodReference.typePointer.map(_.name)
|
||||||
|
|
||||||
|
method.getExternalId.filter { _ =>
|
||||||
|
methodReferenceName == methodPointer.name &&
|
||||||
|
methodReferenceTypeOpt.isEmpty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flatten
|
||||||
|
.headOption
|
||||||
|
|
||||||
|
case _: VisualisationExpression.Text => None
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the cache weights for the provided visualisation.
|
||||||
|
*
|
||||||
|
* @param visualisation the visualisation to update
|
||||||
|
*/
|
||||||
|
private def setCacheWeights(visualisation: Visualisation): Unit = {
|
||||||
|
visualisation.module.getIr.getMetadata(CachePreferenceAnalysis).foreach {
|
||||||
|
metadata =>
|
||||||
|
CacheInvalidation.runVisualisations(
|
||||||
|
Seq(visualisation),
|
||||||
|
CacheInvalidation.Command.SetMetadata(metadata)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Require to send the visualisation update.
|
||||||
|
*
|
||||||
|
* @param stack the execution stack
|
||||||
|
* @param expressionId the expression id to which the visualisation is applied
|
||||||
|
*/
|
||||||
|
private def requireVisualisationSynchronization(
|
||||||
|
stack: Iterable[InstrumentFrame],
|
||||||
|
expressionId: ExpressionId
|
||||||
|
): Unit =
|
||||||
|
stack.foreach(_.syncState.setVisualisationUnsync(expressionId))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user