1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /*
8  * JS object implementation.
9  */
10 
11 #include "vm/JSObject-inl.h"
12 
13 #include "mozilla/MathAlgorithms.h"
14 #include "mozilla/MemoryReporting.h"
15 #include "mozilla/TemplateLib.h"
16 
17 #include <algorithm>
18 #include <string.h>
19 
20 #include "jsapi.h"
21 #include "jsexn.h"
22 #include "jsfriendapi.h"
23 #include "jsnum.h"
24 #include "jstypes.h"
25 
26 #include "builtin/Array.h"
27 #include "builtin/BigInt.h"
28 #include "builtin/Eval.h"
29 #include "builtin/MapObject.h"
30 #include "builtin/Object.h"
31 #include "builtin/String.h"
32 #include "builtin/Symbol.h"
33 #include "builtin/WeakSetObject.h"
34 #include "ds/IdValuePair.h"  // js::IdValuePair
35 #include "frontend/BytecodeCompiler.h"
36 #include "gc/Policy.h"
37 #include "jit/BaselineJIT.h"
38 #include "js/CharacterEncoding.h"
39 #include "js/friend/DumpFunctions.h"  // js::DumpObject
40 #include "js/friend/ErrorMessages.h"  // JSErrNum, js::GetErrorMessage, JSMSG_*
41 #include "js/friend/WindowProxy.h"    // js::IsWindow, js::ToWindowProxyIfWindow
42 #include "js/MemoryMetrics.h"
43 #include "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
44 #include "js/PropertySpec.h"        // JSPropertySpec
45 #include "js/Proxy.h"
46 #include "js/Result.h"
47 #include "js/UbiNode.h"
48 #include "js/UniquePtr.h"
49 #include "js/Wrapper.h"
50 #include "proxy/DeadObjectProxy.h"
51 #include "util/Memory.h"
52 #include "util/Text.h"
53 #include "util/Windows.h"
54 #include "vm/ArgumentsObject.h"
55 #include "vm/BytecodeUtil.h"
56 #include "vm/DateObject.h"
57 #include "vm/Interpreter.h"
58 #include "vm/Iteration.h"
59 #include "vm/JSAtom.h"
60 #include "vm/JSContext.h"
61 #include "vm/JSFunction.h"
62 #include "vm/JSScript.h"
63 #include "vm/ProxyObject.h"
64 #include "vm/RegExpStaticsObject.h"
65 #include "vm/Shape.h"
66 #include "vm/TypedArrayObject.h"
67 #include "vm/WellKnownAtom.h"  // js_*_str
68 
69 #include "builtin/Boolean-inl.h"
70 #include "gc/Marking-inl.h"
71 #include "vm/ArrayObject-inl.h"
72 #include "vm/BooleanObject-inl.h"
73 #include "vm/Caches-inl.h"
74 #include "vm/Compartment-inl.h"
75 #include "vm/Interpreter-inl.h"
76 #include "vm/JSAtom-inl.h"
77 #include "vm/JSContext-inl.h"
78 #include "vm/JSFunction-inl.h"
79 #include "vm/NativeObject-inl.h"
80 #include "vm/NumberObject-inl.h"
81 #include "vm/ObjectFlags-inl.h"
82 #include "vm/PlainObject-inl.h"  // js::CopyInitializerObject
83 #include "vm/Realm-inl.h"
84 #include "vm/Shape-inl.h"
85 #include "vm/StringObject-inl.h"
86 #include "vm/TypedArrayObject-inl.h"
87 #include "wasm/TypedObject-inl.h"
88 
89 using namespace js;
90 
91 using mozilla::Maybe;
92 
ReportNotObject(JSContext * cx,JSErrNum err,int spindex,HandleValue v)93 void js::ReportNotObject(JSContext* cx, JSErrNum err, int spindex,
94                          HandleValue v) {
95   MOZ_ASSERT(!v.isObject());
96   ReportValueError(cx, err, spindex, v, nullptr);
97 }
98 
ReportNotObject(JSContext * cx,JSErrNum err,HandleValue v)99 void js::ReportNotObject(JSContext* cx, JSErrNum err, HandleValue v) {
100   ReportNotObject(cx, err, JSDVG_SEARCH_STACK, v);
101 }
102 
ReportNotObject(JSContext * cx,const Value & v)103 void js::ReportNotObject(JSContext* cx, const Value& v) {
104   RootedValue value(cx, v);
105   ReportNotObject(cx, JSMSG_OBJECT_REQUIRED, value);
106 }
107 
ReportNotObjectArg(JSContext * cx,const char * nth,const char * fun,HandleValue v)108 void js::ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun,
109                             HandleValue v) {
110   MOZ_ASSERT(!v.isObject());
111 
112   UniqueChars bytes;
113   if (const char* chars = ValueToSourceForError(cx, v, bytes)) {
114     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
115                                JSMSG_OBJECT_REQUIRED_ARG, nth, fun, chars);
116   }
117 }
118 
InformalValueTypeName(const Value & v)119 JS_PUBLIC_API const char* JS::InformalValueTypeName(const Value& v) {
120   switch (v.type()) {
121     case ValueType::Double:
122     case ValueType::Int32:
123       return "number";
124     case ValueType::Boolean:
125       return "boolean";
126     case ValueType::Undefined:
127       return "undefined";
128     case ValueType::Null:
129       return "null";
130     case ValueType::String:
131       return "string";
132     case ValueType::Symbol:
133       return "symbol";
134     case ValueType::BigInt:
135       return "bigint";
136     case ValueType::Object:
137       return v.toObject().getClass()->name;
138     case ValueType::Magic:
139       return "magic";
140     case ValueType::PrivateGCThing:
141       break;
142   }
143 
144   MOZ_CRASH("unexpected type");
145 }
146 
147 // ES6 draft rev37 6.2.4.4 FromPropertyDescriptor
FromPropertyDescriptor(JSContext * cx,Handle<Maybe<PropertyDescriptor>> desc_,MutableHandleValue vp)148 JS_PUBLIC_API bool JS::FromPropertyDescriptor(
149     JSContext* cx, Handle<Maybe<PropertyDescriptor>> desc_,
150     MutableHandleValue vp) {
151   AssertHeapIsIdle();
152   CHECK_THREAD(cx);
153   cx->check(desc_);
154 
155   // Step 1.
156   if (desc_.isNothing()) {
157     vp.setUndefined();
158     return true;
159   }
160 
161   Rooted<PropertyDescriptor> desc(cx, *desc_);
162   return FromPropertyDescriptorToObject(cx, desc, vp);
163 }
164 
FromPropertyDescriptorToObject(JSContext * cx,Handle<PropertyDescriptor> desc,MutableHandleValue vp)165 bool js::FromPropertyDescriptorToObject(JSContext* cx,
166                                         Handle<PropertyDescriptor> desc,
167                                         MutableHandleValue vp) {
168   // Step 2-3.
169   RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
170   if (!obj) {
171     return false;
172   }
173 
174   const JSAtomState& names = cx->names();
175 
176   // Step 4.
177   if (desc.hasValue()) {
178     if (!DefineDataProperty(cx, obj, names.value, desc.value())) {
179       return false;
180     }
181   }
182 
183   // Step 5.
184   RootedValue v(cx);
185   if (desc.hasWritable()) {
186     v.setBoolean(desc.writable());
187     if (!DefineDataProperty(cx, obj, names.writable, v)) {
188       return false;
189     }
190   }
191 
192   // Step 6.
193   if (desc.hasGetter()) {
194     if (JSObject* get = desc.getter()) {
195       v.setObject(*get);
196     } else {
197       v.setUndefined();
198     }
199     if (!DefineDataProperty(cx, obj, names.get, v)) {
200       return false;
201     }
202   }
203 
204   // Step 7.
205   if (desc.hasSetter()) {
206     if (JSObject* set = desc.setter()) {
207       v.setObject(*set);
208     } else {
209       v.setUndefined();
210     }
211     if (!DefineDataProperty(cx, obj, names.set, v)) {
212       return false;
213     }
214   }
215 
216   // Step 8.
217   if (desc.hasEnumerable()) {
218     v.setBoolean(desc.enumerable());
219     if (!DefineDataProperty(cx, obj, names.enumerable, v)) {
220       return false;
221     }
222   }
223 
224   // Step 9.
225   if (desc.hasConfigurable()) {
226     v.setBoolean(desc.configurable());
227     if (!DefineDataProperty(cx, obj, names.configurable, v)) {
228       return false;
229     }
230   }
231 
232   vp.setObject(*obj);
233   return true;
234 }
235 
GetFirstArgumentAsObject(JSContext * cx,const CallArgs & args,const char * method,MutableHandleObject objp)236 bool js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args,
237                                   const char* method,
238                                   MutableHandleObject objp) {
239   if (!args.requireAtLeast(cx, method, 1)) {
240     return false;
241   }
242 
243   HandleValue v = args[0];
244   if (!v.isObject()) {
245     UniqueChars bytes =
246         DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
247     if (!bytes) {
248       return false;
249     }
250     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
251                              JSMSG_UNEXPECTED_TYPE, bytes.get(),
252                              "not an object");
253     return false;
254   }
255 
256   objp.set(&v.toObject());
257   return true;
258 }
259 
GetPropertyIfPresent(JSContext * cx,HandleObject obj,HandleId id,MutableHandleValue vp,bool * foundp)260 static bool GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id,
261                                  MutableHandleValue vp, bool* foundp) {
262   if (!HasProperty(cx, obj, id, foundp)) {
263     return false;
264   }
265   if (!*foundp) {
266     vp.setUndefined();
267     return true;
268   }
269 
270   return GetProperty(cx, obj, obj, id, vp);
271 }
272 
Throw(JSContext * cx,HandleId id,unsigned errorNumber,const char * details)273 bool js::Throw(JSContext* cx, HandleId id, unsigned errorNumber,
274                const char* details) {
275   MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1));
276   MOZ_ASSERT_IF(details, JS::StringIsASCII(details));
277 
278   UniqueChars bytes =
279       IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
280   if (!bytes) {
281     return false;
282   }
283 
284   if (details) {
285     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
286                              bytes.get(), details);
287   } else {
288     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
289                              bytes.get());
290   }
291 
292   return false;
293 }
294 
295 /*** PropertyDescriptor operations and DefineProperties *********************/
296 
297 static const char js_getter_str[] = "getter";
298 static const char js_setter_str[] = "setter";
299 
CheckCallable(JSContext * cx,JSObject * obj,const char * fieldName)300 static Result<> CheckCallable(JSContext* cx, JSObject* obj,
301                               const char* fieldName) {
302   if (obj && !obj->isCallable()) {
303     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
304                               JSMSG_BAD_GET_SET_FIELD, fieldName);
305     return cx->alreadyReportedError();
306   }
307   return Ok();
308 }
309 
310 // 6.2.5.5 ToPropertyDescriptor(Obj)
ToPropertyDescriptor(JSContext * cx,HandleValue descval,bool checkAccessors,MutableHandle<PropertyDescriptor> desc_)311 bool js::ToPropertyDescriptor(JSContext* cx, HandleValue descval,
312                               bool checkAccessors,
313                               MutableHandle<PropertyDescriptor> desc_) {
314   // Step 1.
315   RootedObject obj(cx,
316                    RequireObject(cx, JSMSG_OBJECT_REQUIRED_PROP_DESC, descval));
317   if (!obj) {
318     return false;
319   }
320 
321   // Step 2.
322   Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Empty());
323 
324   RootedId id(cx);
325   RootedValue v(cx);
326 
327   // Steps 3-4.
328   id = NameToId(cx->names().enumerable);
329   bool hasEnumerable = false;
330   if (!GetPropertyIfPresent(cx, obj, id, &v, &hasEnumerable)) {
331     return false;
332   }
333   if (hasEnumerable) {
334     desc.setEnumerable(ToBoolean(v));
335   }
336 
337   // Steps 5-6.
338   id = NameToId(cx->names().configurable);
339   bool hasConfigurable = false;
340   if (!GetPropertyIfPresent(cx, obj, id, &v, &hasConfigurable)) {
341     return false;
342   }
343   if (hasConfigurable) {
344     desc.setConfigurable(ToBoolean(v));
345   }
346 
347   // Steps 7-8.
348   id = NameToId(cx->names().value);
349   bool hasValue = false;
350   if (!GetPropertyIfPresent(cx, obj, id, &v, &hasValue)) {
351     return false;
352   }
353   if (hasValue) {
354     desc.setValue(v);
355   }
356 
357   // Steps 9-10.
358   id = NameToId(cx->names().writable);
359   bool hasWritable = false;
360   if (!GetPropertyIfPresent(cx, obj, id, &v, &hasWritable)) {
361     return false;
362   }
363   if (hasWritable) {
364     desc.setWritable(ToBoolean(v));
365   }
366 
367   // Steps 11-12.
368   id = NameToId(cx->names().get);
369   bool hasGet = false;
370   if (!GetPropertyIfPresent(cx, obj, id, &v, &hasGet)) {
371     return false;
372   }
373   RootedObject getter(cx);
374   if (hasGet) {
375     if (v.isObject()) {
376       if (checkAccessors) {
377         JS_TRY_OR_RETURN_FALSE(cx,
378                                CheckCallable(cx, &v.toObject(), js_getter_str));
379       }
380       getter = &v.toObject();
381     } else if (v.isUndefined()) {
382       getter = nullptr;
383     } else {
384       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
385                                 JSMSG_BAD_GET_SET_FIELD, js_getter_str);
386       return false;
387     }
388   }
389 
390   // Steps 13-14.
391   id = NameToId(cx->names().set);
392   bool hasSet = false;
393   if (!GetPropertyIfPresent(cx, obj, id, &v, &hasSet)) {
394     return false;
395   }
396   RootedObject setter(cx);
397   if (hasSet) {
398     if (v.isObject()) {
399       if (checkAccessors) {
400         JS_TRY_OR_RETURN_FALSE(cx,
401                                CheckCallable(cx, &v.toObject(), js_setter_str));
402       }
403       setter = &v.toObject();
404     } else if (v.isUndefined()) {
405       setter = nullptr;
406     } else {
407       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
408                                 JSMSG_BAD_GET_SET_FIELD, js_setter_str);
409       return false;
410     }
411   }
412 
413   // Step 15.
414   if (hasGet || hasSet) {
415     if (hasValue || hasWritable) {
416       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
417                                 JSMSG_INVALID_DESCRIPTOR);
418       return false;
419     }
420 
421     // We delay setGetter/setSetter after the previous check,
422     // because otherwise we would assert.
423     if (hasGet) {
424       desc.setGetter(getter);
425     }
426     if (hasSet) {
427       desc.setSetter(setter);
428     }
429   }
430 
431   desc.assertValid();
432   desc_.set(desc);
433   return true;
434 }
435 
CheckPropertyDescriptorAccessors(JSContext * cx,Handle<PropertyDescriptor> desc)436 Result<> js::CheckPropertyDescriptorAccessors(JSContext* cx,
437                                               Handle<PropertyDescriptor> desc) {
438   if (desc.hasGetter()) {
439     MOZ_TRY(CheckCallable(cx, desc.getter(), js_getter_str));
440   }
441 
442   if (desc.hasSetter()) {
443     MOZ_TRY(CheckCallable(cx, desc.setter(), js_setter_str));
444   }
445 
446   return Ok();
447 }
448 
449 // 6.2.5.6 CompletePropertyDescriptor(Desc)
CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)450 void js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc) {
451   // Step 1.
452   desc.assertValid();
453 
454   // Step 2.
455   // Let like be the Record { [[Value]]: undefined, [[Writable]]: false,
456   //                          [[Get]]: undefined, [[Set]]: undefined,
457   //                          [[Enumerable]]: false, [[Configurable]]: false }.
458 
459   // Step 3.
460   if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
461     // Step 3.a.
462     if (!desc.hasValue()) {
463       desc.setValue(UndefinedHandleValue);
464     }
465     // Step 3.b.
466     if (!desc.hasWritable()) {
467       desc.setWritable(false);
468     }
469   } else {
470     // Step 4.a.
471     if (!desc.hasGetter()) {
472       desc.setGetter(nullptr);
473     }
474     // Step 4.b.
475     if (!desc.hasSetter()) {
476       desc.setSetter(nullptr);
477     }
478   }
479 
480   // Step 5.
481   if (!desc.hasEnumerable()) {
482     desc.setEnumerable(false);
483   }
484 
485   // Step 6.
486   if (!desc.hasConfigurable()) {
487     desc.setConfigurable(false);
488   }
489 
490   desc.assertComplete();
491 }
492 
ReadPropertyDescriptors(JSContext * cx,HandleObject props,bool checkAccessors,MutableHandleIdVector ids,MutableHandle<PropertyDescriptorVector> descs)493 bool js::ReadPropertyDescriptors(
494     JSContext* cx, HandleObject props, bool checkAccessors,
495     MutableHandleIdVector ids, MutableHandle<PropertyDescriptorVector> descs) {
496   if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids)) {
497     return false;
498   }
499 
500   RootedId id(cx);
501   for (size_t i = 0, len = ids.length(); i < len; i++) {
502     id = ids[i];
503     Rooted<PropertyDescriptor> desc(cx);
504     RootedValue v(cx);
505     if (!GetProperty(cx, props, props, id, &v) ||
506         !ToPropertyDescriptor(cx, v, checkAccessors, &desc) ||
507         !descs.append(desc)) {
508       return false;
509     }
510   }
511   return true;
512 }
513 
514 /*** Seal and freeze ********************************************************/
515 
516 /* ES6 draft rev 29 (6 Dec 2014) 7.3.13. */
SetIntegrityLevel(JSContext * cx,HandleObject obj,IntegrityLevel level)517 bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj,
518                            IntegrityLevel level) {
519   cx->check(obj);
520 
521   // Steps 3-5. (Steps 1-2 are redundant assertions.)
522   if (!PreventExtensions(cx, obj)) {
523     return false;
524   }
525 
526   // Steps 6-9, loosely interpreted.
527   if (obj->is<NativeObject>() && !obj->is<TypedArrayObject>() &&
528       !obj->is<MappedArgumentsObject>()) {
529     HandleNativeObject nobj = obj.as<NativeObject>();
530 
531     // Use a fast path to seal/freeze properties. This has the benefit of
532     // creating shared property maps if possible, whereas the slower/generic
533     // implementation below ends up converting non-empty objects to dictionary
534     // mode.
535     if (nobj->shape()->propMapLength() > 0) {
536       if (!NativeObject::freezeOrSealProperties(cx, nobj, level)) {
537         return false;
538       }
539     }
540 
541     // Ordinarily ArraySetLength handles this, but we're going behind its back
542     // right now, so we must do this manually.
543     if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
544       obj->as<ArrayObject>().setNonWritableLength(cx);
545     }
546   } else {
547     // Steps 6-7.
548     RootedIdVector keys(cx);
549     if (!GetPropertyKeys(
550             cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys)) {
551       return false;
552     }
553 
554     RootedId id(cx);
555     Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Empty());
556 
557     // 8.a/9.a. The two different loops are merged here.
558     for (size_t i = 0; i < keys.length(); i++) {
559       id = keys[i];
560 
561       if (level == IntegrityLevel::Sealed) {
562         // 8.a.i.
563         desc.setConfigurable(false);
564       } else {
565         // 9.a.i-ii.
566         Rooted<Maybe<PropertyDescriptor>> currentDesc(cx);
567         if (!GetOwnPropertyDescriptor(cx, obj, id, &currentDesc)) {
568           return false;
569         }
570 
571         // 9.a.iii.
572         if (currentDesc.isNothing()) {
573           continue;
574         }
575 
576         // 9.a.iii.1-2
577         desc = PropertyDescriptor::Empty();
578         if (currentDesc->isAccessorDescriptor()) {
579           desc.setConfigurable(false);
580         } else {
581           desc.setConfigurable(false);
582           desc.setWritable(false);
583         }
584       }
585 
586       // 8.a.i-ii. / 9.a.iii.3-4
587       if (!DefineProperty(cx, obj, id, desc)) {
588         return false;
589       }
590     }
591   }
592 
593   // Finally, freeze or seal the dense elements.
594   if (obj->is<NativeObject>()) {
595     if (!ObjectElements::FreezeOrSeal(cx, obj.as<NativeObject>(), level)) {
596       return false;
597     }
598   }
599 
600   return true;
601 }
602 
ResolveLazyProperties(JSContext * cx,HandleNativeObject obj)603 static bool ResolveLazyProperties(JSContext* cx, HandleNativeObject obj) {
604   const JSClass* clasp = obj->getClass();
605   if (JSEnumerateOp enumerate = clasp->getEnumerate()) {
606     if (!enumerate(cx, obj)) {
607       return false;
608     }
609   }
610   if (clasp->getNewEnumerate() && clasp->getResolve()) {
611     RootedIdVector properties(cx);
612     if (!clasp->getNewEnumerate()(cx, obj, &properties,
613                                   /* enumerableOnly = */ false)) {
614       return false;
615     }
616 
617     RootedId id(cx);
618     for (size_t i = 0; i < properties.length(); i++) {
619       id = properties[i];
620       bool found;
621       if (!HasOwnProperty(cx, obj, id, &found)) {
622         return false;
623       }
624     }
625   }
626   return true;
627 }
628 
629 // ES6 draft rev33 (12 Feb 2015) 7.3.15
TestIntegrityLevel(JSContext * cx,HandleObject obj,IntegrityLevel level,bool * result)630 bool js::TestIntegrityLevel(JSContext* cx, HandleObject obj,
631                             IntegrityLevel level, bool* result) {
632   // Steps 3-6. (Steps 1-2 are redundant assertions.)
633   bool status;
634   if (!IsExtensible(cx, obj, &status)) {
635     return false;
636   }
637   if (status) {
638     *result = false;
639     return true;
640   }
641 
642   // Fast path for native objects.
643   if (obj->is<NativeObject>()) {
644     HandleNativeObject nobj = obj.as<NativeObject>();
645 
646     // Force lazy properties to be resolved.
647     if (!ResolveLazyProperties(cx, nobj)) {
648       return false;
649     }
650 
651     // Typed array elements are configurable, writable properties, so if any
652     // elements are present, the typed array can neither be sealed nor frozen.
653     if (nobj->is<TypedArrayObject>() &&
654         nobj->as<TypedArrayObject>().length() > 0) {
655       *result = false;
656       return true;
657     }
658 
659     bool hasDenseElements = false;
660     for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
661       if (nobj->containsDenseElement(i)) {
662         hasDenseElements = true;
663         break;
664       }
665     }
666 
667     if (hasDenseElements) {
668       // Unless the sealed flag is set, dense elements are configurable.
669       if (!nobj->denseElementsAreSealed()) {
670         *result = false;
671         return true;
672       }
673 
674       // Unless the frozen flag is set, dense elements are writable.
675       if (level == IntegrityLevel::Frozen && !nobj->denseElementsAreFrozen()) {
676         *result = false;
677         return true;
678       }
679     }
680 
681     // Steps 7-9.
682     for (ShapePropertyIter<NoGC> iter(nobj->shape()); !iter.done(); iter++) {
683       // Steps 9.c.i-ii.
684       if (iter->configurable() ||
685           (level == IntegrityLevel::Frozen && iter->isDataDescriptor() &&
686            iter->writable())) {
687         *result = false;
688         return true;
689       }
690     }
691   } else {
692     // Steps 7-8.
693     RootedIdVector props(cx);
694     if (!GetPropertyKeys(
695             cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props)) {
696       return false;
697     }
698 
699     // Step 9.
700     RootedId id(cx);
701     Rooted<Maybe<PropertyDescriptor>> desc(cx);
702     for (size_t i = 0, len = props.length(); i < len; i++) {
703       id = props[i];
704 
705       // Steps 9.a-b.
706       if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
707         return false;
708       }
709 
710       // Step 9.c.
711       if (desc.isNothing()) {
712         continue;
713       }
714 
715       // Steps 9.c.i-ii.
716       if (desc->configurable() ||
717           (level == IntegrityLevel::Frozen && desc->isDataDescriptor() &&
718            desc->writable())) {
719         *result = false;
720         return true;
721       }
722     }
723   }
724 
725   // Step 10.
726   *result = true;
727   return true;
728 }
729 
730 /* * */
731 
NewObject(JSContext * cx,Handle<TaggedProto> proto,const JSClass * clasp,gc::AllocKind kind,NewObjectKind newKind,ObjectFlags objectFlags={})732 static inline JSObject* NewObject(JSContext* cx, Handle<TaggedProto> proto,
733                                   const JSClass* clasp, gc::AllocKind kind,
734                                   NewObjectKind newKind,
735                                   ObjectFlags objectFlags = {}) {
736   MOZ_ASSERT(clasp != &ArrayObject::class_);
737   MOZ_ASSERT_IF(clasp == &JSFunction::class_,
738                 kind == gc::AllocKind::FUNCTION ||
739                     kind == gc::AllocKind::FUNCTION_EXTENDED);
740 
741   // For objects which can have fixed data following the object, only use
742   // enough fixed slots to cover the number of reserved slots in the object,
743   // regardless of the allocation kind specified.
744   size_t nfixed = ClassCanHaveFixedData(clasp)
745                       ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
746                       : GetGCKindSlots(kind, clasp);
747 
748   RootedShape shape(
749       cx, SharedShape::getInitialShape(cx, clasp, cx->realm(), proto, nfixed,
750                                        objectFlags));
751   if (!shape) {
752     return nullptr;
753   }
754 
755   gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
756 
757   JSObject* obj;
758   if (clasp->isJSFunction()) {
759     JS_TRY_VAR_OR_RETURN_NULL(cx, obj,
760                               JSFunction::create(cx, kind, heap, shape));
761   } else if (MOZ_LIKELY(clasp->isNativeObject())) {
762     JS_TRY_VAR_OR_RETURN_NULL(cx, obj,
763                               NativeObject::create(cx, kind, heap, shape));
764   } else {
765     MOZ_ASSERT(IsTypedObjectClass(clasp));
766     JS_TRY_VAR_OR_RETURN_NULL(cx, obj,
767                               TypedObject::create(cx, kind, heap, shape));
768   }
769 
770   probes::CreateObject(cx, obj);
771   return obj;
772 }
773 
fillProto(EntryIndex entry,const JSClass * clasp,js::TaggedProto proto,gc::AllocKind kind,NativeObject * obj)774 void NewObjectCache::fillProto(EntryIndex entry, const JSClass* clasp,
775                                js::TaggedProto proto, gc::AllocKind kind,
776                                NativeObject* obj) {
777   MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
778   MOZ_ASSERT(obj->taggedProto() == proto);
779   return fill(entry, clasp, proto.raw(), kind, obj);
780 }
781 
NewObjectWithTaggedProtoIsCachable(JSContext * cx,Handle<TaggedProto> proto,NewObjectKind newKind,const JSClass * clasp)782 bool js::NewObjectWithTaggedProtoIsCachable(JSContext* cx,
783                                             Handle<TaggedProto> proto,
784                                             NewObjectKind newKind,
785                                             const JSClass* clasp) {
786   return !cx->isHelperThreadContext() && proto.isObject() &&
787          newKind == GenericObject && clasp->isNativeObject() &&
788          !proto.toObject()->is<GlobalObject>();
789 }
790 
NewObjectWithGivenTaggedProto(JSContext * cx,const JSClass * clasp,Handle<TaggedProto> proto,gc::AllocKind allocKind,NewObjectKind newKind,ObjectFlags objectFlags)791 JSObject* js::NewObjectWithGivenTaggedProto(JSContext* cx, const JSClass* clasp,
792                                             Handle<TaggedProto> proto,
793                                             gc::AllocKind allocKind,
794                                             NewObjectKind newKind,
795                                             ObjectFlags objectFlags) {
796   if (CanChangeToBackgroundAllocKind(allocKind, clasp)) {
797     allocKind = ForegroundToBackgroundAllocKind(allocKind);
798   }
799 
800   bool isCachable =
801       NewObjectWithTaggedProtoIsCachable(cx, proto, newKind, clasp);
802   if (isCachable) {
803     NewObjectCache& cache = cx->caches().newObjectCache;
804     NewObjectCache::EntryIndex entry = -1;
805     if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
806       JSObject* obj =
807           cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
808       if (obj) {
809         return obj;
810       }
811     }
812   }
813 
814   RootedObject obj(
815       cx, NewObject(cx, proto, clasp, allocKind, newKind, objectFlags));
816   if (!obj) {
817     return nullptr;
818   }
819 
820   if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
821     NewObjectCache& cache = cx->caches().newObjectCache;
822     NewObjectCache::EntryIndex entry = -1;
823     cache.lookupProto(clasp, proto.toObject(), allocKind, &entry);
824     cache.fillProto(entry, clasp, proto, allocKind, &obj->as<NativeObject>());
825   }
826 
827   return obj;
828 }
829 
NewObjectIsCachable(JSContext * cx,NewObjectKind newKind,const JSClass * clasp)830 static bool NewObjectIsCachable(JSContext* cx, NewObjectKind newKind,
831                                 const JSClass* clasp) {
832   return !cx->isHelperThreadContext() && newKind == GenericObject &&
833          clasp->isNativeObject();
834 }
835 
NewObjectWithClassProto(JSContext * cx,const JSClass * clasp,HandleObject protoArg,gc::AllocKind allocKind,NewObjectKind newKind)836 JSObject* js::NewObjectWithClassProto(JSContext* cx, const JSClass* clasp,
837                                       HandleObject protoArg,
838                                       gc::AllocKind allocKind,
839                                       NewObjectKind newKind) {
840   if (protoArg) {
841     return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(protoArg),
842                                          allocKind, newKind);
843   }
844 
845   if (CanChangeToBackgroundAllocKind(allocKind, clasp)) {
846     allocKind = ForegroundToBackgroundAllocKind(allocKind);
847   }
848 
849   Handle<GlobalObject*> global = cx->global();
850 
851   bool isCachable = NewObjectIsCachable(cx, newKind, clasp);
852   if (isCachable) {
853     NewObjectCache& cache = cx->caches().newObjectCache;
854     NewObjectCache::EntryIndex entry = -1;
855     if (cache.lookupGlobal(clasp, global, allocKind, &entry)) {
856       gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
857       JSObject* obj = cache.newObjectFromHit(cx, entry, heap);
858       if (obj) {
859         return obj;
860       }
861     }
862   }
863 
864   // Find the appropriate proto for clasp. Built-in classes have a cached
865   // proto on cx->global(); all others get %ObjectPrototype%.
866   JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
867   if (protoKey == JSProto_Null) {
868     protoKey = JSProto_Object;
869   }
870 
871   JSObject* proto = GlobalObject::getOrCreatePrototype(cx, protoKey);
872   if (!proto) {
873     return nullptr;
874   }
875 
876   Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
877   JSObject* obj = NewObject(cx, taggedProto, clasp, allocKind, newKind);
878   if (!obj) {
879     return nullptr;
880   }
881 
882   if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
883     NewObjectCache& cache = cx->caches().newObjectCache;
884     NewObjectCache::EntryIndex entry = -1;
885     cache.lookupGlobal(clasp, global, allocKind, &entry);
886     cache.fillGlobal(entry, clasp, global, allocKind, &obj->as<NativeObject>());
887   }
888 
889   return obj;
890 }
891 
NewObjectScriptedCall(JSContext * cx,MutableHandleObject pobj)892 bool js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj) {
893   gc::AllocKind allocKind = NewObjectGCKind();
894   NewObjectKind newKind = GenericObject;
895 
896   JSObject* obj = NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind);
897   if (!obj) {
898     return false;
899   }
900 
901   pobj.set(obj);
902   return true;
903 }
904 
CreateThis(JSContext * cx,const JSClass * newclasp,HandleObject callee)905 JSObject* js::CreateThis(JSContext* cx, const JSClass* newclasp,
906                          HandleObject callee) {
907   RootedObject proto(cx);
908   if (!GetPrototypeFromConstructor(
909           cx, callee, JSCLASS_CACHED_PROTO_KEY(newclasp), &proto)) {
910     return nullptr;
911   }
912   gc::AllocKind kind = NewObjectGCKind();
913   return NewObjectWithClassProto(cx, newclasp, proto, kind);
914 }
915 
GetPrototypeFromConstructor(JSContext * cx,HandleObject newTarget,JSProtoKey intrinsicDefaultProto,MutableHandleObject proto)916 bool js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget,
917                                      JSProtoKey intrinsicDefaultProto,
918                                      MutableHandleObject proto) {
919   RootedValue protov(cx);
920   if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) {
921     return false;
922   }
923   if (protov.isObject()) {
924     proto.set(&protov.toObject());
925   } else if (newTarget->is<JSFunction>() &&
926              newTarget->as<JSFunction>().realm() == cx->realm()) {
927     // Steps 4.a-b fetch the builtin prototype of the current realm, which we
928     // represent as nullptr.
929     proto.set(nullptr);
930   } else if (intrinsicDefaultProto == JSProto_Null) {
931     // Bug 1317416. The caller did not pass a reasonable JSProtoKey, so let the
932     // caller select a prototype object. Most likely they will choose one from
933     // the wrong realm.
934     proto.set(nullptr);
935   } else {
936     // Step 4.a: Let realm be ? GetFunctionRealm(constructor);
937     Realm* realm = JS::GetFunctionRealm(cx, newTarget);
938     if (!realm) {
939       return false;
940     }
941 
942     // Step 4.b: Set proto to realm's intrinsic object named
943     //           intrinsicDefaultProto.
944     {
945       Maybe<AutoRealm> ar;
946       if (cx->realm() != realm) {
947         ar.emplace(cx, realm->maybeGlobal());
948       }
949       proto.set(GlobalObject::getOrCreatePrototype(cx, intrinsicDefaultProto));
950     }
951     if (!proto) {
952       return false;
953     }
954     if (!cx->compartment()->wrap(cx, proto)) {
955       return false;
956     }
957   }
958   return true;
959 }
960 
961 /* static */
nonNativeSetProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result)962 bool JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj,
963                                     HandleId id, HandleValue v,
964                                     HandleValue receiver,
965                                     ObjectOpResult& result) {
966   return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result);
967 }
968 
969 /* static */
nonNativeSetElement(JSContext * cx,HandleObject obj,uint32_t index,HandleValue v,HandleValue receiver,ObjectOpResult & result)970 bool JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj,
971                                    uint32_t index, HandleValue v,
972                                    HandleValue receiver,
973                                    ObjectOpResult& result) {
974   RootedId id(cx);
975   if (!IndexToId(cx, index, &id)) {
976     return false;
977   }
978   return nonNativeSetProperty(cx, obj, id, v, receiver, result);
979 }
980 
CopyPropertyFrom(JSContext * cx,HandleId id,HandleObject target,HandleObject obj)981 static bool CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target,
982                              HandleObject obj) {
983   // |target| must not be a CCW because we need to enter its realm below and
984   // CCWs are not associated with a single realm.
985   MOZ_ASSERT(!IsCrossCompartmentWrapper(target));
986 
987   // |obj| and |cx| are generally not same-compartment with |target| here.
988   cx->check(obj, id);
989   Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
990 
991   if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
992     return false;
993   }
994   MOZ_ASSERT(desc.isSome());
995 
996   JSAutoRealm ar(cx, target);
997   cx->markId(id);
998   RootedId wrappedId(cx, id);
999   if (!cx->compartment()->wrap(cx, &desc)) {
1000     return false;
1001   }
1002 
1003   Rooted<PropertyDescriptor> desc_(cx, *desc);
1004   return DefineProperty(cx, target, wrappedId, desc_);
1005 }
1006 
JS_CopyOwnPropertiesAndPrivateFields(JSContext * cx,HandleObject target,HandleObject obj)1007 JS_PUBLIC_API bool JS_CopyOwnPropertiesAndPrivateFields(JSContext* cx,
1008                                                         HandleObject target,
1009                                                         HandleObject obj) {
1010   // Both |obj| and |target| must not be CCWs because we need to enter their
1011   // realms below and CCWs are not associated with a single realm.
1012   MOZ_ASSERT(!IsCrossCompartmentWrapper(obj));
1013   MOZ_ASSERT(!IsCrossCompartmentWrapper(target));
1014 
1015   JSAutoRealm ar(cx, obj);
1016 
1017   RootedIdVector props(cx);
1018   if (!GetPropertyKeys(
1019           cx, obj,
1020           JSITER_PRIVATE | JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1021           &props)) {
1022     return false;
1023   }
1024 
1025   for (size_t i = 0; i < props.length(); ++i) {
1026     if (!CopyPropertyFrom(cx, props[i], target, obj)) {
1027       return false;
1028     }
1029   }
1030 
1031   return true;
1032 }
1033 
GetScriptArrayObjectElements(HandleArrayObject arr,MutableHandle<GCVector<Value>> values)1034 static bool GetScriptArrayObjectElements(
1035     HandleArrayObject arr, MutableHandle<GCVector<Value>> values) {
1036   MOZ_ASSERT(!arr->isIndexed());
1037 
1038   size_t length = arr->length();
1039   if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length)) {
1040     return false;
1041   }
1042 
1043   size_t initlen = arr->getDenseInitializedLength();
1044   for (size_t i = 0; i < initlen; i++) {
1045     values[i].set(arr->getDenseElement(i));
1046   }
1047 
1048   return true;
1049 }
1050 
GetScriptPlainObjectProperties(HandleObject obj,MutableHandle<IdValueVector> properties)1051 static bool GetScriptPlainObjectProperties(
1052     HandleObject obj, MutableHandle<IdValueVector> properties) {
1053   MOZ_ASSERT(obj->is<PlainObject>());
1054   PlainObject* nobj = &obj->as<PlainObject>();
1055 
1056   if (!properties.appendN(IdValuePair(), nobj->slotSpan())) {
1057     return false;
1058   }
1059 
1060   for (ShapePropertyIter<NoGC> iter(nobj->shape()); !iter.done(); iter++) {
1061     MOZ_ASSERT(iter->isDataDescriptor());
1062     uint32_t slot = iter->slot();
1063     properties[slot].get().id = iter->key();
1064     properties[slot].get().value = nobj->getSlot(slot);
1065   }
1066 
1067   for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
1068     Value v = nobj->getDenseElement(i);
1069     if (!v.isMagic(JS_ELEMENTS_HOLE) &&
1070         !properties.emplaceBack(INT_TO_JSID(i), v)) {
1071       return false;
1072     }
1073   }
1074 
1075   return true;
1076 }
1077 
DeepCloneValue(JSContext * cx,Value * vp)1078 static bool DeepCloneValue(JSContext* cx, Value* vp) {
1079   if (vp->isObject()) {
1080     RootedObject obj(cx, &vp->toObject());
1081     obj = DeepCloneObjectLiteral(cx, obj);
1082     if (!obj) {
1083       return false;
1084     }
1085     vp->setObject(*obj);
1086   } else {
1087     cx->markAtomValue(*vp);
1088   }
1089   return true;
1090 }
1091 
DeepCloneObjectLiteral(JSContext * cx,HandleObject obj)1092 JSObject* js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj) {
1093   /* NB: Keep this in sync with XDRObjectLiteral. */
1094   MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
1095 
1096   if (obj->is<ArrayObject>()) {
1097     Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
1098     if (!GetScriptArrayObjectElements(obj.as<ArrayObject>(), &values)) {
1099       return nullptr;
1100     }
1101 
1102     // Deep clone any elements.
1103     for (uint32_t i = 0; i < values.length(); ++i) {
1104       if (!DeepCloneValue(cx, values[i].address())) {
1105         return nullptr;
1106       }
1107     }
1108 
1109     return NewDenseCopiedArray(cx, values.length(), values.begin(),
1110                                /* proto = */ nullptr, TenuredObject);
1111   }
1112 
1113   Rooted<IdValueVector> properties(cx, IdValueVector(cx));
1114   if (!GetScriptPlainObjectProperties(obj, &properties)) {
1115     return nullptr;
1116   }
1117 
1118   for (size_t i = 0; i < properties.length(); i++) {
1119     cx->markId(properties[i].get().id);
1120     if (!DeepCloneValue(cx, &properties[i].get().value)) {
1121       return nullptr;
1122     }
1123   }
1124 
1125   return NewPlainObjectWithProperties(cx, properties.begin(),
1126                                       properties.length(), TenuredObject);
1127 }
1128 
InitializePropertiesFromCompatibleNativeObject(JSContext * cx,HandleNativeObject dst,HandleNativeObject src)1129 static bool InitializePropertiesFromCompatibleNativeObject(
1130     JSContext* cx, HandleNativeObject dst, HandleNativeObject src) {
1131   cx->check(src, dst);
1132   MOZ_ASSERT(src->getClass() == dst->getClass());
1133   MOZ_ASSERT(dst->shape()->objectFlags().isEmpty());
1134   MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots());
1135   MOZ_ASSERT(!src->inDictionaryMode());
1136   MOZ_ASSERT(!dst->inDictionaryMode());
1137 
1138   if (!dst->ensureElements(cx, src->getDenseInitializedLength())) {
1139     return false;
1140   }
1141 
1142   uint32_t initialized = src->getDenseInitializedLength();
1143   for (uint32_t i = 0; i < initialized; ++i) {
1144     dst->setDenseInitializedLength(i + 1);
1145     dst->initDenseElement(i, src->getDenseElement(i));
1146   }
1147 
1148   // If there are no properties to copy, we're done.
1149   if (!src->shape()->sharedPropMap()) {
1150     return true;
1151   }
1152 
1153   MOZ_ASSERT(!src->hasPrivate());
1154   RootedShape shape(cx);
1155   if (src->staticPrototype() == dst->staticPrototype()) {
1156     shape = src->shape();
1157   } else {
1158     // We need to generate a new shape for dst that has dst's proto but all
1159     // the property information from src.  Note that we asserted above that
1160     // dst's object flags are empty.
1161     Shape* srcShape = src->shape();
1162     ObjectFlags objFlags;
1163     objFlags = CopyPropMapObjectFlags(objFlags, srcShape->objectFlags());
1164     Rooted<SharedPropMap*> map(cx, srcShape->sharedPropMap());
1165     uint32_t mapLength = srcShape->propMapLength();
1166     shape = SharedShape::getPropMapShape(cx, dst->shape()->base(),
1167                                          dst->numFixedSlots(), map, mapLength,
1168                                          objFlags);
1169     if (!shape) {
1170       return false;
1171     }
1172   }
1173 
1174   size_t span = shape->slotSpan();
1175   if (!dst->setShapeAndUpdateSlots(cx, shape)) {
1176     return false;
1177   }
1178   for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++) {
1179     dst->setSlot(i, src->getSlot(i));
1180   }
1181 
1182   return true;
1183 }
1184 
JS_InitializePropertiesFromCompatibleNativeObject(JSContext * cx,HandleObject dst,HandleObject src)1185 JS_PUBLIC_API bool JS_InitializePropertiesFromCompatibleNativeObject(
1186     JSContext* cx, HandleObject dst, HandleObject src) {
1187   return InitializePropertiesFromCompatibleNativeObject(
1188       cx, dst.as<NativeObject>(), src.as<NativeObject>());
1189 }
1190 
1191 template <XDRMode mode>
XDRObjectLiteral(XDRState<mode> * xdr,MutableHandleObject obj)1192 XDRResult js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj) {
1193   /* NB: Keep this in sync with DeepCloneObjectLiteral. */
1194 
1195   JSContext* cx = xdr->cx();
1196   cx->check(obj);
1197 
1198   // Distinguish between objects and array classes.
1199   uint32_t isArray = 0;
1200   {
1201     if (mode == XDR_ENCODE) {
1202       MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
1203       isArray = obj->is<ArrayObject>() ? 1 : 0;
1204     }
1205 
1206     MOZ_TRY(xdr->codeUint32(&isArray));
1207   }
1208 
1209   RootedValue tmpValue(cx), tmpIdValue(cx);
1210   RootedId tmpId(cx);
1211 
1212   if (isArray) {
1213     Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
1214     if (mode == XDR_ENCODE) {
1215       RootedArrayObject arr(cx, &obj->as<ArrayObject>());
1216       if (!GetScriptArrayObjectElements(arr, &values)) {
1217         return xdr->fail(JS::TranscodeResult::Throw);
1218       }
1219     }
1220 
1221     uint32_t initialized;
1222     if (mode == XDR_ENCODE) {
1223       initialized = values.length();
1224     }
1225     MOZ_TRY(xdr->codeUint32(&initialized));
1226     if (mode == XDR_DECODE &&
1227         !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized)) {
1228       return xdr->fail(JS::TranscodeResult::Throw);
1229     }
1230 
1231     // Recursively copy dense elements.
1232     for (unsigned i = 0; i < initialized; i++) {
1233       MOZ_TRY(XDRScriptConst(xdr, values[i]));
1234     }
1235 
1236     if (mode == XDR_DECODE) {
1237       obj.set(NewDenseCopiedArray(cx, values.length(), values.begin(),
1238                                   /* proto = */ nullptr, TenuredObject));
1239       if (!obj) {
1240         return xdr->fail(JS::TranscodeResult::Throw);
1241       }
1242     }
1243 
1244     return Ok();
1245   }
1246 
1247   // Code the properties in the object.
1248   Rooted<IdValueVector> properties(cx, IdValueVector(cx));
1249   if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(obj, &properties)) {
1250     return xdr->fail(JS::TranscodeResult::Throw);
1251   }
1252 
1253   uint32_t nproperties = properties.length();
1254   MOZ_TRY(xdr->codeUint32(&nproperties));
1255 
1256   if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties)) {
1257     return xdr->fail(JS::TranscodeResult::Throw);
1258   }
1259 
1260   for (size_t i = 0; i < nproperties; i++) {
1261     if (mode == XDR_ENCODE) {
1262       tmpIdValue = IdToValue(properties[i].get().id);
1263       tmpValue = properties[i].get().value;
1264     }
1265 
1266     MOZ_TRY(XDRScriptConst(xdr, &tmpIdValue));
1267     MOZ_TRY(XDRScriptConst(xdr, &tmpValue));
1268 
1269     if (mode == XDR_DECODE) {
1270       if (!PrimitiveValueToId<CanGC>(cx, tmpIdValue, &tmpId)) {
1271         return xdr->fail(JS::TranscodeResult::Throw);
1272       }
1273       properties[i].get().id = tmpId;
1274       properties[i].get().value = tmpValue;
1275     }
1276   }
1277 
1278   if (mode == XDR_DECODE) {
1279     obj.set(NewPlainObjectWithProperties(cx, properties.begin(),
1280                                          properties.length(), TenuredObject));
1281     if (!obj) {
1282       return xdr->fail(JS::TranscodeResult::Throw);
1283     }
1284   }
1285 
1286   return Ok();
1287 }
1288 
1289 template XDRResult js::XDRObjectLiteral(XDRState<XDR_ENCODE>* xdr,
1290                                         MutableHandleObject obj);
1291 
1292 template XDRResult js::XDRObjectLiteral(XDRState<XDR_DECODE>* xdr,
1293                                         MutableHandleObject obj);
1294 
1295 /* static */
fillInAfterSwap(JSContext * cx,HandleNativeObject obj,NativeObject * old,HandleValueVector values,void * priv)1296 bool NativeObject::fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
1297                                    NativeObject* old, HandleValueVector values,
1298                                    void* priv) {
1299   // This object has just been swapped with some other object, and its shape
1300   // no longer reflects its allocated size. Correct this information and
1301   // fill the slots in with the specified values.
1302   MOZ_ASSERT(obj->slotSpan() == values.length());
1303   MOZ_ASSERT(!IsInsideNursery(obj));
1304 
1305   // Make sure the shape's numFixedSlots() is correct.
1306   size_t nfixed =
1307       gc::GetGCKindSlots(obj->asTenured().getAllocKind(), obj->getClass());
1308   if (nfixed != obj->shape()->numFixedSlots()) {
1309     if (!NativeObject::changeNumFixedSlotsAfterSwap(cx, obj, nfixed)) {
1310       return false;
1311     }
1312     MOZ_ASSERT(obj->shape()->numFixedSlots() == nfixed);
1313   }
1314 
1315   if (obj->hasPrivate()) {
1316     obj->setPrivate(priv);
1317   } else {
1318     MOZ_ASSERT(!priv);
1319   }
1320 
1321   uint32_t oldDictionarySlotSpan =
1322       obj->inDictionaryMode() ? obj->dictionaryModeSlotSpan() : 0;
1323 
1324   Zone* zone = obj->zone();
1325   if (obj->hasDynamicSlots()) {
1326     ObjectSlots* slotsHeader = obj->getSlotsHeader();
1327     size_t size = ObjectSlots::allocSize(slotsHeader->capacity());
1328     zone->removeCellMemory(old, size, MemoryUse::ObjectSlots);
1329     js_free(slotsHeader);
1330     obj->setEmptyDynamicSlots(0);
1331   }
1332 
1333   size_t ndynamic =
1334       calculateDynamicSlots(nfixed, values.length(), obj->getClass());
1335   size_t currentSlots = obj->getSlotsHeader()->capacity();
1336   MOZ_ASSERT(ndynamic >= currentSlots);
1337   if (ndynamic > currentSlots) {
1338     if (!obj->growSlots(cx, currentSlots, ndynamic)) {
1339       return false;
1340     }
1341   }
1342 
1343   if (obj->inDictionaryMode()) {
1344     obj->setDictionaryModeSlotSpan(oldDictionarySlotSpan);
1345   }
1346 
1347   obj->initSlots(values.begin(), values.length());
1348 
1349   return true;
1350 }
1351 
ObjectMayBeSwapped(const JSObject * obj)1352 bool js::ObjectMayBeSwapped(const JSObject* obj) {
1353   const JSClass* clasp = obj->getClass();
1354 
1355   // We want to optimize Window/globals and Gecko doesn't require transplanting
1356   // them (only the WindowProxy around them). A Window may be a DOMClass, so we
1357   // explicitly check if this is a global.
1358   if (clasp->isGlobal()) {
1359     return false;
1360   }
1361 
1362   // WindowProxy, Wrapper, DeadProxyObject, DOMProxy, and DOMClass (non-global)
1363   // types may be swapped. It is hard to detect DOMProxy from shell, so target
1364   // proxies in general.
1365   return clasp->isProxyObject() || clasp->isDOMClass();
1366 }
1367 
CopyProxyValuesBeforeSwap(JSContext * cx,ProxyObject * proxy,MutableHandleValueVector values)1368 [[nodiscard]] static bool CopyProxyValuesBeforeSwap(
1369     JSContext* cx, ProxyObject* proxy, MutableHandleValueVector values) {
1370   MOZ_ASSERT(values.empty());
1371 
1372   // Remove the GCPtrValues we're about to swap from the store buffer, to
1373   // ensure we don't trace bogus values.
1374   gc::StoreBuffer& sb = cx->runtime()->gc.storeBuffer();
1375 
1376   // Reserve space for the expando, private slot and the reserved slots.
1377   if (!values.reserve(2 + proxy->numReservedSlots())) {
1378     return false;
1379   }
1380 
1381   js::detail::ProxyValueArray* valArray =
1382       js::detail::GetProxyDataLayout(proxy)->values();
1383   sb.unputValue(&valArray->expandoSlot);
1384   sb.unputValue(&valArray->privateSlot);
1385   values.infallibleAppend(valArray->expandoSlot);
1386   values.infallibleAppend(valArray->privateSlot);
1387 
1388   for (size_t i = 0; i < proxy->numReservedSlots(); i++) {
1389     sb.unputValue(&valArray->reservedSlots.slots[i]);
1390     values.infallibleAppend(valArray->reservedSlots.slots[i]);
1391   }
1392 
1393   return true;
1394 }
1395 
initExternalValueArrayAfterSwap(JSContext * cx,const HandleValueVector values)1396 bool ProxyObject::initExternalValueArrayAfterSwap(
1397     JSContext* cx, const HandleValueVector values) {
1398   MOZ_ASSERT(getClass()->isProxyObject());
1399 
1400   size_t nreserved = numReservedSlots();
1401 
1402   // |values| contains the expando slot, private slot and the reserved slots.
1403   MOZ_ASSERT(values.length() == 2 + nreserved);
1404 
1405   size_t nbytes = js::detail::ProxyValueArray::sizeOf(nreserved);
1406 
1407   auto* valArray = reinterpret_cast<js::detail::ProxyValueArray*>(
1408       cx->zone()->pod_malloc<uint8_t>(nbytes));
1409   if (!valArray) {
1410     return false;
1411   }
1412 
1413   valArray->expandoSlot = values[0];
1414   valArray->privateSlot = values[1];
1415 
1416   for (size_t i = 0; i < nreserved; i++) {
1417     valArray->reservedSlots.slots[i] = values[i + 2];
1418   }
1419 
1420   // Note: we allocate external slots iff the proxy had an inline
1421   // ProxyValueArray, so at this point reservedSlots points into the
1422   // old object and we don't have to free anything.
1423   data.reservedSlots = &valArray->reservedSlots;
1424   return true;
1425 }
1426 
1427 /* Use this method with extreme caution. It trades the guts of two objects. */
swap(JSContext * cx,HandleObject a,HandleObject b,AutoEnterOOMUnsafeRegion & oomUnsafe)1428 void JSObject::swap(JSContext* cx, HandleObject a, HandleObject b,
1429                     AutoEnterOOMUnsafeRegion& oomUnsafe) {
1430   // Ensure swap doesn't cause a finalizer to not be run.
1431   MOZ_ASSERT(IsBackgroundFinalized(a->asTenured().getAllocKind()) ==
1432              IsBackgroundFinalized(b->asTenured().getAllocKind()));
1433   MOZ_ASSERT(a->compartment() == b->compartment());
1434 
1435   // You must have entered the objects' compartment before calling this.
1436   MOZ_ASSERT(cx->compartment() == a->compartment());
1437 
1438   // Only certain types of objects are allowed to be swapped. This allows the
1439   // JITs to better optimize objects that can never swap.
1440   MOZ_RELEASE_ASSERT(js::ObjectMayBeSwapped(a));
1441   MOZ_RELEASE_ASSERT(js::ObjectMayBeSwapped(b));
1442 
1443   /*
1444    * Neither object may be in the nursery, but ensure we update any embedded
1445    * nursery pointers in either object.
1446    */
1447   MOZ_ASSERT(!IsInsideNursery(a) && !IsInsideNursery(b));
1448   gc::StoreBuffer& storeBuffer = cx->runtime()->gc.storeBuffer();
1449   storeBuffer.putWholeCell(a);
1450   storeBuffer.putWholeCell(b);
1451   if (a->zone()->wasGCStarted() || b->zone()->wasGCStarted()) {
1452     storeBuffer.setMayHavePointersToDeadCells();
1453   }
1454 
1455   unsigned r = NotifyGCPreSwap(a, b);
1456 
1457   // Do the fundamental swapping of the contents of two objects.
1458   MOZ_ASSERT(a->compartment() == b->compartment());
1459   MOZ_ASSERT(a->is<JSFunction>() == b->is<JSFunction>());
1460 
1461   // Don't try to swap functions with different sizes.
1462   MOZ_ASSERT_IF(a->is<JSFunction>(),
1463                 a->tenuredSizeOfThis() == b->tenuredSizeOfThis());
1464 
1465   // Watch for oddball objects that have special organizational issues and
1466   // can't be swapped.
1467   MOZ_ASSERT(!a->is<RegExpObject>() && !b->is<RegExpObject>());
1468   MOZ_ASSERT(!a->is<ArrayObject>() && !b->is<ArrayObject>());
1469   MOZ_ASSERT(!a->is<ArrayBufferObject>() && !b->is<ArrayBufferObject>());
1470   MOZ_ASSERT(!a->is<TypedArrayObject>() && !b->is<TypedArrayObject>());
1471   MOZ_ASSERT(!a->is<TypedObject>() && !b->is<TypedObject>());
1472 
1473   // Don't swap objects that may currently be participating in shape
1474   // teleporting optimizations.
1475   //
1476   // See: ReshapeForProtoMutation, ReshapeForShadowedProp
1477   MOZ_ASSERT_IF(a->is<NativeObject>() && a->isUsedAsPrototype(),
1478                 a->taggedProto() == TaggedProto());
1479   MOZ_ASSERT_IF(b->is<NativeObject>() && b->isUsedAsPrototype(),
1480                 b->taggedProto() == TaggedProto());
1481 
1482   bool aIsProxyWithInlineValues =
1483       a->is<ProxyObject>() && a->as<ProxyObject>().usingInlineValueArray();
1484   bool bIsProxyWithInlineValues =
1485       b->is<ProxyObject>() && b->as<ProxyObject>().usingInlineValueArray();
1486 
1487   bool aIsUsedAsPrototype = a->isUsedAsPrototype();
1488   bool bIsUsedAsPrototype = b->isUsedAsPrototype();
1489 
1490   // Swap element associations.
1491   Zone* zone = a->zone();
1492   zone->swapCellMemory(a, b, MemoryUse::ObjectElements);
1493 
1494   if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis()) {
1495     // When both objects are the same size, just do a plain swap of their
1496     // contents.
1497 
1498     // Swap slot associations.
1499     zone->swapCellMemory(a, b, MemoryUse::ObjectSlots);
1500 
1501     size_t size = a->tenuredSizeOfThis();
1502 
1503     char tmp[mozilla::tl::Max<sizeof(JSFunction),
1504                               sizeof(JSObject_Slots16)>::value];
1505     MOZ_ASSERT(size <= sizeof(tmp));
1506 
1507     js_memcpy(tmp, a, size);
1508     js_memcpy(a, b, size);
1509     js_memcpy(b, tmp, size);
1510 
1511     if (aIsProxyWithInlineValues) {
1512       b->as<ProxyObject>().setInlineValueArray();
1513     }
1514     if (bIsProxyWithInlineValues) {
1515       a->as<ProxyObject>().setInlineValueArray();
1516     }
1517   } else {
1518     // Avoid GC in here to avoid confusing the tracing code with our
1519     // intermediate state.
1520     gc::AutoSuppressGC suppress(cx);
1521 
1522     // When the objects have different sizes, they will have different
1523     // numbers of fixed slots before and after the swap, so the slots for
1524     // native objects will need to be rearranged.
1525     NativeObject* na = a->is<NativeObject>() ? &a->as<NativeObject>() : nullptr;
1526     NativeObject* nb = b->is<NativeObject>() ? &b->as<NativeObject>() : nullptr;
1527 
1528     // Remember the original values from the objects.
1529     RootedValueVector avals(cx);
1530     void* apriv = nullptr;
1531     if (na) {
1532       apriv = na->hasPrivate() ? na->getPrivate() : nullptr;
1533       for (size_t i = 0; i < na->slotSpan(); i++) {
1534         if (!avals.append(na->getSlot(i))) {
1535           oomUnsafe.crash("JSObject::swap");
1536         }
1537       }
1538     }
1539     RootedValueVector bvals(cx);
1540     void* bpriv = nullptr;
1541     if (nb) {
1542       bpriv = nb->hasPrivate() ? nb->getPrivate() : nullptr;
1543       for (size_t i = 0; i < nb->slotSpan(); i++) {
1544         if (!bvals.append(nb->getSlot(i))) {
1545           oomUnsafe.crash("JSObject::swap");
1546         }
1547       }
1548     }
1549 
1550     // Do the same for proxies storing ProxyValueArray inline.
1551     ProxyObject* proxyA =
1552         a->is<ProxyObject>() ? &a->as<ProxyObject>() : nullptr;
1553     ProxyObject* proxyB =
1554         b->is<ProxyObject>() ? &b->as<ProxyObject>() : nullptr;
1555 
1556     if (aIsProxyWithInlineValues) {
1557       if (!CopyProxyValuesBeforeSwap(cx, proxyA, &avals)) {
1558         oomUnsafe.crash("CopyProxyValuesBeforeSwap");
1559       }
1560     }
1561     if (bIsProxyWithInlineValues) {
1562       if (!CopyProxyValuesBeforeSwap(cx, proxyB, &bvals)) {
1563         oomUnsafe.crash("CopyProxyValuesBeforeSwap");
1564       }
1565     }
1566 
1567     // Swap the main fields of the objects, whether they are native objects or
1568     // proxies.
1569     char tmp[sizeof(JSObject_Slots0)];
1570     js_memcpy(&tmp, a, sizeof tmp);
1571     js_memcpy(a, b, sizeof tmp);
1572     js_memcpy(b, &tmp, sizeof tmp);
1573 
1574     if (na) {
1575       if (!NativeObject::fillInAfterSwap(cx, b.as<NativeObject>(), na, avals,
1576                                          apriv)) {
1577         oomUnsafe.crash("fillInAfterSwap");
1578       }
1579     }
1580     if (nb) {
1581       if (!NativeObject::fillInAfterSwap(cx, a.as<NativeObject>(), nb, bvals,
1582                                          bpriv)) {
1583         oomUnsafe.crash("fillInAfterSwap");
1584       }
1585     }
1586     if (aIsProxyWithInlineValues) {
1587       if (!b->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, avals)) {
1588         oomUnsafe.crash("initExternalValueArray");
1589       }
1590     }
1591     if (bIsProxyWithInlineValues) {
1592       if (!a->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, bvals)) {
1593         oomUnsafe.crash("initExternalValueArray");
1594       }
1595     }
1596   }
1597 
1598   // Preserve the IsUsedAsPrototype flag on the objects.
1599   if (aIsUsedAsPrototype) {
1600     if (!JSObject::setIsUsedAsPrototype(cx, a)) {
1601       oomUnsafe.crash("setIsUsedAsPrototype");
1602     }
1603   }
1604   if (bIsUsedAsPrototype) {
1605     if (!JSObject::setIsUsedAsPrototype(cx, b)) {
1606       oomUnsafe.crash("setIsUsedAsPrototype");
1607     }
1608   }
1609 
1610   /*
1611    * We need a write barrier here. If |a| was marked and |b| was not, then
1612    * after the swap, |b|'s guts would never be marked. The write barrier
1613    * solves this.
1614    *
1615    * Normally write barriers happen before the write. However, that's not
1616    * necessary here because nothing is being destroyed. We're just swapping.
1617    */
1618   PreWriteBarrier(zone, a.get(), [](JSTracer* trc, JSObject* obj) {
1619     obj->traceChildren(trc);
1620   });
1621   PreWriteBarrier(zone, b.get(), [](JSTracer* trc, JSObject* obj) {
1622     obj->traceChildren(trc);
1623   });
1624 
1625   NotifyGCPostSwap(a, b, r);
1626 }
1627 
DefineConstructorAndPrototype(JSContext * cx,HandleObject obj,HandleAtom atom,HandleObject protoProto,const JSClass * clasp,Native constructor,unsigned nargs,const JSPropertySpec * ps,const JSFunctionSpec * fs,const JSPropertySpec * static_ps,const JSFunctionSpec * static_fs,NativeObject ** ctorp)1628 static NativeObject* DefineConstructorAndPrototype(
1629     JSContext* cx, HandleObject obj, HandleAtom atom, HandleObject protoProto,
1630     const JSClass* clasp, Native constructor, unsigned nargs,
1631     const JSPropertySpec* ps, const JSFunctionSpec* fs,
1632     const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
1633     NativeObject** ctorp) {
1634   // Create the prototype object.
1635   RootedNativeObject proto(
1636       cx, GlobalObject::createBlankPrototypeInheriting(cx, clasp, protoProto));
1637   if (!proto) {
1638     return nullptr;
1639   }
1640 
1641   RootedNativeObject ctor(cx);
1642   if (!constructor) {
1643     ctor = proto;
1644   } else {
1645     ctor = NewNativeConstructor(cx, constructor, nargs, atom);
1646     if (!ctor) {
1647       return nullptr;
1648     }
1649 
1650     if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
1651       return nullptr;
1652     }
1653   }
1654 
1655   if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
1656       (ctor != proto &&
1657        !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs))) {
1658     return nullptr;
1659   }
1660 
1661   RootedId id(cx, AtomToId(atom));
1662   RootedValue value(cx, ObjectValue(*ctor));
1663   if (!DefineDataProperty(cx, obj, id, value, 0)) {
1664     return nullptr;
1665   }
1666 
1667   if (ctorp) {
1668     *ctorp = ctor;
1669   }
1670   return proto;
1671 }
1672 
InitClass(JSContext * cx,HandleObject obj,HandleObject protoProto_,const JSClass * clasp,Native constructor,unsigned nargs,const JSPropertySpec * ps,const JSFunctionSpec * fs,const JSPropertySpec * static_ps,const JSFunctionSpec * static_fs,NativeObject ** ctorp)1673 NativeObject* js::InitClass(JSContext* cx, HandleObject obj,
1674                             HandleObject protoProto_, const JSClass* clasp,
1675                             Native constructor, unsigned nargs,
1676                             const JSPropertySpec* ps, const JSFunctionSpec* fs,
1677                             const JSPropertySpec* static_ps,
1678                             const JSFunctionSpec* static_fs,
1679                             NativeObject** ctorp) {
1680   RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
1681   if (!atom) {
1682     return nullptr;
1683   }
1684 
1685   /*
1686    * All instances of the class will inherit properties from the prototype
1687    * object we are about to create (in DefineConstructorAndPrototype), which
1688    * in turn will inherit from protoProto.
1689    *
1690    * If protoProto is null, default to Object.prototype.
1691    */
1692   RootedObject protoProto(cx, protoProto_);
1693   if (!protoProto) {
1694     protoProto = GlobalObject::getOrCreateObjectPrototype(cx, cx->global());
1695     if (!protoProto) {
1696       return nullptr;
1697     }
1698   }
1699 
1700   return DefineConstructorAndPrototype(cx, obj, atom, protoProto, clasp,
1701                                        constructor, nargs, ps, fs, static_ps,
1702                                        static_fs, ctorp);
1703 }
1704 
ReshapeForProtoMutation(JSContext * cx,HandleObject obj)1705 static bool ReshapeForProtoMutation(JSContext* cx, HandleObject obj) {
1706   // To avoid the JIT guarding on each prototype in chain to detect prototype
1707   // mutation, we can instead reshape the rest of the proto chain such that a
1708   // guard on any of them is sufficient. To avoid excessive reshaping and
1709   // invalidation, we apply heuristics to decide when to apply this and when
1710   // to require a guard.
1711   //
1712   // There are two cases:
1713   //
1714   // (1) The object is not marked IsUsedAsPrototype. This is the common case.
1715   //     Because shape implies proto, we rely on the caller changing the
1716   //     object's shape. The JIT guards on this object's shape or prototype so
1717   //     there's nothing we have to do here for objects on the proto chain.
1718   //
1719   // (2) The object is marked IsUsedAsPrototype. This implies the object may be
1720   //     participating in shape teleporting. To invalidate JIT ICs depending on
1721   //     the proto chain being unchanged, set the UncacheableProto shape flag
1722   //     for this object and objects on its proto chain.
1723   //
1724   //     This flag disables future shape teleporting attempts, so next time this
1725   //     happens the loop below will be a no-op.
1726   //
1727   // NOTE: We only handle NativeObjects and don't propagate reshapes through
1728   //       any non-native objects on the chain.
1729   //
1730   // See Also:
1731   //  - GeneratePrototypeGuards
1732   //  - GeneratePrototypeHoleGuards
1733 
1734   if (!obj->isUsedAsPrototype()) {
1735     return true;
1736   }
1737 
1738   RootedObject pobj(cx, obj);
1739 
1740   while (pobj && pobj->is<NativeObject>()) {
1741     if (!pobj->hasUncacheableProto()) {
1742       if (!JSObject::setUncacheableProto(cx, pobj)) {
1743         return false;
1744       }
1745     }
1746     pobj = pobj->staticPrototype();
1747   }
1748 
1749   return true;
1750 }
1751 
SetProto(JSContext * cx,HandleObject obj,Handle<js::TaggedProto> proto)1752 static bool SetProto(JSContext* cx, HandleObject obj,
1753                      Handle<js::TaggedProto> proto) {
1754   // Update prototype shapes if needed to invalidate JIT code that is affected
1755   // by a prototype mutation.
1756   if (!ReshapeForProtoMutation(cx, obj)) {
1757     return false;
1758   }
1759 
1760   if (proto.isObject()) {
1761     RootedObject protoObj(cx, proto.toObject());
1762     if (!JSObject::setIsUsedAsPrototype(cx, protoObj)) {
1763       return false;
1764     }
1765   }
1766 
1767   return JSObject::setProtoUnchecked(cx, obj, proto);
1768 }
1769 
1770 /**
1771  * Returns the original Object.prototype from the embedding-provided incumbent
1772  * global.
1773  *
1774  * Really, we want the incumbent global itself so we can pass it to other
1775  * embedding hooks which need it. Specifically, the enqueue promise hook
1776  * takes an incumbent global so it can set that on the PromiseCallbackJob
1777  * it creates.
1778  *
1779  * The reason for not just returning the global itself is that we'd need to
1780  * wrap it into the current compartment, and later unwrap it. Unwrapping
1781  * globals is tricky, though: we might accidentally unwrap through an inner
1782  * to its outer window and end up with the wrong global. Plain objects don't
1783  * have this problem, so we use the global's Object.prototype. The code using
1784  * it - e.g. EnqueuePromiseReactionJob - can then unwrap the object and get
1785  * its global without fear of unwrapping too far.
1786  */
GetObjectFromIncumbentGlobal(JSContext * cx,MutableHandleObject obj)1787 bool js::GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj) {
1788   Rooted<GlobalObject*> globalObj(cx, cx->runtime()->getIncumbentGlobal(cx));
1789   if (!globalObj) {
1790     obj.set(nullptr);
1791     return true;
1792   }
1793 
1794   {
1795     AutoRealm ar(cx, globalObj);
1796     obj.set(GlobalObject::getOrCreateObjectPrototype(cx, globalObj));
1797     if (!obj) {
1798       return false;
1799     }
1800   }
1801 
1802   // The object might be from a different compartment, so wrap it.
1803   if (obj && !cx->compartment()->wrap(cx, obj)) {
1804     return false;
1805   }
1806 
1807   return true;
1808 }
1809 
IsStandardPrototype(JSObject * obj,JSProtoKey key)1810 static bool IsStandardPrototype(JSObject* obj, JSProtoKey key) {
1811   Value v = obj->nonCCWGlobal().getPrototype(key);
1812   return v.isObject() && obj == &v.toObject();
1813 }
1814 
IdentifyStandardInstance(JSObject * obj)1815 JSProtoKey JS::IdentifyStandardInstance(JSObject* obj) {
1816   // Note: The prototype shares its JSClass with instances.
1817   MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
1818   JSProtoKey key = StandardProtoKeyOrNull(obj);
1819   if (key != JSProto_Null && !IsStandardPrototype(obj, key)) {
1820     return key;
1821   }
1822   return JSProto_Null;
1823 }
1824 
IdentifyStandardPrototype(JSObject * obj)1825 JSProtoKey JS::IdentifyStandardPrototype(JSObject* obj) {
1826   // Note: The prototype shares its JSClass with instances.
1827   MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
1828   JSProtoKey key = StandardProtoKeyOrNull(obj);
1829   if (key != JSProto_Null && IsStandardPrototype(obj, key)) {
1830     return key;
1831   }
1832   return JSProto_Null;
1833 }
1834 
IdentifyStandardInstanceOrPrototype(JSObject * obj)1835 JSProtoKey JS::IdentifyStandardInstanceOrPrototype(JSObject* obj) {
1836   return StandardProtoKeyOrNull(obj);
1837 }
1838 
IdentifyStandardConstructor(JSObject * obj)1839 JSProtoKey JS::IdentifyStandardConstructor(JSObject* obj) {
1840   // Note that isNativeConstructor does not imply that we are a standard
1841   // constructor, but the converse is true (at least until we start having
1842   // self-hosted constructors for standard classes). This lets us avoid a costly
1843   // loop for many functions (which, depending on the call site, may be the
1844   // common case).
1845   if (!obj->is<JSFunction>() ||
1846       !(obj->as<JSFunction>().flags().isNativeConstructor())) {
1847     return JSProto_Null;
1848   }
1849 
1850   GlobalObject& global = obj->as<JSFunction>().global();
1851   for (size_t k = 0; k < JSProto_LIMIT; ++k) {
1852     JSProtoKey key = static_cast<JSProtoKey>(k);
1853     if (global.getConstructor(key) == ObjectValue(*obj)) {
1854       return key;
1855     }
1856   }
1857 
1858   return JSProto_Null;
1859 }
1860 
LookupProperty(JSContext * cx,HandleObject obj,js::HandleId id,MutableHandleObject objp,PropertyResult * propp)1861 bool js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id,
1862                         MutableHandleObject objp, PropertyResult* propp) {
1863   if (LookupPropertyOp op = obj->getOpsLookupProperty()) {
1864     return op(cx, obj, id, objp, propp);
1865   }
1866   return NativeLookupPropertyInline<CanGC>(cx, obj.as<NativeObject>(), id, objp,
1867                                            propp);
1868 }
1869 
LookupName(JSContext * cx,HandlePropertyName name,HandleObject envChain,MutableHandleObject objp,MutableHandleObject pobjp,PropertyResult * propp)1870 bool js::LookupName(JSContext* cx, HandlePropertyName name,
1871                     HandleObject envChain, MutableHandleObject objp,
1872                     MutableHandleObject pobjp, PropertyResult* propp) {
1873   RootedId id(cx, NameToId(name));
1874 
1875   for (RootedObject env(cx, envChain); env; env = env->enclosingEnvironment()) {
1876     if (!LookupProperty(cx, env, id, pobjp, propp)) {
1877       return false;
1878     }
1879     if (propp->isFound()) {
1880       objp.set(env);
1881       return true;
1882     }
1883   }
1884 
1885   objp.set(nullptr);
1886   pobjp.set(nullptr);
1887   propp->setNotFound();
1888   return true;
1889 }
1890 
LookupNameNoGC(JSContext * cx,PropertyName * name,JSObject * envChain,JSObject ** objp,NativeObject ** pobjp,PropertyResult * propp)1891 bool js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* envChain,
1892                         JSObject** objp, NativeObject** pobjp,
1893                         PropertyResult* propp) {
1894   AutoAssertNoPendingException nogc(cx);
1895 
1896   MOZ_ASSERT(!*objp && !*pobjp && propp->isNotFound());
1897 
1898   for (JSObject* env = envChain; env; env = env->enclosingEnvironment()) {
1899     if (env->getOpsLookupProperty()) {
1900       return false;
1901     }
1902     if (!NativeLookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(),
1903                                           NameToId(name), pobjp, propp)) {
1904       return false;
1905     }
1906     if (propp->isFound()) {
1907       *objp = env;
1908       return true;
1909     }
1910   }
1911 
1912   return true;
1913 }
1914 
LookupNameWithGlobalDefault(JSContext * cx,HandlePropertyName name,HandleObject envChain,MutableHandleObject objp)1915 bool js::LookupNameWithGlobalDefault(JSContext* cx, HandlePropertyName name,
1916                                      HandleObject envChain,
1917                                      MutableHandleObject objp) {
1918   RootedId id(cx, NameToId(name));
1919 
1920   RootedObject pobj(cx);
1921   PropertyResult prop;
1922 
1923   RootedObject env(cx, envChain);
1924   for (; !env->is<GlobalObject>(); env = env->enclosingEnvironment()) {
1925     if (!LookupProperty(cx, env, id, &pobj, &prop)) {
1926       return false;
1927     }
1928     if (prop.isFound()) {
1929       break;
1930     }
1931   }
1932 
1933   objp.set(env);
1934   return true;
1935 }
1936 
LookupNameUnqualified(JSContext * cx,HandlePropertyName name,HandleObject envChain,MutableHandleObject objp)1937 bool js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name,
1938                                HandleObject envChain,
1939                                MutableHandleObject objp) {
1940   RootedId id(cx, NameToId(name));
1941 
1942   RootedObject pobj(cx);
1943   PropertyResult prop;
1944 
1945   RootedObject env(cx, envChain);
1946   for (; !env->isUnqualifiedVarObj(); env = env->enclosingEnvironment()) {
1947     if (!LookupProperty(cx, env, id, &pobj, &prop)) {
1948       return false;
1949     }
1950     if (prop.isFound()) {
1951       break;
1952     }
1953   }
1954 
1955   // See note above RuntimeLexicalErrorObject.
1956   if (pobj == env) {
1957     bool isTDZ = false;
1958     if (prop.isFound() && name != cx->names().dotThis) {
1959       // Treat Debugger environments specially for TDZ checks, as they
1960       // look like non-native environments but in fact wrap native
1961       // environments.
1962       if (env->is<DebugEnvironmentProxy>()) {
1963         RootedValue v(cx);
1964         Rooted<DebugEnvironmentProxy*> envProxy(
1965             cx, &env->as<DebugEnvironmentProxy>());
1966         if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id,
1967                                                           &v)) {
1968           return false;
1969         }
1970         isTDZ = IsUninitializedLexical(v);
1971       } else {
1972         isTDZ = IsUninitializedLexicalSlot(env, prop);
1973       }
1974     }
1975 
1976     if (isTDZ) {
1977       env = RuntimeLexicalErrorObject::create(cx, env,
1978                                               JSMSG_UNINITIALIZED_LEXICAL);
1979       if (!env) {
1980         return false;
1981       }
1982     } else if (env->is<LexicalEnvironmentObject>() &&
1983                !prop.propertyInfo().writable()) {
1984       // Assigning to a named lambda callee name is a no-op in sloppy mode.
1985       if (!(env->is<BlockLexicalEnvironmentObject>() &&
1986             env->as<BlockLexicalEnvironmentObject>().scope().kind() ==
1987                 ScopeKind::NamedLambda)) {
1988         MOZ_ASSERT(name != cx->names().dotThis);
1989         env =
1990             RuntimeLexicalErrorObject::create(cx, env, JSMSG_BAD_CONST_ASSIGN);
1991         if (!env) {
1992           return false;
1993         }
1994       }
1995     }
1996   }
1997 
1998   objp.set(env);
1999   return true;
2000 }
2001 
HasOwnProperty(JSContext * cx,HandleObject obj,HandleId id,bool * result)2002 bool js::HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id,
2003                         bool* result) {
2004   if (obj->is<ProxyObject>()) {
2005     return Proxy::hasOwn(cx, obj, id, result);
2006   }
2007 
2008   if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
2009     Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
2010     if (!op(cx, obj, id, &desc)) {
2011       return false;
2012     }
2013     *result = desc.isSome();
2014     return true;
2015   }
2016 
2017   PropertyResult prop;
2018   if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop)) {
2019     return false;
2020   }
2021   *result = prop.isFound();
2022   return true;
2023 }
2024 
LookupPropertyPure(JSContext * cx,JSObject * obj,jsid id,NativeObject ** objp,PropertyResult * propp)2025 bool js::LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id,
2026                             NativeObject** objp, PropertyResult* propp) {
2027   if (obj->getOpsLookupProperty()) {
2028     return false;
2029   }
2030   return NativeLookupPropertyInline<NoGC, LookupResolveMode::CheckMayResolve>(
2031       cx, &obj->as<NativeObject>(), id, objp, propp);
2032 }
2033 
LookupOwnPropertyPure(JSContext * cx,JSObject * obj,jsid id,PropertyResult * propp)2034 bool js::LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id,
2035                                PropertyResult* propp) {
2036   if (obj->getOpsLookupProperty()) {
2037     return false;
2038   }
2039   return NativeLookupOwnPropertyInline<NoGC,
2040                                        LookupResolveMode::CheckMayResolve>(
2041       cx, &obj->as<NativeObject>(), id, propp);
2042 }
2043 
NativeGetPureInline(NativeObject * pobj,jsid id,PropertyResult prop,Value * vp,JSContext * cx)2044 static inline bool NativeGetPureInline(NativeObject* pobj, jsid id,
2045                                        PropertyResult prop, Value* vp,
2046                                        JSContext* cx) {
2047   if (prop.isDenseElement()) {
2048     *vp = pobj->getDenseElement(prop.denseElementIndex());
2049     return true;
2050   }
2051   if (prop.isTypedArrayElement()) {
2052     size_t idx = prop.typedArrayElementIndex();
2053     return pobj->as<TypedArrayObject>().getElement<NoGC>(cx, idx, vp);
2054   }
2055 
2056   // Fail if we have a custom getter.
2057   PropertyInfo propInfo = prop.propertyInfo();
2058   if (!propInfo.isDataProperty()) {
2059     return false;
2060   }
2061 
2062   *vp = pobj->getSlot(propInfo.slot());
2063   MOZ_ASSERT(!vp->isMagic());
2064   return true;
2065 }
2066 
GetPropertyPure(JSContext * cx,JSObject * obj,jsid id,Value * vp)2067 bool js::GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp) {
2068   NativeObject* pobj;
2069   PropertyResult prop;
2070   if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
2071     return false;
2072   }
2073 
2074   if (prop.isNotFound()) {
2075     vp->setUndefined();
2076     return true;
2077   }
2078 
2079   return NativeGetPureInline(pobj, id, prop, vp, cx);
2080 }
2081 
GetOwnPropertyPure(JSContext * cx,JSObject * obj,jsid id,Value * vp,bool * found)2082 bool js::GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp,
2083                             bool* found) {
2084   PropertyResult prop;
2085   if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
2086     return false;
2087   }
2088 
2089   if (prop.isNotFound()) {
2090     *found = false;
2091     vp->setUndefined();
2092     return true;
2093   }
2094 
2095   *found = true;
2096   return obj->is<NativeObject>() &&
2097          NativeGetPureInline(&obj->as<NativeObject>(), id, prop, vp, cx);
2098 }
2099 
NativeGetGetterPureInline(NativeObject * holder,PropertyResult prop,JSFunction ** fp)2100 static inline bool NativeGetGetterPureInline(NativeObject* holder,
2101                                              PropertyResult prop,
2102                                              JSFunction** fp) {
2103   MOZ_ASSERT(prop.isNativeProperty());
2104 
2105   PropertyInfo propInfo = prop.propertyInfo();
2106   if (holder->hasGetter(propInfo)) {
2107     JSObject* getter = holder->getGetter(propInfo);
2108     if (getter->is<JSFunction>()) {
2109       *fp = &getter->as<JSFunction>();
2110       return true;
2111     }
2112   }
2113 
2114   *fp = nullptr;
2115   return true;
2116 }
2117 
GetGetterPure(JSContext * cx,JSObject * obj,jsid id,JSFunction ** fp)2118 bool js::GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp) {
2119   /* Just like GetPropertyPure, but get getter function, without invoking
2120    * it. */
2121   NativeObject* pobj;
2122   PropertyResult prop;
2123   if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) {
2124     return false;
2125   }
2126 
2127   if (prop.isNotFound()) {
2128     *fp = nullptr;
2129     return true;
2130   }
2131 
2132   return prop.isNativeProperty() && NativeGetGetterPureInline(pobj, prop, fp);
2133 }
2134 
GetOwnGetterPure(JSContext * cx,JSObject * obj,jsid id,JSFunction ** fp)2135 bool js::GetOwnGetterPure(JSContext* cx, JSObject* obj, jsid id,
2136                           JSFunction** fp) {
2137   JS::AutoCheckCannotGC nogc;
2138   PropertyResult prop;
2139   if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
2140     return false;
2141   }
2142 
2143   if (prop.isNotFound()) {
2144     *fp = nullptr;
2145     return true;
2146   }
2147 
2148   return prop.isNativeProperty() &&
2149          NativeGetGetterPureInline(&obj->as<NativeObject>(), prop, fp);
2150 }
2151 
GetOwnNativeGetterPure(JSContext * cx,JSObject * obj,jsid id,JSNative * native)2152 bool js::GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id,
2153                                 JSNative* native) {
2154   JS::AutoCheckCannotGC nogc;
2155   *native = nullptr;
2156   PropertyResult prop;
2157   if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
2158     return false;
2159   }
2160 
2161   if (!prop.isNativeProperty()) {
2162     return true;
2163   }
2164 
2165   PropertyInfo propInfo = prop.propertyInfo();
2166 
2167   NativeObject* nobj = &obj->as<NativeObject>();
2168   if (!nobj->hasGetter(propInfo)) {
2169     return true;
2170   }
2171 
2172   JSObject* getterObj = nobj->getGetter(propInfo);
2173   if (!getterObj->is<JSFunction>()) {
2174     return true;
2175   }
2176 
2177   JSFunction* getter = &getterObj->as<JSFunction>();
2178   if (!getter->isNativeFun()) {
2179     return true;
2180   }
2181 
2182   *native = getter->native();
2183   return true;
2184 }
2185 
HasOwnDataPropertyPure(JSContext * cx,JSObject * obj,jsid id,bool * result)2186 bool js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id,
2187                                 bool* result) {
2188   PropertyResult prop;
2189   if (!LookupOwnPropertyPure(cx, obj, id, &prop)) {
2190     return false;
2191   }
2192 
2193   *result = prop.isNativeProperty() && prop.propertyInfo().isDataProperty();
2194   return true;
2195 }
2196 
GetPrototypeIfOrdinary(JSContext * cx,HandleObject obj,bool * isOrdinary,MutableHandleObject protop)2197 bool js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj,
2198                                 bool* isOrdinary, MutableHandleObject protop) {
2199   if (obj->is<js::ProxyObject>()) {
2200     return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop);
2201   }
2202 
2203   *isOrdinary = true;
2204   protop.set(obj->staticPrototype());
2205   return true;
2206 }
2207 
2208 /*** ES6 standard internal methods ******************************************/
2209 
SetPrototype(JSContext * cx,HandleObject obj,HandleObject proto,JS::ObjectOpResult & result)2210 bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto,
2211                       JS::ObjectOpResult& result) {
2212   // The proxy trap subsystem fully handles prototype-setting for proxies
2213   // with dynamic [[Prototype]]s.
2214   if (obj->hasDynamicPrototype()) {
2215     MOZ_ASSERT(obj->is<ProxyObject>());
2216     return Proxy::setPrototype(cx, obj, proto, result);
2217   }
2218 
2219   /*
2220    * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return
2221    * true. Since the values in question are objects, we can just compare
2222    * pointers.
2223    */
2224   if (proto == obj->staticPrototype()) {
2225     return result.succeed();
2226   }
2227 
2228   /* Disallow mutation of immutable [[Prototype]]s. */
2229   if (obj->staticPrototypeIsImmutable()) {
2230     return result.fail(JSMSG_CANT_SET_PROTO);
2231   }
2232 
2233   /*
2234    * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
2235    */
2236   if (obj->is<TypedObject>()) {
2237     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2238                               JSMSG_CANT_SET_PROTO_OF,
2239                               "incompatible TypedObject");
2240     return false;
2241   }
2242 
2243   /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
2244   bool extensible;
2245   if (!IsExtensible(cx, obj, &extensible)) {
2246     return false;
2247   }
2248   if (!extensible) {
2249     return result.fail(JSMSG_CANT_SET_PROTO);
2250   }
2251 
2252   /*
2253    * ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we
2254    * have to do this comparison on the observable WindowProxy, not on the
2255    * possibly-Window object we're setting the proto on.
2256    */
2257   RootedObject objMaybeWindowProxy(cx, ToWindowProxyIfWindow(obj));
2258   RootedObject obj2(cx, proto);
2259   while (obj2) {
2260     MOZ_ASSERT(!IsWindow(obj2));
2261     if (obj2 == objMaybeWindowProxy) {
2262       return result.fail(JSMSG_CANT_SET_PROTO_CYCLE);
2263     }
2264 
2265     bool isOrdinary;
2266     if (!GetPrototypeIfOrdinary(cx, obj2, &isOrdinary, &obj2)) {
2267       return false;
2268     }
2269     if (!isOrdinary) {
2270       break;
2271     }
2272   }
2273 
2274   Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
2275   if (!SetProto(cx, obj, taggedProto)) {
2276     return false;
2277   }
2278 
2279   return result.succeed();
2280 }
2281 
SetPrototype(JSContext * cx,HandleObject obj,HandleObject proto)2282 bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto) {
2283   ObjectOpResult result;
2284   return SetPrototype(cx, obj, proto, result) && result.checkStrict(cx, obj);
2285 }
2286 
PreventExtensions(JSContext * cx,HandleObject obj,ObjectOpResult & result)2287 bool js::PreventExtensions(JSContext* cx, HandleObject obj,
2288                            ObjectOpResult& result) {
2289   if (obj->is<ProxyObject>()) {
2290     return js::Proxy::preventExtensions(cx, obj, result);
2291   }
2292 
2293   if (!obj->nonProxyIsExtensible()) {
2294     // If the following assertion fails, there's somewhere else a missing
2295     // call to shrinkCapacityToInitializedLength() which needs to be found
2296     // and fixed.
2297     MOZ_ASSERT_IF(obj->is<NativeObject>(),
2298                   obj->as<NativeObject>().getDenseInitializedLength() ==
2299                       obj->as<NativeObject>().getDenseCapacity());
2300 
2301     return result.succeed();
2302   }
2303 
2304   if (obj->is<NativeObject>()) {
2305     // Force lazy properties to be resolved.
2306     HandleNativeObject nobj = obj.as<NativeObject>();
2307     if (!ResolveLazyProperties(cx, nobj)) {
2308       return false;
2309     }
2310 
2311     // Prepare the elements. We have to do this before we mark the object
2312     // non-extensible; that's fine because these changes are not observable.
2313     ObjectElements::PrepareForPreventExtensions(cx, nobj);
2314   }
2315 
2316   // Finally, set the NotExtensible flag on the Shape and ObjectElements.
2317   if (!JSObject::setFlag(cx, obj, ObjectFlag::NotExtensible)) {
2318     return false;
2319   }
2320   if (obj->is<NativeObject>()) {
2321     ObjectElements::PreventExtensions(&obj->as<NativeObject>());
2322   }
2323 
2324   return result.succeed();
2325 }
2326 
PreventExtensions(JSContext * cx,HandleObject obj)2327 bool js::PreventExtensions(JSContext* cx, HandleObject obj) {
2328   ObjectOpResult result;
2329   return PreventExtensions(cx, obj, result) && result.checkStrict(cx, obj);
2330 }
2331 
GetOwnPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<Maybe<PropertyDescriptor>> desc)2332 bool js::GetOwnPropertyDescriptor(
2333     JSContext* cx, HandleObject obj, HandleId id,
2334     MutableHandle<Maybe<PropertyDescriptor>> desc) {
2335   if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
2336     bool ok = op(cx, obj, id, desc);
2337     if (ok && desc.isSome()) {
2338       desc->assertComplete();
2339     }
2340     return ok;
2341   }
2342 
2343   return NativeGetOwnPropertyDescriptor(cx, obj.as<NativeObject>(), id, desc);
2344 }
2345 
DefineProperty(JSContext * cx,HandleObject obj,HandleId id,Handle<PropertyDescriptor> desc)2346 bool js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
2347                         Handle<PropertyDescriptor> desc) {
2348   ObjectOpResult result;
2349   return DefineProperty(cx, obj, id, desc, result) &&
2350          result.checkStrict(cx, obj, id);
2351 }
2352 
DefineProperty(JSContext * cx,HandleObject obj,HandleId id,Handle<PropertyDescriptor> desc,ObjectOpResult & result)2353 bool js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
2354                         Handle<PropertyDescriptor> desc,
2355                         ObjectOpResult& result) {
2356   desc.assertValid();
2357   if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
2358     return op(cx, obj, id, desc, result);
2359   }
2360   return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
2361 }
2362 
DefineAccessorProperty(JSContext * cx,HandleObject obj,HandleId id,HandleObject getter,HandleObject setter,unsigned attrs,ObjectOpResult & result)2363 bool js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
2364                                 HandleObject getter, HandleObject setter,
2365                                 unsigned attrs, ObjectOpResult& result) {
2366   Rooted<PropertyDescriptor> desc(
2367       cx, PropertyDescriptor::Accessor(
2368               getter ? mozilla::Some(getter) : mozilla::Nothing(),
2369               setter ? mozilla::Some(setter) : mozilla::Nothing(), attrs));
2370 
2371   if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
2372     MOZ_ASSERT(!cx->isHelperThreadContext());
2373     return op(cx, obj, id, desc, result);
2374   }
2375   return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
2376 }
2377 
DefineDataProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue value,unsigned attrs,ObjectOpResult & result)2378 bool js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id,
2379                             HandleValue value, unsigned attrs,
2380                             ObjectOpResult& result) {
2381   Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Data(value, attrs));
2382   if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
2383     MOZ_ASSERT(!cx->isHelperThreadContext());
2384     return op(cx, obj, id, desc, result);
2385   }
2386   return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
2387 }
2388 
DefineAccessorProperty(JSContext * cx,HandleObject obj,HandleId id,HandleObject getter,HandleObject setter,unsigned attrs)2389 bool js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
2390                                 HandleObject getter, HandleObject setter,
2391                                 unsigned attrs) {
2392   ObjectOpResult result;
2393   if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result)) {
2394     return false;
2395   }
2396   if (!result) {
2397     MOZ_ASSERT(!cx->isHelperThreadContext());
2398     result.reportError(cx, obj, id);
2399     return false;
2400   }
2401   return true;
2402 }
2403 
DefineDataProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue value,unsigned attrs)2404 bool js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id,
2405                             HandleValue value, unsigned attrs) {
2406   ObjectOpResult result;
2407   if (!DefineDataProperty(cx, obj, id, value, attrs, result)) {
2408     return false;
2409   }
2410   if (!result) {
2411     MOZ_ASSERT(!cx->isHelperThreadContext());
2412     result.reportError(cx, obj, id);
2413     return false;
2414   }
2415   return true;
2416 }
2417 
DefineDataProperty(JSContext * cx,HandleObject obj,PropertyName * name,HandleValue value,unsigned attrs)2418 bool js::DefineDataProperty(JSContext* cx, HandleObject obj, PropertyName* name,
2419                             HandleValue value, unsigned attrs) {
2420   RootedId id(cx, NameToId(name));
2421   return DefineDataProperty(cx, obj, id, value, attrs);
2422 }
2423 
DefineDataElement(JSContext * cx,HandleObject obj,uint32_t index,HandleValue value,unsigned attrs)2424 bool js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index,
2425                            HandleValue value, unsigned attrs) {
2426   RootedId id(cx);
2427   if (!IndexToId(cx, index, &id)) {
2428     return false;
2429   }
2430   return DefineDataProperty(cx, obj, id, value, attrs);
2431 }
2432 
2433 /*** SpiderMonkey nonstandard internal methods ******************************/
2434 
2435 // Mark an object as having an immutable prototype
2436 //
2437 // NOTE: This does not correspond to the SetImmutablePrototype ECMAScript
2438 //       method.
SetImmutablePrototype(JSContext * cx,HandleObject obj,bool * succeeded)2439 bool js::SetImmutablePrototype(JSContext* cx, HandleObject obj,
2440                                bool* succeeded) {
2441   if (obj->hasDynamicPrototype()) {
2442     MOZ_ASSERT(!cx->isHelperThreadContext());
2443     return Proxy::setImmutablePrototype(cx, obj, succeeded);
2444   }
2445 
2446   // If this is a global object, resolve the Object class first to ensure the
2447   // global's prototype is set to Object.prototype before we mark the global as
2448   // having an immutable prototype.
2449   if (obj->is<GlobalObject>()) {
2450     Handle<GlobalObject*> global = obj.as<GlobalObject>();
2451     if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object)) {
2452       return false;
2453     }
2454   }
2455 
2456   if (!JSObject::setFlag(cx, obj, ObjectFlag::ImmutablePrototype)) {
2457     return false;
2458   }
2459   *succeeded = true;
2460   return true;
2461 }
2462 
GetPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc,MutableHandleObject holder)2463 bool js::GetPropertyDescriptor(
2464     JSContext* cx, HandleObject obj, HandleId id,
2465     MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc,
2466     MutableHandleObject holder) {
2467   RootedObject pobj(cx);
2468   for (pobj = obj; pobj;) {
2469     if (!GetOwnPropertyDescriptor(cx, pobj, id, desc)) {
2470       return false;
2471     }
2472 
2473     if (desc.isSome()) {
2474       holder.set(pobj);
2475       return true;
2476     }
2477 
2478     if (!GetPrototype(cx, pobj, &pobj)) {
2479       return false;
2480     }
2481   }
2482 
2483   MOZ_ASSERT(desc.isNothing());
2484   holder.set(nullptr);
2485   return true;
2486 }
2487 
2488 /* * */
2489 
2490 extern bool PropertySpecNameToId(JSContext* cx, JSPropertySpec::Name name,
2491                                  MutableHandleId id,
2492                                  js::PinningBehavior pin = js::DoNotPinAtom);
2493 
2494 // If a property or method is part of an experimental feature that can be
2495 // disabled at run-time by a preference, we keep it in the JSFunctionSpec /
2496 // JSPropertySpec list, but omit the definition if the preference is off.
ShouldIgnorePropertyDefinition(JSContext * cx,JSProtoKey key,jsid id)2497 JS_PUBLIC_API bool js::ShouldIgnorePropertyDefinition(JSContext* cx,
2498                                                       JSProtoKey key, jsid id) {
2499   if (!cx->realm()->creationOptions().getToSourceEnabled() &&
2500       (id == NameToId(cx->names().toSource) ||
2501        id == NameToId(cx->names().uneval))) {
2502     return true;
2503   }
2504 
2505   if (key == JSProto_FinalizationRegistry &&
2506       cx->realm()->creationOptions().getWeakRefsEnabled() ==
2507           JS::WeakRefSpecifier::EnabledWithoutCleanupSome &&
2508       id == NameToId(cx->names().cleanupSome)) {
2509     return true;
2510   }
2511 
2512   return false;
2513 }
2514 
DefineFunctionFromSpec(JSContext * cx,HandleObject obj,const JSFunctionSpec * fs,unsigned flags,DefineAsIntrinsic intrinsic)2515 static bool DefineFunctionFromSpec(JSContext* cx, HandleObject obj,
2516                                    const JSFunctionSpec* fs, unsigned flags,
2517                                    DefineAsIntrinsic intrinsic) {
2518   RootedId id(cx);
2519   if (!PropertySpecNameToId(cx, fs->name, &id)) {
2520     return false;
2521   }
2522 
2523   if (ShouldIgnorePropertyDefinition(cx, StandardProtoKeyOrNull(obj), id)) {
2524     return true;
2525   }
2526 
2527   JSFunction* fun = NewFunctionFromSpec(cx, fs, id);
2528   if (!fun) {
2529     return false;
2530   }
2531 
2532   if (intrinsic == AsIntrinsic) {
2533     fun->setIsIntrinsic();
2534   }
2535 
2536   RootedValue funVal(cx, ObjectValue(*fun));
2537   return DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK);
2538 }
2539 
DefineFunctions(JSContext * cx,HandleObject obj,const JSFunctionSpec * fs,DefineAsIntrinsic intrinsic)2540 bool js::DefineFunctions(JSContext* cx, HandleObject obj,
2541                          const JSFunctionSpec* fs,
2542                          DefineAsIntrinsic intrinsic) {
2543   for (; fs->name; fs++) {
2544     if (!DefineFunctionFromSpec(cx, obj, fs, fs->flags, intrinsic)) {
2545       return false;
2546     }
2547   }
2548   return true;
2549 }
2550 
2551 /*** ToPrimitive ************************************************************/
2552 
2553 /*
2554  * Gets |obj[id]|.  If that value's not callable, returns true and stores an
2555  * object value in *vp.  If it's callable, calls it with no arguments and |obj|
2556  * as |this|, returning the result in *vp.
2557  *
2558  * This is a mini-abstraction for ES6 draft rev 36 (2015 Mar 17),
2559  * 7.1.1, second algorithm (OrdinaryToPrimitive), steps 5.a-c.
2560  */
MaybeCallMethod(JSContext * cx,HandleObject obj,HandleId id,MutableHandleValue vp)2561 static bool MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id,
2562                             MutableHandleValue vp) {
2563   if (!GetProperty(cx, obj, obj, id, vp)) {
2564     return false;
2565   }
2566   if (!IsCallable(vp)) {
2567     vp.setObject(*obj);
2568     return true;
2569   }
2570 
2571   return js::Call(cx, vp, obj, vp);
2572 }
2573 
ReportCantConvert(JSContext * cx,unsigned errorNumber,HandleObject obj,JSType hint)2574 static bool ReportCantConvert(JSContext* cx, unsigned errorNumber,
2575                               HandleObject obj, JSType hint) {
2576   const JSClass* clasp = obj->getClass();
2577 
2578   // Avoid recursive death when decompiling in ReportValueError.
2579   RootedString str(cx);
2580   if (hint == JSTYPE_STRING) {
2581     str = JS_AtomizeAndPinString(cx, clasp->name);
2582     if (!str) {
2583       return false;
2584     }
2585   } else {
2586     str = nullptr;
2587   }
2588 
2589   RootedValue val(cx, ObjectValue(*obj));
2590   ReportValueError(cx, errorNumber, JSDVG_SEARCH_STACK, val, str,
2591                    hint == JSTYPE_UNDEFINED ? "primitive type"
2592                    : hint == JSTYPE_STRING  ? "string"
2593                                             : "number");
2594   return false;
2595 }
2596 
OrdinaryToPrimitive(JSContext * cx,HandleObject obj,JSType hint,MutableHandleValue vp)2597 bool JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint,
2598                              MutableHandleValue vp) {
2599   MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING ||
2600              hint == JSTYPE_UNDEFINED);
2601 
2602   Rooted<jsid> id(cx);
2603 
2604   const JSClass* clasp = obj->getClass();
2605   if (hint == JSTYPE_STRING) {
2606     id = NameToId(cx->names().toString);
2607 
2608     /* Optimize (new String(...)).toString(). */
2609     if (clasp == &StringObject::class_) {
2610       StringObject* nobj = &obj->as<StringObject>();
2611       if (HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) {
2612         vp.setString(nobj->unbox());
2613         return true;
2614       }
2615     }
2616 
2617     if (!MaybeCallMethod(cx, obj, id, vp)) {
2618       return false;
2619     }
2620     if (vp.isPrimitive()) {
2621       return true;
2622     }
2623 
2624     id = NameToId(cx->names().valueOf);
2625     if (!MaybeCallMethod(cx, obj, id, vp)) {
2626       return false;
2627     }
2628     if (vp.isPrimitive()) {
2629       return true;
2630     }
2631   } else {
2632     id = NameToId(cx->names().valueOf);
2633 
2634     /* Optimize new String(...).valueOf(). */
2635     if (clasp == &StringObject::class_) {
2636       StringObject* nobj = &obj->as<StringObject>();
2637       if (HasNativeMethodPure(nobj, cx->names().valueOf, str_toString, cx)) {
2638         vp.setString(nobj->unbox());
2639         return true;
2640       }
2641     }
2642 
2643     /* Optimize new Number(...).valueOf(). */
2644     if (clasp == &NumberObject::class_) {
2645       NumberObject* nobj = &obj->as<NumberObject>();
2646       if (HasNativeMethodPure(nobj, cx->names().valueOf, num_valueOf, cx)) {
2647         vp.setNumber(nobj->unbox());
2648         return true;
2649       }
2650     }
2651 
2652     if (!MaybeCallMethod(cx, obj, id, vp)) {
2653       return false;
2654     }
2655     if (vp.isPrimitive()) {
2656       return true;
2657     }
2658 
2659     id = NameToId(cx->names().toString);
2660     if (!MaybeCallMethod(cx, obj, id, vp)) {
2661       return false;
2662     }
2663     if (vp.isPrimitive()) {
2664       return true;
2665     }
2666   }
2667 
2668   return ReportCantConvert(cx, JSMSG_CANT_CONVERT_TO, obj, hint);
2669 }
2670 
ToPrimitiveSlow(JSContext * cx,JSType preferredType,MutableHandleValue vp)2671 bool js::ToPrimitiveSlow(JSContext* cx, JSType preferredType,
2672                          MutableHandleValue vp) {
2673   // Step numbers refer to the first algorithm listed in ES6 draft rev 36
2674   // (2015 Mar 17) 7.1.1 ToPrimitive.
2675   MOZ_ASSERT(preferredType == JSTYPE_UNDEFINED ||
2676              preferredType == JSTYPE_STRING || preferredType == JSTYPE_NUMBER);
2677   RootedObject obj(cx, &vp.toObject());
2678 
2679   // Steps 4-5.
2680   RootedValue method(cx);
2681   if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toPrimitive,
2682                                     &method)) {
2683     return false;
2684   }
2685 
2686   // Step 6.
2687   if (!method.isNullOrUndefined()) {
2688     // Step 6 of GetMethod. js::Call() below would do this check and throw a
2689     // TypeError anyway, but this produces a better error message.
2690     if (!IsCallable(method)) {
2691       return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_NOT_CALLABLE, obj,
2692                                preferredType);
2693     }
2694 
2695     // Steps 1-3, 6.a-b.
2696     RootedValue arg0(
2697         cx,
2698         StringValue(preferredType == JSTYPE_STRING   ? cx->names().string
2699                     : preferredType == JSTYPE_NUMBER ? cx->names().number
2700                                                      : cx->names().default_));
2701 
2702     if (!js::Call(cx, method, vp, arg0, vp)) {
2703       return false;
2704     }
2705 
2706     // Steps 6.c-d.
2707     if (vp.isObject()) {
2708       return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_RETURNED_OBJECT, obj,
2709                                preferredType);
2710     }
2711     return true;
2712   }
2713 
2714   return OrdinaryToPrimitive(cx, obj, preferredType, vp);
2715 }
2716 
2717 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
ToPropertyKeySlow(JSContext * cx,HandleValue argument,MutableHandleId result)2718 bool js::ToPropertyKeySlow(JSContext* cx, HandleValue argument,
2719                            MutableHandleId result) {
2720   MOZ_ASSERT(argument.isObject());
2721 
2722   // Steps 1-2.
2723   RootedValue key(cx, argument);
2724   if (!ToPrimitiveSlow(cx, JSTYPE_STRING, &key)) {
2725     return false;
2726   }
2727 
2728   // Steps 3-4.
2729   return PrimitiveValueToId<CanGC>(cx, key, result);
2730 }
2731 
2732 /* * */
2733 
IsPrototypeOf(JSContext * cx,HandleObject protoObj,JSObject * obj,bool * result)2734 bool js::IsPrototypeOf(JSContext* cx, HandleObject protoObj, JSObject* obj,
2735                        bool* result) {
2736   RootedObject obj2(cx, obj);
2737   for (;;) {
2738     // The [[Prototype]] chain might be cyclic.
2739     if (!CheckForInterrupt(cx)) {
2740       return false;
2741     }
2742     if (!GetPrototype(cx, obj2, &obj2)) {
2743       return false;
2744     }
2745     if (!obj2) {
2746       *result = false;
2747       return true;
2748     }
2749     if (obj2 == protoObj) {
2750       *result = true;
2751       return true;
2752     }
2753   }
2754 }
2755 
PrimitiveToObject(JSContext * cx,const Value & v)2756 JSObject* js::PrimitiveToObject(JSContext* cx, const Value& v) {
2757   MOZ_ASSERT(v.isPrimitive());
2758 
2759   switch (v.type()) {
2760     case ValueType::String: {
2761       Rooted<JSString*> str(cx, v.toString());
2762       return StringObject::create(cx, str);
2763     }
2764     case ValueType::Double:
2765     case ValueType::Int32:
2766       return NumberObject::create(cx, v.toNumber());
2767     case ValueType::Boolean:
2768       return BooleanObject::create(cx, v.toBoolean());
2769     case ValueType::Symbol: {
2770       RootedSymbol symbol(cx, v.toSymbol());
2771       return SymbolObject::create(cx, symbol);
2772     }
2773     case ValueType::BigInt: {
2774       RootedBigInt bigInt(cx, v.toBigInt());
2775       return BigIntObject::create(cx, bigInt);
2776     }
2777     case ValueType::Undefined:
2778     case ValueType::Null:
2779     case ValueType::Magic:
2780     case ValueType::PrivateGCThing:
2781     case ValueType::Object:
2782       break;
2783   }
2784 
2785   MOZ_CRASH("unexpected type");
2786 }
2787 
2788 // Like PrimitiveToObject, but returns the JSProtoKey of the prototype that
2789 // would be used without actually creating the object.
PrimitiveToProtoKey(JSContext * cx,const Value & v)2790 JSProtoKey js::PrimitiveToProtoKey(JSContext* cx, const Value& v) {
2791   MOZ_ASSERT(v.isPrimitive());
2792 
2793   switch (v.type()) {
2794     case ValueType::String:
2795       return JSProto_String;
2796     case ValueType::Double:
2797     case ValueType::Int32:
2798       return JSProto_Number;
2799     case ValueType::Boolean:
2800       return JSProto_Boolean;
2801     case ValueType::Symbol:
2802       return JSProto_Symbol;
2803     case ValueType::BigInt:
2804       return JSProto_BigInt;
2805     case ValueType::Undefined:
2806     case ValueType::Null:
2807     case ValueType::Magic:
2808     case ValueType::PrivateGCThing:
2809     case ValueType::Object:
2810       break;
2811   }
2812 
2813   MOZ_CRASH("unexpected type");
2814 }
2815 
2816 /*
2817  * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
2818  * already be an object, use ToObject. reportScanStack controls how null and
2819  * undefined errors are reported.
2820  *
2821  * Callers must handle the already-object case.
2822  */
ToObjectSlow(JSContext * cx,JS::HandleValue val,bool reportScanStack)2823 JSObject* js::ToObjectSlow(JSContext* cx, JS::HandleValue val,
2824                            bool reportScanStack) {
2825   MOZ_ASSERT(!val.isMagic());
2826   MOZ_ASSERT(!val.isObject());
2827 
2828   if (val.isNullOrUndefined()) {
2829     ReportIsNullOrUndefinedForPropertyAccess(
2830         cx, val, reportScanStack ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK);
2831     return nullptr;
2832   }
2833 
2834   return PrimitiveToObject(cx, val);
2835 }
2836 
ToObjectSlowForPropertyAccess(JSContext * cx,JS::HandleValue val,int valIndex,HandleId key)2837 JSObject* js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
2838                                             int valIndex, HandleId key) {
2839   MOZ_ASSERT(!val.isMagic());
2840   MOZ_ASSERT(!val.isObject());
2841 
2842   if (val.isNullOrUndefined()) {
2843     ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex, key);
2844     return nullptr;
2845   }
2846 
2847   return PrimitiveToObject(cx, val);
2848 }
2849 
ToObjectSlowForPropertyAccess(JSContext * cx,JS::HandleValue val,int valIndex,HandlePropertyName key)2850 JSObject* js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
2851                                             int valIndex,
2852                                             HandlePropertyName key) {
2853   MOZ_ASSERT(!val.isMagic());
2854   MOZ_ASSERT(!val.isObject());
2855 
2856   if (val.isNullOrUndefined()) {
2857     RootedId keyId(cx, NameToId(key));
2858     ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex, keyId);
2859     return nullptr;
2860   }
2861 
2862   return PrimitiveToObject(cx, val);
2863 }
2864 
ToObjectSlowForPropertyAccess(JSContext * cx,JS::HandleValue val,int valIndex,HandleValue keyValue)2865 JSObject* js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
2866                                             int valIndex,
2867                                             HandleValue keyValue) {
2868   MOZ_ASSERT(!val.isMagic());
2869   MOZ_ASSERT(!val.isObject());
2870 
2871   if (val.isNullOrUndefined()) {
2872     RootedId key(cx);
2873     if (keyValue.isPrimitive()) {
2874       if (!PrimitiveValueToId<CanGC>(cx, keyValue, &key)) {
2875         return nullptr;
2876       }
2877       ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex, key);
2878     } else {
2879       ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex);
2880     }
2881     return nullptr;
2882   }
2883 
2884   return PrimitiveToObject(cx, val);
2885 }
2886 
GetThisObject(JSObject * obj)2887 JSObject* js::GetThisObject(JSObject* obj) {
2888   // Use the WindowProxy if the global is a Window, as Window must never be
2889   // exposed to script.
2890   if (obj->is<GlobalObject>()) {
2891     return ToWindowProxyIfWindow(obj);
2892   }
2893 
2894   // We should not expose any environments except NSVOs to script. The NSVO is
2895   // pretending to be the global object in this case.
2896   MOZ_ASSERT(obj->is<NonSyntacticVariablesObject>() ||
2897              !obj->is<EnvironmentObject>());
2898 
2899   return obj;
2900 }
2901 
GetThisObjectOfLexical(JSObject * env)2902 JSObject* js::GetThisObjectOfLexical(JSObject* env) {
2903   return env->as<ExtensibleLexicalEnvironmentObject>().thisObject();
2904 }
2905 
GetThisObjectOfWith(JSObject * env)2906 JSObject* js::GetThisObjectOfWith(JSObject* env) {
2907   MOZ_ASSERT(env->is<WithEnvironmentObject>());
2908   return GetThisObject(env->as<WithEnvironmentObject>().withThis());
2909 }
2910 
2911 class GetObjectSlotNameFunctor : public JS::TracingContext::Functor {
2912   JSObject* obj;
2913 
2914  public:
GetObjectSlotNameFunctor(JSObject * ctx)2915   explicit GetObjectSlotNameFunctor(JSObject* ctx) : obj(ctx) {}
2916   virtual void operator()(JS::TracingContext* trc, char* buf,
2917                           size_t bufsize) override;
2918 };
2919 
operator ()(JS::TracingContext * tcx,char * buf,size_t bufsize)2920 void GetObjectSlotNameFunctor::operator()(JS::TracingContext* tcx, char* buf,
2921                                           size_t bufsize) {
2922   MOZ_ASSERT(tcx->index() != JS::TracingContext::InvalidIndex);
2923 
2924   uint32_t slot = uint32_t(tcx->index());
2925 
2926   Maybe<PropertyKey> key;
2927   if (obj->is<NativeObject>()) {
2928     for (ShapePropertyIter<NoGC> iter(obj->shape()); !iter.done(); iter++) {
2929       if (iter->hasSlot() && iter->slot() == slot) {
2930         key.emplace(iter->key());
2931         break;
2932       }
2933     }
2934   }
2935 
2936   if (key.isNothing()) {
2937     do {
2938       const char* slotname = nullptr;
2939       const char* pattern = nullptr;
2940       if (obj->is<GlobalObject>()) {
2941         pattern = "CLASS_OBJECT(%s)";
2942         if (false) {
2943           ;
2944         }
2945 #define TEST_SLOT_MATCHES_PROTOTYPE(name, clasp) \
2946   else if ((JSProto_##name) == slot) {           \
2947     slotname = js_##name##_str;                  \
2948   }
2949         JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
2950 #undef TEST_SLOT_MATCHES_PROTOTYPE
2951       } else {
2952         pattern = "%s";
2953         if (obj->is<EnvironmentObject>()) {
2954           if (slot == EnvironmentObject::enclosingEnvironmentSlot()) {
2955             slotname = "enclosing_environment";
2956           } else if (obj->is<CallObject>()) {
2957             if (slot == CallObject::calleeSlot()) {
2958               slotname = "callee_slot";
2959             }
2960           } else if (obj->is<WithEnvironmentObject>()) {
2961             if (slot == WithEnvironmentObject::objectSlot()) {
2962               slotname = "with_object";
2963             } else if (slot == WithEnvironmentObject::thisSlot()) {
2964               slotname = "with_this";
2965             }
2966           }
2967         }
2968       }
2969 
2970       if (slotname) {
2971         snprintf(buf, bufsize, pattern, slotname);
2972       } else {
2973         snprintf(buf, bufsize, "**UNKNOWN SLOT %" PRIu32 "**", slot);
2974       }
2975     } while (false);
2976   } else {
2977     if (key->isInt()) {
2978       snprintf(buf, bufsize, "%" PRId32, key->toInt());
2979     } else if (key->isAtom()) {
2980       PutEscapedString(buf, bufsize, key->toAtom(), 0);
2981     } else if (key->isSymbol()) {
2982       snprintf(buf, bufsize, "**SYMBOL KEY**");
2983     } else {
2984       snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
2985     }
2986   }
2987 }
2988 
2989 /*** Debugging routines *****************************************************/
2990 
2991 #if defined(DEBUG) || defined(JS_JITSPEW)
2992 
2993 /*
2994  * Routines to print out values during debugging.  These are FRIEND_API to help
2995  * the debugger find them and to support temporarily hacking js::Dump* calls
2996  * into other code.
2997  */
2998 
dumpValue(const Value & v,js::GenericPrinter & out)2999 static void dumpValue(const Value& v, js::GenericPrinter& out) {
3000   switch (v.type()) {
3001     case ValueType::Null:
3002       out.put("null");
3003       break;
3004     case ValueType::Undefined:
3005       out.put("undefined");
3006       break;
3007     case ValueType::Int32:
3008       out.printf("%d", v.toInt32());
3009       break;
3010     case ValueType::Double:
3011       out.printf("%g", v.toDouble());
3012       break;
3013     case ValueType::String:
3014       v.toString()->dumpNoNewline(out);
3015       break;
3016     case ValueType::Symbol:
3017       v.toSymbol()->dump(out);
3018       break;
3019     case ValueType::BigInt:
3020       v.toBigInt()->dump(out);
3021       break;
3022     case ValueType::Object:
3023       if (v.toObject().is<JSFunction>()) {
3024         JSFunction* fun = &v.toObject().as<JSFunction>();
3025         if (fun->displayAtom()) {
3026           out.put("<function ");
3027           EscapedStringPrinter(out, fun->displayAtom(), 0);
3028         } else {
3029           out.put("<unnamed function");
3030         }
3031         if (fun->hasBaseScript()) {
3032           BaseScript* script = fun->baseScript();
3033           out.printf(" (%s:%u)", script->filename() ? script->filename() : "",
3034                      script->lineno());
3035         }
3036         out.printf(" at %p>", (void*)fun);
3037       } else {
3038         JSObject* obj = &v.toObject();
3039         const JSClass* clasp = obj->getClass();
3040         out.printf("<%s%s at %p>", clasp->name,
3041                    (clasp == &PlainObject::class_) ? "" : " object",
3042                    (void*)obj);
3043       }
3044       break;
3045     case ValueType::Boolean:
3046       if (v.toBoolean()) {
3047         out.put("true");
3048       } else {
3049         out.put("false");
3050       }
3051       break;
3052     case ValueType::Magic:
3053       out.put("<magic");
3054       switch (v.whyMagic()) {
3055         case JS_ELEMENTS_HOLE:
3056           out.put(" elements hole");
3057           break;
3058         case JS_NO_ITER_VALUE:
3059           out.put(" no iter value");
3060           break;
3061         case JS_GENERATOR_CLOSING:
3062           out.put(" generator closing");
3063           break;
3064         case JS_OPTIMIZED_OUT:
3065           out.put(" optimized out");
3066           break;
3067         default:
3068           out.put(" ?!");
3069           break;
3070       }
3071       out.putChar('>');
3072       break;
3073     case ValueType::PrivateGCThing:
3074       out.printf("<PrivateGCThing %p>", v.toGCThing());
3075       break;
3076   }
3077 }
3078 
3079 namespace js {
3080 
3081 // We don't want jsfriendapi.h to depend on GenericPrinter,
3082 // so these functions are declared directly in the cpp.
3083 
3084 JS_PUBLIC_API void DumpValue(const JS::Value& val, js::GenericPrinter& out);
3085 
3086 JS_PUBLIC_API void DumpId(jsid id, js::GenericPrinter& out);
3087 
3088 JS_PUBLIC_API void DumpInterpreterFrame(JSContext* cx, js::GenericPrinter& out,
3089                                         InterpreterFrame* start = nullptr);
3090 
3091 }  // namespace js
3092 
DumpValue(const Value & val,js::GenericPrinter & out)3093 JS_PUBLIC_API void js::DumpValue(const Value& val, js::GenericPrinter& out) {
3094   dumpValue(val, out);
3095   out.putChar('\n');
3096 }
3097 
DumpId(jsid id,js::GenericPrinter & out)3098 JS_PUBLIC_API void js::DumpId(jsid id, js::GenericPrinter& out) {
3099   out.printf("jsid %p = ", (void*)JSID_BITS(id));
3100   dumpValue(IdToValue(id), out);
3101   out.putChar('\n');
3102 }
3103 
DumpProperty(const NativeObject * obj,PropMap * map,uint32_t index,js::GenericPrinter & out)3104 static void DumpProperty(const NativeObject* obj, PropMap* map, uint32_t index,
3105                          js::GenericPrinter& out) {
3106   PropertyInfoWithKey prop = map->getPropertyInfoWithKey(index);
3107   jsid id = prop.key();
3108   if (id.isAtom()) {
3109     id.toAtom()->dumpCharsNoNewline(out);
3110   } else if (id.isInt()) {
3111     out.printf("%d", id.toInt());
3112   } else if (id.isSymbol()) {
3113     id.toSymbol()->dump(out);
3114   } else {
3115     out.printf("id %p", reinterpret_cast<void*>(JSID_BITS(id)));
3116   }
3117 
3118   if (prop.isDataProperty()) {
3119     out.printf(": ");
3120     dumpValue(obj->getSlot(prop.slot()), out);
3121   } else if (prop.isAccessorProperty()) {
3122     out.printf(": getter %p setter %p", obj->getGetter(prop),
3123                obj->getSetter(prop));
3124   }
3125 
3126   out.printf(" (map %p/%u", map, index);
3127 
3128   if (prop.enumerable()) {
3129     out.put(" enumerable");
3130   }
3131   if (prop.configurable()) {
3132     out.put(" configurable");
3133   }
3134   if (prop.isDataDescriptor() && prop.writable()) {
3135     out.put(" writable");
3136   }
3137 
3138   if (prop.isCustomDataProperty()) {
3139     out.printf(" <custom-data-prop>");
3140   }
3141 
3142   if (prop.hasSlot()) {
3143     out.printf(" slot %u", prop.slot());
3144   }
3145 
3146   out.printf(")\n");
3147 }
3148 
hasSameRealmAs(JSContext * cx) const3149 bool JSObject::hasSameRealmAs(JSContext* cx) const {
3150   return nonCCWRealm() == cx->realm();
3151 }
3152 
uninlinedIsProxyObject() const3153 bool JSObject::uninlinedIsProxyObject() const { return is<ProxyObject>(); }
3154 
uninlinedNonProxyIsExtensible() const3155 bool JSObject::uninlinedNonProxyIsExtensible() const {
3156   return nonProxyIsExtensible();
3157 }
3158 
dump(js::GenericPrinter & out) const3159 void JSObject::dump(js::GenericPrinter& out) const {
3160   const JSObject* obj = this;
3161   out.printf("object %p\n", obj);
3162 
3163   if (IsCrossCompartmentWrapper(this)) {
3164     out.printf("  compartment %p\n", compartment());
3165   } else {
3166     JSObject* globalObj = &nonCCWGlobal();
3167     out.printf("  global %p [%s]\n", globalObj, globalObj->getClass()->name);
3168   }
3169 
3170   const JSClass* clasp = obj->getClass();
3171   out.printf("  class %p %s\n", clasp, clasp->name);
3172 
3173   if (IsProxy(obj)) {
3174     auto* handler = GetProxyHandler(obj);
3175     out.printf("    handler %p", handler);
3176     if (IsDeadProxyObject(obj)) {
3177       out.printf(" (DeadObjectProxy)");
3178     } else if (IsCrossCompartmentWrapper(obj)) {
3179       out.printf(" (CCW)");
3180     }
3181     out.putChar('\n');
3182 
3183     Value priv = GetProxyPrivate(obj);
3184     if (!priv.isUndefined()) {
3185       out.printf("    private ");
3186       dumpValue(priv, out);
3187       out.putChar('\n');
3188     }
3189 
3190     Value expando = GetProxyExpando(obj);
3191     if (!expando.isNull()) {
3192       out.printf("    expando ");
3193       dumpValue(expando, out);
3194       out.putChar('\n');
3195     }
3196   }
3197 
3198   const Shape* shape = obj->shape();
3199   out.printf("  shape %p\n", shape);
3200 
3201   out.put("  flags:");
3202   if (obj->isUsedAsPrototype()) {
3203     out.put(" used_as_prototype");
3204   }
3205   if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) {
3206     out.put(" not_extensible");
3207   }
3208   if (obj->maybeHasInterestingSymbolProperty()) {
3209     out.put(" maybe_has_interesting_symbol");
3210   }
3211   if (obj->isBoundFunction()) {
3212     out.put(" bound_function");
3213   }
3214   if (obj->isQualifiedVarObj()) {
3215     out.put(" varobj");
3216   }
3217   if (obj->isUnqualifiedVarObj()) {
3218     out.put(" unqualified_varobj");
3219   }
3220   if (obj->hasUncacheableProto()) {
3221     out.put(" has_uncacheable_proto");
3222   }
3223   if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) {
3224     out.put(" immutable_prototype");
3225   }
3226 
3227   const NativeObject* nobj =
3228       obj->is<NativeObject>() ? &obj->as<NativeObject>() : nullptr;
3229   if (nobj) {
3230     if (nobj->inDictionaryMode()) {
3231       out.put(" inDictionaryMode");
3232     }
3233     if (nobj->hadGetterSetterChange()) {
3234       out.put(" had_getter_setter_change");
3235     }
3236     if (nobj->isIndexed()) {
3237       out.put(" indexed");
3238     }
3239     if (nobj->is<PlainObject>() &&
3240         nobj->as<PlainObject>().hasNonWritableOrAccessorPropExclProto()) {
3241       out.put(" has_non_writable_or_accessor_prop_excl_proto");
3242     }
3243     if (!nobj->denseElementsArePacked()) {
3244       out.put(" non_packed_elements");
3245     }
3246     if (nobj->getElementsHeader()->isNotExtensible()) {
3247       out.put(" not_extensible");
3248     }
3249     if (nobj->getElementsHeader()->isSealed()) {
3250       out.put(" sealed_elements");
3251     }
3252     if (nobj->getElementsHeader()->isFrozen()) {
3253       out.put(" frozen_elements");
3254     }
3255     if (nobj->getElementsHeader()->maybeInIteration()) {
3256       out.put(" elements_maybe_in_iteration");
3257     }
3258   } else {
3259     out.put(" not_native");
3260   }
3261   out.putChar('\n');
3262 
3263   out.put("  proto ");
3264   TaggedProto proto = obj->taggedProto();
3265   if (proto.isDynamic()) {
3266     out.put("<dynamic>");
3267   } else {
3268     dumpValue(ObjectOrNullValue(proto.toObjectOrNull()), out);
3269   }
3270   out.putChar('\n');
3271 
3272   if (nobj) {
3273     if (clasp->flags & JSCLASS_HAS_PRIVATE) {
3274       out.printf("  private %p\n", nobj->getPrivate());
3275     }
3276 
3277     uint32_t reserved = JSCLASS_RESERVED_SLOTS(clasp);
3278     if (reserved) {
3279       out.printf("  reserved slots:\n");
3280       for (uint32_t i = 0; i < reserved; i++) {
3281         out.printf("    %3u ", i);
3282         out.put(": ");
3283         dumpValue(nobj->getSlot(i), out);
3284         out.putChar('\n');
3285       }
3286     }
3287 
3288     out.put("  properties:\n");
3289 
3290     if (PropMap* map = nobj->shape()->propMap()) {
3291       Vector<PropMap*, 8, SystemAllocPolicy> maps;
3292       while (true) {
3293         if (!maps.append(map)) {
3294           out.printf("(OOM while appending maps)\n");
3295           break;
3296         }
3297         if (!map->hasPrevious()) {
3298           break;
3299         }
3300         map = map->asLinked()->previous();
3301       }
3302 
3303       for (size_t i = maps.length(); i > 0; i--) {
3304         size_t index = i - 1;
3305         uint32_t len =
3306             (index == 0) ? nobj->shape()->propMapLength() : PropMap::Capacity;
3307         for (uint32_t j = 0; j < len; j++) {
3308           PropMap* map = maps[index];
3309           if (!map->hasKey(j)) {
3310             MOZ_ASSERT(map->isDictionary());
3311             continue;
3312           }
3313           out.printf("    ");
3314           DumpProperty(nobj, map, j, out);
3315         }
3316       }
3317     }
3318 
3319     uint32_t slots = nobj->getDenseInitializedLength();
3320     if (slots) {
3321       out.put("  elements:\n");
3322       for (uint32_t i = 0; i < slots; i++) {
3323         out.printf("    %3u: ", i);
3324         dumpValue(nobj->getDenseElement(i), out);
3325         out.putChar('\n');
3326       }
3327     }
3328   }
3329 }
3330 
3331 // For debuggers.
dump() const3332 void JSObject::dump() const {
3333   Fprinter out(stderr);
3334   dump(out);
3335 }
3336 
MaybeDumpScope(Scope * scope,js::GenericPrinter & out)3337 static void MaybeDumpScope(Scope* scope, js::GenericPrinter& out) {
3338   if (scope) {
3339     out.printf("  scope: %s\n", ScopeKindString(scope->kind()));
3340     for (BindingIter bi(scope); bi; bi++) {
3341       out.put("    ");
3342       dumpValue(StringValue(bi.name()), out);
3343       out.putChar('\n');
3344     }
3345   }
3346 }
3347 
MaybeDumpValue(const char * name,const Value & v,js::GenericPrinter & out)3348 static void MaybeDumpValue(const char* name, const Value& v,
3349                            js::GenericPrinter& out) {
3350   if (!v.isNull()) {
3351     out.printf("  %s: ", name);
3352     dumpValue(v, out);
3353     out.putChar('\n');
3354   }
3355 }
3356 
DumpInterpreterFrame(JSContext * cx,js::GenericPrinter & out,InterpreterFrame * start)3357 JS_PUBLIC_API void js::DumpInterpreterFrame(JSContext* cx,
3358                                             js::GenericPrinter& out,
3359                                             InterpreterFrame* start) {
3360   /* This should only called during live debugging. */
3361   ScriptFrameIter i(cx);
3362   if (!start) {
3363     if (i.done()) {
3364       out.printf("no stack for cx = %p\n", (void*)cx);
3365       return;
3366     }
3367   } else {
3368     while (!i.done() && !i.isJSJit() && i.interpFrame() != start) {
3369       ++i;
3370     }
3371 
3372     if (i.done()) {
3373       out.printf("fp = %p not found in cx = %p\n", (void*)start, (void*)cx);
3374       return;
3375     }
3376   }
3377 
3378   for (; !i.done(); ++i) {
3379     if (i.isJSJit()) {
3380       out.put("JIT frame\n");
3381     } else {
3382       out.printf("InterpreterFrame at %p\n", (void*)i.interpFrame());
3383     }
3384 
3385     if (i.isFunctionFrame()) {
3386       out.put("callee fun: ");
3387       RootedValue v(cx);
3388       JSObject* fun = i.callee(cx);
3389       v.setObject(*fun);
3390       dumpValue(v, out);
3391     } else {
3392       out.put("global or eval frame, no callee");
3393     }
3394     out.putChar('\n');
3395 
3396     out.printf("file %s line %u\n", i.script()->filename(),
3397                i.script()->lineno());
3398 
3399     if (jsbytecode* pc = i.pc()) {
3400       out.printf("  pc = %p\n", pc);
3401       out.printf("  current op: %s\n", CodeName(JSOp(*pc)));
3402       MaybeDumpScope(i.script()->lookupScope(pc), out);
3403     }
3404     if (i.isFunctionFrame()) {
3405       MaybeDumpValue("this", i.thisArgument(cx), out);
3406     }
3407     if (!i.isJSJit()) {
3408       out.put("  rval: ");
3409       dumpValue(i.interpFrame()->returnValue(), out);
3410       out.putChar('\n');
3411     }
3412 
3413     out.put("  flags:");
3414     if (i.isConstructing()) {
3415       out.put(" constructing");
3416     }
3417     if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame()) {
3418       out.put(" debugger eval");
3419     }
3420     if (i.isEvalFrame()) {
3421       out.put(" eval");
3422     }
3423     out.putChar('\n');
3424 
3425     out.printf("  envChain: (JSObject*) %p\n", (void*)i.environmentChain(cx));
3426 
3427     out.putChar('\n');
3428   }
3429 }
3430 
3431 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */
3432 
3433 namespace js {
3434 
3435 // We don't want jsfriendapi.h to depend on GenericPrinter,
3436 // so these functions are declared directly in the cpp.
3437 
3438 JS_PUBLIC_API void DumpBacktrace(JSContext* cx, js::GenericPrinter& out);
3439 
3440 }  // namespace js
3441 
DumpBacktrace(JSContext * cx,FILE * fp)3442 JS_PUBLIC_API void js::DumpBacktrace(JSContext* cx, FILE* fp) {
3443   Fprinter out(fp);
3444   js::DumpBacktrace(cx, out);
3445 }
3446 
DumpBacktrace(JSContext * cx,js::GenericPrinter & out)3447 JS_PUBLIC_API void js::DumpBacktrace(JSContext* cx, js::GenericPrinter& out) {
3448   size_t depth = 0;
3449   for (AllFramesIter i(cx); !i.done(); ++i, ++depth) {
3450     const char* filename;
3451     unsigned line;
3452     if (i.hasScript()) {
3453       filename = JS_GetScriptFilename(i.script());
3454       line = PCToLineNumber(i.script(), i.pc());
3455     } else {
3456       filename = i.filename();
3457       line = i.computeLine();
3458     }
3459     char frameType = i.isInterp()     ? 'i'
3460                      : i.isBaseline() ? 'b'
3461                      : i.isIon()      ? 'I'
3462                      : i.isWasm()     ? 'W'
3463                                       : '?';
3464 
3465     out.printf("#%zu %14p %c   %s:%u", depth, i.rawFramePtr(), frameType,
3466                filename, line);
3467 
3468     if (i.hasScript()) {
3469       out.printf(" (%p @ %zu)\n", i.script(), i.script()->pcToOffset(i.pc()));
3470     } else {
3471       out.printf(" (%p)\n", i.pc());
3472     }
3473   }
3474 }
3475 
DumpBacktrace(JSContext * cx)3476 JS_PUBLIC_API void js::DumpBacktrace(JSContext* cx) {
3477   DumpBacktrace(cx, stdout);
3478 }
3479 
3480 /* * */
3481 
allocKindForTenure(const js::Nursery & nursery) const3482 js::gc::AllocKind JSObject::allocKindForTenure(
3483     const js::Nursery& nursery) const {
3484   using namespace js::gc;
3485 
3486   MOZ_ASSERT(IsInsideNursery(this));
3487 
3488   if (is<ArrayObject>()) {
3489     const ArrayObject& aobj = as<ArrayObject>();
3490     MOZ_ASSERT(aobj.numFixedSlots() == 0);
3491 
3492     /* Use minimal size object if we are just going to copy the pointer. */
3493     if (!nursery.isInside(aobj.getElementsHeader())) {
3494       return gc::AllocKind::OBJECT0_BACKGROUND;
3495     }
3496 
3497     size_t nelements = aobj.getDenseCapacity();
3498     return ForegroundToBackgroundAllocKind(GetGCArrayKind(nelements));
3499   }
3500 
3501   if (is<JSFunction>()) {
3502     return as<JSFunction>().getAllocKind();
3503   }
3504 
3505   /*
3506    * Typed arrays in the nursery may have a lazily allocated buffer, make
3507    * sure there is room for the array's fixed data when moving the array.
3508    */
3509   if (is<TypedArrayObject>() && !as<TypedArrayObject>().hasBuffer()) {
3510     gc::AllocKind allocKind;
3511     if (as<TypedArrayObject>().hasInlineElements()) {
3512       size_t nbytes = as<TypedArrayObject>().byteLength();
3513       allocKind = TypedArrayObject::AllocKindForLazyBuffer(nbytes);
3514     } else {
3515       allocKind = GetGCObjectKind(getClass());
3516     }
3517     return ForegroundToBackgroundAllocKind(allocKind);
3518   }
3519 
3520   // Proxies that are CrossCompartmentWrappers may be nursery allocated.
3521   if (is<ProxyObject>()) {
3522     return as<ProxyObject>().allocKindForTenure();
3523   }
3524 
3525   // Inlined typed objects are followed by their data, so make sure we copy
3526   // it all over to the new object.
3527   if (is<InlineTypedObject>()) {
3528     // Figure out the size of this object, from the prototype's RttValue.
3529     // The objects we are traversing here are all tenured, so we don't need
3530     // to check forwarding pointers.
3531     RttValue& descr = as<InlineTypedObject>().rttValue();
3532     MOZ_ASSERT(!IsInsideNursery(&descr));
3533     return InlineTypedObject::allocKindForRttValue(&descr);
3534   }
3535 
3536   if (is<OutlineTypedObject>()) {
3537     return OutlineTypedObject::allocKind();
3538   }
3539 
3540   // All nursery allocatable non-native objects are handled above.
3541   return as<NativeObject>().allocKindForTenure();
3542 }
3543 
addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,JS::ClassInfo * info)3544 void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
3545                                       JS::ClassInfo* info) {
3546   if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots()) {
3547     info->objectsMallocHeapSlots +=
3548         mallocSizeOf(as<NativeObject>().getSlotsHeader());
3549   }
3550 
3551   if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
3552     void* allocatedElements = as<NativeObject>().getUnshiftedElementsHeader();
3553     info->objectsMallocHeapElementsNormal += mallocSizeOf(allocatedElements);
3554   }
3555 
3556   // Other things may be measured in the future if DMD indicates it is
3557   // worthwhile.
3558   if (is<JSFunction>() || is<PlainObject>() || is<ArrayObject>() ||
3559       is<CallObject>() || is<RegExpObject>() || is<ProxyObject>()) {
3560     // Do nothing.  But this function is hot, and we win by getting the
3561     // common cases out of the way early.  Some stats on the most common
3562     // classes, as measured during a vanilla browser session:
3563     // - (53.7%, 53.7%): Function
3564     // - (18.0%, 71.7%): Object
3565     // - (16.9%, 88.6%): Array
3566     // - ( 3.9%, 92.5%): Call
3567     // - ( 2.8%, 95.3%): RegExp
3568     // - ( 1.0%, 96.4%): Proxy
3569 
3570     // Note that any JSClass that is special cased below likely needs to
3571     // specify the JSCLASS_DELAY_METADATA_BUILDER flag, or else we will
3572     // probably crash if the object metadata callback attempts to get the
3573     // size of the new object (which Debugger code does) before private
3574     // slots are initialized.
3575   } else if (is<ArgumentsObject>()) {
3576     info->objectsMallocHeapMisc +=
3577         as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
3578   } else if (is<MapObject>()) {
3579     info->objectsMallocHeapMisc += as<MapObject>().sizeOfData(mallocSizeOf);
3580   } else if (is<SetObject>()) {
3581     info->objectsMallocHeapMisc += as<SetObject>().sizeOfData(mallocSizeOf);
3582   } else if (is<RegExpStaticsObject>()) {
3583     info->objectsMallocHeapMisc +=
3584         as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
3585   } else if (is<PropertyIteratorObject>()) {
3586     info->objectsMallocHeapMisc +=
3587         as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
3588   } else if (is<ArrayBufferObject>()) {
3589     ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
3590   } else if (is<SharedArrayBufferObject>()) {
3591     SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
3592   } else if (is<WeakCollectionObject>()) {
3593     info->objectsMallocHeapMisc +=
3594         as<WeakCollectionObject>().sizeOfExcludingThis(mallocSizeOf);
3595   }
3596 #ifdef JS_HAS_CTYPES
3597   else {
3598     // This must be the last case.
3599     info->objectsMallocHeapMisc += ctypes::SizeOfDataIfCDataObject(
3600         mallocSizeOf, const_cast<JSObject*>(this));
3601   }
3602 #endif
3603 }
3604 
sizeOfIncludingThisInNursery() const3605 size_t JSObject::sizeOfIncludingThisInNursery() const {
3606   // This function doesn't concern itself yet with typed objects (bug 1133593).
3607 
3608   MOZ_ASSERT(!isTenured());
3609 
3610   const Nursery& nursery = runtimeFromMainThread()->gc.nursery();
3611   size_t size = gc::Arena::thingSize(allocKindForTenure(nursery));
3612 
3613   if (is<NativeObject>()) {
3614     const NativeObject& native = as<NativeObject>();
3615 
3616     size += native.numDynamicSlots() * sizeof(Value);
3617 
3618     if (native.hasDynamicElements()) {
3619       js::ObjectElements& elements = *native.getElementsHeader();
3620       size += (elements.capacity + elements.numShiftedElements()) *
3621               sizeof(HeapSlot);
3622     }
3623 
3624     if (is<ArgumentsObject>()) {
3625       size += as<ArgumentsObject>().sizeOfData();
3626     }
3627   }
3628 
3629   return size;
3630 }
3631 
size(mozilla::MallocSizeOf mallocSizeOf) const3632 JS::ubi::Node::Size JS::ubi::Concrete<JSObject>::size(
3633     mozilla::MallocSizeOf mallocSizeOf) const {
3634   JSObject& obj = get();
3635 
3636   if (!obj.isTenured()) {
3637     return obj.sizeOfIncludingThisInNursery();
3638   }
3639 
3640   JS::ClassInfo info;
3641   obj.addSizeOfExcludingThis(mallocSizeOf, &info);
3642   return obj.tenuredSizeOfThis() + info.sizeOfAllThings();
3643 }
3644 
3645 const char16_t JS::ubi::Concrete<JSObject>::concreteTypeName[] = u"JSObject";
3646 
traceChildren(JSTracer * trc)3647 void JSObject::traceChildren(JSTracer* trc) {
3648   TraceCellHeaderEdge(trc, this, "shape");
3649 
3650   const JSClass* clasp = getClass();
3651   if (clasp->isNativeObject()) {
3652     NativeObject* nobj = &as<NativeObject>();
3653 
3654     {
3655       GetObjectSlotNameFunctor func(nobj);
3656       JS::AutoTracingDetails ctx(trc, func);
3657       JS::AutoTracingIndex index(trc);
3658       // Tracing can mutate the target but cannot change the slot count,
3659       // but the compiler has no way of knowing this.
3660       const uint32_t nslots = nobj->slotSpan();
3661       for (uint32_t i = 0; i < nslots; ++i) {
3662         TraceEdge(trc, &nobj->getSlotRef(i), "object slot");
3663         ++index;
3664       }
3665       MOZ_ASSERT(nslots == nobj->slotSpan());
3666     }
3667 
3668     TraceRange(trc, nobj->getDenseInitializedLength(),
3669                static_cast<HeapSlot*>(nobj->getDenseElements()),
3670                "objectElements");
3671   }
3672 
3673   // Call the trace hook at the end so that during a moving GC the trace hook
3674   // will see updated fields and slots.
3675   if (clasp->hasTrace()) {
3676     clasp->doTrace(trc, this);
3677   }
3678 
3679   if (trc->isMarkingTracer()) {
3680     GCMarker::fromTracer(trc)->markImplicitEdges(this);
3681   }
3682 }
3683 
3684 // ES 2016 7.3.20.
SpeciesConstructor(JSContext * cx,HandleObject obj,HandleObject defaultCtor,bool (* isDefaultSpecies)(JSContext *,JSFunction *))3685 [[nodiscard]] JSObject* js::SpeciesConstructor(
3686     JSContext* cx, HandleObject obj, HandleObject defaultCtor,
3687     bool (*isDefaultSpecies)(JSContext*, JSFunction*)) {
3688   // Step 1 (implicit).
3689 
3690   // Fast-path for steps 2 - 8. Applies if all of the following conditions
3691   // are met:
3692   // - obj.constructor can be retrieved without side-effects.
3693   // - obj.constructor[[@@species]] can be retrieved without side-effects.
3694   // - obj.constructor[[@@species]] is the builtin's original @@species
3695   //   getter.
3696   RootedValue ctor(cx);
3697   bool ctorGetSucceeded = GetPropertyPure(
3698       cx, obj, NameToId(cx->names().constructor), ctor.address());
3699   if (ctorGetSucceeded && ctor.isObject() && &ctor.toObject() == defaultCtor) {
3700     jsid speciesId = SYMBOL_TO_JSID(cx->wellKnownSymbols().species);
3701     JSFunction* getter;
3702     if (GetGetterPure(cx, defaultCtor, speciesId, &getter) && getter &&
3703         isDefaultSpecies(cx, getter)) {
3704       return defaultCtor;
3705     }
3706   }
3707 
3708   // Step 2.
3709   if (!ctorGetSucceeded &&
3710       !GetProperty(cx, obj, obj, cx->names().constructor, &ctor)) {
3711     return nullptr;
3712   }
3713 
3714   // Step 3.
3715   if (ctor.isUndefined()) {
3716     return defaultCtor;
3717   }
3718 
3719   // Step 4.
3720   if (!ctor.isObject()) {
3721     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3722                               JSMSG_OBJECT_REQUIRED,
3723                               "object's 'constructor' property");
3724     return nullptr;
3725   }
3726 
3727   // Step 5.
3728   RootedObject ctorObj(cx, &ctor.toObject());
3729   RootedValue s(cx);
3730   RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
3731   if (!GetProperty(cx, ctorObj, ctor, speciesId, &s)) {
3732     return nullptr;
3733   }
3734 
3735   // Step 6.
3736   if (s.isNullOrUndefined()) {
3737     return defaultCtor;
3738   }
3739 
3740   // Step 7.
3741   if (IsConstructor(s)) {
3742     return &s.toObject();
3743   }
3744 
3745   // Step 8.
3746   JS_ReportErrorNumberASCII(
3747       cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR,
3748       "[Symbol.species] property of object's constructor");
3749   return nullptr;
3750 }
3751 
SpeciesConstructor(JSContext * cx,HandleObject obj,JSProtoKey ctorKey,bool (* isDefaultSpecies)(JSContext *,JSFunction *))3752 [[nodiscard]] JSObject* js::SpeciesConstructor(
3753     JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
3754     bool (*isDefaultSpecies)(JSContext*, JSFunction*)) {
3755   RootedObject defaultCtor(cx,
3756                            GlobalObject::getOrCreateConstructor(cx, ctorKey));
3757   if (!defaultCtor) {
3758     return nullptr;
3759   }
3760   return SpeciesConstructor(cx, obj, defaultCtor, isDefaultSpecies);
3761 }
3762 
Unbox(JSContext * cx,HandleObject obj,MutableHandleValue vp)3763 bool js::Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp) {
3764   if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
3765     return Proxy::boxedValue_unbox(cx, obj, vp);
3766   }
3767 
3768   if (obj->is<BooleanObject>()) {
3769     vp.setBoolean(obj->as<BooleanObject>().unbox());
3770   } else if (obj->is<NumberObject>()) {
3771     vp.setNumber(obj->as<NumberObject>().unbox());
3772   } else if (obj->is<StringObject>()) {
3773     vp.setString(obj->as<StringObject>().unbox());
3774   } else if (obj->is<DateObject>()) {
3775     vp.set(obj->as<DateObject>().UTCTime());
3776   } else if (obj->is<SymbolObject>()) {
3777     vp.setSymbol(obj->as<SymbolObject>().unbox());
3778   } else if (obj->is<BigIntObject>()) {
3779     vp.setBigInt(obj->as<BigIntObject>().unbox());
3780   } else {
3781     vp.setUndefined();
3782   }
3783 
3784   return true;
3785 }
3786 
3787 #ifdef DEBUG
3788 /* static */
debugCheckNewObject(Shape * shape,js::gc::AllocKind allocKind,js::gc::InitialHeap heap)3789 void JSObject::debugCheckNewObject(Shape* shape, js::gc::AllocKind allocKind,
3790                                    js::gc::InitialHeap heap) {
3791   const JSClass* clasp = shape->getObjectClass();
3792   MOZ_ASSERT(clasp != &ArrayObject::class_);
3793 
3794   if (!ClassCanHaveFixedData(clasp)) {
3795     MOZ_ASSERT(shape);
3796     MOZ_ASSERT(gc::GetGCKindSlots(allocKind, clasp) == shape->numFixedSlots());
3797   }
3798 
3799   // Classes with a finalizer must specify whether instances will be finalized
3800   // on the main thread or in the background, except proxies whose behaviour
3801   // depends on the target object.
3802   static const uint32_t FinalizeMask =
3803       JSCLASS_FOREGROUND_FINALIZE | JSCLASS_BACKGROUND_FINALIZE;
3804   uint32_t flags = clasp->flags;
3805   uint32_t finalizeFlags = flags & FinalizeMask;
3806   if (clasp->hasFinalize() && !clasp->isProxyObject()) {
3807     MOZ_ASSERT(finalizeFlags == JSCLASS_FOREGROUND_FINALIZE ||
3808                finalizeFlags == JSCLASS_BACKGROUND_FINALIZE);
3809     MOZ_ASSERT((finalizeFlags == JSCLASS_BACKGROUND_FINALIZE) ==
3810                IsBackgroundFinalized(allocKind));
3811   } else {
3812     MOZ_ASSERT(finalizeFlags == 0);
3813   }
3814 
3815   MOZ_ASSERT_IF(clasp->hasFinalize(),
3816                 heap == gc::TenuredHeap ||
3817                     CanNurseryAllocateFinalizedClass(clasp) ||
3818                     clasp->isProxyObject());
3819 
3820   MOZ_ASSERT(!shape->realm()->hasObjectPendingMetadata());
3821 
3822   // Non-native classes manage their own data and slots, so numFixedSlots is
3823   // always 0. Note that proxy classes can have reserved slots but they're not
3824   // included in numFixedSlots.
3825   if (!clasp->isNativeObject()) {
3826     MOZ_ASSERT_IF(!clasp->isProxyObject(), JSCLASS_RESERVED_SLOTS(clasp) == 0);
3827     MOZ_ASSERT(!clasp->hasPrivate());
3828     MOZ_ASSERT_IF(shape, shape->numFixedSlots() == 0);
3829   }
3830 }
3831 #endif
3832