@soundscript/soundscript 0.1.15 → 0.1.17

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 (47) hide show
  1. package/decode.d.ts +2 -2
  2. package/decode.js.map +1 -1
  3. package/json.js +1 -1
  4. package/numerics.js +4 -4
  5. package/package.json +6 -6
  6. package/project-transform/src/checker/rules/flow.js +16 -3
  7. package/project-transform/src/checker/rules/flow.ts +20 -3
  8. package/project-transform/src/checker/rules/flow_invalidation.js +18 -21
  9. package/project-transform/src/checker/rules/flow_invalidation.ts +25 -20
  10. package/project-transform/src/checker/rules/relations.js +157 -3
  11. package/project-transform/src/checker/rules/relations.ts +207 -1
  12. package/project-transform/src/checker/rules/unsound_syntax.js +0 -3
  13. package/project-transform/src/checker/rules/unsound_syntax.ts +0 -4
  14. package/project-transform/src/cli.js +1 -1
  15. package/project-transform/src/cli.ts +1 -1
  16. package/project-transform/src/compiler/compile_project.js +75 -9
  17. package/project-transform/src/compiler/compile_project.ts +121 -7
  18. package/project-transform/src/compiler/ir.ts +19 -1
  19. package/project-transform/src/compiler/lower.js +10335 -1477
  20. package/project-transform/src/compiler/lower.ts +16826 -4074
  21. package/project-transform/src/compiler/toolchain.js +36 -4
  22. package/project-transform/src/compiler/toolchain.ts +36 -4
  23. package/project-transform/src/compiler/wasm_js_host_runtime.js +134 -0
  24. package/project-transform/src/compiler/wasm_js_host_runtime.ts +146 -0
  25. package/project-transform/src/compiler/wat_arrays.js +4 -1
  26. package/project-transform/src/compiler/wat_arrays.ts +5 -1
  27. package/project-transform/src/compiler/wat_emitter.js +1497 -311
  28. package/project-transform/src/compiler/wat_emitter.ts +2971 -1017
  29. package/project-transform/src/compiler/wat_tagged.js +5 -0
  30. package/project-transform/src/compiler/wat_tagged.ts +5 -0
  31. package/project-transform/src/compiler_generator_runner.js +2139 -19
  32. package/project-transform/src/compiler_generator_runner.ts +2143 -20
  33. package/project-transform/src/compiler_promise_runner.js +4615 -636
  34. package/project-transform/src/compiler_promise_runner.ts +4703 -659
  35. package/project-transform/src/compiler_test_helpers.js +0 -579
  36. package/project-transform/src/compiler_test_helpers.ts +0 -648
  37. package/project-transform/src/frontend/macro_expander.js +4 -6
  38. package/project-transform/src/frontend/macro_expander.ts +4 -6
  39. package/project-transform/src/frontend/macro_operand_semantics.js +124 -1
  40. package/project-transform/src/frontend/macro_operand_semantics.ts +230 -6
  41. package/project-transform/src/frontend/macro_resolver.js +2 -2
  42. package/project-transform/src/frontend/macro_resolver.ts +2 -1
  43. package/project-transform/src/frontend/project_macro_support.js +29 -5
  44. package/project-transform/src/frontend/project_macro_support.ts +46 -10
  45. package/project-transform/src/stdlib/decode.d.ts +2 -2
  46. package/project-transform/src/stdlib/decode.ts +2 -2
  47. package/soundscript/decode.sts +2 -2
@@ -24,11 +24,6 @@ export interface TempProjectFile {
24
24
  }
25
25
 
26
26
  const REPO_ROOT = dirname(dirname(fromFileUrl(import.meta.url)));
27
- const SOUNDSCRIPT_CLASS_TAG = '__soundscript_class_tag__';
28
- const SOUNDSCRIPT_CLASS_CONSTRUCT = '__soundscript_class_construct';
29
- const SOUNDSCRIPT_CLASS_BASE_CONSTRUCTOR = '__soundscript_class_base_constructor';
30
- const SOUNDSCRIPT_CLOSURE_REF = Symbol('soundscript_closure_ref');
31
- const SOUNDSCRIPT_SYNC_AWARE = Symbol('soundscript_sync_aware');
32
27
  const SOUNDSCRIPT_ISOLATED_TESTS = 'SOUNDSCRIPT_ISOLATED_TESTS';
33
28
  const SOUNDSCRIPT_ISOLATED_TEST_BATCH_SIZE = 'SOUNDSCRIPT_ISOLATED_TEST_BATCH_SIZE';
34
29
  const DEFAULT_ISOLATED_TEST_BATCH_SIZE = 8;
@@ -197,649 +192,6 @@ export async function readCompiledWasmBytes(tempDirectory: string): Promise<Uint
197
192
  return await Deno.readFile(wasmPath);
198
193
  }
199
194
 
200
- function createJsHostImports(
201
- instanceCell: { instance: WebAssembly.Instance | null },
202
- ): WebAssembly.Imports {
203
- const heapIdentityCache = new WeakMap<object, unknown>();
204
- const hostParamIdentityCache = new WeakMap<object, Map<string, unknown>>();
205
- const hostPromiseToInternalCache = new WeakMap<object, object>();
206
- const internalPromiseToHostCache = new WeakMap<object, Promise<unknown>>();
207
- const classConstructorWrappers = new Map<number, Function>();
208
- const getClassMethodSyncExportName = (tag: number, propertyName: string): string =>
209
- `__soundscript_sync_class_method_${tag}__${
210
- [...new TextEncoder().encode(propertyName)]
211
- .map((byte) => byte.toString(16).padStart(2, '0'))
212
- .join('')
213
- }`;
214
- const getClassMethodSyncFromHostExportName = (tag: number, propertyName: string): string =>
215
- `__soundscript_sync_from_class_method_${tag}__${
216
- [...new TextEncoder().encode(propertyName)]
217
- .map((byte) => byte.toString(16).padStart(2, '0'))
218
- .join('')
219
- }`;
220
- const getClassStaticMethodSyncExportName = (tag: number, propertyName: string): string =>
221
- `__soundscript_sync_class_static_method_${tag}__${
222
- [...new TextEncoder().encode(propertyName)]
223
- .map((byte) => byte.toString(16).padStart(2, '0'))
224
- .join('')
225
- }`;
226
- const getClassStaticMethodSyncFromHostExportName = (tag: number, propertyName: string): string =>
227
- `__soundscript_sync_from_class_static_method_${tag}__${
228
- [...new TextEncoder().encode(propertyName)]
229
- .map((byte) => byte.toString(16).padStart(2, '0'))
230
- .join('')
231
- }`;
232
-
233
- const expectHeapIdentityKey = (value: unknown): object => {
234
- if (
235
- (typeof value !== 'object' && typeof value !== 'function') ||
236
- value === null
237
- ) {
238
- throw new TypeError('Expected wasm heap reference for host identity cache.');
239
- }
240
- return value;
241
- };
242
-
243
- const getOrCreateHostClassConstructor = (tag: number): Function => {
244
- const existing = classConstructorWrappers.get(tag);
245
- if (existing) {
246
- return existing;
247
- }
248
- const wrapper = function SoundscriptClassWrapper(this: unknown, ...args: unknown[]): unknown {
249
- if (new.target === undefined) {
250
- throw new TypeError('Class constructors must be invoked with new.');
251
- }
252
- const construct =
253
- (wrapper as unknown as Record<string, unknown>)[SOUNDSCRIPT_CLASS_CONSTRUCT];
254
- if (typeof construct !== 'function') {
255
- throw new TypeError('Compiled class constructor wrapper is missing construct hook.');
256
- }
257
- return construct(...args);
258
- };
259
- Object.defineProperty(wrapper, 'name', {
260
- configurable: true,
261
- value: `SoundscriptClass${tag}`,
262
- });
263
- Object.defineProperty(wrapper, Symbol.hasInstance, {
264
- configurable: true,
265
- value(instance: unknown) {
266
- if (typeof instance !== 'object' || instance === null) {
267
- return false;
268
- }
269
- return wrapper.prototype.isPrototypeOf(instance);
270
- },
271
- });
272
- Object.defineProperty(wrapper.prototype, 'constructor', {
273
- configurable: true,
274
- enumerable: false,
275
- value: wrapper,
276
- writable: true,
277
- });
278
- Object.defineProperty(wrapper, SOUNDSCRIPT_CLASS_TAG, {
279
- configurable: true,
280
- enumerable: false,
281
- value: Number(tag),
282
- writable: true,
283
- });
284
- classConstructorWrappers.set(tag, wrapper);
285
- return wrapper;
286
- };
287
-
288
- const syncClassPrototype = (value: unknown) => {
289
- if (typeof value !== 'object' || value === null) {
290
- throw new TypeError('Expected JS object for soundscript_object.sync_class_prototype.');
291
- }
292
- const objectRecord = value as Record<string, unknown>;
293
- const tag = objectRecord[SOUNDSCRIPT_CLASS_TAG];
294
- if (typeof tag !== 'number') {
295
- return;
296
- }
297
- Object.setPrototypeOf(value, getOrCreateHostClassConstructor(Number(tag)).prototype);
298
- };
299
-
300
- const syncHostBoundaryValue = (value: unknown): unknown => {
301
- if (Array.isArray(value)) {
302
- for (let index = 0; index < value.length; index += 1) {
303
- value[index] = syncHostBoundaryValue(value[index]);
304
- }
305
- return value;
306
- }
307
- if (typeof value === 'object' && value !== null) {
308
- syncClassPrototype(value);
309
- }
310
- return value;
311
- };
312
-
313
- const wrapHostMethodIfNeeded = (
314
- target: Record<string, unknown>,
315
- propertyName: string,
316
- value: unknown,
317
- ): unknown => {
318
- if (typeof value !== 'function') {
319
- return value;
320
- }
321
- if ((value as unknown as Record<PropertyKey, unknown>)[SOUNDSCRIPT_SYNC_AWARE] === true) {
322
- return value;
323
- }
324
- const tag = target[SOUNDSCRIPT_CLASS_TAG];
325
- const closureRef = (value as unknown as Record<PropertyKey, unknown>)[SOUNDSCRIPT_CLOSURE_REF];
326
- if (typeof tag !== 'number' || closureRef === undefined) {
327
- return value;
328
- }
329
- const instance = instanceCell.instance;
330
- if (!instance) {
331
- return value;
332
- }
333
- const syncExportName = typeof target === 'function'
334
- ? getClassStaticMethodSyncExportName(Number(tag), propertyName)
335
- : getClassMethodSyncExportName(Number(tag), propertyName);
336
- const syncFromHostExportName = typeof target === 'function'
337
- ? getClassStaticMethodSyncFromHostExportName(Number(tag), propertyName)
338
- : getClassMethodSyncFromHostExportName(Number(tag), propertyName);
339
- const sync = instance.exports[syncExportName];
340
- const syncFromHost = instance.exports[syncFromHostExportName];
341
- if (typeof sync !== 'function' && typeof syncFromHost !== 'function') {
342
- return value;
343
- }
344
- const callable = value as (...args: unknown[]) => unknown;
345
- const wrapped = function (this: unknown, ...args: unknown[]): unknown {
346
- if (typeof syncFromHost === 'function') {
347
- syncFromHost(closureRef, target);
348
- }
349
- const result = callable.apply(this, args);
350
- if (typeof sync === 'function') {
351
- sync(closureRef, target);
352
- }
353
- return result;
354
- };
355
- Object.defineProperty(wrapped, SOUNDSCRIPT_CLOSURE_REF, {
356
- configurable: true,
357
- enumerable: false,
358
- value: closureRef,
359
- writable: false,
360
- });
361
- return wrapped;
362
- };
363
-
364
- return {
365
- soundscript_array: {
366
- empty: () => [],
367
- empty_number: () => [],
368
- empty_boolean: () => [],
369
- clear: (value: unknown) => {
370
- if (!Array.isArray(value)) {
371
- throw new TypeError('Expected JS array for soundscript_array.clear.');
372
- }
373
- value.length = 0;
374
- },
375
- length: (value: unknown) => {
376
- if (!Array.isArray(value)) {
377
- throw new TypeError('Expected JS array for soundscript_array.length.');
378
- }
379
- return value.length;
380
- },
381
- same: (left: unknown, right: unknown) => Number(Object.is(left, right)),
382
- get: (value: unknown, index: number) => {
383
- if (!Array.isArray(value)) {
384
- throw new TypeError('Expected JS array for soundscript_array.get.');
385
- }
386
- return value[Number(index)];
387
- },
388
- get_number: (value: unknown, index: number) => {
389
- if (!Array.isArray(value)) {
390
- throw new TypeError('Expected JS array for soundscript_array.get_number.');
391
- }
392
- return Number(value[Number(index)]);
393
- },
394
- get_boolean: (value: unknown, index: number) => {
395
- if (!Array.isArray(value)) {
396
- throw new TypeError('Expected JS array for soundscript_array.get_boolean.');
397
- }
398
- return Boolean(value[Number(index)]) ? 1 : 0;
399
- },
400
- push: (target: unknown, value: unknown) => {
401
- if (!Array.isArray(target)) {
402
- throw new TypeError('Expected JS array for soundscript_array.push.');
403
- }
404
- target.push(syncHostBoundaryValue(value));
405
- },
406
- push_number: (target: unknown, value: unknown) => {
407
- if (!Array.isArray(target)) {
408
- throw new TypeError('Expected JS array for soundscript_array.push_number.');
409
- }
410
- target.push(Number(value));
411
- },
412
- push_boolean: (target: unknown, value: unknown) => {
413
- if (!Array.isArray(target)) {
414
- throw new TypeError('Expected JS array for soundscript_array.push_boolean.');
415
- }
416
- target.push(Boolean(value));
417
- },
418
- },
419
- soundscript_length_view: {
420
- length: (value: unknown) => {
421
- if (
422
- value === null ||
423
- value === undefined ||
424
- (!Array.isArray(value) && typeof value !== 'string' &&
425
- (typeof value !== 'object' || value === null || !('length' in value)))
426
- ) {
427
- throw new TypeError('Expected string, array, or length-bearing object.');
428
- }
429
- return Number((value as { length: unknown }).length);
430
- },
431
- from_length: (value: number) => ({ length: Number(value) }),
432
- },
433
- soundscript_object: new Proxy({}, {
434
- get(_target, property) {
435
- if (property === 'same') {
436
- return (left: unknown, right: unknown) => Number(left === right);
437
- }
438
- if (property === 'empty') {
439
- return () => ({});
440
- }
441
- if (property === 'lookup_cached') {
442
- return (value: unknown) => heapIdentityCache.get(expectHeapIdentityKey(value)) ?? null;
443
- }
444
- if (property === 'remember_cached') {
445
- return (value: unknown, hostValue: unknown) => {
446
- heapIdentityCache.set(expectHeapIdentityKey(value), hostValue);
447
- };
448
- }
449
- if (property === 'get_class_tag') {
450
- return (value: unknown) => {
451
- if ((typeof value !== 'object' && typeof value !== 'function') || value === null) {
452
- throw new TypeError(
453
- 'Expected JS object or function for soundscript_object.get_class_tag.',
454
- );
455
- }
456
- const objectRecord = value as Record<string, unknown>;
457
- return typeof objectRecord[SOUNDSCRIPT_CLASS_TAG] === 'number'
458
- ? Number(objectRecord[SOUNDSCRIPT_CLASS_TAG])
459
- : -1;
460
- };
461
- }
462
- if (property === 'set_class_tag') {
463
- return (value: unknown, next: number) => {
464
- if ((typeof value !== 'object' && typeof value !== 'function') || value === null) {
465
- throw new TypeError(
466
- 'Expected JS object or function for soundscript_object.set_class_tag.',
467
- );
468
- }
469
- Object.defineProperty(value, SOUNDSCRIPT_CLASS_TAG, {
470
- configurable: true,
471
- enumerable: false,
472
- value: Number(next),
473
- writable: true,
474
- });
475
- };
476
- }
477
- if (property === 'class_constructor_from_tag') {
478
- return (tag: number) => getOrCreateHostClassConstructor(Number(tag));
479
- }
480
- if (property === 'sync_class_prototype') {
481
- return syncClassPrototype;
482
- }
483
- if (typeof property !== 'string') {
484
- return undefined;
485
- }
486
- const expectMapValue = (value: unknown): Map<unknown, unknown> => {
487
- if (!(value instanceof Map)) {
488
- throw new TypeError('Expected JS Map for soundscript_object collection host boundary.');
489
- }
490
- return value;
491
- };
492
- const expectSetValue = (value: unknown): Set<unknown> => {
493
- if (!(value instanceof Set)) {
494
- throw new TypeError('Expected JS Set for soundscript_object collection host boundary.');
495
- }
496
- return value;
497
- };
498
- if (property === 'map_keys') {
499
- return (value: unknown) =>
500
- Array.from(expectMapValue(value).keys(), (entry) => String(entry));
501
- }
502
- if (property === 'map_values_number') {
503
- return (value: unknown) =>
504
- Array.from(expectMapValue(value).values(), (entry) => Number(entry));
505
- }
506
- if (property === 'map_values_boolean') {
507
- return (value: unknown) =>
508
- Array.from(expectMapValue(value).values(), (entry) => Boolean(entry));
509
- }
510
- if (property === 'map_values_string') {
511
- return (value: unknown) =>
512
- Array.from(expectMapValue(value).values(), (entry) => String(entry));
513
- }
514
- if (property === 'set_values_number') {
515
- return (value: unknown) =>
516
- Array.from(expectSetValue(value).values(), (entry) => Number(entry));
517
- }
518
- if (property === 'set_values_boolean') {
519
- return (value: unknown) =>
520
- Array.from(expectSetValue(value).values(), (entry) => Boolean(entry));
521
- }
522
- if (property === 'set_values_string') {
523
- return (value: unknown) =>
524
- Array.from(expectSetValue(value).values(), (entry) => String(entry));
525
- }
526
- if (property === 'set_values_key') {
527
- return () => '__set_values';
528
- }
529
- const match =
530
- /^(get_number|get_boolean|get_closure|set_number|set_boolean|set_closure|has|get_tagged|set_tagged):(.*)$/
531
- .exec(property);
532
- const paramCacheMatch = /^(lookup_param_cached|remember_param_cached):(.*)$/.exec(property);
533
- if (paramCacheMatch) {
534
- const kind = paramCacheMatch[1];
535
- const cacheKey = decodeURIComponent(paramCacheMatch[2]!);
536
- const expectHostIdentityKey = (value: unknown): object => {
537
- if ((typeof value !== 'object' && typeof value !== 'function') || value === null) {
538
- throw new TypeError(
539
- 'Expected JS object for soundscript_object param identity cache.',
540
- );
541
- }
542
- return value;
543
- };
544
- if (kind === 'lookup_param_cached') {
545
- return (value: unknown) =>
546
- hostParamIdentityCache.get(expectHostIdentityKey(value))?.get(cacheKey) ?? null;
547
- }
548
- return (value: unknown, heapValue: unknown) => {
549
- const hostKey = expectHostIdentityKey(value);
550
- const existing = hostParamIdentityCache.get(hostKey);
551
- if (existing) {
552
- existing.set(cacheKey, heapValue);
553
- return;
554
- }
555
- const created = new Map<string, unknown>();
556
- created.set(cacheKey, heapValue);
557
- hostParamIdentityCache.set(hostKey, created);
558
- };
559
- }
560
- if (!match) {
561
- return undefined;
562
- }
563
- const kind = match[1];
564
- const propertyName = decodeURIComponent(match[2]);
565
- const expectObjectRecord = (value: unknown): Record<string, unknown> => {
566
- if ((typeof value !== 'object' && typeof value !== 'function') || value === null) {
567
- throw new TypeError(
568
- 'Expected JS object or function for soundscript_object host boundary.',
569
- );
570
- }
571
- return value as Record<string, unknown>;
572
- };
573
- switch (kind) {
574
- case 'has':
575
- return (value: unknown) =>
576
- Number(Object.prototype.hasOwnProperty.call(expectObjectRecord(value), propertyName));
577
- case 'get_number':
578
- return (value: unknown) => Number(expectObjectRecord(value)[propertyName]);
579
- case 'get_boolean':
580
- return (value: unknown) => Number(Boolean(expectObjectRecord(value)[propertyName]));
581
- case 'get_closure':
582
- return (value: unknown) => {
583
- const objectRecord = expectObjectRecord(value);
584
- const propertyValue = objectRecord[propertyName];
585
- return typeof propertyValue === 'function'
586
- ? propertyValue.bind(value)
587
- : propertyValue;
588
- };
589
- case 'get_tagged':
590
- return (value: unknown) => expectObjectRecord(value)[propertyName];
591
- case 'set_number':
592
- return (value: unknown, next: number) => {
593
- expectObjectRecord(value)[propertyName] = Number(next);
594
- };
595
- case 'set_boolean':
596
- return (value: unknown, next: number) => {
597
- expectObjectRecord(value)[propertyName] = next !== 0;
598
- };
599
- case 'set_closure':
600
- return (value: unknown, next: unknown) => {
601
- const objectRecord = expectObjectRecord(value);
602
- objectRecord[propertyName] = wrapHostMethodIfNeeded(objectRecord, propertyName, next);
603
- };
604
- case 'set_tagged':
605
- return (value: unknown, next: unknown) => {
606
- const objectRecord = expectObjectRecord(value);
607
- objectRecord[propertyName] = syncHostBoundaryValue(next);
608
- if (
609
- propertyName === SOUNDSCRIPT_CLASS_BASE_CONSTRUCTOR &&
610
- typeof value === 'function' &&
611
- typeof next === 'function'
612
- ) {
613
- Object.setPrototypeOf(value, next);
614
- if (
615
- typeof (value as { prototype?: unknown }).prototype === 'object' &&
616
- (value as { prototype?: unknown }).prototype !== null &&
617
- typeof (next as { prototype?: unknown }).prototype === 'object' &&
618
- (next as { prototype?: unknown }).prototype !== null
619
- ) {
620
- Object.setPrototypeOf(
621
- (value as { prototype: object }).prototype,
622
- (next as { prototype: object }).prototype,
623
- );
624
- }
625
- }
626
- };
627
- default:
628
- return undefined;
629
- }
630
- },
631
- }),
632
- soundscript_string: {
633
- empty: () => '',
634
- from_char_code: (value: number) => String.fromCharCode(Number(value)),
635
- length: (value: unknown) => String(value).length,
636
- char_at: (value: unknown, index: number) => String(value).charAt(Number(index)),
637
- char_code_at: (value: unknown, index: number) => String(value).charCodeAt(Number(index)),
638
- to_upper_case: (value: unknown) => String(value).toUpperCase(),
639
- to_lower_case: (value: unknown) => String(value).toLowerCase(),
640
- trim: (value: unknown) => String(value).trim(),
641
- trim_start: (value: unknown) => String(value).trimStart(),
642
- trim_end: (value: unknown) => String(value).trimEnd(),
643
- starts_with: (value: unknown, search: unknown) =>
644
- Number(String(value).startsWith(String(search))),
645
- ends_with: (value: unknown, search: unknown) =>
646
- Number(String(value).endsWith(String(search))),
647
- includes: (value: unknown, search: unknown) => Number(String(value).includes(String(search))),
648
- index_of: (value: unknown, search: unknown) => String(value).indexOf(String(search)),
649
- last_index_of: (value: unknown, search: unknown) => String(value).lastIndexOf(String(search)),
650
- slice: (value: unknown, start: number, end: number, hasEnd: number) =>
651
- hasEnd
652
- ? String(value).slice(Number(start), Number(end))
653
- : String(value).slice(Number(start)),
654
- substring: (value: unknown, start: number, end: number, hasEnd: number) =>
655
- hasEnd
656
- ? String(value).substring(Number(start), Number(end))
657
- : String(value).substring(Number(start)),
658
- concat: (left: unknown, right: unknown) => String(left) + String(right),
659
- equals: (left: unknown, right: unknown) => Number(String(left) === String(right)),
660
- },
661
- soundscript_tagged: {
662
- undefined_value: () => undefined,
663
- type_tag: (value: unknown) => {
664
- if (value === undefined) {
665
- return 0;
666
- }
667
- if (typeof value === 'boolean') {
668
- return 1;
669
- }
670
- if (typeof value === 'number') {
671
- return 2;
672
- }
673
- if (typeof value === 'string') {
674
- return 3;
675
- }
676
- if (typeof value === 'object' || typeof value === 'function') {
677
- if (value === null) {
678
- return 6;
679
- }
680
- return 4;
681
- }
682
- if (value === null) {
683
- return 6;
684
- }
685
- throw new Error(`Unsupported tagged host value: ${String(value)}`);
686
- },
687
- number_value: (value: unknown) => Number(value),
688
- boolean_value: (value: unknown) => Number(Boolean(value)),
689
- from_number: (value: number) => Number(value),
690
- from_boolean: (value: number) => value !== 0,
691
- },
692
- soundscript_closure: new Proxy({}, {
693
- get(_target, property) {
694
- if (typeof property !== 'string') {
695
- return undefined;
696
- }
697
- const callMatch = /^call_(\d+)$/.exec(property);
698
- if (callMatch) {
699
- return (callback: unknown, ...args: unknown[]) => {
700
- if (typeof callback !== 'function') {
701
- throw new TypeError('Expected JS function for soundscript_closure.call.');
702
- }
703
- return callback(...args);
704
- };
705
- }
706
- const toHostSyncMatch = /^to_host_sync_(\d+):(.*)$/.exec(property);
707
- if (toHostSyncMatch) {
708
- const signatureId = Number(toHostSyncMatch[1]);
709
- const syncExportName = decodeURIComponent(toHostSyncMatch[2] ?? '');
710
- return (target: unknown, closure: unknown) => {
711
- const wrapped = (...args: unknown[]) => {
712
- const instance = instanceCell.instance;
713
- if (!instance) {
714
- throw new Error('Closure export wrapper invoked before instantiation completed.');
715
- }
716
- const invoke = instance.exports[`__soundscript_closure_invoke_${signatureId}`];
717
- if (typeof invoke !== 'function') {
718
- throw new Error(`Missing exported closure invoker for signature ${signatureId}.`);
719
- }
720
- const sync = instance.exports[syncExportName];
721
- if (typeof sync !== 'function') {
722
- throw new Error(`Missing exported closure sync helper ${syncExportName}.`);
723
- }
724
- const syncFromHostExportName =
725
- syncExportName.startsWith('__soundscript_sync_class_method_')
726
- ? syncExportName.replace(
727
- '__soundscript_sync_class_method_',
728
- '__soundscript_sync_from_class_method_',
729
- )
730
- : syncExportName.startsWith('__soundscript_sync_class_static_method_')
731
- ? syncExportName.replace(
732
- '__soundscript_sync_class_static_method_',
733
- '__soundscript_sync_from_class_static_method_',
734
- )
735
- : null;
736
- const syncFromHost = syncFromHostExportName
737
- ? instance.exports[syncFromHostExportName]
738
- : undefined;
739
- if (typeof syncFromHost === 'function') {
740
- syncFromHost(closure, target);
741
- }
742
- const result = invoke(closure, ...args);
743
- sync(closure, target);
744
- return result;
745
- };
746
- Object.defineProperty(wrapped, SOUNDSCRIPT_CLOSURE_REF, {
747
- configurable: true,
748
- enumerable: false,
749
- value: closure,
750
- writable: false,
751
- });
752
- Object.defineProperty(wrapped, SOUNDSCRIPT_SYNC_AWARE, {
753
- configurable: true,
754
- enumerable: false,
755
- value: true,
756
- writable: false,
757
- });
758
- return wrapped;
759
- };
760
- }
761
- const toHostMatch = /^to_host_(\d+)$/.exec(property);
762
- if (toHostMatch) {
763
- const signatureId = Number(toHostMatch[1]);
764
- return (closure: unknown) => {
765
- const wrapped = (...args: unknown[]) => {
766
- const instance = instanceCell.instance;
767
- if (!instance) {
768
- throw new Error('Closure export wrapper invoked before instantiation completed.');
769
- }
770
- const invoke = instance.exports[`__soundscript_closure_invoke_${signatureId}`];
771
- if (typeof invoke !== 'function') {
772
- throw new Error(`Missing exported closure invoker for signature ${signatureId}.`);
773
- }
774
- return invoke(closure, ...args);
775
- };
776
- Object.defineProperty(wrapped, SOUNDSCRIPT_CLOSURE_REF, {
777
- configurable: true,
778
- enumerable: false,
779
- value: closure,
780
- writable: false,
781
- });
782
- return wrapped;
783
- };
784
- }
785
- return undefined;
786
- },
787
- }),
788
- soundscript_promise: {
789
- to_internal: (value: unknown, candidate: unknown) => {
790
- if (!(value instanceof Promise)) {
791
- throw new TypeError('Expected JS Promise for soundscript_promise.to_internal.');
792
- }
793
- const candidateKey = expectHeapIdentityKey(candidate);
794
- const existing = hostPromiseToInternalCache.get(value);
795
- if (existing) {
796
- return existing;
797
- }
798
- const instance = instanceCell.instance;
799
- if (!instance) {
800
- throw new Error('Promise bridge invoked before instantiation completed.');
801
- }
802
- const fulfill = instance.exports.__soundscript_promise_bridge_fulfill;
803
- const reject = instance.exports.__soundscript_promise_bridge_reject;
804
- if (typeof fulfill !== 'function' || typeof reject !== 'function') {
805
- throw new Error('Missing exported Promise bridge helpers.');
806
- }
807
- hostPromiseToInternalCache.set(value, candidateKey);
808
- internalPromiseToHostCache.set(candidateKey, value);
809
- value.then(
810
- (resolved) => {
811
- fulfill(candidateKey, resolved);
812
- },
813
- (reason) => {
814
- reject(candidateKey, reason);
815
- },
816
- );
817
- return candidateKey;
818
- },
819
- to_host: (value: unknown) => {
820
- const internalPromise = expectHeapIdentityKey(value);
821
- const existing = internalPromiseToHostCache.get(internalPromise);
822
- if (existing) {
823
- return existing;
824
- }
825
- const instance = instanceCell.instance;
826
- if (!instance) {
827
- throw new Error('Promise bridge invoked before instantiation completed.');
828
- }
829
- const attach = instance.exports.__soundscript_promise_then_host;
830
- if (typeof attach !== 'function') {
831
- throw new Error('Missing exported Promise host attachment helper.');
832
- }
833
- const created = new Promise<unknown>((resolve, reject) => {
834
- attach(internalPromise, resolve, reject);
835
- });
836
- internalPromiseToHostCache.set(internalPromise, created);
837
- return created;
838
- },
839
- },
840
- };
841
- }
842
-
843
195
  export async function instantiateCompiledModuleInJs(
844
196
  tempDirectory: string,
845
197
  options?: {
@@ -901,13 +901,11 @@ export function expandPreparedProgramWithRegistry(preparedProgram, registry, adv
901
901
  export function expandPreparedProgramWithModules(preparedProgram, modules) {
902
902
  return expandPreparedProgramWithRegistry(preparedProgram, buildMacroRegistryFromModules(modules), buildAdvancedMacroRegistryFromModules(modules));
903
903
  }
904
- export function expandPreparedProgramWithFileRegistries(preparedProgram, registriesByFile, preserveMissingExpanders = false, annotateExpansions = false) {
905
- const collected = collectResolvedMacroPlaceholders(preparedProgram);
904
+ export function expandPreparedProgramWithFileRegistries(preparedProgram, registriesByFile, preserveMissingExpanders = false, annotateExpansions = false, sourceFiles = preparedProgram.program.getSourceFiles()) {
905
+ const nonDeclarationSourceFiles = sourceFiles.filter((sourceFile) => !sourceFile.isDeclarationFile);
906
+ const collected = collectResolvedMacroPlaceholders(preparedProgram, nonDeclarationSourceFiles);
906
907
  const expandedFiles = new Map();
907
- for (const sourceFile of preparedProgram.program.getSourceFiles()) {
908
- if (sourceFile.isDeclarationFile) {
909
- continue;
910
- }
908
+ for (const sourceFile of nonDeclarationSourceFiles) {
911
909
  const fileRegistries = registriesByFile.get(sourceFile.fileName);
912
910
  const advancedRegistry = fileRegistries?.advancedRegistry ?? new Map();
913
911
  const registry = fileRegistries?.registry ?? new Map();