@scriptc/compiler 0.0.32 → 0.0.33

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.
Files changed (51) hide show
  1. package/dist/backend/cc-cache.test.js +308 -1
  2. package/dist/backend/cc-cache.test.js.map +1 -1
  3. package/dist/backend/cc.d.ts +6 -0
  4. package/dist/backend/cc.js +707 -68
  5. package/dist/backend/cc.js.map +1 -1
  6. package/dist/backend/emission/emit-exprs.js +53 -2
  7. package/dist/backend/emission/emit-exprs.js.map +1 -1
  8. package/dist/backend/emission/emitter.d.ts +12 -5
  9. package/dist/backend/emission/emitter.js +160 -22
  10. package/dist/backend/emission/emitter.js.map +1 -1
  11. package/dist/backend/emission/may-throw.js +7 -4
  12. package/dist/backend/emission/may-throw.js.map +1 -1
  13. package/dist/backend/ffi-callbacks.d.ts +38 -1
  14. package/dist/backend/ffi-callbacks.js +76 -3
  15. package/dist/backend/ffi-callbacks.js.map +1 -1
  16. package/dist/backend/llvm/emitter.js +243 -12
  17. package/dist/backend/llvm/emitter.js.map +1 -1
  18. package/dist/diagnostics/diagnostic.js +2 -0
  19. package/dist/diagnostics/diagnostic.js.map +1 -1
  20. package/dist/ffi/profile.d.ts +17 -5
  21. package/dist/ffi/profile.js +169 -22
  22. package/dist/ffi/profile.js.map +1 -1
  23. package/dist/frontend/lowering/lower-calls.d.ts +1 -0
  24. package/dist/frontend/lowering/lower-calls.js +89 -23
  25. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  26. package/dist/frontend/lowering/lower-containers.js +65 -19
  27. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  28. package/dist/frontend/lowering/lowerer.d.ts +12 -0
  29. package/dist/frontend/lowering/lowerer.js +20 -1
  30. package/dist/frontend/lowering/lowerer.js.map +1 -1
  31. package/dist/frontend/ts7/checker.d.ts +2 -0
  32. package/dist/frontend/ts7/checker.js +20 -3
  33. package/dist/frontend/ts7/checker.js.map +1 -1
  34. package/dist/frontend/types.d.ts +8 -0
  35. package/dist/frontend/types.js +33 -5
  36. package/dist/frontend/types.js.map +1 -1
  37. package/dist/index.js +35 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/ir/nodes.d.ts +22 -9
  40. package/dist/ir/nodes.js +7 -1
  41. package/dist/ir/nodes.js.map +1 -1
  42. package/dist/ir/serialize.d.ts +1 -1
  43. package/dist/ir/serialize.js +1 -1
  44. package/dist/ir/validate.js +74 -1
  45. package/dist/ir/validate.js.map +1 -1
  46. package/dist/library/int-infer.test.js +4 -4
  47. package/dist/library/profile.d.ts +3 -0
  48. package/dist/library/profile.js +9 -1
  49. package/dist/library/profile.js.map +1 -1
  50. package/package.json +2 -2
  51. package/surface-manifest.json +1 -1
@@ -4,7 +4,7 @@
4
4
  * generic instantiation (bounded by MAX_GENERIC_INSTANCES). */
5
5
  import * as ts from "../ts7/adapter.js";
6
6
  import { lowerGenMethodCall } from "./lower-generators.js";
7
- import { BOOL, CAUGHT, DYN, F64, JSVAL, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isFfiCallbackParam, isFfiContextParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/nodes.js";
7
+ import { BOOL, CAUGHT, DYN, F64, JSVAL, STRING, SYMBOL_T, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, canDynCheckTo, canMarshalTypedFuncIntoIsland, ffiClassType, ffiSourceParamTypes, funcOf, isFfiCallbackParam, isFfiContextParam, isFfiReleaseParam, isUnitType, shapeHasAccessorSlots, typeEquals } from "../../ir/nodes.js";
8
8
  import { isJsSourceFile, locOf } from "../program.js";
9
9
  import { isGenericCallableMemberType, typeKey } from "../types.js";
10
10
  import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx, nodeThrowExpr } from "./lowerer.js";
@@ -2276,7 +2276,11 @@ function ffiSourceParams(binding) {
2276
2276
  return binding.params.filter((param) => !isFfiContextParam(param));
2277
2277
  }
2278
2278
  function ffiParamDisplay(param) {
2279
- return isFfiCallbackParam(param) ? `callback '${param.callback.id}'` : `class '${param}'`;
2279
+ return isFfiCallbackParam(param)
2280
+ ? `callback '${param.callback.id}'`
2281
+ : isFfiReleaseParam(param)
2282
+ ? `release callback '${param.callback.release}'`
2283
+ : `class '${param}'`;
2280
2284
  }
2281
2285
  /** Callback arguments flow from native code into TypeScript. Their source
2282
2286
  * declarations therefore need to cover the whole scalar domain represented
@@ -2302,12 +2306,23 @@ function ffiCallbackInputDiagnostic(L, descriptor, callbackType) {
2302
2306
  !typeEquals(mapped, ffiClassType(nativeClass))) {
2303
2307
  continue;
2304
2308
  }
2305
- const domain = nativeClass === "bool" ? "boolean" : "number";
2309
+ if (nativeClass === "bytes")
2310
+ continue;
2311
+ const domain = nativeClass === "bool"
2312
+ ? "boolean"
2313
+ : nativeClass === "cstring" || nativeClass === "string"
2314
+ ? "string"
2315
+ : "number";
2306
2316
  const coversDomain = nativeClass === "bool"
2307
2317
  ? (paramType.flags & ts.TypeFlags.Boolean) !== 0
2308
- : (paramType.flags & ts.TypeFlags.Number) !== 0;
2318
+ : nativeClass === "cstring" || nativeClass === "string"
2319
+ ? (paramType.flags & ts.TypeFlags.String) !== 0
2320
+ : (paramType.flags & ts.TypeFlags.Number) !== 0;
2309
2321
  if (!coversDomain) {
2310
- return (`callback '${descriptor.callback.id}' parameter ${i + 1} is '${L.checker.typeToString(paramType)}', ` +
2322
+ const descriptorName = isFfiCallbackParam(descriptor)
2323
+ ? descriptor.callback.id
2324
+ : descriptor.callback.release;
2325
+ return (`callback '${descriptorName}' parameter ${i + 1} is '${L.checker.typeToString(paramType)}', ` +
2311
2326
  `but native class '${nativeClass}' may supply any ${domain}; declare it as '${domain}'`);
2312
2327
  }
2313
2328
  }
@@ -2379,7 +2394,7 @@ function ffiDeclarationDiagnostic(L, binding, symbol, loc) {
2379
2394
  if ((paramType.flags & ts.TypeFlags.Never) !== 0) {
2380
2395
  return signatureDiag(binding.name, `parameter ${i + 1} is 'never', an uninhabited TypeScript type that cannot describe a native ABI parameter`, loc);
2381
2396
  }
2382
- if (isFfiCallbackParam(sourceParam)) {
2397
+ if (isFfiCallbackParam(sourceParam) || isFfiReleaseParam(sourceParam)) {
2383
2398
  const callbackDiagnostic = ffiCallbackInputDiagnostic(L, sourceParam, paramType);
2384
2399
  if (callbackDiagnostic !== null) {
2385
2400
  return signatureDiag(binding.name, callbackDiagnostic, loc);
@@ -2602,7 +2617,45 @@ export function lowerFfiCall(L, expr) {
2602
2617
  signatureError(`this call passes ${expr.arguments.length} argument(s), but the native binding requires exactly ${expectedParams.length}`);
2603
2618
  }
2604
2619
  const expectedReturn = ffiClassType(binding.returns);
2605
- const args = expr.arguments.map((arg, i) => L.lowerExprExpecting(arg, expectedParams[i]));
2620
+ const sourceParams = ffiSourceParams(binding);
2621
+ const args = expr.arguments.map((arg, i) => {
2622
+ const sourceParam = sourceParams[i];
2623
+ const expected = expectedParams[i];
2624
+ const lowered = L.lowerExprExpecting(arg, expected);
2625
+ // Retained identity is the runtime closure pointer. A coercion adapter
2626
+ // would be freshly allocated at registration and release sites, so an
2627
+ // assignable-but-different function shape (notably `() => number` into
2628
+ // `() => void`) cannot honestly participate in explicit release. The
2629
+ // adapter set comes from the mint sites themselves (Lowerer's
2630
+ // freshClosureAdapters), not name-prefix matching, so a new coercion
2631
+ // helper cannot silently slip past this guard.
2632
+ if (isFfiReleaseParam(sourceParam) ||
2633
+ (isFfiCallbackParam(sourceParam) && sourceParam.callback.lifetime === "retained")) {
2634
+ if (lowered.kind === "dynCheck" ||
2635
+ (lowered.kind === "call" && L.freshClosureAdapters.has(lowered.callee))) {
2636
+ signatureError(`retained callback argument ${i + 1} must have the exact manifest function type; ` +
2637
+ `an implicit function adapter would change its release identity`);
2638
+ }
2639
+ // An inline function value at a RELEASE site can never match:
2640
+ // lifted lambdas always carry a captures list (even an empty one),
2641
+ // so both backends mint a fresh closure per evaluation of the
2642
+ // expression — the release argument is a pointer no registration
2643
+ // holds, a guaranteed runtime trap. Declared functions stay valid
2644
+ // here — their value is the interned immortal closure (captures
2645
+ // undefined), one pointer for every mention. Registration sites
2646
+ // still accept literals: an unnameable registration is simply
2647
+ // permanent, released by the exit teardown (the live-at-exit
2648
+ // fixture shape), and hides no matching failure.
2649
+ if (isFfiReleaseParam(sourceParam) &&
2650
+ lowered.kind === "closure" &&
2651
+ L.liftedFns.some((f) => f.name === lowered.fnName && f.captures !== undefined)) {
2652
+ signatureError(`retained callback argument ${i + 1} cannot be an inline function value; ` +
2653
+ `each evaluation creates a fresh closure no registration holds — ` +
2654
+ `pass the same named value used to register`);
2655
+ }
2656
+ }
2657
+ return lowered;
2658
+ });
2606
2659
  return {
2607
2660
  kind: "ffiCall",
2608
2661
  import: binding.name,
@@ -4155,7 +4208,8 @@ function lowerDynReceiverMethodCall(L, call, access) {
4155
4208
  // take the ordinary typed paths, but there is no static home for an
4156
4209
  // any-elemented array). Typed receivers keep their own lowerings.
4157
4210
  const recvTs = L.typeOf(access.expression);
4158
- const anyArray = L.checker.isArrayType(recvTs) &&
4211
+ const arrayReceiver = L.checker.isArrayType(recvTs);
4212
+ const anyArray = arrayReceiver &&
4159
4213
  ((L.checker.getTypeArguments(recvTs)[0]?.flags ?? 0) &
4160
4214
  (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0;
4161
4215
  let recv;
@@ -4234,21 +4288,9 @@ function lowerDynReceiverMethodCall(L, call, access) {
4234
4288
  // unimplemented methods throw a LOUD not-supported Error; names the
4235
4289
  // kind's prototype lacks throw Node's "x.y is not a function"; OBJ
4236
4290
  // receivers call the own member.
4237
- if (DYN_DISPATCH_METHODS.has(access.name.text) && !call.questionDotToken && !access.questionDotToken) {
4238
- if (call.arguments.some((a) => ts.isSpreadElement(a))) {
4239
- L.unsupported("SC1090", call, "spread arguments in calls through 'unknown' values");
4240
- }
4241
- const args = call.arguments.map((a) => L.lowerExprExpecting(a, DYN));
4242
- return {
4243
- kind: "dynInvoke",
4244
- recv,
4245
- method: access.name.text,
4246
- calleeName: access.getText(),
4247
- args,
4248
- type: DYN,
4249
- loc: locOf(call),
4250
- };
4251
- }
4291
+ const dispatched = lowerDynDispatchMethodCall(L, call, access, recv, arrayReceiver);
4292
+ if (dispatched)
4293
+ return dispatched;
4252
4294
  // Names NO dyn-representable prototype declares: the member can only
4253
4295
  // be an OWN property, so "read the member, call it" IS Node's
4254
4296
  // semantics for every possible dyn value — `handlers.onDone(x)` on a
@@ -4359,6 +4401,30 @@ export const DYN_DISPATCH_METHODS = new Set([
4359
4401
  "request", "sendTrailers", "priority", "settings", "goaway", "ping",
4360
4402
  "additionalHeaders", "altsvc", "origin",
4361
4403
  ]);
4404
+ export function lowerDynDispatchMethodCall(L, call, access, recv, arrayReceiver) {
4405
+ const method = access.name.text;
4406
+ if (!DYN_DISPATCH_METHODS.has(method) || call.questionDotToken || access.questionDotToken)
4407
+ return null;
4408
+ if (call.arguments.some((arg) => ts.isSpreadElement(arg))) {
4409
+ L.unsupported("SC1090", call, "spread arguments in calls through 'unknown' values");
4410
+ }
4411
+ const predicate = method === "filter" && call.arguments[0]
4412
+ ? L.lowerExpr(call.arguments[0])
4413
+ : null;
4414
+ if (arrayReceiver && predicate?.type.kind === "func" && predicate.type.ret.kind === "void") {
4415
+ L.unsupported("SC1090", call.arguments[0], "'.filter()' with a void-returning predicate (the callback return value is erased before its truthiness can be tested)");
4416
+ }
4417
+ const args = call.arguments.map((arg, i) => i === 0 && predicate ? L.coerceInto(arg, predicate, DYN) : L.lowerExprExpecting(arg, DYN));
4418
+ return {
4419
+ kind: "dynInvoke",
4420
+ recv,
4421
+ method,
4422
+ calleeName: access.getText(),
4423
+ args,
4424
+ type: DYN,
4425
+ loc: locOf(call),
4426
+ };
4427
+ }
4362
4428
  /** STR_METHODS ∪ the regex-form names, MINUS everything Array (or any
4363
4429
  * other dyn kind's prototype) also declares. */
4364
4430
  const DYN_STRING_ONLY_METHODS = new Set([