mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 23:01:29 +03:00
Add API for dataflow errors (#1422)
This commit is contained in:
parent
4770e72734
commit
2515721799
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,7 +19,6 @@ target/
|
||||
## Rust ##
|
||||
##########
|
||||
|
||||
Cargo.lock
|
||||
**/*.rs.bk
|
||||
wasm-pack.log
|
||||
generated/
|
||||
|
1454
Cargo.lock
generated
Normal file
1454
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,7 @@ transport formats, please look [here](./protocol-architecture).
|
||||
- [`MethodPointer`](#methodpointer)
|
||||
- [`ExpressionValueUpdate`](#expressionvalueupdate)
|
||||
- [`ExpressionUpdate`](#expressionupdate)
|
||||
- [`ExpressionUpdatePayload`](#expressionupdatepayload)
|
||||
- [`ProfilingInfo`](#profilinginfo)
|
||||
- [`VisualisationConfiguration`](#visualisationconfiguration)
|
||||
- [`SuggestionEntryArgument`](#suggestionentryargument)
|
||||
@ -277,13 +278,10 @@ interface ExecutionTime {
|
||||
|
||||
### `ExpressionUpdate`
|
||||
|
||||
```typescript
|
||||
type ExpressionUpdate = Computed | Failed | Poisoned;
|
||||
An update about the computed expression.
|
||||
|
||||
/**
|
||||
* An update about computed expression.
|
||||
*/
|
||||
interface Computed {
|
||||
```typescript
|
||||
interface ExpressionUpdate {
|
||||
/**
|
||||
* The id of updated expression.
|
||||
*/
|
||||
@ -298,36 +296,70 @@ interface Computed {
|
||||
* The updated pointer to the method call.
|
||||
*/
|
||||
methodPointer?: SuggestionId;
|
||||
|
||||
/**
|
||||
* Profiling information about the expression.
|
||||
*/
|
||||
profilingInfo: ProfilingInfo[];
|
||||
|
||||
/**
|
||||
* Wether or not the expression's value came from the cache.
|
||||
*/
|
||||
fromCache: bool;
|
||||
|
||||
/**
|
||||
* An extra information about the computed value.
|
||||
*/
|
||||
payload: ExpressionUpdatePayload;
|
||||
}
|
||||
```
|
||||
|
||||
### `ExpressionUpdatePayload`
|
||||
|
||||
An information about the computed value.
|
||||
|
||||
```typescript
|
||||
type ExpressionUpdatePayload = Value | DatafalowError | RuntimeError | Poisoned;
|
||||
|
||||
/**
|
||||
* An empty payload. Indicates that the expression was computed to a value.
|
||||
*/
|
||||
interface Value {}
|
||||
|
||||
/**
|
||||
* Indicates that the expression was computed to an error.
|
||||
*/
|
||||
interface DataflowError {
|
||||
/**
|
||||
* The list of expressions leading to the root error.
|
||||
*/
|
||||
trace: ExpressionId[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An update about failed expression.
|
||||
* Indicates that the expression failed with the runtime exception.
|
||||
*/
|
||||
interface Failed {
|
||||
/**
|
||||
* The id of updated expression.
|
||||
*/
|
||||
expressionId: ExpressionId;
|
||||
|
||||
interface RuntimeError {
|
||||
/**
|
||||
* The error message.
|
||||
*/
|
||||
message: String;
|
||||
|
||||
/**
|
||||
* The stack trace.
|
||||
*/
|
||||
trace: ExpressionId[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An update about expression not executed due to the failed dependency.
|
||||
* Indicates that the expression was not computed due to a dependency,
|
||||
* that failed with the runtime exception.
|
||||
*/
|
||||
interface Poisoned {
|
||||
/**
|
||||
* The id of updated expression.
|
||||
* The list of expressions leading to the root expression that failed.
|
||||
*/
|
||||
expressionId: ExpressionId;
|
||||
|
||||
/**
|
||||
* The failed expression that prevents the execution of this expression.
|
||||
*/
|
||||
failedExpressionId: ExpressionId;
|
||||
trace: ExpressionId[];
|
||||
}
|
||||
```
|
||||
|
||||
@ -887,6 +919,11 @@ interface Diagnostic {
|
||||
*/
|
||||
location?: Range;
|
||||
|
||||
/**
|
||||
* The id of related expression.
|
||||
*/
|
||||
expressionId?: ExpressionId;
|
||||
|
||||
/**
|
||||
* The stack trace.
|
||||
*/
|
||||
|
@ -104,43 +104,19 @@ final class ContextEventsListener(
|
||||
sessionRouter ! DeliverToJsonController(rpcSession.clientId, payload)
|
||||
|
||||
case RunExpressionUpdates if expressionUpdates.nonEmpty =>
|
||||
val updates = Vector.newBuilder[ContextRegistryProtocol.ExpressionUpdate]
|
||||
val computedExpressions =
|
||||
Vector.newBuilder[Api.ExpressionUpdate.ExpressionComputed]
|
||||
expressionUpdates.foreach {
|
||||
case m: Api.ExpressionUpdate.ExpressionComputed =>
|
||||
computedExpressions += m
|
||||
case m: Api.ExpressionUpdate.ExpressionFailed =>
|
||||
updates += ContextRegistryProtocol.ExpressionUpdate
|
||||
.ExpressionFailed(m.expressionId, m.message)
|
||||
case m: Api.ExpressionUpdate.ExpressionPoisoned =>
|
||||
updates += ContextRegistryProtocol.ExpressionUpdate
|
||||
.ExpressionPoisoned(m.expressionId, m.failedExpressionId)
|
||||
}
|
||||
val notification = ContextRegistryProtocol.ExpressionUpdatesNotification(
|
||||
contextId,
|
||||
updates.result(),
|
||||
None
|
||||
)
|
||||
if (notification.updates.nonEmpty) {
|
||||
sessionRouter ! DeliverToJsonController(
|
||||
rpcSession.clientId,
|
||||
notification
|
||||
)
|
||||
}
|
||||
runExpressionComputed(computedExpressions.result())
|
||||
runExpressionUpdates(expressionUpdates)
|
||||
context.become(withState(Vector()))
|
||||
|
||||
case RunExpressionUpdates if expressionUpdates.isEmpty =>
|
||||
}
|
||||
|
||||
/** Process `ExpressionComputed` notifications.
|
||||
/** Process `ExpressionUpdate` notifications.
|
||||
*
|
||||
* Function resolves method pointers to the corresponding suggestion ids in
|
||||
* the suggestions database, and creates the API updates.
|
||||
*/
|
||||
private def runExpressionComputed(
|
||||
expressionUpdates: Vector[Api.ExpressionUpdate.ExpressionComputed]
|
||||
private def runExpressionUpdates(
|
||||
expressionUpdates: Vector[Api.ExpressionUpdate]
|
||||
): Unit = {
|
||||
def toMethodPointer(call: Api.MethodPointer): (String, String, String) =
|
||||
(call.module, call.definedOnType, call.name)
|
||||
@ -163,7 +139,7 @@ final class ContextEventsListener(
|
||||
}
|
||||
.toMap
|
||||
val computedExpressions = expressionUpdates.map { update =>
|
||||
ContextRegistryProtocol.ExpressionUpdate.ExpressionComputed(
|
||||
ContextRegistryProtocol.ExpressionUpdate(
|
||||
update.expressionId,
|
||||
update.expressionType,
|
||||
update.methodCall.flatMap { call =>
|
||||
@ -175,16 +151,16 @@ final class ContextEventsListener(
|
||||
None
|
||||
}
|
||||
},
|
||||
update.profilingInfo.map { case Api.ProfilingInfo.ExecutionTime(t) =>
|
||||
ProfilingInfo.ExecutionTime(t)
|
||||
},
|
||||
update.fromCache
|
||||
update.profilingInfo.map(toProtocolProfilingInfo),
|
||||
update.fromCache,
|
||||
toProtocolPayload(update.payload)
|
||||
)
|
||||
}
|
||||
val valueUpdates = computedExpressions.collect(toExpressionValueUpdate)
|
||||
val payload = ContextRegistryProtocol.ExpressionUpdatesNotification(
|
||||
contextId,
|
||||
computedExpressions,
|
||||
Some(computedExpressions.map(toExpressionValueUpdate))
|
||||
Option.when(valueUpdates.nonEmpty)(valueUpdates)
|
||||
)
|
||||
DeliverToJsonController(rpcSession.clientId, payload)
|
||||
|
||||
@ -193,16 +169,56 @@ final class ContextEventsListener(
|
||||
}
|
||||
|
||||
/** Conversion between the new and the old expression update message. */
|
||||
private def toExpressionValueUpdate(
|
||||
m: ContextRegistryProtocol.ExpressionUpdate.ExpressionComputed
|
||||
): ExpressionValueUpdate =
|
||||
ExpressionValueUpdate(
|
||||
m.expressionId,
|
||||
m.`type`,
|
||||
m.methodPointer,
|
||||
m.profilingInfo,
|
||||
m.fromCache
|
||||
)
|
||||
private def toExpressionValueUpdate: PartialFunction[
|
||||
ContextRegistryProtocol.ExpressionUpdate,
|
||||
ExpressionValueUpdate
|
||||
] = {
|
||||
case m: ContextRegistryProtocol.ExpressionUpdate
|
||||
if m.payload == ContextRegistryProtocol.ExpressionUpdate.Payload.Value =>
|
||||
ExpressionValueUpdate(
|
||||
m.expressionId,
|
||||
m.`type`,
|
||||
m.methodPointer,
|
||||
m.profilingInfo,
|
||||
m.fromCache
|
||||
)
|
||||
}
|
||||
|
||||
/** Convert the runtime expression update payload to the context registry
|
||||
* protocol representation.
|
||||
*
|
||||
* @param payload the runtime payload
|
||||
* @return the registry protocol representation of the payload message
|
||||
*/
|
||||
private def toProtocolPayload(
|
||||
payload: Api.ExpressionUpdate.Payload
|
||||
): ContextRegistryProtocol.ExpressionUpdate.Payload =
|
||||
payload match {
|
||||
case Api.ExpressionUpdate.Payload.Value() =>
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload.Value
|
||||
|
||||
case Api.ExpressionUpdate.Payload.DataflowError(trace) =>
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload.DataflowError(trace)
|
||||
|
||||
case Api.ExpressionUpdate.Payload.RuntimeError(message, trace) =>
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload
|
||||
.RuntimeError(message, trace)
|
||||
|
||||
case Api.ExpressionUpdate.Payload.Poisoned(trace) =>
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload.Poisoned(trace)
|
||||
}
|
||||
|
||||
/** Convert the runtime profiling info to the context registry protocol
|
||||
* representation.
|
||||
*
|
||||
* @param info the profiling info
|
||||
* @return the registry protocol representation of the profiling info
|
||||
*/
|
||||
private def toProtocolProfilingInfo(info: Api.ProfilingInfo): ProfilingInfo =
|
||||
info match {
|
||||
case Api.ProfilingInfo.ExecutionTime(t) =>
|
||||
ProfilingInfo.ExecutionTime(t)
|
||||
}
|
||||
|
||||
/** Convert the runtime failure message to the context registry protocol
|
||||
* representation.
|
||||
|
@ -107,96 +107,114 @@ object ContextRegistryProtocol {
|
||||
updatesOld: Option[Vector[ExpressionValueUpdate]]
|
||||
)
|
||||
|
||||
sealed trait ExpressionUpdate
|
||||
/** An update about computed expression.
|
||||
*
|
||||
* @param expressionId the id of updated expression
|
||||
* @param `type` the updated type of expression
|
||||
* @param methodPointer the suggestion id of the updated method pointer
|
||||
* @param profilingInfo profiling information about the expression
|
||||
* @param fromCache whether or not the expression's value came from the cache
|
||||
* @param payload an extra information about the computed value
|
||||
*/
|
||||
case class ExpressionUpdate(
|
||||
expressionId: UUID,
|
||||
`type`: Option[String],
|
||||
methodPointer: Option[Long],
|
||||
profilingInfo: Vector[ProfilingInfo],
|
||||
fromCache: Boolean,
|
||||
payload: ExpressionUpdate.Payload
|
||||
)
|
||||
object ExpressionUpdate {
|
||||
|
||||
/** An update about computed expression.
|
||||
*
|
||||
* @param expressionId the id of updated expression
|
||||
* @param `type` the updated type of expression
|
||||
* @param methodPointer the suggestion id of the updated method pointer
|
||||
* @param profilingInfo profiling information about the expression
|
||||
* @param fromCache whether or not the expression's value came from the cache
|
||||
*/
|
||||
case class ExpressionComputed(
|
||||
expressionId: UUID,
|
||||
`type`: Option[String],
|
||||
methodPointer: Option[Long],
|
||||
profilingInfo: Vector[ProfilingInfo],
|
||||
fromCache: Boolean
|
||||
) extends ExpressionUpdate
|
||||
sealed trait Payload
|
||||
object Payload {
|
||||
|
||||
/** An update about failed expression.
|
||||
*
|
||||
* @param expressionId the expression id
|
||||
* @param message the error message
|
||||
*/
|
||||
case class ExpressionFailed(
|
||||
expressionId: UUID,
|
||||
message: String
|
||||
) extends ExpressionUpdate
|
||||
/** An information about computed expression. */
|
||||
case object Value extends Payload
|
||||
|
||||
/** An update about expression not executed due to the failed dependency.
|
||||
*
|
||||
* @param expressionId the expression id
|
||||
* @param failedExpressionId failed expression that blocks the execution
|
||||
* of this expression
|
||||
*/
|
||||
case class ExpressionPoisoned(
|
||||
expressionId: UUID,
|
||||
failedExpressionId: UUID
|
||||
) extends ExpressionUpdate
|
||||
/** Indicates that the expression was computed to an error.
|
||||
*
|
||||
* @param trace the list of expressions leading to the root error.
|
||||
*/
|
||||
case class DataflowError(trace: Seq[UUID]) extends Payload
|
||||
|
||||
private object CodecField {
|
||||
/** Indicates that the expression failed with the runtime exception.
|
||||
*
|
||||
* @param message the error message
|
||||
* @param trace the stack trace
|
||||
*/
|
||||
case class RuntimeError(
|
||||
message: String,
|
||||
trace: Seq[UUID]
|
||||
) extends Payload
|
||||
|
||||
val Type = "type"
|
||||
}
|
||||
/** Indicates that the expression was not computed due to a dependency,
|
||||
* that failed with the runtime exception.
|
||||
*
|
||||
* @param trace the list of expressions leading to the root error.
|
||||
*/
|
||||
case class Poisoned(trace: Seq[UUID]) extends Payload
|
||||
|
||||
private object ExpressionUpdateType {
|
||||
val Computed = "Computed"
|
||||
private object CodecField {
|
||||
|
||||
val Failed = "Failed"
|
||||
|
||||
val Poisoned = "Poisoned"
|
||||
}
|
||||
|
||||
implicit val encoder: Encoder[ExpressionUpdate] =
|
||||
Encoder.instance[ExpressionUpdate] {
|
||||
case m: ExpressionUpdate.ExpressionComputed =>
|
||||
Encoder[ExpressionUpdate.ExpressionComputed]
|
||||
.apply(m)
|
||||
.deepMerge(
|
||||
Json.obj(CodecField.Type -> ExpressionUpdateType.Computed.asJson)
|
||||
)
|
||||
|
||||
case m: ExpressionUpdate.ExpressionFailed =>
|
||||
Encoder[ExpressionUpdate.ExpressionFailed]
|
||||
.apply(m)
|
||||
.deepMerge(
|
||||
Json.obj(CodecField.Type -> ExpressionUpdateType.Failed.asJson)
|
||||
)
|
||||
|
||||
case m: ExpressionUpdate.ExpressionPoisoned =>
|
||||
Encoder[ExpressionUpdate.ExpressionPoisoned]
|
||||
.apply(m)
|
||||
.deepMerge(
|
||||
Json.obj(CodecField.Type -> ExpressionUpdateType.Poisoned.asJson)
|
||||
)
|
||||
val Type = "type"
|
||||
}
|
||||
|
||||
implicit val decoder: Decoder[ExpressionUpdate] =
|
||||
Decoder.instance { cursor =>
|
||||
cursor.downField(CodecField.Type).as[String].flatMap {
|
||||
case ExpressionUpdateType.Computed =>
|
||||
Decoder[ExpressionUpdate.ExpressionComputed].tryDecode(cursor)
|
||||
private object PayloadType {
|
||||
|
||||
case ExpressionUpdateType.Failed =>
|
||||
Decoder[ExpressionUpdate.ExpressionFailed].tryDecode(cursor)
|
||||
val Value = "Value"
|
||||
|
||||
case ExpressionUpdateType.Poisoned =>
|
||||
Decoder[ExpressionUpdate.ExpressionFailed].tryDecode(cursor)
|
||||
val DataflowError = "DataflowError"
|
||||
|
||||
val RuntimeError = "RuntimeError"
|
||||
|
||||
val Poisoned = "Poisoned"
|
||||
}
|
||||
|
||||
implicit val encoder: Encoder[Payload] =
|
||||
Encoder.instance[Payload] {
|
||||
case Payload.Value =>
|
||||
Json.obj(CodecField.Type -> PayloadType.Value.asJson)
|
||||
|
||||
case m: Payload.DataflowError =>
|
||||
Encoder[Payload.DataflowError]
|
||||
.apply(m)
|
||||
.deepMerge(
|
||||
Json.obj(CodecField.Type -> PayloadType.DataflowError.asJson)
|
||||
)
|
||||
|
||||
case m: Payload.RuntimeError =>
|
||||
Encoder[Payload.RuntimeError]
|
||||
.apply(m)
|
||||
.deepMerge(
|
||||
Json.obj(CodecField.Type -> PayloadType.RuntimeError.asJson)
|
||||
)
|
||||
|
||||
case m: Payload.Poisoned =>
|
||||
Encoder[Payload.Poisoned]
|
||||
.apply(m)
|
||||
.deepMerge(
|
||||
Json.obj(CodecField.Type -> PayloadType.Poisoned.asJson)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
implicit val decoder: Decoder[Payload] =
|
||||
Decoder.instance { cursor =>
|
||||
cursor.downField(CodecField.Type).as[String].flatMap {
|
||||
case PayloadType.Value =>
|
||||
Right(Payload.Value)
|
||||
|
||||
case PayloadType.DataflowError =>
|
||||
Decoder[Payload.DataflowError].tryDecode(cursor)
|
||||
|
||||
case PayloadType.RuntimeError =>
|
||||
Decoder[Payload.RuntimeError].tryDecode(cursor)
|
||||
|
||||
case PayloadType.Poisoned =>
|
||||
Decoder[Payload.Poisoned].tryDecode(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Signals that user doesn't have access to the requested context.
|
||||
|
@ -82,18 +82,19 @@ class ContextEventsListenerSpec
|
||||
listener ! Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
Some(Suggestions.method.returnType),
|
||||
Some(
|
||||
Api.MethodPointer(
|
||||
Suggestions.method.module,
|
||||
Suggestions.method.selfType,
|
||||
Suggestions.method.name,
|
||||
Suggestions.method.name
|
||||
)
|
||||
),
|
||||
Vector(),
|
||||
false
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -104,12 +105,13 @@ class ContextEventsListenerSpec
|
||||
ContextRegistryProtocol.ExpressionUpdatesNotification(
|
||||
contextId,
|
||||
Vector(
|
||||
ContextRegistryProtocol.ExpressionUpdate.ExpressionComputed(
|
||||
ContextRegistryProtocol.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
Some(Suggestions.method.returnType),
|
||||
Some(suggestionIds(1).get),
|
||||
Vector(),
|
||||
false
|
||||
false,
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload.Value
|
||||
)
|
||||
),
|
||||
Some(
|
||||
@ -128,14 +130,20 @@ class ContextEventsListenerSpec
|
||||
)
|
||||
}
|
||||
|
||||
"send failure updates" taggedAs Retry in withDb {
|
||||
"send dataflow error updates" taggedAs Retry in withDb {
|
||||
(clientId, contextId, _, router, listener) =>
|
||||
listener ! Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionFailed(
|
||||
Api.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
"Method failure"
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.DataflowError(
|
||||
Seq(Suggestions.function.externalId.get)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -146,9 +154,14 @@ class ContextEventsListenerSpec
|
||||
ContextRegistryProtocol.ExpressionUpdatesNotification(
|
||||
contextId,
|
||||
Vector(
|
||||
ContextRegistryProtocol.ExpressionUpdate.ExpressionFailed(
|
||||
ContextRegistryProtocol.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
"Method failure"
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false,
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload
|
||||
.DataflowError(Seq(Suggestions.function.externalId.get))
|
||||
)
|
||||
),
|
||||
None
|
||||
@ -157,14 +170,18 @@ class ContextEventsListenerSpec
|
||||
)
|
||||
}
|
||||
|
||||
"send poisoning updates" taggedAs Retry in withDb {
|
||||
"send runtime error updates" taggedAs Retry in withDb {
|
||||
(clientId, contextId, _, router, listener) =>
|
||||
listener ! Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionPoisoned(
|
||||
Suggestions.local.externalId.get,
|
||||
Suggestions.method.externalId.get
|
||||
Api.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.RuntimeError("Method failure", Seq())
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -175,9 +192,54 @@ class ContextEventsListenerSpec
|
||||
ContextRegistryProtocol.ExpressionUpdatesNotification(
|
||||
contextId,
|
||||
Vector(
|
||||
ContextRegistryProtocol.ExpressionUpdate.ExpressionPoisoned(
|
||||
ContextRegistryProtocol.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false,
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload
|
||||
.RuntimeError("Method failure", Seq())
|
||||
)
|
||||
),
|
||||
None
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"send poisoning error updates" taggedAs Retry in withDb {
|
||||
(clientId, contextId, _, router, listener) =>
|
||||
listener ! Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate(
|
||||
Suggestions.local.externalId.get,
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Poisoned(
|
||||
Seq(Suggestions.method.externalId.get)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
router.expectMsg(
|
||||
DeliverToJsonController(
|
||||
clientId,
|
||||
ContextRegistryProtocol.ExpressionUpdatesNotification(
|
||||
contextId,
|
||||
Vector(
|
||||
ContextRegistryProtocol.ExpressionUpdate(
|
||||
Suggestions.local.externalId.get,
|
||||
Suggestions.method.externalId.get
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false,
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload
|
||||
.Poisoned(Seq(Suggestions.method.externalId.get))
|
||||
)
|
||||
),
|
||||
None
|
||||
@ -203,12 +265,13 @@ class ContextEventsListenerSpec
|
||||
listener ! Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -216,12 +279,13 @@ class ContextEventsListenerSpec
|
||||
listener ! Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Suggestions.local.externalId.get,
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -234,19 +298,21 @@ class ContextEventsListenerSpec
|
||||
ExpressionUpdatesNotification(
|
||||
contextId,
|
||||
Vector(
|
||||
ContextRegistryProtocol.ExpressionUpdate.ExpressionComputed(
|
||||
ContextRegistryProtocol.ExpressionUpdate(
|
||||
Suggestions.method.externalId.get,
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false
|
||||
false,
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload.Value
|
||||
),
|
||||
ContextRegistryProtocol.ExpressionUpdate.ExpressionComputed(
|
||||
ContextRegistryProtocol.ExpressionUpdate(
|
||||
Suggestions.local.externalId.get,
|
||||
None,
|
||||
None,
|
||||
Vector(),
|
||||
false
|
||||
false,
|
||||
ContextRegistryProtocol.ExpressionUpdate.Payload.Value
|
||||
)
|
||||
),
|
||||
Some(
|
||||
|
@ -269,65 +269,80 @@ object Runtime {
|
||||
fromCache: Boolean
|
||||
)
|
||||
|
||||
/** Base trait for expression updates. */
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
@JsonSubTypes(
|
||||
Array(
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExpressionUpdate.ExpressionComputed],
|
||||
name = "expressionUpdateComputed"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExpressionUpdate.ExpressionFailed],
|
||||
name = "expressionUpdateFailed"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[ExpressionUpdate.ExpressionPoisoned],
|
||||
name = "expressionUpdatePoisoned"
|
||||
)
|
||||
)
|
||||
/** An update about the computed expression.
|
||||
*
|
||||
* @param expressionId the expression id
|
||||
* @param expressionType the type of expression
|
||||
* @param methodCall the pointer to a method definition
|
||||
* @param profilingInfo profiling information about the execution of this
|
||||
* expression
|
||||
* @param fromCache whether or not the value for this expression came
|
||||
* from the cache
|
||||
* @param payload an extra information about the computed value
|
||||
*/
|
||||
case class ExpressionUpdate(
|
||||
expressionId: ExpressionId,
|
||||
expressionType: Option[String],
|
||||
methodCall: Option[MethodPointer],
|
||||
profilingInfo: Vector[ProfilingInfo],
|
||||
fromCache: Boolean,
|
||||
payload: ExpressionUpdate.Payload
|
||||
)
|
||||
sealed trait ExpressionUpdate
|
||||
object ExpressionUpdate {
|
||||
|
||||
/** An update about computed expression.
|
||||
*
|
||||
* @param expressionId the expression id
|
||||
* @param expressionType the type of expression
|
||||
* @param methodCall the pointer to a method definithin
|
||||
* @param profilingInfo profiling information about the execution of this
|
||||
* expression
|
||||
* @param fromCache whether or not the value for this expression came
|
||||
* from the cache
|
||||
*/
|
||||
case class ExpressionComputed(
|
||||
expressionId: ExpressionId,
|
||||
expressionType: Option[String],
|
||||
methodCall: Option[MethodPointer],
|
||||
profilingInfo: Vector[ProfilingInfo],
|
||||
fromCache: Boolean
|
||||
) extends ExpressionUpdate
|
||||
/** Base trait for expression payloads. */
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
@JsonSubTypes(
|
||||
Array(
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Payload.Value],
|
||||
name = "expressionUpdatePayloadValue"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Payload.DataflowError],
|
||||
name = "expressionUpdatePayloadDataflowError"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Payload.RuntimeError],
|
||||
name = "expressionUpdatePayloadRuntimeError"
|
||||
),
|
||||
new JsonSubTypes.Type(
|
||||
value = classOf[Payload.Poisoned],
|
||||
name = "expressionUpdatePayloadPoisoned"
|
||||
)
|
||||
)
|
||||
)
|
||||
sealed trait Payload
|
||||
object Payload {
|
||||
|
||||
/** An update about failed expression.
|
||||
*
|
||||
* @param expressionId the expression id
|
||||
* @param message the error message
|
||||
*/
|
||||
case class ExpressionFailed(
|
||||
expressionId: ExpressionId,
|
||||
message: String
|
||||
) extends ExpressionUpdate
|
||||
/** An empty payload. Indicates that the expression was computed to a
|
||||
* value.
|
||||
*/
|
||||
case class Value() extends Payload
|
||||
|
||||
/** An update about expression not executed due to a failed dependency.
|
||||
*
|
||||
* @param expressionId the expression id
|
||||
* @param failedExpressionId failed expression that blocks the execution
|
||||
* of this expression
|
||||
*/
|
||||
case class ExpressionPoisoned(
|
||||
expressionId: ExpressionId,
|
||||
failedExpressionId: ExpressionId
|
||||
) extends ExpressionUpdate
|
||||
/** Indicates that the expression was computed to an error.
|
||||
*
|
||||
* @param trace the list of expressions leading to the root error.
|
||||
*/
|
||||
case class DataflowError(trace: Seq[ExpressionId]) extends Payload
|
||||
|
||||
/** Indicates that the expression failed with the runtime exception.
|
||||
*
|
||||
* @param message the error message
|
||||
* @param trace the stack trace
|
||||
*/
|
||||
case class RuntimeError(
|
||||
message: String,
|
||||
trace: Seq[ExpressionId]
|
||||
) extends Payload
|
||||
|
||||
/** Indicates that the expression was not computed due to a dependency,
|
||||
* that failed with the runtime exception.
|
||||
*
|
||||
* @param trace the list of expressions leading to the root error.
|
||||
*/
|
||||
case class Poisoned(trace: Seq[ExpressionId]) extends Payload
|
||||
}
|
||||
}
|
||||
|
||||
/** An object representing profiling information about an executed
|
||||
|
@ -35,13 +35,26 @@ object ErrorResolver {
|
||||
val poisoned: Set[Api.ExpressionUpdate] = meta
|
||||
.getExternal(toDataflowDependencyType(expressionId))
|
||||
.getOrElse(Set())
|
||||
.map(
|
||||
Api.ExpressionUpdate
|
||||
.ExpressionPoisoned(_, expressionId.externalId)
|
||||
)
|
||||
.map { id =>
|
||||
Api.ExpressionUpdate(
|
||||
id,
|
||||
None,
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Poisoned(Seq())
|
||||
)
|
||||
}
|
||||
val failed =
|
||||
Api.ExpressionUpdate
|
||||
.ExpressionFailed(expressionId.externalId, error.getMessage)
|
||||
Api.ExpressionUpdate(
|
||||
expressionId.externalId,
|
||||
None,
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload
|
||||
.RuntimeError(error.getMessage, Seq())
|
||||
)
|
||||
poisoned + failed
|
||||
}
|
||||
.getOrElse(Set())
|
||||
@ -84,4 +97,6 @@ object ErrorResolver {
|
||||
DataflowAnalysis.DependencyInfo.Type
|
||||
.Static(id.internalId, Some(id.externalId))
|
||||
|
||||
val DependencyFailed: String =
|
||||
"Dependency failed."
|
||||
}
|
||||
|
@ -269,14 +269,14 @@ class EnsureCompiledJob(protected val files: Iterable[File])
|
||||
private def runCompilationDiagnostics(module: Module)(implicit
|
||||
ctx: RuntimeContext
|
||||
): CompilationStatus = {
|
||||
val errors = GatherDiagnostics
|
||||
val pass = GatherDiagnostics
|
||||
.runModule(module.getIr, ModuleContext(module))
|
||||
.unsafeGetMetadata(
|
||||
GatherDiagnostics,
|
||||
"No diagnostics metadata right after the gathering pass."
|
||||
)
|
||||
.diagnostics
|
||||
val diagnostics = errors.collect {
|
||||
val diagnostics = pass.collect {
|
||||
case warn: IR.Warning =>
|
||||
createDiagnostic(Api.DiagnosticType.Warning(), module, warn)
|
||||
case error: IR.Error =>
|
||||
|
@ -410,14 +410,15 @@ trait ProgramExecutionSupport {
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
value.getExpressionId,
|
||||
Option(value.getType),
|
||||
methodPointer,
|
||||
value.getProfilingInfo.map { case e: ExecutionTime =>
|
||||
Api.ProfilingInfo.ExecutionTime(e.getNanoTimeElapsed)
|
||||
}.toVector,
|
||||
value.wasCached()
|
||||
value.wasCached(),
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -11,7 +11,6 @@ import org.graalvm.polyglot.io.MessageEndpoint
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
import java.io.{ByteArrayOutputStream, File}
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.file.Files
|
||||
@ -102,6 +101,38 @@ class ExpressionErrorsTest
|
||||
Api.Response(Api.ExecutionComplete(contextId))
|
||||
}
|
||||
|
||||
object Update {
|
||||
|
||||
def error(
|
||||
expressionId: UUID,
|
||||
payload: Api.ExpressionUpdate.Payload
|
||||
): Api.ExpressionUpdate =
|
||||
Api.ExpressionUpdate(
|
||||
expressionId,
|
||||
None,
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false,
|
||||
payload
|
||||
)
|
||||
|
||||
def runtimeError(
|
||||
expressionId: UUID,
|
||||
message: String
|
||||
): Api.ExpressionUpdate =
|
||||
Api.ExpressionUpdate(
|
||||
expressionId,
|
||||
None,
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.RuntimeError(message, Seq())
|
||||
)
|
||||
|
||||
def poisonedError(expressionId: UUID): Api.ExpressionUpdate =
|
||||
error(expressionId, Api.ExpressionUpdate.Payload.Poisoned(Seq()))
|
||||
}
|
||||
|
||||
def contentsVersion(content: String): ContentVersion =
|
||||
Sha3_224VersionCalculator.evalVersion(content)
|
||||
|
||||
@ -177,12 +208,12 @@ class ExpressionErrorsTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionFailed(
|
||||
Update.runtimeError(
|
||||
xId,
|
||||
"Compile_Error Variable `undefined` is not defined."
|
||||
),
|
||||
Api.ExpressionUpdate.ExpressionPoisoned(yId, xId),
|
||||
Api.ExpressionUpdate.ExpressionPoisoned(mainResId, xId)
|
||||
Update.poisonedError(yId),
|
||||
Update.poisonedError(mainResId)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -132,7 +132,14 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(expressionId, None, None, Vector(Api.ProfilingInfo.ExecutionTime(0)), false)
|
||||
Api.ExpressionUpdate(
|
||||
expressionId,
|
||||
None,
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -146,12 +153,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
expressionId,
|
||||
Some(expressionType),
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -176,12 +184,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
expressionId,
|
||||
Some(expressionType),
|
||||
Some(methodPointer),
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
fromCache
|
||||
fromCache,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -194,7 +203,15 @@ class RuntimeServerTest
|
||||
Api.Response(
|
||||
Api.ExpressionValuesComputed(
|
||||
contextId,
|
||||
Vector(Api.ExpressionValueUpdate(expressionId, None, None, Vector(Api.ProfilingInfo.ExecutionTime(0)), false))
|
||||
Vector(
|
||||
Api.ExpressionValueUpdate(
|
||||
expressionId,
|
||||
None,
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -286,12 +303,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Main.idMainX,
|
||||
Some(Constants.INTEGER),
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
fromCache
|
||||
fromCache,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -302,18 +320,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Main.idMainY,
|
||||
Some(Constants.INTEGER),
|
||||
Some(
|
||||
Api.MethodPointer(
|
||||
"Test.Main",
|
||||
Constants.NUMBER,
|
||||
"foo"
|
||||
)
|
||||
),
|
||||
Some(Api.MethodPointer("Test.Main", Constants.NUMBER, "foo")),
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
fromCache
|
||||
fromCache,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -324,12 +337,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Main.idMainZ,
|
||||
Some(Constants.INTEGER),
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
fromCache
|
||||
fromCache,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -340,12 +354,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Main.idFooY,
|
||||
Some(Constants.INTEGER),
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
fromCache
|
||||
fromCache,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -356,12 +371,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
Main.idFooZ,
|
||||
Some(Constants.INTEGER),
|
||||
None,
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
fromCache
|
||||
fromCache,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -491,12 +507,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
idMainY,
|
||||
Some(Constants.INTEGER),
|
||||
Some(Api.MethodPointer("Test.Main", "Test.Main", "foo")),
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -507,12 +524,13 @@ class RuntimeServerTest
|
||||
Api.ExpressionUpdates(
|
||||
contextId,
|
||||
Set(
|
||||
Api.ExpressionUpdate.ExpressionComputed(
|
||||
Api.ExpressionUpdate(
|
||||
idMainZ,
|
||||
Some(Constants.INTEGER),
|
||||
Some(Api.MethodPointer("Test.Main", "Test.Main", "bar")),
|
||||
Vector(Api.ProfilingInfo.ExecutionTime(0)),
|
||||
false
|
||||
false,
|
||||
Api.ExpressionUpdate.Payload.Value()
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -660,7 +678,7 @@ class RuntimeServerTest
|
||||
context.receive(4) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PopContextResponse(contextId)),
|
||||
context.Main.UpdateOld.mainY(contextId, fromCache = true),
|
||||
context.Main.Update.mainY(contextId, fromCache = true),
|
||||
context.Main.Update.mainY(contextId, fromCache = true),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
@ -1689,7 +1707,7 @@ class RuntimeServerTest
|
||||
context.receive(4) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PopContextResponse(contextId)),
|
||||
context.Main.UpdateOld.mainY(contextId, fromCache = true),
|
||||
context.Main.Update.mainY(contextId, fromCache = true),
|
||||
context.Main.Update.mainY(contextId, fromCache = true),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
@ -2954,7 +2972,7 @@ class RuntimeServerTest
|
||||
context.receive(4) should contain theSameElementsAs Seq(
|
||||
Api.Response(requestId, Api.PopContextResponse(contextId)),
|
||||
context.Main.UpdateOld.mainY(contextId, fromCache = true),
|
||||
context.Main.Update.mainY(contextId, fromCache = true),
|
||||
context.Main.Update.mainY(contextId, fromCache = true),
|
||||
context.executionComplete(contextId)
|
||||
)
|
||||
|
||||
@ -5189,7 +5207,9 @@ class RuntimeServerTest
|
||||
|
||||
// open file
|
||||
context.send(
|
||||
Api.Request(Api.OpenFileNotification(mainFile, contents, isIndexed = true))
|
||||
Api.Request(
|
||||
Api.OpenFileNotification(mainFile, contents, isIndexed = true)
|
||||
)
|
||||
)
|
||||
context.receiveNone shouldEqual None
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user