"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PreludeGenerator = exports.NameGenerator = exports.Generator = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 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 _index2 = require("../domains/index.js"); var _base = require("base62"); var base62 = _interopRequireWildcard(_base); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _internalizer = require("./internalizer.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function serializeBody(generator, context) { var statements = context.serializeGenerator(generator); return t.blockStatement(statements); } var Generator = exports.Generator = function () { function Generator(realm, name) { _classCallCheck(this, Generator); (0, _invariant2.default)(realm.useAbstractInterpretation); var realmPreludeGenerator = realm.preludeGenerator; (0, _invariant2.default)(realmPreludeGenerator); this.preludeGenerator = realmPreludeGenerator; this.parent = realm.generator; this.realm = realm; this._entries = []; this.id = realm.nextGeneratorId++; this._name = name; } _createClass(Generator, [{ key: "getName", value: function getName() { return this._name || "#" + this.id; } }, { key: "getAsPropertyNameExpression", value: function getAsPropertyNameExpression(key) { var canBeIdentifier = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; // If key is a non-negative numeric string literal, parse it and set it as a numeric index instead. var index = Number.parseInt(key, 10); if (index >= 0 && index.toString() === key) { return t.numericLiteral(index); } if (canBeIdentifier) { // TODO #1020: revert this when Unicode identifiers are supported by all targetted JavaScript engines var keyIsAscii = /^[\u0000-\u007f]*$/.test(key); if (t.isValidIdentifier(key) && keyIsAscii) return t.identifier(key); } return t.stringLiteral(key); } }, { key: "getParent", value: function getParent() { return this.parent; } }, { key: "empty", value: function empty() { return this._entries.length === 0; } // Will force the array of Values to be serialized but not emit anything for a buildNode }, { key: "appendRoots", value: function appendRoots(values) { this._addEntry({ args: values }); } }, { key: "emitGlobalDeclaration", value: function emitGlobalDeclaration(key, value) { this.preludeGenerator.declaredGlobals.add(key); if (!(value instanceof _index.UndefinedValue)) this.emitGlobalAssignment(key, value, true); } }, { key: "emitGlobalAssignment", value: function emitGlobalAssignment(key, value, strictMode) { var _this = this; this._addEntry({ args: [value], buildNode: function buildNode(_ref) { var _ref2 = _slicedToArray(_ref, 1), valueNode = _ref2[0]; return t.expressionStatement(t.assignmentExpression("=", _this.preludeGenerator.globalReference(key, !strictMode), valueNode)); } }); } }, { key: "emitGlobalDelete", value: function emitGlobalDelete(key, strictMode) { var _this2 = this; this._addEntry({ args: [], buildNode: function buildNode(_ref3) { var _ref4 = _toArray(_ref3); return t.expressionStatement(t.unaryExpression("delete", _this2.preludeGenerator.globalReference(key, !strictMode))); } }); } }, { key: "emitBindingAssignment", value: function emitBindingAssignment(binding, value) { this._addEntry({ args: [value], buildNode: function buildNode(_ref5, context) { var _ref6 = _slicedToArray(_ref5, 1), valueNode = _ref6[0]; return t.expressionStatement(t.assignmentExpression("=", context.serializeBinding(binding), valueNode)); } }); } }, { key: "emitPropertyAssignment", value: function emitPropertyAssignment(object, key, value) { if (object.refuseSerialization) return; var propName = this.getAsPropertyNameExpression(key); this._addEntry({ args: [object, value], buildNode: function buildNode(_ref7) { var _ref8 = _slicedToArray(_ref7, 2), objectNode = _ref8[0], valueNode = _ref8[1]; return t.expressionStatement(t.assignmentExpression("=", t.memberExpression(objectNode, propName, !t.isIdentifier(propName)), valueNode)); } }); } }, { key: "emitDefineProperty", value: function emitDefineProperty(object, key, desc) { var isDescChanged = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; if (object.refuseSerialization) return; if (desc.enumerable && desc.configurable && desc.writable && desc.value && !isDescChanged) { var descValue = desc.value; (0, _invariant2.default)(descValue instanceof _index.Value); this.emitPropertyAssignment(object, key, descValue); } else { desc = Object.assign({}, desc); var _descValue = desc.value || object.$Realm.intrinsics.undefined; (0, _invariant2.default)(_descValue instanceof _index.Value); this._addEntry({ args: [object, _descValue, desc.get || object.$Realm.intrinsics.undefined, desc.set || object.$Realm.intrinsics.undefined], buildNode: function buildNode(_, context) { return context.emitDefinePropertyBody(object, key, desc); } }); } } }, { key: "emitPropertyDelete", value: function emitPropertyDelete(object, key) { if (object.refuseSerialization) return; var propName = this.getAsPropertyNameExpression(key); this._addEntry({ args: [object], buildNode: function buildNode(_ref9) { var _ref10 = _slicedToArray(_ref9, 1), objectNode = _ref10[0]; return t.expressionStatement(t.unaryExpression("delete", t.memberExpression(objectNode, propName, !t.isIdentifier(propName)))); } }); } }, { key: "emitCall", value: function emitCall(createCallee, args) { this._addEntry({ args: args, buildNode: function buildNode(values) { return t.expressionStatement(t.callExpression(createCallee(), [].concat(_toConsumableArray(values)))); } }); } }, { key: "emitConsoleLog", value: function emitConsoleLog(method, args) { var _this3 = this; this.emitCall(function () { return t.memberExpression(t.identifier("console"), t.identifier(method)); }, args.map(function (v) { return typeof v === "string" ? new _index.StringValue(_this3.realm, v) : v; })); } // test must be a temporal value, which means that it must have a defined intrinsicName }, { key: "emitDoWhileStatement", value: function emitDoWhileStatement(test, body) { this._addEntry({ args: [], buildNode: function buildNode(_ref11, context) { var _ref12 = _toArray(_ref11); var testId = test.intrinsicName; (0, _invariant2.default)(testId !== undefined); var statements = context.serializeGenerator(body); var block = t.blockStatement(statements); return t.doWhileStatement(t.identifier(testId), block); }, dependencies: [body] }); } }, { key: "emitInvariant", value: function emitInvariant(args, violationConditionFn, appendLastToInvariantFn) { if (this.realm.omitInvariants) return; this._addEntry({ args: args, buildNode: function buildNode(nodes) { var throwString = t.stringLiteral("Prepack model invariant violation"); if (appendLastToInvariantFn) { var last = nodes.pop(); throwString = t.binaryExpression("+", t.stringLiteral("Prepack model invariant violation: "), appendLastToInvariantFn(last)); } var condition = violationConditionFn(nodes); var throwblock = t.blockStatement([t.throwStatement(t.newExpression(t.identifier("Error"), [throwString]))]); return t.ifStatement(condition, throwblock); } }); } }, { key: "emitCallAndCaptureResult", value: function emitCallAndCaptureResult(types, values, createCallee, args, kind) { return this.derive(types, values, args, function (nodes) { return t.callExpression(createCallee(), nodes); }); } }, { key: "emitStatement", value: function emitStatement(args, buildNode_) { this._addEntry({ args: args, buildNode: buildNode_ }); } }, { key: "emitVoidExpression", value: function emitVoidExpression(types, values, args, buildNode_) { this._addEntry({ args: args, buildNode: function buildNode(nodes) { return t.expressionStatement(buildNode_ instanceof Function ? buildNode_(nodes) : buildNode_); } }); return this.realm.intrinsics.undefined; } }, { key: "emitForInStatement", value: function emitForInStatement(o, lh, sourceObject, targetObject, boundName) { this._addEntry({ // duplicate args to ensure refcount > 1 args: [o, targetObject, sourceObject, targetObject, sourceObject], buildNode: function buildNode(_ref13) { var _ref14 = _slicedToArray(_ref13, 6), obj = _ref14[0], tgt = _ref14[1], src = _ref14[2], obj1 = _ref14[3], tgt1 = _ref14[4], src1 = _ref14[5]; return t.forInStatement(lh, obj, t.blockStatement([t.expressionStatement(t.assignmentExpression("=", t.memberExpression(tgt, boundName, true), t.memberExpression(src, boundName, true)))])); } }); } }, { key: "derive", value: function derive(types, values, args, buildNode_, optionalArgs) { (0, _invariant2.default)(buildNode_ instanceof Function || args.length === 0); var id = t.identifier(this.preludeGenerator.nameGenerator.generate("derived")); this.preludeGenerator.derivedIds.set(id.name, args); var options = {}; if (optionalArgs && optionalArgs.kind) options.kind = optionalArgs.kind; var Constructor = _index.Value.isTypeCompatibleWith(types.getType(), _index.ObjectValue) ? _index.AbstractObjectValue : _index.AbstractValue; var res = new Constructor(this.realm, types, values, 0, [], id, options); this._addEntry({ isPure: optionalArgs ? optionalArgs.isPure : undefined, declared: res, args: args, buildNode: function buildNode(nodes, context) { return t.variableDeclaration("var", [t.variableDeclarator(id, buildNode_ instanceof Function ? buildNode_(nodes, context) : buildNode_)]); } }); var type = types.getType(); res.intrinsicName = id.name; if (optionalArgs && optionalArgs.skipInvariant) return res; var typeofString = void 0; if (type instanceof _index.FunctionValue) typeofString = "function";else if (type === _index.UndefinedValue) (0, _invariant2.default)(false);else if (type === _index.NullValue) (0, _invariant2.default)(false);else if (type === _index.StringValue) typeofString = "string";else if (type === _index.BooleanValue) typeofString = "boolean";else if (type === _index.NumberValue) typeofString = "number";else if (type === _index.SymbolValue) typeofString = "symbol";else if (type === _index.ObjectValue) typeofString = "object"; if (typeofString !== undefined) { // Verify that the types are as expected, a failure of this invariant // should mean the model is wrong. this.emitInvariant([res, res], function (nodes) { (0, _invariant2.default)(typeofString !== undefined); var condition = t.binaryExpression("!==", t.unaryExpression("typeof", nodes[0]), t.stringLiteral(typeofString)); if (typeofString === "object") { condition = t.logicalExpression("&&", condition, t.binaryExpression("!==", t.unaryExpression("typeof", nodes[0]), t.stringLiteral("function"))); condition = t.logicalExpression("||", condition, t.binaryExpression("===", nodes[0], _internalizer.nullExpression)); } return condition; }, function (node) { return node; }); } return res; } }, { key: "serialize", value: function serialize(context) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this._entries[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var entry = _step.value; if (!entry.isPure || !entry.declared || !context.canOmit(entry.declared)) { var nodes = entry.args.map(function (boundArg, i) { return context.serializeValue(boundArg); }); if (entry.buildNode) context.emit(entry.buildNode(nodes, context)); if (entry.declared !== undefined) context.declare(entry.declared); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } }, { key: "visitEntry", value: function visitEntry(entry, callbacks) { if (entry.isPure && entry.declared && callbacks.canSkip(entry.declared)) { callbacks.recordDelayedEntry(entry); } else { if (entry.declared) callbacks.recordDeclaration(entry.declared); callbacks.visitValues(entry.args); if (entry.dependencies) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = entry.dependencies[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var dependency = _step2.value; callbacks.visitGenerator(dependency); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } } } }, { key: "visit", value: function visit(callbacks) { var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = this._entries[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var entry = _step3.value; this.visitEntry(entry, callbacks); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } } }, { key: "_addEntry", value: function _addEntry(entry) { this._entries.push(entry); } }, { key: "appendGenerator", value: function appendGenerator(other, leadingComment) { if (other.empty()) return; this._addEntry({ args: [], buildNode: function buildNode(args, context) { var statements = context.serializeGenerator(other); var block = t.blockStatement(statements); if (leadingComment.length > 0) block.leadingComments = [{ type: "BlockComment", value: leadingComment }]; return block; }, dependencies: [other] }); } }, { key: "composeGenerators", value: function composeGenerators(generator1, generator2) { this._addEntry({ args: [], buildNode: function buildNode(_ref15, context) { var _ref16 = _toArray(_ref15); var statements = []; if (!generator1.empty()) statements.push(serializeBody(generator1, context)); if (!generator2.empty()) statements.push(serializeBody(generator2, context)); return t.blockStatement(statements); }, dependencies: [generator1, generator2] }); } }, { key: "joinGenerators", value: function joinGenerators(joinCondition, generator1, generator2) { this._addEntry({ args: [joinCondition], buildNode: function buildNode(_ref17, context) { var _ref18 = _slicedToArray(_ref17, 1), cond = _ref18[0]; var block1 = generator1.empty() ? null : serializeBody(generator1, context); var block2 = generator2.empty() ? null : serializeBody(generator2, context); if (block1) return t.ifStatement(cond, block1, block2); (0, _invariant2.default)(block2); return t.ifStatement(t.unaryExpression("!", cond), block2); }, dependencies: [generator1, generator2] }); } }]); return Generator; }(); // some characters are invalid within a JavaScript identifier, // such as: . , : ( ) ' " ` [ ] - // so we replace these character instacnes with an underscore function replaceInvalidCharactersWithUnderscore(string) { return string.replace(/[.,:\(\)\"\'\`\[\]\-]/g, "_"); } var NameGenerator = exports.NameGenerator = function () { function NameGenerator(forbiddenNames, debugNames, uniqueSuffix, prefix) { _classCallCheck(this, NameGenerator); this.prefix = prefix; this.uidCounter = 0; this.debugNames = debugNames; this.forbiddenNames = forbiddenNames; this.uniqueSuffix = uniqueSuffix; } _createClass(NameGenerator, [{ key: "generate", value: function generate(debugSuffix) { var id = void 0; do { id = this.prefix + base62.encode(this.uidCounter++); if (this.uniqueSuffix.length > 0) id += this.uniqueSuffix; if (this.debugNames) { if (debugSuffix) id += "_" + replaceInvalidCharactersWithUnderscore(debugSuffix);else id += "_"; } } while (this.forbiddenNames.has(id)); return id; } }]); return NameGenerator; }(); var PreludeGenerator = exports.PreludeGenerator = function () { function PreludeGenerator(debugNames, uniqueSuffix) { _classCallCheck(this, PreludeGenerator); this.prelude = []; this.derivedIds = new Map(); this.memoizedRefs = new Map(); this.nameGenerator = new NameGenerator(new Set(), !!debugNames, uniqueSuffix || "", "_$"); this.usesThis = false; this.declaredGlobals = new Set(); } _createClass(PreludeGenerator, [{ key: "createNameGenerator", value: function createNameGenerator(prefix) { return new NameGenerator(this.nameGenerator.forbiddenNames, this.nameGenerator.debugNames, this.nameGenerator.uniqueSuffix, prefix); } }, { key: "convertStringToMember", value: function convertStringToMember(str) { var _this4 = this; return str.split(".").map(function (name) { return name === "global" ? _this4.memoizeReference(name) : t.identifier(name); }).reduce(function (obj, prop) { return t.memberExpression(obj, prop); }); } }, { key: "globalReference", value: function globalReference(key) { var globalScope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (globalScope && t.isValidIdentifier(key)) return t.identifier(key); var keyNode = t.isValidIdentifier(key) ? t.identifier(key) : t.stringLiteral(key); return t.memberExpression(this.memoizeReference("global"), keyNode, !t.isIdentifier(keyNode)); } }, { key: "memoizeReference", value: function memoizeReference(key) { var ref = this.memoizedRefs.get(key); if (ref) return ref; var init = void 0; if (key.includes("(") || key.includes("[")) { // Horrible but effective hack: // Some internal object have intrinsic names such as // ([][Symbol.iterator]().__proto__.__proto__) // and // RegExp.prototype[Symbol.match] // which get turned into a babel node here. // TODO: We should properly parse such a string, and memoize all references in it separately. // Instead, we just turn it into a funky identifier, which Babel seems to accept. init = t.identifier(key); } else if (key === "global") { this.usesThis = true; init = t.thisExpression(); } else { var i = key.lastIndexOf("."); if (i === -1) { init = t.memberExpression(this.memoizeReference("global"), t.identifier(key)); } else { init = t.memberExpression(this.memoizeReference(key.substr(0, i)), t.identifier(key.substr(i + 1))); } } ref = t.identifier(this.nameGenerator.generate(key)); this.prelude.push(t.variableDeclaration("var", [t.variableDeclarator(ref, init)])); this.memoizedRefs.set(key, ref); return ref; } }]); return PreludeGenerator; }(); //# sourceMappingURL=generator.js.map