prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
546 lines (443 loc) • 22 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.JoinImplementation = void 0;
var _realm = require("../realm.js");
var _descriptors = require("../descriptors.js");
var _completions = require("../completions.js");
var _index = require("../methods/index.js");
var _singletons = require("../singletons.js");
var _generator = require("../utils/generator.js");
var _index2 = require("../values/index.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 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.
*/
function joinGenerators(joinCondition, generator1, generator2) {
let realm = joinCondition.$Realm;
let result = new _generator.Generator(realm, "joined", realm.pathConditions);
if (!generator1.empty() || !generator2.empty()) {
result.joinGenerators(joinCondition, generator1, generator2);
}
return result;
}
function joinArrays(realm, v1, v2, getAbstractValue) {
let e = v1 && v1[0] || v2 && v2[0];
if (e instanceof _index2.Value) return joinArraysOfValues(realm, v1, v2, getAbstractValue);else return joinArrayOfsMapEntries(realm, v1, v2, getAbstractValue);
}
function joinArrayOfsMapEntries(realm, a1, a2, getAbstractValue) {
let empty = realm.intrinsics.empty;
let n = Math.max(a1 && a1.length || 0, a2 && a2.length || 0);
let result = [];
for (let i = 0; i < n; i++) {
let {
$Key: key1,
$Value: val1
} = a1 && a1[i] || {
$Key: empty,
$Value: empty
};
let {
$Key: key2,
$Value: val2
} = a2 && a2[i] || {
$Key: empty,
$Value: empty
};
if (key1 === undefined && key2 === undefined) {
result[i] = {
$Key: undefined,
$Value: undefined
};
} else {
let key3 = getAbstractValue(key1, key2);
let val3 = getAbstractValue(val1, val2);
result[i] = {
$Key: key3,
$Value: val3
};
}
}
return result;
}
function joinArraysOfValues(realm, a1, a2, getAbstractValue) {
let n = Math.max(a1 && a1.length || 0, a2 && a2.length || 0);
let result = [];
for (let i = 0; i < n; i++) {
result[i] = getAbstractValue(a1 && a1[i] || undefined, a2 && a2[i] || undefined);
}
return result;
}
class JoinImplementation {
composeCompletions(leftCompletion, rightCompletion) {
if (leftCompletion instanceof _completions.AbruptCompletion) return leftCompletion;
if (leftCompletion instanceof _completions.JoinedNormalAndAbruptCompletions) {
if (rightCompletion instanceof _completions.JoinedNormalAndAbruptCompletions) {
rightCompletion.composedWith = leftCompletion;
rightCompletion.pathConditionsAtCreation = leftCompletion.pathConditionsAtCreation;
return rightCompletion;
}
let c = this.composeCompletions(leftCompletion.consequent, rightCompletion);
if (c instanceof _index2.Value) c = new _completions.SimpleNormalCompletion(c);
let a = this.composeCompletions(leftCompletion.alternate, rightCompletion);
if (a instanceof _index2.Value) a = new _completions.SimpleNormalCompletion(a);
let joinedCompletion = this.joinCompletions(leftCompletion.joinCondition, c, a);
if (joinedCompletion instanceof _completions.JoinedNormalAndAbruptCompletions) {
joinedCompletion.composedWith = leftCompletion.composedWith;
joinedCompletion.pathConditionsAtCreation = leftCompletion.pathConditionsAtCreation;
joinedCompletion.savedEffects = leftCompletion.savedEffects;
}
return joinedCompletion;
}
if (leftCompletion instanceof _index2.Value) leftCompletion = new _completions.SimpleNormalCompletion(leftCompletion);
if (leftCompletion instanceof _completions.Completion && leftCompletion.value === leftCompletion.value.$Realm.intrinsics.__bottomValue) {
return leftCompletion;
}
if (rightCompletion instanceof _index2.Value) rightCompletion = new _completions.SimpleNormalCompletion(rightCompletion);
return rightCompletion;
}
composeWithEffects(completion, normalEffects) {
if (completion instanceof _completions.JoinedNormalAndAbruptCompletions) {
let selectAbrupt = c => c instanceof _completions.AbruptCompletion && c.value !== c.value.$Realm.intrinsics.__bottomValue;
let composableCompletions = _completions.Completion.makeSelectedCompletionsInfeasibleInCopy(selectAbrupt, completion);
let composedNormalCompletion = this.composeCompletions(composableCompletions, normalEffects.result);
normalEffects.result = composedNormalCompletion;
let selectNormal = c => c instanceof _completions.SimpleNormalCompletion && c.value !== c.value.$Realm.intrinsics.__bottomValue;
let nonComposableCompletions = _completions.Completion.makeSelectedCompletionsInfeasibleInCopy(selectNormal, completion);
let nonComposedEffects = (0, _realm.construct_empty_effects)(completion.value.$Realm, nonComposableCompletions);
let joinCondition = _index2.AbstractValue.createJoinConditionForSelectedCompletions(selectNormal, completion);
return this.joinEffects(joinCondition, normalEffects, nonComposedEffects);
} else if (completion instanceof _completions.AbruptCompletion) {
return (0, _realm.construct_empty_effects)(completion.value.$Realm, completion);
} else {
return normalEffects;
}
}
_collapseSimilarCompletions(joinCondition, c1, c2) {
let realm = joinCondition.$Realm;
let getAbstractValue = (v1, v2) => {
if (v1 instanceof _index2.EmptyValue) return v2 || realm.intrinsics.undefined;
if (v2 instanceof _index2.EmptyValue) return v1 || realm.intrinsics.undefined;
return _index2.AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
};
if (c1 instanceof _completions.BreakCompletion && c2 instanceof _completions.BreakCompletion && c1.target === c2.target) {
let val = this.joinValues(realm, c1.value, c2.value, getAbstractValue);
(0, _invariant.default)(val instanceof _index2.Value);
return new _completions.BreakCompletion(val, joinCondition.expressionLocation, c1.target);
}
if (c1 instanceof _completions.ContinueCompletion && c2 instanceof _completions.ContinueCompletion && c1.target === c2.target) {
return new _completions.ContinueCompletion(realm.intrinsics.empty, joinCondition.expressionLocation, c1.target);
}
if (c1 instanceof _completions.ReturnCompletion && c2 instanceof _completions.ReturnCompletion) {
let val = this.joinValues(realm, c1.value, c2.value, getAbstractValue);
(0, _invariant.default)(val instanceof _index2.Value);
return new _completions.ReturnCompletion(val, joinCondition.expressionLocation);
}
if (c1 instanceof _completions.ThrowCompletion && c2 instanceof _completions.ThrowCompletion) {
getAbstractValue = (v1, v2) => {
return _index2.AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
};
let val = this.joinValues(realm, c1.value, c2.value, getAbstractValue);
(0, _invariant.default)(val instanceof _index2.Value);
return new _completions.ThrowCompletion(val, c1.location);
}
if (c1 instanceof _completions.SimpleNormalCompletion && c2 instanceof _completions.SimpleNormalCompletion) {
return new _completions.SimpleNormalCompletion(getAbstractValue(c1.value, c2.value));
}
return undefined;
}
joinCompletions(joinCondition, c1, c2) {
if (!joinCondition.mightNotBeTrue()) return c1;
if (!joinCondition.mightNotBeFalse()) return c2;
(0, _invariant.default)(joinCondition instanceof _index2.AbstractValue);
let c = this._collapseSimilarCompletions(joinCondition, c1, c2);
if (c === undefined) {
if (c1 instanceof _completions.AbruptCompletion && c2 instanceof _completions.AbruptCompletion) c = new _completions.JoinedAbruptCompletions(joinCondition, c1, c2);else {
(0, _invariant.default)(c1 instanceof _completions.AbruptCompletion || c1 instanceof _completions.NormalCompletion);
(0, _invariant.default)(c2 instanceof _completions.AbruptCompletion || c2 instanceof _completions.NormalCompletion);
c = new _completions.JoinedNormalAndAbruptCompletions(joinCondition, c1, c2);
}
}
return c;
}
joinEffects(joinCondition, e1, e2) {
(0, _invariant.default)(e1.canBeApplied);
(0, _invariant.default)(e2.canBeApplied);
if (!joinCondition.mightNotBeTrue()) return e1;
if (!joinCondition.mightNotBeFalse()) return e2;
(0, _invariant.default)(joinCondition instanceof _index2.AbstractValue);
let {
result: c1,
generator: generator1,
modifiedBindings: modifiedBindings1,
modifiedProperties: modifiedProperties1,
createdObjects: createdObjects1
} = e1;
let {
result: c2,
generator: generator2,
modifiedBindings: modifiedBindings2,
modifiedProperties: modifiedProperties2,
createdObjects: createdObjects2
} = e2;
let realm = joinCondition.$Realm;
let c = this.joinCompletions(joinCondition, c1, c2);
let [modifiedGenerator1, modifiedGenerator2, bindings] = this._joinBindings(joinCondition, generator1, modifiedBindings1, generator2, modifiedBindings2);
let generator = joinGenerators(joinCondition, modifiedGenerator1, modifiedGenerator2);
let properties = this.joinPropertyBindings(realm, joinCondition, modifiedProperties1, modifiedProperties2, createdObjects1, createdObjects2);
let createdObjects = new Set();
createdObjects1.forEach(o => {
createdObjects.add(o);
});
createdObjects2.forEach(o => {
createdObjects.add(o);
});
return new _realm.Effects(c, generator, bindings, properties, createdObjects);
}
joinValuesOfSelectedCompletions(selector, completion, keepInfeasiblePaths = false) {
let realm = completion.value.$Realm;
let bottom = realm.intrinsics.__bottomValue;
if (completion instanceof _completions.JoinedAbruptCompletions || completion instanceof _completions.JoinedNormalAndAbruptCompletions) {
let joinCondition = completion.joinCondition;
let c = this.joinValuesOfSelectedCompletions(selector, completion.consequent);
let a = this.joinValuesOfSelectedCompletions(selector, completion.alternate); // do some simplification
if (c === bottom) {
// joinCondition will never be true when this completion is reached
if (a instanceof _index2.AbstractValue) {
a = _singletons.Path.withInverseCondition(joinCondition, () => {
(0, _invariant.default)(a instanceof _index2.AbstractValue);
return realm.simplifyAndRefineAbstractValue(a);
});
}
if (!keepInfeasiblePaths) return a;
} else if (a === bottom) {
// joinCondition will never be false when this completion is reached
if (c instanceof _index2.AbstractValue) {
c = _singletons.Path.withCondition(joinCondition, () => {
(0, _invariant.default)(c instanceof _index2.AbstractValue);
return realm.simplifyAndRefineAbstractValue(c);
});
}
if (!keepInfeasiblePaths) return c;
}
let getAbstractValue = (v1, v2) => {
if (v1 === bottom) v1 = realm.intrinsics.empty;
if (v2 === bottom) v2 = realm.intrinsics.empty;
return _index2.AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
};
let jv = this.joinValues(realm, c, a, getAbstractValue);
(0, _invariant.default)(jv instanceof _index2.Value);
if (completion instanceof _completions.JoinedNormalAndAbruptCompletions && completion.composedWith !== undefined) {
let composedWith = completion.composedWith;
if (!composedWith.containsSelectedCompletion(selector)) return jv;
let cjv = this.joinValuesOfSelectedCompletions(selector, composedWith);
joinCondition = _index2.AbstractValue.createJoinConditionForSelectedCompletions(selector, composedWith);
jv = this.joinValues(realm, jv, cjv, getAbstractValue);
(0, _invariant.default)(jv instanceof _index2.Value);
}
return jv;
}
if (selector(completion)) return completion.value;
return bottom;
} // Creates a single map that joins together maps m1 and m2 using the given join
// operator. If an entry is present in one map but not the other, the missing
// entry is treated as if it were there and its value were undefined.
joinMaps(m1, m2, join) {
let m3 = new Map();
m1.forEach((val1, key, map1) => {
let val2 = m2.get(key);
let val3 = join(key, val1, val2);
m3.set(key, val3);
});
m2.forEach((val2, key, map2) => {
if (!m1.has(key)) {
m3.set(key, join(key, undefined, val2));
}
});
return m3;
} // Creates a single map that has an key, value pair for the union of the key
// sets of m1 and m2. The value of a pair is the join of m1[key] and m2[key]
// where the join is defined to be just m1[key] if m1[key] === m2[key] and
// and abstract value with expression "joinCondition ? m1[key] : m2[key]" if not.
_joinBindings(joinCondition, g1, m1, g2, m2) {
let realm = joinCondition.$Realm;
let getAbstractValue = (v1, v2) => {
return _index2.AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2, undefined, true, true);
};
let rewritten1 = false;
let rewritten2 = false;
let leak = (b, g, v, rewritten) => {
// just like to what happens in leakBinding, we are going to append a
// binding-assignment generator entry; however, we play it safe and don't
// mutate the generator; instead, we create a new one that wraps around the old one.
if (!rewritten) {
let h = new _generator.Generator(realm, "RewrittenToAppendBindingAssignments", g.pathConditions);
if (!g.empty()) h.appendGenerator(g, "");
g = h;
rewritten = true;
}
if (v !== undefined && v !== realm.intrinsics.undefined) g.emitBindingAssignment(b, v);
return [g, rewritten];
};
let join = (b, b1, b2) => {
let l1 = b1 === undefined ? b.hasLeaked : b1.hasLeaked;
let l2 = b2 === undefined ? b.hasLeaked : b2.hasLeaked;
let v1 = b1 === undefined ? b.value : b1.value;
let v2 = b2 === undefined ? b.value : b2.value; // ensure that if either none or both sides have leaked
// note that if one side didn't have a binding entry yet, then there's nothing to actively leak
if (!l1 && l2) [g1, rewritten1] = leak(b, g1, v1, rewritten1);else if (l1 && !l2) [g2, rewritten2] = leak(b, g2, v2, rewritten2);
let hasLeaked = l1 || l2; // For leaked (and mutable) bindings, the actual value is no longer directly available.
// In that case, we reset the value to undefined to prevent any use of the last known value.
let value = hasLeaked ? undefined : this.joinValues(realm, v1, v2, getAbstractValue);
(0, _invariant.default)(value === undefined || value instanceof _index2.Value);
return {
hasLeaked,
value
};
};
let joinedBindings = this.joinMaps(m1, m2, join);
return [g1, g2, joinedBindings];
} // If v1 is known and defined and v1 === v2 return v1,
// otherwise return getAbstractValue(v1, v2)
joinValues(realm, v1, v2, getAbstractValue) {
if (Array.isArray(v1) || Array.isArray(v2)) {
(0, _invariant.default)(v1 === undefined || Array.isArray(v1));
(0, _invariant.default)(v2 === undefined || Array.isArray(v2));
return joinArrays(realm, v1, v2, getAbstractValue);
}
(0, _invariant.default)(v1 === undefined || v1 instanceof _index2.Value);
(0, _invariant.default)(v2 === undefined || v2 instanceof _index2.Value);
if (v1 !== undefined && v2 !== undefined && !(v1 instanceof _index2.AbstractValue) && !(v2 instanceof _index2.AbstractValue) && (0, _index.StrictEqualityComparison)(realm, v1.throwIfNotConcrete(), v2.throwIfNotConcrete())) {
return v1;
} else {
return getAbstractValue(v1, v2);
}
}
joinPropertyBindings(realm, joinCondition, m1, m2, c1, c2) {
let join = (b, d1, d2) => {
// If the PropertyBinding object has been freshly allocated do not join
if (d1 === undefined) {
if (c2.has(b.object)) return d2; // no join
if (b.descriptor !== undefined && m1.has(b)) {
// property was deleted
d1 = (0, _descriptors.cloneDescriptor)(b.descriptor.throwIfNotConcrete(realm));
(0, _invariant.default)(d1 !== undefined);
d1.value = realm.intrinsics.empty;
} else {
// no write to property
d1 = b.descriptor; //Get value of property before the split
}
}
if (d2 === undefined) {
if (c1.has(b.object)) return d1; // no join
if (b.descriptor !== undefined && m2.has(b)) {
// property was deleted
d2 = (0, _descriptors.cloneDescriptor)(b.descriptor.throwIfNotConcrete(realm));
(0, _invariant.default)(d2 !== undefined);
d2.value = realm.intrinsics.empty;
} else {
// no write to property
d2 = b.descriptor; //Get value of property before the split
}
}
return this.joinDescriptors(realm, joinCondition, d1, d2);
};
return this.joinMaps(m1, m2, join);
}
joinDescriptors(realm, joinCondition, d1, d2) {
let getAbstractValue = (v1, v2) => {
return _index2.AbstractValue.createFromConditionalOp(realm, joinCondition, v1, v2);
};
let clone_with_abstract_value = d => {
(0, _invariant.default)(d === d1 || d === d2);
if (!(0, _index.IsDataDescriptor)(realm, d)) {
return new _descriptors.AbstractJoinedDescriptor(joinCondition);
}
let dc;
let dcValue;
if (d instanceof _descriptors.InternalSlotDescriptor) {
dc = new _descriptors.InternalSlotDescriptor(d.value);
dcValue = dc.value;
if (Array.isArray(dcValue)) {
(0, _invariant.default)(dcValue.length > 0);
let elem0 = dcValue[0];
if (elem0 instanceof _index2.Value) {
dc.value = dcValue.map(e => {
return d === d1 ? getAbstractValue(e, realm.intrinsics.empty) : getAbstractValue(realm.intrinsics.empty, e);
});
} else {
dc.value = dcValue.map(e => {
let {
$Key: key1,
$Value: val1
} = e;
let key3 = d === d1 ? getAbstractValue(key1, realm.intrinsics.empty) : getAbstractValue(realm.intrinsics.empty, key1);
let val3 = d === d1 ? getAbstractValue(val1, realm.intrinsics.empty) : getAbstractValue(realm.intrinsics.empty, val1);
return {
$Key: key3,
$Value: val3
};
});
}
}
} else {
dc = (0, _descriptors.cloneDescriptor)(d.throwIfNotConcrete(realm));
(0, _invariant.default)(dc !== undefined);
dcValue = dc.value;
}
(0, _invariant.default)(dcValue === undefined || dcValue instanceof _index2.Value);
dc.value = d === d1 ? getAbstractValue(dcValue, realm.intrinsics.empty) : getAbstractValue(realm.intrinsics.empty, dcValue);
return dc;
};
if (d1 === undefined) {
if (d2 === undefined) return undefined; // d2 is a new property created in only one branch, join with empty
let d3 = clone_with_abstract_value(d2);
if (d3 instanceof _descriptors.AbstractJoinedDescriptor) d3.descriptor2 = d2;
return d3;
} else if (d2 === undefined) {
(0, _invariant.default)(d1 !== undefined); // d1 is a new property created in only one branch, join with empty
let d3 = clone_with_abstract_value(d1);
if (d3 instanceof _descriptors.AbstractJoinedDescriptor) d3.descriptor1 = d1;
return d3;
} else {
if (d1 instanceof _descriptors.PropertyDescriptor && d2 instanceof _descriptors.PropertyDescriptor && (0, _descriptors.equalDescriptors)(d1, d2) && (0, _index.IsDataDescriptor)(realm, d1)) {
let dc = (0, _descriptors.cloneDescriptor)(d1);
(0, _invariant.default)(dc !== undefined);
let dcValue = this.joinValues(realm, d1.value, d2.value, getAbstractValue);
(0, _invariant.default)(dcValue instanceof _index2.Value);
dc.value = dcValue;
return dc;
}
if (d1 instanceof _descriptors.InternalSlotDescriptor && d2 instanceof _descriptors.InternalSlotDescriptor) {
return new _descriptors.InternalSlotDescriptor(this.joinValues(realm, d1.value, d2.value, getAbstractValue));
}
return new _descriptors.AbstractJoinedDescriptor(joinCondition, d1, d2);
}
}
mapAndJoin(realm, values, joinConditionFactory, functionToMap) {
(0, _invariant.default)(values.size > 1);
let joinedEffects;
for (let val of values) {
let condition = joinConditionFactory(val);
let effects = realm.evaluateForEffects(() => {
(0, _invariant.default)(condition instanceof _index2.AbstractValue);
return _singletons.Path.withCondition(condition, () => {
return functionToMap(val);
});
}, undefined, "mapAndJoin");
joinedEffects = joinedEffects === undefined ? effects : this.joinEffects(condition, effects, joinedEffects);
}
(0, _invariant.default)(joinedEffects !== undefined);
realm.applyEffects(joinedEffects);
return realm.returnOrThrowCompletion(joinedEffects.result);
}
}
exports.JoinImplementation = JoinImplementation;
//# sourceMappingURL=join.js.map