279 lines
17 KiB
JavaScript
279 lines
17 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.Referentializer = undefined;
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
|
|
* 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.
|
|
*/
|
|
|
|
var _environment = require("../environment.js");
|
|
|
|
var _errors = require("../errors.js");
|
|
|
|
var _index = require("../values/index.js");
|
|
|
|
var _babelTypes = require("babel-types");
|
|
|
|
var t = _interopRequireWildcard(_babelTypes);
|
|
|
|
var _generator = require("../utils/generator.js");
|
|
|
|
var _invariant = require("../invariant.js");
|
|
|
|
var _invariant2 = _interopRequireDefault(_invariant);
|
|
|
|
var _types = require("./types.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; } }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
/*
|
|
* This class helps fixup names in residual functions for variables that these
|
|
* functions capture from parent scopes.
|
|
* For each ReferentializationScope it creates a _get_scope_binding function
|
|
* that contains the initialization for all of that scope's FunctionInstances
|
|
* which will contain a switch statement with all the initializations.
|
|
*/
|
|
|
|
|
|
// Each of these will correspond to a different preludeGenerator and thus will
|
|
// have different values available for initialization. FunctionValues should
|
|
// only be additional functions.
|
|
var Referentializer = exports.Referentializer = function () {
|
|
function Referentializer(options, scopeNameGenerator, referentializedNameGenerator, statistics) {
|
|
_classCallCheck(this, Referentializer);
|
|
|
|
this._options = options;
|
|
this.scopeNameGenerator = scopeNameGenerator;
|
|
this.statistics = statistics;
|
|
|
|
this.referentializationState = new Map();
|
|
this._referentializedNameGenerator = referentializedNameGenerator;
|
|
}
|
|
|
|
_createClass(Referentializer, [{
|
|
key: "_createReferentializationState",
|
|
value: function _createReferentializationState() {
|
|
return {
|
|
capturedScopeInstanceIdx: 0,
|
|
capturedScopesArray: t.identifier(this.scopeNameGenerator.generate("main")),
|
|
capturedScopeAccessFunctionId: t.identifier(this.scopeNameGenerator.generate("get_scope_binding")),
|
|
serializedScopes: new Map()
|
|
};
|
|
}
|
|
}, {
|
|
key: "_getReferentializationState",
|
|
value: function _getReferentializationState(referentializationScope) {
|
|
return (0, _utils.getOrDefault)(this.referentializationState, referentializationScope, this._createReferentializationState.bind(this));
|
|
}
|
|
|
|
// Generate a shared function for accessing captured scope bindings.
|
|
// TODO: skip generating this function if the captured scope is not shared by multiple residual functions.
|
|
|
|
}, {
|
|
key: "createCaptureScopeAccessFunction",
|
|
value: function createCaptureScopeAccessFunction(referentializationScope) {
|
|
var body = [];
|
|
var selectorParam = t.identifier("selector");
|
|
var captured = t.identifier("__captured");
|
|
var capturedScopesArray = this._getReferentializationState(referentializationScope).capturedScopesArray;
|
|
var selectorExpression = t.memberExpression(capturedScopesArray, selectorParam, /*Indexer syntax*/true);
|
|
|
|
// One switch case for one scope.
|
|
var cases = [];
|
|
var serializedScopes = this._getReferentializationState(referentializationScope).serializedScopes;
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = serializedScopes.values()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var scopeBinding = _step.value;
|
|
|
|
var scopeObjectExpression = t.arrayExpression(scopeBinding.initializationValues);
|
|
cases.push(t.switchCase(t.numericLiteral(scopeBinding.id), [t.expressionStatement(t.assignmentExpression("=", selectorExpression, scopeObjectExpression)), t.breakStatement()]));
|
|
}
|
|
// Default case.
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
|
|
cases.push(t.switchCase(null, [t.throwStatement(t.newExpression(t.identifier("Error"), [t.stringLiteral("Unknown scope selector")]))]));
|
|
|
|
body.push(t.variableDeclaration("var", [t.variableDeclarator(captured, selectorExpression)]));
|
|
body.push(t.ifStatement(t.unaryExpression("!", captured), t.blockStatement([t.switchStatement(selectorParam, cases), t.expressionStatement(t.assignmentExpression("=", captured, selectorExpression))])));
|
|
body.push(t.returnStatement(captured));
|
|
var factoryFunction = t.functionExpression(null, [selectorParam], t.blockStatement(body));
|
|
var accessFunctionId = this._getReferentializationState(referentializationScope).capturedScopeAccessFunctionId;
|
|
return t.variableDeclaration("var", [t.variableDeclarator(accessFunctionId, factoryFunction)]);
|
|
}
|
|
}, {
|
|
key: "_getSerializedBindingScopeInstance",
|
|
value: function _getSerializedBindingScopeInstance(residualBinding) {
|
|
var declarativeEnvironmentRecord = residualBinding.declarativeEnvironmentRecord;
|
|
var referentializationScope = residualBinding.referencedOnlyFromAdditionalFunctions || "GLOBAL";
|
|
(0, _invariant2.default)(declarativeEnvironmentRecord);
|
|
|
|
// figure out if this is accessed only from additional functions
|
|
var serializedScopes = this._getReferentializationState(referentializationScope).serializedScopes;
|
|
var scope = serializedScopes.get(declarativeEnvironmentRecord);
|
|
if (!scope) {
|
|
var refState = this._getReferentializationState(referentializationScope);
|
|
scope = {
|
|
name: this.scopeNameGenerator.generate(),
|
|
id: refState.capturedScopeInstanceIdx++,
|
|
initializationValues: [],
|
|
containingAdditionalFunction: residualBinding.referencedOnlyFromAdditionalFunctions
|
|
};
|
|
serializedScopes.set(declarativeEnvironmentRecord, scope);
|
|
}
|
|
|
|
residualBinding.scope = scope;
|
|
return scope;
|
|
}
|
|
}, {
|
|
key: "getReferentializedScopeInitialization",
|
|
value: function getReferentializedScopeInitialization(scope) {
|
|
var capturedScope = scope.capturedScope;
|
|
(0, _invariant2.default)(capturedScope);
|
|
var funcName = this._getReferentializationState(scope.containingAdditionalFunction || "GLOBAL").capturedScopeAccessFunctionId;
|
|
return [t.variableDeclaration("var", [t.variableDeclarator(t.identifier(capturedScope), t.callExpression(funcName, [t.identifier(scope.name)]))])];
|
|
}
|
|
}, {
|
|
key: "referentializeBinding",
|
|
value: function referentializeBinding(residualBinding, name, instance) {
|
|
if (this._options.simpleClosures) {
|
|
// When simpleClosures is enabled, then space for captured mutable bindings is allocated upfront.
|
|
var serializedBindingId = t.identifier(this._referentializedNameGenerator.generate(name));
|
|
var serializedValue = residualBinding.serializedValue;
|
|
(0, _invariant2.default)(serializedValue);
|
|
var declar = t.variableDeclaration("var", [t.variableDeclarator(serializedBindingId, serializedValue)]);
|
|
instance.initializationStatements.push(declar);
|
|
residualBinding.serializedValue = serializedBindingId;
|
|
} else {
|
|
// When simpleClosures is not enabled, then space for captured mutable bindings is allocated lazily.
|
|
var scope = this._getSerializedBindingScopeInstance(residualBinding);
|
|
var capturedScope = "__captured" + scope.name;
|
|
// Save the serialized value for initialization at the top of
|
|
// the factory.
|
|
// This can serialize more variables than are necessary to execute
|
|
// the function because every function serializes every
|
|
// modified variable of its parent scope. In some cases it could be
|
|
// an improvement to split these variables into multiple
|
|
// scopes.
|
|
var variableIndexInScope = scope.initializationValues.length;
|
|
(0, _invariant2.default)(residualBinding.serializedValue);
|
|
scope.initializationValues.push(residualBinding.serializedValue);
|
|
scope.capturedScope = capturedScope;
|
|
|
|
// Replace binding usage with scope references
|
|
residualBinding.serializedValue = t.memberExpression(t.identifier(capturedScope), t.numericLiteral(variableIndexInScope), true // Array style access.
|
|
);
|
|
}
|
|
|
|
residualBinding.referentialized = true;
|
|
this.statistics.referentialized++;
|
|
}
|
|
}, {
|
|
key: "referentialize",
|
|
value: function referentialize(unbound, instances, shouldReferentializeInstanceFn) {
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = instances[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var instance = _step2.value;
|
|
|
|
var residualBindings = instance.residualFunctionBindings;
|
|
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = unbound[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var name = _step3.value;
|
|
|
|
var residualBinding = residualBindings.get(name);
|
|
(0, _invariant2.default)(residualBinding !== undefined);
|
|
if (residualBinding.modified) {
|
|
// Initialize captured scope at function call instead of globally
|
|
if (!residualBinding.referentialized) {
|
|
if (!shouldReferentializeInstanceFn(instance)) {
|
|
// TODO #989: Fix additional functions and referentialization
|
|
throw new _errors.FatalError("TODO: implement referentialization for prepacked functions");
|
|
}
|
|
this.referentializeBinding(residualBinding, name, instance);
|
|
}
|
|
|
|
(0, _invariant2.default)(residualBinding.referentialized);
|
|
if (residualBinding.declarativeEnvironmentRecord && residualBinding.scope) {
|
|
instance.scopeInstances.set(residualBinding.scope.name, residualBinding.scope);
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
key: "createCapturedScopesArrayInitialization",
|
|
value: function createCapturedScopesArrayInitialization(referentializationScope) {
|
|
return t.variableDeclaration("var", [t.variableDeclarator(this._getReferentializationState(referentializationScope).capturedScopesArray, t.callExpression(t.identifier("Array"), [t.numericLiteral(this._getReferentializationState(referentializationScope).capturedScopeInstanceIdx)]))]);
|
|
}
|
|
}]);
|
|
|
|
return Referentializer;
|
|
}();
|
|
//# sourceMappingURL=Referentializer.js.map
|