@scriptc/compiler 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient/scriptc-node-fallback.d.ts +54 -9
- package/dist/backend/emission/emit-exprs.js +170 -14
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emit-walkers.js +29 -2
- package/dist/backend/emission/emit-walkers.js.map +1 -1
- package/dist/backend/emission/emitter.js +25 -1
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +1 -0
- package/dist/backend/llvm/dyn.js +83 -1
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.js +210 -13
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/coverage/surface-manifest.js +16 -2
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +41 -14
- package/dist/diagnostics/diagnostic.js +79 -27
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/diagnostics/render.js +8 -0
- package/dist/diagnostics/render.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.js +11 -1
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +69 -0
- package/dist/frontend/lowering/lower-builtins.js +380 -4
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +41 -0
- package/dist/frontend/lowering/lower-calls.js +559 -11
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +7 -0
- package/dist/frontend/lowering/lower-classes.js +51 -1
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +21 -0
- package/dist/frontend/lowering/lower-containers.js +262 -17
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.js +92 -17
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-emitter.d.ts +70 -3
- package/dist/frontend/lowering/lower-emitter.js +436 -29
- package/dist/frontend/lowering/lower-emitter.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +80 -5
- package/dist/frontend/lowering/lower-exprs.js +651 -23
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.d.ts +8 -5
- package/dist/frontend/lowering/lower-modules.js +144 -19
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-namespaces.d.ts +39 -11
- package/dist/frontend/lowering/lower-namespaces.js +166 -24
- package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
- package/dist/frontend/lowering/lower-server.js +211 -4
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.d.ts +1 -5
- package/dist/frontend/lowering/lower-stmts.js +293 -10
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +178 -10
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +53 -1
- package/dist/frontend/lowering/lowerer.js +210 -19
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +44 -0
- package/dist/frontend/lowering/surfaces.js +234 -3
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/npm.d.ts +12 -0
- package/dist/frontend/npm.js +30 -0
- package/dist/frontend/npm.js.map +1 -1
- package/dist/frontend/program.d.ts +25 -0
- package/dist/frontend/program.js +144 -135
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/shared.d.ts +1 -1
- package/dist/frontend/shared.js +1 -1
- package/dist/frontend/shared.js.map +1 -1
- package/dist/frontend/types.d.ts +13 -0
- package/dist/frontend/types.js +84 -7
- package/dist/frontend/types.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +157 -12
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +163 -13
- package/dist/ir/nodes.js +65 -6
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +64 -5
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/fence-eval.d.ts +77 -0
- package/dist/library/fence-eval.js +395 -0
- package/dist/library/fence-eval.js.map +1 -0
- package/dist/library/int-infer.d.ts +119 -0
- package/dist/library/int-infer.js +1301 -0
- package/dist/library/int-infer.js.map +1 -0
- package/dist/library/int-infer.test.d.ts +1 -0
- package/dist/library/int-infer.test.js +279 -0
- package/dist/library/int-infer.test.js.map +1 -0
- package/dist/library/profile.d.ts +43 -6
- package/dist/library/profile.js +179 -31
- package/dist/library/profile.js.map +1 -1
- package/dist/library/sidecar.d.ts +23 -0
- package/dist/library/sidecar.js +150 -16
- package/dist/library/sidecar.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +208 -10
|
@@ -13,7 +13,7 @@ import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.
|
|
|
13
13
|
import { requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
14
14
|
import { mixinFnShapeOf } from "./lower-mixins.js";
|
|
15
15
|
import { bufEncoding, dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerRegexMethodCall, lowerStringMethodCall, lowerTupleReadMethodCall } from "./lower-containers.js";
|
|
16
|
-
import { lowerChildStreamMethodCall, lowerDirentMethodCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
|
16
|
+
import { lowerChildStreamMethodCall, lowerCreateRequireCall, lowerDirentMethodCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
|
17
17
|
import { droppableStatic, lowerPromiseAllTupleCall, lowerPromiseRejectCall, probeLower, templateRawTextOf } from "./lower-exprs.js";
|
|
18
18
|
import { httpClientFnBindingOf, isStreamUndefCallExpr, lowerHttpClientFnCall } from "./lower-server.js";
|
|
19
19
|
import { EMITTER_API_MEMBERS, exactInstanceClassOf, findGenericMethodOn, lowerClassGenericMethodCall, lowerStaticMethodCall } from "./lower-classes.js";
|
|
@@ -2273,6 +2273,58 @@ export function lowerTimersMemberCall(L, expr, member, loc) {
|
|
|
2273
2273
|
}
|
|
2274
2274
|
export function lowerCall(L, expr) {
|
|
2275
2275
|
const loc = locOf(expr);
|
|
2276
|
+
// A call whose chain ROOTS at an ambient-undefined name (`declare
|
|
2277
|
+
// const value: Y | undefined; value?.foo("a")`, `declare function
|
|
2278
|
+
// chain...; chain(o).mapValues(f).value()`, a trap binding's read):
|
|
2279
|
+
// Node evaluates the root FIRST and throws the catchable
|
|
2280
|
+
// ReferenceError before any member, type argument, or argument runs —
|
|
2281
|
+
// the whole call IS that throw, typed by the use site (arguments
|
|
2282
|
+
// never lower; Node never evaluates them). Claimed before every
|
|
2283
|
+
// intrinsic and dispatch path: no lowering can answer differently
|
|
2284
|
+
// when the root read itself is the crash.
|
|
2285
|
+
{
|
|
2286
|
+
const root = ambientUndefVarRootOf(L, expr);
|
|
2287
|
+
if (root !== null) {
|
|
2288
|
+
const mapped = L.mapTypeOf(L.typeOf(expr));
|
|
2289
|
+
const t = mapped && mapped.kind !== "void" && !L.typeNamesUnregisteredClass(mapped)
|
|
2290
|
+
? mapped
|
|
2291
|
+
: (contextualUndefReadType(L, expr) ?? F64);
|
|
2292
|
+
return nsUndefRead(L, root.text, expr, t);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
// A method call through a NULLISH binding (`const i: I<A & B> = null
|
|
2296
|
+
// as any; i.fn(...)` — the receiver provably holds null/undefined
|
|
2297
|
+
// forever): the member READ throws Node's exact TypeError before any
|
|
2298
|
+
// argument evaluates — the whole call lowers to that throw. Claimed
|
|
2299
|
+
// when the receiver's type has no mapping (no other story exists) or
|
|
2300
|
+
// the member is a generic signature (the alternative is the
|
|
2301
|
+
// interface-dispatch fence — the runtime truth is this throw).
|
|
2302
|
+
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
2303
|
+
ts.isIdentifier(expr.expression.expression) &&
|
|
2304
|
+
expr.expression.questionDotToken === undefined) {
|
|
2305
|
+
const recvSym = L.resolveValueSymbol(expr.expression.expression);
|
|
2306
|
+
const unit = nullishValueUnitOf(L, recvSym);
|
|
2307
|
+
if (unit !== null) {
|
|
2308
|
+
const recvUnmappable = recvSym !== null && L.mapTypeOf(L.checker.getTypeOfSymbol(recvSym)) === null;
|
|
2309
|
+
const propSym = L.checker.getPropertyOfType(L.typeOf(expr.expression.expression), expr.expression.name.text);
|
|
2310
|
+
const genericMember = propSym !== undefined && propSym !== null &&
|
|
2311
|
+
isGenericCallableMemberType(L.checker.getTypeOfSymbol(propSym), L.checker);
|
|
2312
|
+
if (recvUnmappable || genericMember) {
|
|
2313
|
+
const mapped = L.mapTypeOf(L.typeOf(expr));
|
|
2314
|
+
const t = mapped && mapped.kind !== "void" && !L.typeNamesUnregisteredClass(mapped) ? mapped : F64;
|
|
2315
|
+
return nodeThrowExpr(1, "", `Cannot read properties of ${unit} (reading '${expr.expression.name.text}')`, t, loc);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
// `require("spec")` through a createRequire binding (and the inline
|
|
2320
|
+
// `createRequire(import.meta.url)("spec")` spelling — a CallExpression
|
|
2321
|
+
// callee no other dispatch path serves): the static erasure —
|
|
2322
|
+
// builtins/json/npm per lowerCreateRequireCall's arms.
|
|
2323
|
+
{
|
|
2324
|
+
const crServed = lowerCreateRequireCall(L, expr, loc);
|
|
2325
|
+
if (crServed)
|
|
2326
|
+
return crServed;
|
|
2327
|
+
}
|
|
2276
2328
|
// `process.getuid?.()` — intercepted BEFORE the optional-chain
|
|
2277
2329
|
// machinery (the member always exists on a POSIX target, so the
|
|
2278
2330
|
// optional call IS the call; `process.getuid` itself has no value
|
|
@@ -2685,12 +2737,37 @@ export function lowerCall(L, expr) {
|
|
|
2685
2737
|
return islandPrimitiveExit(L, expr, result);
|
|
2686
2738
|
}
|
|
2687
2739
|
if (L.isIslandExpr(expr.expression)) {
|
|
2740
|
+
// `o.m(...)` where o LOWERS checked-dynamic and the checker types
|
|
2741
|
+
// `o.m` 'any' (a member read behind an 'object'/'unknown'-typed
|
|
2742
|
+
// bag): METHOD-CALL semantics — receiver-kind dispatch (dynInvoke),
|
|
2743
|
+
// so `this` binds and a WRAPPED island receiver runs the ENGINE's
|
|
2744
|
+
// own method (the routed-ops lane). A stored-member dynCall would
|
|
2745
|
+
// call engine prototype methods receiverless — the this-less
|
|
2746
|
+
// `list.slice()` ToObject TypeError. Spread arguments keep the
|
|
2747
|
+
// stored-member path below (the runtime-arity lane owns them).
|
|
2748
|
+
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
2749
|
+
!expr.expression.questionDotToken &&
|
|
2750
|
+
!expr.questionDotToken &&
|
|
2751
|
+
!expr.arguments.some((a) => ts.isSpreadElement(a))) {
|
|
2752
|
+
const recvProbe = probeLower(L, expr.expression.expression);
|
|
2753
|
+
if (recvProbe?.type.kind === "dyn") {
|
|
2754
|
+
const args = expr.arguments.map((a) => L.lowerExprExpecting(a, DYN));
|
|
2755
|
+
return {
|
|
2756
|
+
kind: "dynInvoke",
|
|
2757
|
+
recv: recvProbe,
|
|
2758
|
+
method: expr.expression.name.text,
|
|
2759
|
+
calleeName: expr.expression.getText(),
|
|
2760
|
+
args,
|
|
2761
|
+
type: DYN,
|
|
2762
|
+
loc,
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2688
2766
|
const callee = L.lowerExpr(expr.expression);
|
|
2689
2767
|
// A checker-`any` callee that LOWERED checked-dynamic (a dyn member
|
|
2690
2768
|
// chain's stored function): the DOM's own call — dynCall reads and
|
|
2691
2769
|
// calls the stored member with Node's is-not-a-function TypeError
|
|
2692
|
-
// on refusal.
|
|
2693
|
-
// dynCall rule (no jsval→DOM bridge exists — the named fence).
|
|
2770
|
+
// on refusal.
|
|
2694
2771
|
if (callee.type.kind === "dyn") {
|
|
2695
2772
|
const args = expr.arguments.map((a) => L.lowerExprExpecting(a, DYN));
|
|
2696
2773
|
const calleeName = ts.isPropertyAccessExpression(expr.expression)
|
|
@@ -2807,6 +2884,12 @@ export function lowerCall(L, expr) {
|
|
|
2807
2884
|
const fsTs = L.lowerFsToUnixTimestampCall(expr, bi, loc);
|
|
2808
2885
|
if (fsTs)
|
|
2809
2886
|
return fsTs;
|
|
2887
|
+
// The fs validation-ladder spoke (checked-dynamic lane): misuse
|
|
2888
|
+
// of implemented-namespace members throws Node's typed errors
|
|
2889
|
+
// instead of meeting the table fence.
|
|
2890
|
+
const fsLadder = L.lowerFsLadderCall(expr, bi, loc);
|
|
2891
|
+
if (fsLadder)
|
|
2892
|
+
return fsLadder;
|
|
2810
2893
|
const builtinFn = builtinModuleFnOf(L, bi.module, bi.member);
|
|
2811
2894
|
if (!builtinFn) {
|
|
2812
2895
|
// Typed by @types/node (the fallback declarations only declare
|
|
@@ -3704,10 +3787,17 @@ function lowerDynReceiverMethodCall(L, call, access) {
|
|
|
3704
3787
|
const enc = call.arguments[0]
|
|
3705
3788
|
? bufEncoding(L, "toString", call.arguments[0])
|
|
3706
3789
|
: "utf8";
|
|
3790
|
+
// The source spelling rides along for the ONE receiver whose
|
|
3791
|
+
// prototype lacks toString: a null-prototype dictionary throws
|
|
3792
|
+
// Node's "<spelling> is not a function" at runtime.
|
|
3707
3793
|
return {
|
|
3708
3794
|
kind: "libCall",
|
|
3709
3795
|
fn: "dyn.toString",
|
|
3710
|
-
args: [
|
|
3796
|
+
args: [
|
|
3797
|
+
recv,
|
|
3798
|
+
{ kind: "strLit", value: enc, type: STRING, loc: locOf(call) },
|
|
3799
|
+
{ kind: "strLit", value: access.getText(), type: STRING, loc: locOf(call) },
|
|
3800
|
+
],
|
|
3711
3801
|
type: STRING,
|
|
3712
3802
|
loc: locOf(call),
|
|
3713
3803
|
};
|
|
@@ -6152,6 +6242,57 @@ function lowerObjectStaticCall(L, call, access) {
|
|
|
6152
6242
|
}
|
|
6153
6243
|
L.noLowering(`Object.is over '${L.fmt(left.type)}' and '${L.fmt(right.type)}' operands`, call, "the operands must share one comparable kind (numbers, strings, booleans, units, one union shape, or one reference type)");
|
|
6154
6244
|
}
|
|
6245
|
+
// Object.create — the null-prototype DICTIONARY (`Object.create(null)`
|
|
6246
|
+
// then keyed assignment, the memo-table idiom prettier's index/
|
|
6247
|
+
// group-mode maps spell) and, under --dynamic, the engine's own
|
|
6248
|
+
// Object.create for engine-held prototypes. Everything else is a
|
|
6249
|
+
// NAMED fence: the compiled representations have no prototype chain,
|
|
6250
|
+
// and the own-copy stand-in would answer WRONG observably — Node's
|
|
6251
|
+
// Object.keys/inspect/JSON of the created object list NO own keys,
|
|
6252
|
+
// and mutating the prototype afterwards is visible through the
|
|
6253
|
+
// created object (live delegation), which no copy can honor.
|
|
6254
|
+
if (member === "create") {
|
|
6255
|
+
if (call.arguments.some((a) => ts.isSpreadElement(a))) {
|
|
6256
|
+
L.noLowering("Object.create with spread arguments", call);
|
|
6257
|
+
}
|
|
6258
|
+
if (call.arguments.length >= 2) {
|
|
6259
|
+
L.noLowering("Object.create with a properties-descriptor argument", call, "create first, then assign: const o = Object.create(null); o.k = v");
|
|
6260
|
+
}
|
|
6261
|
+
if (call.arguments.length !== 1) {
|
|
6262
|
+
L.noLowering(`Object.create with ${call.arguments.length} arguments`, call);
|
|
6263
|
+
}
|
|
6264
|
+
const loc = locOf(call);
|
|
6265
|
+
let protoNode = call.arguments[0];
|
|
6266
|
+
while (ts.isParenthesizedExpression(protoNode))
|
|
6267
|
+
protoNode = protoNode.expression;
|
|
6268
|
+
const nullProto = protoNode.kind === ts.SyntaxKind.NullKeyword;
|
|
6269
|
+
if (L.dynamic) {
|
|
6270
|
+
// The checker types the result `any` — an ENGINE value under
|
|
6271
|
+
// --dynamic — and the engine's own Object.create answers with
|
|
6272
|
+
// REAL prototype semantics: reads delegate LIVE, writes shadow,
|
|
6273
|
+
// and inspect renders Node's exact shapes ("[Object: null
|
|
6274
|
+
// prototype]" included). null and engine-held (jsval) prototypes
|
|
6275
|
+
// route; checked-dynamic (DOM) prototypes keep the named fence —
|
|
6276
|
+
// their marshal into the engine is a DEEP COPY, so a later
|
|
6277
|
+
// prototype mutation would be invisible through the created
|
|
6278
|
+
// object where Node delegates live.
|
|
6279
|
+
const objectGlobal = () => ({ kind: "jsOp", op: "globalGet", name: "Object", args: [], type: JSVAL, loc });
|
|
6280
|
+
if (nullProto) {
|
|
6281
|
+
const nullIn = { kind: "jsOp", op: "nullLit", args: [], type: JSVAL, loc };
|
|
6282
|
+
return { kind: "jsOp", op: "callMethod", name: "create", args: [objectGlobal(), nullIn], type: JSVAL, loc };
|
|
6283
|
+
}
|
|
6284
|
+
const proto = L.lowerExpr(protoNode);
|
|
6285
|
+
if (proto.type.kind === "jsval") {
|
|
6286
|
+
return { kind: "jsOp", op: "callMethod", name: "create", args: [objectGlobal(), proto], type: JSVAL, loc };
|
|
6287
|
+
}
|
|
6288
|
+
L.noLowering(`Object.create over '${L.fmt(proto.type)}' prototypes`, call, "prototype reads delegate LIVE in Node (mutating the prototype shows through the created object), which the boundary's deep copy cannot honor — only null and engine-held ('any') prototypes lower");
|
|
6289
|
+
}
|
|
6290
|
+
if (nullProto) {
|
|
6291
|
+
return { kind: "libCall", fn: "dyn.objCreateNullProto", args: [], type: DYN, loc };
|
|
6292
|
+
}
|
|
6293
|
+
const proto = L.lowerExpr(protoNode);
|
|
6294
|
+
L.noLowering(`Object.create over '${L.fmt(proto.type)}' prototypes`, call, "the compiled representations have no prototype chain, and an own-copy would answer wrong observably (Node lists NO own keys on the created object, and prototype mutations show through it live) — only Object.create(null) lowers");
|
|
6295
|
+
}
|
|
6155
6296
|
// `Object.assign(fn, { props })` whose RESULT type maps to the hybrid
|
|
6156
6297
|
// (function-with-properties) record: the chalk-shape CONSTRUCTOR.
|
|
6157
6298
|
if (member === "assign") {
|
|
@@ -6228,6 +6369,91 @@ function lowerObjectStaticCall(L, call, access) {
|
|
|
6228
6369
|
}
|
|
6229
6370
|
}
|
|
6230
6371
|
}
|
|
6372
|
+
// `Object.assign(target, ...sources)` over a CHECKED-DYNAMIC target
|
|
6373
|
+
// — the n-ary/spread form (`Object.assign({}, ...plugins.map(p =>
|
|
6374
|
+
// p.options), coreOptions)`, support.js's option-table merge). The
|
|
6375
|
+
// sources pack into one fresh DOM array FIRST — plain sources
|
|
6376
|
+
// retain in, spread sources flatten through the spread-call walk
|
|
6377
|
+
// (V8's exact TypeError texts, the source spelling carried for the
|
|
6378
|
+
// nullish form) — so every source evaluates and flattens before any
|
|
6379
|
+
// copying (JS's ArgumentListEvaluation: a throwing spread leaves
|
|
6380
|
+
// the target untouched), then one runtime walk copies each source's
|
|
6381
|
+
// own enumerable keys left to right and answers the TARGET
|
|
6382
|
+
// (identity, like JS). Each source must enter the dyn world (dyn
|
|
6383
|
+
// already, or dynFrom's JSON-safe conversion — a STATIC array
|
|
6384
|
+
// spread copies in at the boundary, the documented aliasing
|
|
6385
|
+
// stance); anything else keeps the fence. Targets: dyn values, a
|
|
6386
|
+
// FRESH object-literal target (`Object.assign({}, ...)` — no alias
|
|
6387
|
+
// exists, so building it as a DOM object instead of a record is
|
|
6388
|
+
// unobservable), or a nullish unit (Node's ToObject TypeError
|
|
6389
|
+
// throws at the call, catchably); aliased record targets keep the
|
|
6390
|
+
// fence — their identity could not survive the conversion.
|
|
6391
|
+
if (call.arguments.length >= 1 && !ts.isSpreadElement(call.arguments[0])) {
|
|
6392
|
+
let targetNode = call.arguments[0];
|
|
6393
|
+
while (ts.isParenthesizedExpression(targetNode))
|
|
6394
|
+
targetNode = targetNode.expression;
|
|
6395
|
+
const freshLiteralTarget = ts.isObjectLiteralExpression(targetNode);
|
|
6396
|
+
const tProbe = freshLiteralTarget ? null : probeLower(L, call.arguments[0]);
|
|
6397
|
+
const tKind = tProbe?.type.kind;
|
|
6398
|
+
if (freshLiteralTarget || tKind === "dyn" || tKind === "nullT" || tKind === "undefinedT") {
|
|
6399
|
+
const loc = locOf(call);
|
|
6400
|
+
const target = L.lowerExprExpecting(call.arguments[0], DYN);
|
|
6401
|
+
if (target.type.kind === "dyn") {
|
|
6402
|
+
const t = L.declareHiddenLocal("%oat", DYN);
|
|
6403
|
+
const p = L.declareHiddenLocal("%oap", DYN);
|
|
6404
|
+
const tRef = () => ({ kind: "varRef", localId: t.id, type: DYN, loc });
|
|
6405
|
+
const pRef = () => ({ kind: "varRef", localId: p.id, type: DYN, loc });
|
|
6406
|
+
const stmts = [
|
|
6407
|
+
{ kind: "varDecl", localId: t.id, init: target, loc },
|
|
6408
|
+
{ kind: "varDecl", localId: p.id, init: { kind: "dynArrLit", elems: [], type: DYN, loc }, loc },
|
|
6409
|
+
];
|
|
6410
|
+
// V8 spells the optimized apply-path texts (the expression
|
|
6411
|
+
// named for a nullish source) only when the spread is the
|
|
6412
|
+
// SINGLE LAST argument; every other spread position drives
|
|
6413
|
+
// the real iterator protocol, whose failure describes the
|
|
6414
|
+
// value — the two runtime variants, picked here by position.
|
|
6415
|
+
const sources = call.arguments.slice(1);
|
|
6416
|
+
const spreadCount = sources.filter((a) => ts.isSpreadElement(a)).length;
|
|
6417
|
+
let ok = true;
|
|
6418
|
+
for (let i = 0; i < sources.length; i++) {
|
|
6419
|
+
const argNode = sources[i];
|
|
6420
|
+
const spread = ts.isSpreadElement(argNode);
|
|
6421
|
+
const srcNode = spread ? argNode.expression : argNode;
|
|
6422
|
+
const src = L.coerceToExpected(L.lowerExpr(srcNode), DYN);
|
|
6423
|
+
if (src.type.kind !== "dyn") {
|
|
6424
|
+
ok = false;
|
|
6425
|
+
break;
|
|
6426
|
+
}
|
|
6427
|
+
const argLoc = locOf(argNode);
|
|
6428
|
+
const optimized = spreadCount === 1 && i === sources.length - 1;
|
|
6429
|
+
stmts.push({
|
|
6430
|
+
kind: "exprStmt",
|
|
6431
|
+
expr: spread
|
|
6432
|
+
? optimized
|
|
6433
|
+
? {
|
|
6434
|
+
kind: "libCall",
|
|
6435
|
+
fn: "dyn.packPushSpread",
|
|
6436
|
+
args: [pRef(), src, { kind: "strLit", value: srcNode.getText(), type: STRING, loc: argLoc }],
|
|
6437
|
+
type: VOID,
|
|
6438
|
+
loc: argLoc,
|
|
6439
|
+
}
|
|
6440
|
+
: { kind: "libCall", fn: "dyn.packPushSpreadIter", args: [pRef(), src], type: VOID, loc: argLoc }
|
|
6441
|
+
: { kind: "libCall", fn: "dyn.packPush", args: [pRef(), src], type: VOID, loc: argLoc },
|
|
6442
|
+
loc: argLoc,
|
|
6443
|
+
});
|
|
6444
|
+
}
|
|
6445
|
+
if (ok) {
|
|
6446
|
+
return {
|
|
6447
|
+
kind: "seqExpr",
|
|
6448
|
+
stmts,
|
|
6449
|
+
result: { kind: "libCall", fn: "dyn.assignAll", args: [tRef(), pRef()], type: DYN, loc },
|
|
6450
|
+
type: DYN,
|
|
6451
|
+
loc,
|
|
6452
|
+
};
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6231
6457
|
return null;
|
|
6232
6458
|
}
|
|
6233
6459
|
// Object.defineProperties over a CHECKED-DYNAMIC target (test/common's
|
|
@@ -6864,6 +7090,305 @@ export function bindingNeverReassigned(L, sym, decl) {
|
|
|
6864
7090
|
L.neverReassignedCache.set(sym, !written);
|
|
6865
7091
|
return !written;
|
|
6866
7092
|
}
|
|
7093
|
+
/** Strips the value-preserving wrappers off an expression: parens,
|
|
7094
|
+
* non-null assertions, `as`/`satisfies`/angle-bracket casts. What
|
|
7095
|
+
* remains is the expression that actually evaluates. */
|
|
7096
|
+
export function stripValueWrappers(e) {
|
|
7097
|
+
let v = e;
|
|
7098
|
+
for (;;) {
|
|
7099
|
+
if (ts.isParenthesizedExpression(v) || ts.isNonNullExpression(v) ||
|
|
7100
|
+
ts.isAsExpression(v) || ts.isSatisfiesExpression(v) || ts.isTypeAssertion(v)) {
|
|
7101
|
+
v = v.expression;
|
|
7102
|
+
continue;
|
|
7103
|
+
}
|
|
7104
|
+
return v;
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
/** The nullish unit `e` provably evaluates to: a bare null/undefined
|
|
7108
|
+
* literal (assertion-wrapped — `null as any`, `null!`), or a read of a
|
|
7109
|
+
* registered NULLISH binding (nullishGenericBindingUnitOf). Null when
|
|
7110
|
+
* the value could be anything else. */
|
|
7111
|
+
export function nullishExprUnitOf(L, e) {
|
|
7112
|
+
const v = stripValueWrappers(e);
|
|
7113
|
+
if (v.kind === ts.SyntaxKind.NullKeyword)
|
|
7114
|
+
return "null";
|
|
7115
|
+
if (ts.isIdentifier(v)) {
|
|
7116
|
+
if (v.text === "undefined" && (L.typeOf(v).flags & ts.TypeFlags.Undefined) !== 0) {
|
|
7117
|
+
return "undefined";
|
|
7118
|
+
}
|
|
7119
|
+
return nullishValueUnitOf(L, L.resolveValueSymbol(v));
|
|
7120
|
+
}
|
|
7121
|
+
return null;
|
|
7122
|
+
}
|
|
7123
|
+
/** The nullish unit a binding provably holds FOREVER, by VALUE alone: its
|
|
7124
|
+
* initializer is nullish (`const i: I<A & B> = null as any`) and every
|
|
7125
|
+
* write in its declaring file is nullish too (`a = b` where b is
|
|
7126
|
+
* another nullish binding). No type condition — callers add their own
|
|
7127
|
+
* (nullishGenericBindingUnitOf gates the no-storage family on
|
|
7128
|
+
* unmappable types; the generic-method call path rescues its fence with
|
|
7129
|
+
* the value fact alone). Cached per symbol; the pre-seeded null entry
|
|
7130
|
+
* guards probe cycles (mutually-assigned bindings resolve link by link,
|
|
7131
|
+
* declaration order). */
|
|
7132
|
+
export function nullishValueUnitOf(L, sym) {
|
|
7133
|
+
if (!sym)
|
|
7134
|
+
return null;
|
|
7135
|
+
const cached = L.nullishBindings.get(sym);
|
|
7136
|
+
if (cached !== undefined)
|
|
7137
|
+
return cached;
|
|
7138
|
+
L.nullishBindings.set(sym, null); // cycle guard: self-referential probes answer non-qualifying
|
|
7139
|
+
const decl = L.checker.valueDeclarationOf(sym);
|
|
7140
|
+
// Statement-position declarators only: a for-loop head (`for (let x =
|
|
7141
|
+
// null as any; ...)`) declares a LOCAL with per-iteration semantics —
|
|
7142
|
+
// lowerVarDeclList's contract requires a lowered statement for it, so
|
|
7143
|
+
// the no-storage family never claims it.
|
|
7144
|
+
if (!decl || !ts.isVariableDeclaration(decl) || !ts.isIdentifier(decl.name) ||
|
|
7145
|
+
decl.getSourceFile().isDeclarationFile || decl.initializer === undefined ||
|
|
7146
|
+
!ts.isVariableStatement(decl.parent.parent)) {
|
|
7147
|
+
return null;
|
|
7148
|
+
}
|
|
7149
|
+
const unit = nullishExprUnitOf(L, decl.initializer);
|
|
7150
|
+
if (unit === null)
|
|
7151
|
+
return null;
|
|
7152
|
+
// Bindings with a CHECKED-DYNAMIC fallback (`const maybe: any =
|
|
7153
|
+
// undefined`, JS inference residue) keep that story: the dyn world
|
|
7154
|
+
// already holds null/undefined correctly and serves every read form
|
|
7155
|
+
// (optional chains included) — this family exists for types with NO
|
|
7156
|
+
// other home.
|
|
7157
|
+
if (dynFallbackType(L, decl.name, L.checker.getTypeOfSymbol(sym)) !== null)
|
|
7158
|
+
return null;
|
|
7159
|
+
if (!allWritesNullish(L, sym, decl))
|
|
7160
|
+
return null;
|
|
7161
|
+
// A use inside a class HERITAGE clause (`class X extends Mixin(...)`)
|
|
7162
|
+
// declines the whole family: heritage resolution is structural (the
|
|
7163
|
+
// mixin machinery can pin the instantiation from the ARGUMENT class
|
|
7164
|
+
// expression without ever reading the callee binding), so a claimed
|
|
7165
|
+
// nullish callee would compile a working class where Node throws
|
|
7166
|
+
// "Mixin is not a function" evaluating the extends expression. The
|
|
7167
|
+
// declaration keeps its type fence instead.
|
|
7168
|
+
if (usedInHeritageClause(L, sym))
|
|
7169
|
+
return null;
|
|
7170
|
+
L.nullishBindings.set(sym, unit);
|
|
7171
|
+
return unit;
|
|
7172
|
+
}
|
|
7173
|
+
/** True when any identifier resolving to `sym` sits inside a class
|
|
7174
|
+
* heritage clause anywhere in the program. */
|
|
7175
|
+
function usedInHeritageClause(L, sym) {
|
|
7176
|
+
const symText = sym.name;
|
|
7177
|
+
let found = false;
|
|
7178
|
+
const visit = (n) => {
|
|
7179
|
+
if (found)
|
|
7180
|
+
return;
|
|
7181
|
+
if (ts.isIdentifier(n) && n.text === symText &&
|
|
7182
|
+
L.resolveValueSymbol(n) === sym) {
|
|
7183
|
+
for (let p = n.parent; p !== undefined && !ts.isSourceFile(p); p = p.parent) {
|
|
7184
|
+
if (ts.isHeritageClause(p)) {
|
|
7185
|
+
found = true;
|
|
7186
|
+
return;
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
return;
|
|
7190
|
+
}
|
|
7191
|
+
n.forEachChild(visit);
|
|
7192
|
+
};
|
|
7193
|
+
for (const file of L.program.getSourceFiles()) {
|
|
7194
|
+
if (found)
|
|
7195
|
+
break;
|
|
7196
|
+
if (file.isDeclarationFile)
|
|
7197
|
+
continue;
|
|
7198
|
+
file.forEachChild(visit);
|
|
7199
|
+
}
|
|
7200
|
+
return found;
|
|
7201
|
+
}
|
|
7202
|
+
/** nullishValueUnitOf gated on a declared type that CANNOT hold the
|
|
7203
|
+
* value — the NO-STORAGE family: an unmappable type has no other story,
|
|
7204
|
+
* and a RECORD-mapped one (`const i: I<A & B> = null as any` — an
|
|
7205
|
+
* interface whose members are all generic signatures interns an empty
|
|
7206
|
+
* shape) has a slot null can never inhabit, so storing would throw the
|
|
7207
|
+
* representation error where Node stores null silently. Either way the
|
|
7208
|
+
* declaration emits nothing and reads know the value. Null-tolerant
|
|
7209
|
+
* mappings (unions with a null/undefined arm, dyn) keep their real
|
|
7210
|
+
* storage and every ordinary lowering. */
|
|
7211
|
+
export function nullishGenericBindingUnitOf(L, sym) {
|
|
7212
|
+
if (!sym)
|
|
7213
|
+
return null;
|
|
7214
|
+
// The VALUE probe first — it is purely syntactic, so no checker type
|
|
7215
|
+
// query runs for the overwhelmingly common non-nullish declarations
|
|
7216
|
+
// (a query can even panic upstream — the 1e999 checker bug).
|
|
7217
|
+
const unit = nullishValueUnitOf(L, sym);
|
|
7218
|
+
if (unit === null)
|
|
7219
|
+
return null;
|
|
7220
|
+
const mapped = L.mapTypeOf(L.checker.getTypeOfSymbol(sym));
|
|
7221
|
+
if (mapped !== null) {
|
|
7222
|
+
// Only the EMPTY interned shape qualifies among record mappings —
|
|
7223
|
+
// the all-generic-signature interface (`I<A & B>`) whose struct has
|
|
7224
|
+
// no slot at all. A record with DATA fields (`const value: { inner:
|
|
7225
|
+
// number | string } = null as any`) keeps its real storage and
|
|
7226
|
+
// every ordinary lowering: its reads flow through positions (comma
|
|
7227
|
+
// chains, call arguments) the no-storage read paths never claim,
|
|
7228
|
+
// so claiming the binding would fence working programs.
|
|
7229
|
+
if (mapped.kind !== "record")
|
|
7230
|
+
return null;
|
|
7231
|
+
const shape = L.shapes.get(mapped.shapeId);
|
|
7232
|
+
if (!shape || shape.fields.length > 0 || shape.tuple !== undefined || shape.indexValue !== undefined) {
|
|
7233
|
+
return null;
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
7236
|
+
return unit;
|
|
7237
|
+
}
|
|
7238
|
+
/** True when every write of `sym` in its declaring file is a plain `x =
|
|
7239
|
+
* <nullish>` assignment — the discipline that keeps a nullish binding's
|
|
7240
|
+
* value knowable. Compound assignments, ++/--, for-in/of cursors, and
|
|
7241
|
+
* destructuring targets all disqualify. */
|
|
7242
|
+
function allWritesNullish(L, sym, decl) {
|
|
7243
|
+
const symText = sym.name;
|
|
7244
|
+
const namesSym = (e) => ts.isIdentifier(e) && e.text === symText && L.resolveValueSymbol(e) === sym;
|
|
7245
|
+
let ok = true;
|
|
7246
|
+
const visit = (n) => {
|
|
7247
|
+
if (!ok)
|
|
7248
|
+
return;
|
|
7249
|
+
if (ts.isBinaryExpression(n)) {
|
|
7250
|
+
const k = n.operatorToken.kind;
|
|
7251
|
+
if (k >= ts.SyntaxKind.FirstAssignment && k <= ts.SyntaxKind.LastAssignment) {
|
|
7252
|
+
let lhs = n.left;
|
|
7253
|
+
while (ts.isParenthesizedExpression(lhs))
|
|
7254
|
+
lhs = lhs.expression;
|
|
7255
|
+
if (namesSym(lhs)) {
|
|
7256
|
+
if (k !== ts.SyntaxKind.EqualsToken || nullishExprUnitOf(L, n.right) === null)
|
|
7257
|
+
ok = false;
|
|
7258
|
+
}
|
|
7259
|
+
else if (ts.isArrayLiteralExpression(lhs) || ts.isObjectLiteralExpression(lhs)) {
|
|
7260
|
+
const walk = (m) => {
|
|
7261
|
+
if (namesSym(m))
|
|
7262
|
+
ok = false;
|
|
7263
|
+
else
|
|
7264
|
+
m.forEachChild(walk);
|
|
7265
|
+
};
|
|
7266
|
+
walk(lhs);
|
|
7267
|
+
}
|
|
7268
|
+
}
|
|
7269
|
+
}
|
|
7270
|
+
else if ((ts.isPrefixUnaryExpression(n) || ts.isPostfixUnaryExpression(n)) &&
|
|
7271
|
+
(n.operator === ts.SyntaxKind.PlusPlusToken || n.operator === ts.SyntaxKind.MinusMinusToken)) {
|
|
7272
|
+
let op = n.operand;
|
|
7273
|
+
while (ts.isParenthesizedExpression(op))
|
|
7274
|
+
op = op.expression;
|
|
7275
|
+
if (namesSym(op))
|
|
7276
|
+
ok = false;
|
|
7277
|
+
}
|
|
7278
|
+
else if ((ts.isForOfStatement(n) || ts.isForInStatement(n)) && !ts.isVariableDeclarationList(n.initializer)) {
|
|
7279
|
+
let t = n.initializer;
|
|
7280
|
+
while (ts.isParenthesizedExpression(t))
|
|
7281
|
+
t = t.expression;
|
|
7282
|
+
if (namesSym(t))
|
|
7283
|
+
ok = false;
|
|
7284
|
+
}
|
|
7285
|
+
n.forEachChild(visit);
|
|
7286
|
+
};
|
|
7287
|
+
decl.getSourceFile().forEachChild(visit);
|
|
7288
|
+
return ok;
|
|
7289
|
+
}
|
|
7290
|
+
/** A VALUE-ONLY expression: materializing it has no observable effect
|
|
7291
|
+
* beyond the value itself — function/arrow literals, class-free
|
|
7292
|
+
* literals, nullish units. The dead-binding rule's purity test: Node
|
|
7293
|
+
* builds the value and drops it, so skipping the build entirely is
|
|
7294
|
+
* unobservable. Bare identifier reads stay OUT (a read above a `let`
|
|
7295
|
+
* declaration is a TDZ throw Node WOULD serve). */
|
|
7296
|
+
function sideEffectFreeValueExpr(L, e) {
|
|
7297
|
+
const v = stripValueWrappers(e);
|
|
7298
|
+
if (ts.isArrowFunction(v) || ts.isFunctionExpression(v))
|
|
7299
|
+
return true;
|
|
7300
|
+
if (ts.isLiteralExpression(v) || v.kind === ts.SyntaxKind.NullKeyword ||
|
|
7301
|
+
v.kind === ts.SyntaxKind.TrueKeyword || v.kind === ts.SyntaxKind.FalseKeyword) {
|
|
7302
|
+
return true;
|
|
7303
|
+
}
|
|
7304
|
+
if (ts.isIdentifier(v) && v.text === "undefined" && (L.typeOf(v).flags & ts.TypeFlags.Undefined) !== 0) {
|
|
7305
|
+
return true;
|
|
7306
|
+
}
|
|
7307
|
+
return false;
|
|
7308
|
+
}
|
|
7309
|
+
/** True when `sym` — a binding whose type has NO static mapping — is DEAD:
|
|
7310
|
+
* never read anywhere in the program, not exported through a specifier,
|
|
7311
|
+
* declared with no initializer or a side-effect-free one, and written
|
|
7312
|
+
* (if at all) only by plain assignments of side-effect-free values. Node
|
|
7313
|
+
* materializes those values and drops them — zero observable effect —
|
|
7314
|
+
* so the declaration and its writes lower to NOTHING instead of fencing
|
|
7315
|
+
* on a type the program never consumes (`var xs2: typeof Array;`, the
|
|
7316
|
+
* write-only `var f2: { <T, U>(x: T, y: U): T }`). TS program files
|
|
7317
|
+
* only: JS bindings keep their checked-dynamic fallbacks. Positive
|
|
7318
|
+
* answers register in L.deadBindings (the assignment lowering skips
|
|
7319
|
+
* writes by the same set). */
|
|
7320
|
+
export function deadUnmappableBinding(L, sym, decl) {
|
|
7321
|
+
if (!sym)
|
|
7322
|
+
return false;
|
|
7323
|
+
if (L.deadBindings.has(sym))
|
|
7324
|
+
return true;
|
|
7325
|
+
if (!ts.isIdentifier(decl.name))
|
|
7326
|
+
return false;
|
|
7327
|
+
// Statement-position declarators only: a for-loop head (`for (let x;
|
|
7328
|
+
// false;) {}`) declares a LOCAL with per-iteration semantics —
|
|
7329
|
+
// lowerVarDeclList's contract requires a lowered statement for it, so
|
|
7330
|
+
// the no-storage family never claims it (catch bindings sit outside a
|
|
7331
|
+
// variable statement too and stay out the same way).
|
|
7332
|
+
if (!ts.isVariableStatement(decl.parent.parent))
|
|
7333
|
+
return false;
|
|
7334
|
+
const sf = decl.getSourceFile();
|
|
7335
|
+
if (sf.isDeclarationFile || isJsSourceFile(sf))
|
|
7336
|
+
return false;
|
|
7337
|
+
if (decl.initializer !== undefined && !sideEffectFreeValueExpr(L, decl.initializer))
|
|
7338
|
+
return false;
|
|
7339
|
+
// Exported bindings stay out: a library build's exports are consumed
|
|
7340
|
+
// from outside the graph, and export specifiers double as reads.
|
|
7341
|
+
if (ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Export)
|
|
7342
|
+
return false;
|
|
7343
|
+
// The type gate LAST among the cheap checks: querying the checker for
|
|
7344
|
+
// a type is the expensive step (and can panic upstream — the 1e999
|
|
7345
|
+
// bug), so only survivors of the syntactic filters pay it. Mappable
|
|
7346
|
+
// types keep their real storage.
|
|
7347
|
+
if (L.mapTypeOf(L.checker.getTypeOfSymbol(sym)) !== null)
|
|
7348
|
+
return false;
|
|
7349
|
+
const symText = sym.name;
|
|
7350
|
+
const namesSym = (e) => ts.isIdentifier(e) && e.text === symText && L.resolveValueSymbol(e) === sym;
|
|
7351
|
+
let dead = true;
|
|
7352
|
+
const visit = (n) => {
|
|
7353
|
+
if (!dead)
|
|
7354
|
+
return;
|
|
7355
|
+
if (ts.isIdentifier(n) && n.text === symText) {
|
|
7356
|
+
// Declaration-name occurrences are not reads.
|
|
7357
|
+
if (n.parent !== undefined && ts.isVariableDeclaration(n.parent) && n.parent.name === n) {
|
|
7358
|
+
n.forEachChild(visit);
|
|
7359
|
+
return;
|
|
7360
|
+
}
|
|
7361
|
+
// A plain-assignment LHS is a WRITE — dead only when the RHS
|
|
7362
|
+
// builds no observable effect (the value is dropped with the
|
|
7363
|
+
// binding).
|
|
7364
|
+
const p = n.parent;
|
|
7365
|
+
if (p !== undefined && ts.isBinaryExpression(p) &&
|
|
7366
|
+
p.operatorToken.kind === ts.SyntaxKind.EqualsToken && p.left === n) {
|
|
7367
|
+
if (!namesSym(n))
|
|
7368
|
+
return;
|
|
7369
|
+
if (!sideEffectFreeValueExpr(L, p.right))
|
|
7370
|
+
dead = false;
|
|
7371
|
+
return;
|
|
7372
|
+
}
|
|
7373
|
+
// Import/export specifiers, and every other occurrence, count as
|
|
7374
|
+
// reads.
|
|
7375
|
+
if (namesSym(n))
|
|
7376
|
+
dead = false;
|
|
7377
|
+
return;
|
|
7378
|
+
}
|
|
7379
|
+
n.forEachChild(visit);
|
|
7380
|
+
};
|
|
7381
|
+
for (const file of L.program.getSourceFiles()) {
|
|
7382
|
+
if (!dead)
|
|
7383
|
+
break;
|
|
7384
|
+
if (file.isDeclarationFile)
|
|
7385
|
+
continue;
|
|
7386
|
+
file.forEachChild(visit);
|
|
7387
|
+
}
|
|
7388
|
+
if (dead)
|
|
7389
|
+
L.deadBindings.add(sym);
|
|
7390
|
+
return dead;
|
|
7391
|
+
}
|
|
6867
7392
|
/** The generic function-like INITIALIZER behind a binding declaration —
|
|
6868
7393
|
* `const f = <T>(x: T) => x` or `const f = function g<T>(x: T) {...}`
|
|
6869
7394
|
* (parens stripped). Null when the declaration isn't that shape; the
|
|
@@ -6872,9 +7397,9 @@ export function bindingNeverReassigned(L, sym, decl) {
|
|
|
6872
7397
|
export function bindingGenericFnNodeOf(decl) {
|
|
6873
7398
|
if (!ts.isIdentifier(decl.name) || decl.initializer === undefined)
|
|
6874
7399
|
return null;
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
7400
|
+
// Assertion wrappers strip like parens: `const r = (<T>(x: T) => x) as
|
|
7401
|
+
// Mapper` evaluates the arrow — the cast only renames its type.
|
|
7402
|
+
const init = stripValueWrappers(decl.initializer);
|
|
6878
7403
|
if ((ts.isArrowFunction(init) || ts.isFunctionExpression(init)) &&
|
|
6879
7404
|
init.typeParameters !== undefined && init.body !== undefined) {
|
|
6880
7405
|
return init;
|
|
@@ -6890,11 +7415,16 @@ export function bindingGenericFnNodeOf(decl) {
|
|
|
6890
7415
|
* signature. Null when the shape doesn't match (a concrete annotation, a
|
|
6891
7416
|
* generic arrow — the syntactic probe's case, an overloaded alias). */
|
|
6892
7417
|
export function bindingContextualGenericFnNodeOf(L, decl) {
|
|
6893
|
-
if (!ts.isIdentifier(decl.name) || decl.initializer === undefined
|
|
7418
|
+
if (!ts.isIdentifier(decl.name) || decl.initializer === undefined)
|
|
6894
7419
|
return null;
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
7420
|
+
// The generic signature can arrive as an ANNOTATION or as a type
|
|
7421
|
+
// ASSERTION on the initializer (`var r = < <T>(x: T) => T >((x) => x)`
|
|
7422
|
+
// — the checker contextually types the operand's parameters by the
|
|
7423
|
+
// asserted signature exactly like an annotation would).
|
|
7424
|
+
const asserted = ts.isAsExpression(decl.initializer) || ts.isTypeAssertion(decl.initializer);
|
|
7425
|
+
if (decl.type === undefined && !asserted)
|
|
7426
|
+
return null;
|
|
7427
|
+
const init = stripValueWrappers(decl.initializer);
|
|
6898
7428
|
if (!(ts.isArrowFunction(init) || ts.isFunctionExpression(init)) ||
|
|
6899
7429
|
init.typeParameters !== undefined || init.body === undefined) {
|
|
6900
7430
|
return null;
|
|
@@ -7167,6 +7697,24 @@ export function lowerObjLitGenericMethodCall(L, call, access) {
|
|
|
7167
7697
|
}
|
|
7168
7698
|
const found = objLitGenericFnNodeOf(L, propSym);
|
|
7169
7699
|
if (!found) {
|
|
7700
|
+
// Function.prototype.apply/call/bind spelled through a FUNCTION
|
|
7701
|
+
// receiver: compiled functions are direct calls with no runtime
|
|
7702
|
+
// `this`/arguments object to re-route — name the working spelling
|
|
7703
|
+
// instead of a class-receiver hint (or an SC2020 recitation) no
|
|
7704
|
+
// function value can follow. Before the stdlib decline: these ARE
|
|
7705
|
+
// stdlib members (CallableFunction), but the pointed message is
|
|
7706
|
+
// the honest one.
|
|
7707
|
+
if ((name === "apply" || name === "call" || name === "bind") &&
|
|
7708
|
+
L.checker.getCallSignatures(recvT).length > 0) {
|
|
7709
|
+
L.unsupported("SC1090", call, `Function.prototype.${name} on a compiled function value (compiled calls are direct — no runtime 'this' or arguments object exists to re-route; spell the call directly: '${access.expression.getText()}(...)')`);
|
|
7710
|
+
}
|
|
7711
|
+
// STANDARD-LIBRARY generic members (Promise.then, Object.
|
|
7712
|
+
// defineProperty, Array-augmentation methods) are the lib fence's
|
|
7713
|
+
// story (SC2020, naming the member) — decline so the stdlib
|
|
7714
|
+
// chokepoint downstream reports, instead of an interface-dispatch
|
|
7715
|
+
// recitation about a receiver no user constructed.
|
|
7716
|
+
if (L.isStdlibMember(access))
|
|
7717
|
+
return null;
|
|
7170
7718
|
// Interface-declared generic methods dispatch statically, so the
|
|
7171
7719
|
// receiver's runtime class must be provable — name that discipline
|
|
7172
7720
|
// instead of the object-literal wording when the method lives on an
|