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