1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 "jsobjinlines.h"
12 
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/MathAlgorithms.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/SizePrintfMacros.h"
17 #include "mozilla/TemplateLib.h"
18 #include "mozilla/UniquePtr.h"
19 
20 #include <string.h>
21 
22 #include "jsapi.h"
23 #include "jsarray.h"
24 #include "jsatom.h"
25 #include "jscntxt.h"
26 #include "jsfriendapi.h"
27 #include "jsfun.h"
28 #include "jsgc.h"
29 #include "jsiter.h"
30 #include "jsnum.h"
31 #include "jsopcode.h"
32 #include "jsprf.h"
33 #include "jsscript.h"
34 #include "jsstr.h"
35 #include "jstypes.h"
36 #include "jsutil.h"
37 #include "jswatchpoint.h"
38 #include "jswin.h"
39 #include "jswrapper.h"
40 
41 #include "asmjs/AsmJSModule.h"
42 #include "builtin/Eval.h"
43 #include "builtin/Object.h"
44 #include "builtin/SymbolObject.h"
45 #include "frontend/BytecodeCompiler.h"
46 #include "gc/Marking.h"
47 #include "jit/BaselineJIT.h"
48 #include "js/MemoryMetrics.h"
49 #include "js/Proxy.h"
50 #include "js/UbiNode.h"
51 #include "vm/ArgumentsObject.h"
52 #include "vm/Interpreter.h"
53 #include "vm/ProxyObject.h"
54 #include "vm/RegExpStaticsObject.h"
55 #include "vm/Shape.h"
56 #include "vm/TypedArrayCommon.h"
57 
58 #include "jsatominlines.h"
59 #include "jsboolinlines.h"
60 #include "jscntxtinlines.h"
61 #include "jscompartmentinlines.h"
62 
63 #include "vm/ArrayObject-inl.h"
64 #include "vm/BooleanObject-inl.h"
65 #include "vm/Interpreter-inl.h"
66 #include "vm/NativeObject-inl.h"
67 #include "vm/NumberObject-inl.h"
68 #include "vm/Runtime-inl.h"
69 #include "vm/Shape-inl.h"
70 #include "vm/StringObject-inl.h"
71 
72 using namespace js;
73 using namespace js::gc;
74 
75 using mozilla::DebugOnly;
76 using mozilla::Maybe;
77 using mozilla::UniquePtr;
78 
79 void
ReportNotObject(JSContext * cx,const Value & v)80 js::ReportNotObject(JSContext* cx, const Value& v)
81 {
82     MOZ_ASSERT(!v.isObject());
83 
84     RootedValue value(cx, v);
85     UniquePtr<char[], JS::FreePolicy> bytes =
86         DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
87     if (bytes)
88         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
89 }
90 
91 const char*
InformalValueTypeName(const Value & v)92 js::InformalValueTypeName(const Value& v)
93 {
94     if (v.isObject())
95         return v.toObject().getClass()->name;
96     if (v.isString())
97         return "string";
98     if (v.isSymbol())
99         return "symbol";
100     if (v.isNumber())
101         return "number";
102     if (v.isBoolean())
103         return "boolean";
104     if (v.isNull())
105         return "null";
106     if (v.isUndefined())
107         return "undefined";
108     return "value";
109 }
110 
111 // ES6 draft rev37 6.2.4.4 FromPropertyDescriptor
112 bool
FromPropertyDescriptor(JSContext * cx,Handle<PropertyDescriptor> desc,MutableHandleValue vp)113 js::FromPropertyDescriptor(JSContext* cx, Handle<PropertyDescriptor> desc, MutableHandleValue vp)
114 {
115     // Step 1.
116     if (!desc.object()) {
117         vp.setUndefined();
118         return true;
119     }
120 
121     return FromPropertyDescriptorToObject(cx, desc, vp);
122 }
123 
124 bool
FromPropertyDescriptorToObject(JSContext * cx,Handle<PropertyDescriptor> desc,MutableHandleValue vp)125 js::FromPropertyDescriptorToObject(JSContext* cx, Handle<PropertyDescriptor> desc,
126                                    MutableHandleValue vp)
127 {
128     // Step 2-3.
129     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
130     if (!obj)
131         return false;
132 
133     const JSAtomState& names = cx->names();
134 
135     // Step 4.
136     if (desc.hasValue()) {
137         if (!DefineProperty(cx, obj, names.value, desc.value()))
138             return false;
139     }
140 
141     // Step 5.
142     RootedValue v(cx);
143     if (desc.hasWritable()) {
144         v.setBoolean(desc.writable());
145         if (!DefineProperty(cx, obj, names.writable, v))
146             return false;
147     }
148 
149     // Step 6.
150     if (desc.hasGetterObject()) {
151         if (JSObject* get = desc.getterObject())
152             v.setObject(*get);
153         else
154             v.setUndefined();
155         if (!DefineProperty(cx, obj, names.get, v))
156             return false;
157     }
158 
159     // Step 7.
160     if (desc.hasSetterObject()) {
161         if (JSObject* set = desc.setterObject())
162             v.setObject(*set);
163         else
164             v.setUndefined();
165         if (!DefineProperty(cx, obj, names.set, v))
166             return false;
167     }
168 
169     // Step 8.
170     if (desc.hasEnumerable()) {
171         v.setBoolean(desc.enumerable());
172         if (!DefineProperty(cx, obj, names.enumerable, v))
173             return false;
174     }
175 
176     // Step 9.
177     if (desc.hasConfigurable()) {
178         v.setBoolean(desc.configurable());
179         if (!DefineProperty(cx, obj, names.configurable, v))
180             return false;
181     }
182 
183     vp.setObject(*obj);
184     return true;
185 }
186 
187 bool
GetFirstArgumentAsObject(JSContext * cx,const CallArgs & args,const char * method,MutableHandleObject objp)188 js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
189                              MutableHandleObject objp)
190 {
191     if (args.length() == 0) {
192         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
193                              method, "0", "s");
194         return false;
195     }
196 
197     HandleValue v = args[0];
198     if (!v.isObject()) {
199         UniquePtr<char[], JS::FreePolicy> bytes =
200             DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
201         if (!bytes)
202             return false;
203         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
204                              bytes.get(), "not an object");
205         return false;
206     }
207 
208     objp.set(&v.toObject());
209     return true;
210 }
211 
212 static bool
GetPropertyIfPresent(JSContext * cx,HandleObject obj,HandleId id,MutableHandleValue vp,bool * foundp)213 GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
214                      bool* foundp)
215 {
216     if (!HasProperty(cx, obj, id, foundp))
217         return false;
218     if (!*foundp) {
219         vp.setUndefined();
220         return true;
221     }
222 
223     return GetProperty(cx, obj, obj, id, vp);
224 }
225 
226 bool
Throw(JSContext * cx,jsid id,unsigned errorNumber)227 js::Throw(JSContext* cx, jsid id, unsigned errorNumber)
228 {
229     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1);
230 
231     RootedValue idVal(cx, IdToValue(id));
232     JSString* idstr = ValueToSource(cx, idVal);
233     if (!idstr)
234        return false;
235     JSAutoByteString bytes(cx, idstr);
236     if (!bytes)
237         return false;
238     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr());
239     return false;
240 }
241 
242 bool
Throw(JSContext * cx,JSObject * obj,unsigned errorNumber)243 js::Throw(JSContext* cx, JSObject* obj, unsigned errorNumber)
244 {
245     if (js_ErrorFormatString[errorNumber].argCount == 1) {
246         RootedValue val(cx, ObjectValue(*obj));
247         ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
248                               JSDVG_IGNORE_STACK, val, nullptr,
249                               nullptr, nullptr);
250     } else {
251         MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
252         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber);
253     }
254     return false;
255 }
256 
257 
258 /*** PropertyDescriptor operations and DefineProperties ******************************************/
259 
260 bool
CheckCallable(JSContext * cx,JSObject * obj,const char * fieldName)261 CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName)
262 {
263     if (obj && !obj->isCallable()) {
264         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
265                              fieldName);
266         return false;
267     }
268     return true;
269 }
270 
271 bool
ToPropertyDescriptor(JSContext * cx,HandleValue descval,bool checkAccessors,MutableHandle<PropertyDescriptor> desc)272 js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
273                          MutableHandle<PropertyDescriptor> desc)
274 {
275     // step 2
276     RootedObject obj(cx, NonNullObject(cx, descval));
277     if (!obj)
278         return false;
279 
280     // step 3
281     desc.clear();
282 
283     bool found = false;
284     RootedId id(cx);
285     RootedValue v(cx);
286     unsigned attrs = 0;
287 
288     // step 4
289     id = NameToId(cx->names().enumerable);
290     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
291         return false;
292     if (found) {
293         if (ToBoolean(v))
294             attrs |= JSPROP_ENUMERATE;
295     } else {
296         attrs |= JSPROP_IGNORE_ENUMERATE;
297     }
298 
299     // step 5
300     id = NameToId(cx->names().configurable);
301     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
302         return false;
303     if (found) {
304         if (!ToBoolean(v))
305             attrs |= JSPROP_PERMANENT;
306     } else {
307         attrs |= JSPROP_IGNORE_PERMANENT;
308     }
309 
310     // step 6
311     id = NameToId(cx->names().value);
312     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
313         return false;
314     if (found)
315         desc.value().set(v);
316     else
317         attrs |= JSPROP_IGNORE_VALUE;
318 
319     // step 7
320     id = NameToId(cx->names().writable);
321     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
322         return false;
323     if (found) {
324         if (!ToBoolean(v))
325             attrs |= JSPROP_READONLY;
326     } else {
327         attrs |= JSPROP_IGNORE_READONLY;
328     }
329 
330     // step 8
331     bool hasGetOrSet;
332     id = NameToId(cx->names().get);
333     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
334         return false;
335     hasGetOrSet = found;
336     if (found) {
337         if (v.isObject()) {
338             if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_getter_str))
339                 return false;
340             desc.setGetterObject(&v.toObject());
341         } else if (!v.isUndefined()) {
342             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
343                                  js_getter_str);
344             return false;
345         }
346         attrs |= JSPROP_GETTER | JSPROP_SHARED;
347     }
348 
349     // step 9
350     id = NameToId(cx->names().set);
351     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
352         return false;
353     hasGetOrSet |= found;
354     if (found) {
355         if (v.isObject()) {
356             if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_setter_str))
357                 return false;
358             desc.setSetterObject(&v.toObject());
359         } else if (!v.isUndefined()) {
360             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
361                                  js_setter_str);
362             return false;
363         }
364         attrs |= JSPROP_SETTER | JSPROP_SHARED;
365     }
366 
367     // step 10
368     if (hasGetOrSet) {
369         if (!(attrs & JSPROP_IGNORE_READONLY) || !(attrs & JSPROP_IGNORE_VALUE)) {
370             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INVALID_DESCRIPTOR);
371             return false;
372         }
373 
374         // By convention, these bits are not used on accessor descriptors.
375         attrs &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
376     }
377 
378     desc.setAttributes(attrs);
379     MOZ_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
380     MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
381     return true;
382 }
383 
384 bool
CheckPropertyDescriptorAccessors(JSContext * cx,Handle<PropertyDescriptor> desc)385 js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle<PropertyDescriptor> desc)
386 {
387     if (desc.hasGetterObject()) {
388         if (!CheckCallable(cx, desc.getterObject(), js_getter_str))
389             return false;
390     }
391     if (desc.hasSetterObject()) {
392         if (!CheckCallable(cx, desc.setterObject(), js_setter_str))
393             return false;
394     }
395     return true;
396 }
397 
398 void
CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)399 js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
400 {
401     desc.assertValid();
402 
403     if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
404         if (!desc.hasWritable())
405             desc.attributesRef() |= JSPROP_READONLY;
406         desc.attributesRef() &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
407     } else {
408         if (!desc.hasGetterObject())
409             desc.setGetterObject(nullptr);
410         if (!desc.hasSetterObject())
411             desc.setSetterObject(nullptr);
412         desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
413     }
414     if (!desc.hasConfigurable())
415         desc.attributesRef() |= JSPROP_PERMANENT;
416     desc.attributesRef() &= ~(JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_ENUMERATE);
417 
418     desc.assertComplete();
419 }
420 
421 bool
ReadPropertyDescriptors(JSContext * cx,HandleObject props,bool checkAccessors,AutoIdVector * ids,MutableHandle<PropertyDescriptorVector> descs)422 js::ReadPropertyDescriptors(JSContext* cx, HandleObject props, bool checkAccessors,
423                             AutoIdVector* ids, MutableHandle<PropertyDescriptorVector> descs)
424 {
425     if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids))
426         return false;
427 
428     RootedId id(cx);
429     for (size_t i = 0, len = ids->length(); i < len; i++) {
430         id = (*ids)[i];
431         Rooted<PropertyDescriptor> desc(cx);
432         RootedValue v(cx);
433         if (!GetProperty(cx, props, props, id, &v) ||
434             !ToPropertyDescriptor(cx, v, checkAccessors, &desc) ||
435             !descs.append(desc))
436         {
437             return false;
438         }
439     }
440     return true;
441 }
442 
443 bool
DefineProperties(JSContext * cx,HandleObject obj,HandleObject props)444 js::DefineProperties(JSContext* cx, HandleObject obj, HandleObject props)
445 {
446     AutoIdVector ids(cx);
447     Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
448     if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
449         return false;
450 
451     for (size_t i = 0, len = ids.length(); i < len; i++) {
452         if (!DefineProperty(cx, obj, ids[i], descs[i]))
453             return false;
454     }
455 
456     return true;
457 }
458 
459 
460 /*** Seal and freeze *****************************************************************************/
461 
462 static unsigned
GetSealedOrFrozenAttributes(unsigned attrs,IntegrityLevel level)463 GetSealedOrFrozenAttributes(unsigned attrs, IntegrityLevel level)
464 {
465     /* Make all attributes permanent; if freezing, make data attributes read-only. */
466     if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
467         return JSPROP_PERMANENT | JSPROP_READONLY;
468     return JSPROP_PERMANENT;
469 }
470 
471 /* ES6 draft rev 29 (6 Dec 2014) 7.3.13. */
472 bool
SetIntegrityLevel(JSContext * cx,HandleObject obj,IntegrityLevel level)473 js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
474 {
475     assertSameCompartment(cx, obj);
476 
477     // Steps 3-5. (Steps 1-2 are redundant assertions.)
478     if (!PreventExtensions(cx, obj))
479         return false;
480 
481     // Steps 6-7.
482     AutoIdVector keys(cx);
483     if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys))
484         return false;
485 
486     // PreventExtensions must sparsify dense objects, so we can assign to holes
487     // without checks.
488     MOZ_ASSERT_IF(obj->isNative(), obj->as<NativeObject>().getDenseCapacity() == 0);
489 
490     // Steps 8-9, loosely interpreted.
491     if (obj->isNative() && !obj->as<NativeObject>().inDictionaryMode() && !IsAnyTypedArray(obj)) {
492         HandleNativeObject nobj = obj.as<NativeObject>();
493 
494         // Seal/freeze non-dictionary objects by constructing a new shape
495         // hierarchy mirroring the original one, which can be shared if many
496         // objects with the same structure are sealed/frozen. If we use the
497         // generic path below then any non-empty object will be converted to
498         // dictionary mode.
499         RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(),
500                                                          nobj->getTaggedProto(),
501                                                          nobj->numFixedSlots(),
502                                                          nobj->lastProperty()->getObjectFlags()));
503         if (!last)
504             return false;
505 
506         // Get an in-order list of the shapes in this object.
507         Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
508         for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
509             if (!shapes.append(&r.front()))
510                 return false;
511         }
512         Reverse(shapes.begin(), shapes.end());
513 
514         for (Shape* shape : shapes) {
515             Rooted<StackShape> child(cx, StackShape(shape));
516             child.setAttrs(child.attrs() | GetSealedOrFrozenAttributes(child.attrs(), level));
517 
518             if (!JSID_IS_EMPTY(child.get().propid) && level == IntegrityLevel::Frozen)
519                 MarkTypePropertyNonWritable(cx, nobj, child.get().propid);
520 
521             last = cx->compartment()->propertyTree.getChild(cx, last, child);
522             if (!last)
523                 return false;
524         }
525 
526         MOZ_ASSERT(nobj->lastProperty()->slotSpan() == last->slotSpan());
527         JS_ALWAYS_TRUE(nobj->setLastProperty(cx, last));
528 
529         // Ordinarily ArraySetLength handles this, but we're going behind its back
530         // right now, so we must do this manually.
531         //
532         // ArraySetLength also implements the capacity <= length invariant for
533         // arrays with non-writable length.  We don't need to do anything special
534         // for that, because capacity was zeroed out by preventExtensions.  (See
535         // the assertion about getDenseCapacity above.)
536         if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
537             if (!obj->as<ArrayObject>().maybeCopyElementsForWrite(cx))
538                 return false;
539             obj->as<ArrayObject>().getElementsHeader()->setNonwritableArrayLength();
540         }
541     } else {
542         RootedId id(cx);
543         Rooted<PropertyDescriptor> desc(cx);
544 
545         const unsigned AllowConfigure = JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
546                                         JSPROP_IGNORE_VALUE;
547         const unsigned AllowConfigureAndWritable = AllowConfigure & ~JSPROP_IGNORE_READONLY;
548 
549         // 8.a/9.a. The two different loops are merged here.
550         for (size_t i = 0; i < keys.length(); i++) {
551             id = keys[i];
552 
553             if (level == IntegrityLevel::Sealed) {
554                 // 8.a.i.
555                 desc.setAttributes(AllowConfigure | JSPROP_PERMANENT);
556             } else {
557                 // 9.a.i-ii.
558                 Rooted<PropertyDescriptor> currentDesc(cx);
559                 if (!GetOwnPropertyDescriptor(cx, obj, id, &currentDesc))
560                     return false;
561 
562                 // 9.a.iii.
563                 if (!currentDesc.object())
564                     continue;
565 
566                 // 9.a.iii.1-2
567                 if (currentDesc.isAccessorDescriptor())
568                     desc.setAttributes(AllowConfigure | JSPROP_PERMANENT);
569                 else
570                     desc.setAttributes(AllowConfigureAndWritable | JSPROP_PERMANENT | JSPROP_READONLY);
571             }
572 
573             // 8.a.i-ii. / 9.a.iii.3-4
574             if (!DefineProperty(cx, obj, id, desc))
575                 return false;
576         }
577     }
578 
579     return true;
580 }
581 
582 // ES6 draft rev33 (12 Feb 2015) 7.3.15
583 bool
TestIntegrityLevel(JSContext * cx,HandleObject obj,IntegrityLevel level,bool * result)584 js::TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bool* result)
585 {
586     // Steps 3-6. (Steps 1-2 are redundant assertions.)
587     bool status;
588     if (!IsExtensible(cx, obj, &status))
589         return false;
590     if (status) {
591         *result = false;
592         return true;
593     }
594 
595     // Steps 7-8.
596     AutoIdVector props(cx);
597     if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
598         return false;
599 
600     // Step 9.
601     RootedId id(cx);
602     Rooted<PropertyDescriptor> desc(cx);
603     for (size_t i = 0, len = props.length(); i < len; i++) {
604         id = props[i];
605 
606         // Steps 9.a-b.
607         if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
608             return false;
609 
610         // Step 9.c.
611         if (!desc.object())
612             continue;
613 
614         // Steps 9.c.i-ii.
615         if (desc.configurable() ||
616             (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable()))
617         {
618             *result = false;
619             return true;
620         }
621     }
622 
623     // Step 10.
624     *result = true;
625     return true;
626 }
627 
628 
629 /* * */
630 
631 /*
632  * Get the GC kind to use for scripted 'new' on the given class.
633  * FIXME bug 547327: estimate the size from the allocation site.
634  */
635 static inline gc::AllocKind
NewObjectGCKind(const js::Class * clasp)636 NewObjectGCKind(const js::Class* clasp)
637 {
638     if (clasp == &ArrayObject::class_)
639         return gc::AllocKind::OBJECT8;
640     if (clasp == &JSFunction::class_)
641         return gc::AllocKind::OBJECT2;
642     return gc::AllocKind::OBJECT4;
643 }
644 
645 static inline JSObject*
NewObject(ExclusiveContext * cx,HandleObjectGroup group,gc::AllocKind kind,NewObjectKind newKind,uint32_t initialShapeFlags=0)646 NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind,
647           NewObjectKind newKind, uint32_t initialShapeFlags = 0)
648 {
649     const Class* clasp = group->clasp();
650 
651     MOZ_ASSERT(clasp != &ArrayObject::class_);
652     MOZ_ASSERT_IF(clasp == &JSFunction::class_,
653                   kind == AllocKind::FUNCTION || kind == AllocKind::FUNCTION_EXTENDED);
654 
655     // For objects which can have fixed data following the object, only use
656     // enough fixed slots to cover the number of reserved slots in the object,
657     // regardless of the allocation kind specified.
658     size_t nfixed = ClassCanHaveFixedData(clasp)
659                     ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
660                     : GetGCKindSlots(kind, clasp);
661 
662     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed,
663                                                       initialShapeFlags));
664     if (!shape)
665         return nullptr;
666 
667     gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
668     JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
669     if (!obj)
670         return nullptr;
671 
672     if (newKind == SingletonObject) {
673         RootedObject nobj(cx, obj);
674         if (!JSObject::setSingleton(cx, nobj))
675             return nullptr;
676         obj = nobj;
677     }
678 
679     probes::CreateObject(cx, obj);
680     return obj;
681 }
682 
683 void
fillProto(EntryIndex entry,const Class * clasp,js::TaggedProto proto,gc::AllocKind kind,NativeObject * obj)684 NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto,
685                           gc::AllocKind kind, NativeObject* obj)
686 {
687     MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
688     MOZ_ASSERT(obj->getTaggedProto() == proto);
689     return fill(entry, clasp, proto.raw(), kind, obj);
690 }
691 
692 bool
NewObjectWithTaggedProtoIsCachable(ExclusiveContext * cxArg,Handle<TaggedProto> proto,NewObjectKind newKind,const Class * clasp)693 js::NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
694                                        NewObjectKind newKind, const Class* clasp)
695 {
696     return cxArg->isJSContext() &&
697            proto.isObject() &&
698            newKind == GenericObject &&
699            clasp->isNative() &&
700            !proto.toObject()->is<GlobalObject>();
701 }
702 
703 JSObject*
NewObjectWithGivenTaggedProto(ExclusiveContext * cxArg,const Class * clasp,Handle<TaggedProto> proto,gc::AllocKind allocKind,NewObjectKind newKind,uint32_t initialShapeFlags)704 js::NewObjectWithGivenTaggedProto(ExclusiveContext* cxArg, const Class* clasp,
705                                   Handle<TaggedProto> proto,
706                                   gc::AllocKind allocKind, NewObjectKind newKind,
707                                   uint32_t initialShapeFlags)
708 {
709     if (CanBeFinalizedInBackground(allocKind, clasp))
710         allocKind = GetBackgroundAllocKind(allocKind);
711 
712     bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, proto, newKind, clasp);
713     if (isCachable) {
714         JSContext* cx = cxArg->asJSContext();
715         JSRuntime* rt = cx->runtime();
716         NewObjectCache& cache = rt->newObjectCache;
717         NewObjectCache::EntryIndex entry = -1;
718         if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
719             JSObject* obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
720             if (obj)
721                 return obj;
722         }
723     }
724 
725     RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, clasp, proto, nullptr));
726     if (!group)
727         return nullptr;
728 
729     RootedObject obj(cxArg, NewObject(cxArg, group, allocKind, newKind, initialShapeFlags));
730     if (!obj)
731         return nullptr;
732 
733     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
734         NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache;
735         NewObjectCache::EntryIndex entry = -1;
736         cache.lookupProto(clasp, proto.toObject(), allocKind, &entry);
737         cache.fillProto(entry, clasp, proto, allocKind, &obj->as<NativeObject>());
738     }
739 
740     return obj;
741 }
742 
743 static bool
NewObjectIsCachable(ExclusiveContext * cxArg,NewObjectKind newKind,const Class * clasp)744 NewObjectIsCachable(ExclusiveContext* cxArg, NewObjectKind newKind, const Class* clasp)
745 {
746     return cxArg->isJSContext() &&
747            newKind == GenericObject &&
748            clasp->isNative();
749 }
750 
751 JSObject*
NewObjectWithClassProtoCommon(ExclusiveContext * cxArg,const Class * clasp,HandleObject protoArg,gc::AllocKind allocKind,NewObjectKind newKind)752 js::NewObjectWithClassProtoCommon(ExclusiveContext* cxArg, const Class* clasp,
753                                   HandleObject protoArg,
754                                   gc::AllocKind allocKind, NewObjectKind newKind)
755 {
756     if (protoArg) {
757         return NewObjectWithGivenTaggedProto(cxArg, clasp, AsTaggedProto(protoArg),
758                                              allocKind, newKind);
759     }
760 
761     if (CanBeFinalizedInBackground(allocKind, clasp))
762         allocKind = GetBackgroundAllocKind(allocKind);
763 
764     Handle<GlobalObject*> global = cxArg->global();
765 
766     bool isCachable = NewObjectIsCachable(cxArg, newKind, clasp);
767     if (isCachable) {
768         JSContext* cx = cxArg->asJSContext();
769         JSRuntime* rt = cx->runtime();
770         NewObjectCache& cache = rt->newObjectCache;
771         NewObjectCache::EntryIndex entry = -1;
772         if (cache.lookupGlobal(clasp, global, allocKind, &entry)) {
773             JSObject* obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
774             if (obj)
775                 return obj;
776         }
777     }
778 
779     /*
780      * Find the appropriate proto for clasp. Built-in classes have a cached
781      * proto on cx->global(); all others get %ObjectPrototype%.
782      */
783     JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
784     if (protoKey == JSProto_Null)
785         protoKey = JSProto_Object;
786 
787     RootedObject proto(cxArg, protoArg);
788     if (!GetBuiltinPrototype(cxArg, protoKey, &proto))
789         return nullptr;
790 
791     Rooted<TaggedProto> taggedProto(cxArg, TaggedProto(proto));
792     RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, clasp, taggedProto));
793     if (!group)
794         return nullptr;
795 
796     JSObject* obj = NewObject(cxArg, group, allocKind, newKind);
797     if (!obj)
798         return nullptr;
799 
800     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
801         NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache;
802         NewObjectCache::EntryIndex entry = -1;
803         cache.lookupGlobal(clasp, global, allocKind, &entry);
804         cache.fillGlobal(entry, clasp, global, allocKind,
805                          &obj->as<NativeObject>());
806     }
807 
808     return obj;
809 }
810 
811 static bool
NewObjectWithGroupIsCachable(ExclusiveContext * cx,HandleObjectGroup group,NewObjectKind newKind)812 NewObjectWithGroupIsCachable(ExclusiveContext* cx, HandleObjectGroup group,
813                              NewObjectKind newKind)
814 {
815     return group->proto().isObject() &&
816            newKind == GenericObject &&
817            group->clasp()->isNative() &&
818            (!group->newScript() || group->newScript()->analyzed()) &&
819            cx->isJSContext();
820 }
821 
822 /*
823  * Create a plain object with the specified group. This bypasses getNewGroup to
824  * avoid losing creation site information for objects made by scripted 'new'.
825  */
826 JSObject*
NewObjectWithGroupCommon(ExclusiveContext * cx,HandleObjectGroup group,gc::AllocKind allocKind,NewObjectKind newKind)827 js::NewObjectWithGroupCommon(ExclusiveContext* cx, HandleObjectGroup group,
828                              gc::AllocKind allocKind, NewObjectKind newKind)
829 {
830     MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
831     if (CanBeFinalizedInBackground(allocKind, group->clasp()))
832         allocKind = GetBackgroundAllocKind(allocKind);
833 
834     bool isCachable = NewObjectWithGroupIsCachable(cx, group, newKind);
835     if (isCachable) {
836         NewObjectCache& cache = cx->asJSContext()->runtime()->newObjectCache;
837         NewObjectCache::EntryIndex entry = -1;
838         if (cache.lookupGroup(group, allocKind, &entry)) {
839             JSObject* obj = cache.newObjectFromHit(cx->asJSContext(), entry,
840                                                    GetInitialHeap(newKind, group->clasp()));
841             if (obj)
842                 return obj;
843         }
844     }
845 
846     JSObject* obj = NewObject(cx, group, allocKind, newKind);
847     if (!obj)
848         return nullptr;
849 
850     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
851         NewObjectCache& cache = cx->asJSContext()->runtime()->newObjectCache;
852         NewObjectCache::EntryIndex entry = -1;
853         cache.lookupGroup(group, allocKind, &entry);
854         cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
855     }
856 
857     return obj;
858 }
859 
860 bool
NewObjectScriptedCall(JSContext * cx,MutableHandleObject pobj)861 js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj)
862 {
863     jsbytecode* pc;
864     RootedScript script(cx, cx->currentScript(&pc));
865     gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
866     NewObjectKind newKind = GenericObject;
867     if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_))
868         newKind = SingletonObject;
869     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
870     if (!obj)
871         return false;
872 
873     if (script) {
874         /* Try to specialize the group of the object to the scripted call site. */
875         if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject))
876             return false;
877     }
878 
879     pobj.set(obj);
880     return true;
881 }
882 
883 JSObject*
CreateThis(JSContext * cx,const Class * newclasp,HandleObject callee)884 js::CreateThis(JSContext* cx, const Class* newclasp, HandleObject callee)
885 {
886     RootedObject proto(cx);
887     if (!GetPrototypeFromConstructor(cx, callee, &proto))
888         return nullptr;
889     gc::AllocKind kind = NewObjectGCKind(newclasp);
890     return NewObjectWithClassProto(cx, newclasp, proto, kind);
891 }
892 
893 static inline JSObject*
CreateThisForFunctionWithGroup(JSContext * cx,HandleObjectGroup group,NewObjectKind newKind)894 CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group,
895                                NewObjectKind newKind)
896 {
897     if (group->maybeUnboxedLayout() && newKind != SingletonObject)
898         return UnboxedPlainObject::create(cx, group, newKind);
899 
900     if (TypeNewScript* newScript = group->newScript()) {
901         if (newScript->analyzed()) {
902             // The definite properties analysis has been performed for this
903             // group, so get the shape and alloc kind to use from the
904             // TypeNewScript's template.
905             RootedPlainObject templateObject(cx, newScript->templateObject());
906             MOZ_ASSERT(templateObject->group() == group);
907 
908             RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
909             if (!res)
910                 return nullptr;
911 
912             if (newKind == SingletonObject) {
913                 Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->getProto()));
914                 if (!res->splicePrototype(cx, &PlainObject::class_, proto))
915                     return nullptr;
916             } else {
917                 res->setGroup(group);
918             }
919             return res;
920         }
921 
922         // The initial objects registered with a TypeNewScript can't be in the
923         // nursery.
924         if (newKind == GenericObject)
925             newKind = TenuredObject;
926 
927         // Not enough objects with this group have been created yet, so make a
928         // plain object and register it with the group. Use the maximum number
929         // of fixed slots, as is also required by the TypeNewScript.
930         gc::AllocKind allocKind = GuessObjectGCKind(NativeObject::MAX_FIXED_SLOTS);
931         PlainObject* res = NewObjectWithGroup<PlainObject>(cx, group, allocKind, newKind);
932         if (!res)
933             return nullptr;
934 
935         // Make sure group->newScript is still there.
936         if (newKind != SingletonObject && group->newScript())
937             group->newScript()->registerNewObject(res);
938 
939         return res;
940     }
941 
942     gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
943 
944     if (newKind == SingletonObject) {
945         Rooted<TaggedProto> protoRoot(cx, group->proto());
946         return NewObjectWithGivenTaggedProto(cx, &PlainObject::class_, protoRoot, allocKind, newKind);
947     }
948     return NewObjectWithGroup<PlainObject>(cx, group, allocKind, newKind);
949 }
950 
951 JSObject*
CreateThisForFunctionWithProto(JSContext * cx,HandleObject callee,HandleObject newTarget,HandleObject proto,NewObjectKind newKind)952 js::CreateThisForFunctionWithProto(JSContext* cx, HandleObject callee, HandleObject newTarget,
953                                    HandleObject proto, NewObjectKind newKind /* = GenericObject */)
954 {
955     RootedObject res(cx);
956 
957     if (proto) {
958         RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
959                                                                  newTarget));
960         if (!group)
961             return nullptr;
962 
963         if (group->newScript() && !group->newScript()->analyzed()) {
964             bool regenerate;
965             if (!group->newScript()->maybeAnalyze(cx, group, &regenerate))
966                 return nullptr;
967             if (regenerate) {
968                 // The script was analyzed successfully and may have changed
969                 // the new type table, so refetch the group.
970                 group = ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
971                                                      newTarget);
972                 MOZ_ASSERT(group && group->newScript());
973             }
974         }
975 
976         res = CreateThisForFunctionWithGroup(cx, group, newKind);
977     } else {
978         res = NewBuiltinClassInstance<PlainObject>(cx, newKind);
979     }
980 
981     if (res) {
982         JSScript* script = callee->as<JSFunction>().getOrCreateScript(cx);
983         if (!script)
984             return nullptr;
985         TypeScript::SetThis(cx, script, TypeSet::ObjectType(res));
986     }
987 
988     return res;
989 }
990 
991 bool
GetPrototypeFromConstructor(JSContext * cx,HandleObject newTarget,MutableHandleObject proto)992 js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, MutableHandleObject proto)
993 {
994     RootedValue protov(cx);
995     if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov))
996         return false;
997     proto.set(protov.isObject() ? &protov.toObject() : nullptr);
998     return true;
999 }
1000 
1001 bool
GetPrototypeFromCallableConstructor(JSContext * cx,const CallArgs & args,MutableHandleObject proto)1002 js::GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, MutableHandleObject proto)
1003 {
1004     RootedObject newTarget(cx);
1005     if (args.isConstructing())
1006         newTarget = &args.newTarget().toObject();
1007     else
1008         newTarget = &args.callee();
1009     return GetPrototypeFromConstructor(cx, newTarget, proto);
1010 }
1011 
1012 JSObject*
CreateThisForFunction(JSContext * cx,HandleObject callee,HandleObject newTarget,NewObjectKind newKind)1013 js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget,
1014                           NewObjectKind newKind)
1015 {
1016     RootedObject proto(cx);
1017     if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
1018         return nullptr;
1019 
1020     JSObject* obj = CreateThisForFunctionWithProto(cx, callee, newTarget, proto, newKind);
1021 
1022     if (obj && newKind == SingletonObject) {
1023         RootedPlainObject nobj(cx, &obj->as<PlainObject>());
1024 
1025         /* Reshape the singleton before passing it as the 'this' value. */
1026         NativeObject::clear(cx, nobj);
1027 
1028         JSScript* calleeScript = callee->as<JSFunction>().nonLazyScript();
1029         TypeScript::SetThis(cx, calleeScript, TypeSet::ObjectType(nobj));
1030 
1031         return nobj;
1032     }
1033 
1034     return obj;
1035 }
1036 
1037 /* static */ bool
nonNativeSetProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result)1038 JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
1039                                HandleValue receiver, ObjectOpResult& result)
1040 {
1041     RootedValue value(cx, v);
1042     if (MOZ_UNLIKELY(obj->watched())) {
1043         WatchpointMap* wpmap = cx->compartment()->watchpointMap;
1044         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &value))
1045             return false;
1046     }
1047     return obj->getOps()->setProperty(cx, obj, id, value, receiver, result);
1048 }
1049 
1050 /* static */ bool
nonNativeSetElement(JSContext * cx,HandleObject obj,uint32_t index,HandleValue v,HandleValue receiver,ObjectOpResult & result)1051 JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v,
1052                               HandleValue receiver, ObjectOpResult& result)
1053 {
1054     RootedId id(cx);
1055     if (!IndexToId(cx, index, &id))
1056         return false;
1057     return nonNativeSetProperty(cx, obj, id, v, receiver, result);
1058 }
1059 
1060 JS_FRIEND_API(bool)
JS_CopyPropertyFrom(JSContext * cx,HandleId id,HandleObject target,HandleObject obj,PropertyCopyBehavior copyBehavior)1061 JS_CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target,
1062                     HandleObject obj, PropertyCopyBehavior copyBehavior)
1063 {
1064     // |obj| and |cx| are generally not same-compartment with |target| here.
1065     assertSameCompartment(cx, obj, id);
1066     Rooted<JSPropertyDescriptor> desc(cx);
1067 
1068     if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
1069         return false;
1070     MOZ_ASSERT(desc.object());
1071 
1072     // Silently skip JSGetterOp/JSSetterOp-implemented accessors.
1073     if (desc.getter() && !desc.hasGetterObject())
1074         return true;
1075     if (desc.setter() && !desc.hasSetterObject())
1076         return true;
1077 
1078     if (copyBehavior == MakeNonConfigurableIntoConfigurable) {
1079         // Mask off the JSPROP_PERMANENT bit.
1080         desc.attributesRef() &= ~JSPROP_PERMANENT;
1081     }
1082 
1083     JSAutoCompartment ac(cx, target);
1084     RootedId wrappedId(cx, id);
1085     if (!cx->compartment()->wrap(cx, &desc))
1086         return false;
1087 
1088     return DefineProperty(cx, target, wrappedId, desc);
1089 }
1090 
1091 JS_FRIEND_API(bool)
JS_CopyPropertiesFrom(JSContext * cx,HandleObject target,HandleObject obj)1092 JS_CopyPropertiesFrom(JSContext* cx, HandleObject target, HandleObject obj)
1093 {
1094     JSAutoCompartment ac(cx, obj);
1095 
1096     AutoIdVector props(cx);
1097     if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props))
1098         return false;
1099 
1100     for (size_t i = 0; i < props.length(); ++i) {
1101         if (!JS_CopyPropertyFrom(cx, props[i], target, obj))
1102             return false;
1103     }
1104 
1105     return true;
1106 }
1107 
1108 static bool
CopyProxyObject(JSContext * cx,Handle<ProxyObject * > from,Handle<ProxyObject * > to)1109 CopyProxyObject(JSContext* cx, Handle<ProxyObject*> from, Handle<ProxyObject*> to)
1110 {
1111     MOZ_ASSERT(from->getClass() == to->getClass());
1112 
1113     if (from->is<WrapperObject>() &&
1114         (Wrapper::wrapperHandler(from)->flags() &
1115          Wrapper::CROSS_COMPARTMENT))
1116     {
1117         to->setCrossCompartmentPrivate(GetProxyPrivate(from));
1118     } else {
1119         RootedValue v(cx, GetProxyPrivate(from));
1120         if (!cx->compartment()->wrap(cx, &v))
1121             return false;
1122         to->setSameCompartmentPrivate(v);
1123     }
1124 
1125     RootedValue v(cx);
1126     for (size_t n = 0; n < PROXY_EXTRA_SLOTS; n++) {
1127         v = GetProxyExtra(from, n);
1128         if (!cx->compartment()->wrap(cx, &v))
1129             return false;
1130         SetProxyExtra(to, n, v);
1131     }
1132 
1133     return true;
1134 }
1135 
1136 JSObject*
CloneObject(JSContext * cx,HandleObject obj,Handle<js::TaggedProto> proto)1137 js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
1138 {
1139     if (!obj->isNative() && !obj->is<ProxyObject>()) {
1140         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
1141                              JSMSG_CANT_CLONE_OBJECT);
1142         return nullptr;
1143     }
1144 
1145     RootedObject clone(cx);
1146     if (obj->isNative()) {
1147         clone = NewObjectWithGivenTaggedProto(cx, obj->getClass(), proto);
1148         if (!clone)
1149             return nullptr;
1150 
1151         if (clone->is<JSFunction>() && (obj->compartment() != clone->compartment())) {
1152             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
1153                                  JSMSG_CANT_CLONE_OBJECT);
1154             return nullptr;
1155         }
1156 
1157         if (obj->as<NativeObject>().hasPrivate())
1158             clone->as<NativeObject>().setPrivate(obj->as<NativeObject>().getPrivate());
1159     } else {
1160         ProxyOptions options;
1161         options.setClass(obj->getClass());
1162 
1163         clone = ProxyObject::New(cx, GetProxyHandler(obj), JS::NullHandleValue, proto, options);
1164         if (!clone)
1165             return nullptr;
1166 
1167         if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>()))
1168             return nullptr;
1169     }
1170 
1171     return clone;
1172 }
1173 
1174 static bool
GetScriptArrayObjectElements(JSContext * cx,HandleObject obj,AutoValueVector & values)1175 GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, AutoValueVector& values)
1176 {
1177     MOZ_ASSERT(!obj->isSingleton());
1178     MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
1179 
1180     size_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
1181     if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
1182         return false;
1183 
1184     if (obj->nonProxyIsExtensible()) {
1185         MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().slotSpan() == 0);
1186 
1187         size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
1188         for (size_t i = 0; i < initlen; i++)
1189             values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i));
1190     } else {
1191         // Call site objects are frozen before they escape to script, which
1192         // converts their dense elements into data properties.
1193         ArrayObject* aobj = &obj->as<ArrayObject>();
1194         for (Shape::Range<NoGC> r(aobj->lastProperty()); !r.empty(); r.popFront()) {
1195             Shape& shape = r.front();
1196             if (shape.propid() == NameToId(cx->names().length))
1197                 continue;
1198             MOZ_ASSERT(shape.isDataDescriptor());
1199 
1200             // The 'raw' property is added before freezing call site objects.
1201             // After an XDR or deep clone the script object will no longer be
1202             // frozen, and the two objects will be connected again the first
1203             // time the JSOP_CALLSITEOBJ executes.
1204             if (shape.propid() == NameToId(cx->names().raw))
1205                 continue;
1206 
1207             uint32_t index = JSID_TO_INT(shape.propid());
1208             values[index].set(aobj->getSlot(shape.slot()));
1209         }
1210     }
1211 
1212     return true;
1213 }
1214 
1215 static bool
GetScriptPlainObjectProperties(JSContext * cx,HandleObject obj,MutableHandle<IdValueVector> properties)1216 GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj,
1217                                MutableHandle<IdValueVector> properties)
1218 {
1219     if (obj->is<PlainObject>()) {
1220         PlainObject* nobj = &obj->as<PlainObject>();
1221 
1222         if (!properties.appendN(IdValuePair(), nobj->slotSpan()))
1223             return false;
1224 
1225         for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
1226             Shape& shape = r.front();
1227             MOZ_ASSERT(shape.isDataDescriptor());
1228             uint32_t slot = shape.slot();
1229             properties[slot].get().id = shape.propid();
1230             properties[slot].get().value = nobj->getSlot(slot);
1231         }
1232 
1233         for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
1234             Value v = nobj->getDenseElement(i);
1235             if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v)))
1236                 return false;
1237         }
1238 
1239         return true;
1240     }
1241 
1242     if (obj->is<UnboxedPlainObject>()) {
1243         UnboxedPlainObject* nobj = &obj->as<UnboxedPlainObject>();
1244 
1245         const UnboxedLayout& layout = nobj->layout();
1246         if (!properties.appendN(IdValuePair(), layout.properties().length()))
1247             return false;
1248 
1249         for (size_t i = 0; i < layout.properties().length(); i++) {
1250             const UnboxedLayout::Property& property = layout.properties()[i];
1251             properties[i].get().id = NameToId(property.name);
1252             properties[i].get().value = nobj->getValue(property);
1253         }
1254 
1255         return true;
1256     }
1257 
1258     MOZ_CRASH("Bad object kind");
1259 }
1260 
1261 static bool
DeepCloneValue(JSContext * cx,Value * vp,NewObjectKind newKind)1262 DeepCloneValue(JSContext* cx, Value* vp, NewObjectKind newKind)
1263 {
1264     if (vp->isObject()) {
1265         RootedObject obj(cx, &vp->toObject());
1266         obj = DeepCloneObjectLiteral(cx, obj, newKind);
1267         if (!obj)
1268             return false;
1269         vp->setObject(*obj);
1270     }
1271     return true;
1272 }
1273 
1274 JSObject*
DeepCloneObjectLiteral(JSContext * cx,HandleObject obj,NewObjectKind newKind)1275 js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKind)
1276 {
1277     /* NB: Keep this in sync with XDRObjectLiteral. */
1278     MOZ_ASSERT_IF(obj->isSingleton(),
1279                   JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
1280     MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() ||
1281                obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
1282     MOZ_ASSERT(newKind != SingletonObject);
1283 
1284     if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
1285         AutoValueVector values(cx);
1286         if (!GetScriptArrayObjectElements(cx, obj, values))
1287             return nullptr;
1288 
1289         // Deep clone any elements.
1290         for (uint32_t i = 0; i < values.length(); ++i) {
1291             if (!DeepCloneValue(cx, values[i].address(), newKind))
1292                 return nullptr;
1293         }
1294 
1295         ObjectGroup::NewArrayKind arrayKind = ObjectGroup::NewArrayKind::Normal;
1296         if (obj->is<ArrayObject>() && obj->as<ArrayObject>().denseElementsAreCopyOnWrite())
1297             arrayKind = ObjectGroup::NewArrayKind::CopyOnWrite;
1298 
1299         return ObjectGroup::newArrayObject(cx, values.begin(), values.length(), newKind,
1300                                            arrayKind);
1301     }
1302 
1303     Rooted<IdValueVector> properties(cx, IdValueVector(cx));
1304     if (!GetScriptPlainObjectProperties(cx, obj, &properties))
1305         return nullptr;
1306 
1307     for (size_t i = 0; i < properties.length(); i++) {
1308         if (!DeepCloneValue(cx, &properties[i].get().value, newKind))
1309             return nullptr;
1310     }
1311 
1312     if (obj->isSingleton())
1313         newKind = SingletonObject;
1314 
1315     return ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind);
1316 }
1317 
1318 static bool
InitializePropertiesFromCompatibleNativeObject(JSContext * cx,HandleNativeObject dst,HandleNativeObject src)1319 InitializePropertiesFromCompatibleNativeObject(JSContext* cx,
1320                                                HandleNativeObject dst,
1321                                                HandleNativeObject src)
1322 {
1323     assertSameCompartment(cx, src, dst);
1324     MOZ_ASSERT(src->getClass() == dst->getClass());
1325     MOZ_ASSERT(dst->lastProperty()->getObjectFlags() == 0);
1326     MOZ_ASSERT(!src->isSingleton());
1327     MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots());
1328 
1329     if (!dst->ensureElements(cx, src->getDenseInitializedLength()))
1330         return false;
1331 
1332     uint32_t initialized = src->getDenseInitializedLength();
1333     for (uint32_t i = 0; i < initialized; ++i) {
1334         dst->setDenseInitializedLength(i + 1);
1335         dst->initDenseElement(i, src->getDenseElement(i));
1336     }
1337 
1338     MOZ_ASSERT(!src->hasPrivate());
1339     RootedShape shape(cx);
1340     if (src->getProto() == dst->getProto()) {
1341         shape = src->lastProperty();
1342     } else {
1343         // We need to generate a new shape for dst that has dst's proto but all
1344         // the property information from src.  Note that we asserted above that
1345         // dst's object flags are 0.
1346         shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->getTaggedProto(),
1347                                             dst->numFixedSlots(), 0);
1348         if (!shape)
1349             return false;
1350 
1351         // Get an in-order list of the shapes in the src object.
1352         Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
1353         for (Shape::Range<NoGC> r(src->lastProperty()); !r.empty(); r.popFront()) {
1354             if (!shapes.append(&r.front()))
1355                 return false;
1356         }
1357         Reverse(shapes.begin(), shapes.end());
1358 
1359         for (Shape* shape : shapes) {
1360             Rooted<StackShape> child(cx, StackShape(shape));
1361             shape = cx->compartment()->propertyTree.getChild(cx, shape, child);
1362             if (!shape)
1363                 return false;
1364         }
1365     }
1366     size_t span = shape->slotSpan();
1367     if (!dst->setLastProperty(cx, shape))
1368         return false;
1369     for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++)
1370         dst->setSlot(i, src->getSlot(i));
1371 
1372     return true;
1373 }
1374 
1375 JS_FRIEND_API(bool)
JS_InitializePropertiesFromCompatibleNativeObject(JSContext * cx,HandleObject dst,HandleObject src)1376 JS_InitializePropertiesFromCompatibleNativeObject(JSContext* cx,
1377                                                   HandleObject dst,
1378                                                   HandleObject src)
1379 {
1380     return InitializePropertiesFromCompatibleNativeObject(cx,
1381                                                           dst.as<NativeObject>(),
1382                                                           src.as<NativeObject>());
1383 }
1384 
1385 template<XDRMode mode>
1386 bool
XDRObjectLiteral(XDRState<mode> * xdr,MutableHandleObject obj)1387 js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
1388 {
1389     /* NB: Keep this in sync with DeepCloneObjectLiteral. */
1390 
1391     JSContext* cx = xdr->cx();
1392     MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(),
1393                   JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
1394 
1395     // Distinguish between objects and array classes.
1396     uint32_t isArray = 0;
1397     {
1398         if (mode == XDR_ENCODE) {
1399             MOZ_ASSERT(obj->is<PlainObject>() ||
1400                        obj->is<UnboxedPlainObject>() ||
1401                        obj->is<ArrayObject>() ||
1402                        obj->is<UnboxedArrayObject>());
1403             isArray = (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) ? 1 : 0;
1404         }
1405 
1406         if (!xdr->codeUint32(&isArray))
1407             return false;
1408     }
1409 
1410     RootedValue tmpValue(cx), tmpIdValue(cx);
1411     RootedId tmpId(cx);
1412 
1413     if (isArray) {
1414         AutoValueVector values(cx);
1415         if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, obj, values))
1416             return false;
1417 
1418         uint32_t initialized;
1419         if (mode == XDR_ENCODE)
1420             initialized = values.length();
1421         if (!xdr->codeUint32(&initialized))
1422             return false;
1423         if (mode == XDR_DECODE && !values.appendN(MagicValue(JS_ELEMENTS_HOLE), initialized))
1424             return false;
1425 
1426         // Recursively copy dense elements.
1427         for (unsigned i = 0; i < initialized; i++) {
1428             if (!xdr->codeConstValue(values[i]))
1429                 return false;
1430         }
1431 
1432         uint32_t copyOnWrite;
1433         if (mode == XDR_ENCODE)
1434             copyOnWrite = obj->is<ArrayObject>() &&
1435                           obj->as<ArrayObject>().denseElementsAreCopyOnWrite();
1436         if (!xdr->codeUint32(&copyOnWrite))
1437             return false;
1438 
1439         if (mode == XDR_DECODE) {
1440             ObjectGroup::NewArrayKind arrayKind = copyOnWrite
1441                                                   ? ObjectGroup::NewArrayKind::CopyOnWrite
1442                                                   : ObjectGroup::NewArrayKind::Normal;
1443             obj.set(ObjectGroup::newArrayObject(cx, values.begin(), values.length(),
1444                                                 TenuredObject, arrayKind));
1445             if (!obj)
1446                 return false;
1447         }
1448 
1449         return true;
1450     }
1451 
1452     // Code the properties in the object.
1453     Rooted<IdValueVector> properties(cx, IdValueVector(cx));
1454     if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(cx, obj, &properties))
1455         return false;
1456 
1457     uint32_t nproperties = properties.length();
1458     if (!xdr->codeUint32(&nproperties))
1459         return false;
1460 
1461     if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties))
1462         return false;
1463 
1464     for (size_t i = 0; i < nproperties; i++) {
1465         if (mode == XDR_ENCODE) {
1466             tmpIdValue = IdToValue(properties[i].get().id);
1467             tmpValue = properties[i].get().value;
1468         }
1469 
1470         if (!xdr->codeConstValue(&tmpIdValue) || !xdr->codeConstValue(&tmpValue))
1471             return false;
1472 
1473         if (mode == XDR_DECODE) {
1474             if (!ValueToId<CanGC>(cx, tmpIdValue, &tmpId))
1475                 return false;
1476             properties[i].get().id = tmpId;
1477             properties[i].get().value = tmpValue;
1478         }
1479     }
1480 
1481     // Code whether the object is a singleton.
1482     uint32_t isSingleton;
1483     if (mode == XDR_ENCODE)
1484         isSingleton = obj->isSingleton() ? 1 : 0;
1485     if (!xdr->codeUint32(&isSingleton))
1486         return false;
1487 
1488     if (mode == XDR_DECODE) {
1489         NewObjectKind newKind = isSingleton ? SingletonObject : TenuredObject;
1490         obj.set(ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind));
1491         if (!obj)
1492             return false;
1493     }
1494 
1495     return true;
1496 }
1497 
1498 template bool
1499 js::XDRObjectLiteral(XDRState<XDR_ENCODE>* xdr, MutableHandleObject obj);
1500 
1501 template bool
1502 js::XDRObjectLiteral(XDRState<XDR_DECODE>* xdr, MutableHandleObject obj);
1503 
1504 bool
fillInAfterSwap(JSContext * cx,const Vector<Value> & values,void * priv)1505 NativeObject::fillInAfterSwap(JSContext* cx, const Vector<Value>& values, void* priv)
1506 {
1507     // This object has just been swapped with some other object, and its shape
1508     // no longer reflects its allocated size. Correct this information and
1509     // fill the slots in with the specified values.
1510     MOZ_ASSERT(slotSpan() == values.length());
1511 
1512     // Make sure the shape's numFixedSlots() is correct.
1513     size_t nfixed = gc::GetGCKindSlots(asTenured().getAllocKind(), getClass());
1514     if (nfixed != shape_->numFixedSlots()) {
1515         if (!generateOwnShape(cx))
1516             return false;
1517         shape_->setNumFixedSlots(nfixed);
1518     }
1519 
1520     if (hasPrivate())
1521         setPrivate(priv);
1522     else
1523         MOZ_ASSERT(!priv);
1524 
1525     if (slots_) {
1526         js_free(slots_);
1527         slots_ = nullptr;
1528     }
1529 
1530     if (size_t ndynamic = dynamicSlotsCount(nfixed, values.length(), getClass())) {
1531         slots_ = cx->zone()->pod_malloc<HeapSlot>(ndynamic);
1532         if (!slots_)
1533             return false;
1534         Debug_SetSlotRangeToCrashOnTouch(slots_, ndynamic);
1535     }
1536 
1537     initSlotRange(0, values.begin(), values.length());
1538     return true;
1539 }
1540 
1541 void
fixDictionaryShapeAfterSwap()1542 JSObject::fixDictionaryShapeAfterSwap()
1543 {
1544     // Dictionary shapes can point back to their containing objects, so after
1545     // swapping the guts of those objects fix the pointers up.
1546     if (isNative() && as<NativeObject>().inDictionaryMode())
1547         as<NativeObject>().shape_->listp = &as<NativeObject>().shape_;
1548 }
1549 
1550 /* Use this method with extreme caution. It trades the guts of two objects. */
1551 bool
swap(JSContext * cx,HandleObject a,HandleObject b)1552 JSObject::swap(JSContext* cx, HandleObject a, HandleObject b)
1553 {
1554     // Ensure swap doesn't cause a finalizer to not be run.
1555     MOZ_ASSERT(IsBackgroundFinalized(a->asTenured().getAllocKind()) ==
1556                IsBackgroundFinalized(b->asTenured().getAllocKind()));
1557     MOZ_ASSERT(a->compartment() == b->compartment());
1558 
1559     AutoEnterOOMUnsafeRegion oomUnsafe;
1560 
1561     AutoCompartment ac(cx, a);
1562 
1563     if (!a->getGroup(cx))
1564         oomUnsafe.crash("JSObject::swap");
1565     if (!b->getGroup(cx))
1566         oomUnsafe.crash("JSObject::swap");
1567 
1568     /*
1569      * Neither object may be in the nursery, but ensure we update any embedded
1570      * nursery pointers in either object.
1571      */
1572     MOZ_ASSERT(!IsInsideNursery(a) && !IsInsideNursery(b));
1573     cx->runtime()->gc.storeBuffer.putWholeCell(a);
1574     cx->runtime()->gc.storeBuffer.putWholeCell(b);
1575 
1576     unsigned r = NotifyGCPreSwap(a, b);
1577 
1578     // Do the fundamental swapping of the contents of two objects.
1579     MOZ_ASSERT(a->compartment() == b->compartment());
1580     MOZ_ASSERT(a->is<JSFunction>() == b->is<JSFunction>());
1581 
1582     // Don't try to swap functions with different sizes.
1583     MOZ_ASSERT_IF(a->is<JSFunction>(), a->tenuredSizeOfThis() == b->tenuredSizeOfThis());
1584 
1585     // Watch for oddball objects that have special organizational issues and
1586     // can't be swapped.
1587     MOZ_ASSERT(!a->is<RegExpObject>() && !b->is<RegExpObject>());
1588     MOZ_ASSERT(!a->is<ArrayObject>() && !b->is<ArrayObject>());
1589     MOZ_ASSERT(!a->is<ArrayBufferObject>() && !b->is<ArrayBufferObject>());
1590     MOZ_ASSERT(!a->is<TypedArrayObject>() && !b->is<TypedArrayObject>());
1591     MOZ_ASSERT(!a->is<TypedObject>() && !b->is<TypedObject>());
1592 
1593     if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis()) {
1594         // When both objects are the same size, just do a plain swap of their
1595         // contents.
1596         size_t size = a->tenuredSizeOfThis();
1597 
1598         char tmp[mozilla::tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::value];
1599         MOZ_ASSERT(size <= sizeof(tmp));
1600 
1601         js_memcpy(tmp, a, size);
1602         js_memcpy(a, b, size);
1603         js_memcpy(b, tmp, size);
1604 
1605         a->fixDictionaryShapeAfterSwap();
1606         b->fixDictionaryShapeAfterSwap();
1607     } else {
1608         // Avoid GC in here to avoid confusing the tracing code with our
1609         // intermediate state.
1610         AutoSuppressGC suppress(cx);
1611 
1612         // When the objects have different sizes, they will have different
1613         // numbers of fixed slots before and after the swap, so the slots for
1614         // native objects will need to be rearranged.
1615         NativeObject* na = a->isNative() ? &a->as<NativeObject>() : nullptr;
1616         NativeObject* nb = b->isNative() ? &b->as<NativeObject>() : nullptr;
1617 
1618         // Remember the original values from the objects.
1619         Vector<Value> avals(cx);
1620         void* apriv = nullptr;
1621         if (na) {
1622             apriv = na->hasPrivate() ? na->getPrivate() : nullptr;
1623             for (size_t i = 0; i < na->slotSpan(); i++) {
1624                 if (!avals.append(na->getSlot(i)))
1625                     oomUnsafe.crash("JSObject::swap");
1626             }
1627         }
1628         Vector<Value> bvals(cx);
1629         void* bpriv = nullptr;
1630         if (nb) {
1631             bpriv = nb->hasPrivate() ? nb->getPrivate() : nullptr;
1632             for (size_t i = 0; i < nb->slotSpan(); i++) {
1633                 if (!bvals.append(nb->getSlot(i)))
1634                     oomUnsafe.crash("JSObject::swap");
1635             }
1636         }
1637 
1638         // Swap the main fields of the objects, whether they are native objects or proxies.
1639         char tmp[sizeof(JSObject_Slots0)];
1640         js_memcpy(&tmp, a, sizeof tmp);
1641         js_memcpy(a, b, sizeof tmp);
1642         js_memcpy(b, &tmp, sizeof tmp);
1643 
1644         a->fixDictionaryShapeAfterSwap();
1645         b->fixDictionaryShapeAfterSwap();
1646 
1647         if (na && !b->as<NativeObject>().fillInAfterSwap(cx, avals, apriv))
1648             oomUnsafe.crash("fillInAfterSwap");
1649         if (nb && !a->as<NativeObject>().fillInAfterSwap(cx, bvals, bpriv))
1650             oomUnsafe.crash("fillInAfterSwap");
1651     }
1652 
1653     // Swapping the contents of two objects invalidates type sets which contain
1654     // either of the objects, so mark all such sets as unknown.
1655     MarkObjectGroupUnknownProperties(cx, a->group());
1656     MarkObjectGroupUnknownProperties(cx, b->group());
1657 
1658     /*
1659      * We need a write barrier here. If |a| was marked and |b| was not, then
1660      * after the swap, |b|'s guts would never be marked. The write barrier
1661      * solves this.
1662      *
1663      * Normally write barriers happen before the write. However, that's not
1664      * necessary here because nothing is being destroyed. We're just swapping.
1665      */
1666     JS::Zone* zone = a->zone();
1667     if (zone->needsIncrementalBarrier()) {
1668         a->traceChildren(zone->barrierTracer());
1669         b->traceChildren(zone->barrierTracer());
1670     }
1671 
1672     NotifyGCPostSwap(a, b, r);
1673     return true;
1674 }
1675 
1676 static bool
DefineStandardSlot(JSContext * cx,HandleObject obj,JSProtoKey key,JSAtom * atom,HandleValue v,uint32_t attrs,bool & named)1677 DefineStandardSlot(JSContext* cx, HandleObject obj, JSProtoKey key, JSAtom* atom,
1678                    HandleValue v, uint32_t attrs, bool& named)
1679 {
1680     RootedId id(cx, AtomToId(atom));
1681 
1682     if (key != JSProto_Null) {
1683         /*
1684          * Initializing an actual standard class on a global object. If the
1685          * property is not yet present, force it into a new one bound to a
1686          * reserved slot. Otherwise, go through the normal property path.
1687          */
1688         Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
1689 
1690         if (!global->lookup(cx, id)) {
1691             global->setConstructorPropertySlot(key, v);
1692 
1693             uint32_t slot = GlobalObject::constructorPropertySlot(key);
1694             if (!NativeObject::addProperty(cx, global, id, nullptr, nullptr, slot, attrs, 0))
1695                 return false;
1696 
1697             named = true;
1698             return true;
1699         }
1700     }
1701 
1702     named = DefineProperty(cx, obj, id, v, nullptr, nullptr, attrs);
1703     return named;
1704 }
1705 
1706 static void
SetClassObject(JSObject * obj,JSProtoKey key,JSObject * cobj,JSObject * proto)1707 SetClassObject(JSObject* obj, JSProtoKey key, JSObject* cobj, JSObject* proto)
1708 {
1709     if (!obj->is<GlobalObject>())
1710         return;
1711 
1712     obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
1713     obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
1714 }
1715 
1716 static void
ClearClassObject(JSObject * obj,JSProtoKey key)1717 ClearClassObject(JSObject* obj, JSProtoKey key)
1718 {
1719     if (!obj->is<GlobalObject>())
1720         return;
1721 
1722     obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
1723     obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
1724 }
1725 
1726 static NativeObject*
DefineConstructorAndPrototype(JSContext * cx,HandleObject obj,JSProtoKey key,HandleAtom atom,HandleObject protoProto,const Class * clasp,Native constructor,unsigned nargs,const JSPropertySpec * ps,const JSFunctionSpec * fs,const JSPropertySpec * static_ps,const JSFunctionSpec * static_fs,NativeObject ** ctorp,AllocKind ctorKind)1727 DefineConstructorAndPrototype(JSContext* cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
1728                               HandleObject protoProto, const Class* clasp,
1729                               Native constructor, unsigned nargs,
1730                               const JSPropertySpec* ps, const JSFunctionSpec* fs,
1731                               const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
1732                               NativeObject** ctorp, AllocKind ctorKind)
1733 {
1734     /*
1735      * Create a prototype object for this class.
1736      *
1737      * FIXME: lazy standard (built-in) class initialization and even older
1738      * eager boostrapping code rely on all of these properties:
1739      *
1740      * 1. NewObject attempting to compute a default prototype object when
1741      *    passed null for proto; and
1742      *
1743      * 2. NewObject tolerating no default prototype (null proto slot value)
1744      *    due to this js::InitClass call coming from js::InitFunctionClass on an
1745      *    otherwise-uninitialized global.
1746      *
1747      * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
1748      *    &JSFunction::class_, not a JSObject-sized (smaller) GC-thing.
1749      *
1750      * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
1751      * be &JSFunction::class_ (we could break compatibility easily). But
1752      * fixing (3) is not enough without addressing the bootstrapping dependency
1753      * on (1) and (2).
1754      */
1755 
1756     /*
1757      * Create the prototype object.  (GlobalObject::createBlankPrototype isn't
1758      * used because it won't let us use protoProto as the proto.
1759      */
1760     RootedNativeObject proto(cx, NewNativeObjectWithClassProto(cx, clasp, protoProto, SingletonObject));
1761     if (!proto)
1762         return nullptr;
1763 
1764     /* After this point, control must exit via label bad or out. */
1765     RootedNativeObject ctor(cx);
1766     bool named = false;
1767     bool cached = false;
1768     if (!constructor) {
1769         /*
1770          * Lacking a constructor, name the prototype (e.g., Math) unless this
1771          * class (a) is anonymous, i.e. for internal use only; (b) the class
1772          * of obj (the global object) is has a reserved slot indexed by key;
1773          * and (c) key is not the null key.
1774          */
1775         if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->is<GlobalObject>() ||
1776             key == JSProto_Null)
1777         {
1778             uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
1779                            ? JSPROP_READONLY | JSPROP_PERMANENT
1780                            : 0;
1781             RootedValue value(cx, ObjectValue(*proto));
1782             if (!DefineStandardSlot(cx, obj, key, atom, value, attrs, named))
1783                 goto bad;
1784         }
1785 
1786         ctor = proto;
1787     } else {
1788         RootedFunction fun(cx, NewNativeConstructor(cx, constructor, nargs, atom, ctorKind));
1789         if (!fun)
1790             goto bad;
1791 
1792         /*
1793          * Set the class object early for standard class constructors. Type
1794          * inference may need to access these, and js::GetBuiltinPrototype will
1795          * fail if it tries to do a reentrant reconstruction of the class.
1796          */
1797         if (key != JSProto_Null) {
1798             SetClassObject(obj, key, fun, proto);
1799             cached = true;
1800         }
1801 
1802         RootedValue value(cx, ObjectValue(*fun));
1803         if (!DefineStandardSlot(cx, obj, key, atom, value, 0, named))
1804             goto bad;
1805 
1806         /*
1807          * Optionally construct the prototype object, before the class has
1808          * been fully initialized.  Allow the ctor to replace proto with a
1809          * different object, as is done for operator new.
1810          */
1811         ctor = fun;
1812         if (!LinkConstructorAndPrototype(cx, ctor, proto))
1813             goto bad;
1814 
1815         /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
1816         Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
1817         if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, clasp, tagged))
1818             goto bad;
1819     }
1820 
1821     if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
1822         (ctor != proto && !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs)))
1823     {
1824         goto bad;
1825     }
1826 
1827     /* If this is a standard class, cache its prototype. */
1828     if (!cached && key != JSProto_Null)
1829         SetClassObject(obj, key, ctor, proto);
1830 
1831     if (ctorp)
1832         *ctorp = ctor;
1833     return proto;
1834 
1835 bad:
1836     if (named) {
1837         ObjectOpResult ignored;
1838         RootedId id(cx, AtomToId(atom));
1839 
1840         // XXX FIXME - absurd to call this here; instead define the property last.
1841         DeleteProperty(cx, obj, id, ignored);
1842     }
1843     if (cached)
1844         ClearClassObject(obj, key);
1845     return nullptr;
1846 }
1847 
1848 NativeObject*
InitClass(JSContext * cx,HandleObject obj,HandleObject protoProto_,const Class * clasp,Native constructor,unsigned nargs,const JSPropertySpec * ps,const JSFunctionSpec * fs,const JSPropertySpec * static_ps,const JSFunctionSpec * static_fs,NativeObject ** ctorp,AllocKind ctorKind)1849 js::InitClass(JSContext* cx, HandleObject obj, HandleObject protoProto_,
1850               const Class* clasp, Native constructor, unsigned nargs,
1851               const JSPropertySpec* ps, const JSFunctionSpec* fs,
1852               const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
1853               NativeObject** ctorp, AllocKind ctorKind)
1854 {
1855     RootedObject protoProto(cx, protoProto_);
1856 
1857     /* Check function pointer members. */
1858     MOZ_ASSERT(clasp->getProperty != JS_PropertyStub);
1859     MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub);
1860 
1861     RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
1862     if (!atom)
1863         return nullptr;
1864 
1865     /*
1866      * All instances of the class will inherit properties from the prototype
1867      * object we are about to create (in DefineConstructorAndPrototype), which
1868      * in turn will inherit from protoProto.
1869      *
1870      * When initializing a standard class (other than Object), if protoProto is
1871      * null, default to Object.prototype. The engine's internal uses of
1872      * js::InitClass depend on this nicety.
1873      */
1874     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
1875     if (key != JSProto_Null &&
1876         !protoProto &&
1877         !GetBuiltinPrototype(cx, JSProto_Object, &protoProto))
1878     {
1879         return nullptr;
1880     }
1881 
1882     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
1883                                          ps, fs, static_ps, static_fs, ctorp, ctorKind);
1884 }
1885 
1886 void
fixupAfterMovingGC()1887 JSObject::fixupAfterMovingGC()
1888 {
1889     // For copy-on-write objects that don't own their elements, fix up the
1890     // elements pointer if it points to inline elements in the owning object.
1891     if (is<NativeObject>()) {
1892         NativeObject& obj = as<NativeObject>();
1893         if (obj.denseElementsAreCopyOnWrite()) {
1894             NativeObject* owner = MaybeForwarded(obj.getElementsHeader()->ownerObject().get());
1895             if (owner != &obj && owner->hasFixedElements())
1896                 obj.elements_ = owner->getElementsHeader()->elements();
1897             MOZ_ASSERT(!IsForwarded(obj.getElementsHeader()->ownerObject().get()));
1898         }
1899     }
1900 }
1901 
1902 bool
SetClassAndProto(JSContext * cx,HandleObject obj,const Class * clasp,Handle<js::TaggedProto> proto)1903 js::SetClassAndProto(JSContext* cx, HandleObject obj,
1904                      const Class* clasp, Handle<js::TaggedProto> proto)
1905 {
1906     // Regenerate the object's shape. If the object is a proto (isDelegate()),
1907     // we also need to regenerate shapes for all of the objects along the old
1908     // prototype chain, in case any entries were filled by looking up through
1909     // obj. Stop when a non-native object is found, prototype lookups will not
1910     // be cached across these.
1911     //
1912     // How this shape change is done is very delicate; the change can be made
1913     // either by marking the object's prototype as uncacheable (such that the
1914     // JIT'ed ICs cannot assume the shape determines the prototype) or by just
1915     // generating a new shape for the object. Choosing the former is bad if the
1916     // object is on the prototype chain of other objects, as the uncacheable
1917     // prototype can inhibit iterator caches on those objects and slow down
1918     // prototype accesses. Choosing the latter is bad if there are many similar
1919     // objects to this one which will have their prototype mutated, as the
1920     // generateOwnShape forces the object into dictionary mode and similar
1921     // property lineages will be repeatedly cloned.
1922     //
1923     // :XXX: bug 707717 make this code less brittle.
1924     RootedObject oldproto(cx, obj);
1925     while (oldproto && oldproto->isNative()) {
1926         if (oldproto->isSingleton()) {
1927             if (!oldproto->as<NativeObject>().generateOwnShape(cx))
1928                 return false;
1929         } else {
1930             if (!oldproto->setUncacheableProto(cx))
1931                 return false;
1932         }
1933         if (!obj->isDelegate()) {
1934             // If |obj| is not a proto of another object, we don't need to
1935             // reshape the whole proto chain.
1936             MOZ_ASSERT(obj == oldproto);
1937             break;
1938         }
1939         oldproto = oldproto->getProto();
1940     }
1941 
1942     if (proto.isObject() && !proto.toObject()->setDelegate(cx))
1943         return false;
1944 
1945     if (obj->isSingleton()) {
1946         /*
1947          * Just splice the prototype, but mark the properties as unknown for
1948          * consistent behavior.
1949          */
1950         if (!obj->splicePrototype(cx, clasp, proto))
1951             return false;
1952         MarkObjectGroupUnknownProperties(cx, obj->group());
1953         return true;
1954     }
1955 
1956     if (proto.isObject()) {
1957         RootedObject protoObj(cx, proto.toObject());
1958         if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
1959             return false;
1960     }
1961 
1962     ObjectGroup* group = ObjectGroup::defaultNewGroup(cx, clasp, proto);
1963     if (!group)
1964         return false;
1965 
1966     /*
1967      * Setting __proto__ on an object that has escaped and may be referenced by
1968      * other heap objects can only be done if the properties of both objects
1969      * are unknown. Type sets containing this object will contain the original
1970      * type but not the new type of the object, so we need to treat all such
1971      * type sets as unknown.
1972      */
1973     MarkObjectGroupUnknownProperties(cx, obj->group());
1974     MarkObjectGroupUnknownProperties(cx, group);
1975 
1976     obj->setGroup(group);
1977 
1978     return true;
1979 }
1980 
1981 /* static */ bool
changeToSingleton(JSContext * cx,HandleObject obj)1982 JSObject::changeToSingleton(JSContext* cx, HandleObject obj)
1983 {
1984     MOZ_ASSERT(!obj->isSingleton());
1985 
1986     MarkObjectGroupUnknownProperties(cx, obj->group());
1987 
1988     ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
1989                                                          obj->getTaggedProto());
1990     if (!group)
1991         return false;
1992 
1993     obj->group_ = group;
1994     return true;
1995 }
1996 
1997 static bool
MaybeResolveConstructor(ExclusiveContext * cxArg,Handle<GlobalObject * > global,JSProtoKey key)1998 MaybeResolveConstructor(ExclusiveContext* cxArg, Handle<GlobalObject*> global, JSProtoKey key)
1999 {
2000     if (global->isStandardClassResolved(key))
2001         return true;
2002     if (!cxArg->shouldBeJSContext())
2003         return false;
2004 
2005     JSContext* cx = cxArg->asJSContext();
2006     return GlobalObject::resolveConstructor(cx, global, key);
2007 }
2008 
2009 bool
GetBuiltinConstructor(ExclusiveContext * cx,JSProtoKey key,MutableHandleObject objp)2010 js::GetBuiltinConstructor(ExclusiveContext* cx, JSProtoKey key, MutableHandleObject objp)
2011 {
2012     MOZ_ASSERT(key != JSProto_Null);
2013     Rooted<GlobalObject*> global(cx, cx->global());
2014     if (!MaybeResolveConstructor(cx, global, key))
2015         return false;
2016 
2017     objp.set(&global->getConstructor(key).toObject());
2018     return true;
2019 }
2020 
2021 bool
GetBuiltinPrototype(ExclusiveContext * cx,JSProtoKey key,MutableHandleObject protop)2022 js::GetBuiltinPrototype(ExclusiveContext* cx, JSProtoKey key, MutableHandleObject protop)
2023 {
2024     MOZ_ASSERT(key != JSProto_Null);
2025     Rooted<GlobalObject*> global(cx, cx->global());
2026     if (!MaybeResolveConstructor(cx, global, key))
2027         return false;
2028 
2029     protop.set(&global->getPrototype(key).toObject());
2030     return true;
2031 }
2032 
2033 bool
IsStandardPrototype(JSObject * obj,JSProtoKey key)2034 js::IsStandardPrototype(JSObject* obj, JSProtoKey key)
2035 {
2036     GlobalObject& global = obj->global();
2037     Value v = global.getPrototype(key);
2038     return v.isObject() && obj == &v.toObject();
2039 }
2040 
2041 JSProtoKey
IdentifyStandardInstance(JSObject * obj)2042 JS::IdentifyStandardInstance(JSObject* obj)
2043 {
2044     // Note: The prototype shares its JSClass with instances.
2045     MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
2046     JSProtoKey key = StandardProtoKeyOrNull(obj);
2047     if (key != JSProto_Null && !IsStandardPrototype(obj, key))
2048         return key;
2049     return JSProto_Null;
2050 }
2051 
2052 JSProtoKey
IdentifyStandardPrototype(JSObject * obj)2053 JS::IdentifyStandardPrototype(JSObject* obj)
2054 {
2055     // Note: The prototype shares its JSClass with instances.
2056     MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
2057     JSProtoKey key = StandardProtoKeyOrNull(obj);
2058     if (key != JSProto_Null && IsStandardPrototype(obj, key))
2059         return key;
2060     return JSProto_Null;
2061 }
2062 
2063 JSProtoKey
IdentifyStandardInstanceOrPrototype(JSObject * obj)2064 JS::IdentifyStandardInstanceOrPrototype(JSObject* obj)
2065 {
2066     return StandardProtoKeyOrNull(obj);
2067 }
2068 
2069 JSProtoKey
IdentifyStandardConstructor(JSObject * obj)2070 JS::IdentifyStandardConstructor(JSObject* obj)
2071 {
2072     // Note that NATIVE_CTOR does not imply that we are a standard constructor,
2073     // but the converse is true (at least until we start having self-hosted
2074     // constructors for standard classes). This lets us avoid a costly loop for
2075     // many functions (which, depending on the call site, may be the common case).
2076     if (!obj->is<JSFunction>() || !(obj->as<JSFunction>().flags() & JSFunction::NATIVE_CTOR))
2077         return JSProto_Null;
2078 
2079     GlobalObject& global = obj->global();
2080     for (size_t k = 0; k < JSProto_LIMIT; ++k) {
2081         JSProtoKey key = static_cast<JSProtoKey>(k);
2082         if (global.getConstructor(key) == ObjectValue(*obj))
2083             return key;
2084     }
2085 
2086     return JSProto_Null;
2087 }
2088 
2089 bool
isCallable() const2090 JSObject::isCallable() const
2091 {
2092     if (is<JSFunction>())
2093         return true;
2094     return callHook() != nullptr;
2095 }
2096 
2097 bool
isConstructor() const2098 JSObject::isConstructor() const
2099 {
2100     if (is<JSFunction>()) {
2101         const JSFunction& fun = as<JSFunction>();
2102         return fun.isConstructor();
2103     }
2104     return constructHook() != nullptr;
2105 }
2106 
2107 JSNative
callHook() const2108 JSObject::callHook() const
2109 {
2110     const js::Class* clasp = getClass();
2111 
2112     if (clasp->call)
2113         return clasp->call;
2114 
2115     if (is<js::ProxyObject>()) {
2116         const js::ProxyObject& p = as<js::ProxyObject>();
2117         if (p.handler()->isCallable(const_cast<JSObject*>(this)))
2118             return js::proxy_Call;
2119     }
2120     return nullptr;
2121 }
2122 
2123 JSNative
constructHook() const2124 JSObject::constructHook() const
2125 {
2126     const js::Class* clasp = getClass();
2127 
2128     if (clasp->construct)
2129         return clasp->construct;
2130 
2131     if (is<js::ProxyObject>()) {
2132         const js::ProxyObject& p = as<js::ProxyObject>();
2133         if (p.handler()->isConstructor(const_cast<JSObject*>(this)))
2134             return js::proxy_Construct;
2135     }
2136     return nullptr;
2137 }
2138 
2139 bool
LookupProperty(JSContext * cx,HandleObject obj,js::HandleId id,MutableHandleObject objp,MutableHandleShape propp)2140 js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id,
2141                    MutableHandleObject objp, MutableHandleShape propp)
2142 {
2143     /* NB: The logic of lookupProperty is implicitly reflected in
2144      *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
2145      *     If this changes, please remember to update the logic there as well.
2146      */
2147     if (LookupPropertyOp op = obj->getOps()->lookupProperty)
2148         return op(cx, obj, id, objp, propp);
2149     return LookupPropertyInline<CanGC>(cx, obj.as<NativeObject>(), id, objp, propp);
2150 }
2151 
2152 bool
LookupName(JSContext * cx,HandlePropertyName name,HandleObject scopeChain,MutableHandleObject objp,MutableHandleObject pobjp,MutableHandleShape propp)2153 js::LookupName(JSContext* cx, HandlePropertyName name, HandleObject scopeChain,
2154                MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
2155 {
2156     RootedId id(cx, NameToId(name));
2157 
2158     for (RootedObject scope(cx, scopeChain); scope; scope = scope->enclosingScope()) {
2159         if (!LookupProperty(cx, scope, id, pobjp, propp))
2160             return false;
2161         if (propp) {
2162             objp.set(scope);
2163             return true;
2164         }
2165     }
2166 
2167     objp.set(nullptr);
2168     pobjp.set(nullptr);
2169     propp.set(nullptr);
2170     return true;
2171 }
2172 
2173 bool
LookupNameNoGC(JSContext * cx,PropertyName * name,JSObject * scopeChain,JSObject ** objp,JSObject ** pobjp,Shape ** propp)2174 js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* scopeChain,
2175                    JSObject** objp, JSObject** pobjp, Shape** propp)
2176 {
2177     AutoAssertNoException nogc(cx);
2178 
2179     MOZ_ASSERT(!*objp && !*pobjp && !*propp);
2180 
2181     for (JSObject* scope = scopeChain; scope; scope = scope->enclosingScope()) {
2182         if (scope->getOps()->lookupProperty)
2183             return false;
2184         if (!LookupPropertyInline<NoGC>(cx, &scope->as<NativeObject>(), NameToId(name), pobjp, propp))
2185             return false;
2186         if (*propp) {
2187             *objp = scope;
2188             return true;
2189         }
2190     }
2191 
2192     return true;
2193 }
2194 
2195 bool
LookupNameWithGlobalDefault(JSContext * cx,HandlePropertyName name,HandleObject scopeChain,MutableHandleObject objp)2196 js::LookupNameWithGlobalDefault(JSContext* cx, HandlePropertyName name, HandleObject scopeChain,
2197                                 MutableHandleObject objp)
2198 {
2199     RootedId id(cx, NameToId(name));
2200 
2201     RootedObject pobj(cx);
2202     RootedShape shape(cx);
2203 
2204     RootedObject scope(cx, scopeChain);
2205     for (; !scope->is<GlobalObject>(); scope = scope->enclosingScope()) {
2206         if (!LookupProperty(cx, scope, id, &pobj, &shape))
2207             return false;
2208         if (shape)
2209             break;
2210     }
2211 
2212     objp.set(scope);
2213     return true;
2214 }
2215 
2216 bool
LookupNameUnqualified(JSContext * cx,HandlePropertyName name,HandleObject scopeChain,MutableHandleObject objp)2217 js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject scopeChain,
2218                           MutableHandleObject objp)
2219 {
2220     RootedId id(cx, NameToId(name));
2221 
2222     RootedObject pobj(cx);
2223     RootedShape shape(cx);
2224 
2225     RootedObject scope(cx, scopeChain);
2226     for (; !scope->isUnqualifiedVarObj(); scope = scope->enclosingScope()) {
2227         if (!LookupProperty(cx, scope, id, &pobj, &shape))
2228             return false;
2229         if (shape)
2230             break;
2231     }
2232 
2233     // See note above RuntimeLexicalErrorObject.
2234     if (pobj == scope) {
2235         if (name != cx->names().dotThis && IsUninitializedLexicalSlot(scope, shape)) {
2236             scope = RuntimeLexicalErrorObject::create(cx, scope, JSMSG_UNINITIALIZED_LEXICAL);
2237             if (!scope)
2238                 return false;
2239         } else if (scope->is<ScopeObject>() && !scope->is<DeclEnvObject>() && !shape->writable()) {
2240             MOZ_ASSERT(name != cx->names().dotThis);
2241             scope = RuntimeLexicalErrorObject::create(cx, scope, JSMSG_BAD_CONST_ASSIGN);
2242             if (!scope)
2243                 return false;
2244         }
2245     }
2246 
2247     objp.set(scope);
2248     return true;
2249 }
2250 
2251 bool
HasOwnProperty(JSContext * cx,HandleObject obj,HandleId id,bool * result)2252 js::HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id, bool* result)
2253 {
2254     if (obj->is<ProxyObject>())
2255         return Proxy::hasOwn(cx, obj, id, result);
2256 
2257     if (GetOwnPropertyOp op = obj->getOps()->getOwnPropertyDescriptor) {
2258         Rooted<PropertyDescriptor> desc(cx);
2259         if (!op(cx, obj, id, &desc))
2260             return false;
2261         *result = !!desc.object();
2262         return true;
2263     }
2264 
2265     RootedShape shape(cx);
2266     if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &shape))
2267         return false;
2268     *result = (shape != nullptr);
2269     return true;
2270 }
2271 
2272 bool
LookupPropertyPure(ExclusiveContext * cx,JSObject * obj,jsid id,JSObject ** objp,Shape ** propp)2273 js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp,
2274                        Shape** propp)
2275 {
2276     do {
2277         if (obj->isNative()) {
2278             /* Search for a native dense element, typed array element, or property. */
2279 
2280             if (JSID_IS_INT(id) && obj->as<NativeObject>().containsDenseElement(JSID_TO_INT(id))) {
2281                 *objp = obj;
2282                 MarkDenseOrTypedArrayElementFound<NoGC>(propp);
2283                 return true;
2284             }
2285 
2286             if (IsAnyTypedArray(obj)) {
2287                 uint64_t index;
2288                 if (IsTypedArrayIndex(id, &index)) {
2289                     if (index < AnyTypedArrayLength(obj)) {
2290                         *objp = obj;
2291                         MarkDenseOrTypedArrayElementFound<NoGC>(propp);
2292                     } else {
2293                         *objp = nullptr;
2294                         *propp = nullptr;
2295                     }
2296                     return true;
2297                 }
2298             }
2299 
2300             if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
2301                 *objp = obj;
2302                 *propp = shape;
2303                 return true;
2304             }
2305 
2306             // Fail if there's a resolve hook, unless the mayResolve hook tells
2307             // us the resolve hook won't define a property with this id.
2308             if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
2309                 return false;
2310         } else if (obj->is<UnboxedPlainObject>()) {
2311             if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
2312                 *objp = obj;
2313                 MarkNonNativePropertyFound<NoGC>(propp);
2314                 return true;
2315             }
2316         } else if (obj->is<UnboxedArrayObject>()) {
2317             if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
2318                 *objp = obj;
2319                 MarkNonNativePropertyFound<NoGC>(propp);
2320                 return true;
2321             }
2322         } else if (obj->is<TypedObject>()) {
2323             if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
2324                 *objp = obj;
2325                 MarkNonNativePropertyFound<NoGC>(propp);
2326                 return true;
2327             }
2328         } else {
2329             return false;
2330         }
2331 
2332         obj = obj->getProto();
2333     } while (obj);
2334 
2335     *objp = nullptr;
2336     *propp = nullptr;
2337     return true;
2338 }
2339 
2340 static inline bool
NativeGetPureInline(NativeObject * pobj,Shape * shape,Value * vp)2341 NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp)
2342 {
2343     /* Fail if we have a custom getter. */
2344     if (!shape->hasDefaultGetter())
2345         return false;
2346 
2347     if (shape->hasSlot()) {
2348         *vp = pobj->getSlot(shape->slot());
2349         MOZ_ASSERT(!vp->isMagic());
2350     } else {
2351         vp->setUndefined();
2352     }
2353 
2354     return true;
2355 }
2356 
2357 bool
GetPropertyPure(ExclusiveContext * cx,JSObject * obj,jsid id,Value * vp)2358 js::GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp)
2359 {
2360     JSObject* pobj;
2361     Shape* shape;
2362     if (!LookupPropertyPure(cx, obj, id, &pobj, &shape))
2363         return false;
2364 
2365     if (!shape) {
2366         vp->setUndefined();
2367         return true;
2368     }
2369 
2370     return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), shape, vp);
2371 }
2372 
2373 bool
reportReadOnly(JSContext * cx,jsid id,unsigned report)2374 JSObject::reportReadOnly(JSContext* cx, jsid id, unsigned report)
2375 {
2376     RootedValue val(cx, IdToValue(id));
2377     return ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
2378                                  JSDVG_IGNORE_STACK, val, nullptr,
2379                                  nullptr, nullptr);
2380 }
2381 
2382 bool
reportNotConfigurable(JSContext * cx,jsid id,unsigned report)2383 JSObject::reportNotConfigurable(JSContext* cx, jsid id, unsigned report)
2384 {
2385     RootedValue val(cx, IdToValue(id));
2386     return ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
2387                                  JSDVG_IGNORE_STACK, val, nullptr,
2388                                  nullptr, nullptr);
2389 }
2390 
2391 bool
reportNotExtensible(JSContext * cx,unsigned report)2392 JSObject::reportNotExtensible(JSContext* cx, unsigned report)
2393 {
2394     RootedValue val(cx, ObjectValue(*this));
2395     return ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
2396                                  JSDVG_IGNORE_STACK, val, nullptr,
2397                                  nullptr, nullptr);
2398 }
2399 
2400 // Our immutable-prototype behavior is non-standard, and it's unclear whether
2401 // it's shippable.  (Or at least it's unclear whether it's shippable with any
2402 // provided-by-default uses exposed to script.)  If this bool is true,
2403 // immutable-prototype behavior is enforced; if it's false, behavior is not
2404 // enforced, and immutable-prototype bits stored on objects are completely
2405 // ignored.
2406 static const bool ImmutablePrototypesEnabled = true;
2407 
2408 JS_FRIEND_API(bool)
JS_ImmutablePrototypesEnabled()2409 JS_ImmutablePrototypesEnabled()
2410 {
2411     return ImmutablePrototypesEnabled;
2412 }
2413 
2414 /*** ES6 standard internal methods ***************************************************************/
2415 
2416 bool
SetPrototype(JSContext * cx,HandleObject obj,HandleObject proto,JS::ObjectOpResult & result)2417 js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::ObjectOpResult& result)
2418 {
2419     /*
2420      * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's
2421      * {get,set}Prototype and setImmutablePrototype methods mediate access to
2422      * |obj.[[Prototype]]|.  The Proxy subsystem is responsible for responding
2423      * to such attempts.
2424      */
2425     if (obj->hasLazyPrototype()) {
2426         MOZ_ASSERT(obj->is<ProxyObject>());
2427         return Proxy::setPrototype(cx, obj, proto, result);
2428     }
2429 
2430     /* Disallow mutation of immutable [[Prototype]]s. */
2431     if (obj->nonLazyPrototypeIsImmutable() && ImmutablePrototypesEnabled)
2432         return result.fail(JSMSG_CANT_SET_PROTO);
2433 
2434     /*
2435      * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which
2436      * due to their complicated delegate-object shenanigans can't easily
2437      * have a mutable [[Prototype]].
2438      */
2439     if (obj->is<ArrayBufferObject>()) {
2440         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF,
2441                              "incompatible ArrayBuffer");
2442         return false;
2443     }
2444 
2445     /*
2446      * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
2447      */
2448     if (obj->is<TypedObject>()) {
2449         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF,
2450                              "incompatible TypedObject");
2451         return false;
2452     }
2453 
2454     /*
2455      * Explicitly disallow mutating the [[Prototype]] of Location objects
2456      * for flash-related security reasons.
2457      */
2458     if (!strcmp(obj->getClass()->name, "Location")) {
2459         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF,
2460                              "incompatible Location object");
2461         return false;
2462     }
2463 
2464     /*
2465      * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
2466      * Since the values in question are objects, we can just compare pointers.
2467      */
2468     if (proto == obj->getProto())
2469         return result.succeed();
2470 
2471     /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
2472     bool extensible;
2473     if (!IsExtensible(cx, obj, &extensible))
2474         return false;
2475     if (!extensible)
2476         return result.fail(JSMSG_CANT_SET_PROTO);
2477 
2478     // If this is a global object, resolve the Object class so that its
2479     // [[Prototype]] chain is always properly immutable, even in the presence
2480     // of lazy standard classes.
2481     if (obj->is<GlobalObject>()) {
2482         Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
2483         if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object))
2484             return false;
2485     }
2486 
2487     /*
2488      * ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we
2489      * have to do this comparison on the observable WindowProxy, not on the
2490      * possibly-Window object we're setting the proto on.
2491      */
2492     RootedObject objMaybeWindowProxy(cx, ToWindowProxyIfWindow(obj));
2493     RootedObject obj2(cx);
2494     for (obj2 = proto; obj2; ) {
2495         MOZ_ASSERT(!IsWindow(obj2));
2496         if (obj2 == objMaybeWindowProxy)
2497             return result.fail(JSMSG_CANT_SET_PROTO_CYCLE);
2498 
2499         if (!GetPrototype(cx, obj2, &obj2))
2500             return false;
2501     }
2502 
2503     // Convert unboxed objects to their native representations before changing
2504     // their prototype/group, as they depend on the group for their layout.
2505     if (!MaybeConvertUnboxedObjectToNative(cx, obj))
2506         return false;
2507 
2508     Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
2509     if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto))
2510         return false;
2511 
2512     return result.succeed();
2513 }
2514 
2515 bool
SetPrototype(JSContext * cx,HandleObject obj,HandleObject proto)2516 js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto)
2517 {
2518     ObjectOpResult result;
2519     return SetPrototype(cx, obj, proto, result) && result.checkStrict(cx, obj);
2520 }
2521 
2522 bool
PreventExtensions(JSContext * cx,HandleObject obj,ObjectOpResult & result)2523 js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result)
2524 {
2525     if (obj->is<ProxyObject>())
2526         return js::Proxy::preventExtensions(cx, obj, result);
2527 
2528     if (!obj->nonProxyIsExtensible())
2529         return result.succeed();
2530 
2531     if (!MaybeConvertUnboxedObjectToNative(cx, obj))
2532         return false;
2533 
2534     // Force lazy properties to be resolved.
2535     AutoIdVector props(cx);
2536     if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2537         return false;
2538 
2539     // Convert all dense elements to sparse properties. This will shrink the
2540     // initialized length and capacity of the object to zero and ensure that no
2541     // new dense elements can be added without calling growElements(), which
2542     // checks isExtensible().
2543     if (obj->isNative()) {
2544         if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
2545             return false;
2546     }
2547 
2548     if (!obj->setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE))
2549         return false;
2550     return result.succeed();
2551 }
2552 
2553 bool
PreventExtensions(JSContext * cx,HandleObject obj)2554 js::PreventExtensions(JSContext* cx, HandleObject obj)
2555 {
2556     ObjectOpResult result;
2557     return PreventExtensions(cx, obj, result) && result.checkStrict(cx, obj);
2558 }
2559 
2560 bool
GetOwnPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<PropertyDescriptor> desc)2561 js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
2562                              MutableHandle<PropertyDescriptor> desc)
2563 {
2564     if (GetOwnPropertyOp op = obj->getOps()->getOwnPropertyDescriptor) {
2565         bool ok = op(cx, obj, id, desc);
2566         if (ok)
2567             desc.assertCompleteIfFound();
2568         return ok;
2569     }
2570 
2571     RootedNativeObject nobj(cx, obj.as<NativeObject>());
2572     RootedShape shape(cx);
2573     if (!NativeLookupOwnProperty<CanGC>(cx, nobj, id, &shape))
2574         return false;
2575     if (!shape) {
2576         desc.object().set(nullptr);
2577         return true;
2578     }
2579 
2580     desc.setAttributes(GetShapeAttributes(obj, shape));
2581     if (desc.isAccessorDescriptor()) {
2582         MOZ_ASSERT(desc.isShared());
2583 
2584         // The result of GetOwnPropertyDescriptor() must be either undefined or
2585         // a complete property descriptor (per ES6 draft rev 32 (2015 Feb 2)
2586         // 6.1.7.3, Invariants of the Essential Internal Methods).
2587         //
2588         // It is an unfortunate fact that in SM, properties can exist that have
2589         // JSPROP_GETTER or JSPROP_SETTER but not both. In these cases, rather
2590         // than return true with desc incomplete, we fill out the missing
2591         // getter or setter with a null, following CompletePropertyDescriptor.
2592         if (desc.hasGetterObject()) {
2593             desc.setGetterObject(shape->getterObject());
2594         } else {
2595             desc.setGetterObject(nullptr);
2596             desc.attributesRef() |= JSPROP_GETTER;
2597         }
2598         if (desc.hasSetterObject()) {
2599             desc.setSetterObject(shape->setterObject());
2600         } else {
2601             desc.setSetterObject(nullptr);
2602             desc.attributesRef() |= JSPROP_SETTER;
2603         }
2604 
2605         desc.value().setUndefined();
2606     } else {
2607         // This is either a straight-up data property or (rarely) a
2608         // property with a JSGetterOp/JSSetterOp. The latter must be
2609         // reported to the caller as a plain data property, so clear
2610         // desc.getter/setter, and mask away the SHARED bit.
2611         desc.setGetter(nullptr);
2612         desc.setSetter(nullptr);
2613         desc.attributesRef() &= ~JSPROP_SHARED;
2614 
2615         if (IsImplicitDenseOrTypedArrayElement(shape)) {
2616             desc.value().set(nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
2617         } else {
2618             if (!NativeGetExistingProperty(cx, nobj, nobj, shape, desc.value()))
2619                 return false;
2620         }
2621     }
2622 
2623     desc.object().set(nobj);
2624     desc.assertComplete();
2625     return true;
2626 }
2627 
2628 bool
DefineProperty(JSContext * cx,HandleObject obj,HandleId id,Handle<PropertyDescriptor> desc)2629 js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc)
2630 {
2631     ObjectOpResult result;
2632     return DefineProperty(cx, obj, id, desc, result) &&
2633            result.checkStrict(cx, obj, id);
2634 }
2635 
2636 bool
DefineProperty(JSContext * cx,HandleObject obj,HandleId id,Handle<PropertyDescriptor> desc,ObjectOpResult & result)2637 js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
2638                    ObjectOpResult& result)
2639 {
2640     desc.assertValid();
2641     if (DefinePropertyOp op = obj->getOps()->defineProperty)
2642         return op(cx, obj, id, desc, result);
2643     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
2644 }
2645 
2646 bool
DefineProperty(ExclusiveContext * cx,HandleObject obj,HandleId id,HandleValue value,JSGetterOp getter,JSSetterOp setter,unsigned attrs,ObjectOpResult & result)2647 js::DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value,
2648                    JSGetterOp getter, JSSetterOp setter, unsigned attrs,
2649                    ObjectOpResult& result)
2650 {
2651     MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS));
2652 
2653     Rooted<PropertyDescriptor> desc(cx);
2654     desc.initFields(nullptr, value, attrs, getter, setter);
2655     if (DefinePropertyOp op = obj->getOps()->defineProperty) {
2656         if (!cx->shouldBeJSContext())
2657             return false;
2658         return op(cx->asJSContext(), obj, id, desc, result);
2659     }
2660     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
2661 }
2662 
2663 bool
DefineProperty(ExclusiveContext * cx,HandleObject obj,PropertyName * name,HandleValue value,JSGetterOp getter,JSSetterOp setter,unsigned attrs,ObjectOpResult & result)2664 js::DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
2665                    JSGetterOp getter, JSSetterOp setter, unsigned attrs,
2666                    ObjectOpResult& result)
2667 {
2668     RootedId id(cx, NameToId(name));
2669     return DefineProperty(cx, obj, id, value, getter, setter, attrs, result);
2670 }
2671 
2672 bool
DefineElement(ExclusiveContext * cx,HandleObject obj,uint32_t index,HandleValue value,JSGetterOp getter,JSSetterOp setter,unsigned attrs,ObjectOpResult & result)2673 js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value,
2674                   JSGetterOp getter, JSSetterOp setter, unsigned attrs,
2675                   ObjectOpResult& result)
2676 {
2677     MOZ_ASSERT(getter != JS_PropertyStub);
2678     MOZ_ASSERT(setter != JS_StrictPropertyStub);
2679 
2680     RootedId id(cx);
2681     if (!IndexToId(cx, index, &id))
2682         return false;
2683     return DefineProperty(cx, obj, id, value, getter, setter, attrs, result);
2684 }
2685 
2686 bool
DefineProperty(ExclusiveContext * cx,HandleObject obj,HandleId id,HandleValue value,JSGetterOp getter,JSSetterOp setter,unsigned attrs)2687 js::DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value,
2688                    JSGetterOp getter, JSSetterOp setter, unsigned attrs)
2689 {
2690     ObjectOpResult result;
2691     if (!DefineProperty(cx, obj, id, value, getter, setter, attrs, result))
2692         return false;
2693     if (!result) {
2694         if (!cx->shouldBeJSContext())
2695             return false;
2696         result.reportError(cx->asJSContext(), obj, id);
2697         return false;
2698     }
2699     return true;
2700 }
2701 
2702 bool
DefineProperty(ExclusiveContext * cx,HandleObject obj,PropertyName * name,HandleValue value,JSGetterOp getter,JSSetterOp setter,unsigned attrs)2703 js::DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
2704                    JSGetterOp getter, JSSetterOp setter, unsigned attrs)
2705 {
2706     RootedId id(cx, NameToId(name));
2707     return DefineProperty(cx, obj, id, value, getter, setter, attrs);
2708 }
2709 
2710 bool
DefineElement(ExclusiveContext * cx,HandleObject obj,uint32_t index,HandleValue value,JSGetterOp getter,JSSetterOp setter,unsigned attrs)2711 js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value,
2712                   JSGetterOp getter, JSSetterOp setter, unsigned attrs)
2713 {
2714     MOZ_ASSERT(getter != JS_PropertyStub);
2715     MOZ_ASSERT(setter != JS_StrictPropertyStub);
2716 
2717     RootedId id(cx);
2718     if (!IndexToId(cx, index, &id))
2719         return false;
2720     return DefineProperty(cx, obj, id, value, getter, setter, attrs);
2721 }
2722 
2723 
2724 /*** SpiderMonkey nonstandard internal methods ***************************************************/
2725 
2726 bool
SetImmutablePrototype(ExclusiveContext * cx,HandleObject obj,bool * succeeded)2727 js::SetImmutablePrototype(ExclusiveContext* cx, HandleObject obj, bool* succeeded)
2728 {
2729     if (obj->hasLazyPrototype()) {
2730         if (!cx->shouldBeJSContext())
2731             return false;
2732         return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded);
2733     }
2734 
2735     if (!obj->setFlags(cx, BaseShape::IMMUTABLE_PROTOTYPE))
2736         return false;
2737     *succeeded = true;
2738     return true;
2739 }
2740 
2741 bool
GetPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<PropertyDescriptor> desc)2742 js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
2743                           MutableHandle<PropertyDescriptor> desc)
2744 {
2745     RootedObject pobj(cx);
2746 
2747     for (pobj = obj; pobj;) {
2748         if (pobj->is<ProxyObject>()) {
2749             bool ok = Proxy::getPropertyDescriptor(cx, pobj, id, desc);
2750             if (ok)
2751                 desc.assertCompleteIfFound();
2752             return ok;
2753         }
2754 
2755         if (!GetOwnPropertyDescriptor(cx, pobj, id, desc))
2756             return false;
2757 
2758         if (desc.object())
2759             return true;
2760 
2761         if (!GetPrototype(cx, pobj, &pobj))
2762             return false;
2763     }
2764 
2765     MOZ_ASSERT(!desc.object());
2766     return true;
2767 }
2768 
2769 bool
WatchGuts(JSContext * cx,JS::HandleObject origObj,JS::HandleId id,JS::HandleObject callable)2770 js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
2771 {
2772     RootedObject obj(cx, ToWindowIfWindowProxy(origObj));
2773     if (obj->isNative()) {
2774         // Use sparse indexes for watched objects, as dense elements can be
2775         // written to without checking the watchpoint map.
2776         if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
2777             return false;
2778 
2779         MarkTypePropertyNonData(cx, obj, id);
2780     }
2781 
2782     WatchpointMap* wpmap = cx->compartment()->watchpointMap;
2783     if (!wpmap) {
2784         wpmap = cx->runtime()->new_<WatchpointMap>();
2785         if (!wpmap || !wpmap->init()) {
2786             ReportOutOfMemory(cx);
2787             js_delete(wpmap);
2788             return false;
2789         }
2790         cx->compartment()->watchpointMap = wpmap;
2791     }
2792 
2793     return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
2794 }
2795 
2796 bool
UnwatchGuts(JSContext * cx,JS::HandleObject origObj,JS::HandleId id)2797 js::UnwatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id)
2798 {
2799     // Looking in the map for an unsupported object will never hit, so we don't
2800     // need to check for nativeness or watchable-ness here.
2801     RootedObject obj(cx, ToWindowIfWindowProxy(origObj));
2802     if (WatchpointMap* wpmap = cx->compartment()->watchpointMap)
2803         wpmap->unwatch(obj, id, nullptr, nullptr);
2804     return true;
2805 }
2806 
2807 bool
WatchProperty(JSContext * cx,HandleObject obj,HandleId id,HandleObject callable)2808 js::WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
2809 {
2810     if (WatchOp op = obj->getOps()->watch)
2811         return op(cx, obj, id, callable);
2812 
2813     if (!obj->isNative() || IsAnyTypedArray(obj)) {
2814         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
2815                              obj->getClass()->name);
2816         return false;
2817     }
2818 
2819     return WatchGuts(cx, obj, id, callable);
2820 }
2821 
2822 bool
UnwatchProperty(JSContext * cx,HandleObject obj,HandleId id)2823 js::UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id)
2824 {
2825     if (UnwatchOp op = obj->getOps()->unwatch)
2826         return op(cx, obj, id);
2827 
2828     return UnwatchGuts(cx, obj, id);
2829 }
2830 
2831 const char*
GetObjectClassName(JSContext * cx,HandleObject obj)2832 js::GetObjectClassName(JSContext* cx, HandleObject obj)
2833 {
2834     assertSameCompartment(cx, obj);
2835 
2836     if (obj->is<ProxyObject>())
2837         return Proxy::className(cx, obj);
2838 
2839     return obj->getClass()->name;
2840 }
2841 
2842 bool
callMethod(JSContext * cx,HandleId id,unsigned argc,Value * argv,MutableHandleValue vp)2843 JSObject::callMethod(JSContext* cx, HandleId id, unsigned argc, Value* argv, MutableHandleValue vp)
2844 {
2845     RootedValue fval(cx);
2846     RootedObject obj(cx, this);
2847     if (!GetProperty(cx, obj, obj, id, &fval))
2848         return false;
2849     return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
2850 }
2851 
2852 
2853 /* * */
2854 
2855 bool
HasDataProperty(JSContext * cx,NativeObject * obj,jsid id,Value * vp)2856 js::HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp)
2857 {
2858     if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
2859         *vp = obj->getDenseElement(JSID_TO_INT(id));
2860         return true;
2861     }
2862 
2863     if (Shape* shape = obj->lookup(cx, id)) {
2864         if (shape->hasDefaultGetter() && shape->hasSlot()) {
2865             *vp = obj->getSlot(shape->slot());
2866             return true;
2867         }
2868     }
2869 
2870     return false;
2871 }
2872 
2873 
2874 /*** ToPrimitive *************************************************************/
2875 
2876 /*
2877  * Gets |obj[id]|.  If that value's not callable, returns true and stores an
2878  * object value in *vp.  If it's callable, calls it with no arguments and |obj|
2879  * as |this|, returning the result in *vp.
2880  *
2881  * This is a mini-abstraction for ES6 draft rev 36 (2015 Mar 17),
2882  * 7.1.1, second algorithm (OrdinaryToPrimitive), steps 5.a-c.
2883  */
2884 static bool
MaybeCallMethod(JSContext * cx,HandleObject obj,HandleId id,MutableHandleValue vp)2885 MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
2886 {
2887     if (!GetProperty(cx, obj, obj, id, vp))
2888         return false;
2889     if (!IsCallable(vp)) {
2890         vp.setObject(*obj);
2891         return true;
2892     }
2893     return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
2894 }
2895 
2896 static bool
ReportCantConvert(JSContext * cx,unsigned errorNumber,HandleObject obj,JSType hint)2897 ReportCantConvert(JSContext* cx, unsigned errorNumber, HandleObject obj, JSType hint)
2898 {
2899     const Class* clasp = obj->getClass();
2900 
2901     // Avoid recursive death when decompiling in ReportValueError.
2902     RootedString str(cx);
2903     if (hint == JSTYPE_STRING) {
2904         str = JS_AtomizeAndPinString(cx, clasp->name);
2905         if (!str)
2906             return false;
2907     } else {
2908         str = nullptr;
2909     }
2910 
2911     RootedValue val(cx, ObjectValue(*obj));
2912     ReportValueError2(cx, errorNumber, JSDVG_SEARCH_STACK, val, str,
2913                       hint == JSTYPE_VOID
2914                       ? "primitive type"
2915                       : hint == JSTYPE_STRING ? "string" : "number");
2916     return false;
2917 }
2918 
2919 bool
OrdinaryToPrimitive(JSContext * cx,HandleObject obj,JSType hint,MutableHandleValue vp)2920 JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp)
2921 {
2922     MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
2923 
2924     Rooted<jsid> id(cx);
2925 
2926     const Class* clasp = obj->getClass();
2927     if (hint == JSTYPE_STRING) {
2928         id = NameToId(cx->names().toString);
2929 
2930         /* Optimize (new String(...)).toString(). */
2931         if (clasp == &StringObject::class_) {
2932             StringObject* nobj = &obj->as<StringObject>();
2933             if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
2934                 vp.setString(nobj->unbox());
2935                 return true;
2936             }
2937         }
2938 
2939         if (!MaybeCallMethod(cx, obj, id, vp))
2940             return false;
2941         if (vp.isPrimitive())
2942             return true;
2943 
2944         id = NameToId(cx->names().valueOf);
2945         if (!MaybeCallMethod(cx, obj, id, vp))
2946             return false;
2947         if (vp.isPrimitive())
2948             return true;
2949     } else {
2950         id = NameToId(cx->names().valueOf);
2951 
2952         /* Optimize new String(...).valueOf(). */
2953         if (clasp == &StringObject::class_) {
2954             StringObject* nobj = &obj->as<StringObject>();
2955             if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) {
2956                 vp.setString(nobj->unbox());
2957                 return true;
2958             }
2959         }
2960 
2961         /* Optimize new Number(...).valueOf(). */
2962         if (clasp == &NumberObject::class_) {
2963             NumberObject* nobj = &obj->as<NumberObject>();
2964             if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) {
2965                 vp.setNumber(nobj->unbox());
2966                 return true;
2967             }
2968         }
2969 
2970         if (!MaybeCallMethod(cx, obj, id, vp))
2971             return false;
2972         if (vp.isPrimitive())
2973             return true;
2974 
2975         id = NameToId(cx->names().toString);
2976         if (!MaybeCallMethod(cx, obj, id, vp))
2977             return false;
2978         if (vp.isPrimitive())
2979             return true;
2980     }
2981 
2982     return ReportCantConvert(cx, JSMSG_CANT_CONVERT_TO, obj, hint);
2983 }
2984 
2985 bool
ToPrimitiveSlow(JSContext * cx,JSType preferredType,MutableHandleValue vp)2986 js::ToPrimitiveSlow(JSContext* cx, JSType preferredType, MutableHandleValue vp)
2987 {
2988     // Step numbers refer to the first algorithm listed in ES6 draft rev 36
2989     // (2015 Mar 17) 7.1.1 ToPrimitive.
2990     MOZ_ASSERT(preferredType == JSTYPE_VOID ||
2991                preferredType == JSTYPE_STRING ||
2992                preferredType == JSTYPE_NUMBER);
2993     RootedObject obj(cx, &vp.toObject());
2994 
2995     // Steps 4-5.
2996     RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toPrimitive));
2997     RootedValue method(cx);
2998     if (!GetProperty(cx, obj, obj, id, &method))
2999         return false;
3000 
3001     // Step 6.
3002     if (!method.isUndefined()) {
3003         // Step 6 of GetMethod. Invoke() below would do this check and throw a
3004         // TypeError anyway, but this produces a better error message.
3005         if (!IsCallable(method))
3006             return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_NOT_CALLABLE, obj, preferredType);
3007 
3008         // Steps 1-3.
3009         RootedValue hint(cx, StringValue(preferredType == JSTYPE_STRING ? cx->names().string :
3010                                          preferredType == JSTYPE_NUMBER ? cx->names().number :
3011                                          cx->names().default_));
3012 
3013         // Steps 6.a-b.
3014         if (!Invoke(cx, vp, method, 1, hint.address(), vp))
3015             return false;
3016 
3017         // Steps 6.c-d.
3018         if (vp.isObject())
3019             return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_RETURNED_OBJECT, obj, preferredType);
3020         return true;
3021     }
3022 
3023     return OrdinaryToPrimitive(cx, obj, preferredType, vp);
3024 }
3025 
3026 
3027 /* * */
3028 
3029 bool
IsDelegate(JSContext * cx,HandleObject obj,const js::Value & v,bool * result)3030 js::IsDelegate(JSContext* cx, HandleObject obj, const js::Value& v, bool* result)
3031 {
3032     if (v.isPrimitive()) {
3033         *result = false;
3034         return true;
3035     }
3036     return IsDelegateOfObject(cx, obj, &v.toObject(), result);
3037 }
3038 
3039 bool
IsDelegateOfObject(JSContext * cx,HandleObject protoObj,JSObject * obj,bool * result)3040 js::IsDelegateOfObject(JSContext* cx, HandleObject protoObj, JSObject* obj, bool* result)
3041 {
3042     RootedObject obj2(cx, obj);
3043     for (;;) {
3044         if (!GetPrototype(cx, obj2, &obj2))
3045             return false;
3046         if (!obj2) {
3047             *result = false;
3048             return true;
3049         }
3050         if (obj2 == protoObj) {
3051             *result = true;
3052             return true;
3053         }
3054     }
3055 }
3056 
3057 JSObject*
GetBuiltinPrototypePure(GlobalObject * global,JSProtoKey protoKey)3058 js::GetBuiltinPrototypePure(GlobalObject* global, JSProtoKey protoKey)
3059 {
3060     MOZ_ASSERT(JSProto_Null <= protoKey);
3061     MOZ_ASSERT(protoKey < JSProto_LIMIT);
3062 
3063     if (protoKey != JSProto_Null) {
3064         const Value& v = global->getPrototype(protoKey);
3065         if (v.isObject())
3066             return &v.toObject();
3067     }
3068 
3069     return nullptr;
3070 }
3071 
3072 JSObject*
PrimitiveToObject(JSContext * cx,const Value & v)3073 js::PrimitiveToObject(JSContext* cx, const Value& v)
3074 {
3075     if (v.isString()) {
3076         Rooted<JSString*> str(cx, v.toString());
3077         return StringObject::create(cx, str);
3078     }
3079     if (v.isNumber())
3080         return NumberObject::create(cx, v.toNumber());
3081     if (v.isBoolean())
3082         return BooleanObject::create(cx, v.toBoolean());
3083     MOZ_ASSERT(v.isSymbol());
3084     RootedSymbol symbol(cx, v.toSymbol());
3085     return SymbolObject::create(cx, symbol);
3086 }
3087 
3088 /*
3089  * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
3090  * already be an object, use ToObject. reportCantConvert controls how null and
3091  * undefined errors are reported.
3092  *
3093  * Callers must handle the already-object case.
3094  */
3095 JSObject*
ToObjectSlow(JSContext * cx,JS::HandleValue val,bool reportScanStack)3096 js::ToObjectSlow(JSContext* cx, JS::HandleValue val, bool reportScanStack)
3097 {
3098     MOZ_ASSERT(!val.isMagic());
3099     MOZ_ASSERT(!val.isObject());
3100 
3101     if (val.isNullOrUndefined()) {
3102         if (reportScanStack) {
3103             ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, nullptr);
3104         } else {
3105             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
3106                                  val.isNull() ? "null" : "undefined", "object");
3107         }
3108         return nullptr;
3109     }
3110 
3111     return PrimitiveToObject(cx, val);
3112 }
3113 
3114 Value
GetThisValue(JSObject * obj)3115 js::GetThisValue(JSObject* obj)
3116 {
3117     if (obj->is<GlobalObject>())
3118         return ObjectValue(*ToWindowProxyIfWindow(obj));
3119 
3120     if (obj->is<ClonedBlockObject>())
3121         return obj->as<ClonedBlockObject>().thisValue();
3122 
3123     if (obj->is<ModuleEnvironmentObject>())
3124         return UndefinedValue();
3125 
3126     if (obj->is<DynamicWithObject>())
3127         return ObjectValue(*obj->as<DynamicWithObject>().withThis());
3128 
3129     if (obj->is<NonSyntacticVariablesObject>())
3130         return GetThisValue(obj->enclosingScope());
3131 
3132     return ObjectValue(*obj);
3133 }
3134 
3135 class GetObjectSlotNameFunctor : public JS::CallbackTracer::ContextFunctor
3136 {
3137     JSObject* obj;
3138 
3139   public:
GetObjectSlotNameFunctor(JSObject * ctx)3140     explicit GetObjectSlotNameFunctor(JSObject* ctx) : obj(ctx) {}
3141     virtual void operator()(JS::CallbackTracer* trc, char* buf, size_t bufsize) override;
3142 };
3143 
3144 void
operator ()(JS::CallbackTracer * trc,char * buf,size_t bufsize)3145 GetObjectSlotNameFunctor::operator()(JS::CallbackTracer* trc, char* buf, size_t bufsize)
3146 {
3147     MOZ_ASSERT(trc->contextIndex() != JS::CallbackTracer::InvalidIndex);
3148 
3149     uint32_t slot = uint32_t(trc->contextIndex());
3150 
3151     Shape* shape;
3152     if (obj->isNative()) {
3153         shape = obj->as<NativeObject>().lastProperty();
3154         while (shape && (!shape->hasSlot() || shape->slot() != slot))
3155             shape = shape->previous();
3156     } else {
3157         shape = nullptr;
3158     }
3159 
3160     if (!shape) {
3161         do {
3162             const char* slotname = nullptr;
3163             const char* pattern = nullptr;
3164             if (obj->is<GlobalObject>()) {
3165                 pattern = "CLASS_OBJECT(%s)";
3166                 if (false)
3167                     ;
3168 #define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp) \
3169                 else if ((code) == slot) { slotname = js_##name##_str; }
3170                 JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
3171 #undef TEST_SLOT_MATCHES_PROTOTYPE
3172             } else {
3173                 pattern = "%s";
3174                 if (obj->is<ScopeObject>()) {
3175                     if (slot == ScopeObject::enclosingScopeSlot()) {
3176                         slotname = "enclosing_environment";
3177                     } else if (obj->is<CallObject>()) {
3178                         if (slot == CallObject::calleeSlot())
3179                             slotname = "callee_slot";
3180                     } else if (obj->is<DeclEnvObject>()) {
3181                         if (slot == DeclEnvObject::lambdaSlot())
3182                             slotname = "named_lambda";
3183                     } else if (obj->is<DynamicWithObject>()) {
3184                         if (slot == DynamicWithObject::objectSlot())
3185                             slotname = "with_object";
3186                         else if (slot == DynamicWithObject::thisSlot())
3187                             slotname = "with_this";
3188                     }
3189                 }
3190             }
3191 
3192             if (slotname)
3193                 JS_snprintf(buf, bufsize, pattern, slotname);
3194             else
3195                 JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
3196         } while (false);
3197     } else {
3198         jsid propid = shape->propid();
3199         if (JSID_IS_INT(propid)) {
3200             JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid));
3201         } else if (JSID_IS_ATOM(propid)) {
3202             PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
3203         } else if (JSID_IS_SYMBOL(propid)) {
3204             JS_snprintf(buf, bufsize, "**SYMBOL KEY**");
3205         } else {
3206             JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
3207         }
3208     }
3209 }
3210 
3211 bool
ReportGetterOnlyAssignment(JSContext * cx,bool strict)3212 js::ReportGetterOnlyAssignment(JSContext* cx, bool strict)
3213 {
3214     return JS_ReportErrorFlagsAndNumber(cx,
3215                                         strict
3216                                         ? JSREPORT_ERROR
3217                                         : JSREPORT_WARNING | JSREPORT_STRICT,
3218                                         GetErrorMessage, nullptr,
3219                                         JSMSG_GETTER_ONLY);
3220 }
3221 
3222 
3223 /*** Debugging routines **************************************************************************/
3224 
3225 #ifdef DEBUG
3226 
3227 /*
3228  * Routines to print out values during debugging.  These are FRIEND_API to help
3229  * the debugger find them and to support temporarily hacking js::Dump* calls
3230  * into other code.
3231  */
3232 
3233 static void
dumpValue(const Value & v)3234 dumpValue(const Value& v)
3235 {
3236     if (v.isNull())
3237         fprintf(stderr, "null");
3238     else if (v.isUndefined())
3239         fprintf(stderr, "undefined");
3240     else if (v.isInt32())
3241         fprintf(stderr, "%d", v.toInt32());
3242     else if (v.isDouble())
3243         fprintf(stderr, "%g", v.toDouble());
3244     else if (v.isString())
3245         v.toString()->dump();
3246     else if (v.isSymbol())
3247         v.toSymbol()->dump();
3248     else if (v.isObject() && v.toObject().is<JSFunction>()) {
3249         JSFunction* fun = &v.toObject().as<JSFunction>();
3250         if (fun->displayAtom()) {
3251             fputs("<function ", stderr);
3252             FileEscapedString(stderr, fun->displayAtom(), 0);
3253         } else {
3254             fputs("<unnamed function", stderr);
3255         }
3256         if (fun->hasScript()) {
3257             JSScript* script = fun->nonLazyScript();
3258             fprintf(stderr, " (%s:%" PRIuSIZE ")",
3259                     script->filename() ? script->filename() : "", script->lineno());
3260         }
3261         fprintf(stderr, " at %p>", (void*) fun);
3262     } else if (v.isObject()) {
3263         JSObject* obj = &v.toObject();
3264         const Class* clasp = obj->getClass();
3265         fprintf(stderr, "<%s%s at %p>",
3266                 clasp->name,
3267                 (clasp == &PlainObject::class_) ? "" : " object",
3268                 (void*) obj);
3269     } else if (v.isBoolean()) {
3270         if (v.toBoolean())
3271             fprintf(stderr, "true");
3272         else
3273             fprintf(stderr, "false");
3274     } else if (v.isMagic()) {
3275         fprintf(stderr, "<invalid");
3276 #ifdef DEBUG
3277         switch (v.whyMagic()) {
3278           case JS_ELEMENTS_HOLE:     fprintf(stderr, " elements hole");      break;
3279           case JS_NO_ITER_VALUE:     fprintf(stderr, " no iter value");      break;
3280           case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing");  break;
3281           case JS_OPTIMIZED_OUT:     fprintf(stderr, " optimized out");      break;
3282           default:                   fprintf(stderr, " ?!");                 break;
3283         }
3284 #endif
3285         fprintf(stderr, ">");
3286     } else {
3287         fprintf(stderr, "unexpected value");
3288     }
3289 }
3290 
JS_FRIEND_API(void)3291 JS_FRIEND_API(void)
3292 js::DumpValue(const Value& val)
3293 {
3294     dumpValue(val);
3295     fputc('\n', stderr);
3296 }
3297 
JS_FRIEND_API(void)3298 JS_FRIEND_API(void)
3299 js::DumpId(jsid id)
3300 {
3301     fprintf(stderr, "jsid %p = ", (void*) JSID_BITS(id));
3302     dumpValue(IdToValue(id));
3303     fputc('\n', stderr);
3304 }
3305 
3306 static void
DumpProperty(NativeObject * obj,Shape & shape)3307 DumpProperty(NativeObject* obj, Shape& shape)
3308 {
3309     jsid id = shape.propid();
3310     uint8_t attrs = shape.attributes();
3311 
3312     fprintf(stderr, "    ((js::Shape*) %p) ", (void*) &shape);
3313     if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
3314     if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
3315     if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
3316     if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
3317 
3318     if (shape.hasGetterValue())
3319         fprintf(stderr, "getterValue=%p ", (void*) shape.getterObject());
3320     else if (!shape.hasDefaultGetter())
3321         fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void*, shape.getterOp()));
3322 
3323     if (shape.hasSetterValue())
3324         fprintf(stderr, "setterValue=%p ", (void*) shape.setterObject());
3325     else if (!shape.hasDefaultSetter())
3326         fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void*, shape.setterOp()));
3327 
3328     if (JSID_IS_ATOM(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id))
3329         dumpValue(js::IdToValue(id));
3330     else
3331         fprintf(stderr, "unknown jsid %p", (void*) JSID_BITS(id));
3332 
3333     uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT;
3334     fprintf(stderr, ": slot %d", slot);
3335     if (shape.hasSlot()) {
3336         fprintf(stderr, " = ");
3337         dumpValue(obj->getSlot(slot));
3338     } else if (slot != SHAPE_INVALID_SLOT) {
3339         fprintf(stderr, " (INVALID!)");
3340     }
3341     fprintf(stderr, "\n");
3342 }
3343 
3344 bool
uninlinedIsProxy() const3345 JSObject::uninlinedIsProxy() const
3346 {
3347     return is<ProxyObject>();
3348 }
3349 
3350 void
dump()3351 JSObject::dump()
3352 {
3353     JSObject* obj = this;
3354     JSObject* globalObj = &global();
3355     fprintf(stderr, "object %p from global %p [%s]\n", (void*) obj,
3356             (void*) globalObj, globalObj->getClass()->name);
3357     const Class* clasp = obj->getClass();
3358     fprintf(stderr, "class %p %s\n", (const void*)clasp, clasp->name);
3359 
3360     fprintf(stderr, "flags:");
3361     if (obj->isDelegate()) fprintf(stderr, " delegate");
3362     if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) fprintf(stderr, " not_extensible");
3363     if (obj->isIndexed()) fprintf(stderr, " indexed");
3364     if (obj->isBoundFunction()) fprintf(stderr, " bound_function");
3365     if (obj->isQualifiedVarObj()) fprintf(stderr, " varobj");
3366     if (obj->isUnqualifiedVarObj()) fprintf(stderr, " unqualified_varobj");
3367     if (obj->watched()) fprintf(stderr, " watched");
3368     if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton");
3369     if (obj->isNewGroupUnknown()) fprintf(stderr, " new_type_unknown");
3370     if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
3371     if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
3372     if (obj->wasNewScriptCleared()) fprintf(stderr, " new_script_cleared");
3373     if (!obj->hasLazyPrototype() && obj->nonLazyPrototypeIsImmutable()) fprintf(stderr, " immutable_prototype");
3374 
3375     if (obj->isNative()) {
3376         NativeObject* nobj = &obj->as<NativeObject>();
3377         if (nobj->inDictionaryMode())
3378             fprintf(stderr, " inDictionaryMode");
3379         if (nobj->hasShapeTable())
3380             fprintf(stderr, " hasShapeTable");
3381     }
3382     fprintf(stderr, "\n");
3383 
3384     if (obj->isNative()) {
3385         NativeObject* nobj = &obj->as<NativeObject>();
3386         uint32_t slots = nobj->getDenseInitializedLength();
3387         if (slots) {
3388             fprintf(stderr, "elements\n");
3389             for (uint32_t i = 0; i < slots; i++) {
3390                 fprintf(stderr, " %3d: ", i);
3391                 dumpValue(nobj->getDenseElement(i));
3392                 fprintf(stderr, "\n");
3393                 fflush(stderr);
3394             }
3395         }
3396     }
3397 
3398     fprintf(stderr, "proto ");
3399     TaggedProto proto = obj->getTaggedProto();
3400     if (proto.isLazy())
3401         fprintf(stderr, "<lazy>");
3402     else
3403         dumpValue(ObjectOrNullValue(proto.toObjectOrNull()));
3404     fputc('\n', stderr);
3405 
3406     if (clasp->flags & JSCLASS_HAS_PRIVATE)
3407         fprintf(stderr, "private %p\n", obj->as<NativeObject>().getPrivate());
3408 
3409     if (!obj->isNative())
3410         fprintf(stderr, "not native\n");
3411 
3412     uint32_t reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
3413     uint32_t slots = obj->isNative() ? obj->as<NativeObject>().slotSpan() : 0;
3414     uint32_t stop = obj->isNative() ? reservedEnd : slots;
3415     if (stop > 0)
3416         fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n");
3417     for (uint32_t i = 0; i < stop; i++) {
3418         fprintf(stderr, " %3d ", i);
3419         if (i < reservedEnd)
3420             fprintf(stderr, "(reserved) ");
3421         fprintf(stderr, "= ");
3422         dumpValue(obj->as<NativeObject>().getSlot(i));
3423         fputc('\n', stderr);
3424     }
3425 
3426     if (obj->isNative()) {
3427         fprintf(stderr, "properties:\n");
3428         Vector<Shape*, 8, SystemAllocPolicy> props;
3429         for (Shape::Range<NoGC> r(obj->as<NativeObject>().lastProperty()); !r.empty(); r.popFront())
3430             props.append(&r.front());
3431         for (size_t i = props.length(); i-- != 0;)
3432             DumpProperty(&obj->as<NativeObject>(), *props[i]);
3433     }
3434     fputc('\n', stderr);
3435 }
3436 
3437 static void
MaybeDumpObject(const char * name,JSObject * obj)3438 MaybeDumpObject(const char* name, JSObject* obj)
3439 {
3440     if (obj) {
3441         fprintf(stderr, "  %s: ", name);
3442         dumpValue(ObjectValue(*obj));
3443         fputc('\n', stderr);
3444     }
3445 }
3446 
3447 static void
MaybeDumpValue(const char * name,const Value & v)3448 MaybeDumpValue(const char* name, const Value& v)
3449 {
3450     if (!v.isNull()) {
3451         fprintf(stderr, "  %s: ", name);
3452         dumpValue(v);
3453         fputc('\n', stderr);
3454     }
3455 }
3456 
JS_FRIEND_API(void)3457 JS_FRIEND_API(void)
3458 js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start)
3459 {
3460     /* This should only called during live debugging. */
3461     ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED);
3462     if (!start) {
3463         if (i.done()) {
3464             fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
3465             return;
3466         }
3467     } else {
3468         while (!i.done() && !i.isJit() && i.interpFrame() != start)
3469             ++i;
3470 
3471         if (i.done()) {
3472             fprintf(stderr, "fp = %p not found in cx = %p\n",
3473                     (void*)start, (void*)cx);
3474             return;
3475         }
3476     }
3477 
3478     for (; !i.done(); ++i) {
3479         if (i.isJit())
3480             fprintf(stderr, "JIT frame\n");
3481         else
3482             fprintf(stderr, "InterpreterFrame at %p\n", (void*) i.interpFrame());
3483 
3484         if (i.isFunctionFrame()) {
3485             fprintf(stderr, "callee fun: ");
3486             RootedValue v(cx);
3487             JSObject* fun = i.callee(cx);
3488             v.setObject(*fun);
3489             dumpValue(v);
3490         } else {
3491             fprintf(stderr, "global frame, no callee");
3492         }
3493         fputc('\n', stderr);
3494 
3495         fprintf(stderr, "file %s line %" PRIuSIZE "\n",
3496                 i.script()->filename(), i.script()->lineno());
3497 
3498         if (jsbytecode* pc = i.pc()) {
3499             fprintf(stderr, "  pc = %p\n", pc);
3500             fprintf(stderr, "  current op: %s\n", CodeName[*pc]);
3501             MaybeDumpObject("staticScope", i.script()->getStaticBlockScope(pc));
3502         }
3503         if (i.isNonEvalFunctionFrame())
3504             MaybeDumpValue("this", i.thisArgument(cx));
3505         if (!i.isJit()) {
3506             fprintf(stderr, "  rval: ");
3507             dumpValue(i.interpFrame()->returnValue());
3508             fputc('\n', stderr);
3509         }
3510 
3511         fprintf(stderr, "  flags:");
3512         if (i.isConstructing())
3513             fprintf(stderr, " constructing");
3514         if (!i.isJit() && i.interpFrame()->isDebuggerEvalFrame())
3515             fprintf(stderr, " debugger eval");
3516         if (i.isEvalFrame())
3517             fprintf(stderr, " eval");
3518         fputc('\n', stderr);
3519 
3520         fprintf(stderr, "  scopeChain: (JSObject*) %p\n", (void*) i.scopeChain(cx));
3521 
3522         fputc('\n', stderr);
3523     }
3524 }
3525 
3526 #endif /* DEBUG */
3527 
JS_FRIEND_API(void)3528 JS_FRIEND_API(void)
3529 js::DumpBacktrace(JSContext* cx)
3530 {
3531     Sprinter sprinter(cx);
3532     sprinter.init();
3533     size_t depth = 0;
3534     for (AllFramesIter i(cx); !i.done(); ++i, ++depth) {
3535         const char* filename = JS_GetScriptFilename(i.script());
3536         unsigned line = PCToLineNumber(i.script(), i.pc());
3537         JSScript* script = i.script();
3538         char frameType =
3539             i.isInterp() ? 'i' :
3540             i.isBaseline() ? 'b' :
3541             i.isIon() ? 'I' :
3542             i.isAsmJS() ? 'A' :
3543             '?';
3544 
3545         sprinter.printf("#%d %14p %c   %s:%d (%p @ %d)\n",
3546                         depth, i.rawFramePtr(), frameType, filename, line,
3547                         script, script->pcToOffset(i.pc()));
3548     }
3549     fprintf(stdout, "%s", sprinter.string());
3550 #ifdef XP_WIN32
3551     if (IsDebuggerPresent()) {
3552         OutputDebugStringA(sprinter.string());
3553     }
3554 #endif
3555 }
3556 
3557 
3558 /* * */
3559 
3560 js::gc::AllocKind
allocKindForTenure(const js::Nursery & nursery) const3561 JSObject::allocKindForTenure(const js::Nursery& nursery) const
3562 {
3563     if (is<ArrayObject>()) {
3564         const ArrayObject& aobj = as<ArrayObject>();
3565         MOZ_ASSERT(aobj.numFixedSlots() == 0);
3566 
3567         /* Use minimal size object if we are just going to copy the pointer. */
3568         if (!nursery.isInside(aobj.getElementsHeader()))
3569             return AllocKind::OBJECT0_BACKGROUND;
3570 
3571         size_t nelements = aobj.getDenseCapacity();
3572         return GetBackgroundAllocKind(GetGCArrayKind(nelements));
3573     }
3574 
3575     if (is<JSFunction>())
3576         return as<JSFunction>().getAllocKind();
3577 
3578     /*
3579      * Typed arrays in the nursery may have a lazily allocated buffer, make
3580      * sure there is room for the array's fixed data when moving the array.
3581      */
3582     if (is<TypedArrayObject>() && !as<TypedArrayObject>().hasBuffer()) {
3583         size_t nbytes = as<TypedArrayObject>().byteLength();
3584         return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
3585     }
3586 
3587     // Proxies have finalizers and are not nursery allocated.
3588     MOZ_ASSERT(!IsProxy(this));
3589 
3590     // Unboxed plain objects are sized according to the data they store.
3591     if (is<UnboxedPlainObject>()) {
3592         size_t nbytes = as<UnboxedPlainObject>().layoutDontCheckGeneration().size();
3593         return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
3594     }
3595 
3596     // Unboxed arrays use inline data if their size is small enough.
3597     if (is<UnboxedArrayObject>()) {
3598         const UnboxedArrayObject* nobj = &as<UnboxedArrayObject>();
3599         size_t nbytes = UnboxedArrayObject::offsetOfInlineElements() +
3600                         nobj->capacity() * nobj->elementSize();
3601         if (nbytes <= JSObject::MAX_BYTE_SIZE)
3602             return GetGCObjectKindForBytes(nbytes);
3603         return AllocKind::OBJECT0;
3604     }
3605 
3606     // Inlined typed objects are followed by their data, so make sure we copy
3607     // it all over to the new object.
3608     if (is<InlineTypedObject>()) {
3609         // Figure out the size of this object, from the prototype's TypeDescr.
3610         // The objects we are traversing here are all tenured, so we don't need
3611         // to check forwarding pointers.
3612         TypeDescr& descr = as<InlineTypedObject>().typeDescr();
3613         MOZ_ASSERT(!IsInsideNursery(&descr));
3614         return InlineTypedObject::allocKindForTypeDescriptor(&descr);
3615     }
3616 
3617     // Outline typed objects use the minimum allocation kind.
3618     if (is<OutlineTypedObject>())
3619         return AllocKind::OBJECT0;
3620 
3621     // All nursery allocatable non-native objects are handled above.
3622     MOZ_ASSERT(isNative());
3623 
3624     AllocKind kind = GetGCObjectFixedSlotsKind(as<NativeObject>().numFixedSlots());
3625     MOZ_ASSERT(!IsBackgroundFinalized(kind));
3626     if (!CanBeFinalizedInBackground(kind, getClass()))
3627         return kind;
3628     return GetBackgroundAllocKind(kind);
3629 }
3630 
3631 
3632 void
addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,JS::ClassInfo * info)3633 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info)
3634 {
3635     if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots())
3636         info->objectsMallocHeapSlots += mallocSizeOf(as<NativeObject>().slots_);
3637 
3638     if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
3639         js::ObjectElements* elements = as<NativeObject>().getElementsHeader();
3640         if (!elements->isCopyOnWrite() || elements->ownerObject() == this)
3641             info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(elements);
3642     }
3643 
3644     // Other things may be measured in the future if DMD indicates it is worthwhile.
3645     if (is<JSFunction>() ||
3646         is<PlainObject>() ||
3647         is<ArrayObject>() ||
3648         is<CallObject>() ||
3649         is<RegExpObject>() ||
3650         is<ProxyObject>())
3651     {
3652         // Do nothing.  But this function is hot, and we win by getting the
3653         // common cases out of the way early.  Some stats on the most common
3654         // classes, as measured during a vanilla browser session:
3655         // - (53.7%, 53.7%): Function
3656         // - (18.0%, 71.7%): Object
3657         // - (16.9%, 88.6%): Array
3658         // - ( 3.9%, 92.5%): Call
3659         // - ( 2.8%, 95.3%): RegExp
3660         // - ( 1.0%, 96.4%): Proxy
3661 
3662         // Note that any JSClass that is special cased below likely needs to
3663         // specify the JSCLASS_DELAY_METADATA_CALLBACK flag, or else we will
3664         // probably crash if the object metadata callback attempts to get the
3665         // size of the new object (which Debugger code does) before private
3666         // slots are initialized.
3667     } else if (is<ArgumentsObject>()) {
3668         info->objectsMallocHeapMisc += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
3669     } else if (is<RegExpStaticsObject>()) {
3670         info->objectsMallocHeapMisc += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
3671     } else if (is<PropertyIteratorObject>()) {
3672         info->objectsMallocHeapMisc += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
3673     } else if (is<ArrayBufferObject>()) {
3674         ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
3675     } else if (is<SharedArrayBufferObject>()) {
3676         SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
3677     } else if (is<AsmJSModuleObject>()) {
3678         as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
3679                                               &info->objectsMallocHeapMisc);
3680 #ifdef JS_HAS_CTYPES
3681     } else {
3682         // This must be the last case.
3683         info->objectsMallocHeapMisc +=
3684             js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject*>(this));
3685 #endif
3686     }
3687 }
3688 
3689 size_t
sizeOfIncludingThisInNursery() const3690 JSObject::sizeOfIncludingThisInNursery() const
3691 {
3692     // This function doesn't concern itself yet with typed objects (bug 1133593)
3693     // nor unboxed objects (bug 1133592).
3694 
3695     MOZ_ASSERT(!isTenured());
3696 
3697     const Nursery& nursery = compartment()->runtimeFromAnyThread()->gc.nursery;
3698     size_t size = Arena::thingSize(allocKindForTenure(nursery));
3699 
3700     if (is<NativeObject>()) {
3701         const NativeObject& native = as<NativeObject>();
3702 
3703         size += native.numFixedSlots() * sizeof(Value);
3704         size += native.numDynamicSlots() * sizeof(Value);
3705 
3706         if (native.hasDynamicElements()) {
3707             js::ObjectElements& elements = *native.getElementsHeader();
3708             if (!elements.isCopyOnWrite() || elements.ownerObject() == this)
3709                 size += elements.capacity * sizeof(HeapSlot);
3710         }
3711 
3712         if (is<ArgumentsObject>())
3713             size += as<ArgumentsObject>().sizeOfData();
3714     }
3715 
3716     return size;
3717 }
3718 
3719 JS::ubi::Node::Size
size(mozilla::MallocSizeOf mallocSizeOf) const3720 JS::ubi::Concrete<JSObject>::size(mozilla::MallocSizeOf mallocSizeOf) const
3721 {
3722     JSObject& obj = get();
3723 
3724     if (!obj.isTenured())
3725         return obj.sizeOfIncludingThisInNursery();
3726 
3727     JS::ClassInfo info;
3728     obj.addSizeOfExcludingThis(mallocSizeOf, &info);
3729     return obj.tenuredSizeOfThis() + info.sizeOfAllThings();
3730 }
3731 
3732 template<> const char16_t JS::ubi::TracerConcrete<JSObject>::concreteTypeName[] =
3733     MOZ_UTF16("JSObject");
3734 
3735 void
traceChildren(JSTracer * trc)3736 JSObject::traceChildren(JSTracer* trc)
3737 {
3738     TraceEdge(trc, &group_, "group");
3739 
3740     const Class* clasp = group_->clasp();
3741     if (clasp->trace)
3742         clasp->trace(trc, this);
3743 
3744     if (clasp->isNative()) {
3745         NativeObject* nobj = &as<NativeObject>();
3746 
3747         TraceEdge(trc, &nobj->shape_, "shape");
3748 
3749         {
3750             GetObjectSlotNameFunctor func(nobj);
3751             JS::AutoTracingDetails ctx(trc, func);
3752             JS::AutoTracingIndex index(trc);
3753             // Tracing can mutate the target but cannot change the slot count,
3754             // but the compiler has no way of knowing this.
3755             const uint32_t nslots = nobj->slotSpan();
3756             for (uint32_t i = 0; i < nslots; ++i) {
3757                 TraceManuallyBarrieredEdge(trc, nobj->getSlotRef(i).unsafeUnbarrieredForTracing(),
3758                                            "object slot");
3759                 ++index;
3760             }
3761             MOZ_ASSERT(nslots == nobj->slotSpan());
3762         }
3763 
3764         do {
3765             if (nobj->denseElementsAreCopyOnWrite()) {
3766                 HeapPtrNativeObject& owner = nobj->getElementsHeader()->ownerObject();
3767                 if (owner != nobj) {
3768                     TraceEdge(trc, &owner, "objectElementsOwner");
3769                     break;
3770                 }
3771             }
3772 
3773             TraceRange(trc,
3774                        nobj->getDenseInitializedLength(),
3775                        static_cast<HeapSlot*>(nobj->getDenseElementsAllowCopyOnWrite()),
3776                        "objectElements");
3777         } while (false);
3778     }
3779 }
3780 
3781 static JSAtom*
displayAtomFromObjectGroup(ObjectGroup & group)3782 displayAtomFromObjectGroup(ObjectGroup& group)
3783 {
3784     TypeNewScript* script = group.newScript();
3785     if (!script)
3786         return nullptr;
3787 
3788     return script->function()->displayAtom();
3789 }
3790 
3791 bool
constructorDisplayAtom(JSContext * cx,js::MutableHandleAtom name)3792 JSObject::constructorDisplayAtom(JSContext* cx, js::MutableHandleAtom name)
3793 {
3794     ObjectGroup *g = getGroup(cx);
3795     if (!g)
3796         return false;
3797 
3798     name.set(displayAtomFromObjectGroup(*g));
3799     return true;
3800 }
3801 
3802 JSAtom*
maybeConstructorDisplayAtom() const3803 JSObject::maybeConstructorDisplayAtom() const
3804 {
3805     if (hasLazyGroup())
3806         return nullptr;
3807     return displayAtomFromObjectGroup(*group());
3808 }
3809