@soundscript/soundscript 0.1.16 → 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.
- package/json.js +1 -1
- package/numerics.js +4 -4
- package/package.json +6 -6
- package/project-transform/src/checker/rules/flow.js +16 -3
- package/project-transform/src/checker/rules/flow.ts +20 -3
- package/project-transform/src/checker/rules/unsound_syntax.js +0 -3
- package/project-transform/src/checker/rules/unsound_syntax.ts +0 -4
- package/project-transform/src/cli.js +1 -1
- package/project-transform/src/cli.ts +1 -1
- package/project-transform/src/compiler/compile_project.js +75 -9
- package/project-transform/src/compiler/compile_project.ts +121 -7
- package/project-transform/src/compiler/ir.ts +19 -1
- package/project-transform/src/compiler/lower.js +10335 -1477
- package/project-transform/src/compiler/lower.ts +16826 -4074
- package/project-transform/src/compiler/toolchain.js +36 -4
- package/project-transform/src/compiler/toolchain.ts +36 -4
- package/project-transform/src/compiler/wasm_js_host_runtime.js +134 -0
- package/project-transform/src/compiler/wasm_js_host_runtime.ts +146 -0
- package/project-transform/src/compiler/wat_arrays.js +4 -1
- package/project-transform/src/compiler/wat_arrays.ts +5 -1
- package/project-transform/src/compiler/wat_emitter.js +1497 -311
- package/project-transform/src/compiler/wat_emitter.ts +2971 -1017
- package/project-transform/src/compiler/wat_tagged.js +5 -0
- package/project-transform/src/compiler/wat_tagged.ts +5 -0
- package/project-transform/src/compiler_generator_runner.js +2139 -19
- package/project-transform/src/compiler_generator_runner.ts +2143 -20
- package/project-transform/src/compiler_promise_runner.js +4615 -636
- package/project-transform/src/compiler_promise_runner.ts +4703 -659
- package/project-transform/src/compiler_test_helpers.js +0 -579
- package/project-transform/src/compiler_test_helpers.ts +0 -648
- package/project-transform/src/frontend/macro_expander.js +4 -6
- package/project-transform/src/frontend/macro_expander.ts +4 -6
- package/project-transform/src/frontend/macro_operand_semantics.js +124 -1
- package/project-transform/src/frontend/macro_operand_semantics.ts +230 -6
- package/project-transform/src/frontend/macro_resolver.js +2 -2
- package/project-transform/src/frontend/macro_resolver.ts +2 -1
- package/project-transform/src/frontend/project_macro_support.js +29 -5
- package/project-transform/src/frontend/project_macro_support.ts +46 -10
|
@@ -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
|
|
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
|
|
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();
|