IError.ts (3154B)
1 import { LocationRange } from "peggy"; 2 import { Frame, FrameStack } from "../reducer/frameStack.js"; 3 import { ErrorMessage, REJavaScriptExn, REOther } from "./messages.js"; 4 5 // "I" stands for "Internal", since we also have a more public SqError proxy 6 export class IRuntimeError extends Error { 7 // TODO - it would be better to store `m` in `cause`, to like native Error objects do. 8 private constructor( 9 public m: ErrorMessage, 10 public frameStack: FrameStack 11 ) { 12 // Should we pass `m.toString()`? 13 // It'd be a bit costly and we override `IError.toString()` anyway. 14 super(); 15 } 16 17 // This shouldn't be used much, since frame stack will be empty. 18 // But it's useful for global errors, e.g. in SqProject or somethere in the frontend. 19 static fromMessage(message: ErrorMessage) { 20 return new IRuntimeError(message, FrameStack.make()); 21 } 22 23 static fromMessageWithFrameStack( 24 message: ErrorMessage, 25 frameStack: FrameStack 26 ): IRuntimeError { 27 return new IRuntimeError(message, frameStack); 28 } 29 30 // This shouldn't be used for most runtime errors - the resulting error would have an empty framestack. 31 static fromException(exn: unknown) { 32 if (exn instanceof IRuntimeError) { 33 return exn; 34 } else if (exn instanceof ErrorMessage) { 35 return IRuntimeError.fromMessage(exn); 36 } else if (exn instanceof Error) { 37 return IRuntimeError.fromMessage( 38 new REJavaScriptExn(exn.message, exn.name) 39 ); 40 } else { 41 return IRuntimeError.fromMessage(new REOther("Unknown exception")); 42 } 43 } 44 45 override toString() { 46 return this.m.toString(); 47 } 48 49 toStringWithDetails() { 50 return ( 51 this.toString() + 52 (this.frameStack.isEmpty() 53 ? "" 54 : "\nStack trace:\n" + this.frameStack.toString()) 55 ); 56 } 57 58 getTopFrame(): Frame | undefined { 59 return this.frameStack.getTopFrame(); 60 } 61 62 getFrameArray(): Frame[] { 63 return this.frameStack.toFrameArray(); 64 } 65 } 66 67 // converts raw exceptions into exceptions with framestack attached 68 // already converted exceptions won't be affected 69 export function rethrowWithFrameStack( 70 err: unknown, 71 frameStack: FrameStack 72 ): never { 73 if (err instanceof IRuntimeError) { 74 throw err; // exception already has a framestack 75 } else if (err instanceof ErrorMessage) { 76 throw IRuntimeError.fromMessageWithFrameStack(err, frameStack); // probably comes from FunctionRegistry, adding framestack 77 } else if (err instanceof Error) { 78 throw IRuntimeError.fromMessageWithFrameStack( 79 new REJavaScriptExn(err.message, err.name), 80 frameStack 81 ); 82 } else { 83 throw IRuntimeError.fromMessageWithFrameStack( 84 new REOther("Unknown exception"), 85 frameStack 86 ); 87 } 88 } 89 90 export class ICompileError extends Error { 91 constructor( 92 public override message: string, 93 public location: LocationRange 94 ) { 95 super(); 96 } 97 98 override toString() { 99 return this.message; 100 } 101 102 toStringWithDetails() { 103 return ( 104 this.toString() + 105 "\nLocation:\n " + 106 `at line ${this.location.start.line}, column ${this.location.start.column}, file ${this.location.source}` 107 ); 108 } 109 }