diff options
Diffstat (limited to 'devtools/client/debugger/src/workers/parser/mapBindings.js')
-rw-r--r-- | devtools/client/debugger/src/workers/parser/mapBindings.js | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/workers/parser/mapBindings.js b/devtools/client/debugger/src/workers/parser/mapBindings.js new file mode 100644 index 0000000000..5c6b40c045 --- /dev/null +++ b/devtools/client/debugger/src/workers/parser/mapBindings.js @@ -0,0 +1,125 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ + +// @flow + +import { replaceNode } from "./utils/ast"; +import { isTopLevel } from "./utils/helpers"; + +import generate from "@babel/generator"; +import * as t from "@babel/types"; + +function getAssignmentTarget(node, bindings) { + if (t.isObjectPattern(node)) { + for (const property of node.properties) { + if (t.isRestElement(property)) { + property.argument = getAssignmentTarget(property.argument, bindings); + } else { + property.value = getAssignmentTarget(property.value, bindings); + } + } + + return node; + } + + if (t.isArrayPattern(node)) { + for (const [i, element] of node.elements.entries()) { + node.elements[i] = getAssignmentTarget(element, bindings); + } + + return node; + } + + if (t.isAssignmentPattern(node)) { + node.left = getAssignmentTarget(node.left, bindings); + + return node; + } + + if (t.isRestElement(node)) { + node.argument = getAssignmentTarget(node.argument, bindings); + + return node; + } + + if (t.isIdentifier(node)) { + return bindings.includes(node.name) + ? node + : t.memberExpression(t.identifier("self"), node); + } + + return node; +} + +// translates new bindings `var a = 3` into `self.a = 3` +// and existing bindings `var a = 3` into `a = 3` for re-assignments +function globalizeDeclaration(node, bindings) { + return node.declarations.map(declaration => + t.expressionStatement( + t.assignmentExpression( + "=", + getAssignmentTarget(declaration.id, bindings), + declaration.init || t.unaryExpression("void", t.numericLiteral(0)) + ) + ) + ); +} + +// translates new bindings `a = 3` into `self.a = 3` +// and keeps assignments the same for existing bindings. +function globalizeAssignment(node, bindings) { + return t.assignmentExpression( + node.operator, + getAssignmentTarget(node.left, bindings), + node.right + ); +} + +export default function mapExpressionBindings( + expression: string, + ast?: Object, + bindings: string[] = [] +): string { + let isMapped = false; + let shouldUpdate = true; + + t.traverse(ast, (node, ancestors) => { + const parent = ancestors[ancestors.length - 1]; + + if (t.isWithStatement(node)) { + shouldUpdate = false; + return; + } + + if (!isTopLevel(ancestors)) { + return; + } + + if (t.isAssignmentExpression(node)) { + if (t.isIdentifier(node.left) || t.isPattern(node.left)) { + const newNode = globalizeAssignment(node, bindings); + isMapped = true; + return replaceNode(ancestors, newNode); + } + + return; + } + + if (!t.isVariableDeclaration(node)) { + return; + } + + if (!t.isForStatement(parent.node)) { + const newNodes = globalizeDeclaration(node, bindings); + isMapped = true; + replaceNode(ancestors, newNodes); + } + }); + + if (!shouldUpdate || !isMapped) { + return expression; + } + + return generate(ast).code; +} |