prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
138 lines (125 loc) • 3.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.HashSet = undefined;
exports.hashBinary = hashBinary;
exports.hashCall = hashCall;
exports.hashTernary = hashTernary;
exports.hashString = hashString;
exports.hashUnary = hashUnary;
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
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 hashBinary(op, x, y) {
let xHash = x.getHash();
let yHash = y.getHash();
if (yHash < xHash) {
// Check if the operation is commutative so that we can normalize the arguments on hash value order.
let commutative;
switch (op) {
case "*":
case "==":
case "!=":
case "===":
case "!==":
// If both operands might be objects, the operation does not commute because of the possibility
// that arbitrary code can run on both operands while converting them, in which case the order of the
// operands must be maintained to make sure any side-effects happen in the right order.
commutative = !(x.mightBeObject() && y.mightBeObject());
break;
case "+":
// As above, but in addition, if one of the operands might be a string the operation does not commute
commutative = !(x.mightBeObject() && y.mightBeObject()) && !(x.mightBeString() || y.mightBeString());
break;
default:
// The operation itself is not commutative
commutative = false;
break;
}
if (commutative) {
[x, y] = [y, x];
[xHash, yHash] = [yHash, xHash];
}
}
let hash = (hashString(op) * 13 ^ xHash) * 13 ^ yHash;
return [hash, [x, y]];
}
function hashCall(calleeName, ...args) {
let hash = hashString(calleeName);
for (let a of args) hash = hash * 13 ^ a.getHash();
return [hash, args];
}
function hashTernary(x, y, z) {
let hash = (x.getHash() * 13 ^ y.getHash()) * 13 ^ z.getHash();
return [hash, [x, y, z]];
}
function hashString(value) {
let hash = 5381;
for (let i = value.length - 1; i >= 0; i--) {
hash = hash * 33 ^ value.charCodeAt(i);
}
return hash;
}
function hashUnary(op, x) {
return hashString(op) * 13 ^ x.getHash();
}
class HashSet {
constructor(expectedEntries = 32 * 1024) {
let initialSize = 16;
expectedEntries *= 2;
while (initialSize < expectedEntries) initialSize *= 2;
this._entries = new Array(initialSize);
this._count = 0;
}
add(e) {
let entries = this._entries;
let n = entries.length;
let key = e.getHash();
let i = key & n - 1;
while (true) {
let entry = entries[i];
if (entry === undefined) {
entries[i] = e;
if (++this._count > n / 2) this.expand();
return e;
} else if (e.equals(entry)) {
return entry;
}
if (++i >= n) i = 0;
}
(0, _invariant2.default)(false); // otherwise Flow thinks this method can return undefined
}
expand() {
let oldEntries = this._entries;
let n = oldEntries.length;
let m = n * 2;
if (m <= 0) return;
let entries = new Array(m);
for (let i = 0; i < n; i++) {
let oldEntry = oldEntries[i];
if (oldEntry === undefined) continue;
let key = oldEntry.getHash();
let j = key & m - 1;
while (true) {
let entry = entries[j];
if (entry === undefined) {
entries[j] = oldEntry;
break;
}
if (++j >= m) j = 0;
}
}
this._entries = entries;
}
}
exports.HashSet = HashSet;
//# sourceMappingURL=hash.js.map