| | |
| | | "use strict"; |
| | | |
| | | var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); |
| | | |
| | | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); |
| | | |
| | | var _assert = _interopRequireDefault(require("assert")); |
| | |
| | | var meta = _interopRequireWildcard(require("./meta")); |
| | | |
| | | var util = _interopRequireWildcard(require("./util")); |
| | | |
| | | function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } |
| | | |
| | | function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } |
| | | |
| | | /** |
| | | * Copyright (c) 2014-present, Facebook, Inc. |
| | |
| | | self.emit(t.throwStatement(self.explodeExpression(path.get("argument")))); |
| | | break; |
| | | |
| | | case "ClassDeclaration": |
| | | self.emit(self.explodeClass(path)); |
| | | break; |
| | | |
| | | default: |
| | | throw new Error("unknown Statement of type " + JSON.stringify(stmt.type)); |
| | | } |
| | |
| | | |
| | | |
| | | this.emitAssign(this.contextProperty("prev"), loc); |
| | | }; // In order to save the rest of explodeExpression from a combinatorial |
| | | // trainwreck of special cases, explodeViaTempVar is responsible for |
| | | // deciding when a subexpression needs to be "exploded," which is my |
| | | // very technical term for emitting the subexpression as an assignment |
| | | // to a temporary variable and the substituting the temporary variable |
| | | // for the original subexpression. Think of exploded view diagrams, not |
| | | // Michael Bay movies. The point of exploding subexpressions is to |
| | | // control the precise order in which the generated code realizes the |
| | | // side effects of those subexpressions. |
| | | |
| | | |
| | | Ep.explodeViaTempVar = function (tempVar, childPath, hasLeapingChildren, ignoreChildResult) { |
| | | _assert["default"].ok(!ignoreChildResult || !tempVar, "Ignoring the result of a child expression but forcing it to " + "be assigned to a temporary variable?"); |
| | | |
| | | var t = util.getTypes(); |
| | | var result = this.explodeExpression(childPath, ignoreChildResult); |
| | | |
| | | if (ignoreChildResult) {// Side effects already emitted above. |
| | | } else if (tempVar || hasLeapingChildren && !t.isLiteral(result)) { |
| | | // If tempVar was provided, then the result will always be assigned |
| | | // to it, even if the result does not otherwise need to be assigned |
| | | // to a temporary variable. When no tempVar is provided, we have |
| | | // the flexibility to decide whether a temporary variable is really |
| | | // necessary. Unfortunately, in general, a temporary variable is |
| | | // required whenever any child contains a yield expression, since it |
| | | // is difficult to prove (at all, let alone efficiently) whether |
| | | // this result would evaluate to the same value before and after the |
| | | // yield (see #206). One narrow case where we can prove it doesn't |
| | | // matter (and thus we do not need a temporary variable) is when the |
| | | // result in question is a Literal value. |
| | | result = this.emitAssign(tempVar || this.makeTempVar(), result); |
| | | } |
| | | |
| | | return result; |
| | | }; |
| | | |
| | | Ep.explodeExpression = function (path, ignoreResult) { |
| | |
| | | |
| | | if (ignoreResult) { |
| | | self.emit(expr); |
| | | } else { |
| | | return expr; |
| | | } |
| | | |
| | | return expr; |
| | | } // If the expression does not contain a leap, then we either emit the |
| | | // expression as a standalone statement or return it whole. |
| | | |
| | |
| | | // side effects relative to the leaping child(ren). |
| | | |
| | | |
| | | var hasLeapingChildren = meta.containsLeap.onlyChildren(expr); // In order to save the rest of explodeExpression from a combinatorial |
| | | // trainwreck of special cases, explodeViaTempVar is responsible for |
| | | // deciding when a subexpression needs to be "exploded," which is my |
| | | // very technical term for emitting the subexpression as an assignment |
| | | // to a temporary variable and the substituting the temporary variable |
| | | // for the original subexpression. Think of exploded view diagrams, not |
| | | // Michael Bay movies. The point of exploding subexpressions is to |
| | | // control the precise order in which the generated code realizes the |
| | | // side effects of those subexpressions. |
| | | |
| | | function explodeViaTempVar(tempVar, childPath, ignoreChildResult) { |
| | | _assert["default"].ok(!ignoreChildResult || !tempVar, "Ignoring the result of a child expression but forcing it to " + "be assigned to a temporary variable?"); |
| | | |
| | | var result = self.explodeExpression(childPath, ignoreChildResult); |
| | | |
| | | if (ignoreChildResult) {// Side effects already emitted above. |
| | | } else if (tempVar || hasLeapingChildren && !t.isLiteral(result)) { |
| | | // If tempVar was provided, then the result will always be assigned |
| | | // to it, even if the result does not otherwise need to be assigned |
| | | // to a temporary variable. When no tempVar is provided, we have |
| | | // the flexibility to decide whether a temporary variable is really |
| | | // necessary. Unfortunately, in general, a temporary variable is |
| | | // required whenever any child contains a yield expression, since it |
| | | // is difficult to prove (at all, let alone efficiently) whether |
| | | // this result would evaluate to the same value before and after the |
| | | // yield (see #206). One narrow case where we can prove it doesn't |
| | | // matter (and thus we do not need a temporary variable) is when the |
| | | // result in question is a Literal value. |
| | | result = self.emitAssign(tempVar || self.makeTempVar(), result); |
| | | } |
| | | |
| | | return result; |
| | | } // If ignoreResult is true, then we must take full responsibility for |
| | | var hasLeapingChildren = meta.containsLeap.onlyChildren(expr); // If ignoreResult is true, then we must take full responsibility for |
| | | // emitting the expression with all its side effects, and we should not |
| | | // return a result. |
| | | |
| | | |
| | | switch (expr.type) { |
| | | case "MemberExpression": |
| | | return finish(t.memberExpression(self.explodeExpression(path.get("object")), expr.computed ? explodeViaTempVar(null, path.get("property")) : expr.property, expr.computed)); |
| | | return finish(t.memberExpression(self.explodeExpression(path.get("object")), expr.computed ? self.explodeViaTempVar(null, path.get("property"), hasLeapingChildren) : expr.property, expr.computed)); |
| | | |
| | | case "CallExpression": |
| | | var calleePath = path.get("callee"); |
| | |
| | | // before evaluating the arguments, but if the callee was a member |
| | | // expression, then we must be careful that the object of the |
| | | // member expression still gets bound to `this` for the call. |
| | | var newObject = explodeViaTempVar( // Assign the exploded callee.object expression to a temporary |
| | | var newObject = self.explodeViaTempVar( // Assign the exploded callee.object expression to a temporary |
| | | // variable so that we can use it twice without reevaluating it. |
| | | self.makeTempVar(), calleePath.get("object")); |
| | | var newProperty = calleePath.node.computed ? explodeViaTempVar(null, calleePath.get("property")) : calleePath.node.property; |
| | | self.makeTempVar(), calleePath.get("object"), hasLeapingChildren); |
| | | var newProperty = calleePath.node.computed ? self.explodeViaTempVar(null, calleePath.get("property"), hasLeapingChildren) : calleePath.node.property; |
| | | injectFirstArg = newObject; |
| | | newCallee = t.memberExpression(t.memberExpression(t.cloneDeep(newObject), newProperty, calleePath.node.computed), t.identifier("call"), false); |
| | | } else { |
| | | newCallee = self.explodeExpression(calleePath); |
| | | } |
| | | } else { |
| | | newCallee = explodeViaTempVar(null, calleePath); |
| | | newCallee = self.explodeViaTempVar(null, calleePath, hasLeapingChildren); |
| | | |
| | | if (t.isMemberExpression(newCallee)) { |
| | | // If the callee was not previously a MemberExpression, then the |
| | |
| | | |
| | | if (hasLeapingArgs) { |
| | | newArgs = argsPath.map(function (argPath) { |
| | | return explodeViaTempVar(null, argPath); |
| | | return self.explodeViaTempVar(null, argPath, hasLeapingChildren); |
| | | }); |
| | | if (injectFirstArg) newArgs.unshift(injectFirstArg); |
| | | newArgs = newArgs.map(function (arg) { |
| | |
| | | return finish(t.callExpression(newCallee, newArgs)); |
| | | |
| | | case "NewExpression": |
| | | return finish(t.newExpression(explodeViaTempVar(null, path.get("callee")), path.get("arguments").map(function (argPath) { |
| | | return explodeViaTempVar(null, argPath); |
| | | return finish(t.newExpression(self.explodeViaTempVar(null, path.get("callee"), hasLeapingChildren), path.get("arguments").map(function (argPath) { |
| | | return self.explodeViaTempVar(null, argPath, hasLeapingChildren); |
| | | }))); |
| | | |
| | | case "ObjectExpression": |
| | | return finish(t.objectExpression(path.get("properties").map(function (propPath) { |
| | | if (propPath.isObjectProperty()) { |
| | | return t.objectProperty(propPath.node.key, explodeViaTempVar(null, propPath.get("value")), propPath.node.computed); |
| | | return t.objectProperty(propPath.node.key, self.explodeViaTempVar(null, propPath.get("value"), hasLeapingChildren), propPath.node.computed); |
| | | } else { |
| | | return propPath.node; |
| | | } |
| | |
| | | case "ArrayExpression": |
| | | return finish(t.arrayExpression(path.get("elements").map(function (elemPath) { |
| | | if (elemPath.isSpreadElement()) { |
| | | return t.spreadElement(explodeViaTempVar(null, elemPath.get("argument"))); |
| | | return t.spreadElement(self.explodeViaTempVar(null, elemPath.get("argument"), hasLeapingChildren)); |
| | | } else { |
| | | return explodeViaTempVar(null, elemPath); |
| | | return self.explodeViaTempVar(null, elemPath, hasLeapingChildren); |
| | | } |
| | | }))); |
| | | |
| | |
| | | result = self.makeTempVar(); |
| | | } |
| | | |
| | | var left = explodeViaTempVar(result, path.get("left")); |
| | | var left = self.explodeViaTempVar(result, path.get("left"), hasLeapingChildren); |
| | | |
| | | if (expr.operator === "&&") { |
| | | self.jumpIfNot(left, after); |
| | |
| | | self.jumpIf(left, after); |
| | | } |
| | | |
| | | explodeViaTempVar(result, path.get("right"), ignoreResult); |
| | | self.explodeViaTempVar(result, path.get("right"), hasLeapingChildren, ignoreResult); |
| | | self.mark(after); |
| | | return result; |
| | | |
| | |
| | | result = self.makeTempVar(); |
| | | } |
| | | |
| | | explodeViaTempVar(result, path.get("consequent"), ignoreResult); |
| | | self.explodeViaTempVar(result, path.get("consequent"), hasLeapingChildren, ignoreResult); |
| | | self.jump(after); |
| | | self.mark(elseLoc); |
| | | explodeViaTempVar(result, path.get("alternate"), ignoreResult); |
| | | self.explodeViaTempVar(result, path.get("alternate"), hasLeapingChildren, ignoreResult); |
| | | self.mark(after); |
| | | return result; |
| | | |
| | |
| | | self.explodeExpression(path.get("argument")), !!expr.prefix)); |
| | | |
| | | case "BinaryExpression": |
| | | return finish(t.binaryExpression(expr.operator, explodeViaTempVar(null, path.get("left")), explodeViaTempVar(null, path.get("right")))); |
| | | return finish(t.binaryExpression(expr.operator, self.explodeViaTempVar(null, path.get("left"), hasLeapingChildren), self.explodeViaTempVar(null, path.get("right"), hasLeapingChildren))); |
| | | |
| | | case "AssignmentExpression": |
| | | if (expr.operator === "=") { |
| | |
| | | self.mark(after); |
| | | return self.contextProperty("sent"); |
| | | |
| | | case "ClassExpression": |
| | | return finish(self.explodeClass(path)); |
| | | |
| | | default: |
| | | throw new Error("unknown Expression of type " + JSON.stringify(expr.type)); |
| | | } |
| | | }; |
| | | |
| | | Ep.explodeClass = function (path) { |
| | | var explodingChildren = []; |
| | | |
| | | if (path.node.superClass) { |
| | | explodingChildren.push(path.get("superClass")); |
| | | } |
| | | |
| | | path.get("body.body").forEach(function (member) { |
| | | if (member.node.computed) { |
| | | explodingChildren.push(member.get("key")); |
| | | } |
| | | }); |
| | | var hasLeapingChildren = explodingChildren.some(function (child) { |
| | | return meta.containsLeap(child); |
| | | }); |
| | | |
| | | for (var i = 0; i < explodingChildren.length; i++) { |
| | | var child = explodingChildren[i]; |
| | | var isLast = i === explodingChildren.length - 1; |
| | | |
| | | if (isLast) { |
| | | child.replaceWith(this.explodeExpression(child)); |
| | | } else { |
| | | child.replaceWith(this.explodeViaTempVar(null, child, hasLeapingChildren)); |
| | | } |
| | | } |
| | | |
| | | return path.node; |
| | | }; |