483 lines
23 KiB
JavaScript
483 lines
23 KiB
JavaScript
"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
|