Split the simple-access transforms out of the module transform into a general helper.

This commit is contained in:
Logan Smyth 2017-09-25 15:02:20 -07:00
parent 0125c03303
commit 9dfcf0f116
9 changed files with 137 additions and 65 deletions

View File

@ -9,6 +9,7 @@
"main": "lib/index.js", "main": "lib/index.js",
"dependencies": { "dependencies": {
"babel-helper-module-imports": "7.0.0-beta.2", "babel-helper-module-imports": "7.0.0-beta.2",
"babel-helper-simple-access": "7.0.0-beta.2",
"babel-template": "7.0.0-beta.2", "babel-template": "7.0.0-beta.2",
"babel-types": "7.0.0-beta.2", "babel-types": "7.0.0-beta.2",
"lodash": "^4.2.0" "lodash": "^4.2.0"

View File

@ -1,5 +1,7 @@
import assert from "assert";
import * as t from "babel-types"; import * as t from "babel-types";
import template from "babel-template"; import template from "babel-template";
import simplifyAccess from "babel-helper-simple-access";
import type { ModuleMetadata } from "./"; import type { ModuleMetadata } from "./";
@ -44,6 +46,12 @@ export default function rewriteLiveReferences(
exported, // local name => exported name list exported, // local name => exported name list
}); });
simplifyAccess(
programPath,
// NOTE(logan): The 'Array.from' calls are to make this code with in loose mode.
new Set([...Array.from(imported.keys()), ...Array.from(exported.keys())]),
);
// Rewrite reads/writes from imports and exports to have the correct behavior. // Rewrite reads/writes from imports and exports to have the correct behavior.
programPath.traverse(rewriteReferencesVisitor, { programPath.traverse(rewriteReferencesVisitor, {
seen: new WeakSet(), seen: new WeakSet(),
@ -188,57 +196,6 @@ const rewriteReferencesVisitor = {
} }
}, },
UpdateExpression: {
exit(path) {
const { scope, imported, exported } = this;
const arg = path.get("argument");
if (!arg.isIdentifier()) return;
const localName = arg.node.name;
if (!imported.has(localName) && !exported.has(localName)) {
return;
}
// redeclared in this scope
if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
return;
}
const exportedNames = exported.get(localName) || [];
if (exportedNames.length > 0 || imported.has(localName)) {
if (
path.node.prefix ||
(path.parentPath.isExpressionStatement() &&
!path.isCompletionRecord())
) {
// ++i => (i += 1);
path.replaceWith(
t.assignmentExpression("+=", arg.node, t.numericLiteral(1)),
);
} else {
const varName = path.scope.generateDeclaredUidIdentifier("old");
const assignment = t.binaryExpression(
path.node.operator.slice(0, 1),
varName,
t.numericLiteral(1),
);
// i++ => (_tmp = i, i = _tmp + 1, _tmp)
path.replaceWith(
t.sequenceExpression([
t.assignmentExpression("=", varName, arg.node),
t.assignmentExpression("=", arg.node, assignment),
varName,
]),
);
}
}
},
},
AssignmentExpression: { AssignmentExpression: {
exit(path) { exit(path) {
const { const {
@ -267,23 +224,14 @@ const rewriteReferencesVisitor = {
const exportedNames = exported.get(localName) || []; const exportedNames = exported.get(localName) || [];
const importData = imported.get(localName); const importData = imported.get(localName);
if (exportedNames.length > 0 || importData) { if (exportedNames.length > 0 || importData) {
assert(path.node.operator === "=", "Path was not simplified");
const assignment = path.node; const assignment = path.node;
if (importData) { if (importData) {
assignment.left = assignment.left =
buildImportReference(importData) || assignment.left; buildImportReference(importData) || assignment.left;
if (path.node.operator !== "=") {
const op = path.node.operator.slice(0, -1);
path.node.operator = "=";
assignment.right = t.binaryExpression(
op,
assignment.left,
assignment.right,
);
}
assignment.right = t.sequenceExpression([ assignment.right = t.sequenceExpression([
assignment.right, assignment.right,
buildImportThrow(localName), buildImportThrow(localName),

View File

@ -0,0 +1,3 @@
src
test
*.log

View File

@ -0,0 +1,18 @@
# babel-helper-simple-assignment
There are many cases where it is hard to perform transformations because a
piece of code using complex structures. Say you want to rewrite all accesses
to a given variable, and there are cases like
```
i += 1
--i;
```
It is difficult to work with.
This helper can handle converting these to simple access patterns of
## Usage
TODO

View File

@ -0,0 +1,15 @@
{
"name": "babel-helper-simple-access",
"version": "7.0.0-beta.2",
"description": "Babel helper for ensuring that access to a given value is performed through simple accesses",
"author": "Logan Smyth <loganfsmyth@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-helper-simple-access",
"main": "lib/index.js",
"dependencies": {
"babel-template": "7.0.0-beta.2",
"babel-types": "7.0.0-beta.2",
"lodash": "^4.2.0"
}
}

View File

@ -0,0 +1,87 @@
import * as t from "babel-types";
export default function simplifyAccess(path: NodePath, bindingNames) {
path.traverse(simpleAssignmentVisitor, {
scope: path.scope,
bindingNames,
seen: new WeakSet(),
});
}
const simpleAssignmentVisitor = {
UpdateExpression: {
exit(path) {
const { scope, bindingNames } = this;
const arg = path.get("argument");
if (!arg.isIdentifier()) return;
const localName = arg.node.name;
if (!bindingNames.has(localName)) return;
// redeclared in this scope
if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
return;
}
if (
path.node.prefix ||
(path.parentPath.isExpressionStatement() && !path.isCompletionRecord())
) {
// ++i => (i += 1);
path.replaceWith(
t.assignmentExpression("+=", arg.node, t.numericLiteral(1)),
);
} else {
const varName = path.scope.generateDeclaredUidIdentifier("old");
const assignment = t.binaryExpression(
path.node.operator.slice(0, 1),
varName,
t.numericLiteral(1),
);
// i++ => (_tmp = i, i = _tmp + 1, _tmp)
path.replaceWith(
t.sequenceExpression([
t.assignmentExpression("=", varName, arg.node),
t.assignmentExpression("=", arg.node, assignment),
varName,
]),
);
}
},
},
AssignmentExpression: {
exit(path) {
const { scope, seen, bindingNames } = this;
if (path.node.operator === "=") return;
if (seen.has(path.node)) return;
seen.add(path.node);
const left = path.get("left");
if (!left.isIdentifier()) return;
// Simple update-assign foo += 1;
// => exports.foo = (foo += 1);
const localName = left.node.name;
if (!bindingNames.has(localName)) return;
// redeclared in this scope
if (scope.getBinding(localName) !== path.scope.getBinding(localName)) {
return;
}
path.node.right = t.binaryExpression(
path.node.operator.slice(0, -1),
path.node.left,
path.node.right,
);
path.node.operator = "=";
},
},
};

View File

@ -8,7 +8,7 @@ define(["exports"], function (_exports) {
var test = 2; var test = 2;
_exports.test = test; _exports.test = test;
_exports.test = test = 5; _exports.test = test = 5;
_exports.test = test += 1; _exports.test = test = test + 1;
(function () { (function () {
var test = 2; var test = 2;

View File

@ -7,7 +7,7 @@ exports.f = exports.e = exports.c = exports.a = exports.test = void 0;
var test = 2; var test = 2;
exports.test = test; exports.test = test;
exports.test = test = 5; exports.test = test = 5;
exports.test = test += 1; exports.test = test = test + 1;
(function () { (function () {
var test = 2; var test = 2;

View File

@ -20,7 +20,7 @@
var test = 2; var test = 2;
_exports.test = test; _exports.test = test;
_exports.test = test = 5; _exports.test = test = 5;
_exports.test = test += 1; _exports.test = test = test + 1;
(function () { (function () {
var test = 2; var test = 2;