"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Emitter = 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 _index = require("../values/index.js"); var _generator = require("../utils/generator.js"); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _types = require("./types.js"); var _ResidualFunctions = require("./ResidualFunctions.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // The emitter keeps track of a stack of what's currently being emitted. // There are two kinds of interesting dependencies the emitter is dealing with: // 1. Value dependencies: // If an emission task depends on the result of another emission task which // is still currently being emitted, then the emission task must be performed later, // once the dependency is available. // To this end, the emitter maintains the `_activeValues` and `_waitingForValues` datastructures. // 2. Generator dependencies: // For each generator, there's a corresponding "body", i.e. a stream of babel statements // that the emitter is appending to. // There's always a "current" body that is currently being emitted to. // There's also a distinguished `mainBody` to which all statements get directly or indirectly appended. // If there are multiple generators/bodies involved, then they form a stack. // Nested bodies are usually composed into an instruction emitted to the outer body. // For example, two nested generators may yield the then and else-branch of an `if` statement. // When an emission is supposed to target a body that is the current body, i.e. when it sits // lower on the stack, then the emission task gets delayed until the next emission task on // the lower body entry is finished. // To this end, the emitter maintains the `_activeGeneratorStack` and `_waitingForBodies` datastructures. var Emitter = exports.Emitter = function () { function Emitter(residualFunctions) { _classCallCheck(this, Emitter); var mainBody = { type: "MainGenerator", parentBody: undefined, entries: [] }; this._waitingForValues = new Map(); this._waitingForBodies = new Map(); this._body = mainBody; this._declaredAbstractValues = new Map(); this._residualFunctions = residualFunctions; this._activeStack = []; this._activeValues = new Set(); this._activeGeneratorStack = [mainBody]; this._finalized = false; } // Contains all the active generator bodies in stack order. _createClass(Emitter, [{ key: "beginEmitting", value: function beginEmitting(dependency, targetBody) { var isChild = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; (0, _invariant2.default)(!this._finalized); this._activeStack.push(dependency); if (dependency instanceof _index.Value) { (0, _invariant2.default)(!this._activeValues.has(dependency)); this._activeValues.add(dependency); } else if (dependency instanceof _generator.Generator) { (0, _invariant2.default)(!this._activeGeneratorStack.includes(targetBody)); this._activeGeneratorStack.push(targetBody); } if (isChild) { targetBody.parentBody = this._body; } var oldBody = this._body; this._body = targetBody; return oldBody; } }, { key: "emit", value: function emit(statement) { (0, _invariant2.default)(!this._finalized); this._body.entries.push(statement); this._processCurrentBody(); } }, { key: "endEmitting", value: function endEmitting(dependency, oldBody) { (0, _invariant2.default)(!this._finalized); var lastDependency = this._activeStack.pop(); (0, _invariant2.default)(dependency === lastDependency); if (dependency instanceof _index.Value) { (0, _invariant2.default)(this._activeValues.has(dependency)); this._activeValues.delete(dependency); this._processValue(dependency); } else if (dependency instanceof _generator.Generator) { (0, _invariant2.default)(this._isEmittingActiveGenerator()); this._activeGeneratorStack.pop(); } var lastBody = this._body; this._body = oldBody; return lastBody; } }, { key: "finalize", value: function finalize() { (0, _invariant2.default)(!this._finalized); (0, _invariant2.default)(this._activeGeneratorStack.length === 1); (0, _invariant2.default)(this._activeGeneratorStack[0] === this._body); this._processCurrentBody(); this._activeGeneratorStack.pop(); this._finalized = true; (0, _invariant2.default)(this._waitingForBodies.size === 0); (0, _invariant2.default)(this._waitingForValues.size === 0); (0, _invariant2.default)(this._activeStack.length === 0); (0, _invariant2.default)(this._activeValues.size === 0); (0, _invariant2.default)(this._activeGeneratorStack.length === 0); } /** * Emitter is emitting in two modes: * 1. Emitting to entries in current active generator * 2. Emitting to body of another scope(generator or residual function) * This function checks the first condition above. */ }, { key: "_isEmittingActiveGenerator", value: function _isEmittingActiveGenerator() { (0, _invariant2.default)(this._activeGeneratorStack.length > 0); return this._activeGeneratorStack[this._activeGeneratorStack.length - 1] === this._body; } }, { key: "_isGeneratorBody", value: function _isGeneratorBody(body) { return body.type === "MainGenerator" || body.type === "Generator"; } }, { key: "_processCurrentBody", value: function _processCurrentBody() { if (!this._isEmittingActiveGenerator()) { return; } var a = this._waitingForBodies.get(this._body); if (a === undefined) return; while (a.length > 0) { var _a$shift = a.shift(), _dependencies = _a$shift.dependencies, _func = _a$shift.func; this.emitNowOrAfterWaitingForDependencies(_dependencies, _func); } this._waitingForBodies.delete(this._body); } }, { key: "_processValue", value: function _processValue(value) { var a = this._waitingForValues.get(value); if (a === undefined) return; var currentBody = this._body; while (a.length > 0) { var _a$shift2 = a.shift(), _body = _a$shift2.body, _dependencies2 = _a$shift2.dependencies, _func2 = _a$shift2.func; // If body is not generator body no need to wait for it. if (this._isGeneratorBody(_body) && _body !== currentBody) { this._emitAfterWaitingForGeneratorBody(_body, _dependencies2, _func2); } else { this.emitNowOrAfterWaitingForDependencies(_dependencies2, _func2, _body); } } this._waitingForValues.delete(value); } // Find the first ancestor in input generator body stack that is in current active stack. // It can always find one because the bottom one in the stack is the main generator. }, { key: "_getFirstAncestorGeneratorWithActiveBody", value: function _getFirstAncestorGeneratorWithActiveBody(bodyStack) { var _this = this; var activeBody = bodyStack.slice().reverse().find(function (body) { return _this._activeGeneratorStack.includes(body); }); (0, _invariant2.default)(activeBody); return activeBody; } // Serialization of a statement related to a value MUST be delayed if // the creation of the value's identity requires the availability of either: // 1. a time-dependent value that is declared by some generator entry // that has not yet been processed // (tracked by `_declaredAbstractValues`), or // 2. a value that is also currently being serialized // (tracked by `_activeValues`). // 3. a generator body that is higher(near top) in generator body stack. // (tracked by `_activeGeneratorStack`) }, { key: "getReasonToWaitForDependencies", value: function getReasonToWaitForDependencies(dependencies) { (0, _invariant2.default)(!this._finalized); if (Array.isArray(dependencies)) { var values = dependencies; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = values[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var value = _step.value; var _delayReason = this.getReasonToWaitForDependencies(value); if (_delayReason) return _delayReason; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return undefined; } var val = dependencies; if (this._activeValues.has(val)) return val; var delayReason = void 0; if (val instanceof _index.BoundFunctionValue) { delayReason = this.getReasonToWaitForDependencies(val.$BoundTargetFunction); if (delayReason) return delayReason; delayReason = this.getReasonToWaitForDependencies(val.$BoundThis); if (delayReason) return delayReason; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = val.$BoundArguments[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var arg = _step2.value; delayReason = this.getReasonToWaitForDependencies(arg); if (delayReason) return delayReason; } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } else if (val instanceof _index.FunctionValue) { this._residualFunctions.addFunctionUsage(val, this.getBodyReference()); return undefined; } else if (val instanceof _index.AbstractValue) { if (val.hasIdentifier()) { var valSerializeBodyStack = this._declaredAbstractValues.get(val); if (!valSerializeBodyStack) { // Hasn't been serialized yet. return val; } else { // The dependency has already been serialized(declared). But we may still have to wait for // current generator body to be available, under following conditions: // 1. Currently emitting in generator body. -- and // 2. Not emitting in current active generator.(otherwise no need to wait) -- and // 3. Dependency's active ancestor generator body is higher(near top) in generator stack than current body. var valActiveAncestorBody = this._getFirstAncestorGeneratorWithActiveBody(valSerializeBodyStack); (0, _invariant2.default)(this._activeGeneratorStack.includes(valActiveAncestorBody)); if (this._isGeneratorBody(this._body)) { (0, _invariant2.default)(this._activeGeneratorStack.includes(this._body)); if (!this._isEmittingActiveGenerator() && this._activeGeneratorStack.indexOf(valActiveAncestorBody) > this._activeGeneratorStack.indexOf(this._body)) { return this._body; } } } } var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = val.args[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var _arg = _step3.value; delayReason = this.getReasonToWaitForDependencies(_arg); if (delayReason) return delayReason; } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } } else if (val instanceof _index.ProxyValue) { delayReason = this.getReasonToWaitForDependencies(val.$ProxyTarget); if (delayReason) return delayReason; delayReason = this.getReasonToWaitForDependencies(val.$ProxyHandler); if (delayReason) return delayReason; } else if (val instanceof _index.SymbolValue) { if (val.$Description instanceof _index.Value) { delayReason = this.getReasonToWaitForDependencies(val.$Description); if (delayReason) return delayReason; } } else if (val instanceof _index.ObjectValue) { var kind = val.getKind(); switch (kind) { case "Object": var proto = val.$Prototype; if (proto instanceof _index.ObjectValue) { delayReason = this.getReasonToWaitForDependencies(val.$Prototype); if (delayReason) return delayReason; } break; case "Date": (0, _invariant2.default)(val.$DateValue !== undefined); delayReason = this.getReasonToWaitForDependencies(val.$DateValue); if (delayReason) return delayReason; break; default: break; } } return undefined; } // Wait for a known-to-be active value if a condition is met. }, { key: "getReasonToWaitForActiveValue", value: function getReasonToWaitForActiveValue(value, condition) { (0, _invariant2.default)(!this._finalized); (0, _invariant2.default)(this._activeValues.has(value)); return condition ? value : undefined; } }, { key: "_shouldEmitWithoutWaiting", value: function _shouldEmitWithoutWaiting(delayReason, targetBody) { /** * We can directly emit without waiting if: * 1. No delayReason * 2. delayReason is a generator body while the target body we are not emitting into is not a generator body. */ return !delayReason || !(delayReason instanceof _index.Value) && this._isGeneratorBody(delayReason) && targetBody !== undefined && !this._isGeneratorBody(targetBody); } }, { key: "emitAfterWaiting", value: function emitAfterWaiting(delayReason, dependencies, func, targetBody) { if (this._shouldEmitWithoutWaiting(delayReason, targetBody)) { if (targetBody === undefined || targetBody === this._body) { // Emit into current body. func(); } else { (0, _invariant2.default)(!this._isGeneratorBody(targetBody)); var oldBody = this.beginEmitting(targetBody.type, targetBody); func(); this.endEmitting(targetBody.type, oldBody); } } else { (0, _invariant2.default)(delayReason !== undefined); if (delayReason instanceof _index.Value) { this._emitAfterWaitingForValue(delayReason, dependencies, targetBody === undefined ? this._body : targetBody, func); } else if (this._isGeneratorBody(delayReason)) { // delayReason is a generator body. this._emitAfterWaitingForGeneratorBody(delayReason, dependencies, func); } else { // Unknown delay reason. (0, _invariant2.default)(false); } } } }, { key: "_emitAfterWaitingForValue", value: function _emitAfterWaitingForValue(reason, dependencies, targetBody, func) { (0, _invariant2.default)(!this._finalized); (0, _invariant2.default)(!(reason instanceof _index.AbstractValue && this._declaredAbstractValues.has(reason)) || this._activeValues.has(reason)); var a = this._waitingForValues.get(reason); if (a === undefined) this._waitingForValues.set(reason, a = []); a.push({ body: targetBody, dependencies: dependencies, func: func }); } }, { key: "_emitAfterWaitingForGeneratorBody", value: function _emitAfterWaitingForGeneratorBody(reason, dependencies, func) { (0, _invariant2.default)(this._isGeneratorBody(reason)); (0, _invariant2.default)(!this._finalized); (0, _invariant2.default)(this._activeGeneratorStack.includes(reason)); var b = this._waitingForBodies.get(reason); if (b === undefined) this._waitingForBodies.set(reason, b = []); b.push({ dependencies: dependencies, func: func }); } }, { key: "emitNowOrAfterWaitingForDependencies", value: function emitNowOrAfterWaitingForDependencies(dependencies, func, targetBody) { (0, _invariant2.default)(!this._finalized); this.emitAfterWaiting(this.getReasonToWaitForDependencies(dependencies), dependencies, func, targetBody); } }, { key: "_cloneGeneratorStack", value: function _cloneGeneratorStack() { return this._activeGeneratorStack.slice(); } }, { key: "declare", value: function declare(value) { (0, _invariant2.default)(!this._finalized); (0, _invariant2.default)(!this._activeValues.has(value)); (0, _invariant2.default)(value.hasIdentifier()); (0, _invariant2.default)(this._isEmittingActiveGenerator()); this._declaredAbstractValues.set(value, this._cloneGeneratorStack()); this._processValue(value); } }, { key: "hasBeenDeclared", value: function hasBeenDeclared(value) { (0, _invariant2.default)(!this._finalized); return this._declaredAbstractValues.has(value); } }, { key: "getBody", value: function getBody() { return this._body; } }, { key: "isCurrentBodyOffspringOf", value: function isCurrentBodyOffspringOf(targetBody) { var currentBody = this._body; while (currentBody !== undefined) { if (currentBody === targetBody) { return true; } currentBody = currentBody.parentBody; } return false; } }, { key: "getBodyReference", value: function getBodyReference() { (0, _invariant2.default)(!this._finalized); return new _types.BodyReference(this._body, this._body.entries.length); } }]); return Emitter; }(); //# sourceMappingURL=Emitter.js.map