mirror of https://github.com/status-im/metro.git
Use array lookup for localized dependency IDs
Summary: The current transform for require calls replaces strings with module-local IDs. That means that each module would need a local require function. To save hundreds of closure allocations, we can just use an array that maps local IDs to global IDs. This diff changes the dependency collection and replacement transform to change a call like `require('React')` to something like `require(_dependencyMap[0])` rather than `require(0)`. Reviewed By: cpojer Differential Revision: D4153714 fbshipit-source-id: a63455834c6c2a75da6977cacb9aac9f2cb1b3aa
This commit is contained in:
parent
06de9b9a93
commit
907f08a794
|
@ -295,8 +295,8 @@ describe('inline constants', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can work with transformed require calls', () => {
|
it('can work with transformed require calls', () => {
|
||||||
const code = `__arbitrary(function() {
|
const code = `__arbitrary(require, function(arbitraryMapName) {
|
||||||
var a = require(123, 'react-native').Platform.OS;
|
var a = require(arbitraryMapName[123], 'react-native').Platform.OS;
|
||||||
});`;
|
});`;
|
||||||
const {ast} = inline(
|
const {ast} = inline(
|
||||||
'arbitrary', {code}, {dev: true, platform: 'android', isWrapped: true});
|
'arbitrary', {code}, {dev: true, platform: 'android', isWrapped: true});
|
||||||
|
|
|
@ -25,7 +25,8 @@ class Replacement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndex(name) {
|
getIndex(stringLiteral) {
|
||||||
|
const name = stringLiteral.value;
|
||||||
let index = this.nameToIndex.get(name);
|
let index = this.nameToIndex.get(name);
|
||||||
if (index !== undefined) {
|
if (index !== undefined) {
|
||||||
return index;
|
return index;
|
||||||
|
@ -39,8 +40,9 @@ class Replacement {
|
||||||
return Array.from(this.nameToIndex.keys());
|
return Array.from(this.nameToIndex.keys());
|
||||||
}
|
}
|
||||||
|
|
||||||
makeArgs(newId, oldId) {
|
makeArgs(newId, oldId, dependencyMapIdentifier) {
|
||||||
return [newId, oldId];
|
const mapLookup = createMapLookup(dependencyMapIdentifier, newId);
|
||||||
|
return [mapLookup, oldId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,14 +54,19 @@ class ProdReplacement {
|
||||||
|
|
||||||
isRequireCall(callee, firstArg) {
|
isRequireCall(callee, firstArg) {
|
||||||
return (
|
return (
|
||||||
callee.type === 'Identifier' && callee.name === 'require' &&
|
callee.type === 'Identifier' &&
|
||||||
firstArg && firstArg.type === 'NumericLiteral'
|
callee.name === 'require' &&
|
||||||
|
firstArg &&
|
||||||
|
firstArg.type === 'MemberExpression' &&
|
||||||
|
firstArg.property &&
|
||||||
|
firstArg.property.type === 'NumericLiteral'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndex(id) {
|
getIndex(memberExpression) {
|
||||||
|
const id = memberExpression.property.value;
|
||||||
if (id in this.names) {
|
if (id in this.names) {
|
||||||
return this.replacement.getIndex(this.names[id]);
|
return this.replacement.getIndex({value: this.names[id]});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -72,27 +79,54 @@ class ProdReplacement {
|
||||||
return this.replacement.getNames();
|
return this.replacement.getNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
makeArgs(newId) {
|
makeArgs(newId, _, dependencyMapIdentifier) {
|
||||||
return [newId];
|
const mapLookup = createMapLookup(dependencyMapIdentifier, newId);
|
||||||
|
return [mapLookup];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectDependencies(ast, replacement) {
|
function createMapLookup(dependencyMapIdentifier, propertyIdentifier) {
|
||||||
|
return types.memberExpression(
|
||||||
|
dependencyMapIdentifier,
|
||||||
|
propertyIdentifier,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectDependencies(ast, replacement, dependencyMapIdentifier) {
|
||||||
|
const traversalState = {dependencyMapIdentifier};
|
||||||
traverse(ast, {
|
traverse(ast, {
|
||||||
CallExpression(path) {
|
Program(path, state) {
|
||||||
|
if (!state.dependencyMapIdentifier) {
|
||||||
|
state.dependencyMapIdentifier =
|
||||||
|
path.scope.generateUidIdentifier('dependencyMap');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CallExpression(path, state) {
|
||||||
const node = path.node;
|
const node = path.node;
|
||||||
const arg = node.arguments[0];
|
const arg = node.arguments[0];
|
||||||
if (replacement.isRequireCall(node.callee, arg)) {
|
if (replacement.isRequireCall(node.callee, arg)) {
|
||||||
const index = replacement.getIndex(arg.value);
|
const index = replacement.getIndex(arg);
|
||||||
node.arguments = replacement.makeArgs(types.numericLiteral(index), arg);
|
node.arguments = replacement.makeArgs(
|
||||||
|
types.numericLiteral(index),
|
||||||
|
arg,
|
||||||
|
state.dependencyMapIdentifier,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
}, null, traversalState);
|
||||||
|
|
||||||
return replacement.getNames();
|
return {
|
||||||
|
dependencies: replacement.getNames(),
|
||||||
|
dependencyMapName: traversalState.dependencyMapIdentifier.name,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
exports = module.exports =
|
exports = module.exports =
|
||||||
ast => collectDependencies(ast, new Replacement());
|
ast => collectDependencies(ast, new Replacement());
|
||||||
exports.forOptimization =
|
exports.forOptimization =
|
||||||
(ast, names) => collectDependencies(ast, new ProdReplacement(names));
|
(ast, names, dependencyMapName) => collectDependencies(
|
||||||
|
ast,
|
||||||
|
new ProdReplacement(names),
|
||||||
|
dependencyMapName && types.identifier(dependencyMapName),
|
||||||
|
);
|
||||||
|
|
|
@ -148,7 +148,9 @@ const plugin = () => inlinePlugin;
|
||||||
function checkRequireArgs(args, dependencyId) {
|
function checkRequireArgs(args, dependencyId) {
|
||||||
const pattern = t.stringLiteral(dependencyId);
|
const pattern = t.stringLiteral(dependencyId);
|
||||||
return t.isStringLiteral(args[0], pattern) ||
|
return t.isStringLiteral(args[0], pattern) ||
|
||||||
t.isNumericLiteral(args[0]) && t.isStringLiteral(args[1], pattern);
|
t.isMemberExpression(args[0]) &&
|
||||||
|
t.isNumericLiteral(args[0].property) &&
|
||||||
|
t.isStringLiteral(args[1], pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
type AstResult = {
|
type AstResult = {
|
||||||
|
|
|
@ -144,16 +144,21 @@ function optimizeModule(
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeResult(ast, filename, sourceCode, isPolyfill = false) {
|
function makeResult(ast, filename, sourceCode, isPolyfill = false) {
|
||||||
const dependencies = isPolyfill ? [] : collectDependencies(ast);
|
const {dependencies, dependencyMapName} = isPolyfill
|
||||||
const file = isPolyfill ? wrapPolyfill(ast) : wrapModule(ast);
|
? {dependencies: []}
|
||||||
|
: collectDependencies(ast);
|
||||||
|
const file = isPolyfill
|
||||||
|
? wrapPolyfill(ast)
|
||||||
|
: wrapModule(ast, dependencyMapName);
|
||||||
|
|
||||||
const gen = generate(file, filename, sourceCode);
|
const gen = generate(file, filename, sourceCode);
|
||||||
return {code: gen.code, map: gen.map, dependencies};
|
return {code: gen.code, map: gen.map, dependencies, dependencyMapName};
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapModule(file) {
|
function wrapModule(file, dependencyMapName) {
|
||||||
const t = babel.types;
|
const t = babel.types;
|
||||||
const factory = functionFromProgram(file.program, moduleFactoryParameters);
|
const params = moduleFactoryParameters.concat(dependencyMapName);
|
||||||
|
const factory = functionFromProgram(file.program, params);
|
||||||
const def = t.callExpression(t.identifier('__d'), [factory]);
|
const def = t.callExpression(t.identifier('__d'), [factory]);
|
||||||
return t.file(t.program([t.expressionStatement(def)]));
|
return t.file(t.program([t.expressionStatement(def)]));
|
||||||
}
|
}
|
||||||
|
@ -183,6 +188,7 @@ function optimize(transformed, file, originalCode, options) {
|
||||||
: collectDependencies.forOptimization(
|
: collectDependencies.forOptimization(
|
||||||
optimized.ast,
|
optimized.ast,
|
||||||
transformed.dependencies,
|
transformed.dependencies,
|
||||||
|
transformed.dependencyMapName,
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputMap = transformed.map;
|
const inputMap = transformed.map;
|
||||||
|
|
Loading…
Reference in New Issue