-
-
Notifications
You must be signed in to change notification settings - Fork 184
Allow throwing arbitrary types and add builtin error classes #724
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5a19a37
46fdc43
a0e3f14
d70d3f9
4f5734b
e88392b
d30982f
771152d
91363b9
11b8610
cdb2754
92b680d
99584de
d1ca812
f0fab20
f28da88
5cd860a
37ba17a
21c27ea
893cc7a
ffa9d6d
613d118
dd77294
df05bb6
cbd980d
52f15c6
ebf0c1c
371c841
b9fdd40
a0b63e1
6066a7e
5400f89
817b4e7
7b23ae3
d6d2855
3dea2cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| interface ErrorType { | ||
| name: string; | ||
| new (...args: any[]): Error; | ||
| } | ||
|
|
||
| function __TS__GetErrorStack(constructor: Function): string { | ||
| let level = 1; | ||
| while (true) { | ||
ark120202 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const info = debug.getinfo(level, "f"); | ||
| level += 1; | ||
| if (!info) { | ||
| // constructor is not in call stack | ||
| level = 1; | ||
| break; | ||
| } else if (info.func === constructor) { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return debug.traceback(undefined, level); | ||
TheLartians marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| function __TS__WrapErrorToString<T extends Error>(getDescription: (this: T) => string): (this: T) => string { | ||
| return function(this: Error): string { | ||
| const description = getDescription.call(this); | ||
| const caller = debug.getinfo(3, "f"); | ||
| if (_VERSION === "Lua 5.1" || (caller && caller.func !== error)) { | ||
| return description; | ||
| } else { | ||
| return `${description}\n${this.stack}`; | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| function __TS__InitErrorClass(Type: ErrorType, name: string): any { | ||
| Type.name = name; | ||
| return setmetatable(Type, { | ||
| __call: (_self: any, message: string) => new Type(message), | ||
| }); | ||
| } | ||
|
|
||
| Error = __TS__InitErrorClass( | ||
| class implements Error { | ||
| public name = "Error"; | ||
| public stack: string; | ||
|
|
||
| constructor(public message = "") { | ||
| this.stack = __TS__GetErrorStack((this.constructor as any).new); | ||
| const metatable = getmetatable(this); | ||
| if (!metatable.__errorToStringPatched) { | ||
| metatable.__errorToStringPatched = true; | ||
| metatable.__tostring = __TS__WrapErrorToString(metatable.__tostring); | ||
| } | ||
| } | ||
|
|
||
| public toString(): string { | ||
| return this.message !== "" ? `${this.name}: ${this.message}` : this.name; | ||
| } | ||
| }, | ||
| "Error" | ||
| ); | ||
|
|
||
| for (const errorName of ["RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"]) { | ||
| globalThis[errorName] = __TS__InitErrorClass( | ||
| class extends Error { | ||
| public name = errorName; | ||
| }, | ||
| errorName | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,23 @@ | ||
| /** @noSelfInFile */ | ||
|
|
||
| declare const _VERSION: string; | ||
| declare function error(...args: any[]): never; | ||
|
|
||
| declare namespace debug { | ||
| function traceback(...args: any[]): string; | ||
|
|
||
| interface FunctionInfo<T extends Function = Function> { | ||
| func: T; | ||
| name?: string; | ||
| namewhat: "global" | "local" | "method" | "field" | ""; | ||
| source: string; | ||
| short_src: string; | ||
| linedefined: number; | ||
| lastlinedefined: number; | ||
| what: "Lua" | "C" | "main"; | ||
| currentline: number; | ||
| nups: number; | ||
| } | ||
|
|
||
| function getinfo(i: number, what?: string): Partial<FunctionInfo>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,3 @@ | ||
| import * as TSTLErrors from "../../src/TSTLErrors"; | ||
| import * as util from "../util"; | ||
|
|
||
| test("throwString", () => { | ||
|
|
@@ -7,12 +6,6 @@ test("throwString", () => { | |
| `.expectToEqual(new util.ExecutionError("Some Error")); | ||
| }); | ||
|
|
||
| test("throwError", () => { | ||
| util.testFunction` | ||
| throw Error("Some Error") | ||
| `.expectToHaveDiagnosticOfError(TSTLErrors.InvalidThrowExpression(util.nodeStub)); | ||
| }); | ||
|
|
||
| test.skip.each([0, 1, 2])("re-throw (%p)", i => { | ||
| util.testFunction` | ||
| const i: number = ${i}; | ||
|
|
@@ -292,3 +285,54 @@ test("return from nested finally", () => { | |
| `; | ||
| expect(util.transpileAndExecute(code)).toBe("finally AB"); | ||
| }); | ||
|
|
||
| test.each([ | ||
| `"error string"`, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be a bit cleaner to pass actual values there and convert them with util.valueToString
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, won't atm due to |
||
| `42`, | ||
| `3.141`, | ||
| `true`, | ||
| `false`, | ||
| `undefined`, | ||
| `{ x: "error object" }`, | ||
| `() => "error function"`, | ||
| ])("throw and catch %s", error => { | ||
| util.testFunction` | ||
| try { | ||
| throw ${error}; | ||
| } catch (error) { | ||
| if (typeof error == 'function') { | ||
| return error(); | ||
| } else { | ||
TheLartians marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return error; | ||
| } | ||
| } | ||
| `.expectToMatchJsResult(); | ||
| }); | ||
|
|
||
| const builtinErrors = ["Error", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"]; | ||
|
|
||
| test.each([...builtinErrors, ...builtinErrors.map(type => `new ${type}`)])("%s properties", errorType => { | ||
| util.testFunction` | ||
| const error = ${errorType}(); | ||
TheLartians marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return { name: error.name, message: error.message, string: error.toString() }; | ||
| `.expectToMatchJsResult(); | ||
| }); | ||
|
|
||
| test.each([...builtinErrors, "CustomError"])("get stack from %s", errorType => { | ||
| const stack = util.testFunction` | ||
| class CustomError extends Error { | ||
| public name = "CustomError"; | ||
| } | ||
|
|
||
| let stack: string | undefined; | ||
|
|
||
| function innerFunction() { stack = new ${errorType}().stack; } | ||
| function outerFunction() { innerFunction(); } | ||
| outerFunction(); | ||
|
|
||
| return stack; | ||
| `.getLuaExecutionResult(); | ||
|
|
||
| expect(stack).toMatch("innerFunction"); | ||
| expect(stack).toMatch("outerFunction"); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.