@scriptc/compiler 0.0.28 → 0.0.30
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 +5 -13
- package/dist/backend/cc.d.ts +23 -2
- package/dist/backend/cc.js +250 -9
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/emission/emit-async.d.ts +1 -0
- package/dist/backend/emission/emit-async.js +27 -1
- package/dist/backend/emission/emit-async.js.map +1 -1
- package/dist/backend/emission/emit-exprs.js +109 -18
- package/dist/backend/emission/emit-exprs.js.map +1 -1
- package/dist/backend/emission/emitter.d.ts +1 -0
- package/dist/backend/emission/emitter.js +25 -3
- package/dist/backend/emission/emitter.js.map +1 -1
- package/dist/backend/llvm/emitter.js +152 -0
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/mangle.d.ts +1 -0
- package/dist/backend/mangle.js +3 -0
- package/dist/backend/mangle.js.map +1 -1
- package/dist/coverage/surface-manifest.js +1 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/diagnostics/diagnostic.d.ts +23 -1
- package/dist/diagnostics/diagnostic.js +34 -0
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +3 -1
- package/dist/frontend/lowering/lower-calls.js +196 -33
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +6 -5
- package/dist/frontend/lowering/lower-containers.js +15 -11
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +11 -1
- package/dist/frontend/lowering/lower-exprs.js +366 -25
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-server.d.ts +5 -0
- package/dist/frontend/lowering/lower-server.js +105 -55
- package/dist/frontend/lowering/lower-server.js.map +1 -1
- package/dist/frontend/lowering/lower-stmts.js +71 -9
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +24 -5
- package/dist/frontend/lowering/lowerer.js +28 -4
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +84 -5
- package/dist/index.js.map +1 -1
- package/dist/ir/nodes.d.ts +52 -19
- package/dist/ir/nodes.js +2 -2
- package/dist/ir/nodes.js.map +1 -1
- package/dist/ir/validate.js +67 -9
- package/dist/ir/validate.js.map +1 -1
- package/dist/library/profile.d.ts +33 -0
- package/dist/library/profile.js +178 -4
- package/dist/library/profile.js.map +1 -1
- package/dist/library/sidecar.d.ts +3 -2
- package/dist/library/sidecar.js +7 -3
- package/dist/library/sidecar.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +2 -1
|
@@ -10,12 +10,12 @@ import { isGenericCallableMemberType, typeKey } from "../types.js";
|
|
|
10
10
|
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr } from "./lowerer.js";
|
|
11
11
|
import { enforceLibBoundary } from "./lib-boundary.js";
|
|
12
12
|
import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
|
13
|
-
import { ffiBindingDiag, ffiSignatureDiag, requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
|
13
|
+
import { ffiBindingDiag, ffiSignatureDiag, libCallbackDiag, 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
16
|
import { lowerChildStreamMethodCall, lowerCreateRequireCall, lowerDirentMethodCall, lowerFileHandleMethodCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
|
17
|
-
import { droppableStatic, lowerPromiseAllTupleCall, lowerPromiseRejectCall, probeLower, templateRawTextOf } from "./lower-exprs.js";
|
|
18
|
-
import { httpClientFnBindingOf, isStreamUndefCallExpr, lowerHttpClientFnCall } from "./lower-server.js";
|
|
17
|
+
import { droppableStatic, lowerAbsenceProbe, lowerPromiseAllTupleCall, lowerPromiseRejectCall, probeLower, templateRawTextOf } from "./lower-exprs.js";
|
|
18
|
+
import { httpClientFnBindingOf, isStreamUndefCallExpr, lowerCompatReqStreamOptionalCall, lowerHttpClientFnCall } from "./lower-server.js";
|
|
19
19
|
import { EMITTER_API_MEMBERS, exactInstanceClassOf, findGenericMethodOn, lowerClassGenericMethodCall, lowerStaticMethodCall } from "./lower-classes.js";
|
|
20
20
|
import { emitterRooted, lowerEmitterMethodCall } from "./lower-emitter.js";
|
|
21
21
|
import { lowerConsoleInspectArg, lowerFormatCall } from "./lower-inspect.js";
|
|
@@ -2313,29 +2313,59 @@ function ffiCallbackInputDiagnostic(L, descriptor, callbackType) {
|
|
|
2313
2313
|
}
|
|
2314
2314
|
return null;
|
|
2315
2315
|
}
|
|
2316
|
+
/** Values returned by native code flow into TypeScript, so a declaration
|
|
2317
|
+
* must admit the whole scalar domain the ABI class can produce. `mapType`
|
|
2318
|
+
* intentionally widens literal and enum types to their storage type; that
|
|
2319
|
+
* is sound for script-owned values but not for an external return contract
|
|
2320
|
+
* (`(): true` cannot describe a bool callback that is free to return false). */
|
|
2321
|
+
function ffiReturnDomainDiagnostic(L, nativeClass, returnType) {
|
|
2322
|
+
if (nativeClass === "void")
|
|
2323
|
+
return null;
|
|
2324
|
+
const domain = nativeClass === "bool" ? "boolean" : "number";
|
|
2325
|
+
const coversDomain = nativeClass === "bool"
|
|
2326
|
+
? (returnType.flags & ts.TypeFlags.Boolean) !== 0
|
|
2327
|
+
: (returnType.flags & ts.TypeFlags.Number) !== 0;
|
|
2328
|
+
return coversDomain
|
|
2329
|
+
? null
|
|
2330
|
+
: `the return type is '${L.checker.typeToString(returnType)}', but native class '${nativeClass}' may supply ` +
|
|
2331
|
+
`any ${domain}; declare it as '${domain}'`;
|
|
2332
|
+
}
|
|
2333
|
+
/** The binding surface's diagnostic flavor: native-manifest bindings
|
|
2334
|
+
* speak the SC5002/SC5003 FFI codes; library-mode host callbacks are the
|
|
2335
|
+
* same recognition machinery under the profile's vocabulary — SC4024 for
|
|
2336
|
+
* both the binding and signature halves, with "manifest" respelled
|
|
2337
|
+
* "profile" in the shared detail strings so the teaching names the
|
|
2338
|
+
* document the author actually edits. */
|
|
2339
|
+
function ffiFlavor(L) {
|
|
2340
|
+
if (!L.libraryCallbacks)
|
|
2341
|
+
return { binding: ffiBindingDiag, signature: ffiSignatureDiag };
|
|
2342
|
+
const lib = (name, detail, loc) => libCallbackDiag(name, detail.replaceAll("manifest", "profile"), loc);
|
|
2343
|
+
return { binding: lib, signature: lib };
|
|
2344
|
+
}
|
|
2316
2345
|
/** The declaration half of an outbound FFI binding. Kept independent of
|
|
2317
2346
|
* call-site argument checks so the whole manifest can be validated even
|
|
2318
2347
|
* when a configured function is never called. */
|
|
2319
2348
|
function ffiDeclarationDiagnostic(L, binding, symbol, loc) {
|
|
2349
|
+
const { binding: bindingDiag, signature: signatureDiag } = ffiFlavor(L);
|
|
2320
2350
|
const declarations = L.checker.declarationsOf(symbol);
|
|
2321
2351
|
const functionDecls = declarations.filter(ts.isFunctionDeclaration);
|
|
2322
2352
|
if (functionDecls.length === 0 ||
|
|
2323
2353
|
declarations.some((decl) => !ts.isFunctionDeclaration(decl)) ||
|
|
2324
2354
|
functionDecls.some((decl) => decl.body !== undefined)) {
|
|
2325
|
-
return
|
|
2355
|
+
return bindingDiag(binding.name, "the configured name does not resolve exclusively to signature-only function declarations", loc);
|
|
2326
2356
|
}
|
|
2327
2357
|
if (functionDecls.some((decl) => (decl.typeParameters?.length ?? 0) > 0)) {
|
|
2328
|
-
return
|
|
2358
|
+
return signatureDiag(binding.name, "generic ambient declarations cannot describe one fixed C ABI", loc);
|
|
2329
2359
|
}
|
|
2330
2360
|
const signatures = L.checker.getCallSignatures(L.checker.getTypeOfSymbol(symbol));
|
|
2331
2361
|
if (signatures.length !== 1) {
|
|
2332
|
-
return
|
|
2362
|
+
return signatureDiag(binding.name, `the ambient binding has ${signatures.length} call signatures; exactly one non-overloaded signature is required`, loc);
|
|
2333
2363
|
}
|
|
2334
2364
|
const signature = signatures[0];
|
|
2335
2365
|
const params = signature.getParameters();
|
|
2336
2366
|
const sourceParams = ffiSourceParams(binding);
|
|
2337
2367
|
if (params.length !== sourceParams.length) {
|
|
2338
|
-
return
|
|
2368
|
+
return signatureDiag(binding.name, `the TypeScript declaration has ${params.length} parameter(s), but the manifest declares ${sourceParams.length} source parameter(s) ` +
|
|
2339
2369
|
`(${binding.params.length - sourceParams.length} additional native context slot(s) are compiler-supplied)`, loc);
|
|
2340
2370
|
}
|
|
2341
2371
|
const expectedParams = ffiSourceParamTypes(binding.params);
|
|
@@ -2347,18 +2377,18 @@ function ffiDeclarationDiagnostic(L, binding, symbol, loc) {
|
|
|
2347
2377
|
// declaration is different: it is a callable external contract, so a
|
|
2348
2378
|
// `never` slot cannot truthfully describe any native parameter.
|
|
2349
2379
|
if ((paramType.flags & ts.TypeFlags.Never) !== 0) {
|
|
2350
|
-
return
|
|
2380
|
+
return signatureDiag(binding.name, `parameter ${i + 1} is 'never', an uninhabited TypeScript type that cannot describe a native ABI parameter`, loc);
|
|
2351
2381
|
}
|
|
2352
2382
|
if (isFfiCallbackParam(sourceParam)) {
|
|
2353
2383
|
const callbackDiagnostic = ffiCallbackInputDiagnostic(L, sourceParam, paramType);
|
|
2354
2384
|
if (callbackDiagnostic !== null) {
|
|
2355
|
-
return
|
|
2385
|
+
return signatureDiag(binding.name, callbackDiagnostic, loc);
|
|
2356
2386
|
}
|
|
2357
2387
|
}
|
|
2358
2388
|
const mapped = L.mapTypeOf(paramType);
|
|
2359
2389
|
const expected = expectedParams[i];
|
|
2360
2390
|
if (mapped === null || !typeEquals(mapped, expected)) {
|
|
2361
|
-
return
|
|
2391
|
+
return signatureDiag(binding.name, `parameter ${i + 1} maps to '${mapped === null ? L.checker.typeToString(paramType) : L.fmt(mapped)}', ` +
|
|
2362
2392
|
`which does not fit manifest ${ffiParamDisplay(sourceParam)}`, loc);
|
|
2363
2393
|
}
|
|
2364
2394
|
}
|
|
@@ -2367,21 +2397,36 @@ function ffiDeclarationDiagnostic(L, binding, symbol, loc) {
|
|
|
2367
2397
|
// let tsc erase all control flow after the call while the linked function
|
|
2368
2398
|
// continues, making the generated program disagree with TypeScript.
|
|
2369
2399
|
if ((returnType.flags & ts.TypeFlags.Never) !== 0) {
|
|
2370
|
-
return
|
|
2400
|
+
return signatureDiag(binding.name, "the return type is 'never', but a native ABI return cannot uphold TypeScript's non-returning contract", loc);
|
|
2371
2401
|
}
|
|
2372
2402
|
const declaredReturn = L.mapTypeOf(returnType);
|
|
2373
2403
|
const expectedReturn = ffiClassType(binding.returns);
|
|
2374
2404
|
if (declaredReturn === null || !typeEquals(declaredReturn, expectedReturn)) {
|
|
2375
|
-
return
|
|
2405
|
+
return signatureDiag(binding.name, `the return maps to '${declaredReturn === null ? L.checker.typeToString(returnType) : L.fmt(declaredReturn)}', ` +
|
|
2376
2406
|
`which does not fit manifest class '${binding.returns}'`, loc);
|
|
2377
2407
|
}
|
|
2408
|
+
const returnDomainDiagnostic = ffiReturnDomainDiagnostic(L, binding.returns, returnType);
|
|
2409
|
+
if (returnDomainDiagnostic !== null) {
|
|
2410
|
+
return signatureDiag(binding.name, returnDomainDiagnostic, loc);
|
|
2411
|
+
}
|
|
2378
2412
|
return null;
|
|
2379
2413
|
}
|
|
2414
|
+
/** True when a library callback may claim declarations from this file.
|
|
2415
|
+
* Ordinary program source is always eligible. Declaration files are
|
|
2416
|
+
* eligible only when they are project-owned: the standard library and
|
|
2417
|
+
* installed/workspace package declarations are existing ambient surfaces,
|
|
2418
|
+
* never callback declarations merely because a profile channel shares their
|
|
2419
|
+
* spelling. */
|
|
2420
|
+
function libraryCallbackOwnsFile(L, file) {
|
|
2421
|
+
return !file.isDeclarationFile || (!L.isStdlibFile(file) && !L.isNpmFile(file));
|
|
2422
|
+
}
|
|
2380
2423
|
/** Resolve and validate every configured outbound binding before emit.
|
|
2381
2424
|
* Candidate declarations are signature-only functions bearing the manifest
|
|
2382
2425
|
* name anywhere in the program. Multiple scoped declarations are all native
|
|
2383
2426
|
* bindings under the existing name-based call surface, so every candidate
|
|
2384
|
-
* must fit the one manifest ABI.
|
|
2427
|
+
* must fit the one manifest ABI. Library callbacks additionally exclude
|
|
2428
|
+
* standard-library and package declaration files while retaining project
|
|
2429
|
+
* declaration files as authored callback surface. */
|
|
2385
2430
|
export function validateFfiImports(L) {
|
|
2386
2431
|
const diagnostics = [];
|
|
2387
2432
|
const symbolsByName = new Map();
|
|
@@ -2390,6 +2435,8 @@ export function validateFfiImports(L) {
|
|
|
2390
2435
|
if (configuredNames.size === 0)
|
|
2391
2436
|
return { diagnostics, symbolsByName };
|
|
2392
2437
|
for (const file of L.program.getSourceFiles()) {
|
|
2438
|
+
if (L.libraryCallbacks && !libraryCallbackOwnsFile(L, file))
|
|
2439
|
+
continue;
|
|
2393
2440
|
ts.walkPreorder(file, (node) => {
|
|
2394
2441
|
if (ts.isFunctionDeclaration(node)) {
|
|
2395
2442
|
if (node.body === undefined &&
|
|
@@ -2415,7 +2462,23 @@ export function validateFfiImports(L) {
|
|
|
2415
2462
|
for (const binding of L.ffiImports) {
|
|
2416
2463
|
const bySymbol = candidates.get(binding.name);
|
|
2417
2464
|
if (bySymbol === undefined || bySymbol.size === 0) {
|
|
2418
|
-
|
|
2465
|
+
// A native-manifest binding with no declaration is a broken build
|
|
2466
|
+
// input. A library callback channel with no declaration is unused
|
|
2467
|
+
// CAPACITY: the registration symbol still dispatches it, nothing
|
|
2468
|
+
// calls it, and a later program revision may (a program that calls
|
|
2469
|
+
// the name without declaring it fails ordinary typechecking first).
|
|
2470
|
+
if (!L.libraryCallbacks) {
|
|
2471
|
+
diagnostics.push(ffiBindingDiag(binding.name, "the program has no signature-only function declaration with this name", { file: L.entry.fileName, start: 0, end: 0 }));
|
|
2472
|
+
}
|
|
2473
|
+
else {
|
|
2474
|
+
// Preserve the distinction between unused capacity and a binding
|
|
2475
|
+
// whose program declaration was found but failed validation. The
|
|
2476
|
+
// latter deliberately leaves no map entry so calls poison without
|
|
2477
|
+
// duplicating the program-level diagnostic; the empty set lets call
|
|
2478
|
+
// lowering inspect same-named builtins, implementations, and
|
|
2479
|
+
// unsupported ambient declaration shapes individually.
|
|
2480
|
+
symbolsByName.set(binding.name, new Set());
|
|
2481
|
+
}
|
|
2419
2482
|
continue;
|
|
2420
2483
|
}
|
|
2421
2484
|
const validSymbols = new Set();
|
|
@@ -2444,15 +2507,37 @@ export function lowerFfiCall(L, expr) {
|
|
|
2444
2507
|
if (!ts.isIdentifier(expr.expression))
|
|
2445
2508
|
return null;
|
|
2446
2509
|
const binding = L.ffiImportsByName.get(expr.expression.text);
|
|
2447
|
-
if (binding === undefined)
|
|
2510
|
+
if (binding === undefined) {
|
|
2511
|
+
// LIBRARY mode with a declared callback surface: a CALL of a
|
|
2512
|
+
// program-authored signature-only ambient function that names no
|
|
2513
|
+
// channel is the author reaching for the host seam the profile does
|
|
2514
|
+
// not provide — refuse with the callback teaching instead of the
|
|
2515
|
+
// ambient ReferenceError lowering. Scoped to project-owned source and
|
|
2516
|
+
// declaration files so lib.d.ts/@types/package ambients (parseInt,
|
|
2517
|
+
// setTimeout, …) keep every existing lowering; callback-free profiles
|
|
2518
|
+
// are untouched.
|
|
2519
|
+
if (L.libraryCallbacks) {
|
|
2520
|
+
const symbol = L.resolveValueSymbol(expr.expression);
|
|
2521
|
+
const decls = symbol === null ? [] : L.checker.declarationsOf(symbol);
|
|
2522
|
+
const callbackShaped = decls.length > 0 &&
|
|
2523
|
+
decls.every((decl) => ts.isFunctionDeclaration(decl) &&
|
|
2524
|
+
decl.body === undefined &&
|
|
2525
|
+
libraryCallbackOwnsFile(L, decl.getSourceFile()));
|
|
2526
|
+
if (callbackShaped) {
|
|
2527
|
+
L.pushDiag(libCallbackDiag(expr.expression.text, "the profile declares no callback channel with this name", locOf(expr)));
|
|
2528
|
+
throw new PoisonError();
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2448
2531
|
return null;
|
|
2532
|
+
}
|
|
2449
2533
|
const loc = locOf(expr);
|
|
2534
|
+
const { binding: bindingFlavor, signature: signatureFlavor } = ffiFlavor(L);
|
|
2450
2535
|
const bindingError = (detail) => {
|
|
2451
|
-
L.pushDiag(
|
|
2536
|
+
L.pushDiag(bindingFlavor(binding.name, detail, loc));
|
|
2452
2537
|
throw new PoisonError();
|
|
2453
2538
|
};
|
|
2454
2539
|
const signatureError = (detail) => {
|
|
2455
|
-
L.pushDiag(
|
|
2540
|
+
L.pushDiag(signatureFlavor(binding.name, detail, loc));
|
|
2456
2541
|
throw new PoisonError();
|
|
2457
2542
|
};
|
|
2458
2543
|
const symbol = L.resolveValueSymbol(expr.expression) ??
|
|
@@ -2464,10 +2549,39 @@ export function lowerFfiCall(L, expr) {
|
|
|
2464
2549
|
if (validSymbols === undefined)
|
|
2465
2550
|
throw new PoisonError();
|
|
2466
2551
|
if (!validSymbols.has(symbol)) {
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2552
|
+
if (L.libraryCallbacks) {
|
|
2553
|
+
const declarations = L.checker.declarationsOf(symbol);
|
|
2554
|
+
const programDeclarations = declarations.filter((decl) => libraryCallbackOwnsFile(L, decl.getSourceFile()));
|
|
2555
|
+
const programAmbient = programDeclarations.length > 0 &&
|
|
2556
|
+
programDeclarations.every((decl) => decl.getSourceFile().isDeclarationFile ||
|
|
2557
|
+
(ts.getCombinedModifierFlags(decl) &
|
|
2558
|
+
ts.ModifierFlags.Ambient) !== 0);
|
|
2559
|
+
if (programAmbient) {
|
|
2560
|
+
// A called, program-authored ambient with a configured channel
|
|
2561
|
+
// name is not unused capacity. Validate the resolved symbol now
|
|
2562
|
+
// so unsupported declaration forms (`declare const cb: ...`)
|
|
2563
|
+
// refuse SC4024 instead of silently dropping the call. A valid
|
|
2564
|
+
// declaration missed by the up-front syntax walk may proceed as
|
|
2565
|
+
// the callback binding after this exact-symbol check.
|
|
2566
|
+
const diagnostic = ffiDeclarationDiagnostic(L, binding, symbol, loc);
|
|
2567
|
+
if (diagnostic !== null) {
|
|
2568
|
+
L.pushDiag(diagnostic);
|
|
2569
|
+
throw new PoisonError();
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
else {
|
|
2573
|
+
// Standard-library/package ambients and same-named program
|
|
2574
|
+
// implementations remain their ordinary TypeScript bindings;
|
|
2575
|
+
// the profile does not claim them.
|
|
2576
|
+
return null;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
else {
|
|
2580
|
+
// TypeScript resolved this call to a distinct local declaration.
|
|
2581
|
+
// The manifest owns only the exact validated ambient binding; a
|
|
2582
|
+
// same-named function with a body remains ordinary scriptc code.
|
|
2583
|
+
return null;
|
|
2584
|
+
}
|
|
2471
2585
|
}
|
|
2472
2586
|
}
|
|
2473
2587
|
else {
|
|
@@ -2558,6 +2672,9 @@ export function lowerCall(L, expr) {
|
|
|
2558
2672
|
const processOptional = L.lowerProcessOptionalMethodCall(expr);
|
|
2559
2673
|
if (processOptional)
|
|
2560
2674
|
return processOptional;
|
|
2675
|
+
const reqStreamOptional = lowerCompatReqStreamOptionalCall(L, expr);
|
|
2676
|
+
if (reqStreamOptional)
|
|
2677
|
+
return reqStreamOptional;
|
|
2561
2678
|
// `t.unref?.()` on a Timeout handle — same story: the method always
|
|
2562
2679
|
// exists, so the optional call is the call.
|
|
2563
2680
|
if (expr.questionDotToken && ts.isPropertyAccessExpression(expr.expression)) {
|
|
@@ -2565,20 +2682,13 @@ export function lowerCall(L, expr) {
|
|
|
2565
2682
|
if (timeoutOptional)
|
|
2566
2683
|
return timeoutOptional;
|
|
2567
2684
|
}
|
|
2568
|
-
// `req.
|
|
2569
|
-
// stream
|
|
2570
|
-
//
|
|
2571
|
-
// the optional chain short-circuits, the arguments never evaluate, and
|
|
2572
|
-
// the whole statement is a no-op — exactly what lowers here (a VOID
|
|
2573
|
-
// no-op the emitter drops). The receiver is restricted to an
|
|
2574
|
-
// identifier so no evaluation is skipped, and to statement position so
|
|
2575
|
-
// no value is consumed (an unguarded or computed use meets the pointed
|
|
2576
|
-
// per-member fence in lower-server.ts instead).
|
|
2685
|
+
// `req.session?.m(...)` remains absent on compatibility requests. Live
|
|
2686
|
+
// `req.stream` calls now pass through the normal optional-chain and h2
|
|
2687
|
+
// stream lowering using lowerServerProperty's checked getter.
|
|
2577
2688
|
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
2578
2689
|
ts.isPropertyAccessExpression(expr.expression.expression) &&
|
|
2579
2690
|
!expr.expression.expression.questionDotToken &&
|
|
2580
|
-
|
|
2581
|
-
expr.expression.expression.name.text === "session") &&
|
|
2691
|
+
expr.expression.expression.name.text === "session" &&
|
|
2582
2692
|
ts.isIdentifier(expr.expression.expression.expression) &&
|
|
2583
2693
|
L.mapTypeOf(L.typeOf(expr.expression.expression.expression))?.kind === "httpReq" &&
|
|
2584
2694
|
L.isStdlibMember(expr.expression.expression)) {
|
|
@@ -3203,10 +3313,63 @@ export function lowerCall(L, expr) {
|
|
|
3203
3313
|
const x = L.lowerExprExpecting(expr.arguments[0], F64);
|
|
3204
3314
|
return { kind: "libCall", fn: "num.isNaN", args: [x], type: BOOL, loc };
|
|
3205
3315
|
}
|
|
3206
|
-
const s = L.lowerExprExpecting(expr.arguments[0], STRING);
|
|
3207
3316
|
const radix = expr.arguments[1]
|
|
3208
3317
|
? L.lowerExprExpecting(expr.arguments[1], F64)
|
|
3209
3318
|
: { kind: "numLit", value: 0, type: F64, loc };
|
|
3319
|
+
const optional = lowerAbsenceProbe(L, expr.arguments[0]);
|
|
3320
|
+
if (optional?.type.kind === "union") {
|
|
3321
|
+
const def = L.unions.get(optional.type.unionId);
|
|
3322
|
+
const stringTag = def?.arms.findIndex((arm) => arm.kind === "string") ?? -1;
|
|
3323
|
+
if (def &&
|
|
3324
|
+
stringTag >= 0 &&
|
|
3325
|
+
def.arms.every((arm) => arm.kind === "string" || isUnitType(arm))) {
|
|
3326
|
+
const unionT = optional.type;
|
|
3327
|
+
const key = `parseInt.optional:${unionT.unionId}`;
|
|
3328
|
+
let helper = L.widthHelpers.get(key);
|
|
3329
|
+
if (!helper) {
|
|
3330
|
+
helper = `%parseInt.optional.${L.widthHelpers.size}`;
|
|
3331
|
+
L.widthHelpers.set(key, helper);
|
|
3332
|
+
const value = { kind: "varRef", localId: "value.0", type: unionT, loc };
|
|
3333
|
+
const radixRef = { kind: "varRef", localId: "radix.0", type: F64, loc };
|
|
3334
|
+
const body = def.arms.flatMap((arm, tag) => isUnitType(arm)
|
|
3335
|
+
? [{
|
|
3336
|
+
kind: "if",
|
|
3337
|
+
cond: { kind: "unionIsTag", unionId: unionT.unionId, tag, negated: false, value, type: BOOL, loc },
|
|
3338
|
+
then: [{ kind: "return", value: { kind: "numLit", value: NaN, type: F64, loc }, loc }],
|
|
3339
|
+
else_: null,
|
|
3340
|
+
loc,
|
|
3341
|
+
}]
|
|
3342
|
+
: []);
|
|
3343
|
+
body.push({
|
|
3344
|
+
kind: "return",
|
|
3345
|
+
value: {
|
|
3346
|
+
kind: "libCall",
|
|
3347
|
+
fn: "num.parseInt",
|
|
3348
|
+
args: [{ kind: "unionNarrow", unionId: unionT.unionId, tag: stringTag, value, type: STRING, loc }, radixRef],
|
|
3349
|
+
type: F64,
|
|
3350
|
+
loc,
|
|
3351
|
+
},
|
|
3352
|
+
loc,
|
|
3353
|
+
});
|
|
3354
|
+
L.liftedFns.push({
|
|
3355
|
+
name: helper,
|
|
3356
|
+
params: [
|
|
3357
|
+
{ localId: "value.0", name: "value", type: unionT },
|
|
3358
|
+
{ localId: "radix.0", name: "radix", type: F64 },
|
|
3359
|
+
],
|
|
3360
|
+
returnType: F64,
|
|
3361
|
+
locals: [
|
|
3362
|
+
{ id: "value.0", name: "value", type: unionT, mutable: true },
|
|
3363
|
+
{ id: "radix.0", name: "radix", type: F64, mutable: true },
|
|
3364
|
+
],
|
|
3365
|
+
body,
|
|
3366
|
+
loc,
|
|
3367
|
+
});
|
|
3368
|
+
}
|
|
3369
|
+
return { kind: "call", callee: helper, args: [optional, radix], type: F64, loc };
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
const s = L.lowerExprExpecting(expr.arguments[0], STRING);
|
|
3210
3373
|
return { kind: "libCall", fn: "num.parseInt", args: [s, radix], type: F64, loc };
|
|
3211
3374
|
}
|
|
3212
3375
|
// STATIC parseFloat/isFinite over exactly-typed arguments —
|