@@ -7,14 +7,14 @@ Here's the body of the `__generator` helper:
7
7
8
8
``` js
9
9
__generator = function (thisArg , body ) {
10
- var _ = { label: 0 , sent : function () { if (t[0 ] & 1 ) throw t[1 ]; return t[1 ]; }, trys: [], ops: [] }, f, y, t;
11
- return { next: verb (0 ), " throw" : verb (1 ), " return" : verb (2 ) };
10
+ var _ = { label: 0 , sent : function () { if (t[0 ] & 1 ) throw t[1 ]; return t[1 ]; }, trys: [], ops: [] }, f, y, t, g ;
11
+ return g = { next: verb (0 ), " throw" : verb (1 ), " return" : verb (2 ) }, typeof Symbol === " function " && (g[ Symbol . iterator ] = function () { return this ; }), g ;
12
12
function verb (n ) { return function (v ) { return step ([n, v]); }; }
13
13
function step (op ) {
14
14
if (f) throw new TypeError (" Generator is already executing." );
15
- while (_) try {
16
- if (f = 1 , y && (t = y[ op[0 ] & 2 ? " return" : op[0 ] ? " throw" : " next " ] ) && ! (t = t .call (y, op[1 ])).done ) return t;
17
- if (y = 0 , t) op = [0 , t .value ];
15
+ while (g && (g = 0 , op[ 0 ] && (_ = 0 )), _) try {
16
+ if (f = 1 , y && (t = op[0 ] & 2 ? y[ " return" ] : op[0 ] ? y[ " throw" ] || ((t = y[ " return " ]) && t . call (y), 0 ) : y . next ) && ! (t = t .call (y, op[1 ])).done ) return t;
17
+ if (y = 0 , t) op = [op[ 0 ] & 2 , t .value ];
18
18
switch (op[0 ]) {
19
19
case 0 : case 1 : t = op; break ;
20
20
case 4 : _ .label ++ ; return { value: op[1 ], done: false };
@@ -98,7 +98,7 @@ arguments, and their purpose:
98
98
| 7 (endfinally) | | Exits a finally block, resuming any previous operation (such as a break, return, throw, etc.) |
99
99
100
100
# State
101
- The ` _ ` , ` f ` , ` y ` , and ` t ` variables make up the persistent state of the ` __generator ` function. Each variable
101
+ The ` _ ` , ` f ` , ` y ` , ` t ` , and ` g ` variables make up the persistent state of the ` __generator ` function. Each variable
102
102
has a specific purpose, as described in the following sections:
103
103
104
104
## The ` _ ` variable
@@ -148,6 +148,12 @@ The `t` variable is a temporary variable that stores one of the following values
148
148
149
149
> NOTE: None of the above cases overlap.
150
150
151
+ ## The ` g ` variable
152
+ The ` g ` variable is a temporary variable that holds onto the generator object for the purpose of attaching a
153
+ ` Symbol.iterator ` method (if its available), and holds onto that value until the generator is started, allowing
154
+
155
+ it to also act as the [ ` suspendedStart ` ] ( https://tc39.es/ecma262/#table-internal-slots-of-generator-instances ) state.
156
+
151
157
# Protected Regions
152
158
A ** Protected Region** is a region within the ` body ` function that indicates a
153
159
` try..catch..finally ` statement. It consists of a 4-tuple that contains 4 labels:
@@ -164,13 +170,18 @@ The final step of the `__generator` helper is the allocation of an object that i
164
170
` Generator ` protocol, to be used by the ` __awaiter ` helper:
165
171
166
172
``` ts
167
- return { next: verb (0 ), " throw" : verb (1 ), " return" : verb (2 ) };
173
+ return g = { next: verb (0 ), " throw" : verb (1 ), " return" : verb (2 ) },
174
+ typeof Symbol === " function" && (g [Symbol .iterator ] = function () { return this ; }),
175
+ g ;
168
176
function verb(n ) { return function (v ) { return step ([n , v ]); }; }
169
177
```
170
178
171
179
This object translates calls to ` next ` , ` throw ` , and ` return ` to the appropriate Opcodes and
172
180
invokes the ` step ` orchestration function to continue execution. The ` throw ` and ` return ` method
173
- names are quoted to better support ES3.
181
+ names are quoted to better support ES3. In addition, a ` Symbol.iterator ` method is added to the
182
+ generator if the global ` Symbol ` constructor is available. Once we return, the object reference in
183
+ the ` g ` variable isn't used by the main [ orchestration method] ( #orchestration ) , so we can use its
184
+ truthiness as a mechanism to determine whether the generator is in the ` suspendedStart ` state.
174
185
175
186
# Orchestration
176
187
The ` step ` function is the main orechestration mechanism for the ` __generator ` helper. It
@@ -181,9 +192,9 @@ Here's a closer look at the `step` function:
181
192
``` ts
182
193
function step(op ) {
183
194
if (f ) throw new TypeError (" Generator is already executing." );
184
- while (_ ) try {
185
- if (f = 1 , y && (t = y [ op [0 ] & 2 ? " return" : op [0 ] ? " throw" : " next " ] ) && ! (t = t .call (y , op [1 ])).done ) return t ;
186
- if (y = 0 , t ) op = [0 , t .value ];
195
+ while (g && ( g = 0 , op [ 0 ] && ( _ = 0 )), _ ) try {
196
+ if (f = 1 , y && (t = op [0 ] & 2 ? y [ " return" ] : op [0 ] ? y [ " throw" ] || (( t = y [ " return " ]) && t . call ( y ), 0 ) : y . next ) && ! (t = t .call (y , op [1 ])).done ) return t ;
197
+ if (y = 0 , t ) op = [op [ 0 ] & 2 , t .value ];
187
198
switch (op [0 ]) {
188
199
case 0 : case 1 : t = op ; break ;
189
200
case 4 : _ .label ++ ; return { value: op [1 ], done: false };
@@ -219,13 +230,25 @@ The main body of the `step` function consists of a `while` loop which continues
219
230
instructions until the generator exits or is suspended:
220
231
221
232
``` ts
222
- while (_ ) try ...
233
+ while (g && ( g = 0 , op [ 0 ] && ( _ = 0 )), _ ) try ...
223
234
```
224
235
236
+ During the first call to ` next() ` , ` return() ` , or ` throw() ` , the generator will be in the ` suspendedStart ` state. This
237
+ is indicated by the ` g ` variable being "truthy" since it still holds a reference to the generator object. If ` g ` is
238
+ "truthy", then we reset it and check whether the first instruction sent to the generator (` op[0] ` ) is either a
239
+ ` return ` (` op[0] === 1 ` ) or ` throw ` (` op[0] === 2 ` ) Opcode, indicating an abrupt completion.
240
+
241
+ If the first instruction is abrupt, we can emulate [ GeneratorResumeAbrupt] ( https://tc39.es/ecma262/#sec-generatorresumeabrupt )
242
+ by setting the state variable (` _ ` ) to a falsy value, which will skip the loop body and
243
+ [ complete the generator] ( #handling-a-completed-generator ) .
244
+
245
+ If this is _ not_ the first instruction sent to the generator, or if the first instruction was ` next() ` , we will proceed
246
+ to evaluate the loop body.
247
+
225
248
When the generator has run to completion, the ` _ ` state variable will be cleared, forcing the loop
226
249
to exit.
227
250
228
- ## Evaluating the generator body.
251
+ ## Evaluating the generator body
229
252
``` ts
230
253
try {
231
254
...
@@ -272,8 +295,8 @@ reduce the overall footprint of the helper.
272
295
The first two statements of the ` try..finally ` statement handle delegation for ` yield* ` :
273
296
274
297
``` ts
275
- if (f = 1 , y && (t = y [ op [0 ] & 2 ? " return" : op [0 ] ? " throw" : " next " ] ) && ! (t = t .call (y , op [1 ])).done ) return t ;
276
- if (y = 0 , t ) op = [0 , t .value ];
298
+ if (f = 1 , y && (t = op [0 ] & 2 ? y [ " return" ] : op [0 ] ? y [ " throw" ] || (( t = y [ " return " ]) && t . call ( y ), 0 ) : y . next ) && ! (t = t .call (y , op [1 ])).done ) return t ;
299
+ if (y = 0 , t ) op = [op [ 0 ] & 2 , t .value ];
277
300
```
278
301
279
302
If the ` y ` variable is set, and ` y ` has a ` next ` , ` throw ` , or ` return ` method (depending on the
@@ -282,7 +305,7 @@ current operation), we invoke this method and store the return value (an Iterato
282
305
If ` t ` indicates it is a yielded value (e.g. ` t.done === false ` ), we return ` t ` to the caller.
283
306
If ` t ` indicates it is a returned value (e.g. ` t.done === true ` ), we mark the operation with the
284
307
` next ` Opcode, and the returned value.
285
- If ` y ` did not have the appropriate method, or ` t ` was a returned value, we reset ` y ` to a falsey
308
+ If ` y ` did not have the appropriate method, or ` t ` was a returned value, we reset ` y ` to a falsy
286
309
value and continue processing the operation.
287
310
288
311
## Handling operations
@@ -402,7 +425,7 @@ if (!(t = ...) && (op[0] === 6 || op[0] === 2)) {
402
425
```
403
426
404
427
If we encounter an Opcode 6 ("catch") or Opcode 2 ("return"), and we are not in a protected region,
405
- then this operation completes the generator by setting the ` _ ` variable to a falsey value. The
428
+ then this operation completes the generator by setting the ` _ ` variable to a falsy value. The
406
429
` continue ` statement resumes execution at the top of the ` while ` statement, which will exit the loop
407
430
so that we continue execution at the statement following the loop.
408
431
@@ -463,7 +486,7 @@ current **protected region** from the stack and spin the `while` statement to ev
463
486
operation again in the next ** protected region** or at the function boundary.
464
487
465
488
## Handling a completed generator
466
- Once the generator has completed, the ` _ ` state variable will be falsey . As a result, the ` while `
489
+ Once the generator has completed, the ` _ ` state variable will be falsy . As a result, the ` while `
467
490
loop will terminate and hand control off to the final statement of the orchestration function,
468
491
which deals with how a completed generator is evaluated:
469
492
0 commit comments