Coverage Report

Created: 2018-09-25 14:53

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