`third_party/node/node_modules` is generated by `./mach vendor node`. Differential Revision: https://phabricator.services.mozilla.com/D320322
481 lines
13 KiB
JavaScript
481 lines
13 KiB
JavaScript
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Sergey Melyukov @smelukov
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const { UsageState } = require("../ExportsInfo");
|
|
const JavascriptParser = require("../javascript/JavascriptParser");
|
|
|
|
/** @typedef {import("../Compilation")} Compilation */
|
|
/** @typedef {import("../Dependency")} Dependency */
|
|
/** @typedef {import("../Dependency").GetConditionFn} GetConditionFn */
|
|
/** @typedef {import("../Module")} Module */
|
|
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
|
/** @typedef {import("../Parser").ParserState} ParserState */
|
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
|
|
|
/** @typedef {boolean | ((compilation: Compilation, module: Module) => boolean)} PureCondition */
|
|
/** @typedef {Set<string | TopLevelSymbol>} InnerGraphValueSet */
|
|
/** @typedef {InnerGraphValueSet | true} InnerGraphValue */
|
|
/** @typedef {TopLevelSymbol | null} InnerGraphKey */
|
|
/** @typedef {Map<InnerGraphKey, InnerGraphValue | undefined>} InnerGraph */
|
|
/** @typedef {(value: boolean | Set<string> | undefined, module: Module) => void} UsageCallback */
|
|
|
|
/**
|
|
* Defines the state object type used by this module.
|
|
* @typedef {object} StateObject
|
|
* @property {InnerGraph} innerGraph
|
|
* @property {TopLevelSymbol=} currentTopLevelSymbol
|
|
* @property {Map<TopLevelSymbol, Set<UsageCallback>>} usageCallbackMap
|
|
*/
|
|
|
|
/** @typedef {false | StateObject} State */
|
|
/** @typedef {string | TopLevelSymbol | true} Usage */
|
|
/** @typedef {Set<string> | boolean} UsedByExports */
|
|
/** @typedef {Dependency & { usedByExports: UsedByExports | undefined }} DependencyWithUsedByExports */
|
|
|
|
class TopLevelSymbol {
|
|
/**
|
|
* Creates an instance of TopLevelSymbol.
|
|
* @param {string} name name of the variable
|
|
* @param {PureCondition=} pure pure condition
|
|
*/
|
|
constructor(name, pure = false) {
|
|
/** @type {string} */
|
|
this.name = name;
|
|
|
|
/** @type {boolean} */
|
|
this.conditional = false;
|
|
/** @type {boolean} */
|
|
this._pure = false;
|
|
/** @type {((compilation: Compilation, module: Module) => boolean) | undefined} */
|
|
this.pureFn = undefined;
|
|
|
|
this.setPure(pure);
|
|
}
|
|
|
|
/**
|
|
* Sets the pure condition
|
|
* @param {PureCondition} pure pure condition
|
|
* @returns {void}
|
|
*/
|
|
setPure(pure) {
|
|
this.conditional = typeof pure === "function";
|
|
this._pure = pure === true;
|
|
this.pureFn = typeof pure === "function" ? pure : undefined;
|
|
}
|
|
|
|
/**
|
|
* @param {Compilation} compilation compilation
|
|
* @param {Module} module module
|
|
* @returns {boolean} if the symbol is pure
|
|
*/
|
|
isPure(compilation, module) {
|
|
if (!this.conditional) return this._pure;
|
|
|
|
const pureFn =
|
|
/** @type {(compilation: Compilation, module: Module) => boolean} */ (
|
|
this.pureFn
|
|
);
|
|
return pureFn(compilation, module);
|
|
}
|
|
}
|
|
|
|
module.exports.TopLevelSymbol = TopLevelSymbol;
|
|
|
|
const topLevelSymbolTag = Symbol("top level symbol");
|
|
|
|
/** @type {WeakMap<Compilation, InnerGraphUtils>} */
|
|
const innerGraphByCompilation = new WeakMap();
|
|
|
|
/**
|
|
* @typedef {object} InnerGraphUtils
|
|
* @property {(parserState: ParserState) => void} enable
|
|
* @property {(parserState: ParserState) => void} bailout
|
|
* @property {(parserState: ParserState) => boolean} isEnabled
|
|
* @property {(parserState: ParserState, symbol: TopLevelSymbol | null, usage: Usage) => void} addUsage
|
|
* @property {(parserState: ParserState, onUsageCallback: UsageCallback) => void} onUsage
|
|
* @property {(parserState: ParserState, symbol: TopLevelSymbol | undefined) => void} setTopLevelSymbol
|
|
* @property {(parserState: ParserState) => TopLevelSymbol | void} getTopLevelSymbol
|
|
* @property {(parser: JavascriptParser, name: string, pure?: PureCondition) => TopLevelSymbol | undefined} tagTopLevelSymbol
|
|
* @property {(parser: JavascriptParser, name: string, usage: Usage) => void} addVariableUsage
|
|
* @property {(module: Module) => void} inferDependencyUsage
|
|
* @property {(module: Module) => void} release
|
|
*/
|
|
|
|
/**
|
|
* Returns false, when unused. Otherwise true.
|
|
* @param {Dependency} dependency the dependency
|
|
* @param {UsedByExports | undefined} usedByExports usedByExports info
|
|
* @param {ModuleGraph} moduleGraph moduleGraph
|
|
* @param {RuntimeSpec} runtime runtime
|
|
* @returns {boolean} false, when unused. Otherwise true
|
|
*/
|
|
function isDependencyUsedByExports(
|
|
dependency,
|
|
usedByExports,
|
|
moduleGraph,
|
|
runtime
|
|
) {
|
|
if (usedByExports === false) return false;
|
|
if (usedByExports !== true && usedByExports !== undefined) {
|
|
const selfModule =
|
|
/** @type {Module} */
|
|
(moduleGraph.getParentModule(dependency));
|
|
const exportsInfo = moduleGraph.getExportsInfo(selfModule);
|
|
for (const exportName of usedByExports) {
|
|
if (exportsInfo.getUsed(exportName, runtime) !== UsageState.Unused) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns dependency condition
|
|
* @param {Dependency} dependency the dependency
|
|
* @param {ModuleGraph} moduleGraph moduleGraph
|
|
* @returns {null | false | GetConditionFn} function to determine if the connection is active
|
|
*/
|
|
module.exports.getDependencyUsedByExportsCondition = (
|
|
dependency,
|
|
moduleGraph
|
|
) => {
|
|
switch (
|
|
/** @type {DependencyWithUsedByExports} */ (dependency).usedByExports
|
|
) {
|
|
case false:
|
|
return false;
|
|
case true:
|
|
return null;
|
|
default:
|
|
return (_connections, runtime) =>
|
|
isDependencyUsedByExports(
|
|
dependency,
|
|
/** @type {DependencyWithUsedByExports} */ (dependency).usedByExports,
|
|
moduleGraph,
|
|
runtime
|
|
);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the InnerGraph utils scoped to a single compilation.
|
|
* @param {Compilation} compilation the compilation
|
|
* @returns {InnerGraphUtils} utils
|
|
*/
|
|
module.exports.getInnerGraphUtils = (compilation) => {
|
|
let utils = innerGraphByCompilation.get(compilation);
|
|
if (utils) return utils;
|
|
|
|
/** @type {WeakMap<Module, State>} */
|
|
const states = new WeakMap();
|
|
|
|
/**
|
|
* @param {ParserState} parserState parser state
|
|
* @returns {State | undefined} state
|
|
*/
|
|
function getState(parserState) {
|
|
return states.get(parserState.module);
|
|
}
|
|
|
|
/**
|
|
* @param {ParserState} parserState parser state
|
|
* @returns {void}
|
|
*/
|
|
function enable(parserState) {
|
|
const state = states.get(parserState.module);
|
|
if (state === false) {
|
|
return;
|
|
}
|
|
states.set(parserState.module, {
|
|
innerGraph: new Map(),
|
|
currentTopLevelSymbol: undefined,
|
|
usageCallbackMap: new Map()
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {ParserState} parserState parser state
|
|
* @returns {void}
|
|
*/
|
|
function bailout(parserState) {
|
|
states.set(parserState.module, false);
|
|
}
|
|
|
|
/**
|
|
* @param {ParserState} parserState parser state
|
|
* @returns {boolean} true, when enabled
|
|
*/
|
|
function isEnabled(parserState) {
|
|
return Boolean(states.get(parserState.module));
|
|
}
|
|
|
|
/**
|
|
* @param {ParserState} state parser state
|
|
* @param {TopLevelSymbol | null} symbol the symbol, or null for all symbols
|
|
* @param {Usage} usage usage data
|
|
* @returns {void}
|
|
*/
|
|
function addUsage(state, symbol, usage) {
|
|
const innerGraphState = getState(state);
|
|
|
|
if (innerGraphState) {
|
|
const { innerGraph } = innerGraphState;
|
|
const info = innerGraph.get(symbol);
|
|
if (usage === true) {
|
|
innerGraph.set(symbol, true);
|
|
} else if (info === undefined) {
|
|
innerGraph.set(symbol, new Set([usage]));
|
|
} else if (info !== true) {
|
|
info.add(usage);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {JavascriptParser} parser the parser
|
|
* @param {string} name name of variable
|
|
* @param {Usage} usage usage data
|
|
* @returns {void}
|
|
*/
|
|
function addVariableUsage(parser, name, usage) {
|
|
const symbol =
|
|
/** @type {TopLevelSymbol} */ (
|
|
parser.getTagData(name, topLevelSymbolTag)
|
|
) || tagTopLevelSymbol(parser, name);
|
|
if (symbol) {
|
|
addUsage(parser.state, symbol, usage);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {ParserState} state parser state
|
|
* @returns {TopLevelSymbol | void} usage data
|
|
*/
|
|
function getTopLevelSymbol(state) {
|
|
const innerGraphState = getState(state);
|
|
|
|
if (innerGraphState) {
|
|
return innerGraphState.currentTopLevelSymbol;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {ParserState} state parser state
|
|
* @param {TopLevelSymbol | undefined} symbol the symbol
|
|
*/
|
|
function setTopLevelSymbol(state, symbol) {
|
|
const innerGraphState = getState(state);
|
|
|
|
if (innerGraphState) {
|
|
innerGraphState.currentTopLevelSymbol = symbol;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {ParserState} state parser state
|
|
* @param {UsageCallback} onUsageCallback on usage callback
|
|
*/
|
|
function onUsage(state, onUsageCallback) {
|
|
const innerGraphState = getState(state);
|
|
|
|
if (innerGraphState) {
|
|
const { usageCallbackMap, currentTopLevelSymbol } = innerGraphState;
|
|
if (currentTopLevelSymbol) {
|
|
let callbacks = usageCallbackMap.get(currentTopLevelSymbol);
|
|
|
|
if (callbacks === undefined) {
|
|
/** @type {Set<UsageCallback>} */
|
|
callbacks = new Set();
|
|
usageCallbackMap.set(currentTopLevelSymbol, callbacks);
|
|
}
|
|
|
|
callbacks.add(onUsageCallback);
|
|
} else {
|
|
onUsageCallback(true, state.module);
|
|
}
|
|
} else {
|
|
onUsageCallback(undefined, state.module);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {JavascriptParser} parser parser
|
|
* @param {string} name name of variable
|
|
* @param {PureCondition=} pure pure condition
|
|
* @returns {TopLevelSymbol=} symbol
|
|
*/
|
|
function tagTopLevelSymbol(parser, name, pure) {
|
|
const innerGraphState = getState(parser.state);
|
|
if (!innerGraphState) return;
|
|
|
|
parser.defineVariable(name);
|
|
|
|
const existingTag = /** @type {TopLevelSymbol} */ (
|
|
parser.getTagData(name, topLevelSymbolTag)
|
|
);
|
|
if (existingTag) {
|
|
if (pure !== undefined) {
|
|
existingTag.setPure(pure);
|
|
}
|
|
return existingTag;
|
|
}
|
|
|
|
const symbol = new TopLevelSymbol(name, pure);
|
|
parser.tagVariable(
|
|
name,
|
|
topLevelSymbolTag,
|
|
symbol,
|
|
JavascriptParser.VariableInfoFlags.Normal
|
|
);
|
|
return symbol;
|
|
}
|
|
|
|
/**
|
|
* @param {Module} module module
|
|
* @returns {void}
|
|
*/
|
|
function inferDependencyUsage(module) {
|
|
const innerGraphState = states.get(module);
|
|
|
|
if (!innerGraphState) {
|
|
return;
|
|
}
|
|
|
|
const { innerGraph, usageCallbackMap } = innerGraphState;
|
|
/** @type {Map<InnerGraphKey, InnerGraphValueSet | undefined>} */
|
|
const processed = new Map();
|
|
// flatten graph to terminal nodes (string, undefined or true)
|
|
const nonTerminal = new Set(innerGraph.keys());
|
|
while (nonTerminal.size > 0) {
|
|
for (const key of nonTerminal) {
|
|
if (key !== null && !key.isPure(compilation, module)) {
|
|
innerGraph.set(key, true);
|
|
nonTerminal.delete(key);
|
|
continue;
|
|
}
|
|
|
|
/** @type {InnerGraphValue} */
|
|
let newSet = new Set();
|
|
let isTerminal = true;
|
|
const value = innerGraph.get(key);
|
|
let alreadyProcessed = processed.get(key);
|
|
if (alreadyProcessed === undefined) {
|
|
/** @type {InnerGraphValueSet} */
|
|
alreadyProcessed = new Set();
|
|
processed.set(key, alreadyProcessed);
|
|
}
|
|
if (value !== true && value !== undefined) {
|
|
for (const item of value) {
|
|
alreadyProcessed.add(item);
|
|
}
|
|
for (const item of value) {
|
|
if (typeof item === "string") {
|
|
newSet.add(item);
|
|
} else if (!item.isPure(compilation, module)) {
|
|
newSet = true;
|
|
break;
|
|
} else {
|
|
const itemValue = innerGraph.get(item);
|
|
if (itemValue === true) {
|
|
newSet = true;
|
|
break;
|
|
}
|
|
if (itemValue !== undefined) {
|
|
for (const i of itemValue) {
|
|
if (i === key) continue;
|
|
if (alreadyProcessed.has(i)) continue;
|
|
newSet.add(i);
|
|
if (typeof i !== "string") {
|
|
isTerminal = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (newSet === true) {
|
|
innerGraph.set(key, true);
|
|
} else if (newSet.size === 0) {
|
|
innerGraph.set(key, undefined);
|
|
} else {
|
|
innerGraph.set(key, newSet);
|
|
}
|
|
}
|
|
if (isTerminal) {
|
|
nonTerminal.delete(key);
|
|
|
|
// For the global key, merge with all other keys
|
|
if (key === null) {
|
|
const globalValue = innerGraph.get(null);
|
|
if (globalValue) {
|
|
for (const [key, value] of innerGraph) {
|
|
if (key !== null && value !== true) {
|
|
if (globalValue === true) {
|
|
innerGraph.set(key, true);
|
|
} else {
|
|
const newSet = new Set(value);
|
|
for (const item of globalValue) {
|
|
newSet.add(item);
|
|
}
|
|
innerGraph.set(key, newSet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @type {Map<Dependency, true | Set<string>>} */
|
|
for (const [symbol, callbacks] of usageCallbackMap) {
|
|
const usage = symbol.isPure(compilation, module)
|
|
? /** @type {true | Set<string> | undefined} */ (innerGraph.get(symbol))
|
|
: true;
|
|
|
|
for (const callback of callbacks) {
|
|
callback(usage === undefined ? false : usage, module);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Module} module module
|
|
* @returns {void}
|
|
*/
|
|
function release(module) {
|
|
states.delete(module);
|
|
}
|
|
|
|
utils = {
|
|
enable,
|
|
bailout,
|
|
isEnabled,
|
|
addUsage,
|
|
addVariableUsage,
|
|
getTopLevelSymbol,
|
|
setTopLevelSymbol,
|
|
onUsage,
|
|
tagTopLevelSymbol,
|
|
inferDependencyUsage,
|
|
release
|
|
};
|
|
innerGraphByCompilation.set(compilation, utils);
|
|
return utils;
|
|
};
|
|
|
|
/**
|
|
* Usage callback map.
|
|
* @param {Dependency} dependency the dependency
|
|
* @param {UsedByExports | undefined} usedByExports usedByExports info
|
|
* @param {ModuleGraph} moduleGraph moduleGraph
|
|
* @returns {null | false | GetConditionFn} function to determine if the connection is active
|
|
*/
|
|
|
|
module.exports.topLevelSymbolTag = topLevelSymbolTag;
|