framestack reimplemented
This commit is contained in:
parent
a764f3075c
commit
26dbd29ec8
|
@ -1,4 +1,4 @@
|
|||
import { SqError, SqLocation } from "@quri/squiggle-lang";
|
||||
import { SqError, SqFrame } from "@quri/squiggle-lang";
|
||||
import React from "react";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
||||
|
@ -6,24 +6,26 @@ type Props = {
|
|||
error: SqError;
|
||||
};
|
||||
|
||||
const StackTraceLocation: React.FC<{ location: SqLocation }> = ({
|
||||
location,
|
||||
}) => {
|
||||
const StackTraceFrame: React.FC<{ frame: SqFrame }> = ({ frame }) => {
|
||||
const location = frame.location();
|
||||
return (
|
||||
<div>
|
||||
Line {location.start.line}, column {location.start.column}
|
||||
{frame.name()}
|
||||
{location
|
||||
? ` at line ${location.start.line}, column ${location.start.column}`
|
||||
: ""}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const StackTrace: React.FC<Props> = ({ error }) => {
|
||||
const locations = error.toLocationArray();
|
||||
return locations.length ? (
|
||||
const frames = error.getFrameArray();
|
||||
return frames.length ? (
|
||||
<div>
|
||||
<div>Traceback:</div>
|
||||
<div className="ml-4">
|
||||
{locations.map((location, i) => (
|
||||
<StackTraceLocation location={location} key={i} />
|
||||
{frames.map((frame, i) => (
|
||||
<StackTraceFrame frame={frame} key={i} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -45,7 +45,7 @@ export function getValueToRender({ result, bindings }: ResultAndBindings) {
|
|||
|
||||
export function getErrorLocations(result: ResultAndBindings["result"]) {
|
||||
if (result.tag === "Error") {
|
||||
const location = result.value.toLocation();
|
||||
const location = result.value.location();
|
||||
return location ? [location] : [];
|
||||
} else {
|
||||
return [];
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import * as RSError from "../rescript/SqError.gen";
|
||||
import * as RSReducerT from "../rescript/Reducer/Reducer_T.gen";
|
||||
|
||||
import * as RSCallStack from "../rescript/Reducer/Reducer_CallStack.gen";
|
||||
import * as RSFrameStack from "../rescript/Reducer/Reducer_FrameStack.gen";
|
||||
|
||||
export type SqFrame = RSCallStack.frame;
|
||||
export { location as SqLocation } from "../rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.gen";
|
||||
|
||||
export class SqError {
|
||||
constructor(private _value: RSError.t) {}
|
||||
|
@ -19,17 +20,31 @@ export class SqError {
|
|||
return new SqError(RSError.createOtherError(v));
|
||||
}
|
||||
|
||||
getTopFrame(): SqCallFrame | undefined {
|
||||
const frame = RSCallStack.getTopFrame(RSError.getStackTrace(this._value));
|
||||
return frame ? new SqCallFrame(frame) : undefined;
|
||||
getTopFrame(): SqFrame | undefined {
|
||||
const frame = RSFrameStack.getTopFrame(RSError.getFrameStack(this._value));
|
||||
return frame ? new SqFrame(frame) : undefined;
|
||||
}
|
||||
|
||||
getFrameArray(): SqCallFrame[] {
|
||||
getFrameArray(): SqFrame[] {
|
||||
const frames = RSError.getFrameArray(this._value);
|
||||
return frames.map((frame) => new SqCallFrame(frame));
|
||||
return frames.map((frame) => new SqFrame(frame));
|
||||
}
|
||||
|
||||
location() {
|
||||
return this.getTopFrame()?.location();
|
||||
}
|
||||
}
|
||||
|
||||
export class SqCallFrame {
|
||||
constructor(private _value: SqFrame) {}
|
||||
export class SqFrame {
|
||||
constructor(private _value: RSReducerT.frame) {}
|
||||
|
||||
name(): string {
|
||||
return RSFrameStack.Frame.toName(this._value);
|
||||
}
|
||||
|
||||
location() {
|
||||
console.log(RSFrameStack);
|
||||
console.log(RSFrameStack.Frame);
|
||||
return RSFrameStack.Frame.toLocation(this._value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { isParenthesisNode } from "mathjs";
|
||||
import { SqProject } from "./SqProject";
|
||||
|
||||
type PathItem = string | number;
|
||||
|
|
|
@ -13,7 +13,7 @@ export {
|
|||
environment,
|
||||
defaultEnvironment,
|
||||
} from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
|
||||
export { SqError } from "./SqError";
|
||||
export { SqError, SqFrame, SqLocation } from "./SqError";
|
||||
export { SqShape } from "./SqPointSetDist";
|
||||
|
||||
export { resultMap } from "./types";
|
||||
|
|
|
@ -4,8 +4,13 @@ let defaultEnvironment: Reducer_T.environment = DistributionOperation.defaultEnv
|
|||
|
||||
let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => {
|
||||
{
|
||||
callStack: list{},
|
||||
frameStack: list{},
|
||||
bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend,
|
||||
environment: environment,
|
||||
inFunction: None,
|
||||
}
|
||||
}
|
||||
|
||||
let currentFunctionName = (t: t): string => {
|
||||
t.inFunction->E.O2.fmap(Reducer_Lambda_T.name)->E.O2.default("<top>")
|
||||
}
|
||||
|
|
|
@ -2,14 +2,16 @@ module Bindings = Reducer_Bindings
|
|||
module Result = Belt.Result
|
||||
module T = Reducer_T
|
||||
|
||||
let toLocation = (expression: T.expression): SqError.location => {
|
||||
let toLocation = (expression: T.expression): Reducer_Peggy_Parse.location => {
|
||||
expression.ast.location
|
||||
}
|
||||
|
||||
let throwFrom = (error: SqError.Message.t, expression: T.expression, context: T.context) =>
|
||||
error->SqError.throwMessage(
|
||||
context.callStack,
|
||||
location: Some(expression->toLocation)
|
||||
error->SqError.throwMessageWithFrameStack(
|
||||
context.frameStack->Reducer_FrameStack.extend(
|
||||
context->Reducer_Context.currentFunctionName,
|
||||
Some(expression->toLocation),
|
||||
),
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -93,9 +95,9 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
|||
}
|
||||
}
|
||||
|
||||
| T.ELambda(parameters, body) => (
|
||||
| T.ELambda(parameters, body, name) => (
|
||||
Reducer_Lambda.makeLambda(
|
||||
None, // TODO - pass function name from parser
|
||||
name,
|
||||
parameters,
|
||||
context.bindings,
|
||||
body,
|
||||
|
@ -112,7 +114,13 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
|||
})
|
||||
switch lambda {
|
||||
| T.IEvLambda(lambda) => {
|
||||
let result = Reducer_Lambda.doLambdaCall(lambda, argValues, context, evaluate)
|
||||
let result = Reducer_Lambda.doLambdaCallFrom(
|
||||
lambda,
|
||||
argValues,
|
||||
context,
|
||||
evaluate,
|
||||
Some(expression->toLocation), // we have to pass the location of a current expression here, to put it on frameStack
|
||||
)
|
||||
(result, context)
|
||||
}
|
||||
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression, context)
|
||||
|
|
|
@ -9,10 +9,11 @@ let eBool = aBool => aBool->T.IEvBool->T.EValue
|
|||
|
||||
let eCall = (fn: expression, args: array<expression>): expressionContent => T.ECall(fn, args)
|
||||
|
||||
let eLambda = (parameters: array<string>, expr: expression): expressionContent => T.ELambda(
|
||||
parameters,
|
||||
expr,
|
||||
)
|
||||
let eLambda = (
|
||||
parameters: array<string>,
|
||||
expr: expression,
|
||||
name: option<string>,
|
||||
): expressionContent => T.ELambda(parameters, expr, name)
|
||||
|
||||
let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ let rec toString = (expression: t) =>
|
|||
`${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})`
|
||||
| EAssign(name, value) => `${name} = ${value->toString}`
|
||||
| ECall(fn, args) => `(${fn->toString})(${args->Js.Array2.map(toString)->commaJoin})`
|
||||
| ELambda(parameters, body) => `{|${parameters->commaJoin}| ${body->toString}}`
|
||||
| ELambda(parameters, body, _) => `{|${parameters->commaJoin}| ${body->toString}}`
|
||||
| EValue(aValue) => Reducer_Value.toString(aValue)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,37 +1,43 @@
|
|||
type t = Reducer_T.frameStack
|
||||
|
||||
module Frame = {
|
||||
let toString = ({lambda, location}: Reducer_T.frame) =>
|
||||
`${fromFrame} at ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}`
|
||||
let toString = ({name, location}: Reducer_T.frame) =>
|
||||
name ++
|
||||
switch location {
|
||||
| Some(location) =>
|
||||
` at line ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}`
|
||||
| None => ""
|
||||
}
|
||||
|
||||
@genType
|
||||
let toLocation = (t: Reducer_T.frame): option<Reducer_Peggy_Parse.location> => t.location
|
||||
|
||||
@genType
|
||||
let toName = (t: Reducer_T.frame): string => t.name
|
||||
}
|
||||
|
||||
let make = (): t => list{}
|
||||
|
||||
let topFrameName = (t: t) =>
|
||||
switch t->getTopFrame {
|
||||
| Some({lambda}) =>
|
||||
switch lambda {
|
||||
| FnLambda({name}) => name
|
||||
| FnBuiltin({name}) => name
|
||||
}
|
||||
| None => "<main>"
|
||||
}
|
||||
@genType
|
||||
let getTopFrame = (t: t): option<Reducer_T.frame> => Belt.List.head(t)
|
||||
|
||||
let extend = (t: t, lambda: Reducer_T.lambdaValue, location: option<location>) =>
|
||||
let extend = (t: t, name: string, location: option<Reducer_Peggy_Parse.location>) =>
|
||||
t->Belt.List.add({
|
||||
lambda: lambda,
|
||||
fromLocation: location,
|
||||
fromFrame: t->topFrameName,
|
||||
name: name,
|
||||
location: location,
|
||||
})
|
||||
|
||||
let toString = (t: t) =>
|
||||
t->Belt.List.map(s => " " ++ s->toStringFrame ++ "\n")->Belt.List.toArray->Js.Array2.joinWith("")
|
||||
t
|
||||
->Belt.List.map(s => " " ++ s->Frame.toString ++ "\n")
|
||||
->Belt.List.toArray
|
||||
->Js.Array2.joinWith("")
|
||||
|
||||
@genType
|
||||
let toFrameArray = (t: t): array<frame> => t->Belt.List.toArray
|
||||
let toFrameArray = (t: t): array<Reducer_T.frame> => t->Belt.List.toArray
|
||||
|
||||
@genType
|
||||
let getTopFrame = (t: t): option<frame> => t->Belt.List.head
|
||||
let getTopFrame = (t: t): option<Reducer_T.frame> => t->Belt.List.head
|
||||
|
||||
let isEmpty = (t: t): bool =>
|
||||
switch t->Belt.List.head {
|
||||
|
|
|
@ -33,7 +33,8 @@ let makeLambda = (
|
|||
let lambdaContext: Reducer_T.context = {
|
||||
bindings: localBindingsWithParameters, // based on bindings at the moment of lambda creation
|
||||
environment: context.environment, // environment at the moment when lambda is called
|
||||
callStack: context.callStack, // extended by main `evaluate` function
|
||||
frameStack: context.frameStack, // already extended in `doLambdaCall`
|
||||
inFunction: context.inFunction, // already updated in `doLambdaCall`
|
||||
}
|
||||
|
||||
let (value, _) = reducer(body, lambdaContext)
|
||||
|
@ -49,7 +50,7 @@ let makeLambda = (
|
|||
})
|
||||
}
|
||||
|
||||
// stdlib lambdas (everything in FunctionRegistry) is built by this method. Body is generated in SquiggleLibrary_StdLib.res
|
||||
// stdlib functions (everything in FunctionRegistry) are built by this method. Body is generated in SquiggleLibrary_StdLib.res
|
||||
let makeFFILambda = (name: string, body: Reducer_T.lambdaBody): t => FnBuiltin({
|
||||
// Note: current bindings could be accidentally exposed here through context (compare with native lambda implementation above, where we override them with local bindings).
|
||||
// But FunctionRegistry API is too limited for that to matter. Please take care not to violate that in the future by accident.
|
||||
|
@ -65,10 +66,20 @@ let parameters = (t: t): array<string> => {
|
|||
}
|
||||
}
|
||||
|
||||
let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
|
||||
let doLambdaCallFrom = (
|
||||
t: t,
|
||||
args: array<Reducer_T.value>,
|
||||
context: Reducer_T.context,
|
||||
reducer,
|
||||
location: option<Reducer_Peggy_Parse.location>,
|
||||
) => {
|
||||
let newContext = {
|
||||
...context,
|
||||
callStack: t->Reducer_CallStack.extend(t),
|
||||
frameStack: context.frameStack->Reducer_FrameStack.extend(
|
||||
context->Reducer_Context.currentFunctionName,
|
||||
location,
|
||||
),
|
||||
inFunction: Some(t),
|
||||
}
|
||||
|
||||
SqError.rethrowWithStacktrace(() => {
|
||||
|
@ -76,5 +87,9 @@ let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
|
|||
| FnLambda({body}) => body(args, newContext, reducer)
|
||||
| FnBuiltin({body}) => body(args, newContext, reducer)
|
||||
}
|
||||
}, newContext.callStack)
|
||||
}, newContext.frameStack)
|
||||
}
|
||||
|
||||
let doLambdaCall = (t: t, args, context, reducer) => {
|
||||
doLambdaCallFrom(t, args, context, reducer, None)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
type t = Reducer_T.lambdaValue
|
||||
|
||||
let name = (t: t): string => {
|
||||
switch t {
|
||||
| FnLambda({name}) => name->E.O2.default("<anonymous>")
|
||||
| FnBuiltin({name}) => name
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ letStatement
|
|||
|
||||
defunStatement
|
||||
= variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression
|
||||
{ var value = h.nodeLambda(args, body, location())
|
||||
{ var value = h.nodeLambda(args, body, location(), variable)
|
||||
return h.nodeLetStatement(variable, value, location()) }
|
||||
|
||||
assignmentOp "assignment" = '='
|
||||
|
@ -261,9 +261,9 @@ valueConstructor
|
|||
lambda
|
||||
= '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
|
||||
{ statements.push(finalExpression)
|
||||
return h.nodeLambda(args, h.nodeBlock(statements, location()), location()) }
|
||||
return h.nodeLambda(args, h.nodeBlock(statements, location()), location(), undefined) }
|
||||
/ '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}'
|
||||
{ return h.nodeLambda(args, finalExpression, location()) }
|
||||
{ return h.nodeLambda(args, finalExpression, location(), undefined) }
|
||||
|
||||
arrayConstructor 'array'
|
||||
= '[' _nl ']'
|
||||
|
|
|
@ -44,7 +44,7 @@ type nodeIdentifier = {...node, "value": string}
|
|||
type nodeInteger = {...node, "value": int}
|
||||
type nodeKeyValue = {...node, "key": node, "value": node}
|
||||
type nodeRecord = {...node, "elements": array<nodeKeyValue>}
|
||||
type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": node}
|
||||
type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": node, "name": option<string>}
|
||||
type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
|
||||
type nodeModuleIdentifier = {...node, "value": string}
|
||||
type nodeString = {...node, "value": string}
|
||||
|
|
|
@ -20,7 +20,7 @@ let rec fromNode = (node: Parse.node): expression => {
|
|||
nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"])
|
||||
let body = nodeLambda["body"]->fromNode
|
||||
|
||||
ExpressionBuilder.eLambda(args, body)
|
||||
ExpressionBuilder.eLambda(args, body, nodeLambda["name"])
|
||||
}
|
||||
|
||||
let caseRecord = (nodeRecord): expressionContent => {
|
||||
|
|
|
@ -89,6 +89,7 @@ type NodeLambda = Node & {
|
|||
type: "Lambda";
|
||||
args: AnyPeggyNode[];
|
||||
body: AnyPeggyNode;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
type NodeTernary = Node & {
|
||||
|
@ -217,9 +218,10 @@ export function nodeKeyValue(
|
|||
export function nodeLambda(
|
||||
args: AnyPeggyNode[],
|
||||
body: AnyPeggyNode,
|
||||
location: LocationRange
|
||||
location: LocationRange,
|
||||
name?: NodeIdentifier
|
||||
): NodeLambda {
|
||||
return { type: "Lambda", args, body, location };
|
||||
return { type: "Lambda", args, body, location, name: name?.value };
|
||||
}
|
||||
export function nodeLetStatement(
|
||||
variable: NodeIdentifier,
|
||||
|
|
|
@ -36,7 +36,7 @@ and expressionContent =
|
|||
| ETernary(expression, expression, expression)
|
||||
| EAssign(string, expression)
|
||||
| ECall(expression, array<expression>)
|
||||
| ELambda(array<string>, expression)
|
||||
| ELambda(array<string>, expression, option<string>)
|
||||
| EValue(value)
|
||||
|
||||
and expression = {
|
||||
|
@ -50,18 +50,18 @@ and bindings = {
|
|||
parent: option<bindings>,
|
||||
}
|
||||
|
||||
@genType.opaque
|
||||
and frame = {
|
||||
name: string,
|
||||
location: option<Reducer_Peggy_Parse.location>, // can be empty for calls from builtin functions
|
||||
}
|
||||
|
||||
and frameStack = list<frame>
|
||||
@genType.opaque and frameStack = list<frame>
|
||||
|
||||
and context = {
|
||||
bindings: bindings,
|
||||
environment: environment,
|
||||
inFunction: option<string>, // used to build the next frame in frameStack
|
||||
callStack: frameStack,
|
||||
frameStack: frameStack,
|
||||
inFunction: option<lambdaValue>,
|
||||
}
|
||||
|
||||
and reducerFn = (expression, context) => (value, context)
|
||||
|
|
|
@ -95,19 +95,19 @@ type t = {
|
|||
|
||||
exception SqException(t)
|
||||
|
||||
// `context` should be specified for runtime errors, but can be left empty for errors from Reducer_Project and so on.
|
||||
// `location` can be empty for errors raised from FunctionRegistry.
|
||||
let fromMessage = (message: Message.t, frameStack: Reducer_FrameStack.t): t => {
|
||||
let fromMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t): t => {
|
||||
message: message,
|
||||
stackTrace: stackTrace,
|
||||
frameStack: frameStack,
|
||||
}
|
||||
|
||||
let fromMessageWithoutFrameStack = (message: Message.t) =>
|
||||
fromMessage(message, Reducer_FrameStack.make())
|
||||
// this shouldn't be used much, since frame stack will be empty
|
||||
// but it's useful for global errors, e.g. in ReducerProject or somethere in the frontend
|
||||
@genType
|
||||
let fromMessage = (message: Message.t) =>
|
||||
fromMessageWithFrameStack(message, Reducer_FrameStack.make())
|
||||
|
||||
@genType
|
||||
let getTopFrame = (t: t): option<Reducer_CallStack.frame> =>
|
||||
t.stackTrace->Reducer_FrameStack.getTopFrame
|
||||
let getTopFrame = (t: t): option<Reducer_T.frame> => t.frameStack->Reducer_FrameStack.getTopFrame
|
||||
|
||||
@genType
|
||||
let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack
|
||||
|
@ -116,42 +116,42 @@ let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack
|
|||
let toString = (t: t): string => t.message->Message.toString
|
||||
|
||||
@genType
|
||||
let createOtherError = (v: string): t => Message.REOther(v)->fromMessageWithoutFrameStack
|
||||
let createOtherError = (v: string): t => Message.REOther(v)->fromMessage
|
||||
|
||||
@genType
|
||||
let getFrameArray = (t: t): array<Reducer_CallStack.frame> =>
|
||||
t.stackTrace->Reducer_CallStack.toFrameArray
|
||||
let getFrameArray = (t: t): array<Reducer_T.frame> => t.frameStack->Reducer_FrameStack.toFrameArray
|
||||
|
||||
@genType
|
||||
let toStringWithStacktrace = (t: t) =>
|
||||
let toStringWithStackTrace = (t: t) =>
|
||||
t->toString ++ if t.frameStack->Reducer_FrameStack.isEmpty {
|
||||
"Traceback:\n" ++ t.frameStack->Reducer_FrameStack.toString
|
||||
"\nTraceback:\n" ++ t.frameStack->Reducer_FrameStack.toString
|
||||
} else {
|
||||
""
|
||||
}
|
||||
let throw = (t: t) => t->SqException->raise
|
||||
|
||||
let throwMessage = (message: Message.t, frameStack: Reducer_FrameStack.t) =>
|
||||
fromMessage(message, frameStack)->throw
|
||||
let throwMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t) =>
|
||||
fromMessageWithFrameStack(message, frameStack)->throw
|
||||
|
||||
// this shouldn't be used for most runtime errors - the resulting error would have an empty stacktrace
|
||||
let fromException = exn =>
|
||||
switch exn {
|
||||
| SqException(e) => e
|
||||
| Message.MessageException(e) => e->fromMessage(Reducer_CallStack.make())
|
||||
| Js.Exn.Error(obj) =>
|
||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessageWithoutStacktrace
|
||||
| _ => REOther("Unknown exception")->fromMessageWithoutStacktrace
|
||||
| Message.MessageException(e) => e->fromMessage
|
||||
| Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage
|
||||
| _ => REOther("Unknown exception")->fromMessage
|
||||
}
|
||||
|
||||
// converts raw exceptions into exceptions with stacktrace attached
|
||||
// already converted exceptions won't be affected
|
||||
let rethrowWithStacktrace = (fn: unit => 'a, stackTrace: Reducer_CallStack.t) => {
|
||||
let rethrowWithStacktrace = (fn: unit => 'a, frameStack: Reducer_FrameStack.t) => {
|
||||
try {
|
||||
fn()
|
||||
} catch {
|
||||
| SqException(e) => e->throw // exception already has a stacktrace
|
||||
| Message.MessageException(e) => e->throwMessage(stackTrace) // probably comes from FunctionRegistry, adding stacktrace
|
||||
| Message.MessageException(e) => e->throwMessageWithFrameStack(frameStack) // probably comes from FunctionRegistry, adding stacktrace
|
||||
| Js.Exn.Error(obj) =>
|
||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessage(stackTrace)
|
||||
| _ => REOther("Unknown exception")->throwMessage(stackTrace)
|
||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessageWithFrameStack(frameStack)
|
||||
| _ => REOther("Unknown exception")->throwMessageWithFrameStack(frameStack)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user