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, ¤tDesc))
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, ®enerate))
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(©OnWrite))
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