"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