prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
167 lines (152 loc) • 9.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (ast, strictCode, env, realm) {
let wasInPureTryStatement = realm.isInPureTryStatement;
if (realm.isInPureScope()) {
// TODO(1264): This is used to issue a warning if we have abstract function calls in here.
// We might not need it once we have full support for handling potential errors. Even
// then we might need it to know whether we should bother tracking error handling.
realm.isInPureTryStatement = true;
}
let blockRes;
try {
blockRes = env.evaluateCompletionDeref(ast.block, strictCode);
} finally {
realm.isInPureTryStatement = wasInPureTryStatement;
}
let handlerRes = blockRes;
let handler = ast.handler;
if (handler) {
// The start of the catch handler is a join point where all throw completions come together
blockRes = _singletons.Functions.incorporateSavedCompletion(realm, blockRes);
if (blockRes instanceof _completions.ThrowCompletion) {
handlerRes = env.evaluateCompletionDeref(handler, strictCode, blockRes);
// Note: The handler may have introduced new forks
} else if (blockRes instanceof _completions.JoinedAbruptCompletions || blockRes instanceof _completions.PossiblyNormalCompletion) {
if (blockRes instanceof _completions.PossiblyNormalCompletion) {
// Nothing has been joined and we are going to keep it that way.
// The current state may have advanced since the time control forked into the various paths recorded in blockRes.
// Update the normal path and restore the global state to what it was at the time of the fork.
let subsequentEffects = realm.getCapturedEffects(blockRes, blockRes.value);
(0, _invariant2.default)(subsequentEffects !== undefined);
realm.stopEffectCaptureAndUndoEffects(blockRes);
_singletons.Join.updatePossiblyNormalCompletionWithSubsequentEffects(realm, blockRes, subsequentEffects);
}
// All of the forked threads of control are now joined together and the global state reflects their joint effects
let handlerEffects = composeNestedThrowEffectsWithHandler(blockRes);
handlerRes = handlerEffects[0];
if (handlerRes instanceof _index2.Value) {
// This can happen if all of the abrupt completions in blockRes were throw completions
// and if the handler does not introduce any abrupt completions of its own.
realm.applyEffects(handlerEffects);
// The global state is now all joined up
} else {
// more than thread of control leaves the handler
// The effects of each thread is tracked in handlerRes
}
} else {
// The handler is not invoked, so just carry on.
}
}
let finalizerRes = handlerRes;
if (ast.finalizer) {
// The start of the finalizer is a join point where all threads of control come together.
// However, we choose to keep the threads unjoined and to apply the finalizer separately to each thread.
if (blockRes instanceof _completions.PossiblyNormalCompletion || blockRes instanceof _completions.JoinedAbruptCompletions) {
// The current global state is a the point of the fork that led to blockRes
// All subsequent effects are kept inside the branches of blockRes.
let finalizerEffects = composeNestedEffectsWithFinalizer(blockRes);
finalizerRes = finalizerEffects[0];
// The result may become abrupt because of the finalizer, but it cannot become normal.
(0, _invariant2.default)(!(finalizerRes instanceof _index2.Value));
} else {
// A single thread of control has produced a normal blockRes and the global state is up to date.
finalizerRes = env.evaluateCompletion(ast.finalizer, strictCode);
}
}
if (finalizerRes instanceof _completions.AbruptCompletion) throw finalizerRes;
if (finalizerRes instanceof _completions.PossiblyNormalCompletion) realm.composeWithSavedCompletion(finalizerRes);
if (handlerRes instanceof _completions.PossiblyNormalCompletion) handlerRes = handlerRes.value;
if (handlerRes instanceof _index2.Value) return (0, _index.UpdateEmpty)(realm, handlerRes, realm.intrinsics.undefined);
throw handlerRes;
// The handler is a potential join point for all throw completions, but is easier to not do the join here because
// it is tricky to join the joined and composed result of the throw completions with the non exceptional completions.
// Unfortunately, things are still complicated because the handler may turn abrupt completions into normal
// completions and the other way around. When this happens the container has to change its type.
// We do this by call joinEffects to create a new container at every level of the recursion.
function composeNestedThrowEffectsWithHandler(c, priorEffects = []) {
let consequent = c.consequent;
let consequentEffects = c.consequentEffects;
priorEffects.push(consequentEffects);
if (consequent instanceof _completions.JoinedAbruptCompletions || consequent instanceof _completions.PossiblyNormalCompletion) {
consequentEffects = composeNestedThrowEffectsWithHandler(consequent, priorEffects);
} else if (consequent instanceof _completions.ThrowCompletion) {
consequentEffects = realm.evaluateForEffects(() => {
for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
(0, _invariant2.default)(ast.handler);
return env.evaluateCompletionDeref(ast.handler, strictCode, consequent);
}, undefined, "composeNestedThrowEffectsWithHandler/1");
}
priorEffects.pop();
let alternate = c.alternate;
let alternateEffects = c.alternateEffects;
priorEffects.push(alternateEffects);
if (alternate instanceof _completions.PossiblyNormalCompletion || alternate instanceof _completions.JoinedAbruptCompletions) {
alternateEffects = composeNestedThrowEffectsWithHandler(alternate, priorEffects);
} else if (alternate instanceof _completions.ThrowCompletion) {
alternateEffects = realm.evaluateForEffects(() => {
for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
(0, _invariant2.default)(ast.handler);
return env.evaluateCompletionDeref(ast.handler, strictCode, alternate);
}, undefined, "composeNestedThrowEffectsWithHandler/2");
}
priorEffects.pop();
return _singletons.Join.joinEffects(realm, c.joinCondition, consequentEffects, alternateEffects);
}
// The finalizer is not a join point, so update each path in the completion separately.
// Things are complicated because the finalizer may turn normal completions into abrupt completions.
// When this happens the container has to change its type.
// We do this by call joinEffects to create a new container at every level of the recursion.
function composeNestedEffectsWithFinalizer(c, priorEffects = []) {
let consequent = c.consequent;
let consequentEffects = c.consequentEffects;
priorEffects.push(consequentEffects);
if (consequent instanceof _completions.JoinedAbruptCompletions || consequent instanceof _completions.PossiblyNormalCompletion) {
consequentEffects = composeNestedThrowEffectsWithHandler(consequent, priorEffects);
} else {
consequentEffects = realm.evaluateForEffects(() => {
for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
(0, _invariant2.default)(ast.finalizer);
return env.evaluateCompletionDeref(ast.finalizer, strictCode);
}, undefined, "composeNestedEffectsWithFinalizer/1");
if (!(consequentEffects[0] instanceof _completions.AbruptCompletion)) consequentEffects[0] = consequent;
}
priorEffects.pop();
let alternate = c.alternate;
let alternateEffects = c.alternateEffects;
priorEffects.push(alternateEffects);
if (alternate instanceof _completions.PossiblyNormalCompletion || alternate instanceof _completions.JoinedAbruptCompletions) {
alternateEffects = composeNestedThrowEffectsWithHandler(alternate, priorEffects);
} else {
alternateEffects = realm.evaluateForEffects(() => {
for (let priorEffect of priorEffects) realm.applyEffects(priorEffect);
(0, _invariant2.default)(ast.finalizer);
return env.evaluateCompletionDeref(ast.finalizer, strictCode);
}, undefined, "composeNestedThrowEffectsWithHandler");
if (!(alternateEffects[0] instanceof _completions.AbruptCompletion)) alternateEffects[0] = alternate;
}
priorEffects.pop();
return _singletons.Join.joinEffects(realm, c.joinCondition, consequentEffects, alternateEffects);
}
};
require("../environment.js");
var _completions = require("../completions.js");
var _index = require("../methods/index.js");
var _singletons = require("../singletons.js");
var _index2 = require("../values/index.js");
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
//# sourceMappingURL=TryStatement.js.map