emit.js 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  1. /**
  2. * Copyright (c) 2014, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * https://raw.github.com/facebook/regenerator/master/LICENSE file. An
  7. * additional grant of patent rights can be found in the PATENTS file in
  8. * the same directory.
  9. */
  10. var assert = require("assert");
  11. var types = require("ast-types");
  12. var isArray = types.builtInTypes.array;
  13. var b = types.builders;
  14. var n = types.namedTypes;
  15. var leap = require("./leap");
  16. var meta = require("./meta");
  17. var util = require("./util");
  18. var hasOwn = Object.prototype.hasOwnProperty;
  19. function Emitter(contextId) {
  20. assert.ok(this instanceof Emitter);
  21. n.Identifier.assert(contextId);
  22. Object.defineProperties(this, {
  23. // In order to make sure the context object does not collide with
  24. // anything in the local scope, we might have to rename it, so we
  25. // refer to it symbolically instead of just assuming that it will be
  26. // called "context".
  27. contextId: { value: contextId },
  28. // An append-only list of Statements that grows each time this.emit is
  29. // called.
  30. listing: { value: [] },
  31. // A sparse array whose keys correspond to locations in this.listing
  32. // that have been marked as branch/jump targets.
  33. marked: { value: [true] },
  34. // The last location will be marked when this.getDispatchLoop is
  35. // called.
  36. finalLoc: { value: loc() },
  37. // A list of all leap.TryEntry statements emitted.
  38. tryEntries: { value: [] }
  39. });
  40. // The .leapManager property needs to be defined by a separate
  41. // defineProperties call so that .finalLoc will be visible to the
  42. // leap.LeapManager constructor.
  43. Object.defineProperties(this, {
  44. // Each time we evaluate the body of a loop, we tell this.leapManager
  45. // to enter a nested loop context that determines the meaning of break
  46. // and continue statements therein.
  47. leapManager: { value: new leap.LeapManager(this) }
  48. });
  49. }
  50. var Ep = Emitter.prototype;
  51. exports.Emitter = Emitter;
  52. // Offsets into this.listing that could be used as targets for branches or
  53. // jumps are represented as numeric Literal nodes. This representation has
  54. // the amazingly convenient benefit of allowing the exact value of the
  55. // location to be determined at any time, even after generating code that
  56. // refers to the location.
  57. function loc() {
  58. return b.literal(-1);
  59. }
  60. // Sets the exact value of the given location to the offset of the next
  61. // Statement emitted.
  62. Ep.mark = function(loc) {
  63. n.Literal.assert(loc);
  64. var index = this.listing.length;
  65. if (loc.value === -1) {
  66. loc.value = index;
  67. } else {
  68. // Locations can be marked redundantly, but their values cannot change
  69. // once set the first time.
  70. assert.strictEqual(loc.value, index);
  71. }
  72. this.marked[index] = true;
  73. return loc;
  74. };
  75. Ep.emit = function(node) {
  76. if (n.Expression.check(node))
  77. node = b.expressionStatement(node);
  78. n.Statement.assert(node);
  79. this.listing.push(node);
  80. };
  81. // Shorthand for emitting assignment statements. This will come in handy
  82. // for assignments to temporary variables.
  83. Ep.emitAssign = function(lhs, rhs) {
  84. this.emit(this.assign(lhs, rhs));
  85. return lhs;
  86. };
  87. // Shorthand for an assignment statement.
  88. Ep.assign = function(lhs, rhs) {
  89. return b.expressionStatement(
  90. b.assignmentExpression("=", lhs, rhs));
  91. };
  92. // Convenience function for generating expressions like context.next,
  93. // context.sent, and context.rval.
  94. Ep.contextProperty = function(name, computed) {
  95. return b.memberExpression(
  96. this.contextId,
  97. computed ? b.literal(name) : b.identifier(name),
  98. !!computed
  99. );
  100. };
  101. var volatileContextPropertyNames = {
  102. prev: true,
  103. next: true,
  104. sent: true,
  105. rval: true
  106. };
  107. // A "volatile" context property is a MemberExpression like context.sent
  108. // that should probably be stored in a temporary variable when there's a
  109. // possibility the property will get overwritten.
  110. Ep.isVolatileContextProperty = function(expr) {
  111. if (n.MemberExpression.check(expr)) {
  112. if (expr.computed) {
  113. // If it's a computed property such as context[couldBeAnything],
  114. // assume the worst in terms of volatility.
  115. return true;
  116. }
  117. if (n.Identifier.check(expr.object) &&
  118. n.Identifier.check(expr.property) &&
  119. expr.object.name === this.contextId.name &&
  120. hasOwn.call(volatileContextPropertyNames,
  121. expr.property.name)) {
  122. return true;
  123. }
  124. }
  125. return false;
  126. };
  127. // Shorthand for setting context.rval and jumping to `context.stop()`.
  128. Ep.stop = function(rval) {
  129. if (rval) {
  130. this.setReturnValue(rval);
  131. }
  132. this.jump(this.finalLoc);
  133. };
  134. Ep.setReturnValue = function(valuePath) {
  135. n.Expression.assert(valuePath.value);
  136. this.emitAssign(
  137. this.contextProperty("rval"),
  138. this.explodeExpression(valuePath)
  139. );
  140. };
  141. Ep.clearPendingException = function(tryLoc, assignee) {
  142. n.Literal.assert(tryLoc);
  143. var catchCall = b.callExpression(
  144. this.contextProperty("catch", true),
  145. [tryLoc]
  146. );
  147. if (assignee) {
  148. this.emitAssign(assignee, catchCall);
  149. } else {
  150. this.emit(catchCall);
  151. }
  152. };
  153. // Emits code for an unconditional jump to the given location, even if the
  154. // exact value of the location is not yet known.
  155. Ep.jump = function(toLoc) {
  156. this.emitAssign(this.contextProperty("next"), toLoc);
  157. this.emit(b.breakStatement());
  158. };
  159. // Conditional jump.
  160. Ep.jumpIf = function(test, toLoc) {
  161. n.Expression.assert(test);
  162. n.Literal.assert(toLoc);
  163. this.emit(b.ifStatement(
  164. test,
  165. b.blockStatement([
  166. this.assign(this.contextProperty("next"), toLoc),
  167. b.breakStatement()
  168. ])
  169. ));
  170. };
  171. // Conditional jump, with the condition negated.
  172. Ep.jumpIfNot = function(test, toLoc) {
  173. n.Expression.assert(test);
  174. n.Literal.assert(toLoc);
  175. var negatedTest;
  176. if (n.UnaryExpression.check(test) &&
  177. test.operator === "!") {
  178. // Avoid double negation.
  179. negatedTest = test.argument;
  180. } else {
  181. negatedTest = b.unaryExpression("!", test);
  182. }
  183. this.emit(b.ifStatement(
  184. negatedTest,
  185. b.blockStatement([
  186. this.assign(this.contextProperty("next"), toLoc),
  187. b.breakStatement()
  188. ])
  189. ));
  190. };
  191. // Returns a unique MemberExpression that can be used to store and
  192. // retrieve temporary values. Since the object of the member expression is
  193. // the context object, which is presumed to coexist peacefully with all
  194. // other local variables, and since we just increment `nextTempId`
  195. // monotonically, uniqueness is assured.
  196. var nextTempId = 0;
  197. Ep.makeTempVar = function() {
  198. return this.contextProperty("t" + nextTempId++);
  199. };
  200. Ep.getContextFunction = function(id) {
  201. var func = b.functionExpression(
  202. id || null/*Anonymous*/,
  203. [this.contextId],
  204. b.blockStatement([this.getDispatchLoop()]),
  205. false, // Not a generator anymore!
  206. false // Nor an expression.
  207. );
  208. func._aliasFunction = true;
  209. return func;
  210. };
  211. // Turns this.listing into a loop of the form
  212. //
  213. // while (1) switch (context.next) {
  214. // case 0:
  215. // ...
  216. // case n:
  217. // return context.stop();
  218. // }
  219. //
  220. // Each marked location in this.listing will correspond to one generated
  221. // case statement.
  222. Ep.getDispatchLoop = function() {
  223. var self = this;
  224. var cases = [];
  225. var current;
  226. // If we encounter a break, continue, or return statement in a switch
  227. // case, we can skip the rest of the statements until the next case.
  228. var alreadyEnded = false;
  229. self.listing.forEach(function(stmt, i) {
  230. if (self.marked.hasOwnProperty(i)) {
  231. cases.push(b.switchCase(
  232. b.literal(i),
  233. current = []));
  234. alreadyEnded = false;
  235. }
  236. if (!alreadyEnded) {
  237. current.push(stmt);
  238. if (isSwitchCaseEnder(stmt))
  239. alreadyEnded = true;
  240. }
  241. });
  242. // Now that we know how many statements there will be in this.listing,
  243. // we can finally resolve this.finalLoc.value.
  244. this.finalLoc.value = this.listing.length;
  245. cases.push(
  246. b.switchCase(this.finalLoc, [
  247. // Intentionally fall through to the "end" case...
  248. ]),
  249. // So that the runtime can jump to the final location without having
  250. // to know its offset, we provide the "end" case as a synonym.
  251. b.switchCase(b.literal("end"), [
  252. // This will check/clear both context.thrown and context.rval.
  253. b.returnStatement(
  254. b.callExpression(this.contextProperty("stop"), [])
  255. )
  256. ])
  257. );
  258. return b.whileStatement(
  259. b.literal(1),
  260. b.switchStatement(
  261. b.assignmentExpression(
  262. "=",
  263. this.contextProperty("prev"),
  264. this.contextProperty("next")
  265. ),
  266. cases
  267. )
  268. );
  269. };
  270. // See comment above re: alreadyEnded.
  271. function isSwitchCaseEnder(stmt) {
  272. return n.BreakStatement.check(stmt)
  273. || n.ContinueStatement.check(stmt)
  274. || n.ReturnStatement.check(stmt)
  275. || n.ThrowStatement.check(stmt);
  276. }
  277. Ep.getTryLocsList = function() {
  278. if (this.tryEntries.length === 0) {
  279. // To avoid adding a needless [] to the majority of runtime.wrap
  280. // argument lists, force the caller to handle this case specially.
  281. return null;
  282. }
  283. var lastLocValue = 0;
  284. return b.arrayExpression(
  285. this.tryEntries.map(function(tryEntry) {
  286. var thisLocValue = tryEntry.firstLoc.value;
  287. assert.ok(thisLocValue >= lastLocValue, "try entries out of order");
  288. lastLocValue = thisLocValue;
  289. var ce = tryEntry.catchEntry;
  290. var fe = tryEntry.finallyEntry;
  291. var locs = [
  292. tryEntry.firstLoc,
  293. // The null here makes a hole in the array.
  294. ce ? ce.firstLoc : null
  295. ];
  296. if (fe) {
  297. locs[2] = fe.firstLoc;
  298. locs[3] = fe.afterLoc;
  299. }
  300. return b.arrayExpression(locs);
  301. })
  302. );
  303. };
  304. // All side effects must be realized in order.
  305. // If any subexpression harbors a leap, all subexpressions must be
  306. // neutered of side effects.
  307. // No destructive modification of AST nodes.
  308. Ep.explode = function(path, ignoreResult) {
  309. assert.ok(path instanceof types.NodePath);
  310. var node = path.value;
  311. var self = this;
  312. n.Node.assert(node);
  313. if (n.Statement.check(node))
  314. return self.explodeStatement(path);
  315. if (n.Expression.check(node))
  316. return self.explodeExpression(path, ignoreResult);
  317. if (n.Declaration.check(node))
  318. throw getDeclError(node);
  319. switch (node.type) {
  320. case "Program":
  321. return path.get("body").map(
  322. self.explodeStatement,
  323. self
  324. );
  325. case "VariableDeclarator":
  326. throw getDeclError(node);
  327. // These node types should be handled by their parent nodes
  328. // (ObjectExpression, SwitchStatement, and TryStatement, respectively).
  329. case "Property":
  330. case "SwitchCase":
  331. case "CatchClause":
  332. throw new Error(
  333. node.type + " nodes should be handled by their parents");
  334. default:
  335. throw new Error(
  336. "unknown Node of type " +
  337. JSON.stringify(node.type));
  338. }
  339. };
  340. function getDeclError(node) {
  341. return new Error(
  342. "all declarations should have been transformed into " +
  343. "assignments before the Exploder began its work: " +
  344. JSON.stringify(node));
  345. }
  346. Ep.explodeStatement = function(path, labelId) {
  347. assert.ok(path instanceof types.NodePath);
  348. var stmt = path.value;
  349. var self = this;
  350. n.Statement.assert(stmt);
  351. if (labelId) {
  352. n.Identifier.assert(labelId);
  353. } else {
  354. labelId = null;
  355. }
  356. // Explode BlockStatement nodes even if they do not contain a yield,
  357. // because we don't want or need the curly braces.
  358. if (n.BlockStatement.check(stmt)) {
  359. return path.get("body").each(
  360. self.explodeStatement,
  361. self
  362. );
  363. }
  364. if (!meta.containsLeap(stmt)) {
  365. // Technically we should be able to avoid emitting the statement
  366. // altogether if !meta.hasSideEffects(stmt), but that leads to
  367. // confusing generated code (for instance, `while (true) {}` just
  368. // disappears) and is probably a more appropriate job for a dedicated
  369. // dead code elimination pass.
  370. self.emit(stmt);
  371. return;
  372. }
  373. switch (stmt.type) {
  374. case "ExpressionStatement":
  375. self.explodeExpression(path.get("expression"), true);
  376. break;
  377. case "LabeledStatement":
  378. var after = loc();
  379. // Did you know you can break from any labeled block statement or
  380. // control structure? Well, you can! Note: when a labeled loop is
  381. // encountered, the leap.LabeledEntry created here will immediately
  382. // enclose a leap.LoopEntry on the leap manager's stack, and both
  383. // entries will have the same label. Though this works just fine, it
  384. // may seem a bit redundant. In theory, we could check here to
  385. // determine if stmt knows how to handle its own label; for example,
  386. // stmt happens to be a WhileStatement and so we know it's going to
  387. // establish its own LoopEntry when we explode it (below). Then this
  388. // LabeledEntry would be unnecessary. Alternatively, we might be
  389. // tempted not to pass stmt.label down into self.explodeStatement,
  390. // because we've handled the label here, but that's a mistake because
  391. // labeled loops may contain labeled continue statements, which is not
  392. // something we can handle in this generic case. All in all, I think a
  393. // little redundancy greatly simplifies the logic of this case, since
  394. // it's clear that we handle all possible LabeledStatements correctly
  395. // here, regardless of whether they interact with the leap manager
  396. // themselves. Also remember that labels and break/continue-to-label
  397. // statements are rare, and all of this logic happens at transform
  398. // time, so it has no additional runtime cost.
  399. self.leapManager.withEntry(
  400. new leap.LabeledEntry(after, stmt.label),
  401. function() {
  402. self.explodeStatement(path.get("body"), stmt.label);
  403. }
  404. );
  405. self.mark(after);
  406. break;
  407. case "WhileStatement":
  408. var before = loc();
  409. var after = loc();
  410. self.mark(before);
  411. self.jumpIfNot(self.explodeExpression(path.get("test")), after);
  412. self.leapManager.withEntry(
  413. new leap.LoopEntry(after, before, labelId),
  414. function() { self.explodeStatement(path.get("body")); }
  415. );
  416. self.jump(before);
  417. self.mark(after);
  418. break;
  419. case "DoWhileStatement":
  420. var first = loc();
  421. var test = loc();
  422. var after = loc();
  423. self.mark(first);
  424. self.leapManager.withEntry(
  425. new leap.LoopEntry(after, test, labelId),
  426. function() { self.explode(path.get("body")); }
  427. );
  428. self.mark(test);
  429. self.jumpIf(self.explodeExpression(path.get("test")), first);
  430. self.mark(after);
  431. break;
  432. case "ForStatement":
  433. var head = loc();
  434. var update = loc();
  435. var after = loc();
  436. if (stmt.init) {
  437. // We pass true here to indicate that if stmt.init is an expression
  438. // then we do not care about its result.
  439. self.explode(path.get("init"), true);
  440. }
  441. self.mark(head);
  442. if (stmt.test) {
  443. self.jumpIfNot(self.explodeExpression(path.get("test")), after);
  444. } else {
  445. // No test means continue unconditionally.
  446. }
  447. self.leapManager.withEntry(
  448. new leap.LoopEntry(after, update, labelId),
  449. function() { self.explodeStatement(path.get("body")); }
  450. );
  451. self.mark(update);
  452. if (stmt.update) {
  453. // We pass true here to indicate that if stmt.update is an
  454. // expression then we do not care about its result.
  455. self.explode(path.get("update"), true);
  456. }
  457. self.jump(head);
  458. self.mark(after);
  459. break;
  460. case "ForInStatement":
  461. n.Identifier.assert(stmt.left);
  462. var head = loc();
  463. var after = loc();
  464. var keyIterNextFn = self.makeTempVar();
  465. self.emitAssign(
  466. keyIterNextFn,
  467. b.callExpression(
  468. util.runtimeProperty("keys"),
  469. [self.explodeExpression(path.get("right"))]
  470. )
  471. );
  472. self.mark(head);
  473. var keyInfoTmpVar = self.makeTempVar();
  474. self.jumpIf(
  475. b.memberExpression(
  476. b.assignmentExpression(
  477. "=",
  478. keyInfoTmpVar,
  479. b.callExpression(keyIterNextFn, [])
  480. ),
  481. b.identifier("done"),
  482. false
  483. ),
  484. after
  485. );
  486. self.emitAssign(
  487. stmt.left,
  488. b.memberExpression(
  489. keyInfoTmpVar,
  490. b.identifier("value"),
  491. false
  492. )
  493. );
  494. self.leapManager.withEntry(
  495. new leap.LoopEntry(after, head, labelId),
  496. function() { self.explodeStatement(path.get("body")); }
  497. );
  498. self.jump(head);
  499. self.mark(after);
  500. break;
  501. case "BreakStatement":
  502. self.emitAbruptCompletion({
  503. type: "break",
  504. target: self.leapManager.getBreakLoc(stmt.label)
  505. });
  506. break;
  507. case "ContinueStatement":
  508. self.emitAbruptCompletion({
  509. type: "continue",
  510. target: self.leapManager.getContinueLoc(stmt.label)
  511. });
  512. break;
  513. case "SwitchStatement":
  514. // Always save the discriminant into a temporary variable in case the
  515. // test expressions overwrite values like context.sent.
  516. var disc = self.emitAssign(
  517. self.makeTempVar(),
  518. self.explodeExpression(path.get("discriminant"))
  519. );
  520. var after = loc();
  521. var defaultLoc = loc();
  522. var condition = defaultLoc;
  523. var caseLocs = [];
  524. // If there are no cases, .cases might be undefined.
  525. var cases = stmt.cases || [];
  526. for (var i = cases.length - 1; i >= 0; --i) {
  527. var c = cases[i];
  528. n.SwitchCase.assert(c);
  529. if (c.test) {
  530. condition = b.conditionalExpression(
  531. b.binaryExpression("===", disc, c.test),
  532. caseLocs[i] = loc(),
  533. condition
  534. );
  535. } else {
  536. caseLocs[i] = defaultLoc;
  537. }
  538. }
  539. self.jump(self.explodeExpression(
  540. new types.NodePath(condition, path, "discriminant")
  541. ));
  542. self.leapManager.withEntry(
  543. new leap.SwitchEntry(after),
  544. function() {
  545. path.get("cases").each(function(casePath) {
  546. var c = casePath.value;
  547. var i = casePath.name;
  548. self.mark(caseLocs[i]);
  549. casePath.get("consequent").each(
  550. self.explodeStatement,
  551. self
  552. );
  553. });
  554. }
  555. );
  556. self.mark(after);
  557. if (defaultLoc.value === -1) {
  558. self.mark(defaultLoc);
  559. assert.strictEqual(after.value, defaultLoc.value);
  560. }
  561. break;
  562. case "IfStatement":
  563. var elseLoc = stmt.alternate && loc();
  564. var after = loc();
  565. self.jumpIfNot(
  566. self.explodeExpression(path.get("test")),
  567. elseLoc || after
  568. );
  569. self.explodeStatement(path.get("consequent"));
  570. if (elseLoc) {
  571. self.jump(after);
  572. self.mark(elseLoc);
  573. self.explodeStatement(path.get("alternate"));
  574. }
  575. self.mark(after);
  576. break;
  577. case "ReturnStatement":
  578. self.emitAbruptCompletion({
  579. type: "return",
  580. value: self.explodeExpression(path.get("argument"))
  581. });
  582. break;
  583. case "WithStatement":
  584. throw new Error(
  585. node.type + " not supported in generator functions.");
  586. case "TryStatement":
  587. var after = loc();
  588. var handler = stmt.handler;
  589. if (!handler && stmt.handlers) {
  590. handler = stmt.handlers[0] || null;
  591. }
  592. var catchLoc = handler && loc();
  593. var catchEntry = catchLoc && new leap.CatchEntry(
  594. catchLoc,
  595. handler.param
  596. );
  597. var finallyLoc = stmt.finalizer && loc();
  598. var finallyEntry = finallyLoc &&
  599. new leap.FinallyEntry(finallyLoc, after);
  600. var tryEntry = new leap.TryEntry(
  601. self.getUnmarkedCurrentLoc(),
  602. catchEntry,
  603. finallyEntry
  604. );
  605. self.tryEntries.push(tryEntry);
  606. self.updateContextPrevLoc(tryEntry.firstLoc);
  607. self.leapManager.withEntry(tryEntry, function() {
  608. self.explodeStatement(path.get("block"));
  609. if (catchLoc) {
  610. if (finallyLoc) {
  611. // If we have both a catch block and a finally block, then
  612. // because we emit the catch block first, we need to jump over
  613. // it to the finally block.
  614. self.jump(finallyLoc);
  615. } else {
  616. // If there is no finally block, then we need to jump over the
  617. // catch block to the fall-through location.
  618. self.jump(after);
  619. }
  620. self.updateContextPrevLoc(self.mark(catchLoc));
  621. var bodyPath = path.get("handler", "body");
  622. var safeParam = self.makeTempVar();
  623. self.clearPendingException(tryEntry.firstLoc, safeParam);
  624. var catchScope = bodyPath.scope;
  625. var catchParamName = handler.param.name;
  626. n.CatchClause.assert(catchScope.node);
  627. assert.strictEqual(catchScope.lookup(catchParamName), catchScope);
  628. types.visit(bodyPath, {
  629. visitIdentifier: function(path) {
  630. if (util.isReference(path, catchParamName) &&
  631. path.scope.lookup(catchParamName) === catchScope) {
  632. return safeParam;
  633. }
  634. this.traverse(path);
  635. },
  636. visitFunction: function(path) {
  637. if (path.scope.declares(catchParamName)) {
  638. // Don't descend into nested scopes that shadow the catch
  639. // parameter with their own declarations. This isn't
  640. // logically necessary because of the path.scope.lookup we
  641. // do in visitIdentifier, but it saves time.
  642. return false;
  643. }
  644. this.traverse(path);
  645. }
  646. });
  647. self.leapManager.withEntry(catchEntry, function() {
  648. self.explodeStatement(bodyPath);
  649. });
  650. }
  651. if (finallyLoc) {
  652. self.updateContextPrevLoc(self.mark(finallyLoc));
  653. self.leapManager.withEntry(finallyEntry, function() {
  654. self.explodeStatement(path.get("finalizer"));
  655. });
  656. self.emit(b.returnStatement(b.callExpression(
  657. self.contextProperty("finish"),
  658. [finallyEntry.firstLoc]
  659. )));
  660. }
  661. });
  662. self.mark(after);
  663. break;
  664. case "ThrowStatement":
  665. self.emit(b.throwStatement(
  666. self.explodeExpression(path.get("argument"))
  667. ));
  668. break;
  669. default:
  670. throw new Error(
  671. "unknown Statement of type " +
  672. JSON.stringify(stmt.type));
  673. }
  674. };
  675. Ep.emitAbruptCompletion = function(record) {
  676. if (!isValidCompletion(record)) {
  677. assert.ok(
  678. false,
  679. "invalid completion record: " +
  680. JSON.stringify(record)
  681. );
  682. }
  683. assert.notStrictEqual(
  684. record.type, "normal",
  685. "normal completions are not abrupt"
  686. );
  687. var abruptArgs = [b.literal(record.type)];
  688. if (record.type === "break" ||
  689. record.type === "continue") {
  690. n.Literal.assert(record.target);
  691. abruptArgs[1] = record.target;
  692. } else if (record.type === "return" ||
  693. record.type === "throw") {
  694. if (record.value) {
  695. n.Expression.assert(record.value);
  696. abruptArgs[1] = record.value;
  697. }
  698. }
  699. this.emit(
  700. b.returnStatement(
  701. b.callExpression(
  702. this.contextProperty("abrupt"),
  703. abruptArgs
  704. )
  705. )
  706. );
  707. };
  708. function isValidCompletion(record) {
  709. var type = record.type;
  710. if (type === "normal") {
  711. return !hasOwn.call(record, "target");
  712. }
  713. if (type === "break" ||
  714. type === "continue") {
  715. return !hasOwn.call(record, "value")
  716. && n.Literal.check(record.target);
  717. }
  718. if (type === "return" ||
  719. type === "throw") {
  720. return hasOwn.call(record, "value")
  721. && !hasOwn.call(record, "target");
  722. }
  723. return false;
  724. }
  725. // Not all offsets into emitter.listing are potential jump targets. For
  726. // example, execution typically falls into the beginning of a try block
  727. // without jumping directly there. This method returns the current offset
  728. // without marking it, so that a switch case will not necessarily be
  729. // generated for this offset (I say "not necessarily" because the same
  730. // location might end up being marked in the process of emitting other
  731. // statements). There's no logical harm in marking such locations as jump
  732. // targets, but minimizing the number of switch cases keeps the generated
  733. // code shorter.
  734. Ep.getUnmarkedCurrentLoc = function() {
  735. return b.literal(this.listing.length);
  736. };
  737. // The context.prev property takes the value of context.next whenever we
  738. // evaluate the switch statement discriminant, which is generally good
  739. // enough for tracking the last location we jumped to, but sometimes
  740. // context.prev needs to be more precise, such as when we fall
  741. // successfully out of a try block and into a finally block without
  742. // jumping. This method exists to update context.prev to the freshest
  743. // available location. If we were implementing a full interpreter, we
  744. // would know the location of the current instruction with complete
  745. // precision at all times, but we don't have that luxury here, as it would
  746. // be costly and verbose to set context.prev before every statement.
  747. Ep.updateContextPrevLoc = function(loc) {
  748. if (loc) {
  749. n.Literal.assert(loc);
  750. if (loc.value === -1) {
  751. // If an uninitialized location literal was passed in, set its value
  752. // to the current this.listing.length.
  753. loc.value = this.listing.length;
  754. } else {
  755. // Otherwise assert that the location matches the current offset.
  756. assert.strictEqual(loc.value, this.listing.length);
  757. }
  758. } else {
  759. loc = this.getUnmarkedCurrentLoc();
  760. }
  761. // Make sure context.prev is up to date in case we fell into this try
  762. // statement without jumping to it. TODO Consider avoiding this
  763. // assignment when we know control must have jumped here.
  764. this.emitAssign(this.contextProperty("prev"), loc);
  765. };
  766. Ep.explodeExpression = function(path, ignoreResult) {
  767. assert.ok(path instanceof types.NodePath);
  768. var expr = path.value;
  769. if (expr) {
  770. n.Expression.assert(expr);
  771. } else {
  772. return expr;
  773. }
  774. var self = this;
  775. var result; // Used optionally by several cases below.
  776. function finish(expr) {
  777. n.Expression.assert(expr);
  778. if (ignoreResult) {
  779. self.emit(expr);
  780. } else {
  781. return expr;
  782. }
  783. }
  784. // If the expression does not contain a leap, then we either emit the
  785. // expression as a standalone statement or return it whole.
  786. if (!meta.containsLeap(expr)) {
  787. return finish(expr);
  788. }
  789. // If any child contains a leap (such as a yield or labeled continue or
  790. // break statement), then any sibling subexpressions will almost
  791. // certainly have to be exploded in order to maintain the order of their
  792. // side effects relative to the leaping child(ren).
  793. var hasLeapingChildren = meta.containsLeap.onlyChildren(expr);
  794. // In order to save the rest of explodeExpression from a combinatorial
  795. // trainwreck of special cases, explodeViaTempVar is responsible for
  796. // deciding when a subexpression needs to be "exploded," which is my
  797. // very technical term for emitting the subexpression as an assignment
  798. // to a temporary variable and the substituting the temporary variable
  799. // for the original subexpression. Think of exploded view diagrams, not
  800. // Michael Bay movies. The point of exploding subexpressions is to
  801. // control the precise order in which the generated code realizes the
  802. // side effects of those subexpressions.
  803. function explodeViaTempVar(tempVar, childPath, ignoreChildResult) {
  804. assert.ok(childPath instanceof types.NodePath);
  805. assert.ok(
  806. !ignoreChildResult || !tempVar,
  807. "Ignoring the result of a child expression but forcing it to " +
  808. "be assigned to a temporary variable?"
  809. );
  810. var result = self.explodeExpression(childPath, ignoreChildResult);
  811. if (ignoreChildResult) {
  812. // Side effects already emitted above.
  813. } else if (tempVar || (hasLeapingChildren &&
  814. (self.isVolatileContextProperty(result) ||
  815. meta.hasSideEffects(result)))) {
  816. // If tempVar was provided, then the result will always be assigned
  817. // to it, even if the result does not otherwise need to be assigned
  818. // to a temporary variable. When no tempVar is provided, we have
  819. // the flexibility to decide whether a temporary variable is really
  820. // necessary. In general, temporary assignment is required only
  821. // when some other child contains a leap and the child in question
  822. // is a context property like $ctx.sent that might get overwritten
  823. // or an expression with side effects that need to occur in proper
  824. // sequence relative to the leap.
  825. result = self.emitAssign(
  826. tempVar || self.makeTempVar(),
  827. result
  828. );
  829. }
  830. return result;
  831. }
  832. // If ignoreResult is true, then we must take full responsibility for
  833. // emitting the expression with all its side effects, and we should not
  834. // return a result.
  835. switch (expr.type) {
  836. case "MemberExpression":
  837. return finish(b.memberExpression(
  838. self.explodeExpression(path.get("object")),
  839. expr.computed
  840. ? explodeViaTempVar(null, path.get("property"))
  841. : expr.property,
  842. expr.computed
  843. ));
  844. case "CallExpression":
  845. var oldCalleePath = path.get("callee");
  846. var newCallee = self.explodeExpression(oldCalleePath);
  847. // If the callee was not previously a MemberExpression, then the
  848. // CallExpression was "unqualified," meaning its `this` object should
  849. // be the global object. If the exploded expression has become a
  850. // MemberExpression, then we need to force it to be unqualified by
  851. // using the (0, object.property)(...) trick; otherwise, it will
  852. // receive the object of the MemberExpression as its `this` object.
  853. if (!n.MemberExpression.check(oldCalleePath.node) &&
  854. n.MemberExpression.check(newCallee)) {
  855. newCallee = b.sequenceExpression([
  856. b.literal(0),
  857. newCallee
  858. ]);
  859. }
  860. return finish(b.callExpression(
  861. newCallee,
  862. path.get("arguments").map(function(argPath) {
  863. return explodeViaTempVar(null, argPath);
  864. })
  865. ));
  866. case "NewExpression":
  867. return finish(b.newExpression(
  868. explodeViaTempVar(null, path.get("callee")),
  869. path.get("arguments").map(function(argPath) {
  870. return explodeViaTempVar(null, argPath);
  871. })
  872. ));
  873. case "ObjectExpression":
  874. return finish(b.objectExpression(
  875. path.get("properties").map(function(propPath) {
  876. return b.property(
  877. propPath.value.kind,
  878. propPath.value.key,
  879. explodeViaTempVar(null, propPath.get("value"))
  880. );
  881. })
  882. ));
  883. case "ArrayExpression":
  884. return finish(b.arrayExpression(
  885. path.get("elements").map(function(elemPath) {
  886. return explodeViaTempVar(null, elemPath);
  887. })
  888. ));
  889. case "SequenceExpression":
  890. var lastIndex = expr.expressions.length - 1;
  891. path.get("expressions").each(function(exprPath) {
  892. if (exprPath.name === lastIndex) {
  893. result = self.explodeExpression(exprPath, ignoreResult);
  894. } else {
  895. self.explodeExpression(exprPath, true);
  896. }
  897. });
  898. return result;
  899. case "LogicalExpression":
  900. var after = loc();
  901. if (!ignoreResult) {
  902. result = self.makeTempVar();
  903. }
  904. var left = explodeViaTempVar(result, path.get("left"));
  905. if (expr.operator === "&&") {
  906. self.jumpIfNot(left, after);
  907. } else {
  908. assert.strictEqual(expr.operator, "||");
  909. self.jumpIf(left, after);
  910. }
  911. explodeViaTempVar(result, path.get("right"), ignoreResult);
  912. self.mark(after);
  913. return result;
  914. case "ConditionalExpression":
  915. var elseLoc = loc();
  916. var after = loc();
  917. var test = self.explodeExpression(path.get("test"));
  918. self.jumpIfNot(test, elseLoc);
  919. if (!ignoreResult) {
  920. result = self.makeTempVar();
  921. }
  922. explodeViaTempVar(result, path.get("consequent"), ignoreResult);
  923. self.jump(after);
  924. self.mark(elseLoc);
  925. explodeViaTempVar(result, path.get("alternate"), ignoreResult);
  926. self.mark(after);
  927. return result;
  928. case "UnaryExpression":
  929. return finish(b.unaryExpression(
  930. expr.operator,
  931. // Can't (and don't need to) break up the syntax of the argument.
  932. // Think about delete a[b].
  933. self.explodeExpression(path.get("argument")),
  934. !!expr.prefix
  935. ));
  936. case "BinaryExpression":
  937. return finish(b.binaryExpression(
  938. expr.operator,
  939. explodeViaTempVar(null, path.get("left")),
  940. explodeViaTempVar(null, path.get("right"))
  941. ));
  942. case "AssignmentExpression":
  943. return finish(b.assignmentExpression(
  944. expr.operator,
  945. self.explodeExpression(path.get("left")),
  946. self.explodeExpression(path.get("right"))
  947. ));
  948. case "UpdateExpression":
  949. return finish(b.updateExpression(
  950. expr.operator,
  951. self.explodeExpression(path.get("argument")),
  952. expr.prefix
  953. ));
  954. case "YieldExpression":
  955. var after = loc();
  956. var arg = expr.argument && self.explodeExpression(path.get("argument"));
  957. if (arg && expr.delegate) {
  958. var result = self.makeTempVar();
  959. self.emit(b.returnStatement(b.callExpression(
  960. self.contextProperty("delegateYield"), [
  961. arg,
  962. b.literal(result.property.name),
  963. after
  964. ]
  965. )));
  966. self.mark(after);
  967. return result;
  968. }
  969. self.emitAssign(self.contextProperty("next"), after);
  970. self.emit(b.returnStatement(arg || null));
  971. self.mark(after);
  972. return self.contextProperty("sent");
  973. default:
  974. throw new Error(
  975. "unknown Expression of type " +
  976. JSON.stringify(expr.type));
  977. }
  978. };