@scriptc/compiler 0.0.32 → 0.0.34

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 (120) hide show
  1. package/dist/backend/cc-cache.test.js +430 -1
  2. package/dist/backend/cc-cache.test.js.map +1 -1
  3. package/dist/backend/cc.d.ts +25 -0
  4. package/dist/backend/cc.js +818 -88
  5. package/dist/backend/cc.js.map +1 -1
  6. package/dist/backend/emission/emit-exprs.js +79 -3
  7. package/dist/backend/emission/emit-exprs.js.map +1 -1
  8. package/dist/backend/emission/emit-shapes.js +37 -4
  9. package/dist/backend/emission/emit-shapes.js.map +1 -1
  10. package/dist/backend/emission/emitter.d.ts +23 -7
  11. package/dist/backend/emission/emitter.js +171 -27
  12. package/dist/backend/emission/emitter.js.map +1 -1
  13. package/dist/backend/emission/may-throw.js +7 -4
  14. package/dist/backend/emission/may-throw.js.map +1 -1
  15. package/dist/backend/ffi-callbacks.d.ts +38 -1
  16. package/dist/backend/ffi-callbacks.js +76 -3
  17. package/dist/backend/ffi-callbacks.js.map +1 -1
  18. package/dist/backend/library-identity.d.ts +22 -0
  19. package/dist/backend/library-identity.js +103 -0
  20. package/dist/backend/library-identity.js.map +1 -0
  21. package/dist/backend/llvm/emitter.d.ts +3 -0
  22. package/dist/backend/llvm/emitter.js +278 -17
  23. package/dist/backend/llvm/emitter.js.map +1 -1
  24. package/dist/backend/llvm/shapes.d.ts +1 -0
  25. package/dist/backend/llvm/shapes.js +25 -1
  26. package/dist/backend/llvm/shapes.js.map +1 -1
  27. package/dist/backend/mangle.d.ts +1 -0
  28. package/dist/backend/mangle.js +3 -0
  29. package/dist/backend/mangle.js.map +1 -1
  30. package/dist/backend/object-localize.d.ts +5 -5
  31. package/dist/backend/object-localize.js +19 -16
  32. package/dist/backend/object-localize.js.map +1 -1
  33. package/dist/backend/object-localize.test.js +37 -13
  34. package/dist/backend/object-localize.test.js.map +1 -1
  35. package/dist/diagnostics/diagnostic.js +2 -0
  36. package/dist/diagnostics/diagnostic.js.map +1 -1
  37. package/dist/ffi/profile.d.ts +17 -5
  38. package/dist/ffi/profile.js +169 -22
  39. package/dist/ffi/profile.js.map +1 -1
  40. package/dist/frontend/input-tracker.d.ts +82 -0
  41. package/dist/frontend/input-tracker.js +380 -0
  42. package/dist/frontend/input-tracker.js.map +1 -0
  43. package/dist/frontend/input-tracker.test.d.ts +1 -0
  44. package/dist/frontend/input-tracker.test.js +137 -0
  45. package/dist/frontend/input-tracker.test.js.map +1 -0
  46. package/dist/frontend/lowering/lower-builtins.js +2 -8
  47. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  48. package/dist/frontend/lowering/lower-calls.d.ts +1 -0
  49. package/dist/frontend/lowering/lower-calls.js +244 -163
  50. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  51. package/dist/frontend/lowering/lower-classes.js +3 -0
  52. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  53. package/dist/frontend/lowering/lower-containers.js +113 -28
  54. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  55. package/dist/frontend/lowering/lower-exprs.js +67 -1
  56. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  57. package/dist/frontend/lowering/lower-inspect.js +45 -26
  58. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  59. package/dist/frontend/lowering/lower-modules.js +1 -1
  60. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  61. package/dist/frontend/lowering/lower-stmts.js +48 -3
  62. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  63. package/dist/frontend/lowering/lowerer.d.ts +79 -29
  64. package/dist/frontend/lowering/lowerer.js +429 -87
  65. package/dist/frontend/lowering/lowerer.js.map +1 -1
  66. package/dist/frontend/npm-static-rewrite.js +25 -26
  67. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  68. package/dist/frontend/npm-static.js +29 -30
  69. package/dist/frontend/npm-static.js.map +1 -1
  70. package/dist/frontend/npm-static.test.d.ts +1 -0
  71. package/dist/frontend/npm-static.test.js +15 -0
  72. package/dist/frontend/npm-static.test.js.map +1 -0
  73. package/dist/frontend/npm.js +5 -17
  74. package/dist/frontend/npm.js.map +1 -1
  75. package/dist/frontend/program.js +2 -6
  76. package/dist/frontend/program.js.map +1 -1
  77. package/dist/frontend/resolve.js +8 -29
  78. package/dist/frontend/resolve.js.map +1 -1
  79. package/dist/frontend/ts7/checker.d.ts +2 -0
  80. package/dist/frontend/ts7/checker.js +20 -3
  81. package/dist/frontend/ts7/checker.js.map +1 -1
  82. package/dist/frontend/ts7/program.js +17 -18
  83. package/dist/frontend/ts7/program.js.map +1 -1
  84. package/dist/frontend/types.d.ts +41 -0
  85. package/dist/frontend/types.js +147 -8
  86. package/dist/frontend/types.js.map +1 -1
  87. package/dist/index.d.ts +1 -1
  88. package/dist/index.js +275 -41
  89. package/dist/index.js.map +1 -1
  90. package/dist/ir/nodes.d.ts +42 -12
  91. package/dist/ir/nodes.js +7 -1
  92. package/dist/ir/nodes.js.map +1 -1
  93. package/dist/ir/serialize.d.ts +1 -1
  94. package/dist/ir/serialize.js +1 -1
  95. package/dist/ir/validate.js +104 -1
  96. package/dist/ir/validate.js.map +1 -1
  97. package/dist/library/early-cache.d.ts +65 -0
  98. package/dist/library/early-cache.js +380 -0
  99. package/dist/library/early-cache.js.map +1 -0
  100. package/dist/library/early-cache.test.d.ts +1 -0
  101. package/dist/library/early-cache.test.js +528 -0
  102. package/dist/library/early-cache.test.js.map +1 -0
  103. package/dist/library/int-infer.js +14 -0
  104. package/dist/library/int-infer.js.map +1 -1
  105. package/dist/library/int-infer.test.js +21 -4
  106. package/dist/library/int-infer.test.js.map +1 -1
  107. package/dist/library/profile.d.ts +3 -0
  108. package/dist/library/profile.js +9 -1
  109. package/dist/library/profile.js.map +1 -1
  110. package/dist/library/semantic-source.d.ts +13 -0
  111. package/dist/library/semantic-source.js +257 -0
  112. package/dist/library/semantic-source.js.map +1 -0
  113. package/dist/library/semantic-source.test.d.ts +1 -0
  114. package/dist/library/semantic-source.test.js +113 -0
  115. package/dist/library/semantic-source.test.js.map +1 -0
  116. package/dist/library/sidecar.d.ts +6 -0
  117. package/dist/library/sidecar.js +11 -0
  118. package/dist/library/sidecar.js.map +1 -1
  119. package/package.json +2 -2
  120. package/surface-manifest.json +1 -1
package/dist/index.js CHANGED
@@ -1,14 +1,15 @@
1
- import { mkdir, rm, writeFile } from "node:fs/promises";
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
- import { CcCompileError, compileC, compileLibArchive, mobileLibraryTarget, mobileTargetRefusal, resolveCc, targetPlatform } from "./backend/cc.js";
3
+ import { buildCacheRoot, CcCompileError, compileC, compileLibArchive, mobileLibraryTarget, mobileTargetRefusal, prepareBuildCacheRoot, pruneBuildCache, resolveCc, targetPlatform } from "./backend/cc.js";
4
4
  import { emitModule } from "./backend/emission/emitter.js";
5
5
  import { emitLlvmModule, LlvmUnsupportedError } from "./backend/llvm/emitter.js";
6
+ import { rebaseLibrarySourceComments, replaceLibraryIdentity, stripLibraryIdentity, stripLibrarySourceComments } from "./backend/library-identity.js";
6
7
  import { checkerPanicDiag, ffiNativeBuildDiag, libAsyncExportDiag, libAsyncSurfaceDiag, libExportUnresolvedDiag, libGenericExportDiag, libIntBoundaryDiag, libNpmIneligibleDiag, libSidecarDiag, libUnmappableSignatureDiag, iceDiag, isCheckerPanic, LIB_INBOUND_BYTES_TRAP_CODE, LIB_RUNTIME_TRAP_CODES } from "./diagnostics/diagnostic.js";
7
8
  import { checkLibraryIntegerSlots, classSeed, hasIntSlots, numberCarrierKind } from "./library/int-infer.js";
8
9
  import { loadLibraryProfile, profileRemediation, profileTeaching } from "./library/profile.js";
9
10
  import { decorateLibraryRefusals, evaluateLibraryFences } from "./library/fence-eval.js";
10
11
  import { assembleTrapTeaching } from "./library/trap-teaching.js";
11
- import { buildSidecar, canonicalModuleGraph, canonicalPath, compilerReleaseVersion, libraryIdentityHashes, } from "./library/sidecar.js";
12
+ import { buildSidecar, canonicalModuleGraph, canonicalPath, compilerReleaseVersion, libraryIdentityHashes, updateSidecarIdentity, } from "./library/sidecar.js";
12
13
  import { validateSidecar } from "./library/sidecar-validate.js";
13
14
  import { entryFunctionExports } from "./frontend/lib-exports.js";
14
15
  import { entryContractFacts } from "./frontend/lib-contract.js";
@@ -18,10 +19,14 @@ import { validateModule } from "./ir/validate.js";
18
19
  import { canonicalBuiltinModule, checkPreflight, isNodeTypesPath, loadProgram, locOf, requiresOf, resolveNpmImport } from "./frontend/program.js";
19
20
  import { npmStaticIneligibleReason, npmStaticOffenders, npmStaticPackageOfPath } from "./frontend/npm-static.js";
20
21
  import { provenanceSources } from "./frontend/provenance-registry.js";
21
- import { resolveBareModule } from "./frontend/resolve.js";
22
+ import { clearResolveCaches, resolveBareModule } from "./frontend/resolve.js";
22
23
  import { isJsSourceFileName, isRelativeSpecifier } from "./frontend/shared.js";
23
24
  import { lowerToIr } from "./frontend/lowering/lowerer.js";
24
25
  import { loadFfiProfile } from "./ffi/profile.js";
26
+ import { hasForeignFfiCallback } from "./backend/ffi-callbacks.js";
27
+ import { FrontendInputTracker, trackedReadFile } from "./frontend/input-tracker.js";
28
+ import { libraryFrontendImplementationFingerprint, publishEarlyLibraryCache, readEarlyLibraryCache, readSemanticLibraryCache } from "./library/early-cache.js";
29
+ import { createSourceLineRebaser } from "./library/semantic-source.js";
25
30
  export const VERSION = "0.0.1";
26
31
  export { compileC, runtimeSrcDir } from "./backend/cc.js";
27
32
  export { ANDROID_MIN_API, IPHONEOS_MIN_VERSION, isAndroidTarget, isIosTarget, isMobileTarget, mobileLibraryTarget, mobileTargetRefusal } from "./backend/cc.js";
@@ -316,6 +321,11 @@ function packageNameOfBareSpecifier(specifier) {
316
321
  * static-or-refuse teaching, and the detection closes over the opted-in
317
322
  * packages' own bare edges (no island exists to serve a dep from). */
318
323
  function runFrontend(entryPath, npmStatic, externalTypes) {
324
+ // Resolver package/workspace metadata is intentionally shared across the
325
+ // several load attempts of ONE auto-detection fixpoint, but never across
326
+ // separate compiles in a long-lived process. A cache miss must observe
327
+ // package.json edits before it can publish a new early-library entry.
328
+ clearResolveCaches();
319
329
  const statuses = [];
320
330
  const npmSites = new Map();
321
331
  const judged = new Set();
@@ -844,6 +854,7 @@ export async function compile(entryPath, opts) {
844
854
  dgram: moduleUsesDgram(lowered.module),
845
855
  // The link switch for scr_watch.c: fs.watch/watcher.* libCalls on the IR.
846
856
  watch: moduleUsesFsWatch(lowered.module),
857
+ foreignFfi: hasForeignFfiCallback(lowered.module.ffiImports ?? []),
847
858
  // The link switch for scr_test.c: test.* libCalls on the IR.
848
859
  nodeTest: moduleUsesNodeTest(lowered.module),
849
860
  // The link switch for scr_tls.c + the vendored mbedTLS archive:
@@ -1200,14 +1211,205 @@ function mergeSidecarIntSlots(cfg, facts, mod) {
1200
1211
  }
1201
1212
  return { ok: true, config: cfg };
1202
1213
  }
1214
+ function libraryNativeFeatures(mod, backend) {
1215
+ return {
1216
+ backend,
1217
+ regex: moduleUsesRegex(mod),
1218
+ assert: moduleUsesAssert(mod),
1219
+ inspect: moduleUsesInspect(mod),
1220
+ symbol: moduleUsesSymbol(mod),
1221
+ searchParams: moduleUsesSearchParams(mod),
1222
+ emitter: moduleUsesEmitter(mod),
1223
+ zlib: moduleUsesZlib(mod),
1224
+ copying: moduleUsesCopying(mod),
1225
+ textDecoderLegacy: moduleUsesLegacyTextDecoder(mod),
1226
+ ...(mod.lib?.identity !== undefined ? { buildId: mod.lib.identity.buildId } : {}),
1227
+ };
1228
+ }
1229
+ function libraryLocalizeSymbols(profile) {
1230
+ return profile.localizeRuntime
1231
+ ? [
1232
+ profile.initSymbol,
1233
+ profile.sinkRegisterSymbol,
1234
+ ...(profile.collectSymbol !== null ? [profile.collectSymbol] : []),
1235
+ ...(profile.resultResetSymbol !== null ? [profile.resultResetSymbol] : []),
1236
+ ...(profile.callbackRegisterSymbol !== null ? [profile.callbackRegisterSymbol] : []),
1237
+ ...(profile.sidecar !== null
1238
+ ? [profile.sidecar.buildIdSymbol, profile.sidecar.abiVersionSymbol]
1239
+ : []),
1240
+ ...profile.exports.map((entry) => entry.symbol),
1241
+ ]
1242
+ : undefined;
1243
+ }
1244
+ async function compileLibraryNative(profile, cPath, archivePath, sanitize, features) {
1245
+ const localizeSymbols = libraryLocalizeSymbols(profile);
1246
+ let identityCSource;
1247
+ let programSource;
1248
+ if (profile.sidecar !== null || profile.emission === "c") {
1249
+ const publicSource = await readFile(cPath, "utf8");
1250
+ programSource = publicSource;
1251
+ }
1252
+ if (profile.sidecar !== null) {
1253
+ if (features.buildId === undefined)
1254
+ throw new Error("library identity TU has no build id");
1255
+ const withoutIdentity = stripLibraryIdentity(programSource, profile.emission);
1256
+ if (withoutIdentity === programSource) {
1257
+ throw new Error("generated public library TU has no identity region");
1258
+ }
1259
+ programSource = withoutIdentity;
1260
+ identityCSource = [
1261
+ "#include <stdint.h>",
1262
+ "#include <inttypes.h>",
1263
+ `uint64_t ${profile.sidecar.buildIdSymbol}(void) { return UINT64_C(0x${features.buildId}); }`,
1264
+ `uint32_t ${profile.sidecar.abiVersionSymbol}(void) { return ${profile.sidecar.abiVersion}u; }`,
1265
+ "",
1266
+ ].join("\n");
1267
+ }
1268
+ if (profile.emission === "c") {
1269
+ programSource = stripLibrarySourceComments(programSource, profile.entry);
1270
+ }
1271
+ await compileLibArchive({
1272
+ cPath,
1273
+ ...(programSource !== undefined ? { programSource } : {}),
1274
+ ...(identityCSource !== undefined ? { identityCSource } : {}),
1275
+ outPath: archivePath,
1276
+ cacheIdentity: "scriptc-generated-library-v1",
1277
+ sanitize,
1278
+ optimization: profile.optimization,
1279
+ ...(localizeSymbols !== undefined ? { localizeSymbols } : {}),
1280
+ ...(profile.instancePerThread ? { threadInstances: true } : {}),
1281
+ regex: features.regex,
1282
+ assert: features.assert,
1283
+ inspect: features.inspect,
1284
+ symbol: features.symbol,
1285
+ searchParams: features.searchParams,
1286
+ emitter: features.emitter,
1287
+ zlib: features.zlib,
1288
+ copying: features.copying,
1289
+ textDecoderLegacy: features.textDecoderLegacy,
1290
+ });
1291
+ }
1292
+ async function emitSemanticLibraryHit(hit, profile, opts, archivePath, cacheRoot, cacheOptions, timing) {
1293
+ const mod = hit.mod;
1294
+ const rootDir = dirname(resolve(opts.profilePath));
1295
+ let sidecarJson = hit.sidecarJson;
1296
+ if (profile.sidecar !== null) {
1297
+ if (mod.lib?.identity === undefined || sidecarJson === null) {
1298
+ throw new Error("semantic library cache lost sidecar identity metadata");
1299
+ }
1300
+ const modules = canonicalModuleGraph(rootDir, hit.sourceTexts);
1301
+ const { buildId, sourceHash } = libraryIdentityHashes(compilerReleaseVersion(), profile.profileBytes, modules);
1302
+ mod.lib.identity.buildId = buildId;
1303
+ hit.native.buildId = buildId;
1304
+ sidecarJson = updateSidecarIdentity(sidecarJson, buildId, sourceHash);
1305
+ }
1306
+ const validation = validateModule(mod);
1307
+ if (validation.length > 0) {
1308
+ return {
1309
+ ok: false,
1310
+ diagnostics: validation.map((violation) => iceDiag(violation.message, violation.loc)),
1311
+ sourceTexts: hit.sourceTexts,
1312
+ };
1313
+ }
1314
+ await mkdir(opts.outDir, { recursive: true });
1315
+ const stem = basename(profile.entry).replace(/\.(ts|js|mjs|cjs)$/, "");
1316
+ const cPath = join(opts.outDir, `${stem}.lib.${profile.emission === "llvm" ? "ll" : "c"}`);
1317
+ let translationUnit = hit.translationUnit;
1318
+ if (profile.sidecar !== null) {
1319
+ translationUnit = replaceLibraryIdentity(translationUnit, profile.emission, mod.lib.identity);
1320
+ }
1321
+ if (profile.emission === "llvm") {
1322
+ await writeFile(cPath, translationUnit);
1323
+ }
1324
+ else {
1325
+ const previous = hit.previousSources.get(mod.sourceFile);
1326
+ const current = hit.sourceTexts.get(mod.sourceFile);
1327
+ if (previous === undefined || current === undefined) {
1328
+ throw new Error("semantic library cache lost the entry source text");
1329
+ }
1330
+ translationUnit = rebaseLibrarySourceComments(translationUnit, mod.sourceFile, createSourceLineRebaser(mod.sourceFile, previous, current));
1331
+ await writeFile(cPath, translationUnit);
1332
+ }
1333
+ timing("semantic-tu-restore", { output_bytes: Buffer.byteLength(translationUnit) });
1334
+ await rm(join(opts.outDir, `${stem}.lib.${profile.emission === "llvm" ? "c" : "ll"}`), { force: true });
1335
+ let irPath;
1336
+ if (opts.emitIr) {
1337
+ irPath = join(opts.outDir, `${stem}.lib.ir.json`);
1338
+ await writeFile(irPath, serializeModule(mod));
1339
+ }
1340
+ await compileLibraryNative(profile, cPath, archivePath, opts.sanitize ?? false, hit.native);
1341
+ timing("native-archive");
1342
+ let sidecarPath;
1343
+ if (sidecarJson !== null) {
1344
+ sidecarPath = profile.sidecar.path !== null
1345
+ ? resolve(dirname(archivePath), profile.sidecar.path)
1346
+ : `${archivePath}.contract.json`;
1347
+ await writeFile(sidecarPath, sidecarJson);
1348
+ }
1349
+ await publishEarlyLibraryCache(cacheRoot, cacheOptions, {
1350
+ cPath,
1351
+ native: hit.native,
1352
+ frontend: hit.frontend,
1353
+ semantic: { mod, sources: hit.sourceTexts },
1354
+ ...(irPath !== undefined ? { irPath } : {}),
1355
+ ...(sidecarPath !== undefined ? { sidecarPath } : {}),
1356
+ }).catch(() => undefined);
1357
+ await pruneBuildCache(cacheRoot);
1358
+ timing("semantic-cache-publish");
1359
+ timing("complete");
1360
+ return {
1361
+ ok: true,
1362
+ archivePath,
1363
+ cPath,
1364
+ backend: profile.emission,
1365
+ ...(irPath !== undefined ? { irPath } : {}),
1366
+ ...(sidecarPath !== undefined ? { sidecarPath } : {}),
1367
+ };
1368
+ }
1203
1369
  export async function compileLibrary(opts) {
1370
+ const frontendInputs = new FrontendInputTracker();
1371
+ return frontendInputs.run(() => compileLibraryTracked(opts, frontendInputs));
1372
+ }
1373
+ async function compileLibraryTracked(opts, frontendInputs) {
1374
+ const timingOn = process.env["SCRIPTC_TIMING"] === "1";
1375
+ const timingStart = performance.now();
1376
+ let timingLast = timingStart;
1377
+ const timing = (phase, detail = {}) => {
1378
+ if (!timingOn)
1379
+ return;
1380
+ const now = performance.now();
1381
+ process.stderr.write(`scriptc timing ${JSON.stringify({
1382
+ phase,
1383
+ phase_ms: Math.round((now - timingLast) * 10) / 10,
1384
+ total_ms: Math.round((now - timingStart) * 10) / 10,
1385
+ rss_mb: Math.round(process.memoryUsage().rss / 1024 / 1024),
1386
+ ...detail,
1387
+ })}\n`);
1388
+ timingLast = now;
1389
+ };
1204
1390
  const loadedProfile = loadLibraryProfile(resolve(opts.profilePath));
1391
+ timing("profile-load");
1205
1392
  if (!loadedProfile.ok) {
1206
1393
  return { ok: false, diagnostics: loadedProfile.diagnostics, sourceTexts: new Map() };
1207
1394
  }
1208
1395
  const profile = loadedProfile.profile;
1209
1396
  const entryPath = profile.entry;
1210
1397
  const buildPlatform = buildTargetPlatform();
1398
+ const profileDir = dirname(resolve(opts.profilePath));
1399
+ for (let directory = dirname(entryPath);; directory = dirname(directory)) {
1400
+ for (const name of ["tsconfig.json", "package.json"]) {
1401
+ // Project configuration and package-realm metadata can affect the
1402
+ // frontend even when TypeScript did not request their bytes through
1403
+ // its delegated filesystem callbacks (tsgo may read them server-side).
1404
+ frontendInputs.run(() => {
1405
+ const path = join(directory, name);
1406
+ trackedReadFile(path);
1407
+ });
1408
+ }
1409
+ if (directory === profileDir || dirname(directory) === directory)
1410
+ break;
1411
+ }
1412
+ const archivePath = opts.outPath ?? join(opts.outDir, `${basename(entryPath).replace(/\.(ts|js|mjs|cjs)$/, "")}.lib.a`);
1211
1413
  // Mobile-target admission first — a pure env/host check, so a refused
1212
1414
  // pairing never reaches toolchain discovery. iOS targets (device and
1213
1415
  // simulator) build on darwin hosts only: the Apple SDK sysroot and the
@@ -1264,6 +1466,44 @@ export async function compileLibrary(opts) {
1264
1466
  };
1265
1467
  }
1266
1468
  }
1469
+ const cacheRoot = provenanceSources() === null
1470
+ ? await prepareBuildCacheRoot(buildCacheRoot())
1471
+ : null;
1472
+ const earlyCacheOptions = {
1473
+ profilePath: opts.profilePath,
1474
+ profileBytes: profile.profileBytes,
1475
+ entryPath,
1476
+ outDir: opts.outDir,
1477
+ ...(opts.outPath !== undefined ? { outPath: opts.outPath } : {}),
1478
+ emitIr: opts.emitIr ?? false,
1479
+ sanitize: opts.sanitize ?? false,
1480
+ target: `${process.env["SCRIPTC_TARGET"] ?? "native"}:${buildPlatform}:${process.arch}`,
1481
+ compiler: [process.env["SCRIPTC_CC"] ?? "clang"],
1482
+ nodeVersion: process.version,
1483
+ implementation: await libraryFrontendImplementationFingerprint(),
1484
+ };
1485
+ const earlyHit = await readEarlyLibraryCache(cacheRoot, earlyCacheOptions, profile.sidecar === null ? undefined : profile.sidecar.path);
1486
+ if (earlyHit !== null) {
1487
+ timing("early-cache-hit");
1488
+ await compileLibraryNative(profile, earlyHit.cPath, archivePath, opts.sanitize ?? false, earlyHit.native);
1489
+ timing("native-archive");
1490
+ timing("complete");
1491
+ return {
1492
+ ok: true,
1493
+ archivePath,
1494
+ cPath: earlyHit.cPath,
1495
+ backend: earlyHit.native.backend,
1496
+ ...(earlyHit.irPath !== undefined ? { irPath: earlyHit.irPath } : {}),
1497
+ ...(earlyHit.sidecarPath !== undefined ? { sidecarPath: earlyHit.sidecarPath } : {}),
1498
+ };
1499
+ }
1500
+ timing("early-cache-miss");
1501
+ const semanticHit = await readSemanticLibraryCache(cacheRoot, earlyCacheOptions, profile.sidecar === null ? undefined : profile.sidecar.path);
1502
+ if (semanticHit !== null) {
1503
+ timing("semantic-cache-hit", { changed_sources: semanticHit.changedSources.length });
1504
+ return emitSemanticLibraryHit(semanticHit, profile, opts, archivePath, cacheRoot, earlyCacheOptions, timing);
1505
+ }
1506
+ timing("semantic-cache-miss");
1267
1507
  // Bare npm specifiers in a library graph take the STATIC-OR-REFUSE
1268
1508
  // posture: "lib" runs the same auto-detection and eligibility bar as
1269
1509
  // the executable lane's --npm-static (own .d.ts, unminified shipped JS,
@@ -1271,6 +1511,10 @@ export async function compileLibrary(opts) {
1271
1511
  // island/dynamic tier to offer (SC4006's ground), so eligibility needs
1272
1512
  // no flag and a miss is a refusal, never a fallback.
1273
1513
  const fe = runFrontend(entryPath, "lib");
1514
+ timing("frontend-load", {
1515
+ entry_bytes: fe.entryText().length,
1516
+ source_files: fe.sourceTexts().size,
1517
+ });
1274
1518
  let lowered;
1275
1519
  let entryText;
1276
1520
  let sourceTexts;
@@ -1372,6 +1616,9 @@ export async function compileLibrary(opts) {
1372
1616
  ]),
1373
1617
  ],
1374
1618
  });
1619
+ timing("lower", {
1620
+ lib_roots: profile.exports.length + contractSurfaceRoots.length,
1621
+ });
1375
1622
  }
1376
1623
  catch (e) {
1377
1624
  if (!isCheckerPanic(e))
@@ -1388,6 +1635,7 @@ export async function compileLibrary(opts) {
1388
1635
  fe.dispose();
1389
1636
  }
1390
1637
  const mod = lowered.module;
1638
+ timing("frontend-dispose");
1391
1639
  const fail = (diagnostics) => ({
1392
1640
  ok: false,
1393
1641
  diagnostics: decorateLibraryRefusals(diagnostics, profile),
@@ -1418,9 +1666,9 @@ export async function compileLibrary(opts) {
1418
1666
  // The ask-2 contract sidecar rides the same invocation. Identity first
1419
1667
  // (schema §2's worked build_id definition over compiler version, profile
1420
1668
  // bytes, and the sorted canonical module graph; source_hash per the
1421
- // profile's "module-graph" contract) — the u64 lands on the IR so both
1422
- // backends emit the identity getters from the ONE value the sidecar
1423
- // records (V12's coherence by construction), then the projection into
1669
+ // profile's "module-graph" contract) — the u64 lands on the IR so native
1670
+ // archive assembly emits the identity getters from the ONE value the
1671
+ // sidecar records (V12's coherence by construction), then the projection into
1424
1672
  // the schema (declaration orders from the AST) and the V1–V14
1425
1673
  // self-check before anything is written.
1426
1674
  let sidecarJson = null;
@@ -1457,6 +1705,7 @@ export async function compileLibrary(opts) {
1457
1705
  return fail([merged.diagnostic]);
1458
1706
  intCfg = merged.config;
1459
1707
  }
1708
+ timing("contract-sidecar", { source_files: sourceTexts.size });
1460
1709
  // Ask 4: the integer-boundary inference — every value that can reach a
1461
1710
  // profile-declared i64/u64 slot must PROVE representability, wholeness,
1462
1711
  // and range, or the build refuses with the failed obligation, the
@@ -1471,17 +1720,21 @@ export async function compileLibrary(opts) {
1471
1720
  return fail(refusals.map((v) => libIntBoundaryDiag(v.path, v.cls, v.obligation, v.detail, v.fix, v.loc)));
1472
1721
  }
1473
1722
  }
1723
+ timing("integer-proof");
1474
1724
  const validation = validateModule(mod);
1475
1725
  if (validation.length > 0)
1476
1726
  return fail(validation.map((v) => iceDiag(v.message, v.loc)));
1727
+ timing("ir-validate");
1477
1728
  await mkdir(opts.outDir, { recursive: true });
1478
1729
  const stem = basename(entryPath).replace(/\.(ts|js|mjs|cjs)$/, "");
1479
1730
  let cPath;
1480
1731
  if (profile.emission === "llvm") {
1481
1732
  try {
1482
1733
  const ll = emitLlvmModule(mod);
1734
+ timing("llvm-emit", { output_bytes: Buffer.byteLength(ll) });
1483
1735
  cPath = join(opts.outDir, `${stem}.lib.ll`);
1484
1736
  await writeFile(cPath, ll);
1737
+ timing("llvm-write");
1485
1738
  }
1486
1739
  catch (err) {
1487
1740
  if (!(err instanceof LlvmUnsupportedError))
@@ -1500,40 +1753,9 @@ export async function compileLibrary(opts) {
1500
1753
  irPath = join(opts.outDir, `${stem}.lib.ir.json`);
1501
1754
  await writeFile(irPath, serializeModule(mod));
1502
1755
  }
1503
- const archivePath = opts.outPath ?? join(opts.outDir, `${stem}.lib.a`);
1504
- // Multi-instance library mode: the profile-declared external surface —
1505
- // the mode-provided entries, the identity getters, and the export map —
1506
- // is exactly the set the localization step keeps global.
1507
- const localizeSymbols = profile.localizeRuntime
1508
- ? [
1509
- profile.initSymbol,
1510
- profile.sinkRegisterSymbol,
1511
- ...(profile.collectSymbol !== null ? [profile.collectSymbol] : []),
1512
- ...(profile.resultResetSymbol !== null ? [profile.resultResetSymbol] : []),
1513
- ...(profile.callbackRegisterSymbol !== null ? [profile.callbackRegisterSymbol] : []),
1514
- ...(profile.sidecar !== null
1515
- ? [profile.sidecar.buildIdSymbol, profile.sidecar.abiVersionSymbol]
1516
- : []),
1517
- ...profile.exports.map((e) => e.symbol),
1518
- ]
1519
- : undefined;
1520
- await compileLibArchive({
1521
- cPath,
1522
- outPath: archivePath,
1523
- cacheIdentity: "scriptc-generated-library-v1",
1524
- sanitize: opts.sanitize ?? false,
1525
- ...(localizeSymbols !== undefined ? { localizeSymbols } : {}),
1526
- ...(profile.instancePerThread ? { threadInstances: true } : {}),
1527
- regex: moduleUsesRegex(mod),
1528
- assert: moduleUsesAssert(mod),
1529
- inspect: moduleUsesInspect(mod),
1530
- symbol: moduleUsesSymbol(mod),
1531
- searchParams: moduleUsesSearchParams(mod),
1532
- emitter: moduleUsesEmitter(mod),
1533
- zlib: moduleUsesZlib(mod),
1534
- copying: moduleUsesCopying(mod),
1535
- textDecoderLegacy: moduleUsesLegacyTextDecoder(mod),
1536
- });
1756
+ const nativeFeatures = libraryNativeFeatures(mod, profile.emission);
1757
+ await compileLibraryNative(profile, cPath, archivePath, opts.sanitize ?? false, nativeFeatures);
1758
+ timing("native-archive");
1537
1759
  // The sidecar lands beside the compiled object, written by the same
1538
1760
  // invocation (profile-declared name; the neutral default when the
1539
1761
  // profile states none is <out>.contract.json).
@@ -1545,6 +1767,18 @@ export async function compileLibrary(opts) {
1545
1767
  : `${archivePath}.contract.json`;
1546
1768
  await writeFile(sidecarPath, sidecarJson);
1547
1769
  }
1770
+ const earlyPublish = {
1771
+ cPath,
1772
+ native: nativeFeatures,
1773
+ frontend: frontendInputs.snapshot(),
1774
+ semantic: { mod, sources: sourceTexts },
1775
+ ...(irPath !== undefined ? { irPath } : {}),
1776
+ ...(sidecarPath !== undefined ? { sidecarPath } : {}),
1777
+ };
1778
+ await publishEarlyLibraryCache(cacheRoot, earlyCacheOptions, earlyPublish).catch(() => undefined);
1779
+ await pruneBuildCache(cacheRoot);
1780
+ timing("early-cache-publish");
1781
+ timing("complete");
1548
1782
  return {
1549
1783
  ok: true,
1550
1784
  archivePath,