UNPKG

32.7 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _index = require("../domains/index.js");
9
10var _errors = require("../errors.js");
11
12var _index2 = require("./index.js");
13
14var _utils = require("../react/utils.js");
15
16var _builder = _interopRequireDefault(require("../utils/builder.js"));
17
18var _index3 = require("../methods/index.js");
19
20var _singletons = require("../singletons.js");
21
22var _invariant = _interopRequireDefault(require("../invariant.js"));
23
24var _generator = require("../utils/generator.js");
25
26function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
28function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
29
30function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
31
32function isWidenedValue(v) {
33 if (!(v instanceof _index2.AbstractValue)) return false;
34 if (v.kind === "widened" || v.kind === "widened property") return true;
35
36 for (let a of v.args) {
37 if (isWidenedValue(a)) return true;
38 }
39
40 return false;
41}
42
43const lengthTemplateSrc = "(A).length";
44const lengthTemplate = (0, _builder.default)(lengthTemplateSrc);
45
46class ObjectValue extends _index2.ConcreteValue {
47 constructor(realm, proto, intrinsicName, refuseSerialization = false) {
48 super(realm, intrinsicName);
49 realm.recordNewObject(this);
50 if (realm.useAbstractInterpretation) this.setupBindings(this.getTrackedPropertyNames());
51 this.$Prototype = proto || realm.intrinsics.null;
52 this.$Extensible = realm.intrinsics.true;
53 this._isPartial = realm.intrinsics.false;
54 this._isHavoced = realm.intrinsics.false;
55 this._isSimple = realm.intrinsics.false;
56 this._simplicityIsTransitive = realm.intrinsics.false;
57 this._isFinal = realm.intrinsics.false;
58 this.properties = new Map();
59 this.symbols = new Map();
60 this.refuseSerialization = refuseSerialization; // this.$IsClassPrototype should be the last thing that gets initialized,
61 // as other code checks whether this.$IsClassPrototype === undefined
62 // as a proxy for whether initialization is still ongoing.
63
64 this.$IsClassPrototype = false;
65 }
66
67 getTrackedPropertyNames() {
68 return ObjectValue.trackedPropertyNames;
69 }
70
71 setupBindings(propertyNames) {
72 for (let propName of propertyNames) {
73 let propBindingName = ObjectValue.trackedPropertyBindingNames.get(propName);
74 (0, _invariant.default)(propBindingName !== undefined);
75 this[propBindingName] = undefined;
76 }
77 }
78
79 static setupTrackedPropertyAccessors(propertyNames) {
80 for (let propName of propertyNames) {
81 let propBindingName = ObjectValue.trackedPropertyBindingNames.get(propName);
82 if (propBindingName === undefined) ObjectValue.trackedPropertyBindingNames.set(propName, propBindingName = propName + "_binding");
83 Object.defineProperty(ObjectValue.prototype, propName, {
84 configurable: true,
85 get: function () {
86 let binding = this[propBindingName];
87 return binding === undefined ? undefined : binding.descriptor.value;
88 },
89 set: function (v) {
90 // Let's make sure that the object is not havoced.
91 // To that end, we'd like to call this.isHavocedObject().
92 // However, while the object is still being initialized,
93 // properties may be set, but this.isHavocedObject() may not be called yet.
94 // To check if we are still initializing, guard the call by looking at
95 // whether this.$IsClassPrototype has been initialized as a proxy for
96 // object initialization in general.
97 (0, _invariant.default)( // We're still initializing so we can set a property.
98 this.$IsClassPrototype === undefined || // It's not havoced so we can set a property.
99 this.mightNotBeHavocedObject() || // Object.assign() implementation needs to temporarily
100 // make potentially havoced objects non-partial and back.
101 // We don't gain anything from checking whether it's havoced
102 // before calling makePartial() so we'll whitelist this property.
103 propBindingName === "_isPartial_binding", "cannot mutate a havoced object");
104 let binding = this[propBindingName];
105
106 if (binding === undefined) {
107 let desc = {
108 writeable: true,
109 value: undefined
110 };
111 this[propBindingName] = binding = {
112 descriptor: desc,
113 object: this,
114 key: propName,
115 internalSlot: true
116 };
117 }
118
119 this.$Realm.recordModifiedProperty(binding);
120 binding.descriptor.value = v;
121 }
122 });
123 }
124 }
125
126 equals(x) {
127 return this === x;
128 }
129
130 getHash() {
131 if (!this.hashValue) {
132 this.hashValue = ++this.$Realm.objectCount;
133 }
134
135 return this.hashValue;
136 }
137
138 get temporalAlias() {
139 return this._temporalAlias;
140 }
141
142 set temporalAlias(value) {
143 this._temporalAlias = value;
144 }
145
146 hasStringOrSymbolProperties() {
147 for (let prop of this.properties.values()) {
148 if (prop.descriptor === undefined) continue;
149 return true;
150 }
151
152 for (let prop of this.symbols.values()) {
153 if (prop.descriptor === undefined) continue;
154 return true;
155 }
156
157 return false;
158 }
159
160 mightBeFalse() {
161 return false;
162 }
163
164 mightNotBeObject() {
165 return false;
166 }
167
168 throwIfNotObject() {
169 return this;
170 }
171
172 makeNotPartial() {
173 this._isPartial = this.$Realm.intrinsics.false;
174 }
175
176 makePartial() {
177 this._isPartial = this.$Realm.intrinsics.true;
178 }
179
180 makeSimple(option) {
181 this._isSimple = this.$Realm.intrinsics.true;
182 this._simplicityIsTransitive = new _index2.BooleanValue(this.$Realm, option === "transitive" || option instanceof _index2.StringValue && option.value === "transitive");
183 }
184
185 makeFinal() {
186 this._isFinal = this.$Realm.intrinsics.true;
187 }
188
189 makeNotFinal() {
190 this._isFinal = this.$Realm.intrinsics.false;
191 }
192
193 isPartialObject() {
194 return this._isPartial.mightBeTrue();
195 }
196
197 mightBeFinalObject() {
198 return this._isFinal.mightBeTrue();
199 }
200
201 mightNotBeFinalObject() {
202 return this._isFinal.mightNotBeTrue();
203 }
204
205 havoc() {
206 this._isHavoced = this.$Realm.intrinsics.true;
207 }
208
209 mightBeHavocedObject() {
210 return this._isHavoced.mightBeTrue();
211 }
212
213 mightNotBeHavocedObject() {
214 return this._isHavoced.mightNotBeTrue();
215 }
216
217 isSimpleObject() {
218 if (!this._isSimple.mightNotBeTrue()) return true;
219 if (this.isPartialObject()) return false;
220 if (this.symbols.size > 0) return false;
221
222 for (let propertyBinding of this.properties.values()) {
223 let desc = propertyBinding.descriptor;
224 if (desc === undefined) continue; // deleted
225
226 if (!(0, _index3.IsDataDescriptor)(this.$Realm, desc)) return false;
227 if (!desc.writable) return false;
228 }
229
230 if (this.$Prototype instanceof _index2.NullValue) return true;
231 if (this.$Prototype === this.$Realm.intrinsics.ObjectPrototype) return true;
232 (0, _invariant.default)(this.$Prototype);
233 return this.$Prototype.isSimpleObject();
234 }
235
236 isTransitivelySimple() {
237 return !this._simplicityIsTransitive.mightNotBeTrue();
238 }
239
240 getExtensible() {
241 return this.$Extensible.throwIfNotConcreteBoolean().value;
242 }
243
244 setExtensible(v) {
245 this.$Extensible = v ? this.$Realm.intrinsics.true : this.$Realm.intrinsics.false;
246 }
247
248 getKind() {
249 // we can deduce the natural prototype by checking whether the following internal slots are present
250 if (this.$SymbolData !== undefined) return "Symbol";
251 if (this.$StringData !== undefined) return "String";
252 if (this.$NumberData !== undefined) return "Number";
253 if (this.$BooleanData !== undefined) return "Boolean";
254 if (this.$DateValue !== undefined) return "Date";
255 if (this.$RegExpMatcher !== undefined) return "RegExp";
256 if (this.$SetData !== undefined) return "Set";
257 if (this.$MapData !== undefined) return "Map";
258 if (this.$DataView !== undefined) return "DataView";
259 if (this.$ArrayBufferData !== undefined) return "ArrayBuffer";
260 if (this.$WeakMapData !== undefined) return "WeakMap";
261 if (this.$WeakSetData !== undefined) return "WeakSet";
262 if ((0, _utils.isReactElement)(this) && this.$Realm.react.enabled) return "ReactElement";
263 if (this.$TypedArrayName !== undefined) return this.$TypedArrayName; // TODO #26 #712: Promises. All kinds of iterators. Generators.
264
265 return "Object";
266 }
267
268 defineNativeMethod(name, length, callback, desc = {}) {
269 let intrinsicName;
270
271 if (typeof name === "string") {
272 if (this.intrinsicName) intrinsicName = `${this.intrinsicName}.${name}`;
273 } else if (name instanceof _index2.SymbolValue) {
274 if (this.intrinsicName && name.intrinsicName) intrinsicName = `${this.intrinsicName}[${name.intrinsicName}]`;
275 } else {
276 (0, _invariant.default)(false);
277 }
278
279 let fnValue = new _index2.NativeFunctionValue(this.$Realm, intrinsicName, name, length, callback, false);
280 this.defineNativeProperty(name, fnValue, desc);
281 return fnValue;
282 }
283
284 defineNativeProperty(name, value, desc = {}) {
285 (0, _invariant.default)(!value || value instanceof _index2.Value);
286 this.$DefineOwnProperty(name, _objectSpread({
287 value,
288 writable: true,
289 enumerable: false,
290 configurable: true
291 }, desc));
292 }
293
294 defineNativeGetter(name, callback, desc = {}) {
295 let intrinsicName, funcName;
296
297 if (typeof name === "string") {
298 funcName = `get ${name}`;
299 if (this.intrinsicName) intrinsicName = `${this.intrinsicName}.${name}`;
300 } else if (name instanceof _index2.SymbolValue) {
301 funcName = name.$Description instanceof _index2.Value ? `get [${name.$Description.throwIfNotConcreteString().value}]` : `get [${"?"}]`;
302 if (this.intrinsicName && name.intrinsicName) intrinsicName = `${this.intrinsicName}[${name.intrinsicName}]`;
303 } else {
304 (0, _invariant.default)(false);
305 }
306
307 let func = new _index2.NativeFunctionValue(this.$Realm, intrinsicName, funcName, 0, callback);
308 this.$DefineOwnProperty(name, _objectSpread({
309 get: func,
310 set: this.$Realm.intrinsics.undefined,
311 enumerable: false,
312 configurable: true
313 }, desc));
314 }
315
316 defineNativeConstant(name, value, desc = {}) {
317 (0, _invariant.default)(!value || value instanceof _index2.Value);
318 this.$DefineOwnProperty(name, _objectSpread({
319 value,
320 writable: false,
321 enumerable: false,
322 configurable: false
323 }, desc));
324 }
325
326 getOwnPropertyKeysArray(allowAbstractKeys = false) {
327 if (this.isPartialObject() || this.mightBeHavocedObject() || this.unknownProperty !== undefined) {
328 _index2.AbstractValue.reportIntrospectionError(this);
329
330 throw new _errors.FatalError();
331 }
332
333 let keyArray = Array.from(this.properties.keys());
334 keyArray = keyArray.filter(x => {
335 let pb = this.properties.get(x);
336 if (!pb || pb.descriptor === undefined) return false;
337 let pv = pb.descriptor.value;
338 if (pv === undefined) return true;
339 (0, _invariant.default)(pv instanceof _index2.Value);
340 if (!pv.mightHaveBeenDeleted()) return true; // The property may or may not be there at runtime.
341 // We can at best return an abstract keys array.
342 // For now, unless the caller has told us that is okay,
343 // just terminate.
344
345 (0, _invariant.default)(pv instanceof _index2.AbstractValue);
346 if (allowAbstractKeys) return true;
347
348 _index2.AbstractValue.reportIntrospectionError(pv);
349
350 throw new _errors.FatalError();
351 });
352 this.$Realm.callReportObjectGetOwnProperties(this);
353 return keyArray;
354 } // Note that internal properties will not be copied to the snapshot, nor will they be removed.
355
356
357 getSnapshot(options) {
358 try {
359 if (this.temporalAlias !== undefined) return this.temporalAlias;
360 (0, _invariant.default)(!this.isPartialObject());
361 let template = new ObjectValue(this.$Realm, this.$Realm.intrinsics.ObjectPrototype);
362 this.copyKeys(this.$OwnPropertyKeys(), this, template);
363 let realm = this.$Realm; // The snapshot is an immutable object snapshot
364
365 template.makeFinal(); // The original object might be a React props object, thus
366 // if it is, we need to ensure we mark it with the same rules
367
368 if (realm.react.enabled && realm.react.reactProps.has(this)) {
369 realm.react.reactProps.add(template);
370 }
371
372 let operationDescriptor = (0, _generator.createOperationDescriptor)("SINGLE_ARG");
373
374 let result = _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, ObjectValue, [template], operationDescriptor, {
375 skipInvariant: true,
376 isPure: true
377 });
378
379 (0, _invariant.default)(result instanceof _index2.AbstractObjectValue);
380 result.values = new _index.ValuesDomain(template);
381 return result;
382 } finally {
383 if (options && options.removeProperties) {
384 this.properties = new Map();
385 this.symbols = new Map();
386 this.unknownProperty = undefined;
387 }
388 }
389 }
390
391 copyKeys(keys, from, to) {
392 // c. Repeat for each element nextKey of keys in List order,
393 for (let nextKey of keys) {
394 // i. Let desc be ? from.[[GetOwnProperty]](nextKey).
395 let desc = from.$GetOwnProperty(nextKey); // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
396
397 if (desc && desc.enumerable) {
398 _singletons.Properties.ThrowIfMightHaveBeenDeleted(desc.value); // 1. Let propValue be ? Get(from, nextKey).
399
400
401 let propValue = (0, _index3.Get)(this.$Realm, from, nextKey); // 2. Perform ? Set(to, nextKey, propValue, true).
402
403 _singletons.Properties.Set(this.$Realm, to, nextKey, propValue, true);
404 }
405 }
406 }
407
408 _serialize(set, stack) {
409 let obj = set({});
410
411 for (let [key, propertyBinding] of this.properties) {
412 let desc = propertyBinding.descriptor;
413 if (desc === undefined) continue; // deleted
414
415 _singletons.Properties.ThrowIfMightHaveBeenDeleted(desc.value);
416
417 let serializedDesc = {
418 enumerable: desc.enumerable,
419 configurable: desc.configurable
420 };
421
422 if (desc.value) {
423 serializedDesc.writable = desc.writable;
424 (0, _invariant.default)(desc.value instanceof _index2.Value);
425 serializedDesc.value = desc.value.serialize(stack);
426 } else {
427 (0, _invariant.default)(desc.get !== undefined);
428 serializedDesc.get = desc.get.serialize(stack);
429 (0, _invariant.default)(desc.set !== undefined);
430 serializedDesc.set = desc.set.serialize(stack);
431 }
432
433 Object.defineProperty(obj, key, serializedDesc);
434 }
435
436 return obj;
437 } // Whether [[{Get,Set}PrototypeOf]] delegate to Ordinary{Get,Set}PrototypeOf.
438 // E.g. ProxyValue overrides this to return false.
439 // See ECMA262 9.1.2.1 for an algorithm where this is relevant
440
441
442 usesOrdinaryObjectInternalPrototypeMethods() {
443 return true;
444 } // ECMA262 9.1.1
445
446
447 $GetPrototypeOf() {
448 return this.$Prototype;
449 } // ECMA262 9.1.2
450
451
452 $SetPrototypeOf(V) {
453 // 1. Return ! OrdinarySetPrototypeOf(O, V).
454 return _singletons.Properties.OrdinarySetPrototypeOf(this.$Realm, this, V);
455 } // ECMA262 9.1.3
456
457
458 $IsExtensible() {
459 // 1. Return ! OrdinaryIsExtensible(O).
460 return (0, _index3.OrdinaryIsExtensible)(this.$Realm, this);
461 } // ECMA262 9.1.4
462
463
464 $PreventExtensions() {
465 // 1. Return ! OrdinaryPreventExtensions(O).
466 return (0, _index3.OrdinaryPreventExtensions)(this.$Realm, this);
467 } // ECMA262 9.1.5
468
469
470 $GetOwnProperty(P) {
471 // 1. Return ! OrdinaryGetOwnProperty(O, P).
472 return _singletons.Properties.OrdinaryGetOwnProperty(this.$Realm, this, P);
473 } // ECMA262 9.1.6
474
475
476 $DefineOwnProperty(P, Desc) {
477 // 1. Return ? OrdinaryDefineOwnProperty(O, P, Desc).
478 return _singletons.Properties.OrdinaryDefineOwnProperty(this.$Realm, this, P, Desc);
479 } // ECMA262 9.1.7
480
481
482 $HasProperty(P) {
483 if (this.unknownProperty !== undefined && this.$GetOwnProperty(P) === undefined) {
484 _index2.AbstractValue.reportIntrospectionError(this, P);
485
486 throw new _errors.FatalError();
487 }
488
489 return (0, _index3.OrdinaryHasProperty)(this.$Realm, this, P);
490 } // ECMA262 9.1.8
491
492
493 $Get(P, Receiver) {
494 let prop = this.unknownProperty;
495
496 if (prop !== undefined && prop.descriptor !== undefined && this.$GetOwnProperty(P) === undefined) {
497 let desc = prop.descriptor;
498 (0, _invariant.default)(desc !== undefined);
499 let val = desc.value;
500 (0, _invariant.default)(val instanceof _index2.AbstractValue);
501 let propValue;
502
503 if (P instanceof _index2.StringValue) {
504 propValue = P;
505 } else if (typeof P === "string") {
506 propValue = new _index2.StringValue(this.$Realm, P);
507 }
508
509 if (val.kind === "widened numeric property") {
510 (0, _invariant.default)(Receiver instanceof _index2.ArrayValue && _index2.ArrayValue.isIntrinsicAndHasWidenedNumericProperty(Receiver));
511 let propName;
512
513 if (P instanceof _index2.StringValue) {
514 propName = P.value;
515 } else {
516 propName = P;
517 }
518
519 return (0, _index3.GetFromArrayWithWidenedNumericProperty)(this.$Realm, Receiver, propName);
520 } else if (!propValue) {
521 _index2.AbstractValue.reportIntrospectionError(val, "abstract computed property name");
522
523 throw new _errors.FatalError();
524 }
525
526 return this.specializeJoin(val, propValue);
527 } // 1. Return ? OrdinaryGet(O, P, Receiver).
528
529
530 return (0, _index3.OrdinaryGet)(this.$Realm, this, P, Receiver);
531 }
532
533 _SafeGetDataPropertyValue(P) {
534 let savedInvariantLevel = this.$Realm.invariantLevel;
535
536 try {
537 this.$Realm.invariantLevel = 0;
538 let desc = this.$GetOwnProperty(P);
539 return desc !== undefined && desc.value instanceof _index2.Value ? desc.value : this.$Realm.intrinsics.undefined;
540 } finally {
541 this.$Realm.invariantLevel = savedInvariantLevel;
542 }
543 }
544
545 $GetPartial(P, Receiver) {
546 if (Receiver instanceof _index2.AbstractValue && Receiver.getType() === _index2.StringValue && P === "length") {
547 return _index2.AbstractValue.createFromTemplate(this.$Realm, lengthTemplate, _index2.NumberValue, [Receiver], lengthTemplateSrc);
548 }
549
550 if (!(P instanceof _index2.AbstractValue)) return this.$Get(P, Receiver); // A string coercion might have side-effects.
551 // TODO #1682: We assume that simple objects mean that they don't have a
552 // side-effectful valueOf and toString but that's not enforced.
553
554 if (P.mightNotBeString() && P.mightNotBeNumber() && !P.isSimpleObject()) {
555 if (this.$Realm.isInPureScope()) {
556 // If we're in pure scope, we can havoc the key and keep going.
557 // Coercion can only have effects on anything reachable from the key.
558 _singletons.Havoc.value(this.$Realm, P);
559 } else {
560 let error = new _errors.CompilerDiagnostic("property key might not have a well behaved toString or be a symbol", this.$Realm.currentLocation, "PP0002", "RecoverableError");
561
562 if (this.$Realm.handleError(error) !== "Recover") {
563 throw new _errors.FatalError();
564 }
565 }
566 } // We assume that simple objects have no getter/setter properties.
567
568
569 if (!this.isSimpleObject()) {
570 if (this.$Realm.isInPureScope()) {
571 // If we're in pure scope, we can havoc the object. Coercion
572 // can only have effects on anything reachable from this object.
573 // We assume that if the receiver is different than this object,
574 // then we only got here because there were no other keys with
575 // this name on other parts of the prototype chain.
576 // TODO #1675: A fix to 1675 needs to take this into account.
577 _singletons.Havoc.value(this.$Realm, Receiver);
578
579 return _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, _index2.Value, [Receiver, P], (0, _generator.createOperationDescriptor)("OBJECT_GET_PARTIAL"), {
580 skipInvariant: true,
581 isPure: true
582 });
583 } else {
584 let error = new _errors.CompilerDiagnostic("unknown property access might need to invoke a getter", this.$Realm.currentLocation, "PP0030", "RecoverableError");
585
586 if (this.$Realm.handleError(error) !== "Recover") {
587 throw new _errors.FatalError();
588 }
589 }
590 }
591
592 P = _singletons.To.ToStringAbstract(this.$Realm, P); // If all else fails, use this expression
593 // TODO #1675: Check the prototype chain for known properties too.
594
595 let result;
596
597 if (this.isPartialObject()) {
598 if (isWidenedValue(P)) {
599 // TODO #1678: Use a snapshot or havoc this object.
600 return _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, _index2.Value, [this, P], (0, _generator.createOperationDescriptor)("OBJECT_GET_PARTIAL"), {
601 skipInvariant: true,
602 isPure: true
603 });
604 }
605
606 result = _index2.AbstractValue.createFromType(this.$Realm, _index2.Value, "sentinel member expression", [this, P]);
607 } else {
608 result = _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, _index2.Value, [this, P], (0, _generator.createOperationDescriptor)("OBJECT_GET_PARTIAL"), {
609 skipInvariant: true,
610 isPure: true
611 });
612 } // Get a specialization of the join of all values written to the object
613 // with abstract property names.
614
615
616 let prop = this.unknownProperty;
617
618 if (prop !== undefined) {
619 let desc = prop.descriptor;
620
621 if (desc !== undefined) {
622 let val = desc.value;
623 (0, _invariant.default)(val instanceof _index2.AbstractValue);
624
625 if (val.kind === "widened numeric property") {
626 (0, _invariant.default)(Receiver instanceof _index2.ArrayValue && _index2.ArrayValue.isIntrinsicAndHasWidenedNumericProperty(Receiver));
627 return (0, _index3.GetFromArrayWithWidenedNumericProperty)(this.$Realm, Receiver, P instanceof _index2.StringValue ? P.value : P);
628 }
629
630 result = this.specializeJoin(val, P);
631 }
632 } // Join in all of the other values that were written to the object with
633 // concrete property names.
634
635
636 for (let [key, propertyBinding] of this.properties) {
637 let desc = propertyBinding.descriptor;
638 if (desc === undefined) continue; // deleted
639
640 (0, _invariant.default)(desc.value !== undefined); // otherwise this is not simple
641
642 let val = desc.value;
643 (0, _invariant.default)(val instanceof _index2.Value);
644
645 let cond = _index2.AbstractValue.createFromBinaryOp(this.$Realm, "===", P, new _index2.StringValue(this.$Realm, key), undefined, "check for known property");
646
647 result = _index2.AbstractValue.createFromConditionalOp(this.$Realm, cond, val, result);
648 }
649
650 return result;
651 }
652
653 specializeJoin(absVal, propName) {
654 if (absVal.kind === "widened property") {
655 let ob = absVal.args[0];
656
657 if (propName instanceof _index2.StringValue) {
658 let pName = propName.value;
659 let pNumber = +pName;
660 if (pName === pNumber + "") propName = new _index2.NumberValue(this.$Realm, pNumber);
661 }
662
663 return _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, absVal.getType(), [ob, propName], (0, _generator.createOperationDescriptor)("OBJECT_GET_PARTIAL"), {
664 skipInvariant: true,
665 isPure: true
666 });
667 }
668
669 (0, _invariant.default)(absVal.args.length === 3 && absVal.kind === "conditional");
670 let generic_cond = absVal.args[0];
671 (0, _invariant.default)(generic_cond instanceof _index2.AbstractValue);
672 let cond = this.specializeCond(generic_cond, propName);
673 let arg1 = absVal.args[1];
674 if (arg1 instanceof _index2.AbstractValue && arg1.args.length === 3) arg1 = this.specializeJoin(arg1, propName);
675 let arg2 = absVal.args[2];
676
677 if (arg2 instanceof _index2.AbstractValue) {
678 if (arg2.kind === "template for prototype member expression") {
679 let ob = arg2.args[0];
680 arg2 = _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, absVal.getType(), [ob, propName], (0, _generator.createOperationDescriptor)("OBJECT_GET_PARTIAL"), {
681 skipInvariant: true,
682 isPure: true
683 });
684 } else if (arg2.args.length === 3) {
685 arg2 = this.specializeJoin(arg2, propName);
686 }
687 }
688
689 return _index2.AbstractValue.createFromConditionalOp(this.$Realm, cond, arg1, arg2, absVal.expressionLocation);
690 }
691
692 specializeCond(absVal, propName) {
693 if (absVal.kind === "template for property name condition") return _index2.AbstractValue.createFromBinaryOp(this.$Realm, "===", absVal.args[0], propName);
694 return absVal;
695 } // ECMA262 9.1.9
696
697
698 $Set(P, V, Receiver) {
699 // 1. Return ? OrdinarySet(O, P, V, Receiver).
700 return _singletons.Properties.OrdinarySet(this.$Realm, this, P, V, Receiver);
701 }
702
703 $SetPartial(P, V, Receiver) {
704 if (!(P instanceof _index2.AbstractValue)) return this.$Set(P, V, Receiver);
705 let pIsLoopVar = isWidenedValue(P);
706
707 let pIsNumeric = _index2.Value.isTypeCompatibleWith(P.getType(), _index2.NumberValue); // A string coercion might have side-effects.
708 // TODO #1682: We assume that simple objects mean that they don't have a
709 // side-effectful valueOf and toString but that's not enforced.
710
711
712 if (P.mightNotBeString() && P.mightNotBeNumber() && !P.isSimpleObject()) {
713 if (this.$Realm.isInPureScope()) {
714 // If we're in pure scope, we can havoc the key and keep going.
715 // Coercion can only have effects on anything reachable from the key.
716 _singletons.Havoc.value(this.$Realm, P);
717 } else {
718 let error = new _errors.CompilerDiagnostic("property key might not have a well behaved toString or be a symbol", this.$Realm.currentLocation, "PP0002", "RecoverableError");
719
720 if (this.$Realm.handleError(error) !== "Recover") {
721 throw new _errors.FatalError();
722 }
723 }
724 } // We assume that simple objects have no getter/setter properties and
725 // that all properties are writable.
726
727
728 if (!this.isSimpleObject()) {
729 if (this.$Realm.isInPureScope()) {
730 // If we're in pure scope, we can havoc the object and leave an
731 // assignment in place.
732 _singletons.Havoc.value(this.$Realm, Receiver); // We also need to havoc the value since it might leak to a setter.
733
734
735 _singletons.Havoc.value(this.$Realm, V);
736
737 this.$Realm.evaluateWithPossibleThrowCompletion(() => {
738 let generator = this.$Realm.generator;
739 (0, _invariant.default)(generator);
740 (0, _invariant.default)(P instanceof _index2.AbstractValue);
741 generator.emitStatement([Receiver, P, V], (0, _generator.createOperationDescriptor)("OBJECT_SET_PARTIAL"));
742 return this.$Realm.intrinsics.undefined;
743 }, _index.TypesDomain.topVal, _index.ValuesDomain.topVal); // The emitted assignment might throw at runtime but if it does, that
744 // is handled by evaluateWithPossibleThrowCompletion. Anything that
745 // happens after this, can assume we didn't throw and therefore,
746 // we return true here.
747
748 return true;
749 } else {
750 let error = new _errors.CompilerDiagnostic("unknown property access might need to invoke a setter", this.$Realm.currentLocation, "PP0030", "RecoverableError");
751
752 if (this.$Realm.handleError(error) !== "Recover") {
753 throw new _errors.FatalError();
754 }
755 }
756 } // We should never consult the prototype chain for unknown properties.
757 // If it was simple, it would've been an assignment to the receiver.
758 // The only case the Receiver isn't this, if this was a ToObject
759 // coercion from a PrimitiveValue.
760
761
762 (0, _invariant.default)(this === Receiver || (0, _index3.HasCompatibleType)(Receiver, _index2.PrimitiveValue));
763 P = _singletons.To.ToStringAbstract(this.$Realm, P);
764
765 function createTemplate(realm, propName) {
766 return _index2.AbstractValue.createFromBinaryOp(realm, "===", propName, new _index2.StringValue(realm, ""), undefined, "template for property name condition");
767 }
768
769 let prop;
770
771 if (this.unknownProperty === undefined) {
772 prop = {
773 descriptor: undefined,
774 object: this,
775 key: P
776 };
777 this.unknownProperty = prop;
778 } else {
779 prop = this.unknownProperty;
780 }
781
782 this.$Realm.recordModifiedProperty(prop);
783 let desc = prop.descriptor;
784
785 if (desc === undefined) {
786 let newVal = V;
787
788 if (!(V instanceof _index2.UndefinedValue) && !isWidenedValue(P)) {
789 // join V with sentinel, using a property name test as the condition
790 let cond = createTemplate(this.$Realm, P);
791
792 let sentinel = _index2.AbstractValue.createFromType(this.$Realm, _index2.Value, "template for prototype member expression", [Receiver, P]);
793
794 newVal = _index2.AbstractValue.createFromConditionalOp(this.$Realm, cond, V, sentinel);
795 }
796
797 prop.descriptor = {
798 writable: true,
799 enumerable: true,
800 configurable: true,
801 value: newVal
802 };
803 } else {
804 // join V with current value of this.unknownProperty. I.e. weak update.
805 let oldVal = desc.value;
806 (0, _invariant.default)(oldVal instanceof _index2.Value);
807 let newVal = oldVal;
808
809 if (!(V instanceof _index2.UndefinedValue)) {
810 if (isWidenedValue(P)) {
811 newVal = V; // It will be widened later on
812 } else {
813 let cond = createTemplate(this.$Realm, P);
814 newVal = _index2.AbstractValue.createFromConditionalOp(this.$Realm, cond, V, oldVal);
815 }
816 }
817
818 desc.value = newVal;
819 } // Since we don't know the name of the property we are writing to, we also need
820 // to perform weak updates of all of the known properties.
821 // First clear out this.unknownProperty so that helper routines know its OK to update the properties
822
823
824 let savedUnknownProperty = this.unknownProperty;
825 this.unknownProperty = undefined;
826
827 for (let [key, propertyBinding] of this.properties) {
828 if (pIsLoopVar && pIsNumeric) {
829 // Delete numeric properties and don't do weak updates on other properties.
830 if (key !== +key + "") continue;
831 this.properties.delete(key);
832 continue;
833 }
834
835 let oldVal = this.$Realm.intrinsics.empty;
836
837 if (propertyBinding.descriptor && propertyBinding.descriptor.value) {
838 oldVal = propertyBinding.descriptor.value;
839 (0, _invariant.default)(oldVal instanceof _index2.Value); // otherwise this is not simple
840 }
841
842 let cond = _index2.AbstractValue.createFromBinaryOp(this.$Realm, "===", P, new _index2.StringValue(this.$Realm, key));
843
844 let newVal = _index2.AbstractValue.createFromConditionalOp(this.$Realm, cond, V, oldVal);
845
846 _singletons.Properties.OrdinarySet(this.$Realm, this, key, newVal, Receiver);
847 }
848
849 this.unknownProperty = savedUnknownProperty;
850 return true;
851 } // ECMA262 9.1.10
852
853
854 $Delete(P) {
855 if (this.unknownProperty !== undefined) {
856 // TODO #946: generate a delete from the object
857 _index2.AbstractValue.reportIntrospectionError(this, P);
858
859 throw new _errors.FatalError();
860 } // 1. Return ? OrdinaryDelete(O, P).
861
862
863 return _singletons.Properties.OrdinaryDelete(this.$Realm, this, P);
864 } // ECMA262 9.1.11
865
866
867 $OwnPropertyKeys() {
868 return (0, _index3.OrdinaryOwnPropertyKeys)(this.$Realm, this);
869 }
870
871 static refuseSerializationOnPropertyBinding(pb) {
872 if (pb.object.refuseSerialization) return true;
873 if (pb.internalSlot && typeof pb.key === "string" && pb.key[0] === "_") return true;
874 return false;
875 }
876
877}
878
879exports.default = ObjectValue;
880
881_defineProperty(ObjectValue, "trackedPropertyNames", ["_isPartial", "_isHavoced", "_isSimple", "_isFinal", "_simplicityIsTransitive", "_temporalAlias", "$ArrayIteratorNextIndex", "$DateValue", "$Extensible", "$IteratedList", "$IteratedObject", "$IteratedSet", "$IteratedString", "$Map", "$MapData", "$MapNextIndex", "$Prototype", "$SetData", "$SetNextIndex", "$StringIteratorNextIndex", "$WeakMapData", "$WeakSetData"]);
882
883_defineProperty(ObjectValue, "trackedPropertyBindingNames", new Map());
884//# sourceMappingURL=ObjectValue.js.map
\No newline at end of file