UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

575 lines (457 loc) 21.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _completions = require("../completions.js"); var _errors = require("../errors.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _index = require("../methods/index.js"); var _singletons = require("../singletons.js"); var _index2 = require("../values/index.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. */ /* strict-local */ /* An abstract domain that collects together a set of concrete values that might be the value of a variable at runtime. Initially, every variable has the value undefined. A property that has been weakly deleted will have more than one value, one of which will by the EmptyValue. */ class ValuesDomain { constructor(_values) { let values = _values; if (values instanceof _index2.ConcreteValue) { let valueSet = new Set(); valueSet.add(values); values = valueSet; } this._elements = values; } contains(x) { let elems = this._elements; let xelems = x._elements; if (elems === xelems) return true; if (elems === undefined) return true; if (xelems === undefined) return false; if (elems.size < xelems.size) return false; for (let e of xelems) { if (!elems.has(e)) return false; } return true; } containsValue(x) { let elems = this._elements; if (elems === undefined) return true; // Top contains everything if (x instanceof _index2.AbstractValue) return this.contains(x.values); (0, _invariant.default)(x instanceof _index2.ConcreteValue); return elems.has(x); } isBottom() { return this._elements !== undefined && this._elements.size === 0; } isTop() { return this._elements === undefined; } getElements() { (0, _invariant.default)(this._elements !== undefined); return this._elements; } // return a set of values that may be result of performing the given operation on each pair in the // Cartesian product of the value sets of the operands. static binaryOp(realm, op, left, right) { if (left.isBottom() || right.isBottom()) return ValuesDomain.bottomVal; let leftElements = left._elements; let rightElements = right._elements; // Return top if left and/or right are top or if the size of the value set would get to be quite large. // Note: the larger the set of values, the less we know and therefore the less we get value from computing // all of these values. TODO #1000: probably the upper bound can be quite a bit smaller. if (!leftElements || !rightElements || leftElements.size > 100 || rightElements.size > 100) return ValuesDomain.topVal; let resultSet = new Set(); let savedHandler = realm.errorHandler; let savedIsReadOnly = realm.isReadOnly; realm.isReadOnly = true; try { realm.errorHandler = () => { throw new _errors.FatalError(); }; for (let leftElem of leftElements) { for (let rightElem of rightElements) { let result = ValuesDomain.computeBinary(realm, op, leftElem, rightElem); if (result instanceof _index2.ConcreteValue) { resultSet.add(result); } else { (0, _invariant.default)(result instanceof _index2.AbstractValue); if (result.values.isTop()) { return ValuesDomain.topVal; } for (let subResult of result.values.getElements()) { resultSet.add(subResult); } } } } } catch (e) { if (e instanceof _completions.AbruptCompletion) return ValuesDomain.topVal; } finally { realm.errorHandler = savedHandler; realm.isReadOnly = savedIsReadOnly; } return new ValuesDomain(resultSet); } // Note that calling this can result in user code running, which can side-effect the heap. // If that is not the desired behavior, mark the realm as read-only for the duration of the call. static computeBinary(realm, op, lval, rval) { if (op === "+") { // ECMA262 12.8.3 The Addition Operator let lprim = _singletons.To.ToPrimitiveOrAbstract(realm, lval); let rprim = _singletons.To.ToPrimitiveOrAbstract(realm, rval); if (lprim instanceof _index2.AbstractValue || rprim instanceof _index2.AbstractValue) { return _index2.AbstractValue.createFromBinaryOp(realm, op, lprim, rprim); } if (lprim instanceof _index2.StringValue || rprim instanceof _index2.StringValue) { let lstr = _singletons.To.ToString(realm, lprim); let rstr = _singletons.To.ToString(realm, rprim); return new _index2.StringValue(realm, lstr + rstr); } let lnum = _singletons.To.ToNumber(realm, lprim); let rnum = _singletons.To.ToNumber(realm, rprim); return (0, _index.Add)(realm, lnum, rnum); } else if (op === "<" || op === ">" || op === ">=" || op === "<=") { // ECMA262 12.10.3 if (op === "<") { let r = (0, _index.AbstractRelationalComparison)(realm, lval, rval, true, op); if (r instanceof _index2.UndefinedValue) { return realm.intrinsics.false; } else { return r; } } else if (op === "<=") { let r = (0, _index.AbstractRelationalComparison)(realm, rval, lval, false, op); if (r instanceof _index2.UndefinedValue || r instanceof _index2.BooleanValue && r.value) { return realm.intrinsics.false; } else if (r instanceof _index2.AbstractValue) { return r; } else { return realm.intrinsics.true; } } else if (op === ">") { let r = (0, _index.AbstractRelationalComparison)(realm, rval, lval, false, op); if (r instanceof _index2.UndefinedValue) { return realm.intrinsics.false; } else { return r; } } else if (op === ">=") { let r = (0, _index.AbstractRelationalComparison)(realm, lval, rval, true, op); if (r instanceof _index2.UndefinedValue || r instanceof _index2.BooleanValue && r.value) { return realm.intrinsics.false; } else if (r instanceof _index2.AbstractValue) { return r; } else { return realm.intrinsics.true; } } } else if (op === ">>>") { // ECMA262 12.9.5.1 let lnum = _singletons.To.ToUint32(realm, lval); let rnum = _singletons.To.ToUint32(realm, rval); return _index2.IntegralValue.createFromNumberValue(realm, lnum >>> rnum); } else if (op === "<<" || op === ">>") { let lnum = _singletons.To.ToInt32(realm, lval); let rnum = _singletons.To.ToUint32(realm, rval); if (op === "<<") { // ECMA262 12.9.3.1 return _index2.IntegralValue.createFromNumberValue(realm, lnum << rnum); } else if (op === ">>") { // ECMA262 12.9.4.1 return _index2.IntegralValue.createFromNumberValue(realm, lnum >> rnum); } } else if (op === "**") { // ECMA262 12.6.3 // 5. Let base be ? ToNumber(leftValue). let base = _singletons.To.ToNumberOrAbstract(realm, lval); // 6. Let exponent be ? ToNumber(rightValue). let exponent = _singletons.To.ToNumberOrAbstract(realm, rval); if (base instanceof _index2.AbstractValue || exponent instanceof _index2.AbstractValue) { const baseVal = base instanceof _index2.AbstractValue ? base : new _index2.NumberValue(realm, base); const exponentVal = exponent instanceof _index2.AbstractValue ? exponent : new _index2.NumberValue(realm, exponent); return _index2.AbstractValue.createFromBinaryOp(realm, op, baseVal, exponentVal); } // 7. Return the result of Applying the ** operator with base and exponent as specified in 12.7.3.4. return new _index2.NumberValue(realm, Math.pow(base, exponent)); } else if (op === "%" || op === "/" || op === "*" || op === "-") { // ECMA262 12.7.3 let lnum = _singletons.To.ToNumberOrAbstract(realm, lval); let rnum = _singletons.To.ToNumberOrAbstract(realm, rval); if (lnum instanceof _index2.AbstractValue || rnum instanceof _index2.AbstractValue) { const lnumVal = lnum instanceof _index2.AbstractValue ? lnum : new _index2.NumberValue(realm, lnum); const rnumVal = rnum instanceof _index2.AbstractValue ? rnum : new _index2.NumberValue(realm, rnum); return _index2.AbstractValue.createFromBinaryOp(realm, op, lnumVal, rnumVal); } if (isNaN(rnum)) return realm.intrinsics.NaN; if (isNaN(lnum)) return realm.intrinsics.NaN; if (op === "-") { return (0, _index.Add)(realm, lnum, rnum, true); } else if (op === "%") { // The sign of the result equals the sign of the dividend. // If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN. // If the dividend is finite and the divisor is an infinity, the result equals the dividend. // If the dividend is a zero and the divisor is nonzero and finite, the result is the same as the dividend. return new _index2.NumberValue(realm, lnum % rnum); } else if (op === "/") { // The sign of the result is positive if both operands have the same sign, negative if the operands have different signs. // Division of an infinity by an infinity results in NaN. // Division of an infinity by a zero results in an infinity. The sign is determined by the rule already stated above. // Division of an infinity by a nonzero finite value results in a signed infinity. The sign is determined by the rule already stated above. // Division of a finite value by an infinity results in zero. The sign is determined by the rule already stated above. // Division of a zero by a zero results in NaN; division of zero by any other finite value results in zero, with the sign determined by the rule already stated above. // Division of a nonzero finite value by a zero results in a signed infinity. The sign is determined by the rule already stated above. return new _index2.NumberValue(realm, lnum / rnum); } else if (op === "*") { // The sign of the result is positive if both operands have the same sign, negative if the operands have different signs. // Multiplication of an infinity by a zero results in NaN. // Multiplication of an infinity by an infinity results in an infinity. The sign is determined by the rule already stated above. // Multiplication of an infinity by a finite nonzero value results in a signed infinity. The sign is determined by the rule already stated above. return new _index2.NumberValue(realm, lnum * rnum); } } else if (op === "!==") { return new _index2.BooleanValue(realm, !(0, _index.StrictEqualityComparison)(realm, lval, rval)); } else if (op === "===") { return new _index2.BooleanValue(realm, (0, _index.StrictEqualityComparison)(realm, lval, rval)); } else if (op === "!=" || op === "==") { return (0, _index.AbstractEqualityComparison)(realm, lval, rval, op); } else if (op === "&" || op === "|" || op === "^") { // ECMA262 12.12.3 let lnum = _singletons.To.ToInt32(realm, lval); // 6. Let rnum be ? ToInt32(rval). let rnum = _singletons.To.ToInt32(realm, rval); // 7. Return the result of applying the bitwise operator @ to lnum and rnum. The result is a signed 32 bit integer. if (op === "&") { return _index2.IntegralValue.createFromNumberValue(realm, lnum & rnum); } else if (op === "|") { return _index2.IntegralValue.createFromNumberValue(realm, lnum | rnum); } else if (op === "^") { return _index2.IntegralValue.createFromNumberValue(realm, lnum ^ rnum); } } else if (op === "in") { // ECMA262 12.10.3 // 5. If Type(rval) is not Object, throw a TypeError exception. if (!(rval instanceof _index2.ObjectValue)) { throw new _errors.FatalError(); } // 6. Return ? HasProperty(rval, ToPropertyKey(lval)). return new _index2.BooleanValue(realm, (0, _index.HasProperty)(realm, rval, _singletons.To.ToPropertyKey(realm, lval))); } else if (op === "instanceof") { // ECMA262 12.10.3 // 5. Return ? InstanceofOperator(lval, rval).; return new _index2.BooleanValue(realm, (0, _index.InstanceofOperator)(realm, lval, rval)); } (0, _invariant.default)(false, "unimplemented " + op); } static logicalOp(realm, op, left, right) { let leftElements = left._elements; let rightElements = right._elements; // Return top if left and/or right are top or if the size of the value set would get to be quite large. // Note: the larger the set of values, the less we know and therefore the less we get value from computing // all of these values. TODO #1000: probably the upper bound can be quite a bit smaller. if (!leftElements || !rightElements || leftElements.size > 100 || rightElements.size > 100) return ValuesDomain.topVal; let resultSet = new Set(); let savedHandler = realm.errorHandler; let savedIsReadOnly = realm.isReadOnly; realm.isReadOnly = true; try { realm.errorHandler = () => { throw new _errors.FatalError(); }; for (let leftElem of leftElements) { for (let rightElem of rightElements) { let result = ValuesDomain.computeLogical(realm, op, leftElem, rightElem); resultSet.add(result); } } } catch (e) { if (e instanceof _completions.AbruptCompletion) return ValuesDomain.topVal; } finally { realm.errorHandler = savedHandler; realm.isReadOnly = savedIsReadOnly; } return new ValuesDomain(resultSet); } // Note that calling this can result in user code running, which can side-effect the heap. // If that is not the desired behavior, mark the realm as read-only for the duration of the call. static computeLogical(realm, op, lval, rval) { let lbool = _singletons.To.ToBoolean(realm, lval); if (op === "&&") { // ECMA262 12.13.3 if (lbool === false) return lval; } else if (op === "||") { // ECMA262 12.13.3 if (lbool === true) return lval; } return rval; } // Note that calling this can result in user code running, which can side-effect the heap. // If that is not the desired behavior, mark the realm as read-only for the duration of the call. static computeUnary(realm, op, value) { if (op === "+") { // ECMA262 12.5.6.1 // 1. Let expr be the result of evaluating UnaryExpression. // 2. Return ? ToNumber(? GetValue(expr)). return _index2.IntegralValue.createFromNumberValue(realm, _singletons.To.ToNumber(realm, value)); } else if (op === "-") { // ECMA262 12.5.7.1 // 1. Let expr be the result of evaluating UnaryExpression. // 2. Let oldValue be ? ToNumber(? GetValue(expr)). let oldValue = _singletons.To.ToNumber(realm, value); // 3. If oldValue is NaN, return NaN. if (isNaN(oldValue)) { return realm.intrinsics.NaN; } // 4. Return the result of negating oldValue; that is, compute a Number with the same magnitude but opposite sign. return _index2.IntegralValue.createFromNumberValue(realm, -oldValue); } else if (op === "~") { // ECMA262 12.5.8 // 1. Let expr be the result of evaluating UnaryExpression. // 2. Let oldValue be ? ToInt32(? GetValue(expr)). let oldValue = _singletons.To.ToInt32(realm, value); // 3. Return the result of applying bitwise complement to oldValue. The result is a signed 32-bit integer. return _index2.IntegralValue.createFromNumberValue(realm, ~oldValue); } else if (op === "!") { // ECMA262 12.6.9 // 1. Let expr be the result of evaluating UnaryExpression. // 2. Let oldValue be ToBoolean(? GetValue(expr)). let oldValue = _singletons.To.ToBoolean(realm, value); // 3. If oldValue is true, return false. if (oldValue === true) return realm.intrinsics.false; // 4. Return true. return realm.intrinsics.true; } else if (op === "void") { // 1. Let expr be the result of evaluating UnaryExpression. // 2. Perform ? GetValue(expr). // 3. Return undefined. return realm.intrinsics.undefined; } else if (op === "typeof") { // ECMA262 12.6.5 // 1. Let val be the result of evaluating UnaryExpression. // 2. If Type(val) is Reference, then // 3. Let val be ? GetValue(val). let val = value; // 4. Return a String according to Table 35. let typeString = _singletons.Utils.typeToString(val.getType()); (0, _invariant.default)(typeString !== undefined); return new _index2.StringValue(realm, typeString); } else { (0, _invariant.default)(false, `${op} is a state update, not a pure operation, so we don't support it`); } } static unaryOp(realm, op, operandValues) { if (operandValues.isBottom()) return ValuesDomain.bottomVal; let operandElements = operandValues._elements; if (operandElements === undefined) return ValuesDomain.topVal; let resultSet = new Set(); let savedHandler = realm.errorHandler; let savedIsReadOnly = realm.isReadOnly; realm.isReadOnly = true; try { realm.errorHandler = () => { throw new _errors.FatalError(); }; for (let operandElem of operandElements) { let result = ValuesDomain.computeUnary(realm, op, operandElem); if (result instanceof _index2.ConcreteValue) { resultSet.add(result); } else { (0, _invariant.default)(result instanceof _index2.AbstractValue); if (result.values.isTop()) { return ValuesDomain.topVal; } for (let subResult of result.values.getElements()) { resultSet.add(subResult); } } } } catch (e) { if (e instanceof _completions.AbruptCompletion) return ValuesDomain.topVal; } finally { realm.errorHandler = savedHandler; realm.isReadOnly = savedIsReadOnly; } return new ValuesDomain(resultSet); } includesValueNotOfType(type) { (0, _invariant.default)(!this.isTop()); for (let cval of this.getElements()) { if (!(cval instanceof type)) return true; } return false; } includesValueOfType(type) { (0, _invariant.default)(!this.isTop()); for (let cval of this.getElements()) { if (cval instanceof type) return true; } return false; } mightBeFalse() { (0, _invariant.default)(!this.isTop()); for (let cval of this.getElements()) { if (cval.mightBeFalse()) return true; } return false; } mightNotBeFalse() { (0, _invariant.default)(!this.isTop()); for (let cval of this.getElements()) { if (cval.mightNotBeFalse()) return true; } return false; } static joinValues(realm, v1 = realm.intrinsics.undefined, v2 = realm.intrinsics.undefined) { if (v1 instanceof _index2.AbstractValue) return v1.values.joinWith(v2); if (v2 instanceof _index2.AbstractValue) return v2.values.joinWith(v1); let union = new Set(); (0, _invariant.default)(v1 instanceof _index2.ConcreteValue); union.add(v1); (0, _invariant.default)(v2 instanceof _index2.ConcreteValue); union.add(v2); return new ValuesDomain(union); } joinWith(y) { if (this.isTop()) return this; let union = new Set(this.getElements()); if (y instanceof _index2.AbstractValue) { if (y.values.isTop()) return y.values; y.values.getElements().forEach(v => union.add(v)); } else { (0, _invariant.default)(y instanceof _index2.ConcreteValue); union.add(y); } if (union.size === 0) return ValuesDomain.bottomVal; return new ValuesDomain(union); } static meetValues(realm, v1 = realm.intrinsics.undefined, v2 = realm.intrinsics.undefined) { if (v1 instanceof _index2.AbstractValue) return v1.values.meetWith(v2); if (v2 instanceof _index2.AbstractValue) return v2.values.meetWith(v1); let intersection = new Set(); (0, _invariant.default)(v1 instanceof _index2.ConcreteValue); (0, _invariant.default)(v2 instanceof _index2.ConcreteValue); if (v1 === v2) intersection.add(v1); if (intersection.size === 0) return ValuesDomain.bottomVal; return new ValuesDomain(intersection); } meetWith(y) { let intersection = new Set(); let elements = this._elements; if (y instanceof _index2.AbstractValue) { if (y.values.isTop()) return this; y.values.getElements().forEach(v => { if (elements === undefined || elements.has(v)) intersection.add(v); }); } else { (0, _invariant.default)(y instanceof _index2.ConcreteValue); if (elements === undefined || elements.has(y)) intersection.add(y); } if (intersection.size === 0) return ValuesDomain.bottomVal; return new ValuesDomain(intersection); } promoteEmptyToUndefined() { if (this.isTop() || this.isBottom()) return this; let newSet = new Set(); for (let cval of this.getElements()) { if (cval instanceof _index2.EmptyValue) newSet.add(cval.$Realm.intrinsics.undefined);else newSet.add(cval); } return new ValuesDomain(newSet); } } exports.default = ValuesDomain; //# sourceMappingURL=ValuesDomain.js.map