Revert "Read error constructors as values and destructure workerData in JavaScript"

This reverts #1776, which made main CI fail.
This commit is contained in:
Chris Tate
2026-10-22 04:04:16 -05:00
parent ce2e517f6b
commit b99094543f
12 changed files with 15 additions and 216 deletions
@@ -36,7 +36,6 @@ import { tryLowerExpression } from "./try-lower-expression.js";
import { fenceSymbolFieldCopy } from "../symbol-fields.js";
import { planUnionRetag } from "../union-retag.js";
import { jsHoldsDynObject, runtimeOptionalStorageOperand } from "../lower-exprs.js";
import { builtinGlobalValue } from "./property-access.js";
/** `{ a: 1, b: "x" }` → recordLit. The record type comes from the
* contextual type when tsc has one (annotated declarations, arguments,
@@ -395,11 +394,9 @@ export function lowerDynObjectLiteral(
const lowerValue = (): IrExpr =>
ts.isMethodDeclaration(prop)
? lowerer.lowerLambda(prop, true)
: ts.isShorthandPropertyAssignment(prop) && shorthandBuiltinValue(lowerer, prop)
? shorthandBuiltinValue(lowerer, prop)!
: !boxValue && ts.isObjectLiteralExpression(valueExpr)
? lowerer.lowerExprExpecting(valueExpr, DYN)
: lowerer.lowerExpr(valueExpr as ts.Expression);
: !boxValue && ts.isObjectLiteralExpression(valueExpr)
? lowerer.lowerExprExpecting(valueExpr, DYN)
: lowerer.lowerExpr(valueExpr as ts.Expression);
raw = fenceClosureProbe(lowerer, valueExpr, undefined, lowerValue) ?? lowerValue();
} catch (err) {
// A PURE member read a JS file cannot lower (a namespace object
@@ -3157,20 +3154,6 @@ function lowerDeclaredSpreadMerge(
/** The value of a shorthand property (`{ x }`): the binding `x` refers to.
* The property name's own symbol is the PROPERTY, so resolution goes
* through getShorthandAssignmentValueSymbol. */
/** `{ TypeError }`: a stdlib global in shorthand position reads as the value
* its name reads as elsewhere (the interned constructor or function value),
* or null when the global has no such value. The shorthand's name carries
* the property's symbol, so the identifier path cannot resolve it. */
function shorthandBuiltinValue(
lowerer: Lowerer,
prop: ts.ShorthandPropertyAssignment,
): IrExpr | null {
const symbol = lowerer.checker.getShorthandAssignmentValueSymbol(prop);
if (!symbol || !lowerer.isStdlibSymbol(symbol)) return null;
const js = isJsSourceFile(ts.getSourceFileOfNode(prop));
return builtinGlobalValue(ts.symbolName(symbol), locOf(prop.name), js);
}
export function lowerShorthandValue(
lowerer: Lowerer,
prop: ts.ShorthandPropertyAssignment,
@@ -3187,8 +3170,6 @@ export function lowerShorthandValue(
const callable = lowerer.lowerBuiltinCallableValue(builtin, loc);
if (callable) return callable;
}
const builtinValue = shorthandBuiltinValue(lowerer, prop);
if (builtinValue) return builtinValue;
if (symbol) {
if (lowerer.ctx.selfSymbol === symbol) {
return { kind: "selfRef", type: lowerer.ctx.selfType!, loc };
@@ -4024,26 +4024,10 @@ function lowerStreamObjectProperty(
return lowerStreamProperty(lowerer, expr, info);
}
/** The error constructors among GLOBAL_CONSTRUCTOR_VALUES. Their statics
* (`Error.stackTraceLimit`, `Error.captureStackTrace`) keep their own
* lowerings, so fenceBuiltinConstructorStaticRead leaves them alone. */
const ERROR_CONSTRUCTOR_VALUES: ReadonlySet<string> = new Set([
"Error",
"TypeError",
"RangeError",
"SyntaxError",
"ReferenceError",
"EvalError",
"URIError",
"AggregateError",
]);
/** The builtin constructors JavaScript sources read as real function
* values rather than identity tokens: the runtime interns one function per
* constructor, and dynamic values answer `.constructor` with the same one,
* so `o.constructor === Object` and `err.constructor === TypeError` are
* exact identity tests, and `err.constructor.name` is the constructor's
* name. */
* so `o.constructor === Object` is an exact identity test. */
const GLOBAL_CONSTRUCTOR_VALUES: ReadonlySet<string> = new Set([
"Object",
"Function",
@@ -4054,7 +4038,6 @@ const GLOBAL_CONSTRUCTOR_VALUES: ReadonlySet<string> = new Set([
"Promise",
"WeakMap",
"WeakSet",
...ERROR_CONSTRUCTOR_VALUES,
]);
/** The builtin global functions JavaScript sources read as interned
@@ -4197,7 +4180,6 @@ function fenceBuiltinConstructorStaticRead(
symbol !== undefined &&
ts.symbolName(symbol).endsWith("Constructor") &&
GLOBAL_CONSTRUCTOR_VALUES.has(ts.symbolName(symbol).slice(0, -"Constructor".length)) &&
!ERROR_CONSTRUCTOR_VALUES.has(ts.symbolName(symbol).slice(0, -"Constructor".length)) &&
lowerer.isStdlibSymbol(symbol)
)
lowerer.stdlibMemberFence(access);
@@ -3889,9 +3889,6 @@ function stdlibGlobalTokenDestructure(
if (decl.initializer === undefined || !isJsSourceFile(ts.getSourceFileOfNode(decl))) return null;
const globalName = stdlibGlobalNameOf(lowerer, decl.initializer);
if (globalName === null) return null;
// `workerData` is a value (the data the worker was started with), not an
// API surface: its members destructure as any untyped value's do.
if (globalName === "workerData") return null;
if (!ts.isObjectBindingPattern(decl.name)) {
lowerer.unsupported(
"SC1031",
@@ -1,6 +0,0 @@
{
"order": [
"<repo>/tests/corpus/6870-error-constructor-values.js"
],
"diags": []
}
@@ -1,6 +0,0 @@
{
"order": [
"<repo>/tests/corpus/6871-worker-data-destructuring.cjs"
],
"diags": []
}
-3
View File
@@ -725,9 +725,6 @@ ScrDyn *scr_dyn_array_constructor(void);
/* The interned builtin constructor `name` (Object, Date, RegExp, Map, Set,
* Promise, WeakMap, WeakSet) as a dynamic function value: +1. */
ScrDyn *scr_dyn_global_constructor(const ScrStr *name);
/* The interned function value of an error constructor ("TypeError"), or
* NULL for other names. */
ScrDyn *scr_dyn_error_constructor(const char *name);
/* A builtin global function (setTimeout, setImmediate, process.nextTick, ...)
* as an interned dynamic function value: +1. */
ScrDyn *scr_dyn_builtin_function(const ScrStr *name);
-2
View File
@@ -1913,8 +1913,6 @@ static ScrDyn *dyn_function_constructor(void) {
ScrDyn *scr_dyn_global_constructor(const ScrStr *name) {
if (strcmp(name->data, dyn_s20) == 0) return dyn_function_constructor();
ScrDyn *error = scr_dyn_error_constructor(name->data);
if (error) return error;
ScrDyn *ctor = dyn_global_ctor(name->data);
if (!ctor) scr_trap(dyn_s3);
return ctor;
+11 -68
View File
@@ -1801,55 +1801,6 @@ static void scr_builtin_object_prototype_cleanup(void) {
scr_dyn_release(prototype);
}
/* The error constructors as values (an error's `constructor`, and the
* globals in JavaScript): one interned function per constructor and thread,
* indexed like scr_error_vts, with the constructor's name and length.
* Calling one through the value is not supported yet, as for the other
* interned constructors (scr_dyn_global_constructor). */
static const char *const scr_error_ctor_names[] = {
"Error", "TypeError", "RangeError", "SyntaxError", NULL,
"ReferenceError", "EvalError", "URIError", "AggregateError",
};
#define SCR_ERROR_CTORS (sizeof scr_error_ctor_names / sizeof scr_error_ctor_names[0])
static SCR_TL ScrDyn *scr_error_ctor_values[SCR_ERROR_CTORS];
static SCR_TL bool scr_error_ctors_registered;
static void scr_error_ctors_cleanup(void) {
for (size_t i = 0; i < SCR_ERROR_CTORS; i++) {
scr_dyn_release(scr_error_ctor_values[i]);
scr_error_ctor_values[i] = NULL;
}
}
static ScrDyn *scr_error_ctor_call(ScrClosure *closure, ScrDyn *const *args, size_t argc) {
(void)args;
(void)argc;
char message[128];
int length = snprintf(message, sizeof message,
"calling the %s constructor through a stored value is not supported yet",
(const char *)closure->fn);
scr_throw_error_msg_code(SCR_ERR_ERROR, message, (size_t)length, "SC2020");
return NULL;
}
ScrDyn *scr_dyn_error_constructor(const char *name) {
for (size_t i = 0; i < SCR_ERROR_CTORS; i++) {
if (!scr_error_ctor_names[i] || strcmp(scr_error_ctor_names[i], name) != 0) continue;
if (!scr_error_ctors_registered) {
scr_error_ctors_registered = true;
scr_atexit(scr_error_ctors_cleanup);
}
if (!scr_error_ctor_values[i]) {
uint32_t length = strcmp(name, "AggregateError") == 0 ? 2 : 1;
scr_error_ctor_values[i] =
scr_dyn_new_func(scr_closure_new((void *)scr_error_ctor_names[i], 0), scr_error_ctor_call, length,
"native:constructor", scr_error_ctor_names[i]);
}
return scr_dyn_retain(scr_error_ctor_values[i]);
}
return NULL;
}
ScrDyn *scr_dyn_object_prototype(void) {
if (!scr_builtin_object_prototype) {
scr_builtin_object_prototype = scr_dyn_alloc(SCR_DYN_OBJ);
@@ -7756,17 +7707,6 @@ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc) {
return out;
}
case SCR_DYN_FUNC: {
/* The interned builtin constructors print with their names, as in Node
* (function Map() { [native code] }); other functions without a source
* text print the anonymous native form. */
if (d->v.fn.sig && strcmp(d->v.fn.sig, "native:constructor") == 0 && d->v.fn.name) {
ScrJsonBuf b;
scr_jb_init(&b);
scr_jb_puts(&b, "function ");
scr_jb_puts(&b, d->v.fn.name);
scr_jb_puts(&b, "() { [native code] }");
return scr_jb_finish(&b);
}
static const char f[] = "function () { [native code] }";
return scr_str_new(f, sizeof f - 1);
}
@@ -11720,18 +11660,21 @@ static ScrDyn *scr_dyn_obj_read_receiver(const ScrDyn *d, const char *key, size_
ScrDyn *own = scr_dyn_obj_get(d, key, key_len);
if (own) return scr_dyn_retain(own);
if (key_len == 11 && memcmp(key, "constructor", 11) == 0) {
/* An error's constructor: the interned function value its global
* reads as (scr_dyn_error_constructor). */
static const char *const names[] = {
"Error", "TypeError", "RangeError", "SyntaxError", NULL,
"ReferenceError", "EvalError", "URIError", "AggregateError",
static const char *const tokens[] = {
"[builtin Error]", "[builtin TypeError]",
"[builtin RangeError]", "[builtin SyntaxError]",
NULL, "[builtin ReferenceError]", "[builtin EvalError]", "[builtin URIError]",
"[builtin AggregateError]",
};
size_t hit = scr_errdyn_find(d, false);
const ScrVt *vt = hit == SIZE_MAX ? NULL : scr_errdyn_cache[hit].err->vt;
for (size_t kind = 0; vt && kind < sizeof names / sizeof names[0]; kind++) {
if (!names[kind]) continue;
for (size_t kind = 0; vt && kind < sizeof tokens / sizeof tokens[0]; kind++) {
if (!tokens[kind]) continue;
if (vt != &scr_error_vts[kind]) continue;
return scr_dyn_error_constructor(names[kind]);
ScrStr *token = scr_str_new(tokens[kind], strlen(tokens[kind]));
ScrDyn *result = scr_dyn_new_str(token);
scr_str_release(token);
return result;
}
}
return scr_dyn_retain(scr_dyn_undefined());
@@ -1,55 +0,0 @@
// The error constructors as values in JavaScript: an error's `constructor`
// is the same function the global names, whose name, length, typeof and
// String() are Node's, for errors the program throws, errors the runtime
// throws, and errors read back from catch bindings and containers.
function describe(label, error) {
const ctor = error.constructor;
console.log(label, error.name, error.constructor.name, typeof ctor, ctor.length, String(ctor));
}
describe('Error', new Error('e'));
describe('TypeError', new TypeError('t'));
describe('RangeError', new RangeError('r'));
describe('SyntaxError', new SyntaxError('s'));
describe('ReferenceError', new ReferenceError('f'));
describe('EvalError', new EvalError('v'));
describe('URIError', new URIError('u'));
describe('AggregateError', new AggregateError([], 'a'));
const thrown = [
() => JSON.parse('{'),
() => {
const holder = JSON.parse('{"value":null}');
return holder.value.missing;
},
() => new Array(-1),
() => decodeURIComponent('%'),
() => {
throw new TypeError('own');
},
];
const constructors = { SyntaxError, TypeError, RangeError, URIError };
const caught = [];
for (const run of thrown) {
try {
run();
} catch (error) {
caught.push(error);
console.log('caught', error.constructor.name, error.constructor === constructors[error.name]);
}
}
const byName = new Map();
for (const error of caught) byName.set(error.constructor.name, (byName.get(error.constructor.name) ?? 0) + 1);
console.log('kinds', JSON.stringify([...byName]));
console.log('identity', new TypeError('x').constructor === TypeError, new RangeError('x').constructor === TypeError);
console.log('globals', Error.name, TypeError.length, AggregateError.length, typeof RangeError, String(SyntaxError));
const renamed = new TypeError('x');
renamed.name = 'Custom';
console.log('renamed', renamed.name, renamed.constructor.name);
class ParseFailure extends SyntaxError {}
const failure = new ParseFailure('p');
console.log('subclass', failure.name, failure.constructor.name, failure instanceof SyntaxError);
@@ -1,30 +0,0 @@
// Destructuring the members of a worker's workerData in JavaScript: plain,
// renamed, defaulted, nested and rest bindings read the data the worker was
// started with, as reading `workerData.<member>` does.
'use strict';
const { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads');
if (isMainThread) {
const sab = new SharedArrayBuffer(8);
const data = { name: 'job-7', size: 3, options: { verbose: true, tags: ['a', 'b'] }, counters: new Int32Array(sab), extra: 1, more: 'x' };
const worker = new Worker(__filename, { workerData: data });
worker.on('message', (lines) => {
for (const line of lines) console.log(line);
console.log('main sees', new Int32Array(sab)[0]);
});
} else {
const lines = [];
const { name, size } = workerData;
lines.push(`plain ${name} ${size}`);
const { name: jobName, missing = 'fallback', size: count = 10 } = workerData;
lines.push(`renamed ${jobName} ${missing} ${count}`);
const {
options: { verbose, tags: [firstTag, ...otherTags] },
} = workerData;
lines.push(`nested ${verbose} ${firstTag} ${JSON.stringify(otherTags)}`);
const { name: ignored, size: alsoIgnored, options, counters, ...rest } = workerData;
lines.push(`rest ${JSON.stringify(rest)} ${typeof options} ${ignored === name && alsoIgnored === size}`);
counters[0] = 41;
counters[0] += 1;
parentPort.postMessage(lines);
}
@@ -1 +0,0 @@
{ "plain": 1200, "san": 3400 }
@@ -1 +0,0 @@
{ "plain": 1300, "san": 3490 }