@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
@@ -9,7 +9,14 @@ import {
9
9
  type GeneratorCompilerCase = {
10
10
  name: string;
11
11
  source: string;
12
- expected: number;
12
+ expected?: number;
13
+ expectedThrow?: unknown;
14
+ run?: (
15
+ exported: unknown,
16
+ instance: WebAssembly.Instance,
17
+ tempDirectory: string,
18
+ ) => Promise<void> | void;
19
+ exportName?: string;
13
20
  };
14
21
 
15
22
  async function runGeneratorCompilerCase(testCase: GeneratorCompilerCase): Promise<void> {
@@ -19,11 +26,26 @@ async function runGeneratorCompilerCase(testCase: GeneratorCompilerCase): Promis
19
26
  assertEquals(result.exitCode, 0, testCase.name);
20
27
  assertEquals(result.diagnostics, [], testCase.name);
21
28
  const instance = await instantiateCompiledModuleInJs(tempDirectory);
22
- const exportName = await resolveQualifiedExportName(tempDirectory, 'main');
29
+ const exportName = await resolveQualifiedExportName(tempDirectory, testCase.exportName ?? 'main');
23
30
  const exported = instance.exports[exportName];
31
+ if (testCase.run) {
32
+ await testCase.run(exported, instance, tempDirectory);
33
+ return;
34
+ }
24
35
  if (typeof exported !== 'function') {
25
36
  throw new Error(`Expected exported function "${exportName}" for ${testCase.name}.`);
26
37
  }
38
+ if ('expectedThrow' in testCase) {
39
+ let threw = false;
40
+ try {
41
+ exported();
42
+ } catch (error) {
43
+ threw = true;
44
+ assertEquals(error, testCase.expectedThrow, testCase.name);
45
+ }
46
+ assertEquals(threw, true, `${testCase.name} should throw.`);
47
+ return;
48
+ }
27
49
  assertEquals(exported(), testCase.expected, testCase.name);
28
50
  }
29
51
 
@@ -35,7 +57,9 @@ async function main(): Promise<void> {
35
57
 
36
58
  const caseFilter = Deno.env.get('SOUNDSCRIPT_GENERATOR_CASE');
37
59
  const caseFiltersValue = Deno.env.get('SOUNDSCRIPT_GENERATOR_CASES');
38
- const caseFilters = caseFiltersValue ? new Set(JSON.parse(caseFiltersValue) as string[]) : undefined;
60
+ const caseFilters = caseFiltersValue
61
+ ? new Set(JSON.parse(caseFiltersValue) as string[])
62
+ : undefined;
39
63
  const selectedCases = cases.filter((testCase) =>
40
64
  caseFilter ? testCase.name === caseFilter : caseFilters ? caseFilters.has(testCase.name) : true
41
65
  );
@@ -63,8 +87,8 @@ const cases: GeneratorCompilerCase[] = [
63
87
  ' const firstValue = first.done ? 0 : first.value;',
64
88
  ' const secondValue = second.done ? 0 : second.value;',
65
89
  ' const thirdValue = third.done ? third.value : 0;',
66
- ' return (first.done ? 1000000 : 0) + (firstValue * 10000) + (second.done ? 1000 : 0)'
67
- + ' + (secondValue * 100) + (third.done ? 10 : 0) + thirdValue;',
90
+ ' return (first.done ? 1000000 : 0) + (firstValue * 10000) + (second.done ? 1000 : 0)' +
91
+ ' + (secondValue * 100) + (third.done ? 10 : 0) + thirdValue;',
68
92
  '}',
69
93
  '',
70
94
  ].join('\n'),
@@ -85,14 +109,531 @@ const cases: GeneratorCompilerCase[] = [
85
109
  ' const third = iterator.next();',
86
110
  ' const firstValue = first.done ? 0 : first.value;',
87
111
  ' const secondValue = second.done ? second.value : 0;',
88
- ' return (firstValue * 10000)'
89
- + ' + (first.done ? 1000 : 0)'
90
- + ' + (secondValue * 100)'
91
- + ' + (second.done ? 10 : 0)'
92
- + ' + (third.done ? 1 : 0);',
112
+ ' return (firstValue * 10000)' +
113
+ ' + (first.done ? 1000 : 0)' +
114
+ ' + (secondValue * 100)' +
115
+ ' + (second.done ? 10 : 0)' +
116
+ ' + (third.done ? 1 : 0);',
117
+ '}',
118
+ '',
119
+ ].join('\n'),
120
+ },
121
+ {
122
+ name: 'compileProject exports sync generator functions as host iterators',
123
+ exportName: 'iterate',
124
+ source: [
125
+ 'export function* iterate(): Generator<number, number, unknown> {',
126
+ ' yield 3;',
127
+ ' yield 5;',
128
+ ' return 7;',
129
+ '}',
130
+ '',
131
+ ].join('\n'),
132
+ run: (exported) => {
133
+ if (typeof exported !== 'function') {
134
+ throw new Error('Expected exported sync generator function.');
135
+ }
136
+ const iterator = exported();
137
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
138
+ throw new Error('Expected exported sync generator to return an iterator object.');
139
+ }
140
+ const next = (iterator as { next?: unknown }).next;
141
+ if (typeof next !== 'function') {
142
+ throw new Error('Expected exported sync generator iterator to expose next().');
143
+ }
144
+ const first = (iterator as Iterator<number, number, unknown>).next();
145
+ const second = (iterator as Iterator<number, number, unknown>).next();
146
+ const third = (iterator as Iterator<number, number, unknown>).next();
147
+ assertEquals(first, { value: 3, done: false });
148
+ assertEquals(second, { value: 5, done: false });
149
+ assertEquals(third, { value: 7, done: true });
150
+ },
151
+ },
152
+ {
153
+ name: 'compileProject exports sync generator yielded Promises as host Promises',
154
+ exportName: 'iterate',
155
+ source: [
156
+ 'export function* iterate(): Generator<Promise<number>, void, unknown> {',
157
+ ' yield Promise.resolve(3);',
158
+ '}',
159
+ '',
160
+ ].join('\n'),
161
+ run: async (exported) => {
162
+ if (typeof exported !== 'function') {
163
+ throw new Error('Expected exported sync generator function.');
164
+ }
165
+ const iterator = exported();
166
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
167
+ throw new Error('Expected exported sync generator to return an iterator object.');
168
+ }
169
+ const first = (iterator as Iterator<Promise<number>, void, unknown>).next();
170
+ if (!(first.value instanceof Promise)) {
171
+ throw new Error('Expected yielded sync generator Promise to bridge to a host Promise.');
172
+ }
173
+ const second = (iterator as Iterator<Promise<number>, void, unknown>).next();
174
+ assertEquals(await first.value, 3);
175
+ assertEquals(first.done, false);
176
+ assertEquals(second, { value: undefined, done: true });
177
+ },
178
+ },
179
+ {
180
+ name: 'compileProject exports sync generator return calls as host iterators',
181
+ exportName: 'iterate',
182
+ source: [
183
+ 'export function* iterate(): Generator<number, number, unknown> {',
184
+ ' yield 3;',
185
+ ' yield 5;',
186
+ ' return 7;',
187
+ '}',
188
+ '',
189
+ ].join('\n'),
190
+ run: (exported) => {
191
+ if (typeof exported !== 'function') {
192
+ throw new Error('Expected exported sync generator function.');
193
+ }
194
+ const iterator = exported();
195
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
196
+ throw new Error('Expected exported sync generator to return an iterator object.');
197
+ }
198
+ const first = (iterator as Iterator<number, number, unknown>).next();
199
+ const second = (iterator as Iterator<number, number, unknown>).return?.(20);
200
+ const third = (iterator as Iterator<number, number, unknown>).next();
201
+ assertEquals(first, { value: 3, done: false });
202
+ assertEquals(second, { value: 20, done: true });
203
+ assertEquals(third, { value: undefined, done: true });
204
+ },
205
+ },
206
+ {
207
+ name: 'compileProject exports sync generator throw calls as host iterators',
208
+ exportName: 'iterate',
209
+ source: [
210
+ 'export function* iterate(): Generator<number, number, number> {',
211
+ ' try {',
212
+ ' yield 3;',
213
+ ' return 8;',
214
+ ' } catch {',
215
+ ' yield 7;',
216
+ ' return 9;',
217
+ ' }',
218
+ '}',
219
+ '',
220
+ ].join('\n'),
221
+ run: (exported) => {
222
+ if (typeof exported !== 'function') {
223
+ throw new Error('Expected exported sync generator function.');
224
+ }
225
+ const iterator = exported();
226
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
227
+ throw new Error('Expected exported sync generator to return an iterator object.');
228
+ }
229
+ const first = (iterator as Iterator<number, number, number>).next();
230
+ const second = (iterator as Iterator<number, number, number>).throw?.(5);
231
+ const third = (iterator as Iterator<number, number, number>).next();
232
+ assertEquals(first, { value: 3, done: false });
233
+ assertEquals(second, { value: 7, done: false });
234
+ assertEquals(third, { value: 9, done: true });
235
+ },
236
+ },
237
+ {
238
+ name: 'compileProject exports sync generator yield star over local arrays as host iterators',
239
+ exportName: 'iterate',
240
+ source: [
241
+ 'export function* iterate(): Generator<number, number, unknown> {',
242
+ ' const values = [3, 5, 7];',
243
+ ' yield* values;',
244
+ ' return 9;',
245
+ '}',
246
+ '',
247
+ ].join('\n'),
248
+ run: (exported) => {
249
+ if (typeof exported !== 'function') {
250
+ throw new Error('Expected exported sync generator function.');
251
+ }
252
+ const iterator = exported();
253
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
254
+ throw new Error('Expected exported sync generator to return an iterator object.');
255
+ }
256
+ const first = (iterator as Iterator<number, number, unknown>).next();
257
+ const second = (iterator as Iterator<number, number, unknown>).next();
258
+ const third = (iterator as Iterator<number, number, unknown>).next();
259
+ const fourth = (iterator as Iterator<number, number, unknown>).next();
260
+ assertEquals(first, { value: 3, done: false });
261
+ assertEquals(second, { value: 5, done: false });
262
+ assertEquals(third, { value: 7, done: false });
263
+ assertEquals(fourth, { value: 9, done: true });
264
+ },
265
+ },
266
+ {
267
+ name: 'compileProject exports sync generator yield star over local Promise arrays as host iterators',
268
+ exportName: 'iterate',
269
+ source: [
270
+ 'export function* iterate(): Generator<Promise<number>, number, unknown> {',
271
+ ' const values = [Promise.resolve(3), Promise.resolve(5), Promise.resolve(7)];',
272
+ ' yield* values;',
273
+ ' return 9;',
274
+ '}',
275
+ '',
276
+ ].join('\n'),
277
+ run: async (exported) => {
278
+ if (typeof exported !== 'function') {
279
+ throw new Error('Expected exported sync generator function.');
280
+ }
281
+ const iterator = exported();
282
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
283
+ throw new Error('Expected exported sync generator to return an iterator object.');
284
+ }
285
+ const first = (iterator as Iterator<Promise<number>, number, unknown>).next();
286
+ const second = (iterator as Iterator<Promise<number>, number, unknown>).next();
287
+ const third = (iterator as Iterator<Promise<number>, number, unknown>).next();
288
+ const fourth = (iterator as Iterator<Promise<number>, number, unknown>).next();
289
+ if (!(first.value instanceof Promise) || !(second.value instanceof Promise) ||
290
+ !(third.value instanceof Promise)) {
291
+ throw new Error('Expected yielded Promise-array values to bridge to host Promises.');
292
+ }
293
+ assertEquals(await first.value, 3);
294
+ assertEquals(await second.value, 5);
295
+ assertEquals(await third.value, 7);
296
+ assertEquals(fourth, { value: 9, done: true });
297
+ },
298
+ },
299
+ {
300
+ name: 'compileProject exports sync generator throw through array yield star as host iterators',
301
+ exportName: 'iterate',
302
+ source: [
303
+ 'export function* iterate(): Generator<number, number, unknown> {',
304
+ ' yield* [3, 5, 7];',
305
+ ' return 9;',
306
+ '}',
307
+ '',
308
+ ].join('\n'),
309
+ run: (exported) => {
310
+ if (typeof exported !== 'function') {
311
+ throw new Error('Expected exported sync generator function.');
312
+ }
313
+ const iterator = exported();
314
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
315
+ throw new Error('Expected exported sync generator to return an iterator object.');
316
+ }
317
+ const first = (iterator as Iterator<number, number, unknown>).next();
318
+ assertEquals(first, { value: 3, done: false });
319
+ let threw = false;
320
+ try {
321
+ (iterator as Iterator<number, number, unknown>).throw?.(5);
322
+ } catch (error) {
323
+ threw = true;
324
+ assertEquals(error, {
325
+ name: 'TypeError',
326
+ message: 'yield* delegate does not support throw',
327
+ });
328
+ }
329
+ assertEquals(threw, true, 'Expected exported array yield* throw to rethrow to host.');
330
+ },
331
+ },
332
+ {
333
+ name: 'compileProject exports sync generator yield star over iterator-valued locals as host iterators',
334
+ exportName: 'iterate',
335
+ source: [
336
+ 'export function* iterate(): Generator<number, number, unknown> {',
337
+ ' const values = new Map([["a", 3], ["b", 5], ["c", 7]]).values();',
338
+ ' yield* values;',
339
+ ' return 9;',
340
+ '}',
341
+ '',
342
+ ].join('\n'),
343
+ run: (exported) => {
344
+ if (typeof exported !== 'function') {
345
+ throw new Error('Expected exported sync generator function.');
346
+ }
347
+ const iterator = exported();
348
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
349
+ throw new Error('Expected exported sync generator to return an iterator object.');
350
+ }
351
+ const first = (iterator as Iterator<number, number, unknown>).next();
352
+ const second = (iterator as Iterator<number, number, unknown>).next();
353
+ const third = (iterator as Iterator<number, number, unknown>).next();
354
+ const fourth = (iterator as Iterator<number, number, unknown>).next();
355
+ assertEquals(first, { value: 3, done: false });
356
+ assertEquals(second, { value: 5, done: false });
357
+ assertEquals(third, { value: 7, done: false });
358
+ assertEquals(fourth, { value: 9, done: true });
359
+ },
360
+ },
361
+ {
362
+ name: 'compileProject exports sync generator yield star through sync generator delegates as host iterators',
363
+ exportName: 'outer',
364
+ source: [
365
+ 'function* inner(): Generator<number, number, number> {',
366
+ ' const received = yield 10;',
367
+ ' yield received + 1;',
368
+ ' return received + 2;',
369
+ '}',
370
+ '',
371
+ 'export function* outer(): Generator<number, number, number> {',
372
+ ' const delegated = yield* inner();',
373
+ ' yield delegated + 3;',
374
+ ' return delegated + 4;',
375
+ '}',
376
+ '',
377
+ ].join('\n'),
378
+ run: (exported) => {
379
+ if (typeof exported !== 'function') {
380
+ throw new Error('Expected exported sync generator function.');
381
+ }
382
+ const iterator = exported();
383
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
384
+ throw new Error('Expected exported sync generator to return an iterator object.');
385
+ }
386
+ const first = (iterator as Iterator<number, number, number>).next();
387
+ const second = (iterator as Iterator<number, number, number>).next(7);
388
+ const third = (iterator as Iterator<number, number, number>).next();
389
+ const fourth = (iterator as Iterator<number, number, number>).next();
390
+ assertEquals(first, { value: 10, done: false });
391
+ assertEquals(second, { value: 8, done: false });
392
+ assertEquals(third, { value: 12, done: false });
393
+ assertEquals(fourth, { value: 13, done: true });
394
+ },
395
+ },
396
+ {
397
+ name: 'compileProject exports sync generator throw delegation through yield star as host iterators',
398
+ exportName: 'outer',
399
+ source: [
400
+ 'function* inner(): Generator<number, number, number> {',
401
+ ' try {',
402
+ ' yield 3;',
403
+ ' return 8;',
404
+ ' } catch {',
405
+ ' yield 7;',
406
+ ' return 9;',
407
+ ' }',
408
+ '}',
409
+ '',
410
+ 'export function* outer(): Generator<number, number, number> {',
411
+ ' const delegated = yield* inner();',
412
+ ' yield delegated + 1;',
413
+ ' return delegated + 2;',
414
+ '}',
415
+ '',
416
+ ].join('\n'),
417
+ run: (exported) => {
418
+ if (typeof exported !== 'function') {
419
+ throw new Error('Expected exported sync generator function.');
420
+ }
421
+ const iterator = exported();
422
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
423
+ throw new Error('Expected exported sync generator to return an iterator object.');
424
+ }
425
+ const first = (iterator as Iterator<number, number, number>).next();
426
+ const second = (iterator as Iterator<number, number, number>).throw?.(5);
427
+ const third = (iterator as Iterator<number, number, number>).next();
428
+ const fourth = (iterator as Iterator<number, number, number>).next();
429
+ assertEquals(first, { value: 3, done: false });
430
+ assertEquals(second, { value: 7, done: false });
431
+ assertEquals(third, { value: 10, done: false });
432
+ assertEquals(fourth, { value: 11, done: true });
433
+ },
434
+ },
435
+ {
436
+ name: 'compileProject exports sync generator return delegation through yield star as host iterators',
437
+ exportName: 'outer',
438
+ source: [
439
+ 'function* inner(): Generator<number, number, number> {',
440
+ ' try {',
441
+ ' yield 3;',
442
+ ' yield 5;',
443
+ ' return 7;',
444
+ ' } finally {',
445
+ ' yield 9;',
446
+ ' }',
447
+ '}',
448
+ '',
449
+ 'export function* outer(): Generator<number, number, number> {',
450
+ ' const delegated = yield* inner();',
451
+ ' return delegated + 1;',
452
+ '}',
453
+ '',
454
+ ].join('\n'),
455
+ run: (exported) => {
456
+ if (typeof exported !== 'function') {
457
+ throw new Error('Expected exported sync generator function.');
458
+ }
459
+ const iterator = exported();
460
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
461
+ throw new Error('Expected exported sync generator to return an iterator object.');
462
+ }
463
+ const first = (iterator as Iterator<number, number, number>).next();
464
+ const second = (iterator as Iterator<number, number, number>).return?.(4);
465
+ const third = (iterator as Iterator<number, number, number>).next();
466
+ const fourth = (iterator as Iterator<number, number, number>).next();
467
+ assertEquals(first, { value: 3, done: false });
468
+ assertEquals(second, { value: 9, done: false });
469
+ assertEquals(third, { value: 5, done: true });
470
+ assertEquals(fourth, { value: undefined, done: true });
471
+ },
472
+ },
473
+ {
474
+ name: 'compileProject exports sync generator yielded delegated Promises as host Promises',
475
+ exportName: 'outer',
476
+ source: [
477
+ 'function* inner(): Generator<Promise<number>, number, unknown> {',
478
+ ' const values = [Promise.resolve(3), Promise.resolve(5)];',
479
+ ' yield* values;',
480
+ ' return 7;',
481
+ '}',
482
+ '',
483
+ 'export function* outer(): Generator<Promise<number>, number, unknown> {',
484
+ ' const delegated = yield* inner();',
485
+ ' return delegated + 1;',
486
+ '}',
487
+ '',
488
+ ].join('\n'),
489
+ run: async (exported) => {
490
+ if (typeof exported !== 'function') {
491
+ throw new Error('Expected exported sync generator function.');
492
+ }
493
+ const iterator = exported();
494
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
495
+ throw new Error('Expected exported sync generator to return an iterator object.');
496
+ }
497
+ const first = (iterator as Iterator<Promise<number>, number, unknown>).next();
498
+ const second = (iterator as Iterator<Promise<number>, number, unknown>).next();
499
+ const third = (iterator as Iterator<Promise<number>, number, unknown>).next();
500
+ if (!(first.value instanceof Promise) || !(second.value instanceof Promise)) {
501
+ throw new Error('Expected delegated yielded Promise values to bridge to host Promises.');
502
+ }
503
+ assertEquals(await first.value, 3);
504
+ assertEquals(await second.value, 5);
505
+ assertEquals(third, { value: 8, done: true });
506
+ },
507
+ },
508
+ {
509
+ name: 'compileProject exports sync generator class methods as host iterators',
510
+ exportName: 'iterateFromCounter',
511
+ source: [
512
+ 'class Counter {',
513
+ ' base = 4;',
514
+ '',
515
+ ' *iterate(): Generator<number, number, unknown> {',
516
+ ' yield this.base;',
517
+ ' yield this.base + 1;',
518
+ ' return this.base + 2;',
519
+ ' }',
520
+ '}',
521
+ '',
522
+ 'export function iterateFromCounter(): Generator<number, number, unknown> {',
523
+ ' return new Counter().iterate();',
524
+ '}',
525
+ '',
526
+ ].join('\n'),
527
+ run: (exported) => {
528
+ if (typeof exported !== 'function') {
529
+ throw new Error('Expected exported generator wrapper function.');
530
+ }
531
+ const iterator = (exported as () => Iterator<number, number, unknown>)();
532
+ const first = iterator.next();
533
+ const second = iterator.next();
534
+ const third = iterator.next();
535
+ assertEquals(first, { value: 4, done: false });
536
+ assertEquals(second, { value: 5, done: false });
537
+ assertEquals(third, { value: 6, done: true });
538
+ },
539
+ },
540
+ {
541
+ name: 'compileProject exports sync generator class methods with super calls as host iterators',
542
+ exportName: 'iterateFromCounter',
543
+ source: [
544
+ 'class Base {',
545
+ ' bump(value: number): number {',
546
+ ' return value + 1;',
547
+ ' }',
548
+ '}',
549
+ '',
550
+ 'class Counter extends Base {',
551
+ ' base = 4;',
552
+ '',
553
+ ' *iterate(): Generator<number, number, unknown> {',
554
+ ' yield super.bump(this.base);',
555
+ ' yield super.bump(this.base + 1);',
556
+ ' return super.bump(this.base + 2);',
557
+ ' }',
558
+ '}',
559
+ '',
560
+ 'export function iterateFromCounter(): Generator<number, number, unknown> {',
561
+ ' return new Counter().iterate();',
562
+ '}',
563
+ '',
564
+ ].join('\n'),
565
+ run: (exported) => {
566
+ if (typeof exported !== 'function') {
567
+ throw new Error('Expected exported generator wrapper function.');
568
+ }
569
+ const iterator = (exported as () => Iterator<number, number, unknown>)();
570
+ const first = iterator.next();
571
+ const second = iterator.next();
572
+ const third = iterator.next();
573
+ assertEquals(first, { value: 5, done: false });
574
+ assertEquals(second, { value: 6, done: false });
575
+ assertEquals(third, { value: 7, done: true });
576
+ },
577
+ },
578
+ {
579
+ name: 'compileProject exports uncaught sync generator builtin Error throws to host iterators',
580
+ exportName: 'iterate',
581
+ source: [
582
+ 'export function* iterate(): Generator<number, number, unknown> {',
583
+ ' yield 3;',
584
+ ' throw new Error("boom");',
585
+ '}',
586
+ '',
587
+ ].join('\n'),
588
+ run: (exported) => {
589
+ if (typeof exported !== 'function') {
590
+ throw new Error('Expected exported sync generator function.');
591
+ }
592
+ const iterator = exported();
593
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
594
+ throw new Error('Expected exported sync generator to return an iterator object.');
595
+ }
596
+ const first = (iterator as Iterator<number, number, unknown>).next();
597
+ assertEquals(first, { value: 3, done: false });
598
+ let threw = false;
599
+ try {
600
+ (iterator as Iterator<number, number, unknown>).next();
601
+ } catch (error) {
602
+ threw = true;
603
+ assertEquals(error, { name: 'Error', message: 'boom' });
604
+ }
605
+ assertEquals(threw, true, 'Expected exported sync generator next() to rethrow Error.');
606
+ },
607
+ },
608
+ {
609
+ name: 'compileProject exports uncaught sync generator throw Error calls to host iterators',
610
+ exportName: 'iterate',
611
+ source: [
612
+ 'export function* iterate(): Generator<number, number, number> {',
613
+ ' yield 3;',
614
+ ' return 8;',
93
615
  '}',
94
616
  '',
95
617
  ].join('\n'),
618
+ run: (exported) => {
619
+ if (typeof exported !== 'function') {
620
+ throw new Error('Expected exported sync generator function.');
621
+ }
622
+ const iterator = exported();
623
+ if ((typeof iterator !== 'object' && typeof iterator !== 'function') || iterator === null) {
624
+ throw new Error('Expected exported sync generator to return an iterator object.');
625
+ }
626
+ const first = (iterator as Iterator<number, number, number>).next();
627
+ assertEquals(first, { value: 3, done: false });
628
+ let threw = false;
629
+ try {
630
+ (iterator as Iterator<number, number, number>).throw?.(new Error('boom'));
631
+ } catch (error) {
632
+ threw = true;
633
+ assertEquals(error, { name: 'Error', message: 'boom' });
634
+ }
635
+ assertEquals(threw, true, 'Expected exported sync generator throw(Error) to rethrow.');
636
+ },
96
637
  },
97
638
  {
98
639
  name: 'compileProject lowers for-of over sync generator results',
@@ -133,21 +674,210 @@ const cases: GeneratorCompilerCase[] = [
133
674
  ' const second = iterator.next();',
134
675
  ' const third = iterator.next();',
135
676
  ' const fourth = iterator.next();',
136
- ' return (first.done ? 100000 : 0) + ((first.done ? 0 : first.value) * 10000)'
137
- + ' + (second.done ? 1000 : 0) + ((second.done ? 0 : second.value) * 100)'
138
- + ' + ((third.done ? 0 : third.value) * 10) + (fourth.done ? fourth.value : 0);',
677
+ ' return (first.done ? 100000 : 0) + ((first.done ? 0 : first.value) * 10000)' +
678
+ ' + (second.done ? 1000 : 0) + ((second.done ? 0 : second.value) * 100)' +
679
+ ' + ((third.done ? 0 : third.value) * 10) + (fourth.done ? fourth.value : 0);',
139
680
  '}',
140
681
  '',
141
682
  ].join('\n'),
142
683
  },
143
684
  {
144
- name: 'compileProject lowers sync generator for bodies',
145
- expected: 4568,
685
+ name: 'compileProject lowers sync generator do while bodies',
686
+ expected: 456,
146
687
  source: [
147
688
  'function* iterate(): Generator<number, number, unknown> {',
148
689
  ' let index = 0;',
149
- ' for (; index < 3; index = index + 1) {',
690
+ ' do {',
150
691
  ' yield index + 4;',
692
+ ' index = index + 1;',
693
+ ' } while (index < 2);',
694
+ ' return index + 4;',
695
+ '}',
696
+ '',
697
+ 'export function main(): number {',
698
+ ' const iterator = iterate();',
699
+ ' const first = iterator.next();',
700
+ ' const second = iterator.next();',
701
+ ' const third = iterator.next();',
702
+ ' return ((first.done ? 0 : first.value) * 100)' +
703
+ ' + ((second.done ? 0 : second.value) * 10)' +
704
+ ' + (third.done ? third.value : 0);',
705
+ '}',
706
+ '',
707
+ ].join('\n'),
708
+ },
709
+ {
710
+ name: 'compileProject preserves outer locals across sync generator block scoped yields',
711
+ expected: 122,
712
+ source: [
713
+ 'function* iterate(): Generator<number, number, number> {',
714
+ ' let total = 20;',
715
+ ' {',
716
+ ' let total = 0;',
717
+ ' total = yield 1;',
718
+ ' }',
719
+ ' return total + 2;',
720
+ '}',
721
+ '',
722
+ 'export function main(): number {',
723
+ ' const iterator = iterate();',
724
+ ' const first = iterator.next();',
725
+ ' const second = iterator.next(5);',
726
+ ' return ((first.done ? 0 : first.value) * 100) + (second.done ? second.value : 0);',
727
+ '}',
728
+ '',
729
+ ].join('\n'),
730
+ },
731
+ {
732
+ name: 'compileProject preserves sync generator do while continue and break through finally',
733
+ expected: 357621,
734
+ source: [
735
+ 'function* iterate(): Generator<number, number, unknown> {',
736
+ ' let index = 0;',
737
+ ' let total = 0;',
738
+ ' do {',
739
+ ' try {',
740
+ ' index = index + 1;',
741
+ ' if (index === 1) {',
742
+ ' yield 3;',
743
+ ' continue;',
744
+ ' }',
745
+ ' yield 7;',
746
+ ' break;',
747
+ ' } finally {',
748
+ ' total = total + 5;',
749
+ ' yield 5;',
750
+ ' }',
751
+ ' } while (index < 3);',
752
+ ' return total + index;',
753
+ '}',
754
+ '',
755
+ 'export function main(): number {',
756
+ ' const iterator = iterate();',
757
+ ' const first = iterator.next();',
758
+ ' const second = iterator.next();',
759
+ ' const third = iterator.next();',
760
+ ' const fourth = iterator.next();',
761
+ ' const fifth = iterator.next();',
762
+ ' return ((first.done ? 0 : first.value) * 100000)' +
763
+ ' + ((second.done ? 0 : second.value) * 10000)' +
764
+ ' + ((third.done ? 0 : third.value) * 1000)' +
765
+ ' + ((fourth.done ? 0 : fourth.value) * 100)' +
766
+ ' + ((fifth.done ? fifth.value : 0) * 10)' +
767
+ ' + (fifth.done ? 1 : 0);',
768
+ '}',
769
+ '',
770
+ ].join('\n'),
771
+ },
772
+ {
773
+ name: 'compileProject lowers sync generator for of loops over Sets',
774
+ expected: 359,
775
+ source: [
776
+ 'function* iterate(): Generator<number, number, unknown> {',
777
+ ' for (const value of new Set([2, 4])) {',
778
+ ' yield value + 1;',
779
+ ' }',
780
+ ' return 9;',
781
+ '}',
782
+ '',
783
+ 'export function main(): number {',
784
+ ' const iterator = iterate();',
785
+ ' const first = iterator.next();',
786
+ ' const second = iterator.next();',
787
+ ' const third = iterator.next();',
788
+ ' return ((first.done ? 0 : first.value) * 100)' +
789
+ ' + ((second.done ? 0 : second.value) * 10)' +
790
+ ' + (third.done ? third.value : 0);',
791
+ '}',
792
+ '',
793
+ ].join('\n'),
794
+ },
795
+ {
796
+ name: 'compileProject lowers sync generator for of loops over sync generator results',
797
+ expected: 358,
798
+ source: [
799
+ 'function* values(): Generator<number, number, unknown> {',
800
+ ' yield 2;',
801
+ ' yield 4;',
802
+ ' return 9;',
803
+ '}',
804
+ '',
805
+ 'function* iterate(): Generator<number, number, unknown> {',
806
+ ' for (const value of values()) {',
807
+ ' yield value + 1;',
808
+ ' }',
809
+ ' return 8;',
810
+ '}',
811
+ '',
812
+ 'export function main(): number {',
813
+ ' const iterator = iterate();',
814
+ ' const first = iterator.next();',
815
+ ' const second = iterator.next();',
816
+ ' const third = iterator.next();',
817
+ ' return ((first.done ? 0 : first.value) * 100)' +
818
+ ' + ((second.done ? 0 : second.value) * 10)' +
819
+ ' + (third.done ? third.value : 0);',
820
+ '}',
821
+ '',
822
+ ].join('\n'),
823
+ },
824
+ {
825
+ name: 'compileProject lowers sync generator for of loops over Map values iterators',
826
+ expected: 358,
827
+ source: [
828
+ 'function* iterate(): Generator<number, number, unknown> {',
829
+ " for (const value of new Map<string, number>([['a', 2], ['b', 4]]).values()) {",
830
+ ' yield value + 1;',
831
+ ' }',
832
+ ' return 8;',
833
+ '}',
834
+ '',
835
+ 'export function main(): number {',
836
+ ' const iterator = iterate();',
837
+ ' const first = iterator.next();',
838
+ ' const second = iterator.next();',
839
+ ' const third = iterator.next();',
840
+ ' return ((first.done ? 0 : first.value) * 100)' +
841
+ ' + ((second.done ? 0 : second.value) * 10)' +
842
+ ' + (third.done ? third.value : 0);',
843
+ '}',
844
+ '',
845
+ ].join('\n'),
846
+ },
847
+ {
848
+ name: 'compileProject lowers sync generator for of loops over iterator-valued locals',
849
+ expected: 1238,
850
+ source: [
851
+ 'function* iterate(): Generator<number, number, unknown> {',
852
+ ' const values = new Map([["a", 1], ["b", 2], ["c", 3]]).values();',
853
+ ' for (const value of values) {',
854
+ ' yield value;',
855
+ ' }',
856
+ ' return 8;',
857
+ '}',
858
+ '',
859
+ 'export function main(): number {',
860
+ ' const iterator = iterate();',
861
+ ' const first = iterator.next();',
862
+ ' const second = iterator.next();',
863
+ ' const third = iterator.next();',
864
+ ' const fourth = iterator.next();',
865
+ ' return ((first.done ? 0 : first.value) * 1000)' +
866
+ ' + ((second.done ? 0 : second.value) * 100)' +
867
+ ' + ((third.done ? 0 : third.value) * 10)' +
868
+ ' + (fourth.done ? fourth.value : 0);',
869
+ '}',
870
+ '',
871
+ ].join('\n'),
872
+ },
873
+ {
874
+ name: 'compileProject lowers sync generator for of loops over owned number array locals',
875
+ expected: 2468,
876
+ source: [
877
+ 'function* iterate(): Generator<number, number, unknown> {',
878
+ ' const values = [2, 4, 6];',
879
+ ' for (const value of values) {',
880
+ ' yield value;',
151
881
  ' }',
152
882
  ' return 8;',
153
883
  '}',
@@ -158,10 +888,1403 @@ const cases: GeneratorCompilerCase[] = [
158
888
  ' const second = iterator.next();',
159
889
  ' const third = iterator.next();',
160
890
  ' const fourth = iterator.next();',
161
- ' return ((first.done ? 0 : first.value) * 1000)'
162
- + ' + ((second.done ? 0 : second.value) * 100)'
163
- + ' + ((third.done ? 0 : third.value) * 10)'
164
- + ' + (fourth.done ? fourth.value : 0);',
891
+ ' return ((first.done ? 0 : first.value) * 1000)' +
892
+ ' + ((second.done ? 0 : second.value) * 100)' +
893
+ ' + ((third.done ? 0 : third.value) * 10)' +
894
+ ' + (fourth.done ? fourth.value : 0);',
895
+ '}',
896
+ '',
897
+ ].join('\n'),
898
+ },
899
+ {
900
+ name: 'compileProject lowers sync generator for of loops over owned string array locals',
901
+ expected: 128,
902
+ source: [
903
+ 'function* iterate(): Generator<number, number, unknown> {',
904
+ " const values = ['a', 'b'];",
905
+ ' for (const value of values) {',
906
+ " yield value === 'a' ? 1 : 2;",
907
+ ' }',
908
+ ' return 8;',
909
+ '}',
910
+ '',
911
+ 'export function main(): number {',
912
+ ' const iterator = iterate();',
913
+ ' const first = iterator.next();',
914
+ ' const second = iterator.next();',
915
+ ' const third = iterator.next();',
916
+ ' return ((first.done ? 0 : first.value) * 100)' +
917
+ ' + ((second.done ? 0 : second.value) * 10)' +
918
+ ' + (third.done ? third.value : 0);',
919
+ '}',
920
+ '',
921
+ ].join('\n'),
922
+ },
923
+ {
924
+ name: 'compileProject lowers sync generator for of loops over owned boolean array locals',
925
+ expected: 1018,
926
+ source: [
927
+ 'function* iterate(): Generator<number, number, unknown> {',
928
+ ' const values = [true, false, true];',
929
+ ' for (const value of values) {',
930
+ ' yield value ? 1 : 0;',
931
+ ' }',
932
+ ' return 8;',
933
+ '}',
934
+ '',
935
+ 'export function main(): number {',
936
+ ' const iterator = iterate();',
937
+ ' const first = iterator.next();',
938
+ ' const second = iterator.next();',
939
+ ' const third = iterator.next();',
940
+ ' const fourth = iterator.next();',
941
+ ' return ((first.done ? 0 : first.value) * 1000)' +
942
+ ' + ((second.done ? 0 : second.value) * 100)' +
943
+ ' + ((third.done ? 0 : third.value) * 10)' +
944
+ ' + (fourth.done ? fourth.value : 0);',
945
+ '}',
946
+ '',
947
+ ].join('\n'),
948
+ },
949
+ {
950
+ name: 'compileProject lowers sync generator for of loops over owned tagged array locals',
951
+ expected: 2948,
952
+ source: [
953
+ 'function* iterate(): Generator<number, number, unknown> {',
954
+ " const values: Array<number | string> = [2, 'a', 4];",
955
+ ' for (const value of values) {',
956
+ " yield typeof value === 'number' ? value : 9;",
957
+ ' }',
958
+ ' return 8;',
959
+ '}',
960
+ '',
961
+ 'export function main(): number {',
962
+ ' const iterator = iterate();',
963
+ ' const first = iterator.next();',
964
+ ' const second = iterator.next();',
965
+ ' const third = iterator.next();',
966
+ ' const fourth = iterator.next();',
967
+ ' return ((first.done ? 0 : first.value) * 1000)' +
968
+ ' + ((second.done ? 0 : second.value) * 100)' +
969
+ ' + ((third.done ? 0 : third.value) * 10)' +
970
+ ' + (fourth.done ? fourth.value : 0);',
971
+ '}',
972
+ '',
973
+ ].join('\n'),
974
+ },
975
+ {
976
+ name: 'compileProject lowers sync generator for in loops over ordinary objects',
977
+ expected: 12119,
978
+ source: [
979
+ 'function* iterate(): Generator<number, number, unknown> {',
980
+ ' const pair = { left: 1, right: 2, stop: 3 };',
981
+ ' for (const key in pair) {',
982
+ ' try {',
983
+ ' if (key === "left") {',
984
+ ' continue;',
985
+ ' }',
986
+ ' if (key === "stop") {',
987
+ ' break;',
988
+ ' }',
989
+ ' yield key === "right" ? 2 : 9;',
990
+ ' } finally {',
991
+ ' yield 1;',
992
+ ' }',
993
+ ' }',
994
+ ' return 9;',
995
+ '}',
996
+ '',
997
+ 'export function main(): number {',
998
+ ' const iterator = iterate();',
999
+ ' const first = iterator.next();',
1000
+ ' const second = iterator.next();',
1001
+ ' const third = iterator.next();',
1002
+ ' const fourth = iterator.next();',
1003
+ ' const fifth = iterator.next();',
1004
+ ' return ((first.done ? 0 : first.value) * 10000)' +
1005
+ ' + ((second.done ? 0 : second.value) * 1000)' +
1006
+ ' + ((third.done ? 0 : third.value) * 100)' +
1007
+ ' + ((fourth.done ? 0 : fourth.value) * 10)' +
1008
+ ' + (fifth.done ? fifth.value : 0);',
1009
+ '}',
1010
+ '',
1011
+ ].join('\n'),
1012
+ },
1013
+ {
1014
+ name: 'compileProject lowers sync generator for bodies',
1015
+ expected: 4568,
1016
+ source: [
1017
+ 'function* iterate(): Generator<number, number, unknown> {',
1018
+ ' let index = 0;',
1019
+ ' for (; index < 3; index = index + 1) {',
1020
+ ' yield index + 4;',
1021
+ ' }',
1022
+ ' return 8;',
1023
+ '}',
1024
+ '',
1025
+ 'export function main(): number {',
1026
+ ' const iterator = iterate();',
1027
+ ' const first = iterator.next();',
1028
+ ' const second = iterator.next();',
1029
+ ' const third = iterator.next();',
1030
+ ' const fourth = iterator.next();',
1031
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1032
+ ' + ((second.done ? 0 : second.value) * 100)' +
1033
+ ' + ((third.done ? 0 : third.value) * 10)' +
1034
+ ' + (fourth.done ? fourth.value : 0);',
1035
+ '}',
1036
+ '',
1037
+ ].join('\n'),
1038
+ },
1039
+ {
1040
+ name: 'compileProject lowers sync generator for let bodies and preserves outer locals',
1041
+ expected: 1223,
1042
+ source: [
1043
+ 'function* iterate(): Generator<number, number, unknown> {',
1044
+ ' let current = 20;',
1045
+ ' for (let current = 0; current < 2; current = current + 1) {',
1046
+ ' yield current + 1;',
1047
+ ' }',
1048
+ ' return current + 3;',
1049
+ '}',
1050
+ '',
1051
+ 'export function main(): number {',
1052
+ ' const iterator = iterate();',
1053
+ ' const first = iterator.next();',
1054
+ ' const second = iterator.next();',
1055
+ ' const third = iterator.next();',
1056
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1057
+ ' + ((second.done ? 0 : second.value) * 100)' +
1058
+ ' + (third.done ? third.value : 0);',
1059
+ '}',
1060
+ '',
1061
+ ].join('\n'),
1062
+ },
1063
+ {
1064
+ name: 'compileProject lowers sync generator for let continue statements and preserves outer locals',
1065
+ expected: 1323,
1066
+ source: [
1067
+ 'function* iterate(): Generator<number, number, unknown> {',
1068
+ ' let current = 20;',
1069
+ ' for (let current = 0; current < 3; current = current + 1) {',
1070
+ ' if (current === 1) {',
1071
+ ' continue;',
1072
+ ' }',
1073
+ ' yield current + 1;',
1074
+ ' }',
1075
+ ' return current + 3;',
1076
+ '}',
1077
+ '',
1078
+ 'export function main(): number {',
1079
+ ' const iterator = iterate();',
1080
+ ' const first = iterator.next();',
1081
+ ' const second = iterator.next();',
1082
+ ' const third = iterator.next();',
1083
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1084
+ ' + ((second.done ? 0 : second.value) * 100)' +
1085
+ ' + (third.done ? third.value : 0);',
1086
+ '}',
1087
+ '',
1088
+ ].join('\n'),
1089
+ },
1090
+ {
1091
+ name: 'compileProject lowers sync generator for let continue through finally and preserves outer locals',
1092
+ expected: 15503731,
1093
+ source: [
1094
+ 'function* iterate(): Generator<number, number, unknown> {',
1095
+ ' let current = 20;',
1096
+ ' for (let current = 0; current < 3; current = current + 1) {',
1097
+ ' try {',
1098
+ ' if (current === 1) {',
1099
+ ' continue;',
1100
+ ' }',
1101
+ ' yield current + 1;',
1102
+ ' } finally {',
1103
+ ' yield 5;',
1104
+ ' }',
1105
+ ' }',
1106
+ ' return current + 3;',
1107
+ '}',
1108
+ '',
1109
+ 'export function main(): number {',
1110
+ ' const iterator = iterate();',
1111
+ ' const first = iterator.next();',
1112
+ ' const second = iterator.next();',
1113
+ ' const third = iterator.next();',
1114
+ ' const fourth = iterator.next();',
1115
+ ' const fifth = iterator.next();',
1116
+ ' const sixth = iterator.next();',
1117
+ ' return ((first.done ? 0 : first.value) * 10000000)' +
1118
+ ' + ((second.done ? 0 : second.value) * 1000000)' +
1119
+ ' + ((third.done ? 0 : third.value) * 100000)' +
1120
+ ' + ((fourth.done ? 0 : fourth.value) * 1000)' +
1121
+ ' + ((fifth.done ? 0 : fifth.value) * 100)' +
1122
+ ' + ((sixth.done ? sixth.value : 0) * 10)' +
1123
+ ' + (sixth.done ? 1 : 0);',
1124
+ '}',
1125
+ '',
1126
+ ].join('\n'),
1127
+ },
1128
+ {
1129
+ name: 'compileProject lowers nested sync generator try finally regions',
1130
+ expected: 15,
1131
+ source: [
1132
+ 'function* iterate(): Generator<number, number, unknown> {',
1133
+ ' let total = 0;',
1134
+ ' try {',
1135
+ ' try {',
1136
+ ' yield 1;',
1137
+ ' } finally {',
1138
+ ' total = total + 2;',
1139
+ ' }',
1140
+ ' } finally {',
1141
+ ' total = total + 3;',
1142
+ ' }',
1143
+ ' return total;',
1144
+ '}',
1145
+ '',
1146
+ 'export function main(): number {',
1147
+ ' const iterator = iterate();',
1148
+ ' const first = iterator.next();',
1149
+ ' const second = iterator.next();',
1150
+ ' return ((first.done ? 0 : first.value) * 10) + (second.done ? second.value : 0);',
1151
+ '}',
1152
+ '',
1153
+ ].join('\n'),
1154
+ },
1155
+ {
1156
+ name: 'compileProject hoists local function declarations inside sync generators across yield boundaries',
1157
+ expected: 222,
1158
+ source: [
1159
+ 'function* iterate(): Generator<number, number, number> {',
1160
+ ' function addTwo(value: number): number {',
1161
+ ' return value + 2;',
1162
+ ' }',
1163
+ ' const received = yield 20;',
1164
+ ' return addTwo(received);',
1165
+ '}',
1166
+ '',
1167
+ 'export function main(): number {',
1168
+ ' const iterator = iterate();',
1169
+ ' const first = iterator.next();',
1170
+ ' const second = iterator.next(20);',
1171
+ ' return ((first.done ? 0 : first.value) * 10) + (second.done ? second.value : 0);',
1172
+ '}',
1173
+ '',
1174
+ ].join('\n'),
1175
+ },
1176
+ {
1177
+ name: 'compileProject preserves hoisted local function captures over persisted generator locals',
1178
+ expected: 22,
1179
+ source: [
1180
+ 'function* iterate(): Generator<number, number, number> {',
1181
+ ' let total = 20;',
1182
+ ' function readTotal(offset: number): number {',
1183
+ ' return total + offset;',
1184
+ ' }',
1185
+ ' total = yield 0;',
1186
+ ' return readTotal(1);',
1187
+ '}',
1188
+ '',
1189
+ 'export function main(): number {',
1190
+ ' const iterator = iterate();',
1191
+ ' iterator.next();',
1192
+ ' const result = iterator.next(21);',
1193
+ ' return result.done ? result.value : 0;',
1194
+ '}',
1195
+ '',
1196
+ ].join('\n'),
1197
+ },
1198
+ {
1199
+ name: 'compileProject hoists block-scoped local function declarations inside sync generators across yield boundaries',
1200
+ expected: 24,
1201
+ source: [
1202
+ 'function* iterate(): Generator<number, number, number> {',
1203
+ ' if (true) {',
1204
+ ' let total = 20;',
1205
+ ' function readTotal(offset: number): number {',
1206
+ ' return total + offset;',
1207
+ ' }',
1208
+ ' total = yield 0;',
1209
+ ' return readTotal(3);',
1210
+ ' }',
1211
+ ' return 0;',
1212
+ '}',
1213
+ '',
1214
+ 'export function main(): number {',
1215
+ ' const iterator = iterate();',
1216
+ ' iterator.next();',
1217
+ ' const result = iterator.next(21);',
1218
+ ' return result.done ? result.value : 0;',
1219
+ '}',
1220
+ '',
1221
+ ].join('\n'),
1222
+ },
1223
+ {
1224
+ name: 'compileProject lowers sync generator class methods',
1225
+ expected: 456,
1226
+ source: [
1227
+ 'class Counter {',
1228
+ ' *iterate(): Generator<number, number, unknown> {',
1229
+ ' yield 4;',
1230
+ ' yield 5;',
1231
+ ' return 6;',
1232
+ ' }',
1233
+ '}',
1234
+ '',
1235
+ 'export function main(): number {',
1236
+ ' const iterator = new Counter().iterate();',
1237
+ ' const first = iterator.next();',
1238
+ ' const second = iterator.next();',
1239
+ ' const third = iterator.next();',
1240
+ ' return ((first.done ? 0 : first.value) * 100)' +
1241
+ ' + ((second.done ? 0 : second.value) * 10)' +
1242
+ ' + (third.done ? third.value : 0);',
1243
+ '}',
1244
+ '',
1245
+ ].join('\n'),
1246
+ },
1247
+ {
1248
+ name: 'compileProject lowers sync generator class methods with this field reads',
1249
+ expected: 456,
1250
+ source: [
1251
+ 'class Counter {',
1252
+ ' base = 4;',
1253
+ '',
1254
+ ' *iterate(): Generator<number, number, unknown> {',
1255
+ ' yield this.base;',
1256
+ ' yield this.base + 1;',
1257
+ ' return this.base + 2;',
1258
+ ' }',
1259
+ '}',
1260
+ '',
1261
+ 'export function main(): number {',
1262
+ ' const iterator = new Counter().iterate();',
1263
+ ' const first = iterator.next();',
1264
+ ' const second = iterator.next();',
1265
+ ' const third = iterator.next();',
1266
+ ' return ((first.done ? 0 : first.value) * 100)' +
1267
+ ' + ((second.done ? 0 : second.value) * 10)' +
1268
+ ' + (third.done ? third.value : 0);',
1269
+ '}',
1270
+ '',
1271
+ ].join('\n'),
1272
+ },
1273
+ {
1274
+ name: 'compileProject lowers sync generator class methods with super calls',
1275
+ expected: 567,
1276
+ source: [
1277
+ 'class Base {',
1278
+ ' bump(value: number): number {',
1279
+ ' return value + 1;',
1280
+ ' }',
1281
+ '}',
1282
+ '',
1283
+ 'class Counter extends Base {',
1284
+ ' base = 4;',
1285
+ '',
1286
+ ' *iterate(): Generator<number, number, unknown> {',
1287
+ ' yield super.bump(this.base);',
1288
+ ' yield super.bump(this.base + 1);',
1289
+ ' return super.bump(this.base + 2);',
1290
+ ' }',
1291
+ '}',
1292
+ '',
1293
+ 'export function main(): number {',
1294
+ ' const iterator = new Counter().iterate();',
1295
+ ' const first = iterator.next();',
1296
+ ' const second = iterator.next();',
1297
+ ' const third = iterator.next();',
1298
+ ' return ((first.done ? 0 : first.value) * 100)' +
1299
+ ' + ((second.done ? 0 : second.value) * 10)' +
1300
+ ' + (third.done ? third.value : 0);',
1301
+ '}',
1302
+ '',
1303
+ ].join('\n'),
1304
+ },
1305
+ {
1306
+ name: 'compileProject lowers sync generator static class methods',
1307
+ expected: 456,
1308
+ source: [
1309
+ 'class Counter {',
1310
+ ' static *iterate(): Generator<number, number, unknown> {',
1311
+ ' yield 4;',
1312
+ ' yield 5;',
1313
+ ' return 6;',
1314
+ ' }',
1315
+ '}',
1316
+ '',
1317
+ 'export function main(): number {',
1318
+ ' const iterator = Counter.iterate();',
1319
+ ' const first = iterator.next();',
1320
+ ' const second = iterator.next();',
1321
+ ' const third = iterator.next();',
1322
+ ' return ((first.done ? 0 : first.value) * 100)' +
1323
+ ' + ((second.done ? 0 : second.value) * 10)' +
1324
+ ' + (third.done ? third.value : 0);',
1325
+ '}',
1326
+ '',
1327
+ ].join('\n'),
1328
+ },
1329
+ {
1330
+ name: 'compileProject lowers sync generator static class methods with this field reads',
1331
+ expected: 456,
1332
+ source: [
1333
+ 'class Counter {',
1334
+ ' static base = 4;',
1335
+ '',
1336
+ ' static *iterate(): Generator<number, number, unknown> {',
1337
+ ' yield this.base;',
1338
+ ' yield this.base + 1;',
1339
+ ' return this.base + 2;',
1340
+ ' }',
1341
+ '}',
1342
+ '',
1343
+ 'export function main(): number {',
1344
+ ' const iterator = Counter.iterate();',
1345
+ ' const first = iterator.next();',
1346
+ ' const second = iterator.next();',
1347
+ ' const third = iterator.next();',
1348
+ ' return ((first.done ? 0 : first.value) * 100)' +
1349
+ ' + ((second.done ? 0 : second.value) * 10)' +
1350
+ ' + (third.done ? third.value : 0);',
1351
+ '}',
1352
+ '',
1353
+ ].join('\n'),
1354
+ },
1355
+ {
1356
+ name: 'compileProject lowers local object literal sync generator methods',
1357
+ expected: 456,
1358
+ source: [
1359
+ 'export function main(): number {',
1360
+ ' const counter = {',
1361
+ ' *iterate(): Generator<number, number, unknown> {',
1362
+ ' yield 4;',
1363
+ ' yield 5;',
1364
+ ' return 6;',
1365
+ ' },',
1366
+ ' };',
1367
+ ' const iterator = counter.iterate();',
1368
+ ' const first = iterator.next();',
1369
+ ' const second = iterator.next();',
1370
+ ' const third = iterator.next();',
1371
+ ' return ((first.done ? 0 : first.value) * 100)' +
1372
+ ' + ((second.done ? 0 : second.value) * 10)' +
1373
+ ' + (third.done ? third.value : 0);',
1374
+ '}',
1375
+ '',
1376
+ ].join('\n'),
1377
+ },
1378
+ {
1379
+ name: 'compileProject lowers local object literal sync generator methods with this field reads',
1380
+ expected: 456,
1381
+ source: [
1382
+ 'export function main(): number {',
1383
+ ' const counter = {',
1384
+ ' base: 4,',
1385
+ ' *iterate(): Generator<number, number, unknown> {',
1386
+ ' yield this.base;',
1387
+ ' yield this.base + 1;',
1388
+ ' return this.base + 2;',
1389
+ ' },',
1390
+ ' };',
1391
+ ' const iterator = counter.iterate();',
1392
+ ' const first = iterator.next();',
1393
+ ' const second = iterator.next();',
1394
+ ' const third = iterator.next();',
1395
+ ' return ((first.done ? 0 : first.value) * 100)' +
1396
+ ' + ((second.done ? 0 : second.value) * 10)' +
1397
+ ' + (third.done ? third.value : 0);',
1398
+ '}',
1399
+ '',
1400
+ ].join('\n'),
1401
+ },
1402
+ {
1403
+ name: 'compileProject lowers sync generator yield star delegation',
1404
+ expected: 10081213,
1405
+ source: [
1406
+ 'function* inner(): Generator<number, number, number> {',
1407
+ ' const received = yield 10;',
1408
+ ' yield received + 1;',
1409
+ ' return received + 2;',
1410
+ '}',
1411
+ '',
1412
+ 'function* outer(): Generator<number, number, number> {',
1413
+ ' const delegated = yield* inner();',
1414
+ ' yield delegated + 3;',
1415
+ ' return delegated + 4;',
1416
+ '}',
1417
+ '',
1418
+ 'export function main(): number {',
1419
+ ' const iterator = outer();',
1420
+ ' const first = iterator.next();',
1421
+ ' const second = iterator.next(7);',
1422
+ ' const third = iterator.next();',
1423
+ ' const fourth = iterator.next();',
1424
+ ' return ((first.done ? 0 : first.value) * 1000000)' +
1425
+ ' + ((second.done ? 0 : second.value) * 10000)' +
1426
+ ' + ((third.done ? 0 : third.value) * 100)' +
1427
+ ' + (fourth.done ? fourth.value : 0);',
1428
+ '}',
1429
+ '',
1430
+ ].join('\n'),
1431
+ },
1432
+ {
1433
+ name: 'compileProject lowers sync generator yield star over arrays',
1434
+ expected: 3579,
1435
+ source: [
1436
+ 'function* outer(): Generator<number, number, unknown> {',
1437
+ ' yield* [3, 5, 7];',
1438
+ ' return 9;',
1439
+ '}',
1440
+ '',
1441
+ 'export function main(): number {',
1442
+ ' const iterator = outer();',
1443
+ ' const first = iterator.next();',
1444
+ ' const second = iterator.next();',
1445
+ ' const third = iterator.next();',
1446
+ ' const fourth = iterator.next();',
1447
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1448
+ ' + ((second.done ? 0 : second.value) * 100)' +
1449
+ ' + ((third.done ? 0 : third.value) * 10)' +
1450
+ ' + (fourth.done ? fourth.value : 0);',
1451
+ '}',
1452
+ '',
1453
+ ].join('\n'),
1454
+ },
1455
+ {
1456
+ name: 'compileProject lowers sync generator return during array yield star',
1457
+ expected: 3081,
1458
+ source: [
1459
+ 'function* outer(): Generator<number, number, unknown> {',
1460
+ ' yield* [3, 5, 7];',
1461
+ ' return 9;',
1462
+ '}',
1463
+ '',
1464
+ 'export function main(): number {',
1465
+ ' const iterator = outer();',
1466
+ ' const first = iterator.next();',
1467
+ ' const second = iterator.return(8);',
1468
+ ' const third = iterator.next();',
1469
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1470
+ ' + ((second.done ? second.value : 0) * 10)' +
1471
+ ' + (third.done ? 1 : 0);',
1472
+ '}',
1473
+ '',
1474
+ ].join('\n'),
1475
+ },
1476
+ {
1477
+ name: 'compileProject lowers sync generator yield star over iterator-valued locals',
1478
+ expected: 3579,
1479
+ source: [
1480
+ 'function* outer(): Generator<number, number, unknown> {',
1481
+ ' const values = new Map([["a", 3], ["b", 5], ["c", 7]]).values();',
1482
+ ' yield* values;',
1483
+ ' return 9;',
1484
+ '}',
1485
+ '',
1486
+ 'export function main(): number {',
1487
+ ' const iterator = outer();',
1488
+ ' const first = iterator.next();',
1489
+ ' const second = iterator.next();',
1490
+ ' const third = iterator.next();',
1491
+ ' const fourth = iterator.next();',
1492
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1493
+ ' + ((second.done ? 0 : second.value) * 100)' +
1494
+ ' + ((third.done ? 0 : third.value) * 10)' +
1495
+ ' + (fourth.done ? fourth.value : 0);',
1496
+ '}',
1497
+ '',
1498
+ ].join('\n'),
1499
+ },
1500
+ {
1501
+ name: 'compileProject lowers sync generator yield star over strings',
1502
+ expected: 1359,
1503
+ source: [
1504
+ 'function* outer(): Generator<string, number, unknown> {',
1505
+ ' yield* "ace";',
1506
+ ' return 9;',
1507
+ '}',
1508
+ '',
1509
+ 'export function main(): number {',
1510
+ ' const iterator = outer();',
1511
+ ' const first = iterator.next();',
1512
+ ' const second = iterator.next();',
1513
+ ' const third = iterator.next();',
1514
+ ' const fourth = iterator.next();',
1515
+ ' const firstValue = first.done ? 0 : first.value.charCodeAt(0) - 96;',
1516
+ ' const secondValue = second.done ? 0 : second.value.charCodeAt(0) - 96;',
1517
+ ' const thirdValue = third.done ? 0 : third.value.charCodeAt(0) - 96;',
1518
+ ' return (firstValue * 1000) + (secondValue * 100) + (thirdValue * 10)' +
1519
+ ' + (fourth.done ? fourth.value : 0);',
1520
+ '}',
1521
+ '',
1522
+ ].join('\n'),
1523
+ },
1524
+ {
1525
+ name: 'compileProject lowers sync generator yield star over local string arrays',
1526
+ expected: 1359,
1527
+ source: [
1528
+ 'function* outer(): Generator<string, number, unknown> {',
1529
+ " const values = ['a', 'c', 'e'];",
1530
+ ' yield* values;',
1531
+ ' return 9;',
1532
+ '}',
1533
+ '',
1534
+ 'export function main(): number {',
1535
+ ' const iterator = outer();',
1536
+ ' const first = iterator.next();',
1537
+ ' const second = iterator.next();',
1538
+ ' const third = iterator.next();',
1539
+ ' const fourth = iterator.next();',
1540
+ ' const firstValue = first.done ? 0 : first.value.charCodeAt(0) - 96;',
1541
+ ' const secondValue = second.done ? 0 : second.value.charCodeAt(0) - 96;',
1542
+ ' const thirdValue = third.done ? 0 : third.value.charCodeAt(0) - 96;',
1543
+ ' return (firstValue * 1000) + (secondValue * 100) + (thirdValue * 10)' +
1544
+ ' + (fourth.done ? fourth.value : 0);',
1545
+ '}',
1546
+ '',
1547
+ ].join('\n'),
1548
+ },
1549
+ {
1550
+ name: 'compileProject lowers sync generator yield star over local boolean arrays',
1551
+ expected: 1018,
1552
+ source: [
1553
+ 'function* outer(): Generator<boolean, number, unknown> {',
1554
+ ' const values = [true, false, true];',
1555
+ ' yield* values;',
1556
+ ' return 8;',
1557
+ '}',
1558
+ '',
1559
+ 'export function main(): number {',
1560
+ ' const iterator = outer();',
1561
+ ' const first = iterator.next();',
1562
+ ' const second = iterator.next();',
1563
+ ' const third = iterator.next();',
1564
+ ' const fourth = iterator.next();',
1565
+ ' return ((first.done ? 0 : (first.value ? 1 : 0)) * 1000)' +
1566
+ ' + ((second.done ? 0 : (second.value ? 1 : 0)) * 100)' +
1567
+ ' + ((third.done ? 0 : (third.value ? 1 : 0)) * 10)' +
1568
+ ' + (fourth.done ? fourth.value : 0);',
1569
+ '}',
1570
+ '',
1571
+ ].join('\n'),
1572
+ },
1573
+ {
1574
+ name: 'compileProject lowers sync generator yield star over local tagged arrays',
1575
+ expected: 2948,
1576
+ source: [
1577
+ 'function* outer(): Generator<number | string, number, unknown> {',
1578
+ " const values: Array<number | string> = [2, 'a', 4];",
1579
+ ' yield* values;',
1580
+ ' return 8;',
1581
+ '}',
1582
+ '',
1583
+ 'export function main(): number {',
1584
+ ' const iterator = outer();',
1585
+ ' const first = iterator.next();',
1586
+ ' const second = iterator.next();',
1587
+ ' const third = iterator.next();',
1588
+ ' const fourth = iterator.next();',
1589
+ " const firstValue = first.done ? 0 : (typeof first.value === 'number' ? first.value : 9);",
1590
+ " const secondValue = second.done ? 0 : (typeof second.value === 'number' ? second.value : 9);",
1591
+ " const thirdValue = third.done ? 0 : (typeof third.value === 'number' ? third.value : 9);",
1592
+ ' return (firstValue * 1000) + (secondValue * 100) + (thirdValue * 10)' +
1593
+ ' + (fourth.done ? fourth.value : 0);',
1594
+ '}',
1595
+ '',
1596
+ ].join('\n'),
1597
+ },
1598
+ {
1599
+ name: 'compileProject lowers sync generator yield star over primitive Sets',
1600
+ expected: 3579,
1601
+ source: [
1602
+ 'function* outer(): Generator<number, number, unknown> {',
1603
+ ' yield* new Set([3, 5, 7]);',
1604
+ ' return 9;',
1605
+ '}',
1606
+ '',
1607
+ 'export function main(): number {',
1608
+ ' const iterator = outer();',
1609
+ ' const first = iterator.next();',
1610
+ ' const second = iterator.next();',
1611
+ ' const third = iterator.next();',
1612
+ ' const fourth = iterator.next();',
1613
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1614
+ ' + ((second.done ? 0 : second.value) * 100)' +
1615
+ ' + ((third.done ? 0 : third.value) * 10)' +
1616
+ ' + (fourth.done ? fourth.value : 0);',
1617
+ '}',
1618
+ '',
1619
+ ].join('\n'),
1620
+ },
1621
+ {
1622
+ name: 'compileProject lowers sync generator yield star over Map values iterables',
1623
+ expected: 3579,
1624
+ source: [
1625
+ 'function* outer(): Generator<number, number, unknown> {',
1626
+ ' yield* new Map([["a", 3], ["b", 5], ["c", 7]]).values();',
1627
+ ' return 9;',
1628
+ '}',
1629
+ '',
1630
+ 'export function main(): number {',
1631
+ ' const iterator = outer();',
1632
+ ' const first = iterator.next();',
1633
+ ' const second = iterator.next();',
1634
+ ' const third = iterator.next();',
1635
+ ' const fourth = iterator.next();',
1636
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1637
+ ' + ((second.done ? 0 : second.value) * 100)' +
1638
+ ' + ((third.done ? 0 : third.value) * 10)' +
1639
+ ' + (fourth.done ? fourth.value : 0);',
1640
+ '}',
1641
+ '',
1642
+ ].join('\n'),
1643
+ },
1644
+ {
1645
+ name: 'compileProject lowers sync generator yield star over Map key iterables',
1646
+ expected: 1359,
1647
+ source: [
1648
+ 'function* outer(): Generator<string, number, unknown> {',
1649
+ ' yield* new Map([["a", 3], ["c", 5], ["e", 7]]).keys();',
1650
+ ' return 9;',
1651
+ '}',
1652
+ '',
1653
+ 'export function main(): number {',
1654
+ ' const iterator = outer();',
1655
+ ' const first = iterator.next();',
1656
+ ' const second = iterator.next();',
1657
+ ' const third = iterator.next();',
1658
+ ' const fourth = iterator.next();',
1659
+ ' const firstValue = first.done ? 0 : first.value.charCodeAt(0) - 96;',
1660
+ ' const secondValue = second.done ? 0 : second.value.charCodeAt(0) - 96;',
1661
+ ' const thirdValue = third.done ? 0 : third.value.charCodeAt(0) - 96;',
1662
+ ' return (firstValue * 1000) + (secondValue * 100) + (thirdValue * 10)' +
1663
+ ' + (fourth.done ? fourth.value : 0);',
1664
+ '}',
1665
+ '',
1666
+ ].join('\n'),
1667
+ },
1668
+ {
1669
+ name: 'compileProject lowers sync generator yield star over direct Set iterables',
1670
+ expected: 3579,
1671
+ source: [
1672
+ 'function* outer(): Generator<number, number, unknown> {',
1673
+ ' const values = new Set([3, 5, 7]);',
1674
+ ' yield* values;',
1675
+ ' return 9;',
1676
+ '}',
1677
+ '',
1678
+ 'export function main(): number {',
1679
+ ' const iterator = outer();',
1680
+ ' const first = iterator.next();',
1681
+ ' const second = iterator.next();',
1682
+ ' const third = iterator.next();',
1683
+ ' const fourth = iterator.next();',
1684
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1685
+ ' + ((second.done ? 0 : second.value) * 100)' +
1686
+ ' + ((third.done ? 0 : third.value) * 10)' +
1687
+ ' + (fourth.done ? fourth.value : 0);',
1688
+ '}',
1689
+ '',
1690
+ ].join('\n'),
1691
+ },
1692
+ {
1693
+ name: 'compileProject lowers sync generator yield star over direct Map iterables',
1694
+ expected: 133557,
1695
+ source: [
1696
+ 'function* outer(): Generator<[string, number], number, unknown> {',
1697
+ ' const values = new Map([["a", 3], ["c", 5], ["e", 7]]);',
1698
+ ' yield* values;',
1699
+ ' return 9;',
1700
+ '}',
1701
+ '',
1702
+ 'export function main(): number {',
1703
+ ' const iterator = outer();',
1704
+ ' const first = iterator.next();',
1705
+ ' const second = iterator.next();',
1706
+ ' const third = iterator.next();',
1707
+ ' const fourth = iterator.next();',
1708
+ ' const firstValue = first.done ? 0 : ((first.value[0].charCodeAt(0) - 96) * 10) + first.value[1];',
1709
+ ' const secondValue = second.done ? 0 : ((second.value[0].charCodeAt(0) - 96) * 10) + second.value[1];',
1710
+ ' const thirdValue = third.done ? 0 : ((third.value[0].charCodeAt(0) - 96) * 10) + third.value[1];',
1711
+ ' return (firstValue * 10000) + (secondValue * 100) + (thirdValue) + (fourth.done ? 0 : 0);',
1712
+ '}',
1713
+ '',
1714
+ ].join('\n'),
1715
+ },
1716
+ {
1717
+ name: 'compileProject lowers sync generator yield star over Set entries iterables',
1718
+ expected: 336677,
1719
+ source: [
1720
+ 'function* outer(): Generator<[number, number], number, unknown> {',
1721
+ ' yield* new Set([3, 6, 7]).entries();',
1722
+ ' return 9;',
1723
+ '}',
1724
+ '',
1725
+ 'export function main(): number {',
1726
+ ' const iterator = outer();',
1727
+ ' const first = iterator.next();',
1728
+ ' const second = iterator.next();',
1729
+ ' const third = iterator.next();',
1730
+ ' const fourth = iterator.next();',
1731
+ ' const firstValue = first.done ? 0 : (first.value[0] * 10) + first.value[1];',
1732
+ ' const secondValue = second.done ? 0 : (second.value[0] * 10) + second.value[1];',
1733
+ ' const thirdValue = third.done ? 0 : (third.value[0] * 10) + third.value[1];',
1734
+ ' return (firstValue * 10000) + (secondValue * 100) + thirdValue + (fourth.done ? 0 : 0);',
1735
+ '}',
1736
+ '',
1737
+ ].join('\n'),
1738
+ },
1739
+ {
1740
+ name: 'compileProject lowers sync generator try finally on natural completion',
1741
+ expected: 3591,
1742
+ source: [
1743
+ 'function* iterate(): Generator<number, number, unknown> {',
1744
+ ' try {',
1745
+ ' yield 3;',
1746
+ ' return 9;',
1747
+ ' } finally {',
1748
+ ' yield 5;',
1749
+ ' }',
1750
+ '}',
1751
+ '',
1752
+ 'export function main(): number {',
1753
+ ' const iterator = iterate();',
1754
+ ' const first = iterator.next();',
1755
+ ' const second = iterator.next();',
1756
+ ' const third = iterator.next();',
1757
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1758
+ ' + ((second.done ? 0 : second.value) * 100)' +
1759
+ ' + ((third.done ? third.value : 0) * 10)' +
1760
+ ' + (third.done ? 1 : 0);',
1761
+ '}',
1762
+ '',
1763
+ ].join('\n'),
1764
+ },
1765
+ {
1766
+ name: 'compileProject lowers sync generator return through finally',
1767
+ expected: 3581,
1768
+ source: [
1769
+ 'function* iterate(): Generator<number, number, unknown> {',
1770
+ ' try {',
1771
+ ' yield 3;',
1772
+ ' return 9;',
1773
+ ' } finally {',
1774
+ ' yield 5;',
1775
+ ' }',
1776
+ '}',
1777
+ '',
1778
+ 'export function main(): number {',
1779
+ ' const iterator = iterate();',
1780
+ ' const first = iterator.next();',
1781
+ ' const second = iterator.return(8);',
1782
+ ' const third = iterator.next();',
1783
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1784
+ ' + ((second.done ? 0 : second.value) * 100)' +
1785
+ ' + ((third.done ? third.value : 0) * 10)' +
1786
+ ' + (third.done ? 1 : 0);',
1787
+ '}',
1788
+ '',
1789
+ ].join('\n'),
1790
+ },
1791
+ {
1792
+ name: 'compileProject lowers sync generator external return through finally cleanup yields',
1793
+ expected: 175,
1794
+ source: [
1795
+ 'function* iterate(): Generator<number, number, unknown> {',
1796
+ ' try {',
1797
+ ' yield 1;',
1798
+ ' yield 2;',
1799
+ ' } finally {',
1800
+ ' yield 7;',
1801
+ ' }',
1802
+ ' return 3;',
1803
+ '}',
1804
+ '',
1805
+ 'export function main(): number {',
1806
+ ' const iterator = iterate();',
1807
+ ' const first = iterator.next();',
1808
+ ' const second = iterator.return(5);',
1809
+ ' const third = iterator.next();',
1810
+ ' return ((first.done ? 9 : first.value) * 100)' +
1811
+ ' + ((second.done ? 9 : second.value) * 10)' +
1812
+ ' + (third.done ? third.value : 9);',
1813
+ '}',
1814
+ '',
1815
+ ].join('\n'),
1816
+ },
1817
+ {
1818
+ name: 'compileProject lowers sync generator break through finally',
1819
+ expected: 3591,
1820
+ source: [
1821
+ 'function* iterate(): Generator<number, number, unknown> {',
1822
+ ' let index = 0;',
1823
+ ' while (index < 1) {',
1824
+ ' try {',
1825
+ ' index = index + 1;',
1826
+ ' yield 3;',
1827
+ ' break;',
1828
+ ' } finally {',
1829
+ ' yield 5;',
1830
+ ' }',
1831
+ ' }',
1832
+ ' return 9;',
1833
+ '}',
1834
+ '',
1835
+ 'export function main(): number {',
1836
+ ' const iterator = iterate();',
1837
+ ' const first = iterator.next();',
1838
+ ' const second = iterator.next();',
1839
+ ' const third = iterator.next();',
1840
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1841
+ ' + ((second.done ? 0 : second.value) * 100)' +
1842
+ ' + ((third.done ? third.value : 0) * 10)' +
1843
+ ' + (third.done ? 1 : 0);',
1844
+ '}',
1845
+ '',
1846
+ ].join('\n'),
1847
+ },
1848
+ {
1849
+ name: 'compileProject lowers sync generator continue through finally',
1850
+ expected: 3507591,
1851
+ source: [
1852
+ 'function* iterate(): Generator<number, number, unknown> {',
1853
+ ' let index = 0;',
1854
+ ' while (index < 2) {',
1855
+ ' try {',
1856
+ ' index = index + 1;',
1857
+ ' if (index === 1) {',
1858
+ ' yield 3;',
1859
+ ' continue;',
1860
+ ' }',
1861
+ ' yield 7;',
1862
+ ' } finally {',
1863
+ ' yield 5;',
1864
+ ' }',
1865
+ ' }',
1866
+ ' return 9;',
1867
+ '}',
1868
+ '',
1869
+ 'export function main(): number {',
1870
+ ' const iterator = iterate();',
1871
+ ' const first = iterator.next();',
1872
+ ' const second = iterator.next();',
1873
+ ' const third = iterator.next();',
1874
+ ' const fourth = iterator.next();',
1875
+ ' const fifth = iterator.next();',
1876
+ ' return ((first.done ? 0 : first.value) * 1000000)' +
1877
+ ' + ((second.done ? 0 : second.value) * 100000)' +
1878
+ ' + ((third.done ? 0 : third.value) * 1000)' +
1879
+ ' + ((fourth.done ? 0 : fourth.value) * 100)' +
1880
+ ' + ((fifth.done ? fifth.value : 0) * 10)' +
1881
+ ' + (fifth.done ? 1 : 0);',
1882
+ '}',
1883
+ '',
1884
+ ].join('\n'),
1885
+ },
1886
+ {
1887
+ name: 'compileProject lowers sync generator throw calls caught by try catch',
1888
+ expected: 37091,
1889
+ source: [
1890
+ 'function* iterate(): Generator<number, number, number> {',
1891
+ ' try {',
1892
+ ' yield 3;',
1893
+ ' return 8;',
1894
+ ' } catch {',
1895
+ ' yield 7;',
1896
+ ' return 9;',
1897
+ ' }',
1898
+ '}',
1899
+ '',
1900
+ 'export function main(): number {',
1901
+ ' const iterator = iterate();',
1902
+ ' const first = iterator.next();',
1903
+ ' const second = iterator.throw(5);',
1904
+ ' const third = iterator.next();',
1905
+ ' return ((first.done ? 0 : first.value) * 10000)' +
1906
+ ' + ((second.done ? 0 : second.value) * 1000)' +
1907
+ ' + ((third.done ? third.value : 0) * 10)' +
1908
+ ' + (third.done ? 1 : 0);',
1909
+ '}',
1910
+ '',
1911
+ ].join('\n'),
1912
+ },
1913
+ {
1914
+ name: 'compileProject lowers sync generator throw calls caught by try catch finally',
1915
+ expected: 37591,
1916
+ source: [
1917
+ 'function* iterate(): Generator<number, number, number> {',
1918
+ ' try {',
1919
+ ' yield 3;',
1920
+ ' return 8;',
1921
+ ' } catch {',
1922
+ ' yield 7;',
1923
+ ' return 9;',
1924
+ ' } finally {',
1925
+ ' yield 5;',
1926
+ ' }',
1927
+ '}',
1928
+ '',
1929
+ 'export function main(): number {',
1930
+ ' const iterator = iterate();',
1931
+ ' const first = iterator.next();',
1932
+ ' const second = iterator.throw(5);',
1933
+ ' const third = iterator.next();',
1934
+ ' const fourth = iterator.next();',
1935
+ ' return ((first.done ? 0 : first.value) * 10000)' +
1936
+ ' + ((second.done ? 0 : second.value) * 1000)' +
1937
+ ' + ((third.done ? 0 : third.value) * 100)' +
1938
+ ' + ((fourth.done ? fourth.value : 0) * 10)' +
1939
+ ' + (fourth.done ? 1 : 0);',
1940
+ '}',
1941
+ '',
1942
+ ].join('\n'),
1943
+ },
1944
+ {
1945
+ name: 'compileProject lowers authored sync generator throw caught by try catch',
1946
+ expected: 3791,
1947
+ source: [
1948
+ 'function* iterate(): Generator<number, number, unknown> {',
1949
+ ' try {',
1950
+ ' yield 3;',
1951
+ ' throw new Error("boom");',
1952
+ ' } catch {',
1953
+ ' yield 7;',
1954
+ ' return 9;',
1955
+ ' }',
1956
+ '}',
1957
+ '',
1958
+ 'export function main(): number {',
1959
+ ' const iterator = iterate();',
1960
+ ' const first = iterator.next();',
1961
+ ' const second = iterator.next();',
1962
+ ' const third = iterator.next();',
1963
+ ' return ((first.done ? 0 : first.value) * 1000)' +
1964
+ ' + ((second.done ? 0 : second.value) * 100)' +
1965
+ ' + ((third.done ? third.value : 0) * 10)' +
1966
+ ' + (third.done ? 1 : 0);',
1967
+ '}',
1968
+ '',
1969
+ ].join('\n'),
1970
+ },
1971
+ {
1972
+ name: 'compileProject lowers authored sync generator throw caught by try catch finally',
1973
+ expected: 37591,
1974
+ source: [
1975
+ 'function* iterate(): Generator<number, number, unknown> {',
1976
+ ' try {',
1977
+ ' yield 3;',
1978
+ ' throw new Error("boom");',
1979
+ ' } catch {',
1980
+ ' yield 7;',
1981
+ ' return 9;',
1982
+ ' } finally {',
1983
+ ' yield 5;',
1984
+ ' }',
1985
+ '}',
1986
+ '',
1987
+ 'export function main(): number {',
1988
+ ' const iterator = iterate();',
1989
+ ' const first = iterator.next();',
1990
+ ' const second = iterator.next();',
1991
+ ' const third = iterator.next();',
1992
+ ' const fourth = iterator.next();',
1993
+ ' return ((first.done ? 0 : first.value) * 10000)' +
1994
+ ' + ((second.done ? 0 : second.value) * 1000)' +
1995
+ ' + ((third.done ? 0 : third.value) * 100)' +
1996
+ ' + ((fourth.done ? fourth.value : 0) * 10)' +
1997
+ ' + (fourth.done ? 1 : 0);',
1998
+ '}',
1999
+ '',
2000
+ ].join('\n'),
2001
+ },
2002
+ {
2003
+ name: 'compileProject lowers sync generator throw delegation through yield star',
2004
+ expected: 3071111,
2005
+ source: [
2006
+ 'function* inner(): Generator<number, number, number> {',
2007
+ ' try {',
2008
+ ' yield 3;',
2009
+ ' return 8;',
2010
+ ' } catch {',
2011
+ ' yield 7;',
2012
+ ' return 9;',
2013
+ ' }',
2014
+ '}',
2015
+ '',
2016
+ 'function* outer(): Generator<number, number, number> {',
2017
+ ' const delegated = yield* inner();',
2018
+ ' yield delegated + 1;',
2019
+ ' return delegated + 2;',
2020
+ '}',
2021
+ '',
2022
+ 'export function main(): number {',
2023
+ ' const iterator = outer();',
2024
+ ' const first = iterator.next();',
2025
+ ' const second = iterator.throw(5);',
2026
+ ' const third = iterator.next();',
2027
+ ' const fourth = iterator.next();',
2028
+ ' return ((first.done ? 0 : first.value) * 1000000)' +
2029
+ ' + ((second.done ? 0 : second.value) * 10000)' +
2030
+ ' + ((third.done ? 0 : third.value) * 100)' +
2031
+ ' + ((fourth.done ? fourth.value : 0) * 10)' +
2032
+ ' + (fourth.done ? 1 : 0);',
2033
+ '}',
2034
+ '',
2035
+ ].join('\n'),
2036
+ },
2037
+ {
2038
+ name: 'compileProject rethrows TypeError for sync generator throw through array yield star',
2039
+ expectedThrow: { name: 'TypeError', message: 'yield* delegate does not support throw' },
2040
+ source: [
2041
+ 'function* outer(): Generator<number, number, unknown> {',
2042
+ ' yield* [3, 5, 7];',
2043
+ ' return 9;',
2044
+ '}',
2045
+ '',
2046
+ 'export function main(): number {',
2047
+ ' const iterator = outer();',
2048
+ ' iterator.next();',
2049
+ ' iterator.throw(5);',
2050
+ ' return 0;',
2051
+ '}',
2052
+ '',
2053
+ ].join('\n'),
2054
+ },
2055
+ {
2056
+ name: 'compileProject catches TypeError for sync generator throw through array yield star',
2057
+ expected: 3861,
2058
+ source: [
2059
+ 'function* outer(): Generator<number, number, unknown> {',
2060
+ ' try {',
2061
+ ' yield* [3, 5, 7];',
2062
+ ' return 9;',
2063
+ ' } catch (error) {',
2064
+ ' if (error instanceof TypeError) {',
2065
+ ' yield 8;',
2066
+ ' return 6;',
2067
+ ' }',
2068
+ ' return 0;',
2069
+ ' }',
2070
+ '}',
2071
+ '',
2072
+ 'export function main(): number {',
2073
+ ' const iterator = outer();',
2074
+ ' const first = iterator.next();',
2075
+ ' const second = iterator.throw(5);',
2076
+ ' const third = iterator.next();',
2077
+ ' return ((first.done ? 0 : first.value) * 1000)' +
2078
+ ' + ((second.done ? 0 : second.value) * 100)' +
2079
+ ' + ((third.done ? third.value : 0) * 10)' +
2080
+ ' + (third.done ? 1 : 0);',
2081
+ '}',
2082
+ '',
2083
+ ].join('\n'),
2084
+ },
2085
+ {
2086
+ name: 'compileProject lowers sync generator catch bindings for thrown values',
2087
+ expected: 37091,
2088
+ source: [
2089
+ 'function* iterate(): Generator<number, number, unknown> {',
2090
+ ' try {',
2091
+ ' yield 3;',
2092
+ ' return 8;',
2093
+ ' } catch (error) {',
2094
+ ' if (typeof error === "number") {',
2095
+ ' yield error + 2;',
2096
+ ' return error + 4;',
2097
+ ' }',
2098
+ ' return 0;',
2099
+ ' }',
2100
+ '}',
2101
+ '',
2102
+ 'export function main(): number {',
2103
+ ' const iterator = iterate();',
2104
+ ' const first = iterator.next();',
2105
+ ' const second = iterator.throw(5);',
2106
+ ' const third = iterator.next();',
2107
+ ' return ((first.done ? 0 : first.value) * 10000)' +
2108
+ ' + ((second.done ? 0 : second.value) * 1000)' +
2109
+ ' + ((third.done ? third.value : 0) * 10)' +
2110
+ ' + (third.done ? 1 : 0);',
2111
+ '}',
2112
+ '',
2113
+ ].join('\n'),
2114
+ },
2115
+ {
2116
+ name: 'compileProject rethrows uncaught sync generator throw calls to host',
2117
+ expectedThrow: 5,
2118
+ source: [
2119
+ 'function* iterate(): Generator<number, number, unknown> {',
2120
+ ' yield 3;',
2121
+ ' return 8;',
2122
+ '}',
2123
+ '',
2124
+ 'export function main(): number {',
2125
+ ' const iterator = iterate();',
2126
+ ' iterator.next();',
2127
+ ' iterator.throw(5);',
2128
+ ' return 0;',
2129
+ '}',
2130
+ '',
2131
+ ].join('\n'),
2132
+ },
2133
+ {
2134
+ name: 'compileProject rethrows uncaught authored builtin Error throws from sync generators to host',
2135
+ expectedThrow: { name: 'Error', message: 'boom' },
2136
+ source: [
2137
+ 'function* iterate(): Generator<number, number, unknown> {',
2138
+ ' yield 3;',
2139
+ ' throw new Error("boom");',
2140
+ '}',
2141
+ '',
2142
+ 'export function main(): number {',
2143
+ ' const iterator = iterate();',
2144
+ ' iterator.next();',
2145
+ ' iterator.next();',
2146
+ ' return 0;',
2147
+ '}',
2148
+ '',
2149
+ ].join('\n'),
2150
+ },
2151
+ {
2152
+ name: 'compileProject rethrows uncaught sync generator throw Error calls to host',
2153
+ expectedThrow: { name: 'Error', message: 'boom' },
2154
+ source: [
2155
+ 'function* iterate(): Generator<number, number, unknown> {',
2156
+ ' yield 3;',
2157
+ ' return 8;',
2158
+ '}',
2159
+ '',
2160
+ 'export function main(): number {',
2161
+ ' const iterator = iterate();',
2162
+ ' iterator.next();',
2163
+ ' iterator.throw(new Error("boom"));',
2164
+ ' return 0;',
2165
+ '}',
2166
+ '',
2167
+ ].join('\n'),
2168
+ },
2169
+ {
2170
+ name: 'compileProject lowers sync generator switch statements with fallthrough',
2171
+ expected: 12021844,
2172
+ source: [
2173
+ 'function* iterate(flag: number): Generator<number, number, unknown> {',
2174
+ ' let total = 1;',
2175
+ ' switch (flag) {',
2176
+ ' case 1:',
2177
+ ' yield total + 10;',
2178
+ ' break;',
2179
+ ' case 2:',
2180
+ ' yield total + 20;',
2181
+ ' default:',
2182
+ ' total = total + 3;',
2183
+ ' yield total;',
2184
+ ' break;',
2185
+ ' }',
2186
+ ' return total * 10;',
2187
+ '}',
2188
+ '',
2189
+ 'export function main(): number {',
2190
+ ' const one = iterate(1);',
2191
+ ' const oneFirst = one.next();',
2192
+ ' const oneSecond = one.next();',
2193
+ ' const two = iterate(2);',
2194
+ ' const twoFirst = two.next();',
2195
+ ' const twoSecond = two.next();',
2196
+ ' const twoThird = two.next();',
2197
+ ' const three = iterate(3);',
2198
+ ' const threeFirst = three.next();',
2199
+ ' const threeSecond = three.next();',
2200
+ ' return ((oneFirst.done ? 0 : oneFirst.value) * 1000000)' +
2201
+ ' + ((oneSecond.done ? oneSecond.value : 0) * 100000)' +
2202
+ ' + ((twoFirst.done ? 0 : twoFirst.value) * 1000)' +
2203
+ ' + ((twoSecond.done ? 0 : twoSecond.value) * 100)' +
2204
+ ' + ((twoThird.done ? twoThird.value : 0) * 10)' +
2205
+ ' + (threeFirst.done ? 0 : threeFirst.value)' +
2206
+ ' + (threeSecond.done ? threeSecond.value : 0);',
2207
+ '}',
2208
+ '',
2209
+ ].join('\n'),
2210
+ },
2211
+ {
2212
+ name: 'compileProject lowers sync generator string switch statements with fallthrough',
2213
+ expected: 12021844,
2214
+ source: [
2215
+ 'function* iterate(flag: string): Generator<number, number, unknown> {',
2216
+ ' let total = 1;',
2217
+ ' switch (flag) {',
2218
+ ' case "one":',
2219
+ ' yield total + 10;',
2220
+ ' break;',
2221
+ ' case "two":',
2222
+ ' yield total + 20;',
2223
+ ' default:',
2224
+ ' total = total + 3;',
2225
+ ' yield total;',
2226
+ ' break;',
2227
+ ' }',
2228
+ ' return total * 10;',
2229
+ '}',
2230
+ '',
2231
+ 'export function main(): number {',
2232
+ ' const one = iterate("one");',
2233
+ ' const oneFirst = one.next();',
2234
+ ' const oneSecond = one.next();',
2235
+ ' const two = iterate("two");',
2236
+ ' const twoFirst = two.next();',
2237
+ ' const twoSecond = two.next();',
2238
+ ' const twoThird = two.next();',
2239
+ ' const three = iterate("other");',
2240
+ ' const threeFirst = three.next();',
2241
+ ' const threeSecond = three.next();',
2242
+ ' return ((oneFirst.done ? 0 : oneFirst.value) * 1000000)' +
2243
+ ' + ((oneSecond.done ? oneSecond.value : 0) * 100000)' +
2244
+ ' + ((twoFirst.done ? 0 : twoFirst.value) * 1000)' +
2245
+ ' + ((twoSecond.done ? 0 : twoSecond.value) * 100)' +
2246
+ ' + ((twoThird.done ? twoThird.value : 0) * 10)' +
2247
+ ' + (threeFirst.done ? 0 : threeFirst.value)' +
2248
+ ' + (threeSecond.done ? threeSecond.value : 0);',
2249
+ '}',
2250
+ '',
2251
+ ].join('\n'),
2252
+ },
2253
+ {
2254
+ name: 'compileProject preserves sync generator switch break inside try finally loops',
2255
+ expected: 213223,
2256
+ source: [
2257
+ 'function* iterate(): Generator<number, number, unknown> {',
2258
+ ' let count = 0;',
2259
+ ' let total = 0;',
2260
+ ' while (count < 2) {',
2261
+ ' try {',
2262
+ ' switch (count) {',
2263
+ ' case 0:',
2264
+ ' total = total + 1;',
2265
+ ' break;',
2266
+ ' default:',
2267
+ ' total = total + 2;',
2268
+ ' break;',
2269
+ ' }',
2270
+ ' yield total;',
2271
+ ' total = total + 10;',
2272
+ ' } finally {',
2273
+ ' count = count + 1;',
2274
+ ' total = total + 100;',
2275
+ ' }',
2276
+ ' }',
2277
+ ' return total;',
2278
+ '}',
2279
+ '',
2280
+ 'export function main(): number {',
2281
+ ' const iterator = iterate();',
2282
+ ' const first = iterator.next();',
2283
+ ' const second = iterator.next();',
2284
+ ' const third = iterator.next();',
2285
+ ' return ((first.done ? 0 : first.value) * 100000) +',
2286
+ ' ((second.done ? 0 : second.value) * 1000) +',
2287
+ ' (third.done ? third.value : 0);',
165
2288
  '}',
166
2289
  '',
167
2290
  ].join('\n'),