first commit
This commit is contained in:
239
build/node_modules/prepack/lib/serializer/visitors.js
generated
vendored
Normal file
239
build/node_modules/prepack/lib/serializer/visitors.js
generated
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ClosureRefVisitor = exports.ClosureRefReplacer = undefined;
|
||||
|
||||
var _realm = require("../realm.js");
|
||||
|
||||
var _index = require("../values/index.js");
|
||||
|
||||
var _babelTypes = require("babel-types");
|
||||
|
||||
var t = _interopRequireWildcard(_babelTypes);
|
||||
|
||||
var _jsx = require("../react/jsx");
|
||||
|
||||
var _internalizer = require("../utils/internalizer.js");
|
||||
|
||||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
||||
|
||||
function markVisited(node, data) {
|
||||
node._renamedOnce = data;
|
||||
} /**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
function shouldVisit(node, data) {
|
||||
return node._renamedOnce !== data;
|
||||
}
|
||||
|
||||
// replaceWith causes the node to be re-analyzed, so to prevent double replacement
|
||||
// we add this property on the node to mark it such that it does not get replaced
|
||||
// again on this pass
|
||||
// TODO: Make this work when replacing with arbitrary BabelNodeExpressions. Currently
|
||||
// if the node that we're substituting contains identifiers as children,
|
||||
// they will be visited again and possibly transformed.
|
||||
// If necessary we could implement this by following node.parentPath and checking
|
||||
// if any parent nodes are marked visited, but that seem unnecessary right now.let closureRefReplacer = {
|
||||
function replaceName(path, residualFunctionBinding, name, data) {
|
||||
if (path.scope.hasBinding(name, /*noGlobals*/true)) return;
|
||||
|
||||
if (residualFunctionBinding && shouldVisit(path.node, data)) {
|
||||
markVisited(residualFunctionBinding.serializedValue, data);
|
||||
var serializedValue = residualFunctionBinding.serializedValue;
|
||||
|
||||
if (path.node.type === "JSXIdentifier" || path.node.type === "JSXMemberIdentifier") {
|
||||
path.replaceWith((0, _jsx.convertExpressionToJSXIdentifier)(serializedValue, true));
|
||||
} else {
|
||||
path.replaceWith(serializedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getLiteralTruthiness(node) {
|
||||
// In the return value, 'known' is true only if this is a literal of known truthiness and with no side effects; if 'known' is true, 'value' is its truthiness.
|
||||
if (t.isBooleanLiteral(node) || t.isNumericLiteral(node) || t.isStringLiteral(node)) {
|
||||
return { known: true, value: !!node.value };
|
||||
}
|
||||
if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node) || t.isRegExpLiteral(node) || t.isClassExpression(node) && node.superClass === null && node.body.body.length === 0 || t.isObjectExpression(node) && node.properties.length === 0 || t.isArrayExpression(node) && node.elements.length === 0) {
|
||||
return { known: true, value: true };
|
||||
}
|
||||
if (t.isNullLiteral(node)) {
|
||||
return { known: true, value: false };
|
||||
}
|
||||
return { known: false };
|
||||
}
|
||||
|
||||
function canShareFunctionBody(duplicateFunctionInfo) {
|
||||
// Only share function when:
|
||||
// 1. it does not access any free variables.
|
||||
// 2. it does not use "this".
|
||||
var _duplicateFunctionInf = duplicateFunctionInfo.functionInfo,
|
||||
unbound = _duplicateFunctionInf.unbound,
|
||||
modified = _duplicateFunctionInf.modified,
|
||||
usesThis = _duplicateFunctionInf.usesThis;
|
||||
|
||||
return unbound.size === 0 && modified.size === 0 && !usesThis;
|
||||
}
|
||||
|
||||
var ClosureRefReplacer = exports.ClosureRefReplacer = {
|
||||
ReferencedIdentifier: function ReferencedIdentifier(path, state) {
|
||||
if (ignorePath(path)) return;
|
||||
|
||||
var residualFunctionBindings = state.residualFunctionBindings;
|
||||
var name = path.node.name;
|
||||
var residualFunctionBinding = residualFunctionBindings.get(name);
|
||||
if (residualFunctionBinding) replaceName(path, residualFunctionBinding, name, residualFunctionBindings);
|
||||
},
|
||||
CallExpression: function CallExpression(path, state) {
|
||||
// Here we apply the require optimization by replacing require calls with their
|
||||
// corresponding initialized modules.
|
||||
var requireReturns = state.requireReturns;
|
||||
if (!state.isRequire || !state.isRequire(path.scope, path.node)) return;
|
||||
state.requireStatistics.count++;
|
||||
if (state.modified.has(path.node.callee.name)) return;
|
||||
|
||||
var moduleId = "" + path.node.arguments[0].value;
|
||||
var new_node = requireReturns.get(moduleId);
|
||||
if (new_node !== undefined) {
|
||||
markVisited(new_node, state.residualFunctionBindings);
|
||||
path.replaceWith(new_node);
|
||||
state.requireStatistics.replaced++;
|
||||
}
|
||||
},
|
||||
"AssignmentExpression|UpdateExpression": function AssignmentExpressionUpdateExpression(path, state) {
|
||||
var residualFunctionBindings = state.residualFunctionBindings;
|
||||
var ids = path.getBindingIdentifierPaths();
|
||||
for (var name in ids) {
|
||||
var residualFunctionBinding = residualFunctionBindings.get(name);
|
||||
if (residualFunctionBinding) {
|
||||
var nestedPath = ids[name];
|
||||
replaceName(nestedPath, residualFunctionBinding, name, residualFunctionBindings);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// TODO: handle FunctionDeclaration.
|
||||
// Replace "function () {}" ==> "factory_id.bind(null)".
|
||||
FunctionExpression: function FunctionExpression(path, state) {
|
||||
if (t.isProgram(path.parentPath.parentPath.node)) {
|
||||
// Our goal is replacing duplicate nested function so skip root residual function itself.
|
||||
// This assumes the root function is wrapped with: t.file(t.program([t.expressionStatement(rootFunction).
|
||||
return;
|
||||
}
|
||||
|
||||
var functionExpression = path.node;
|
||||
var functionTag = functionExpression.body.uniqueOrderedTag;
|
||||
if (!functionTag) {
|
||||
// Un-interpreted nested function.
|
||||
return;
|
||||
}
|
||||
var duplicateFunctionInfo = state.factoryFunctionInfos.get(functionTag);
|
||||
if (duplicateFunctionInfo && canShareFunctionBody(duplicateFunctionInfo)) {
|
||||
var factoryId = duplicateFunctionInfo.factoryId;
|
||||
|
||||
path.replaceWith(t.callExpression(t.memberExpression(factoryId, t.identifier("bind")), [_internalizer.nullExpression]));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// A few very simple dead code elimination helpers. Eventually these should be subsumed by the partial evaluators.
|
||||
IfStatement: {
|
||||
exit: function exit(path, state) {
|
||||
var node = path.node;
|
||||
var testTruthiness = getLiteralTruthiness(node.test);
|
||||
if (testTruthiness.known) {
|
||||
if (testTruthiness.value) {
|
||||
// Strictly speaking this is not safe: Annex B.3.4 allows FunctionDeclarations as the body of IfStatements in sloppy mode,
|
||||
// which have weird hoisting behavior: `console.log(typeof f); if (true) function f(){} console.log(typeof f)` will print 'undefined', 'function', but
|
||||
// `console.log(typeof f); function f(){} console.log(typeof f)` will print 'function', 'function'.
|
||||
// However, Babylon can't parse these, so it doesn't come up.
|
||||
path.replaceWith(node.consequent);
|
||||
} else {
|
||||
if (node.alternate !== null) {
|
||||
path.replaceWith(node.alternate);
|
||||
} else {
|
||||
path.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ConditionalExpression: {
|
||||
exit: function exit(path, state) {
|
||||
var node = path.node;
|
||||
var testTruthiness = getLiteralTruthiness(node.test);
|
||||
if (testTruthiness.known) {
|
||||
path.replaceWith(testTruthiness.value ? node.consequent : node.alternate);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
LogicalExpression: {
|
||||
exit: function exit(path, state) {
|
||||
var node = path.node;
|
||||
var leftTruthiness = getLiteralTruthiness(node.left);
|
||||
if (node.operator === "&&" && leftTruthiness.known) {
|
||||
path.replaceWith(leftTruthiness.value ? node.right : node.left);
|
||||
} else if (node.operator === "||" && leftTruthiness.known) {
|
||||
path.replaceWith(leftTruthiness.value ? node.left : node.right);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
WhileStatement: {
|
||||
exit: function exit(path, state) {
|
||||
var node = path.node;
|
||||
var testTruthiness = getLiteralTruthiness(node.test);
|
||||
if (testTruthiness.known && !testTruthiness.value) {
|
||||
path.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function visitName(path, state, name, modified) {
|
||||
// Is the name bound to some local identifier? If so, we don't need to do anything
|
||||
if (path.scope.hasBinding(name, /*noGlobals*/true)) return;
|
||||
|
||||
// Otherwise, let's record that there's an unbound identifier
|
||||
state.functionInfo.unbound.add(name);
|
||||
if (modified) state.functionInfo.modified.add(name);
|
||||
}
|
||||
|
||||
function ignorePath(path) {
|
||||
var parent = path.parent;
|
||||
return t.isLabeledStatement(parent) || t.isBreakStatement(parent) || t.isContinueStatement(parent);
|
||||
}
|
||||
|
||||
// TODO #886: doesn't check that `arguments` and `this` is in top function
|
||||
var ClosureRefVisitor = exports.ClosureRefVisitor = {
|
||||
ReferencedIdentifier: function ReferencedIdentifier(path, state) {
|
||||
if (ignorePath(path)) return;
|
||||
|
||||
var innerName = path.node.name;
|
||||
if (innerName === "arguments") {
|
||||
state.functionInfo.usesArguments = true;
|
||||
return;
|
||||
}
|
||||
visitName(path, state, innerName, false);
|
||||
},
|
||||
ThisExpression: function ThisExpression(path, state) {
|
||||
state.functionInfo.usesThis = true;
|
||||
},
|
||||
"AssignmentExpression|UpdateExpression": function AssignmentExpressionUpdateExpression(path, state) {
|
||||
for (var name in path.getBindingIdentifiers()) {
|
||||
visitName(path, state, name, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=visitors.js.map
|
||||
Reference in New Issue
Block a user