UNPKG

19.7 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = simplifyAndRefineAbstractValue;
7
8var _errors = require("../errors.js");
9
10var _index = require("../domains/index.js");
11
12var _invariant = _interopRequireDefault(require("../invariant.js"));
13
14var _realm = require("../realm.js");
15
16var _index2 = require("../values/index.js");
17
18var _singletons = require("../singletons.js");
19
20var _EmptyValue = _interopRequireDefault(require("../values/EmptyValue.js"));
21
22var _generator = require("./generator.js");
23
24function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
26/**
27 * Copyright (c) 2017-present, Facebook, Inc.
28 * All rights reserved.
29 *
30 * This source code is licensed under the BSD-style license found in the
31 * LICENSE file in the root directory of this source tree. An additional grant
32 * of patent rights can be found in the PATENTS file in the same directory.
33 */
34function simplifyAndRefineAbstractValue(realm, isCondition, // The value is only used after converting it to a Boolean
35value) {
36 let savedHandler = realm.errorHandler;
37 let savedIsReadOnly = realm.isReadOnly;
38 realm.isReadOnly = true;
39 let isRootSimplification = false;
40 realm.statistics.simplificationAttempts++;
41
42 if (!realm.inSimplificationPath) {
43 realm.inSimplificationPath = isRootSimplification = true;
44 }
45
46 try {
47 realm.errorHandler = diagnostic => {
48 if (diagnostic.errorCode === "PP0029") {
49 throw new _errors.FatalError(`${diagnostic.errorCode}: ${diagnostic.message}`);
50 }
51
52 throw new _errors.FatalError();
53 };
54
55 let result = simplify(realm, value, isCondition);
56 if (result !== value) realm.statistics.simplifications++;
57 return result;
58 } catch (e) {
59 if (e.name === "Invariant Violation") throw e;
60
61 if (e instanceof _errors.FatalError && typeof e.message === "string" && e.message.includes("PP0029")) {
62 if (isRootSimplification) {
63 return value;
64 }
65
66 throw e;
67 }
68
69 return value;
70 } finally {
71 if (isRootSimplification) {
72 realm.abstractValueImpliesCounter = 0;
73 realm.inSimplificationPath = false;
74 }
75
76 realm.errorHandler = savedHandler;
77 realm.isReadOnly = savedIsReadOnly;
78 }
79}
80
81function simplify(realm, value, isCondition = false) {
82 if (value instanceof _index2.ConcreteValue) return value;
83 (0, _invariant.default)(value instanceof _index2.AbstractValue);
84
85 if (isCondition || value.getType() === _index2.BooleanValue) {
86 if (_singletons.Path.implies(value)) return realm.intrinsics.true;
87 if (_singletons.Path.impliesNot(value)) return realm.intrinsics.false;
88 }
89
90 let loc = value.expressionLocation;
91 let op = value.kind;
92
93 switch (op) {
94 case "!":
95 {
96 let [x0] = value.args;
97 (0, _invariant.default)(x0 instanceof _index2.AbstractValue);
98
99 if (x0.kind === "!") {
100 (0, _invariant.default)(x0 instanceof _index2.AbstractValue);
101 let [x00] = x0.args;
102 let xx = simplify(realm, x00, true);
103 if (isCondition || xx.getType() === _index2.BooleanValue) return xx;
104 }
105
106 return negate(realm, x0, loc, value, isCondition);
107 }
108
109 case "||":
110 case "&&":
111 {
112 let [x0, y0] = value.args;
113 let x = simplify(realm, x0, isCondition);
114 let y = simplify(realm, y0, isCondition);
115 if (x instanceof _index2.AbstractValue && x.equals(y)) return x; // true && y <=> y
116 // true || y <=> true
117
118 if (!x.mightNotBeTrue()) return op === "&&" ? y : x; // (x == false) && y <=> x
119 // false || y <=> y
120
121 if (!x.mightNotBeFalse()) return op === "||" ? y : x;
122
123 if (isCondition || x.getType() === _index2.BooleanValue && y.getType() === _index2.BooleanValue) {
124 // (x: boolean) && true <=> x
125 // x || true <=> true
126 if (!y.mightNotBeTrue()) return op === "&&" ? x : realm.intrinsics.true; // (x: boolean) && false <=> false
127 // (x: boolean) || false <=> x
128
129 if (!y.mightNotBeFalse()) return op === "||" ? x : realm.intrinsics.false;
130 }
131
132 if (op === "||" && y instanceof _index2.AbstractValue && y.kind === "||" && x.equals(y.args[0]) && !y.args[1].mightNotBeTrue()) return y;
133
134 if (realm.instantRender.enabled) {
135 if (op === "||" && x0 instanceof _index2.AbstractValue && y0 instanceof _index2.AbstractValue) {
136 if (x0.kind === "===" && y0.kind === "===") {
137 let [xa, xb] = x0.args;
138 let [ya, yb] = y0.args;
139 if (xa.equals(ya) && !xb.equals(yb) && nullOrUndefined(xb) && nullOrUndefined(yb)) return rewrite(xa);else if (xb.equals(yb) && !xa.equals(ya) && nullOrUndefined(xa) && nullOrUndefined(ya)) return rewrite(xb);else if (xa.equals(yb) && !xb.equals(ya) && nullOrUndefined(xb) && nullOrUndefined(ya)) return rewrite(xa);else if (xb.equals(ya) && !xa.equals(yb) && nullOrUndefined(xa) && nullOrUndefined(yb)) return rewrite(xb);
140
141 function nullOrUndefined(z) {
142 return !z.mightNotBeNull() || !z.mightNotBeUndefined();
143 }
144
145 function rewrite(z) {
146 return _index2.AbstractValue.createFromBuildFunction(realm, _index2.BooleanValue, [xa], (0, _generator.createOperationDescriptor)("CANNOT_BECOME_OBJECT"), {
147 kind: "global.__cannotBecomeObject(A)"
148 });
149 }
150 }
151 }
152 }
153
154 if (x.equals(x0) && y.equals(y0)) return value;
155 return _index2.AbstractValue.createFromLogicalOp(realm, value.kind, x, y, loc, isCondition, true);
156 }
157
158 case "<":
159 case "<=":
160 case ">":
161 case ">=":
162 return distributeConditional(realm, value, isCondition, args => _index2.AbstractValue.createFromBinaryOp(realm, op, args[0], args[1], loc, undefined, isCondition));
163
164 case "==":
165 case "!=":
166 case "===":
167 case "!==":
168 return simplifyEquality(realm, value);
169
170 case "conditional":
171 {
172 let [c0, x0, y0] = value.args;
173 let c = simplify(realm, c0, true);
174 let x, y;
175
176 if (c0 instanceof _index2.AbstractValue && c.mightBeFalse() && c.mightBeTrue()) {
177 try {
178 x = _singletons.Path.withCondition(c0, () => simplify(realm, x0, isCondition));
179 } catch (e) {
180 if (e instanceof _errors.InfeasiblePathError) {
181 // We now know that c0 cannot be be true on this path
182 return simplify(realm, y0, isCondition);
183 }
184
185 throw e;
186 }
187
188 try {
189 y = _singletons.Path.withInverseCondition(c0, () => simplify(realm, y0, isCondition));
190 } catch (e) {
191 if (e instanceof _errors.InfeasiblePathError) {
192 // We now know that c0 cannot be be false on this path
193 return x;
194 }
195
196 throw e;
197 }
198 }
199
200 if (x === undefined) x = simplify(realm, x0, isCondition);
201 if (y === undefined) y = simplify(realm, y0, isCondition);
202 if (!c.mightNotBeTrue()) return x;
203 if (!c.mightNotBeFalse()) return y;
204 (0, _invariant.default)(c instanceof _index2.AbstractValue);
205 if (_singletons.Path.implies(c)) return x;
206
207 let notc = _index2.AbstractValue.createFromUnaryOp(realm, "!", c, true, loc, isCondition, true);
208
209 if (!notc.mightNotBeTrue()) return y;
210 if (!notc.mightNotBeFalse()) return x;
211 (0, _invariant.default)(notc instanceof _index2.AbstractValue);
212 if (_singletons.Path.implies(notc)) return y;
213
214 if (!isCondition) {
215 if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, x))) return x;
216 if (!x.mightBeNumber() && _singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, x))) return y;
217 if (!y.mightBeNumber() && _singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, y))) return x;
218 if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, y))) return y;
219 } // c ? x : x <=> x
220
221
222 if (x.equals(y)) return x; // x ? x : y <=> x || y
223
224 let cs = isCondition ? c : simplify(realm, c0);
225 if (cs.equals(x)) return _index2.AbstractValue.createFromLogicalOp(realm, "||", x, y, loc, isCondition, true); // y ? x : y <=> y && x
226
227 if (cs.equals(y)) return _index2.AbstractValue.createFromLogicalOp(realm, "&&", y, x, loc, isCondition, true); // c ? (c ? xx : xy) : y <=> c ? xx : y
228
229 if (x instanceof _index2.AbstractValue && x.kind === "conditional") {
230 let [xc, xx] = x.args;
231 if (c.equals(xc)) return _index2.AbstractValue.createFromConditionalOp(realm, c, xx, y, value.expressionLocation, isCondition, true);
232 } // c ? x : (c ? y : z) : z <=> c ? x : z
233
234
235 if (y instanceof _index2.AbstractValue && y.kind === "conditional") {
236 let [yc,, z] = y.args;
237 if (c.equals(yc)) return _index2.AbstractValue.createFromConditionalOp(realm, c, x, z, value.expressionLocation, isCondition, true);
238 }
239
240 if (isCondition || x.getType() === _index2.BooleanValue && y.getType() === _index2.BooleanValue) {
241 // c ? true : false <=> c
242 if (!x.mightNotBeTrue() && !y.mightNotBeFalse()) return c; // c ? false : true <=> !c
243
244 if (!x.mightNotBeFalse() && !y.mightNotBeTrue()) return _index2.AbstractValue.createFromUnaryOp(realm, "!", c, true, loc, true);
245 }
246
247 if (c.equals(c0) && x.equals(x0) && y.equals(y0)) return value;
248 return _index2.AbstractValue.createFromConditionalOp(realm, c, x, y, value.expressionLocation, isCondition, true);
249 }
250
251 case "abstractConcreteUnion":
252 {
253 // The union of an abstract value with one or more concrete values.
254 if (realm.pathConditions.length === 0) return value;
255 let [abstractValue, ...concreteValues] = value.args;
256 (0, _invariant.default)(abstractValue instanceof _index2.AbstractValue);
257 let remainingConcreteValues = [];
258
259 for (let concreteValue of concreteValues) {
260 if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "!==", value, concreteValue))) continue;
261 if (_singletons.Path.implies(_index2.AbstractValue.createFromBinaryOp(realm, "===", value, concreteValue))) return concreteValue;
262 remainingConcreteValues.push(concreteValue);
263 }
264
265 if (remainingConcreteValues.length === 0) return abstractValue;
266 if (remainingConcreteValues.length === concreteValues.length) return value;
267 return _index2.AbstractValue.createAbstractConcreteUnion(realm, abstractValue, ...remainingConcreteValues);
268 }
269
270 default:
271 return value;
272 }
273}
274
275function distributeConditional(realm, value, isCondition, create) {
276 // Find a conditional argument
277 let condition;
278 let args = value.args;
279
280 for (let arg of args) if (arg instanceof _index2.AbstractValue && arg.kind === "conditional") {
281 if (condition === undefined) condition = arg.args[0];else if (condition !== arg.args[0]) return value; // giving up, multiple conditions involved
282 }
283
284 if (condition === undefined) return value; // no conditional found, nothing to do
285 // We have at least one conditional argument; if there are more than one, they all share the same condition
286
287 let leftArgs = args.slice(0);
288 let rightArgs = args.slice(0);
289
290 for (let i = 0; i < args.length; i++) {
291 let arg = args[i];
292
293 if (arg instanceof _index2.AbstractValue && arg.kind === "conditional") {
294 leftArgs[i] = arg.args[1];
295 rightArgs[i] = arg.args[2];
296 }
297 }
298
299 return _index2.AbstractValue.createFromConditionalOp(realm, condition, create(leftArgs), create(rightArgs), condition.expressionLocation, isCondition, true);
300}
301
302function simplifyEquality(realm, equality) {
303 let loc = equality.expressionLocation;
304 let op = equality.kind;
305 let [x, y] = equality.args;
306 if (y instanceof _EmptyValue.default) return equality;
307 if (x instanceof _index2.ConcreteValue) [x, y] = [y, x];
308
309 if (x instanceof _index2.AbstractValue && x.kind === "conditional" && (!y.mightNotBeUndefined() || !y.mightNotBeNull())) {
310 function simplified(v) {
311 return v instanceof _index2.AbstractValue ? v.kind !== op : true;
312 } // try to simplify "(cond ? xx : xy) op undefined/null" to just "cond" or "!cond"
313
314
315 let [cond, xx, xy] = x.args;
316 (0, _invariant.default)(cond instanceof _index2.AbstractValue); // otherwise the the conditional should not have been created
317
318 if (op === "===" || op === "!==") {
319 if (!y.mightNotBeUndefined()) {
320 // if xx === undefined && xy !== undefined then cond <=> x === undefined
321 if (!xx.mightNotBeUndefined() && !xy.mightBeUndefined()) return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc); // if xx !== undefined && xy === undefined then !cond <=> x === undefined
322
323 if (!xx.mightBeUndefined() && !xy.mightNotBeUndefined()) return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc); // distribute equality test, creating more simplication opportunities
324
325 let sxx = _index2.AbstractValue.createFromBinaryOp(realm, op, xx, realm.intrinsics.undefined, xx.expressionLocation);
326
327 let sxy = _index2.AbstractValue.createFromBinaryOp(realm, op, xy, realm.intrinsics.undefined, xy.expressionLocation);
328
329 if (simplified(sxx) || simplified(sxy)) return _index2.AbstractValue.createFromConditionalOp(realm, cond, sxx, sxy, equality.expressionLocation, true);
330 }
331
332 if (!y.mightNotBeNull()) {
333 // if xx === null && xy !== null then cond <=> x === null
334 if (!xx.mightNotBeNull() && !xy.mightBeNull()) return op === "===" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc); // if xx !== null && xy === null then !cond <=> x === null
335
336 if (!xx.mightBeNull() && !xy.mightNotBeNull()) return op === "===" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc); // distribute equality test, creating more simplication opportunities
337
338 let sxx = _index2.AbstractValue.createFromBinaryOp(realm, op, xx, realm.intrinsics.null, xx.expressionLocation);
339
340 let sxy = _index2.AbstractValue.createFromBinaryOp(realm, op, xy, realm.intrinsics.null, xy.expressionLocation);
341
342 if (simplified(sxx) || simplified(sxy)) return _index2.AbstractValue.createFromConditionalOp(realm, cond, sxx, sxy, equality.expressionLocation, true);
343 }
344 } else {
345 (0, _invariant.default)(op === "==" || op === "!="); // if xx cannot be undefined/null and xy is undefined/null then !cond <=> x == undefined/null
346
347 if (!xx.mightBeUndefined() && !xx.mightBeNull() && (!xy.mightNotBeUndefined() || !xy.mightNotBeNull())) return op === "==" ? negate(realm, cond, loc) : makeBoolean(realm, cond, loc); // if xx is undefined/null and xy cannot be undefined/null then cond <=> x == undefined/null
348
349 if ((!xx.mightNotBeUndefined() || !xx.mightNotBeNull()) && !xy.mightBeUndefined() && !xy.mightBeNull()) return op === "==" ? makeBoolean(realm, cond, loc) : negate(realm, cond, loc); // distribute equality test, creating more simplication opportunities
350
351 let sxx = _index2.AbstractValue.createFromBinaryOp(realm, op, xx, y, xx.expressionLocation);
352
353 let sxy = _index2.AbstractValue.createFromBinaryOp(realm, op, xy, y, xy.expressionLocation);
354
355 if (simplified(sxx) || simplified(sxy)) return _index2.AbstractValue.createFromConditionalOp(realm, cond, sxx, sxy, equality.expressionLocation, true);
356 }
357 } else {
358 if (op === "===") {
359 if (x instanceof _index2.AbstractValue && x.kind === "conditional") {
360 let [cond, xx, xy] = x.args; // ((cond ? xx : xy) === y) && xx === y && xy !== y <=> cond
361
362 if (xx.equals(y) && !xy.equals(y)) return cond; // ((!cond ? xx : xy) === y) && xx !== y && xy === y <=> !cond
363
364 if (!xx.equals(y) && xy.equals(y)) return negate(realm, cond, loc);
365 } else if (y instanceof _index2.AbstractValue && y.kind === "conditional") {
366 let [cond, yx, yy] = y.args; // (x === (cond ? yx : yy) === y) && x === yx && x !== yy <=> cond
367
368 if (yx.equals(x) && !yy.equals(x)) return cond; // (x === (!cond ? yx : yy) === y) && x !== yx && x === yy <=> !cond
369
370 if (!x.equals(yx) && x.equals(yy)) return negate(realm, cond, loc);
371 }
372 }
373 }
374
375 return equality;
376}
377
378function makeBoolean(realm, value, loc = undefined) {
379 if (value.getType() === _index2.BooleanValue) return value;
380 if (value instanceof _index2.ConcreteValue) return new _index2.BooleanValue(realm, _singletons.To.ToBoolean(realm, value));
381 (0, _invariant.default)(value instanceof _index2.AbstractValue);
382
383 let v = _index2.AbstractValue.createFromUnaryOp(realm, "!", value, true, value.expressionLocation);
384
385 if (v instanceof _index2.ConcreteValue) return new _index2.BooleanValue(realm, !_singletons.To.ToBoolean(realm, v));
386 (0, _invariant.default)(v instanceof _index2.AbstractValue);
387 return _index2.AbstractValue.createFromUnaryOp(realm, "!", v, true, loc || value.expressionLocation);
388}
389
390function negate(realm, value, loc = undefined, unsimplifiedNegation = undefined, isCondition) {
391 if (value instanceof _index2.ConcreteValue) return _index.ValuesDomain.computeUnary(realm, "!", value);
392 (0, _invariant.default)(value instanceof _index2.AbstractValue);
393 value = simplify(realm, value, true);
394 if (!value.mightNotBeTrue()) return realm.intrinsics.false;
395 if (!value.mightNotBeFalse()) return realm.intrinsics.true;
396 (0, _invariant.default)(value instanceof _index2.AbstractValue);
397
398 if (value.kind === "!") {
399 let [x] = value.args;
400 if (isCondition || x.getType() === _index2.BooleanValue) return simplify(realm, x, true);
401 if (unsimplifiedNegation !== undefined) return unsimplifiedNegation;
402 return makeBoolean(realm, x, loc);
403 } // If NaN is not an issue, invert binary ops
404
405
406 if (value.args.length === 2 && !value.args[0].mightBeNumber() && !value.args[1].mightBeNumber()) {
407 let invertedComparison;
408
409 switch (value.kind) {
410 case "===":
411 invertedComparison = "!==";
412 break;
413
414 case "==":
415 invertedComparison = "!=";
416 break;
417
418 case "!==":
419 invertedComparison = "===";
420 break;
421
422 case "!=":
423 invertedComparison = "==";
424 break;
425
426 case "<":
427 invertedComparison = ">=";
428 break;
429
430 case "<=":
431 invertedComparison = ">";
432 break;
433
434 case ">":
435 invertedComparison = "<=";
436 break;
437
438 case ">=":
439 invertedComparison = "<";
440 break;
441
442 default:
443 break;
444 }
445
446 if (invertedComparison !== undefined) {
447 let left = simplify(realm, value.args[0]);
448 let right = simplify(realm, value.args[1]);
449 return _index2.AbstractValue.createFromBinaryOp(realm, invertedComparison, left, right, loc || value.expressionLocation);
450 }
451
452 let invertedLogicalOp;
453
454 switch (value.kind) {
455 case "&&":
456 invertedLogicalOp = "||";
457 break;
458
459 case "||":
460 invertedLogicalOp = "&&";
461 break;
462
463 default:
464 break;
465 }
466
467 if (invertedLogicalOp !== undefined) {
468 let left = negate(realm, value.args[0]);
469 let right = negate(realm, value.args[1]);
470 return _index2.AbstractValue.createFromLogicalOp(realm, invertedLogicalOp, left, right, loc || value.expressionLocation, true);
471 }
472 }
473
474 if (unsimplifiedNegation !== undefined) return unsimplifiedNegation;
475 return _index2.AbstractValue.createFromUnaryOp(realm, "!", value, true, loc || value.expressionLocation, true);
476}
477//# sourceMappingURL=simplifier.js.map
\No newline at end of file