prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
615 lines (533 loc) • 28.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ResidualFunctions = undefined;
var _errors = require("../errors.js");
var _realm = require("../realm.js");
var _index = require("../values/index.js");
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _babelTraverse = require("babel-traverse");
var _babelTraverse2 = _interopRequireDefault(_babelTraverse);
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
var _types = require("./types.js");
var _visitors = require("./visitors.js");
var _modules = require("../utils/modules.js");
var _ResidualFunctionInitializers = require("./ResidualFunctionInitializers.js");
var _internalizer = require("../utils/internalizer.js");
var _Referentializer = require("./Referentializer.js");
var _utils = require("./utils.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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; } }
class ResidualFunctions {
constructor(realm, statistics, options, modules, requireReturns, locationService, prelude, initializerNameGenerator, factoryNameGenerator, residualFunctionInfos, residualFunctionInstances, residualClassMethodInstances, additionalFunctionValueInfos, additionalFunctionValueNestedFunctions, referentializer) {
this.realm = realm;
this.statistics = statistics;
this.modules = modules;
this.requireReturns = requireReturns;
this.locationService = locationService;
this.prelude = prelude;
this.factoryNameGenerator = factoryNameGenerator;
this.functionPrototypes = new Map();
this.firstFunctionUsages = new Map();
this.functions = new Map();
this.classes = new Map();
this.functionInstances = [];
this.residualFunctionInitializers = new _ResidualFunctionInitializers.ResidualFunctionInitializers(locationService, prelude, initializerNameGenerator);
this.residualFunctionInfos = residualFunctionInfos;
this.residualFunctionInstances = residualFunctionInstances;
this.residualClassMethodInstances = residualClassMethodInstances;
this.additionalFunctionValueInfos = additionalFunctionValueInfos;
this.referentializer = referentializer;
for (let instance of residualFunctionInstances.values()) {
(0, _invariant2.default)(instance !== undefined);
if (!additionalFunctionValueInfos.has(instance.functionValue)) this.addFunctionInstance(instance);
}
this.additionalFunctionValueNestedFunctions = additionalFunctionValueNestedFunctions;
this.simpleClosures = !!options.simpleClosures;
}
addFunctionInstance(instance) {
this.functionInstances.push(instance);
let code = instance.functionValue.$ECMAScriptCode;
(0, _invariant2.default)(code != null);
(0, _utils.getOrDefault)(this.functions, code, () => []).push(instance);
}
setFunctionPrototype(constructor, prototypeId) {
this.functionPrototypes.set(constructor, prototypeId);
}
addFunctionUsage(val, bodyReference) {
if (!this.firstFunctionUsages.has(val)) this.firstFunctionUsages.set(val, bodyReference);
}
_shouldUseFactoryFunction(funcBody, instances) {
function shouldInlineFunction() {
let shouldInline = true;
if (funcBody.start && funcBody.end) {
let bodySize = funcBody.end - funcBody.start;
shouldInline = bodySize <= 30;
}
return shouldInline;
}
let functionInfo = this.residualFunctionInfos.get(funcBody);
(0, _invariant2.default)(functionInfo);
let { usesArguments } = functionInfo;
return !shouldInlineFunction() && instances.length > 1 && !usesArguments && !this.simpleClosures;
}
// Note: this function takes linear time. Please do not call it inside loop.
_hasRewrittenFunctionInstance(rewrittenAdditionalFunctions, instances) {
return instances.find(instance => rewrittenAdditionalFunctions.has(instance.functionValue)) !== undefined;
}
_generateFactoryFunctionInfos(rewrittenAdditionalFunctions) {
const factoryFunctionInfos = new Map();
for (const [functionBody, instances] of this.functions) {
(0, _invariant2.default)(instances.length > 0);
let factoryId;
const suffix = instances[0].functionValue.__originalName || this.realm.debugNames ? "factoryFunction" : "";
if (this._shouldUseFactoryFunction(functionBody, instances)) {
// Rewritten function should never use factory function.
(0, _invariant2.default)(!this._hasRewrittenFunctionInstance(rewrittenAdditionalFunctions, instances));
factoryId = t.identifier(this.factoryNameGenerator.generate(suffix));
} else {
// For inline function body case, use the first function as the factory function.
factoryId = this.locationService.getLocation(instances[0].functionValue);
}
const functionUniqueTag = functionBody.uniqueOrderedTag;
(0, _invariant2.default)(functionUniqueTag);
const functionInfo = this.residualFunctionInfos.get(functionBody);
(0, _invariant2.default)(functionInfo);
factoryFunctionInfos.set(functionUniqueTag, { factoryId, functionInfo });
}
return factoryFunctionInfos;
}
// Preserve residual functions' ordering based on its ast dfs traversal order.
// This is necessary to prevent unexpected code locality issues.
_sortFunctionByOriginalOrdering(functionEntries) {
functionEntries.sort((funcA, funcB) => {
const funcAUniqueTag = funcA[0].uniqueOrderedTag;
(0, _invariant2.default)(funcAUniqueTag);
const funcBUniqueTag = funcB[0].uniqueOrderedTag;
(0, _invariant2.default)(funcBUniqueTag);
return funcAUniqueTag - funcBUniqueTag;
});
}
spliceFunctions(rewrittenAdditionalFunctions) {
this.residualFunctionInitializers.scrubFunctionInitializers();
let functionBodies = new Map();
// these need to get spliced in at the end
let additionalFunctionPreludes = new Map();
let additionalFunctionModifiedBindingsSegment = new Map();
let getModifiedBindingsSegment = additionalFunction => (0, _utils.getOrDefault)(additionalFunctionModifiedBindingsSegment, additionalFunction, () => []);
let getFunctionBody = instance => (0, _utils.getOrDefault)(functionBodies, instance, () => []);
let globalPrelude = this.prelude;
function getPrelude(instance) {
let additionalFunction = instance.containingAdditionalFunction;
let b;
if (additionalFunction) {
b = (0, _utils.getOrDefault)(additionalFunctionPreludes, additionalFunction, () => []);
} else {
b = globalPrelude;
}
return b;
}
let requireStatistics = { replaced: 0, count: 0 };
let functionEntries = Array.from(this.functions.entries());
this._sortFunctionByOriginalOrdering(functionEntries);
this.statistics.functions = functionEntries.length;
let unstrictFunctionBodies = [];
let strictFunctionBodies = [];
let funcNodes = new Map();
let defineFunction = (instance, funcId, funcOrClassNode) => {
let { functionValue } = instance;
if (instance.initializationStatements.length > 0) {
// always add initialization statements to insertion point
let initializationBody = getFunctionBody(instance);
Array.prototype.push.apply(initializationBody, instance.initializationStatements);
}
let body;
if (t.isFunctionExpression(funcOrClassNode)) {
funcNodes.set(functionValue, funcOrClassNode);
body = getPrelude(instance);
} else {
(0, _invariant2.default)(t.isCallExpression(funcOrClassNode) || t.isClassExpression(funcOrClassNode)); // .bind call
body = getFunctionBody(instance);
}
body.push(t.variableDeclaration("var", [t.variableDeclarator(funcId, funcOrClassNode)]));
let prototypeId = this.functionPrototypes.get(functionValue);
if (prototypeId !== undefined) {
let id = this.locationService.getLocation(functionValue);
(0, _invariant2.default)(id !== undefined);
body.push(t.variableDeclaration("var", [t.variableDeclarator(prototypeId, t.memberExpression(id, t.identifier("prototype")))]));
}
};
// Emit code for ModifiedBindings for additional functions
for (let [funcValue, funcInfo] of this.additionalFunctionValueInfos) {
for (let [, residualBinding] of funcInfo.modifiedBindings) {
let scope = residualBinding.scope;
// TODO #989: This should probably be an invariant once captures work properly
// Currently we don't referentialize bindings in additional functions (but we
// do for bindings nested in additional functions)
if (!residualBinding.referentialized) continue;
// Find the proper prelude to emit to (global vs additional function's prelude)
let bodySegment = getModifiedBindingsSegment(funcValue);
// binding has been referentialized, so setup the scope to be able to
// access bindings from other __captured_scopes initializers
if (scope && scope.containingAdditionalFunction !== funcValue) {
let decl = t.variableDeclaration("var", [t.variableDeclarator(t.identifier(scope.name), t.numericLiteral(scope.id))]);
let init = this.referentializer.getReferentializedScopeInitialization(scope);
bodySegment.push(decl);
// flow forces me to do this
Array.prototype.push.apply(bodySegment, init);
}
let newValue = residualBinding.additionalValueSerialized;
(0, _invariant2.default)(newValue);
let binding_reference = residualBinding.serializedValue;
(0, _invariant2.default)(binding_reference);
(0, _invariant2.default)(t.isLVal(binding_reference), "Referentialized values are always LVals");
// This mutation is safe because it should always be either a global identifier (for global bindings)
// or an accessor to a referentialized value.
bodySegment.push(t.expressionStatement(t.assignmentExpression("=", binding_reference, newValue)));
}
}
// Process Additional Functions
for (let [funcValue, additionalFunctionInfo] of this.additionalFunctionValueInfos.entries()) {
let { instance } = additionalFunctionInfo;
let functionValue = funcValue;
let params = functionValue.$FormalParameters;
(0, _invariant2.default)(params !== undefined);
let rewrittenBody = rewrittenAdditionalFunctions.get(funcValue);
(0, _invariant2.default)(rewrittenBody);
// rewritten functions shouldn't have references fixed up because the body,
// consists of serialized code. For simplicity we emit their instances in a naive way
let functionBody = t.blockStatement(rewrittenBody);
let funcParams = params.slice();
let funcOrClassNode;
if (this.residualClassMethodInstances.has(funcValue)) {
let classMethodInstance = this.residualClassMethodInstances.get(funcValue);
(0, _invariant2.default)(classMethodInstance);
let {
methodType,
classMethodKeyNode,
classSuperNode,
classMethodComputed,
classPrototype,
classMethodIsStatic
} = classMethodInstance;
let isConstructor = methodType === "constructor";
(0, _invariant2.default)(classPrototype instanceof _index.ObjectValue);
(0, _invariant2.default)(classMethodKeyNode && (t.isExpression(classMethodKeyNode) || t.isIdentifier(classMethodKeyNode)));
// we use the classPrototype as the key to get the class expression ast node
funcOrClassNode = this._getOrCreateClassNode(classPrototype);
let classMethod = t.classMethod(methodType, classMethodKeyNode, funcParams, functionBody, classMethodComputed, classMethodIsStatic);
// add the class method to the class expression node body
if (isConstructor) {
funcOrClassNode.body.body.unshift(classMethod);
} else {
funcOrClassNode.body.body.push(classMethod);
}
// we only return the funcOrClassNode if this is the constructor
if (!isConstructor) {
continue;
}
// handle the class super
if (classSuperNode !== undefined) {
funcOrClassNode.superClass = classSuperNode;
}
} else {
funcOrClassNode = t.functionExpression(null, funcParams, functionBody);
}
let id = this.locationService.getLocation(funcValue);
(0, _invariant2.default)(id !== undefined);
if (funcValue.$Strict) {
strictFunctionBodies.push(funcOrClassNode);
} else {
unstrictFunctionBodies.push(funcOrClassNode);
}
defineFunction(instance, id, funcOrClassNode);
}
// Process normal functions
const factoryFunctionInfos = this._generateFactoryFunctionInfos(rewrittenAdditionalFunctions);
for (let [funcBody, instances] of functionEntries) {
let functionInfo = this.residualFunctionInfos.get(funcBody);
(0, _invariant2.default)(functionInfo);
let { unbound, modified, usesThis } = functionInfo;
let params = instances[0].functionValue.$FormalParameters;
(0, _invariant2.default)(params !== undefined);
// Split instances into normal or nested in an additional function
let normalInstances = [];
let additionalFunctionNestedInstances = [];
for (let instance of instances) {
if (this.additionalFunctionValueNestedFunctions.has(instance.functionValue)) additionalFunctionNestedInstances.push(instance);else normalInstances.push(instance);
}
let naiveProcessInstances = instancesToSplice => {
this.statistics.functionClones += instancesToSplice.length - 1;
for (let instance of instancesToSplice) {
let { functionValue, residualFunctionBindings, scopeInstances } = instance;
let funcOrClassNode;
if (this.residualClassMethodInstances.has(functionValue)) {
let classMethodInstance = this.residualClassMethodInstances.get(functionValue);
(0, _invariant2.default)(classMethodInstance);
let {
classSuperNode,
classMethodKeyNode,
methodType,
classMethodComputed,
classPrototype,
classMethodIsStatic
} = classMethodInstance;
let isConstructor = methodType === "constructor";
(0, _invariant2.default)(classPrototype instanceof _index.ObjectValue);
(0, _invariant2.default)(classMethodKeyNode);
(0, _invariant2.default)(t.isExpression(classMethodKeyNode) || t.isIdentifier(classMethodKeyNode));
// we use the classPrototype as the key to get the class expression ast node
funcOrClassNode = this._getOrCreateClassNode(classPrototype);
// if we are dealing with a constructor, don't serialize it if the original
// had an empty user-land constructor (because we create a constructor behind the scenes for them)
let hasEmptyConstructor = !!functionValue.$HasEmptyConstructor;
if (!isConstructor || isConstructor && !hasEmptyConstructor) {
let methodParams = params.slice();
let methodBody = t.cloneDeep(funcBody);
// create the class method AST
let classMethod = t.classMethod(methodType, classMethodKeyNode, methodParams, methodBody, classMethodComputed, classMethodIsStatic);
// traverse and replace refs in the class method
(0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(t.classExpression(null, null, t.classBody([classMethod]), []))])), _visitors.ClosureRefReplacer, null, {
residualFunctionBindings,
modified,
requireReturns: this.requireReturns,
requireStatistics,
getModuleIdIfNodeIsRequireFunction: this.modules.getGetModuleIdIfNodeIsRequireFunction(methodParams, [functionValue]),
factoryFunctionInfos
});
// add the class method to the class expression node body
if (isConstructor) {
funcOrClassNode.body.body.unshift(classMethod);
} else {
funcOrClassNode.body.body.push(classMethod);
}
}
// we only return the funcOrClassNode if this is the constructor
if (!isConstructor) {
continue;
}
// handle the class super
if (classSuperNode !== undefined) {
funcOrClassNode.superClass = classSuperNode;
}
} else {
let funcParams = params.slice();
funcOrClassNode = t.functionExpression(null, funcParams, t.cloneDeep(funcBody));
let scopeInitialization = [];
for (let [scopeName, scope] of scopeInstances) {
scopeInitialization.push(t.variableDeclaration("var", [t.variableDeclarator(t.identifier(scopeName), t.numericLiteral(scope.id))]));
scopeInitialization = scopeInitialization.concat(this.referentializer.getReferentializedScopeInitialization(scope));
}
funcOrClassNode.body.body = scopeInitialization.concat(funcOrClassNode.body.body);
(0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(funcOrClassNode)])), _visitors.ClosureRefReplacer, null, {
residualFunctionBindings,
modified,
requireReturns: this.requireReturns,
requireStatistics,
getModuleIdIfNodeIsRequireFunction: this.modules.getGetModuleIdIfNodeIsRequireFunction(funcParams, [functionValue]),
factoryFunctionInfos
});
}
let id = this.locationService.getLocation(functionValue);
(0, _invariant2.default)(id !== undefined);
if (functionValue.$Strict) {
strictFunctionBodies.push(funcOrClassNode);
} else {
unstrictFunctionBodies.push(funcOrClassNode);
}
(0, _invariant2.default)(id !== undefined);
(0, _invariant2.default)(funcOrClassNode !== undefined);
defineFunction(instance, id, funcOrClassNode);
}
};
if (additionalFunctionNestedInstances.length > 0) naiveProcessInstances(additionalFunctionNestedInstances);
if (!this._shouldUseFactoryFunction(funcBody, normalInstances)) {
naiveProcessInstances(normalInstances);
} else if (normalInstances.length > 0) {
const functionUniqueTag = funcBody.uniqueOrderedTag;
(0, _invariant2.default)(functionUniqueTag);
const factoryInfo = factoryFunctionInfos.get(functionUniqueTag);
(0, _invariant2.default)(factoryInfo);
const { factoryId } = factoryInfo;
// filter included variables to only include those that are different
let factoryNames = [];
let sameResidualBindings = new Map();
for (let name of unbound) {
let isDifferent = false;
let lastBinding;
let firstBinding = normalInstances[0].residualFunctionBindings.get(name);
(0, _invariant2.default)(firstBinding);
if (firstBinding.modified) {
// Must modify for traversal
sameResidualBindings.set(name, firstBinding);
continue;
}
for (let _ref of normalInstances) {
let { residualFunctionBindings } = _ref;
let residualBinding = residualFunctionBindings.get(name);
(0, _invariant2.default)(residualBinding);
(0, _invariant2.default)(!residualBinding.modified);
if (!lastBinding) {
lastBinding = residualBinding;
} else if (!(0, _types.AreSameResidualBinding)(this.realm, residualBinding, lastBinding)) {
isDifferent = true;
break;
}
}
if (isDifferent) {
factoryNames.push(name);
} else {
(0, _invariant2.default)(lastBinding);
sameResidualBindings.set(name, lastBinding);
}
}
let factoryParams = [];
for (let key of factoryNames) {
factoryParams.push(t.identifier(key));
}
let scopeInitialization = [];
for (let [scopeName, scope] of normalInstances[0].scopeInstances) {
factoryParams.push(t.identifier(scopeName));
scopeInitialization = scopeInitialization.concat(this.referentializer.getReferentializedScopeInitialization(scope));
}
factoryParams = factoryParams.concat(params).slice();
// The Replacer below mutates the AST while the original AST may still be referenced
// by another outer residual function so let's clone the original AST to avoid modifying it.
let factoryNode = t.functionExpression(null, factoryParams, t.cloneDeep(funcBody));
if (normalInstances[0].functionValue.$Strict) {
strictFunctionBodies.push(factoryNode);
} else {
unstrictFunctionBodies.push(factoryNode);
}
factoryNode.body.body = scopeInitialization.concat(factoryNode.body.body);
// factory functions do not depend on any nested generator scope, so they go to the prelude
let factoryDeclaration = t.variableDeclaration("var", [t.variableDeclarator(factoryId, factoryNode)]);
this.prelude.push(factoryDeclaration);
(0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(factoryNode)])), _visitors.ClosureRefReplacer, null, {
residualFunctionBindings: sameResidualBindings,
modified,
requireReturns: this.requireReturns,
requireStatistics,
getModuleIdIfNodeIsRequireFunction: this.modules.getGetModuleIdIfNodeIsRequireFunction(factoryParams, normalInstances.map(instance => instance.functionValue)),
factoryFunctionInfos
});
for (let instance of normalInstances) {
let { functionValue, residualFunctionBindings, insertionPoint } = instance;
let functionId = this.locationService.getLocation(functionValue);
(0, _invariant2.default)(functionId !== undefined);
let hasFunctionArg = false;
let flatArgs = factoryNames.map(name => {
let residualBinding = residualFunctionBindings.get(name);
(0, _invariant2.default)(residualBinding);
let serializedValue = residualBinding.serializedValue;
hasFunctionArg = hasFunctionArg || residualBinding.value && residualBinding.value instanceof _index.FunctionValue;
(0, _invariant2.default)(serializedValue);
return serializedValue;
});
for (let entry of instance.scopeInstances) {
flatArgs.push(t.numericLiteral(entry[1].id));
}
let funcNode;
let firstUsage = this.firstFunctionUsages.get(functionValue);
(0, _invariant2.default)(insertionPoint !== undefined);
if (
// The same free variables in shared instances may refer to objects with different initialization values
// so a stub forward function is needed during delay initializations.
this.residualFunctionInitializers.hasInitializerStatement(functionValue) || usesThis || hasFunctionArg || firstUsage !== undefined && !firstUsage.isNotEarlierThan(insertionPoint) || this.functionPrototypes.get(functionValue) !== undefined || this.simpleClosures) {
let callArgs = [t.thisExpression()];
for (let flatArg of flatArgs) callArgs.push(flatArg);
for (let param of params) {
if (param.type !== "Identifier") {
throw new _errors.FatalError("TODO: do not know how to deal with non-Identifier parameters");
}
callArgs.push(param);
}
let callee = t.memberExpression(factoryId, t.identifier("call"));
let childBody = t.blockStatement([t.returnStatement(t.callExpression(callee, callArgs))]);
funcNode = t.functionExpression(null, params, childBody);
if (functionValue.$Strict) {
strictFunctionBodies.push(funcNode);
} else {
unstrictFunctionBodies.push(funcNode);
}
} else {
funcNode = t.callExpression(t.memberExpression(factoryId, t.identifier("bind")), [_internalizer.nullExpression].concat(flatArgs));
}
defineFunction(instance, functionId, funcNode);
}
}
}
for (let referentializationScope of this.referentializer.referentializationState.keys()) {
let prelude = this.prelude;
// Get the prelude for this additional function value
if (referentializationScope !== "GLOBAL") {
let additionalFunction = referentializationScope;
prelude = (0, _utils.getOrDefault)(additionalFunctionPreludes, additionalFunction, () => []);
}
prelude.unshift(this.referentializer.createCaptureScopeAccessFunction(referentializationScope));
prelude.unshift(this.referentializer.createCapturedScopesArrayInitialization(referentializationScope));
}
for (let instance of this.functionInstances.reverse()) {
let functionBody = functionBodies.get(instance);
if (functionBody !== undefined) {
let insertionPoint = instance.insertionPoint;
(0, _invariant2.default)(insertionPoint instanceof _types.BodyReference);
// v8 seems to do something clever with array splicing, so this potentially
// expensive operations seems to be actually cheap.
Array.prototype.splice.apply(insertionPoint.body.entries, [insertionPoint.index, 0].concat(functionBody));
}
}
for (let [additionalFunction, body] of Array.from(rewrittenAdditionalFunctions.entries()).reverse()) {
let additionalFunctionInfo = this.additionalFunctionValueInfos.get(additionalFunction);
(0, _invariant2.default)(additionalFunctionInfo);
let bodySegment = additionalFunctionModifiedBindingsSegment.get(additionalFunction);
let funcBody = getFunctionBody(additionalFunctionInfo.instance);
let prelude = additionalFunctionPreludes.get(additionalFunction);
let insertionPoint = additionalFunctionInfo.instance.insertionPoint;
(0, _invariant2.default)(insertionPoint);
Array.prototype.splice.apply(insertionPoint.body.entries, [insertionPoint.index, 0].concat(funcBody));
if (prelude) body.unshift(...prelude);
if (bodySegment) {
if (body.length > 0 && additionalFunctionInfo.hasReturn) {
let returnStatement = body.pop();
body.push(...bodySegment, returnStatement);
} else {
body.push(...bodySegment);
}
}
}
// Inject initializer code for indexed vars into functions (for delay initializations)
for (let [functionValue, funcNode] of funcNodes) {
let initializerStatement = this.residualFunctionInitializers.getInitializerStatement(functionValue);
if (initializerStatement !== undefined) {
(0, _invariant2.default)(t.isFunctionExpression(funcNode));
let blockStatement = funcNode.body;
blockStatement.body.unshift(initializerStatement);
}
}
return { unstrictFunctionBodies, strictFunctionBodies, requireStatistics };
}
_getOrCreateClassNode(classPrototype) {
if (!this.classes.has(classPrototype)) {
let funcOrClassNode = t.classExpression(null, null, t.classBody([]), []);
this.classes.set(classPrototype, funcOrClassNode);
return funcOrClassNode;
} else {
let funcOrClassNode = this.classes.get(classPrototype);
(0, _invariant2.default)(funcOrClassNode && t.isClassExpression(funcOrClassNode));
return funcOrClassNode;
}
}
}
exports.ResidualFunctions = ResidualFunctions; /**
* 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.
*/
//# sourceMappingURL=ResidualFunctions.js.map