mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Run a constructor getter that this.constructor reads in Error.captureStackTrace
Inside a class constructor, Error.captureStackTrace(this, this.constructor) now reads this.constructor before the capture when the class or a base class declares a constructor getter, as V8 evaluates the argument: the getter runs and its exception propagates.
This commit is contained in:
@@ -431,6 +431,16 @@ function enclosingConstructorClass(node: ts.Node): ts.ClassLikeDeclaration | und
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Whether `access` (`this.constructor`) reads a `constructor` accessor
|
||||
* declared on the class or a base class. */
|
||||
function readsConstructorAccessor(lowerer: Lowerer, access: ts.PropertyAccessExpression): boolean {
|
||||
const property = lowerer.checker.getPropertyOfType(
|
||||
lowerer.typeOf(access.expression),
|
||||
"constructor",
|
||||
);
|
||||
return property !== undefined && (property.flags & ts.SymbolFlags.GetAccessor) !== 0;
|
||||
}
|
||||
|
||||
/** `Error.captureStackTrace(target, constructorOpt?)`, also through `?.`:
|
||||
* target.stack becomes the target's heading and the current stack frames,
|
||||
* as V8 does it (scr_error_capture_stack_trace). The target is an error, a
|
||||
@@ -504,6 +514,12 @@ export function lowerErrorCaptureStackTraceCall(
|
||||
const sig = resolved !== undefined ? lowerer.fnSigsBySymbol.get(resolved) : undefined;
|
||||
if (constructing && cls !== undefined) {
|
||||
skip = false;
|
||||
// `this.constructor` runs a `constructor` getter of the class, whose
|
||||
// calls and exceptions are observable.
|
||||
if (ts.isPropertyAccessExpression(node) && readsConstructorAccessor(lowerer, node)) {
|
||||
const value = lowerer.lowerExpr(ctor);
|
||||
if (!isSafeToDiscard(value)) evaluated = value;
|
||||
}
|
||||
} else if (sig !== undefined) {
|
||||
lowerer.noteEdge(sig.name);
|
||||
through = sig.name;
|
||||
|
||||
@@ -85,3 +85,32 @@ console.log("limit", frames(outer(new Error("limited")).stack));
|
||||
Error.stackTraceLimit = 0;
|
||||
console.log("limit 0", JSON.stringify(outer(new Error("none")).stack));
|
||||
Error.stackTraceLimit = 10;
|
||||
|
||||
// `this.constructor` in a constructor runs a `constructor` getter, and its
|
||||
// exception propagates.
|
||||
class Watched extends Error {
|
||||
constructor() {
|
||||
super("watched");
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
get ["constructor"](): typeof Watched {
|
||||
console.log("constructor getter ran");
|
||||
return Watched;
|
||||
}
|
||||
}
|
||||
console.log("getter", typeof new Watched().stack);
|
||||
class Throwing extends Error {
|
||||
constructor() {
|
||||
super("throwing");
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
get ["constructor"](): typeof Throwing {
|
||||
throw new RangeError("constructor getter threw");
|
||||
}
|
||||
}
|
||||
try {
|
||||
new Throwing();
|
||||
console.log("getter did not throw");
|
||||
} catch (e: any) {
|
||||
console.log("getter threw", e instanceof RangeError, e.message);
|
||||
}
|
||||
|
||||
@@ -121,3 +121,32 @@ try {
|
||||
} catch (caught) {
|
||||
console.log('throwing constructorOpt', caught.name, caught.message, 'stack' in untouched);
|
||||
}
|
||||
|
||||
// `this.constructor` in a constructor runs a `constructor` getter, and its
|
||||
// exception propagates.
|
||||
class Watched extends Error {
|
||||
constructor() {
|
||||
super('watched');
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
get ['constructor']() {
|
||||
console.log('constructor getter ran');
|
||||
return Watched;
|
||||
}
|
||||
}
|
||||
console.log('getter', typeof new Watched().stack);
|
||||
class Throwing extends Error {
|
||||
constructor() {
|
||||
super('throwing');
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
get ['constructor']() {
|
||||
throw new RangeError('constructor getter threw');
|
||||
}
|
||||
}
|
||||
try {
|
||||
new Throwing();
|
||||
console.log('getter did not throw');
|
||||
} catch (e) {
|
||||
console.log('getter threw', e instanceof RangeError, e.message);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user