195f10fcd7dafc17aaab7d6b57c9c95ed226509168762675fe69f8a891b523ea2631e0fcb3e956f2de8246b8e42ecedc15cbaccd5d3acbab673d524efde5dd 32 KB

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