mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-28 06:53:42 +08:00
Gate thenable adoption on then fields, keep Promise.prototype bindings checked, and cache the gate on the lowerer
This commit is contained in:
@@ -116,6 +116,7 @@ export function isNativeBuiltinValueInitializer(
|
||||
["parseInt", "parseFloat"].includes(lowerer.stdlibGlobalMember(expr, "Number") ?? "") ||
|
||||
lowerer.stdlibGlobalMember(expr, "Buffer") === "isBuffer" ||
|
||||
lowerer.stdlibGlobalMember(expr, "Array") === "prototype" ||
|
||||
lowerer.stdlibGlobalMember(expr, "Promise") === "prototype" ||
|
||||
(ts.isPropertyAccessExpression(expr.expression) &&
|
||||
lowerer.stdlibGlobalMember(expr.expression, "Array") === "prototype") ||
|
||||
lowerer.stdlibGlobalMember(expr, "Object") === "assign" ||
|
||||
|
||||
@@ -2073,6 +2073,9 @@ export class Lowerer {
|
||||
* setEncoding call whose receiver is a socket or an untyped value.
|
||||
* Computed once on first use (null until then). */
|
||||
socketEncodingReachable: boolean | null = null;
|
||||
/** Whether some program source can define a thenable
|
||||
* (programMayDefineThenables). Null until first asked. */
|
||||
thenablesMayBeDefined: boolean | null = null;
|
||||
/** Whether some server in this program may listen on a Unix socket: a
|
||||
* listen call whose first argument is a string. Computed once on first
|
||||
* use (null until then); address() answers the path only when true. */
|
||||
|
||||
@@ -15,22 +15,19 @@ import { thenReader } from "./checked-promise-all.js";
|
||||
import { builtinGlobalValue } from "./expressions/property-access.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
|
||||
const mayDefineThenables = new WeakMap<Lowerer, boolean>();
|
||||
|
||||
/** Whether any program source can define a thenable: a `then` method or
|
||||
* function, a `then:` key, an assignment to `.then`, or a quoted `'then'`
|
||||
* key. `.then(` calls alone do not count. Programs without one keep the
|
||||
* plain checked-dynamic await. */
|
||||
* function (generic ones too), a `then:` key, a quoted `'then'` key, or an
|
||||
* assignment to `then` (a class field such as `then = (resolve) => ...` or
|
||||
* a `.then` property). `.then(` calls alone do not count. Programs without
|
||||
* one keep the plain checked-dynamic await. */
|
||||
export function programMayDefineThenables(lowerer: Lowerer): boolean {
|
||||
let known = mayDefineThenables.get(lowerer);
|
||||
if (known === undefined) {
|
||||
const pattern = /(^|[^.\w$])then\s*\(|\bthen\s*:|['"`]then['"`]|\.then\s*=(?!=)/m;
|
||||
known = lowerer.program
|
||||
if (lowerer.thenablesMayBeDefined === null) {
|
||||
const pattern = /(^|[^.\w$])then\s*[(<]|\bthen\s*:|['"`]then['"`]|(^|[^\w$])then\s*=(?![=>])/m;
|
||||
lowerer.thenablesMayBeDefined = lowerer.program
|
||||
.getImplementationSourceFiles()
|
||||
.some((sf) => !sf.isDeclarationFile && pattern.test(sf.text));
|
||||
mayDefineThenables.set(lowerer, known);
|
||||
}
|
||||
return known;
|
||||
return lowerer.thenablesMayBeDefined;
|
||||
}
|
||||
|
||||
/** `await v` over a checked-dynamic value: PromiseResolve(v), then the await
|
||||
|
||||
@@ -1514,7 +1514,6 @@ describe("builtin constructor values (JS lane)", () => {
|
||||
["Date.prototype", "Date.prototype"],
|
||||
["RegExp.prototype", "RegExp.prototype"],
|
||||
["Set.prototype", "Set.prototype"],
|
||||
["Promise.prototype", "Promise.prototype"],
|
||||
["WeakMap.prototype", "WeakMap.prototype"],
|
||||
["WeakSet.prototype", "WeakSet.prototype"],
|
||||
["Date.prototype.getYear", "Date.prototype.getYear"],
|
||||
@@ -1547,6 +1546,8 @@ function report(label, e) {
|
||||
${reads.map(([label, read]) => `try { const value = ${read}; console.log(${JSON.stringify(label)}, "read", typeof value); } catch (e) { report(${JSON.stringify(label)}, e); }`).join("\n")}
|
||||
console.log(Date.length, RegExp.length, Promise.length, Object.length, Map.length, Set.length);
|
||||
console.log(Date.name, Map.name, Map.constructor === Function, Object.constructor === Function);
|
||||
const promisePrototype = Promise.prototype;
|
||||
console.log(typeof promisePrototype, typeof promisePrototype.then, promisePrototype.constructor === Promise);
|
||||
`;
|
||||
const r = await compileAndRun("builtin-constructor-members", source, "cjs");
|
||||
expect(r.stderr).toBe("");
|
||||
@@ -1561,7 +1562,12 @@ console.log(Date.name, Map.name, Map.constructor === Function, Object.constructo
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(lines.slice(reads.length)).toEqual(["7 2 1 1 0 0", "Date Map true true"]);
|
||||
// Promise.prototype reads as an object holding the runtime's methods.
|
||||
expect(lines.slice(reads.length)).toEqual([
|
||||
"7 2 1 1 0 0",
|
||||
"Date Map true true",
|
||||
"object function true",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user