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