Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

generator.md 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. # The `__generator` helper
  2. The `__generator` helper is a function designed to support TypeScript's down-level emit for
  3. async functions when targeting ES5 and earlier. But how, exactly, does it work?
  4. Here's the body of the `__generator` helper:
  5. ```js
  6. __generator = function (thisArg, body) {
  7. var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
  8. return { next: verb(0), "throw": verb(1), "return": verb(2) };
  9. function verb(n) { return function (v) { return step([n, v]); }; }
  10. function step(op) {
  11. if (f) throw new TypeError("Generator is already executing.");
  12. while (_) try {
  13. if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
  14. if (y = 0, t) op = [0, t.value];
  15. switch (op[0]) {
  16. case 0: case 1: t = op; break;
  17. case 4: _.label++; return { value: op[1], done: false };
  18. case 5: _.label++; y = op[1]; op = [0]; continue;
  19. case 7: op = _.ops.pop(); _.trys.pop(); continue;
  20. default:
  21. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
  22. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
  23. if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
  24. if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
  25. if (t[2]) _.ops.pop();
  26. _.trys.pop(); continue;
  27. }
  28. op = body.call(thisArg, _);
  29. } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
  30. if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
  31. }
  32. };
  33. ```
  34. And here's an example of it in use:
  35. ```ts
  36. // source
  37. async function func(x) {
  38. try {
  39. await x;
  40. }
  41. catch (e) {
  42. console.error(e);
  43. }
  44. finally {
  45. console.log("finally");
  46. }
  47. }
  48. // generated
  49. function func(x) {
  50. return __awaiter(this, void 0, void 0, function () {
  51. var e_1;
  52. return __generator(this, function (_a) {
  53. switch (_a.label) {
  54. case 0:
  55. _a.trys.push([0, 1, 3, 4]);
  56. return [4 /*yield*/, x];
  57. case 1:
  58. _a.sent();
  59. return [3 /*break*/, 4];
  60. case 2:
  61. e_1 = _a.sent();
  62. console.error(e_1);
  63. return [3 /*break*/, 4];
  64. case 3:
  65. console.log("finally");
  66. return [7 /*endfinally*/];
  67. case 4: return [2 /*return*/];
  68. }
  69. });
  70. });
  71. }
  72. ```
  73. There is a lot going on in this function, so the following will break down what each part of the
  74. `__generator` helper does and how it works.
  75. # Opcodes
  76. The `__generator` helper uses opcodes which represent various operations that are interpreted by
  77. the helper to affect its internal state. The following table lists the various opcodes, their
  78. arguments, and their purpose:
  79. | Opcode | Arguments | Purpose |
  80. |----------------|-----------|--------------------------------------------------------------------------------------------------------------------------------|
  81. | 0 (next) | *value* | Starts the generator, or resumes the generator with *value* as the result of the `AwaitExpression` where execution was paused. |
  82. | 1 (throw) | *value* | Resumes the generator, throwing *value* at `AwaitExpression` where execution was paused. |
  83. | 2 (return) | *value* | Exits the generator, executing any `finally` blocks starting at the `AwaitExpression` where execution was paused. |
  84. | 3 (break) | *label* | Performs an unconditional jump to the specified label, executing any `finally` between the current instruction and the label. |
  85. | 4 (yield) | *value* | Suspends the generator, setting the resume point at the next label and yielding the value. |
  86. | 5 (yieldstar) | *value* | Suspends the generator, setting the resume point at the next label and delegating operations to the supplied value. |
  87. | 6 (catch) | *error* | An internal instruction used to indicate an exception that was thrown from the body of the generator. |
  88. | 7 (endfinally) | | Exits a finally block, resuming any previous operation (such as a break, return, throw, etc.) |
  89. # State
  90. The `_`, `f`, `y`, and `t` variables make up the persistent state of the `__generator` function. Each variable
  91. has a specific purpose, as described in the following sections:
  92. ## The `_` variable
  93. The `__generator` helper must share state between its internal `step` orchestration function and
  94. the `body` function passed to the helper.
  95. ```ts
  96. var _ = {
  97. label: 0,
  98. sent: function() {
  99. if (t[0] & 1) // NOTE: true for `throw`, but not `next` or `catch`
  100. throw t[1];
  101. return sent[1];
  102. },
  103. trys: [],
  104. ops: []
  105. };
  106. ```
  107. The following table describes the members of the `_` state object and their purpose:
  108. | Name | Description |
  109. |---------|---------------------------------------------------------------------------------------------------------------------------|
  110. | `label` | Specifies the next switch case to execute in the `body` function. |
  111. | `sent` | Handles the completion result passed to the generator. |
  112. | `trys` | A stack of **Protected Regions**, which are 4-tuples that describe the labels that make up a `try..catch..finally` block. |
  113. | `ops` | A stack of pending operations used for `try..finally` blocks. |
  114. The `__generator` helper passes this state object to the `body` function for use with switching
  115. between switch cases in the body, handling completions from `AwaitExpression`, etc.
  116. ## The `f` variable
  117. The `f` variable indicates whether the generator is currently executing, to prevent re-entry of
  118. the same generator during its execution.
  119. ## The `y` variable
  120. The `y` variable stores the iterator passed to a `yieldstar` instruction to which operations should be delegated.
  121. ## The `t` variable
  122. The `t` variable is a temporary variable that stores one of the following values:
  123. - The completion value when resuming from a `yield` or `yield*`.
  124. - The error value for a catch block.
  125. - The current **Protected Region**.
  126. - The verb (`next`, `throw`, or `return` method) to delegate to the expression of a `yield*`.
  127. - The result of evaluating the verb delegated to the expression of a `yield*`.
  128. > NOTE: None of the above cases overlap.
  129. # Protected Regions
  130. A **Protected Region** is a region within the `body` function that indicates a
  131. `try..catch..finally` statement. It consists of a 4-tuple that contains 4 labels:
  132. | Offset | Description |
  133. |--------|-----------------------------------------------------------------------------------------|
  134. | 0 | *Required* The label that indicates the beginning of a `try..catch..finally` statement. |
  135. | 1 | *Optional* The label that indicates the beginning of a `catch` clause. |
  136. | 2 | *Optional* The label that indicates the beginning of a `finally` clause. |
  137. | 3 | *Required* The label that indicates the end of the `try..catch..finally` statement. |
  138. # The generator object
  139. The final step of the `__generator` helper is the allocation of an object that implements the
  140. `Generator` protocol, to be used by the `__awaiter` helper:
  141. ```ts
  142. return { next: verb(0), "throw": verb(1), "return": verb(2) };
  143. function verb(n) { return function (v) { return step([n, v]); }; }
  144. ```
  145. This object translates calls to `next`, `throw`, and `return` to the appropriate Opcodes and
  146. invokes the `step` orchestration function to continue execution. The `throw` and `return` method
  147. names are quoted to better support ES3.
  148. # Orchestration
  149. The `step` function is the main orechestration mechanism for the `__generator` helper. It
  150. interprets opcodes, handles **protected regions**, and communicates results back to the caller.
  151. Here's a closer look at the `step` function:
  152. ```ts
  153. function step(op) {
  154. if (f) throw new TypeError("Generator is already executing.");
  155. while (_) try {
  156. if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
  157. if (y = 0, t) op = [0, t.value];
  158. switch (op[0]) {
  159. case 0: case 1: t = op; break;
  160. case 4: _.label++; return { value: op[1], done: false };
  161. case 5: _.label++; y = op[1]; op = [0]; continue;
  162. case 7: op = _.ops.pop(); _.trys.pop(); continue;
  163. default:
  164. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
  165. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
  166. if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
  167. if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
  168. if (t[2]) _.ops.pop();
  169. _.trys.pop(); continue;
  170. }
  171. op = body.call(thisArg, _);
  172. } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
  173. if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
  174. }
  175. ```
  176. The main body of `step` exists in a `while` loop. This allows us to continually interpret
  177. operations until we have reached some completion value, be it a `return`, `await`, or `throw`.
  178. ## Preventing re-entry
  179. The first part of the `step` function is used as a check to prevent re-entry into a currently
  180. executing generator:
  181. ```ts
  182. if (f) throw new TypeError("Generator is already executing.");
  183. ```
  184. ## Running the generator
  185. The main body of the `step` function consists of a `while` loop which continues to evaluate
  186. instructions until the generator exits or is suspended:
  187. ```ts
  188. while (_) try ...
  189. ```
  190. When the generator has run to completion, the `_` state variable will be cleared, forcing the loop
  191. to exit.
  192. ## Evaluating the generator body.
  193. ```ts
  194. try {
  195. ...
  196. op = body.call(thisArg, _);
  197. }
  198. catch (e) {
  199. op = [6, e];
  200. y = 0;
  201. }
  202. finally {
  203. f = t = 0;
  204. }
  205. ```
  206. Depending on the current operation, we re-enter the generator body to start or continue execution.
  207. Here we invoke `body` with `thisArg` as the `this` binding and the `_` state object as the only
  208. argument. The result is a tuple that contains the next Opcode and argument.
  209. If evaluation of the body resulted in an exception, we convert this into an Opcode 6 ("catch")
  210. operation to be handled in the next spin of the `while` loop. We also clear the `y` variable in
  211. case it is set to ensure we are no longer delegating operations as the exception occurred in
  212. user code *outside* of, or at the function boundary of, the delegated iterator (otherwise the
  213. iterator would have handled the exception itself).
  214. After executing user code, we clear the `f` flag that indicates we are executing the generator,
  215. as well as the `t` temporary value so that we don't hold onto values sent to the generator for
  216. longer than necessary.
  217. Inside of the `try..finally` statement are a series of statements that are used to evaluate the
  218. operations of the transformed generator body.
  219. The first thing we do is mark the generator as executing:
  220. ```ts
  221. if (f = 1, ...)
  222. ```
  223. Despite the fact this expression is part of the head of an `if` statement, the comma operator
  224. causes it to be evaluated and the result thrown out. This is a minification added purely to
  225. reduce the overall footprint of the helper.
  226. ## Delegating `yield*`
  227. The first two statements of the `try..finally` statement handle delegation for `yield*`:
  228. ```ts
  229. if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
  230. if (y = 0, t) op = [0, t.value];
  231. ```
  232. If the `y` variable is set, and `y` has a `next`, `throw`, or `return` method (depending on the
  233. current operation), we invoke this method and store the return value (an IteratorResult) in `t`.
  234. If `t` indicates it is a yielded value (e.g. `t.done === false`), we return `t` to the caller.
  235. If `t` indicates it is a returned value (e.g. `t.done === true`), we mark the operation with the
  236. `next` Opcode, and the returned value.
  237. If `y` did not have the appropriate method, or `t` was a returned value, we reset `y` to a falsey
  238. value and continue processing the operation.
  239. ## Handling operations
  240. The various Opcodes are handled in the following switch statement:
  241. ```ts
  242. switch (op[0]) {
  243. case 0: case 1: t = op; break;
  244. case 4: _.label++; return { value: op[1], done: false };
  245. case 5: _.label++; y = op[1]; op = [0]; continue;
  246. case 7: op = _.ops.pop(); _.trys.pop(); continue;
  247. default:
  248. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
  249. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
  250. if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
  251. if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
  252. if (t[2]) _.ops.pop();
  253. _.trys.pop(); continue;
  254. }
  255. ```
  256. The following sections describe the various Opcodes:
  257. ### Opcode 0 ("next") and Opcode 1 ("throw")
  258. ```ts
  259. case 0: // next
  260. case 1: // throw
  261. t = op;
  262. break;
  263. ```
  264. Both Opcode 0 ("next") and Opcode 1 ("throw") have the same behavior. The current operation is
  265. stored in the `t` variable and the `body` function is invoked. The `body` function should call
  266. `_.sent()` which will evaluate the appropriate completion result.
  267. ### Opcode 4 ("yield")
  268. ```ts
  269. case 4: // yield
  270. _.label++;
  271. return { value: op[1], done: false };
  272. ```
  273. When we encounter Opcode 4 ("yield"), we increment the label by one to indicate the point at which
  274. the generator will resume execution. We then return an `IteratorResult` whose `value` is the
  275. yielded value, and `done` is `false`.
  276. ### Opcode 5 ("yieldstar")
  277. ```ts
  278. case 5: // yieldstar
  279. _.label++;
  280. y = op[1];
  281. op = [0];
  282. continue;
  283. ```
  284. When we receive Opcode 5 ("yieldstar"), we increment the label by one to indicate the point at which
  285. the generator will resume execution. We then store the iterator in `op[1]` in the `y` variable, and
  286. set the operation to delegate to Opcode 0 ("next") with no value. Finally, we continue execution at
  287. the top of the loop to start delegation.
  288. ### Opcode 7 ("endfinally")
  289. ```ts
  290. case 7:
  291. op = _.ops.pop();
  292. _.trys.pop();
  293. continue;
  294. ```
  295. Opcode 7 ("endfinally") indicates that we have hit the end of a `finally` clause, and that the last
  296. operation recorded before entering the `finally` block should be evaluated.
  297. ### Opcode 2 ("return"), Opcode 3 ("break"), and Opcode 6 ("catch")
  298. ```ts
  299. default:
  300. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
  301. _ = 0;
  302. continue;
  303. }
  304. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
  305. _.label = op[1];
  306. break;
  307. }
  308. if (op[0] === 6 && _.label < t[1]) {
  309. _.label = t[1];
  310. t = op;
  311. break;
  312. }
  313. if (t && _.label < t[2]) {
  314. _.label = t[2];
  315. _.ops.push(op);
  316. break;
  317. }
  318. if (t[2])
  319. _.ops.pop();
  320. _.trys.pop();
  321. continue;
  322. }
  323. ```
  324. The handling for Opcode 2 ("return"), Opcode 3 ("break") and Opcode 6 ("catch") is more
  325. complicated, as we must obey the specified runtime semantics of generators. The first line in this
  326. clause gets the current **Protected Region** if found and stores it in the `t` temp variable:
  327. ```ts
  328. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && ...) ...
  329. ```
  330. The remainder of this statement, as well as the following by several `if` statements test for more
  331. complex conditions. The first of these is the following:
  332. ```ts
  333. if (!(t = ...) && (op[0] === 6 || op[0] === 2)) {
  334. _ = 0;
  335. continue;
  336. }
  337. ```
  338. If we encounter an Opcode 6 ("catch") or Opcode 2 ("return"), and we are not in a protected region,
  339. then this operation completes the generator by setting the `_` variable to a falsey value. The
  340. `continue` statement resumes execution at the top of the `while` statement, which will exit the loop
  341. so that we continue execution at the statement following the loop.
  342. ```ts
  343. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
  344. _.label = op[1];
  345. break;
  346. }
  347. ```
  348. The `if` statement above handles Opcode 3 ("break") when we are either not in a **protected region**, or
  349. are performing an unconditional jump to a label inside of the current **protected region**. In this case
  350. we can unconditionally jump to the specified label.
  351. ```ts
  352. if (op[0] === 6 && _.label < t[1]) {
  353. _.label = t[1];
  354. t = op;
  355. break;
  356. }
  357. ```
  358. The `if` statement above handles Opcode 6 ("catch") when inside the `try` block of a **protected
  359. region**. In this case we jump to the `catch` block, if present. We replace the value of `t` with
  360. the operation so that the exception can be read as the first statement of the transformed `catch`
  361. clause of the transformed generator body.
  362. ```ts
  363. if (t && _.label < t[2]) {
  364. _.label = t[2];
  365. _.ops.push(op);
  366. break;
  367. }
  368. ```
  369. This `if` statement handles all Opcodes when in a **protected region** with a `finally` clause.
  370. As long as we are not already inside the `finally` clause, we jump to the `finally` clause and
  371. push the pending operation onto the `_.ops` stack. This allows us to resume execution of the
  372. pending operation once we have completed execution of the `finally` clause, as long as it does not
  373. supersede this operation with its own completion value.
  374. ```ts
  375. if (t[2])
  376. _.ops.pop();
  377. ```
  378. Any other completion value inside of a `finally` clause will supersede the pending completion value
  379. from the `try` or `catch` clauses. The above `if` statement pops the pending completion from the
  380. stack.
  381. ```ts
  382. _.trys.pop();
  383. continue;
  384. ```
  385. The remaining statements handle the point at which we exit a **protected region**. Here we pop the
  386. current **protected region** from the stack and spin the `while` statement to evaluate the current
  387. operation again in the next **protected region** or at the function boundary.
  388. ## Handling a completed generator
  389. Once the generator has completed, the `_` state variable will be falsey. As a result, the `while`
  390. loop will terminate and hand control off to the final statement of the orchestration function,
  391. which deals with how a completed generator is evaluated:
  392. ```ts
  393. if (op[0] & 5)
  394. throw op[1];
  395. return { value: op[0] ? op[1] : void 0, done: true };
  396. ```
  397. If the caller calls `throw` on the generator it will send Opcode 1 ("throw"). If an exception
  398. is uncaught within the body of the generator, it will send Opcode 6 ("catch"). As the generator has
  399. completed, it throws the exception. Both of these cases are caught by the bitmask `5`, which does
  400. not collide with the only two other valid completion Opcodes.
  401. If the caller calls `next` on the generator, it will send Opcode 0 ("next"). As the generator has
  402. completed, it returns an `IteratorResult` where `value` is `undefined` and `done` is true.
  403. If the caller calls `return` on the generator, it will send Opcode 2 ("return"). As the generator
  404. has completed, it returns an `IteratorResult` where `value` is the value provided to `return`, and
  405. `done` is true.