Merge pull request #10286 from smelukov/inner-graph-decouple-parser-state

decouple inner-graph and parser state
This commit is contained in:
Tobias Koppers 2020-01-22 10:03:39 +01:00 committed by GitHub
commit 1be6dc1dcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 152 additions and 74 deletions

View File

@ -5,6 +5,8 @@
"use strict";
const InnerGraph = require("./optimize/InnerGraph");
/** @typedef {import("./Compiler")} Compiler */
class FlagUsingEvalPlugin {
@ -20,6 +22,7 @@ class FlagUsingEvalPlugin {
parser.hooks.call.for("eval").tap("FlagUsingEvalPlugin", () => {
parser.state.module.buildInfo.moduleConcatenationBailout = "eval()";
parser.state.module.buildInfo.usingEval = true;
InnerGraph.bailout(parser.state);
});
};

View File

@ -5,7 +5,7 @@
"use strict";
const { topLevelSymbolTag } = require("../optimize/InnerGraphPlugin");
const { topLevelSymbolTag } = require("../optimize/InnerGraph");
const ConstDependency = require("./ConstDependency");
const HarmonyExportExpressionDependency = require("./HarmonyExportExpressionDependency");
const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency");

View File

@ -6,6 +6,7 @@
"use strict";
const HotModuleReplacementPlugin = require("../HotModuleReplacementPlugin");
const InnerGraph = require("../optimize/InnerGraph");
const ConstDependency = require("./ConstDependency");
const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
@ -13,8 +14,8 @@ const HarmonyExports = require("./HarmonyExports");
const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
/** @typedef {import("../optimize/InnerGraphPlugin").InnerGraph} InnerGraph */
/** @typedef {import("../optimize/InnerGraphPlugin").TopLevelSymbol} TopLevelSymbol */
/** @typedef {import("../optimize/InnerGraph").InnerGraph} InnerGraph */
/** @typedef {import("../optimize/InnerGraph").TopLevelSymbol} TopLevelSymbol */
/** @typedef {import("./HarmonyImportDependency")} HarmonyImportDependency */
const harmonySpecifierTag = Symbol("harmony import");
@ -79,14 +80,14 @@ module.exports = class HarmonyImportDependencyParserPlugin {
* @returns {void}
*/
const addDepToInnerGraph = dep => {
const { harmonyAllExportDependentDependencies } = parser.state;
if (!harmonyAllExportDependentDependencies) return;
const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
if (!innerGraph) return;
harmonyAllExportDependentDependencies.add(dep);
const currentTopLevelSymbol =
/** @type {TopLevelSymbol} */ (parser.state.currentTopLevelSymbol);
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
const {
innerGraph,
allExportDependentDependencies,
currentTopLevelSymbol
} = innerGraphState;
allExportDependentDependencies.add(dep);
if (!currentTopLevelSymbol) {
innerGraph.set(dep, true);
} else {

View File

@ -0,0 +1,90 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sergey Melyukov @smelukov
*/
"use strict";
/** @typedef {import("estree").Node} AnyNode */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
/** @typedef {import("../dependencies/PureExpressionDependency")} PureExpressionDependency */
/** @typedef {import("../Parser").ParserState} ParserState */
/** @typedef {Map<TopLevelSymbol | Dependency, Set<string | TopLevelSymbol> | true>} InnerGraph */
/** @typedef {false|{innerGraph: InnerGraph, allExportDependentDependencies: Set<PureExpressionDependency|HarmonyImportSpecifierDependency>, currentTopLevelSymbol: TopLevelSymbol|void}} State */
/** @type {WeakMap<ParserState, State>} */
const parserStateMap = new WeakMap();
const topLevelSymbolTag = Symbol("top level symbol");
exports.parserStateMap = parserStateMap;
/**
* @param {ParserState} parserState parser state
* @returns {void}
*/
exports.bailout = parserState => {
parserStateMap.set(parserState, false);
};
/**
* @param {ParserState} parserState parser state
* @returns {void}
*/
exports.enable = parserState => {
const state = parserStateMap.get(parserState);
if (state === false) {
return;
}
parserStateMap.set(parserState, {
innerGraph: new Map(),
allExportDependentDependencies: new Set(),
currentTopLevelSymbol: undefined
});
};
/**
* @param {ParserState} parserState parser state
* @returns {boolean} true, when enabled
*/
exports.isEnabled = parserState => {
const state = parserStateMap.get(parserState);
return !!state;
};
/**
* @param {ParserState} parserState parser state
* @returns {State} state
*/
exports.getState = parserState => {
return parserStateMap.get(parserState);
};
class TopLevelSymbol {
/**
* @param {string} name name of the function
* @param {InnerGraph} innerGraph reference to the graph
*/
constructor(name, innerGraph) {
this.name = name;
this.innerGraph = innerGraph;
}
/**
* @param {string | TopLevelSymbol | true} dep export or top level symbol or always
* @returns {void}
*/
addDependency(dep) {
const info = this.innerGraph.get(this);
if (dep === true) {
this.innerGraph.set(this, true);
} else if (info === undefined) {
this.innerGraph.set(this, new Set([dep]));
} else if (info !== true) {
info.add(dep);
}
}
}
exports.TopLevelSymbol = TopLevelSymbol;
exports.topLevelSymbolTag = topLevelSymbolTag;

View File

@ -9,14 +9,16 @@ const {
harmonySpecifierTag
} = require("../dependencies/HarmonyImportDependencyParserPlugin");
const PureExpressionDependency = require("../dependencies/PureExpressionDependency");
const InnerGraph = require("./InnerGraph");
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("./InnerGraph").InnerGraph} InnerGraph */
/** @typedef {import("./InnerGraph").TopLevelSymbol} TopLevelSymbol */
const topLevelSymbolTag = Symbol("top level symbol");
/** @typedef {Map<TopLevelSymbol | Dependency, Set<string | TopLevelSymbol> | true>} InnerGraph */
const { TopLevelSymbol, topLevelSymbolTag } = InnerGraph;
/**
* @param {any} expr an expression
@ -90,32 +92,6 @@ const isPure = (expr, parser, commentsStartPos) => {
return false;
};
class TopLevelSymbol {
/**
* @param {string} name name of the function
* @param {InnerGraph} innerGraph reference to the graph
*/
constructor(name, innerGraph) {
this.name = name;
this.innerGraph = innerGraph;
}
/**
* @param {string | TopLevelSymbol | true} dep export or top level symbol or always
* @returns {void}
*/
addDependency(dep) {
const info = this.innerGraph.get(this);
if (dep === true) {
this.innerGraph.set(this, true);
} else if (info === undefined) {
this.innerGraph.set(this, new Set([dep]));
} else if (info !== true) {
info.add(dep);
}
}
}
class InnerGraphPlugin {
/**
* @param {Compiler} compiler webpack compiler
@ -136,14 +112,17 @@ class InnerGraphPlugin {
*/
const handler = (parser, parserOptions) => {
parser.hooks.program.tap("InnerGraphPlugin", () => {
parser.state.harmonyInnerGraph = new Map();
parser.state.harmonyAllExportDependentDependencies = new Set();
InnerGraph.enable(parser.state);
});
parser.hooks.finish.tap("InnerGraphPlugin", () => {
if (parser.state.module.buildInfo.usingEval) return;
const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
const {
innerGraph,
allExportDependentDependencies
} = innerGraphState;
// flatten graph to terminal nodes (string, undefined or true)
const nonTerminal = new Set(innerGraph.keys());
while (nonTerminal.size > 0) {
@ -188,8 +167,7 @@ class InnerGraphPlugin {
}
}
for (const dep of parser.state
.harmonyAllExportDependentDependencies) {
for (const dep of allExportDependentDependencies) {
const value = innerGraph.get(dep);
switch (value) {
case undefined:
@ -199,7 +177,7 @@ class InnerGraphPlugin {
dep.usedByExports = true;
break;
default:
dep.usedByExports = value;
dep.usedByExports = /** @type {Set<string>} */ (value);
break;
}
}
@ -207,10 +185,12 @@ class InnerGraphPlugin {
/** @type {WeakMap<{}, TopLevelSymbol>} */
const statementWithTopLevelSymbol = new WeakMap();
parser.hooks.preStatement.tap("InnerGraphPlugin", statement => {
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
if (parser.scope.topLevelScope === true) {
if (statement.type === "FunctionDeclaration") {
const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
const { innerGraph } = innerGraphState;
const name = statement.id ? statement.id.name : "*default*";
parser.defineVariable(name);
const fn = new TopLevelSymbol(name, innerGraph);
@ -221,10 +201,12 @@ class InnerGraphPlugin {
}
});
parser.hooks.blockPreStatement.tap("InnerGraphPlugin", statement => {
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
if (parser.scope.topLevelScope === true) {
if (statement.type === "ClassDeclaration") {
const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
const { innerGraph } = innerGraphState;
const name = statement.id ? statement.id.name : "*default*";
parser.defineVariable(name);
const fn = new TopLevelSymbol(name, innerGraph);
@ -240,8 +222,7 @@ class InnerGraphPlugin {
decl.type === "ClassExpression" ||
decl.type === "Identifier"
) {
const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
const { innerGraph } = innerGraphState;
const name = "*default*";
parser.defineVariable(name);
const fn = new TopLevelSymbol(name, innerGraph);
@ -251,9 +232,8 @@ class InnerGraphPlugin {
}
}
});
const tagVar = name => {
const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
const tagVar = (innerGraphState, name) => {
const { innerGraph } = innerGraphState;
parser.defineVariable(name);
const existingTag = parser.getTagData(name, topLevelSymbolTag);
const fn = existingTag || new TopLevelSymbol(name, innerGraph);
@ -268,6 +248,8 @@ class InnerGraphPlugin {
parser.hooks.preDeclarator.tap(
"InnerGraphPlugin",
(decl, statement) => {
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
if (
parser.scope.topLevelScope === true &&
decl.init &&
@ -279,13 +261,13 @@ class InnerGraphPlugin {
decl.init.type === "ClassExpression"
) {
const name = decl.id.name;
const fn = tagVar(name);
const fn = tagVar(innerGraphState, name);
declWithTopLevelSymbol.set(decl, fn);
return true;
}
if (isPure(decl.init, parser, decl.id.range[1])) {
const name = decl.id.name;
const fn = tagVar(name);
const fn = tagVar(innerGraphState, name);
declWithTopLevelSymbol.set(decl, fn);
pureDeclarators.add(decl);
return true;
@ -294,43 +276,52 @@ class InnerGraphPlugin {
}
);
parser.hooks.statement.tap("InnerGraphPlugin", statement => {
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
if (parser.scope.topLevelScope === true) {
parser.state.currentTopLevelSymbol = undefined;
innerGraphState.currentTopLevelSymbol = undefined;
const fn = statementWithTopLevelSymbol.get(statement);
if (fn) {
parser.state.currentTopLevelSymbol = fn;
innerGraphState.currentTopLevelSymbol = fn;
}
}
});
parser.hooks.declarator.tap("InnerGraphPlugin", (decl, statement) => {
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
const {
innerGraph,
allExportDependentDependencies
} = innerGraphState;
const fn = declWithTopLevelSymbol.get(decl);
if (fn) {
if (pureDeclarators.has(decl)) {
const innerGraph =
/** @type {InnerGraph} */ (parser.state.harmonyInnerGraph);
const dep = new PureExpressionDependency(decl.init.range);
dep.loc = decl.loc;
parser.state.module.addDependency(dep);
innerGraph.set(dep, new Set([fn]));
parser.state.harmonyAllExportDependentDependencies.add(dep);
allExportDependentDependencies.add(dep);
}
parser.state.currentTopLevelSymbol = fn;
innerGraphState.currentTopLevelSymbol = fn;
parser.walkExpression(decl.init);
parser.state.currentTopLevelSymbol = undefined;
innerGraphState.currentTopLevelSymbol = undefined;
return true;
}
});
parser.hooks.expression
.for(topLevelSymbolTag)
.tap("InnerGraphPlugin", expr => {
const innerGraphState = InnerGraph.getState(parser.state);
if (!innerGraphState) return;
const topLevelSymbol =
/** @type {TopLevelSymbol} */ (parser.currentTagData);
const currentTopLevelSymbol = parser.state.currentTopLevelSymbol;
const { currentTopLevelSymbol } = innerGraphState;
topLevelSymbol.addDependency(currentTopLevelSymbol || true);
});
parser.hooks.assign
.for(topLevelSymbolTag)
.tap("InnerGraphPlugin", expr => {
if (!InnerGraph.isEnabled(parser.state)) return;
if (expr.operator === "=") return true;
});
};
@ -340,16 +331,9 @@ class InnerGraphPlugin {
normalModuleFactory.hooks.parser
.for("javascript/esm")
.tap("InnerGraphPlugin", handler);
compilation.hooks.optimizeDependencies.tap(
"InnerGraphPlugin",
modules => {}
);
}
);
}
}
module.exports = InnerGraphPlugin;
module.exports.TopLevelSymbol = TopLevelSymbol;
module.exports.topLevelSymbolTag = topLevelSymbolTag;