"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResidualFunctionInitializers = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 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 _index = require("../values/index.js"); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); var _generator = require("../utils/generator.js"); var _traverseFast = require("../utils/traverse-fast.js"); var _traverseFast2 = _interopRequireDefault(_traverseFast); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _internalizer = require("../utils/internalizer.js"); var _factorify = require("./factorify.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 manages information about values // which are only referenced by residual functions, // and it provides the ability to generate initialization code for those values that // can be placed into the residual functions. var ResidualFunctionInitializers = exports.ResidualFunctionInitializers = function () { function ResidualFunctionInitializers(locationService, prelude, initializerNameGenerator) { _classCallCheck(this, ResidualFunctionInitializers); this.functionInitializerInfos = new Map(); this.initializers = new Map(); this.sharedInitializers = new Map(); this.locationService = locationService; this.initializerNameGenerator = initializerNameGenerator; this.prelude = prelude; } _createClass(ResidualFunctionInitializers, [{ key: "registerValueOnlyReferencedByResidualFunctions", value: function registerValueOnlyReferencedByResidualFunctions(functionValues, val) { (0, _invariant2.default)(functionValues.length >= 1); var infos = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = functionValues[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var functionValue = _step.value; var info = this.functionInitializerInfos.get(functionValue); if (info === undefined) this.functionInitializerInfos.set(functionValue, info = { ownId: this.functionInitializerInfos.size.toString(), initializerIds: new Set() }); infos.push(info); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var id = infos.map(function (info) { return info.ownId; }).sort().join(); var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = infos[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var info = _step2.value; info.initializerIds.add(id); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } var initializer = this.initializers.get(id); if (initializer === undefined) this.initializers.set(id, initializer = { id: id, order: infos.length, values: [], body: { type: "DelayInitializations", parentBody: undefined, entries: [] } }); initializer.values.push(val); return initializer.body; } }, { key: "scrubFunctionInitializers", value: function scrubFunctionInitializers() { // Deleting trivial entries in order to avoid creating empty initialization functions that serve no purpose. var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = this.initializers.values()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var initializer = _step3.value; if (initializer.body.entries.length === 0) this.initializers.delete(initializer.id); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = this.functionInitializerInfos[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var _ref = _step4.value; var _ref2 = _slicedToArray(_ref, 2); var functionValue = _ref2[0]; var info = _ref2[1]; var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = info.initializerIds[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var _id = _step5.value; var _initializer = this.initializers.get(_id); if (_initializer === undefined) { info.initializerIds.delete(_id); } } } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } if (info.initializerIds.size === 0) this.functionInitializerInfos.delete(functionValue); } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } } }, { key: "_conditionalInitialization", value: function _conditionalInitialization(initializedValues, initializationStatements) { if (initializationStatements.length === 1 && t.isIfStatement(initializationStatements[0])) { return initializationStatements[0]; } // We have some initialization code, and it should only get executed once, // so we are going to guard it. // First, let's see if one of the initialized values is guaranteed to not // be undefined after initialization. In that case, we can use that state-change // to figure out if initialization needs to run. var location = void 0; var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = initializedValues[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var value = _step6.value; if (!value.mightBeUndefined()) { location = this.locationService.getLocation(value); if (location !== undefined) break; } } } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6.return) { _iterator6.return(); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } if (location === undefined) { // Second, if we didn't find a non-undefined value, let's make one up. // It will transition from `undefined` to `null`. location = this.locationService.createLocation(); initializationStatements.unshift(t.expressionStatement(t.assignmentExpression("=", location, _internalizer.nullExpression))); } return t.ifStatement(t.binaryExpression("===", location, _internalizer.voidExpression), t.blockStatement(initializationStatements)); } }, { key: "hasInitializerStatement", value: function hasInitializerStatement(functionValue) { return !!this.functionInitializerInfos.get(functionValue); } }, { key: "factorifyInitializers", value: function factorifyInitializers(nameGenerator) { var _iteratorNormalCompletion7 = true; var _didIteratorError7 = false; var _iteratorError7 = undefined; try { for (var _iterator7 = this.initializers.values()[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { var initializer = _step7.value; (0, _factorify.factorifyObjects)(initializer.body.entries, nameGenerator); } } catch (err) { _didIteratorError7 = true; _iteratorError7 = err; } finally { try { if (!_iteratorNormalCompletion7 && _iterator7.return) { _iterator7.return(); } } finally { if (_didIteratorError7) { throw _iteratorError7; } } } } }, { key: "getInitializerStatement", value: function getInitializerStatement(functionValue) { var _this = this; var initializerInfo = this.functionInitializerInfos.get(functionValue); if (initializerInfo === undefined) return undefined; (0, _invariant2.default)(initializerInfo.initializerIds.size > 0); var ownInitializer = this.initializers.get(initializerInfo.ownId); var initializedValues = void 0; var initializationStatements = []; var initializers = []; var _iteratorNormalCompletion8 = true; var _didIteratorError8 = false; var _iteratorError8 = undefined; try { for (var _iterator8 = initializerInfo.initializerIds[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { var initializerId = _step8.value; var initializer = this.initializers.get(initializerId); (0, _invariant2.default)(initializer !== undefined); (0, _invariant2.default)(initializer.body.entries.length > 0); initializers.push(initializer); } // Sorting initializers by the number of scopes they are required by. // Note that the scope sets form a lattice, and this sorting effectively // ensures that value initializers that depend on other value initializers // get called in the right order. } catch (err) { _didIteratorError8 = true; _iteratorError8 = err; } finally { try { if (!_iteratorNormalCompletion8 && _iterator8.return) { _iterator8.return(); } } finally { if (_didIteratorError8) { throw _iteratorError8; } } } initializers.sort(function (i, j) { return j.order - i.order; }); var _iteratorNormalCompletion9 = true; var _didIteratorError9 = false; var _iteratorError9 = undefined; try { for (var _iterator9 = initializers[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { var initializer = _step9.value; if (initializerInfo.initializerIds.size === 1 || initializer === ownInitializer) { initializedValues = initializer.values; } if (initializer === ownInitializer) { initializationStatements = initializationStatements.concat(initializer.body.entries); } else { var ast = this.sharedInitializers.get(initializer.id); if (ast === undefined) { (function () { ast = _this._conditionalInitialization(initializer.values, initializer.body.entries); // We inline compact initializers, as calling a function would introduce too much // overhead. To determine if an initializer is compact, we count the number of // nodes in the AST, and check if it exceeds a certain threshold. // TODO #885: Study in more detail which threshold is the best compromise in terms of // code size and performance. var count = 0; (0, _traverseFast2.default)(t.file(t.program([ast])), function (node) { count++; return false; }); if (count > 24) { var _id2 = t.identifier(_this.initializerNameGenerator.generate()); _this.prelude.push(t.functionDeclaration(_id2, [], t.blockStatement([ast]))); ast = t.expressionStatement(t.callExpression(_id2, [])); } _this.sharedInitializers.set(initializer.id, ast); })(); } initializationStatements.push(ast); } } } catch (err) { _didIteratorError9 = true; _iteratorError9 = err; } finally { try { if (!_iteratorNormalCompletion9 && _iterator9.return) { _iterator9.return(); } } finally { if (_didIteratorError9) { throw _iteratorError9; } } } return this._conditionalInitialization(initializedValues || [], initializationStatements); } }]); return ResidualFunctionInitializers; }(); //# sourceMappingURL=ResidualFunctionInitializers.js.map