406 lines
20 KiB
JavaScript
406 lines
20 KiB
JavaScript
"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
|