@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.
Files changed (38) hide show
  1. package/json.js +1 -1
  2. package/numerics.js +4 -4
  3. package/package.json +6 -6
  4. package/project-transform/src/checker/rules/flow.js +16 -3
  5. package/project-transform/src/checker/rules/flow.ts +20 -3
  6. package/project-transform/src/checker/rules/unsound_syntax.js +0 -3
  7. package/project-transform/src/checker/rules/unsound_syntax.ts +0 -4
  8. package/project-transform/src/cli.js +1 -1
  9. package/project-transform/src/cli.ts +1 -1
  10. package/project-transform/src/compiler/compile_project.js +75 -9
  11. package/project-transform/src/compiler/compile_project.ts +121 -7
  12. package/project-transform/src/compiler/ir.ts +19 -1
  13. package/project-transform/src/compiler/lower.js +10335 -1477
  14. package/project-transform/src/compiler/lower.ts +16826 -4074
  15. package/project-transform/src/compiler/toolchain.js +36 -4
  16. package/project-transform/src/compiler/toolchain.ts +36 -4
  17. package/project-transform/src/compiler/wasm_js_host_runtime.js +134 -0
  18. package/project-transform/src/compiler/wasm_js_host_runtime.ts +146 -0
  19. package/project-transform/src/compiler/wat_arrays.js +4 -1
  20. package/project-transform/src/compiler/wat_arrays.ts +5 -1
  21. package/project-transform/src/compiler/wat_emitter.js +1497 -311
  22. package/project-transform/src/compiler/wat_emitter.ts +2971 -1017
  23. package/project-transform/src/compiler/wat_tagged.js +5 -0
  24. package/project-transform/src/compiler/wat_tagged.ts +5 -0
  25. package/project-transform/src/compiler_generator_runner.js +2139 -19
  26. package/project-transform/src/compiler_generator_runner.ts +2143 -20
  27. package/project-transform/src/compiler_promise_runner.js +4615 -636
  28. package/project-transform/src/compiler_promise_runner.ts +4703 -659
  29. package/project-transform/src/compiler_test_helpers.js +0 -579
  30. package/project-transform/src/compiler_test_helpers.ts +0 -648
  31. package/project-transform/src/frontend/macro_expander.js +4 -6
  32. package/project-transform/src/frontend/macro_expander.ts +4 -6
  33. package/project-transform/src/frontend/macro_operand_semantics.js +124 -1
  34. package/project-transform/src/frontend/macro_operand_semantics.ts +230 -6
  35. package/project-transform/src/frontend/macro_resolver.js +2 -2
  36. package/project-transform/src/frontend/macro_resolver.ts +2 -1
  37. package/project-transform/src/frontend/project_macro_support.js +29 -5
  38. package/project-transform/src/frontend/project_macro_support.ts +46 -10
@@ -53,9 +53,11 @@ function rebaseWrapperModuleSpecifier(binding, projectPath) {
53
53
  }
54
54
  function createRuntimeWrapperModuleText(jsHostImports, projectPath) {
55
55
  const wrapperBindings = jsHostImports.map((binding) => ({
56
+ bindingKind: binding.bindingKind,
56
57
  exportName: binding.exportName,
57
58
  hostImportName: binding.hostImportName,
58
59
  importKind: binding.importKind,
60
+ memberName: binding.memberName,
59
61
  moduleSpecifier: rebaseWrapperModuleSpecifier(binding, projectPath),
60
62
  originalModuleSpecifier: binding.moduleSpecifier,
61
63
  relative: isRelativeModuleSpecifier(binding.moduleSpecifier),
@@ -93,10 +95,40 @@ function createRuntimeWrapperModuleText(jsHostImports, projectPath) {
93
95
  " const exportedValue = binding.importKind === 'default'",
94
96
  ' ? hostModule.default',
95
97
  ' : hostModule[binding.exportName];',
96
- " if (typeof exportedValue !== 'function') {",
97
- " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a function.`);",
98
- ' }',
99
- ' hostFunctions[binding.hostImportName] = exportedValue;',
98
+ " hostFunctions[binding.hostImportName] = binding.bindingKind === 'constructor'",
99
+ ' ? (() => {',
100
+ " if (typeof exportedValue !== 'function') {",
101
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a constructor.`);",
102
+ ' }',
103
+ ' return (...args) => new exportedValue(...args);',
104
+ ' })()',
105
+ " : binding.bindingKind === 'property'",
106
+ ' ? (() => {',
107
+ ' if (binding.memberName === undefined) {',
108
+ ' return () => exportedValue;',
109
+ ' }',
110
+ " if ((typeof exportedValue !== 'function' && typeof exportedValue !== 'object') || exportedValue === null) {",
111
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a property owner.`);",
112
+ ' }',
113
+ ' return () => exportedValue[binding.memberName];',
114
+ ' })()',
115
+ " : binding.bindingKind === 'static_method'",
116
+ ' ? (() => {',
117
+ " if ((typeof exportedValue !== 'function' && typeof exportedValue !== 'object') || exportedValue === null) {",
118
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a static method owner.`);",
119
+ ' }',
120
+ ' const methodValue = exportedValue[binding.memberName];',
121
+ " if (typeof methodValue !== 'function') {",
122
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a function.`);",
123
+ ' }',
124
+ ' return (...args) => exportedValue[binding.memberName](...args);',
125
+ ' })()',
126
+ ' : (() => {',
127
+ " if (typeof exportedValue !== 'function') {",
128
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a function.`);",
129
+ ' }',
130
+ ' return exportedValue;',
131
+ ' })();',
100
132
  ' }',
101
133
  ' return hostFunctions;',
102
134
  '}',
@@ -97,9 +97,11 @@ function createRuntimeWrapperModuleText(
97
97
  projectPath: string,
98
98
  ): string {
99
99
  const wrapperBindings = jsHostImports.map((binding) => ({
100
+ bindingKind: binding.bindingKind,
100
101
  exportName: binding.exportName,
101
102
  hostImportName: binding.hostImportName,
102
103
  importKind: binding.importKind,
104
+ memberName: binding.memberName,
103
105
  moduleSpecifier: rebaseWrapperModuleSpecifier(binding, projectPath),
104
106
  originalModuleSpecifier: binding.moduleSpecifier,
105
107
  relative: isRelativeModuleSpecifier(binding.moduleSpecifier),
@@ -137,10 +139,40 @@ function createRuntimeWrapperModuleText(
137
139
  " const exportedValue = binding.importKind === 'default'",
138
140
  ' ? hostModule.default',
139
141
  ' : hostModule[binding.exportName];',
140
- " if (typeof exportedValue !== 'function') {",
141
- " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a function.`);",
142
- ' }',
143
- ' hostFunctions[binding.hostImportName] = exportedValue;',
142
+ " hostFunctions[binding.hostImportName] = binding.bindingKind === 'constructor'",
143
+ ' ? (() => {',
144
+ " if (typeof exportedValue !== 'function') {",
145
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a constructor.`);",
146
+ ' }',
147
+ ' return (...args) => new exportedValue(...args);',
148
+ ' })()',
149
+ " : binding.bindingKind === 'property'",
150
+ ' ? (() => {',
151
+ ' if (binding.memberName === undefined) {',
152
+ ' return () => exportedValue;',
153
+ ' }',
154
+ " if ((typeof exportedValue !== 'function' && typeof exportedValue !== 'object') || exportedValue === null) {",
155
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a property owner.`);",
156
+ ' }',
157
+ ' return () => exportedValue[binding.memberName];',
158
+ ' })()',
159
+ " : binding.bindingKind === 'static_method'",
160
+ ' ? (() => {',
161
+ " if ((typeof exportedValue !== 'function' && typeof exportedValue !== 'object') || exportedValue === null) {",
162
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a static method owner.`);",
163
+ ' }',
164
+ ' const methodValue = exportedValue[binding.memberName];',
165
+ " if (typeof methodValue !== 'function') {",
166
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a function.`);",
167
+ ' }',
168
+ ' return (...args) => exportedValue[binding.memberName](...args);',
169
+ ' })()',
170
+ ' : (() => {',
171
+ " if (typeof exportedValue !== 'function') {",
172
+ " throw new TypeError(`Expected interop import \"${binding.hostImportName}\" to resolve to a function.`);",
173
+ ' }',
174
+ ' return exportedValue;',
175
+ ' })();',
144
176
  ' }',
145
177
  ' return hostFunctions;',
146
178
  '}',
@@ -634,6 +634,140 @@ function createJsHostImports(instanceCell) {
634
634
  return created;
635
635
  },
636
636
  },
637
+ soundscript_async_generator: {
638
+ wrap: (step) => {
639
+ if (typeof step !== 'function') {
640
+ throw new TypeError('Expected host-callable async generator step.');
641
+ }
642
+ return {
643
+ next(value) {
644
+ return Promise.resolve(step(0, value));
645
+ },
646
+ return(value) {
647
+ return Promise.resolve(step(1, value));
648
+ },
649
+ throw(value) {
650
+ return Promise.resolve(step(2, value));
651
+ },
652
+ };
653
+ },
654
+ step: (step, mode, value, candidate) => {
655
+ const instance = instanceCell.instance;
656
+ if (!instance) {
657
+ throw new Error('Async generator bridge invoked before instantiation completed.');
658
+ }
659
+ const fulfill = instance.exports.__soundscript_async_generator_step_bridge_fulfill;
660
+ const fulfillStringArray = instance.exports.__soundscript_async_generator_step_bridge_fulfill_string_array;
661
+ const fulfillNumberArray = instance.exports.__soundscript_async_generator_step_bridge_fulfill_number_array;
662
+ const fulfillBooleanArray = instance.exports.__soundscript_async_generator_step_bridge_fulfill_boolean_array;
663
+ const fulfillTaggedArray = instance.exports.__soundscript_async_generator_step_bridge_fulfill_tagged_array;
664
+ const reject = instance.exports.__soundscript_promise_bridge_reject;
665
+ if (typeof fulfill !== 'function' || typeof reject !== 'function') {
666
+ throw new Error('Missing async generator bridge helpers.');
667
+ }
668
+ Promise.resolve()
669
+ .then(() => {
670
+ if (typeof step !== 'function') {
671
+ throw new TypeError('Expected host-callable async generator step.');
672
+ }
673
+ return step(mode, value);
674
+ })
675
+ .then((result) => {
676
+ if ((typeof result !== 'object' && typeof result !== 'function') || result === null) {
677
+ throw new TypeError('Expected async generator step to produce an iterator result object.');
678
+ }
679
+ const iteratorResult = result;
680
+ const yieldedValue = Object.hasOwn(iteratorResult, 'value')
681
+ ? iteratorResult.value
682
+ : undefined;
683
+ if (Array.isArray(yieldedValue)) {
684
+ const done = iteratorResult.done ? 1 : 0;
685
+ if (yieldedValue.length === 0) {
686
+ if (typeof fulfillTaggedArray !== 'function') {
687
+ throw new Error('Missing async generator tagged-array bridge helper.');
688
+ }
689
+ fulfillTaggedArray(candidate, done, yieldedValue);
690
+ return;
691
+ }
692
+ if (yieldedValue.every((entry) => typeof entry === 'string')) {
693
+ if (typeof fulfillStringArray !== 'function') {
694
+ throw new Error('Missing async generator string-array bridge helper.');
695
+ }
696
+ fulfillStringArray(candidate, done, yieldedValue);
697
+ return;
698
+ }
699
+ if (yieldedValue.every((entry) => typeof entry === 'number')) {
700
+ if (typeof fulfillNumberArray !== 'function') {
701
+ throw new Error('Missing async generator number-array bridge helper.');
702
+ }
703
+ fulfillNumberArray(candidate, done, yieldedValue);
704
+ return;
705
+ }
706
+ if (yieldedValue.every((entry) => typeof entry === 'boolean')) {
707
+ if (typeof fulfillBooleanArray !== 'function') {
708
+ throw new Error('Missing async generator boolean-array bridge helper.');
709
+ }
710
+ fulfillBooleanArray(candidate, done, yieldedValue);
711
+ return;
712
+ }
713
+ if (typeof fulfillTaggedArray !== 'function') {
714
+ throw new Error('Missing async generator tagged-array bridge helper.');
715
+ }
716
+ fulfillTaggedArray(candidate, done, yieldedValue);
717
+ return;
718
+ }
719
+ fulfill(candidate, iteratorResult.done ? 1 : 0, yieldedValue);
720
+ }, (error) => {
721
+ reject(candidate, error);
722
+ });
723
+ },
724
+ },
725
+ soundscript_generator: {
726
+ wrap: (step) => {
727
+ if (typeof step !== 'function') {
728
+ throw new TypeError('Expected host-callable generator step.');
729
+ }
730
+ return {
731
+ next(value) {
732
+ return step(0, value);
733
+ },
734
+ return(value) {
735
+ return step(1, value);
736
+ },
737
+ throw(value) {
738
+ return step(2, value);
739
+ },
740
+ };
741
+ },
742
+ },
743
+ soundscript_throw: {
744
+ throw: (value) => {
745
+ throw value;
746
+ },
747
+ try_tagged: (callback) => {
748
+ if (typeof callback !== 'function') {
749
+ throw new TypeError('Expected JS function for soundscript_throw.try_tagged.');
750
+ }
751
+ try {
752
+ return { threw: false, value: callback() };
753
+ }
754
+ catch (error) {
755
+ return { threw: true, value: error };
756
+ }
757
+ },
758
+ try_result_threw: (result) => {
759
+ if ((typeof result !== 'object' && typeof result !== 'function') || result === null) {
760
+ throw new TypeError('Expected JS object for soundscript_throw.try_result_threw.');
761
+ }
762
+ return Number(Boolean(result.threw));
763
+ },
764
+ try_result_value: (result) => {
765
+ if ((typeof result !== 'object' && typeof result !== 'function') || result === null) {
766
+ throw new TypeError('Expected JS object for soundscript_throw.try_result_value.');
767
+ }
768
+ return result.value;
769
+ },
770
+ },
637
771
  };
638
772
  }
639
773
  export async function instantiateSoundscriptWasmModule(wasmSource, options = {}) {
@@ -727,6 +727,152 @@ function createJsHostImports(
727
727
  return created;
728
728
  },
729
729
  },
730
+ soundscript_async_generator: {
731
+ wrap: (step: unknown) => {
732
+ if (typeof step !== 'function') {
733
+ throw new TypeError('Expected host-callable async generator step.');
734
+ }
735
+ return {
736
+ next(value?: unknown) {
737
+ return Promise.resolve(step(0, value));
738
+ },
739
+ return(value?: unknown) {
740
+ return Promise.resolve(step(1, value));
741
+ },
742
+ throw(value?: unknown) {
743
+ return Promise.resolve(step(2, value));
744
+ },
745
+ };
746
+ },
747
+ step: (step: unknown, mode: number, value: unknown, candidate: unknown) => {
748
+ const instance = instanceCell.instance;
749
+ if (!instance) {
750
+ throw new Error('Async generator bridge invoked before instantiation completed.');
751
+ }
752
+ const fulfill = instance.exports.__soundscript_async_generator_step_bridge_fulfill;
753
+ const fulfillStringArray =
754
+ instance.exports.__soundscript_async_generator_step_bridge_fulfill_string_array;
755
+ const fulfillNumberArray =
756
+ instance.exports.__soundscript_async_generator_step_bridge_fulfill_number_array;
757
+ const fulfillBooleanArray =
758
+ instance.exports.__soundscript_async_generator_step_bridge_fulfill_boolean_array;
759
+ const fulfillTaggedArray =
760
+ instance.exports.__soundscript_async_generator_step_bridge_fulfill_tagged_array;
761
+ const reject = instance.exports.__soundscript_promise_bridge_reject;
762
+ if (typeof fulfill !== 'function' || typeof reject !== 'function') {
763
+ throw new Error('Missing async generator bridge helpers.');
764
+ }
765
+ Promise.resolve()
766
+ .then(() => {
767
+ if (typeof step !== 'function') {
768
+ throw new TypeError('Expected host-callable async generator step.');
769
+ }
770
+ return step(mode, value);
771
+ })
772
+ .then(
773
+ (result) => {
774
+ if ((typeof result !== 'object' && typeof result !== 'function') || result === null) {
775
+ throw new TypeError(
776
+ 'Expected async generator step to produce an iterator result object.',
777
+ );
778
+ }
779
+ const iteratorResult = result as Record<string, unknown>;
780
+ const yieldedValue = Object.hasOwn(iteratorResult, 'value')
781
+ ? iteratorResult.value
782
+ : undefined;
783
+ if (Array.isArray(yieldedValue)) {
784
+ const done = iteratorResult.done ? 1 : 0;
785
+ if (yieldedValue.length === 0) {
786
+ if (typeof fulfillTaggedArray !== 'function') {
787
+ throw new Error('Missing async generator tagged-array bridge helper.');
788
+ }
789
+ fulfillTaggedArray(candidate, done, yieldedValue);
790
+ return;
791
+ }
792
+ if (yieldedValue.every((entry) => typeof entry === 'string')) {
793
+ if (typeof fulfillStringArray !== 'function') {
794
+ throw new Error('Missing async generator string-array bridge helper.');
795
+ }
796
+ fulfillStringArray(candidate, done, yieldedValue);
797
+ return;
798
+ }
799
+ if (yieldedValue.every((entry) => typeof entry === 'number')) {
800
+ if (typeof fulfillNumberArray !== 'function') {
801
+ throw new Error('Missing async generator number-array bridge helper.');
802
+ }
803
+ fulfillNumberArray(candidate, done, yieldedValue);
804
+ return;
805
+ }
806
+ if (yieldedValue.every((entry) => typeof entry === 'boolean')) {
807
+ if (typeof fulfillBooleanArray !== 'function') {
808
+ throw new Error('Missing async generator boolean-array bridge helper.');
809
+ }
810
+ fulfillBooleanArray(candidate, done, yieldedValue);
811
+ return;
812
+ }
813
+ if (typeof fulfillTaggedArray !== 'function') {
814
+ throw new Error('Missing async generator tagged-array bridge helper.');
815
+ }
816
+ fulfillTaggedArray(candidate, done, yieldedValue);
817
+ return;
818
+ }
819
+ fulfill(
820
+ candidate,
821
+ iteratorResult.done ? 1 : 0,
822
+ yieldedValue,
823
+ );
824
+ },
825
+ (error) => {
826
+ reject(candidate, error);
827
+ },
828
+ );
829
+ },
830
+ },
831
+ soundscript_generator: {
832
+ wrap: (step: unknown) => {
833
+ if (typeof step !== 'function') {
834
+ throw new TypeError('Expected host-callable generator step.');
835
+ }
836
+ return {
837
+ next(value?: unknown) {
838
+ return step(0, value);
839
+ },
840
+ return(value?: unknown) {
841
+ return step(1, value);
842
+ },
843
+ throw(value?: unknown) {
844
+ return step(2, value);
845
+ },
846
+ };
847
+ },
848
+ },
849
+ soundscript_throw: {
850
+ throw: (value: unknown) => {
851
+ throw value;
852
+ },
853
+ try_tagged: (callback: unknown) => {
854
+ if (typeof callback !== 'function') {
855
+ throw new TypeError('Expected JS function for soundscript_throw.try_tagged.');
856
+ }
857
+ try {
858
+ return { threw: false, value: callback() };
859
+ } catch (error) {
860
+ return { threw: true, value: error };
861
+ }
862
+ },
863
+ try_result_threw: (result: unknown) => {
864
+ if ((typeof result !== 'object' && typeof result !== 'function') || result === null) {
865
+ throw new TypeError('Expected JS object for soundscript_throw.try_result_threw.');
866
+ }
867
+ return Number(Boolean((result as { threw?: unknown }).threw));
868
+ },
869
+ try_result_value: (result: unknown) => {
870
+ if ((typeof result !== 'object' && typeof result !== 'function') || result === null) {
871
+ throw new TypeError('Expected JS object for soundscript_throw.try_result_value.');
872
+ }
873
+ return (result as { value?: unknown }).value;
874
+ },
875
+ },
730
876
  };
731
877
  }
732
878
 
@@ -3016,7 +3016,10 @@ export function emitOwnedArrayBoundaryHelpers(module, options) {
3016
3016
  throw createUnsupportedHeapRuntimeBackendError('Owned string-array host boundaries require the owned string runtime.');
3017
3017
  }
3018
3018
  const taggedKindSets = usesTaggedParamBoundary || usesTaggedParamCopyBack || usesTaggedResultBoundary
3019
- ? collectTaggedArrayBoundaryKindSets(module, layoutsByRepresentationName)
3019
+ ? [
3020
+ ...collectTaggedArrayBoundaryKindSets(module, layoutsByRepresentationName),
3021
+ ...(options.extraTaggedKindSets ?? []),
3022
+ ]
3020
3023
  : [];
3021
3024
  return [
3022
3025
  ...(usesStringParamBoundary ? emitHostArrayToOwnedArrayHelper('string', indent) : []),
@@ -44,6 +44,7 @@ export interface OwnedArrayTypeUsage {
44
44
  export interface OwnedArrayBoundaryHelperOptions extends ArrayRuntimeImportUsage {
45
45
  indent(level: number): string;
46
46
  createUnsupportedHeapRuntimeBackendError(message: string): CompilerUnsupportedError;
47
+ extraTaggedKindSets?: readonly CompilerFunctionHostTaggedArrayBoundaryIR[];
47
48
  fallbackObjectWatTypeId?: string;
48
49
  layoutsByRepresentationName?: ReadonlyMap<string, {
49
50
  watTypeId: string;
@@ -3554,7 +3555,10 @@ export function emitOwnedArrayBoundaryHelpers(
3554
3555
  );
3555
3556
  }
3556
3557
  const taggedKindSets = usesTaggedParamBoundary || usesTaggedParamCopyBack || usesTaggedResultBoundary
3557
- ? collectTaggedArrayBoundaryKindSets(module, layoutsByRepresentationName)
3558
+ ? [
3559
+ ...collectTaggedArrayBoundaryKindSets(module, layoutsByRepresentationName),
3560
+ ...(options.extraTaggedKindSets ?? []),
3561
+ ]
3558
3562
  : [];
3559
3563
 
3560
3564
  return [