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 #include "builtin/Object.h"
8
9 #include "mozilla/ArrayUtils.h"
10
11 #include "jscntxt.h"
12
13 #include "builtin/Eval.h"
14 #include "frontend/BytecodeCompiler.h"
15 #include "jit/InlinableNatives.h"
16 #include "js/UniquePtr.h"
17 #include "vm/StringBuffer.h"
18
19 #include "jsobjinlines.h"
20
21 #include "vm/NativeObject-inl.h"
22 #include "vm/Shape-inl.h"
23
24 using namespace js;
25
26 using js::frontend::IsIdentifier;
27 using mozilla::ArrayLength;
28
29 bool
obj_construct(JSContext * cx,unsigned argc,Value * vp)30 js::obj_construct(JSContext* cx, unsigned argc, Value* vp)
31 {
32 CallArgs args = CallArgsFromVp(argc, vp);
33
34 RootedObject obj(cx, nullptr);
35 if (args.isConstructing() && (&args.newTarget().toObject() != &args.callee())) {
36 RootedObject newTarget(cx, &args.newTarget().toObject());
37 obj = CreateThis(cx, &PlainObject::class_, newTarget);
38 if (!obj)
39 return false;
40 } else if (args.length() > 0 && !args[0].isNullOrUndefined()) {
41 obj = ToObject(cx, args[0]);
42 if (!obj)
43 return false;
44 } else {
45 /* Make an object whether this was called with 'new' or not. */
46 if (!NewObjectScriptedCall(cx, &obj))
47 return false;
48 }
49
50 args.rval().setObject(*obj);
51 return true;
52 }
53
54 /* ES5 15.2.4.7. */
55 bool
obj_propertyIsEnumerable(JSContext * cx,unsigned argc,Value * vp)56 js::obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp)
57 {
58 CallArgs args = CallArgsFromVp(argc, vp);
59
60 HandleValue idValue = args.get(0);
61
62 // As an optimization, provide a fast path when rooting is not necessary and
63 // we can safely retrieve the attributes from the object's shape.
64
65 /* Steps 1-2. */
66 jsid id;
67 if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
68 JSObject* obj = &args.thisv().toObject();
69
70 /* Step 3. */
71 Shape* shape;
72 if (obj->isNative() &&
73 NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &shape))
74 {
75 /* Step 4. */
76 if (!shape) {
77 args.rval().setBoolean(false);
78 return true;
79 }
80
81 /* Step 5. */
82 unsigned attrs = GetShapeAttributes(obj, shape);
83 args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
84 return true;
85 }
86 }
87
88 /* Step 1. */
89 RootedId idRoot(cx);
90 if (!ToPropertyKey(cx, idValue, &idRoot))
91 return false;
92
93 /* Step 2. */
94 RootedObject obj(cx, ToObject(cx, args.thisv()));
95 if (!obj)
96 return false;
97
98 /* Step 3. */
99 Rooted<PropertyDescriptor> desc(cx);
100 if (!GetOwnPropertyDescriptor(cx, obj, idRoot, &desc))
101 return false;
102
103 /* Steps 4-5. */
104 args.rval().setBoolean(desc.object() && desc.enumerable());
105 return true;
106 }
107
108 #if JS_HAS_TOSOURCE
109 static bool
obj_toSource(JSContext * cx,unsigned argc,Value * vp)110 obj_toSource(JSContext* cx, unsigned argc, Value* vp)
111 {
112 CallArgs args = CallArgsFromVp(argc, vp);
113 JS_CHECK_RECURSION(cx, return false);
114
115 RootedObject obj(cx, ToObject(cx, args.thisv()));
116 if (!obj)
117 return false;
118
119 JSString* str = ObjectToSource(cx, obj);
120 if (!str)
121 return false;
122
123 args.rval().setString(str);
124 return true;
125 }
126
127 /*
128 * Given a function source string, return the offset and length of the part
129 * between '(function $name' and ')'.
130 */
131 template <typename CharT>
132 static bool
ArgsAndBodySubstring(mozilla::Range<const CharT> chars,size_t * outOffset,size_t * outLen)133 ArgsAndBodySubstring(mozilla::Range<const CharT> chars, size_t* outOffset, size_t* outLen)
134 {
135 const CharT* const start = chars.begin().get();
136 const CharT* const end = chars.end().get();
137 const CharT* s = start;
138
139 uint8_t parenChomp = 0;
140 if (s[0] == '(') {
141 s++;
142 parenChomp = 1;
143 }
144
145 /* Try to jump "function" keyword. */
146 s = js_strchr_limit(s, ' ', end);
147 if (!s)
148 return false;
149
150 /*
151 * Jump over the function's name: it can't be encoded as part
152 * of an ECMA getter or setter.
153 */
154 s = js_strchr_limit(s, '(', end);
155 if (!s)
156 return false;
157
158 if (*s == ' ')
159 s++;
160
161 *outOffset = s - start;
162 *outLen = end - s - parenChomp;
163 MOZ_ASSERT(*outOffset + *outLen <= chars.length());
164 return true;
165 }
166
167 JSString*
ObjectToSource(JSContext * cx,HandleObject obj)168 js::ObjectToSource(JSContext* cx, HandleObject obj)
169 {
170 /* If outermost, we need parentheses to be an expression, not a block. */
171 bool outermost = (cx->cycleDetectorSet.count() == 0);
172
173 AutoCycleDetector detector(cx, obj);
174 if (!detector.init())
175 return nullptr;
176 if (detector.foundCycle())
177 return NewStringCopyZ<CanGC>(cx, "{}");
178
179 StringBuffer buf(cx);
180 if (outermost && !buf.append('('))
181 return nullptr;
182 if (!buf.append('{'))
183 return nullptr;
184
185 RootedValue v0(cx), v1(cx);
186 MutableHandleValue val[2] = {&v0, &v1};
187
188 RootedString str0(cx), str1(cx);
189 MutableHandleString gsop[2] = {&str0, &str1};
190
191 AutoIdVector idv(cx);
192 if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv))
193 return nullptr;
194
195 bool comma = false;
196 for (size_t i = 0; i < idv.length(); ++i) {
197 RootedId id(cx, idv[i]);
198 Rooted<PropertyDescriptor> desc(cx);
199 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
200 return nullptr;
201
202 int valcnt = 0;
203 if (desc.object()) {
204 if (desc.isAccessorDescriptor()) {
205 if (desc.hasGetterObject() && desc.getterObject()) {
206 val[valcnt].setObject(*desc.getterObject());
207 gsop[valcnt].set(cx->names().get);
208 valcnt++;
209 }
210 if (desc.hasSetterObject() && desc.setterObject()) {
211 val[valcnt].setObject(*desc.setterObject());
212 gsop[valcnt].set(cx->names().set);
213 valcnt++;
214 }
215 } else {
216 valcnt = 1;
217 val[0].set(desc.value());
218 gsop[0].set(nullptr);
219 }
220 }
221
222 /* Convert id to a string. */
223 RootedString idstr(cx);
224 if (JSID_IS_SYMBOL(id)) {
225 RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
226 idstr = ValueToSource(cx, v);
227 if (!idstr)
228 return nullptr;
229 } else {
230 RootedValue idv(cx, IdToValue(id));
231 idstr = ToString<CanGC>(cx, idv);
232 if (!idstr)
233 return nullptr;
234
235 /*
236 * If id is a string that's not an identifier, or if it's a negative
237 * integer, then it must be quoted.
238 */
239 if (JSID_IS_ATOM(id)
240 ? !IsIdentifier(JSID_TO_ATOM(id))
241 : JSID_TO_INT(id) < 0)
242 {
243 idstr = QuoteString(cx, idstr, char16_t('\''));
244 if (!idstr)
245 return nullptr;
246 }
247 }
248
249 for (int j = 0; j < valcnt; j++) {
250 /* Convert val[j] to its canonical source form. */
251 JSString* valsource = ValueToSource(cx, val[j]);
252 if (!valsource)
253 return nullptr;
254
255 RootedLinearString valstr(cx, valsource->ensureLinear(cx));
256 if (!valstr)
257 return nullptr;
258
259 size_t voffset = 0;
260 size_t vlength = valstr->length();
261
262 /*
263 * Remove '(function ' from the beginning of valstr and ')' from the
264 * end so that we can put "get" in front of the function definition.
265 */
266 if (gsop[j] && IsFunctionObject(val[j])) {
267 bool success;
268 JS::AutoCheckCannotGC nogc;
269 if (valstr->hasLatin1Chars())
270 success = ArgsAndBodySubstring(valstr->latin1Range(nogc), &voffset, &vlength);
271 else
272 success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset, &vlength);
273 if (!success)
274 gsop[j].set(nullptr);
275 }
276
277 if (comma && !buf.append(", "))
278 return nullptr;
279 comma = true;
280
281 if (gsop[j]) {
282 if (!buf.append(gsop[j]) || !buf.append(' '))
283 return nullptr;
284 }
285 if (JSID_IS_SYMBOL(id) && !buf.append('['))
286 return nullptr;
287 if (!buf.append(idstr))
288 return nullptr;
289 if (JSID_IS_SYMBOL(id) && !buf.append(']'))
290 return nullptr;
291 if (!buf.append(gsop[j] ? ' ' : ':'))
292 return nullptr;
293
294 if (!buf.appendSubstring(valstr, voffset, vlength))
295 return nullptr;
296 }
297 }
298
299 if (!buf.append('}'))
300 return nullptr;
301 if (outermost && !buf.append(')'))
302 return nullptr;
303
304 return buf.finishString();
305 }
306 #endif /* JS_HAS_TOSOURCE */
307
308 // ES6 19.1.3.6
309 bool
obj_toString(JSContext * cx,unsigned argc,Value * vp)310 js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
311 {
312 CallArgs args = CallArgsFromVp(argc, vp);
313
314 // Step 1.
315 if (args.thisv().isUndefined()) {
316 args.rval().setString(cx->names().objectUndefined);
317 return true;
318 }
319
320 // Step 2.
321 if (args.thisv().isNull()) {
322 args.rval().setString(cx->names().objectNull);
323 return true;
324 }
325
326 // Step 3.
327 RootedObject obj(cx, ToObject(cx, args.thisv()));
328 if (!obj)
329 return false;
330
331 // Step 4.
332 bool isArray;
333 if (!IsArray(cx, obj, &isArray))
334 return false;
335
336 // Step 5.
337 RootedString builtinTag(cx);
338 if (isArray) {
339 builtinTag = cx->names().objectArray;
340 } else {
341 // Steps 6-13.
342 ESClass cls;
343 if (!GetBuiltinClass(cx, obj, &cls))
344 return false;
345
346 switch (cls) {
347 case ESClass::String:
348 builtinTag = cx->names().objectString;
349 break;
350 case ESClass::Arguments:
351 builtinTag = cx->names().objectArguments;
352 break;
353 case ESClass::Error:
354 builtinTag = cx->names().objectError;
355 break;
356 case ESClass::Boolean:
357 builtinTag = cx->names().objectBoolean;
358 break;
359 case ESClass::Number:
360 builtinTag = cx->names().objectNumber;
361 break;
362 case ESClass::Date:
363 builtinTag = cx->names().objectDate;
364 break;
365 case ESClass::RegExp:
366 builtinTag = cx->names().objectRegExp;
367 break;
368 default:
369 if (obj->isCallable()) {
370 // Non-standard: Prevent <object> from showing up as Function.
371 RootedObject unwrapped(cx, CheckedUnwrap(obj));
372 if (!unwrapped || !unwrapped->getClass()->isDOMClass())
373 builtinTag = cx->names().objectFunction;
374 }
375 break;
376 }
377 }
378 // Step 14.
379 // Currently omitted for non-standard fallback.
380
381 // Step 15.
382 RootedValue tag(cx);
383 RootedId toStringTagId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
384 if (!GetProperty(cx, obj, obj, toStringTagId, &tag))
385 return false;
386
387 // Step 16.
388 if (!tag.isString()) {
389 // Non-standard (bug 1277801): Use ClassName as a fallback in the interim
390 if (!builtinTag) {
391 const char* className = GetObjectClassName(cx, obj);
392
393 StringBuffer sb(cx);
394 if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
395 !sb.append("]"))
396 {
397 return false;
398 }
399
400 builtinTag = sb.finishString();
401 if (!builtinTag)
402 return false;
403 }
404
405 args.rval().setString(builtinTag);
406 return true;
407 }
408
409 // Step 17.
410 StringBuffer sb(cx);
411 if (!sb.append("[object ") || !sb.append(tag.toString()) || !sb.append("]"))
412 return false;
413
414 RootedString str(cx, sb.finishString());
415 if (!str)
416 return false;
417
418 args.rval().setString(str);
419 return true;
420 }
421
422
423 bool
obj_valueOf(JSContext * cx,unsigned argc,Value * vp)424 js::obj_valueOf(JSContext* cx, unsigned argc, Value* vp)
425 {
426 CallArgs args = CallArgsFromVp(argc, vp);
427 RootedObject obj(cx, ToObject(cx, args.thisv()));
428 if (!obj)
429 return false;
430 args.rval().setObject(*obj);
431 return true;
432 }
433
434 static bool
obj_setPrototypeOf(JSContext * cx,unsigned argc,Value * vp)435 obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
436 {
437 CallArgs args = CallArgsFromVp(argc, vp);
438
439 if (args.length() < 2) {
440 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
441 "Object.setPrototypeOf", "1", "");
442 return false;
443 }
444
445 /* Step 1-2. */
446 if (args[0].isNullOrUndefined()) {
447 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
448 args[0].isNull() ? "null" : "undefined", "object");
449 return false;
450 }
451
452 /* Step 3. */
453 if (!args[1].isObjectOrNull()) {
454 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
455 "Object.setPrototypeOf", "an object or null",
456 InformalValueTypeName(args[1]));
457 return false;
458 }
459
460 /* Step 4. */
461 if (!args[0].isObject()) {
462 args.rval().set(args[0]);
463 return true;
464 }
465
466 /* Step 5-7. */
467 RootedObject obj(cx, &args[0].toObject());
468 RootedObject newProto(cx, args[1].toObjectOrNull());
469 if (!SetPrototype(cx, obj, newProto))
470 return false;
471
472 /* Step 8. */
473 args.rval().set(args[0]);
474 return true;
475 }
476
477 #if JS_HAS_OBJ_WATCHPOINT
478
479 bool
WatchHandler(JSContext * cx,JSObject * obj_,jsid id_,const JS::Value & old,JS::Value * nvp,void * closure)480 js::WatchHandler(JSContext* cx, JSObject* obj_, jsid id_, const JS::Value& old,
481 JS::Value* nvp, void* closure)
482 {
483 RootedObject obj(cx, obj_);
484 RootedId id(cx, id_);
485
486 /* Avoid recursion on (obj, id) already being watched on cx. */
487 AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
488 if (resolving.alreadyStarted())
489 return true;
490
491 FixedInvokeArgs<3> args(cx);
492
493 args[0].set(IdToValue(id));
494 args[1].set(old);
495 args[2].set(*nvp);
496
497 RootedValue callable(cx, ObjectValue(*static_cast<JSObject*>(closure)));
498 RootedValue thisv(cx, ObjectValue(*obj));
499 RootedValue rv(cx);
500 if (!Call(cx, callable, thisv, args, &rv))
501 return false;
502
503 *nvp = rv;
504 return true;
505 }
506
507 static bool
obj_watch(JSContext * cx,unsigned argc,Value * vp)508 obj_watch(JSContext* cx, unsigned argc, Value* vp)
509 {
510 CallArgs args = CallArgsFromVp(argc, vp);
511
512 RootedObject obj(cx, ToObject(cx, args.thisv()));
513 if (!obj)
514 return false;
515
516 if (!GlobalObject::warnOnceAboutWatch(cx, obj))
517 return false;
518
519 if (args.length() <= 1) {
520 ReportMissingArg(cx, args.calleev(), 1);
521 return false;
522 }
523
524 RootedObject callable(cx, ValueToCallable(cx, args[1], args.length() - 2));
525 if (!callable)
526 return false;
527
528 RootedId propid(cx);
529 if (!ValueToId<CanGC>(cx, args[0], &propid))
530 return false;
531
532 if (!WatchProperty(cx, obj, propid, callable))
533 return false;
534
535 args.rval().setUndefined();
536 return true;
537 }
538
539 static bool
obj_unwatch(JSContext * cx,unsigned argc,Value * vp)540 obj_unwatch(JSContext* cx, unsigned argc, Value* vp)
541 {
542 CallArgs args = CallArgsFromVp(argc, vp);
543
544 RootedObject obj(cx, ToObject(cx, args.thisv()));
545 if (!obj)
546 return false;
547
548 if (!GlobalObject::warnOnceAboutWatch(cx, obj))
549 return false;
550
551 RootedId id(cx);
552 if (args.length() != 0) {
553 if (!ValueToId<CanGC>(cx, args[0], &id))
554 return false;
555 } else {
556 id = JSID_VOID;
557 }
558
559 if (!UnwatchProperty(cx, obj, id))
560 return false;
561
562 args.rval().setUndefined();
563 return true;
564 }
565
566 #endif /* JS_HAS_OBJ_WATCHPOINT */
567
568 /* ECMA 15.2.4.5. */
569 bool
obj_hasOwnProperty(JSContext * cx,unsigned argc,Value * vp)570 js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp)
571 {
572 CallArgs args = CallArgsFromVp(argc, vp);
573
574 HandleValue idValue = args.get(0);
575
576 // As an optimization, provide a fast path when rooting is not necessary and
577 // we can safely retrieve the object's shape.
578
579 /* Step 1, 2. */
580 jsid id;
581 if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
582 JSObject* obj = &args.thisv().toObject();
583 Shape* prop;
584 if (obj->isNative() &&
585 NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
586 {
587 args.rval().setBoolean(!!prop);
588 return true;
589 }
590 }
591
592 /* Step 1. */
593 RootedId idRoot(cx);
594 if (!ToPropertyKey(cx, idValue, &idRoot))
595 return false;
596
597 /* Step 2. */
598 RootedObject obj(cx, ToObject(cx, args.thisv()));
599 if (!obj)
600 return false;
601
602 /* Step 3. */
603 bool found;
604 if (!HasOwnProperty(cx, obj, idRoot, &found))
605 return false;
606
607 /* Step 4,5. */
608 args.rval().setBoolean(found);
609 return true;
610 }
611
612 /* ES5 15.2.4.6. */
613 static bool
obj_isPrototypeOf(JSContext * cx,unsigned argc,Value * vp)614 obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
615 {
616 CallArgs args = CallArgsFromVp(argc, vp);
617
618 /* Step 1. */
619 if (args.length() < 1 || !args[0].isObject()) {
620 args.rval().setBoolean(false);
621 return true;
622 }
623
624 /* Step 2. */
625 RootedObject obj(cx, ToObject(cx, args.thisv()));
626 if (!obj)
627 return false;
628
629 /* Step 3. */
630 bool isDelegate;
631 if (!IsDelegate(cx, obj, args[0], &isDelegate))
632 return false;
633 args.rval().setBoolean(isDelegate);
634 return true;
635 }
636
637 PlainObject*
ObjectCreateImpl(JSContext * cx,HandleObject proto,NewObjectKind newKind,HandleObjectGroup group)638 js::ObjectCreateImpl(JSContext* cx, HandleObject proto, NewObjectKind newKind,
639 HandleObjectGroup group)
640 {
641 // Give the new object a small number of fixed slots, like we do for empty
642 // object literals ({}).
643 gc::AllocKind allocKind = GuessObjectGCKind(0);
644
645 if (!proto) {
646 // Object.create(null) is common, optimize it by using an allocation
647 // site specific ObjectGroup. Because GetCallerInitGroup is pretty
648 // slow, the caller can pass in the group if it's known and we use that
649 // instead.
650 RootedObjectGroup ngroup(cx, group);
651 if (!ngroup) {
652 ngroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Null);
653 if (!ngroup)
654 return nullptr;
655 }
656
657 MOZ_ASSERT(!ngroup->proto().toObjectOrNull());
658
659 return NewObjectWithGroup<PlainObject>(cx, ngroup, allocKind, newKind);
660 }
661
662 return NewObjectWithGivenProto<PlainObject>(cx, proto, allocKind, newKind);
663 }
664
665 PlainObject*
ObjectCreateWithTemplate(JSContext * cx,HandlePlainObject templateObj)666 js::ObjectCreateWithTemplate(JSContext* cx, HandlePlainObject templateObj)
667 {
668 RootedObject proto(cx, templateObj->staticPrototype());
669 RootedObjectGroup group(cx, templateObj->group());
670 return ObjectCreateImpl(cx, proto, GenericObject, group);
671 }
672
673 // ES 2017 draft 19.1.2.3.1
674 static bool
ObjectDefineProperties(JSContext * cx,HandleObject obj,HandleValue properties)675 ObjectDefineProperties(JSContext* cx, HandleObject obj, HandleValue properties)
676 {
677 // Step 1. implicit
678 // Step 2.
679 RootedObject props(cx, ToObject(cx, properties));
680 if (!props)
681 return false;
682
683 // Step 3.
684 AutoIdVector keys(cx);
685 if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS | JSITER_HIDDEN, &keys))
686 return false;
687
688 RootedId nextKey(cx);
689 Rooted<PropertyDescriptor> desc(cx);
690 RootedValue descObj(cx);
691
692 // Step 4.
693 Rooted<PropertyDescriptorVector> descriptors(cx, PropertyDescriptorVector(cx));
694 AutoIdVector descriptorKeys(cx);
695
696 // Step 5.
697 for (size_t i = 0, len = keys.length(); i < len; i++) {
698 nextKey = keys[i];
699
700 // Step 5.a.
701 if (!GetOwnPropertyDescriptor(cx, props, nextKey, &desc))
702 return false;
703
704 // Step 5.b.
705 if (desc.object() && desc.enumerable()) {
706 if (!GetProperty(cx, props, props, nextKey, &descObj) ||
707 !ToPropertyDescriptor(cx, descObj, true, &desc) ||
708 !descriptors.append(desc) ||
709 !descriptorKeys.append(nextKey))
710 {
711 return false;
712 }
713 }
714 }
715
716 // Step 6.
717 for (size_t i = 0, len = descriptors.length(); i < len; i++) {
718 if (!DefineProperty(cx, obj, descriptorKeys[i], descriptors[i]))
719 return false;
720 }
721
722 return true;
723 }
724
725 // ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
726 bool
obj_create(JSContext * cx,unsigned argc,Value * vp)727 js::obj_create(JSContext* cx, unsigned argc, Value* vp)
728 {
729 CallArgs args = CallArgsFromVp(argc, vp);
730
731 // Step 1.
732 if (args.length() == 0) {
733 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
734 "Object.create", "0", "s");
735 return false;
736 }
737
738 if (!args[0].isObjectOrNull()) {
739 RootedValue v(cx, args[0]);
740 UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
741 if (!bytes)
742 return false;
743
744 JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
745 bytes.get(), "not an object or null");
746 return false;
747 }
748
749 // Step 2.
750 RootedObject proto(cx, args[0].toObjectOrNull());
751 RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
752 if (!obj)
753 return false;
754
755 // Step 3.
756 if (args.hasDefined(1)) {
757 if (!ObjectDefineProperties(cx, obj, args[1]))
758 return false;
759 }
760
761 // Step 4.
762 args.rval().setObject(*obj);
763 return true;
764 }
765
766 // ES6 draft rev27 (2014/08/24) 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)
767 bool
obj_getOwnPropertyDescriptor(JSContext * cx,unsigned argc,Value * vp)768 js::obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp)
769 {
770 CallArgs args = CallArgsFromVp(argc, vp);
771
772 // Steps 1-2.
773 RootedObject obj(cx, ToObject(cx, args.get(0)));
774 if (!obj)
775 return false;
776
777 // Steps 3-4.
778 RootedId id(cx);
779 if (!ToPropertyKey(cx, args.get(1), &id))
780 return false;
781
782 // Steps 5-7.
783 Rooted<PropertyDescriptor> desc(cx);
784 return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
785 JS::FromPropertyDescriptor(cx, desc, args.rval());
786 }
787
788 enum EnumerableOwnPropertiesKind {
789 Keys,
790 Values,
791 KeysAndValues
792 };
793
794 // ES7 proposal 2015-12-14
795 // http://tc39.github.io/proposal-object-values-entries/#EnumerableOwnProperties
796 static bool
EnumerableOwnProperties(JSContext * cx,const JS::CallArgs & args,EnumerableOwnPropertiesKind kind)797 EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args, EnumerableOwnPropertiesKind kind)
798 {
799 // Step 1. (Step 1 of Object.{keys,values,entries}, really.)
800 RootedObject obj(cx, ToObject(cx, args.get(0)));
801 if (!obj)
802 return false;
803
804 // Step 2.
805 AutoIdVector ids(cx);
806 if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &ids))
807 return false;
808
809 // Step 3.
810 AutoValueVector properties(cx);
811 size_t len = ids.length();
812 if (!properties.resize(len))
813 return false;
814
815 RootedId id(cx);
816 RootedValue key(cx);
817 RootedValue value(cx);
818 RootedShape shape(cx);
819 Rooted<PropertyDescriptor> desc(cx);
820 // Step 4.
821 size_t out = 0;
822 for (size_t i = 0; i < len; i++) {
823 id = ids[i];
824
825 // Step 4.a. (Symbols were filtered out in step 2.)
826 MOZ_ASSERT(!JSID_IS_SYMBOL(id));
827
828 if (kind != Values) {
829 if (!IdToStringOrSymbol(cx, id, &key))
830 return false;
831 }
832
833 // Step 4.a.i.
834 if (obj->is<NativeObject>()) {
835 HandleNativeObject nobj = obj.as<NativeObject>();
836 if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) {
837 value = nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
838 } else {
839 shape = nobj->lookup(cx, id);
840 if (!shape || !(GetShapeAttributes(nobj, shape) & JSPROP_ENUMERATE))
841 continue;
842 if (!shape->isAccessorShape()) {
843 if (!NativeGetExistingProperty(cx, nobj, nobj, shape, &value))
844 return false;
845 } else if (!GetProperty(cx, obj, obj, id, &value)) {
846 return false;
847 }
848 }
849 } else {
850 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
851 return false;
852
853 // Step 4.a.ii. (inverted.)
854 if (!desc.object() || !desc.enumerable())
855 continue;
856
857 // Step 4.a.ii.1.
858 // (Omitted because Object.keys doesn't use this implementation.)
859
860 // Step 4.a.ii.2.a.
861 if (obj->isNative() && desc.hasValue())
862 value = desc.value();
863 else if (!GetProperty(cx, obj, obj, id, &value))
864 return false;
865 }
866
867 // Steps 4.a.ii.2.b-c.
868 if (kind == Values)
869 properties[out++].set(value);
870 else if (!NewValuePair(cx, key, value, properties[out++]))
871 return false;
872 }
873
874 // Step 5.
875 // (Implemented in step 2.)
876
877 // Step 3 of Object.{keys,values,entries}
878 JSObject* aobj = NewDenseCopiedArray(cx, out, properties.begin());
879 if (!aobj)
880 return false;
881
882 args.rval().setObject(*aobj);
883 return true;
884 }
885
886 // ES7 proposal 2015-12-14
887 // http://tc39.github.io/proposal-object-values-entries/#Object.keys
888 static bool
obj_keys(JSContext * cx,unsigned argc,Value * vp)889 obj_keys(JSContext* cx, unsigned argc, Value* vp)
890 {
891 CallArgs args = CallArgsFromVp(argc, vp);
892 return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY);
893 }
894
895 // ES7 proposal 2015-12-14
896 // http://tc39.github.io/proposal-object-values-entries/#Object.values
897 static bool
obj_values(JSContext * cx,unsigned argc,Value * vp)898 obj_values(JSContext* cx, unsigned argc, Value* vp)
899 {
900 CallArgs args = CallArgsFromVp(argc, vp);
901 return EnumerableOwnProperties(cx, args, Values);
902 }
903
904 // ES7 proposal 2015-12-14
905 // http://tc39.github.io/proposal-object-values-entries/#Object.entries
906 static bool
obj_entries(JSContext * cx,unsigned argc,Value * vp)907 obj_entries(JSContext* cx, unsigned argc, Value* vp)
908 {
909 CallArgs args = CallArgsFromVp(argc, vp);
910 return EnumerableOwnProperties(cx, args, KeysAndValues);
911 }
912
913 /* ES6 draft 15.2.3.16 */
914 static bool
obj_is(JSContext * cx,unsigned argc,Value * vp)915 obj_is(JSContext* cx, unsigned argc, Value* vp)
916 {
917 CallArgs args = CallArgsFromVp(argc, vp);
918
919 bool same;
920 if (!SameValue(cx, args.get(0), args.get(1), &same))
921 return false;
922
923 args.rval().setBoolean(same);
924 return true;
925 }
926
927 bool
IdToStringOrSymbol(JSContext * cx,HandleId id,MutableHandleValue result)928 js::IdToStringOrSymbol(JSContext* cx, HandleId id, MutableHandleValue result)
929 {
930 if (JSID_IS_INT(id)) {
931 JSString* str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
932 if (!str)
933 return false;
934 result.setString(str);
935 } else if (JSID_IS_ATOM(id)) {
936 result.setString(JSID_TO_STRING(id));
937 } else {
938 result.setSymbol(JSID_TO_SYMBOL(id));
939 }
940 return true;
941 }
942
943 /* ES6 draft rev 25 (2014 May 22) 19.1.2.8.1 */
944 bool
GetOwnPropertyKeys(JSContext * cx,const JS::CallArgs & args,unsigned flags)945 js::GetOwnPropertyKeys(JSContext* cx, const JS::CallArgs& args, unsigned flags)
946 {
947 // Steps 1-2.
948 RootedObject obj(cx, ToObject(cx, args.get(0)));
949 if (!obj)
950 return false;
951
952 // Steps 3-10.
953 AutoIdVector keys(cx);
954 if (!GetPropertyKeys(cx, obj, flags, &keys))
955 return false;
956
957 // Step 11.
958 AutoValueVector vals(cx);
959 if (!vals.resize(keys.length()))
960 return false;
961
962 for (size_t i = 0, len = keys.length(); i < len; i++) {
963 MOZ_ASSERT_IF(JSID_IS_SYMBOL(keys[i]), flags & JSITER_SYMBOLS);
964 MOZ_ASSERT_IF(!JSID_IS_SYMBOL(keys[i]), !(flags & JSITER_SYMBOLSONLY));
965 if (!IdToStringOrSymbol(cx, keys[i], vals[i]))
966 return false;
967 }
968
969 JSObject* aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
970 if (!aobj)
971 return false;
972
973 args.rval().setObject(*aobj);
974 return true;
975 }
976
977 bool
obj_getOwnPropertyNames(JSContext * cx,unsigned argc,Value * vp)978 js::obj_getOwnPropertyNames(JSContext* cx, unsigned argc, Value* vp)
979 {
980 CallArgs args = CallArgsFromVp(argc, vp);
981 return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN);
982 }
983
984 /* ES6 draft rev 25 (2014 May 22) 19.1.2.8 */
985 static bool
obj_getOwnPropertySymbols(JSContext * cx,unsigned argc,Value * vp)986 obj_getOwnPropertySymbols(JSContext* cx, unsigned argc, Value* vp)
987 {
988 CallArgs args = CallArgsFromVp(argc, vp);
989 return GetOwnPropertyKeys(cx, args,
990 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY);
991 }
992
993 /* ES6 draft rev 32 (2015 Feb 2) 19.1.2.4: Object.defineProperty(O, P, Attributes) */
994 bool
obj_defineProperty(JSContext * cx,unsigned argc,Value * vp)995 js::obj_defineProperty(JSContext* cx, unsigned argc, Value* vp)
996 {
997 CallArgs args = CallArgsFromVp(argc, vp);
998
999 // Steps 1-3.
1000 RootedObject obj(cx);
1001 if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperty", &obj))
1002 return false;
1003 RootedId id(cx);
1004 if (!ToPropertyKey(cx, args.get(1), &id))
1005 return false;
1006
1007 // Steps 4-5.
1008 Rooted<PropertyDescriptor> desc(cx);
1009 if (!ToPropertyDescriptor(cx, args.get(2), true, &desc))
1010 return false;
1011
1012 // Steps 6-8.
1013 if (!DefineProperty(cx, obj, id, desc))
1014 return false;
1015 args.rval().setObject(*obj);
1016 return true;
1017 }
1018
1019 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
1020 static bool
obj_defineProperties(JSContext * cx,unsigned argc,Value * vp)1021 obj_defineProperties(JSContext* cx, unsigned argc, Value* vp)
1022 {
1023 CallArgs args = CallArgsFromVp(argc, vp);
1024
1025 /* Steps 1 and 7. */
1026 RootedObject obj(cx);
1027 if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperties", &obj))
1028 return false;
1029 args.rval().setObject(*obj);
1030
1031 /* Step 2. */
1032 if (args.length() < 2) {
1033 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
1034 "Object.defineProperties", "0", "s");
1035 return false;
1036 }
1037
1038 /* Steps 3-6. */
1039 return ObjectDefineProperties(cx, obj, args[1]);
1040 }
1041
1042 // ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
1043 static bool
obj_preventExtensions(JSContext * cx,unsigned argc,Value * vp)1044 obj_preventExtensions(JSContext* cx, unsigned argc, Value* vp)
1045 {
1046 CallArgs args = CallArgsFromVp(argc, vp);
1047 args.rval().set(args.get(0));
1048
1049 // Step 1.
1050 if (!args.get(0).isObject())
1051 return true;
1052
1053 // Steps 2-5.
1054 RootedObject obj(cx, &args.get(0).toObject());
1055 return PreventExtensions(cx, obj);
1056 }
1057
1058 // ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
1059 static bool
obj_freeze(JSContext * cx,unsigned argc,Value * vp)1060 obj_freeze(JSContext* cx, unsigned argc, Value* vp)
1061 {
1062 CallArgs args = CallArgsFromVp(argc, vp);
1063 args.rval().set(args.get(0));
1064
1065 // Step 1.
1066 if (!args.get(0).isObject())
1067 return true;
1068
1069 // Steps 2-5.
1070 RootedObject obj(cx, &args.get(0).toObject());
1071 return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen);
1072 }
1073
1074 // ES6 draft rev27 (2014/08/24) 19.1.2.12 Object.isFrozen(O)
1075 static bool
obj_isFrozen(JSContext * cx,unsigned argc,Value * vp)1076 obj_isFrozen(JSContext* cx, unsigned argc, Value* vp)
1077 {
1078 CallArgs args = CallArgsFromVp(argc, vp);
1079
1080 // Step 1.
1081 bool frozen = true;
1082
1083 // Step 2.
1084 if (args.get(0).isObject()) {
1085 RootedObject obj(cx, &args.get(0).toObject());
1086 if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Frozen, &frozen))
1087 return false;
1088 }
1089 args.rval().setBoolean(frozen);
1090 return true;
1091 }
1092
1093 // ES6 draft rev27 (2014/08/24) 19.1.2.17 Object.seal(O)
1094 static bool
obj_seal(JSContext * cx,unsigned argc,Value * vp)1095 obj_seal(JSContext* cx, unsigned argc, Value* vp)
1096 {
1097 CallArgs args = CallArgsFromVp(argc, vp);
1098 args.rval().set(args.get(0));
1099
1100 // Step 1.
1101 if (!args.get(0).isObject())
1102 return true;
1103
1104 // Steps 2-5.
1105 RootedObject obj(cx, &args.get(0).toObject());
1106 return SetIntegrityLevel(cx, obj, IntegrityLevel::Sealed);
1107 }
1108
1109 // ES6 draft rev27 (2014/08/24) 19.1.2.13 Object.isSealed(O)
1110 static bool
obj_isSealed(JSContext * cx,unsigned argc,Value * vp)1111 obj_isSealed(JSContext* cx, unsigned argc, Value* vp)
1112 {
1113 CallArgs args = CallArgsFromVp(argc, vp);
1114
1115 // Step 1.
1116 bool sealed = true;
1117
1118 // Step 2.
1119 if (args.get(0).isObject()) {
1120 RootedObject obj(cx, &args.get(0).toObject());
1121 if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Sealed, &sealed))
1122 return false;
1123 }
1124 args.rval().setBoolean(sealed);
1125 return true;
1126 }
1127
1128 static bool
ProtoGetter(JSContext * cx,unsigned argc,Value * vp)1129 ProtoGetter(JSContext* cx, unsigned argc, Value* vp)
1130 {
1131 CallArgs args = CallArgsFromVp(argc, vp);
1132
1133 RootedValue thisv(cx, args.thisv());
1134 if (thisv.isPrimitive()) {
1135 if (thisv.isNullOrUndefined()) {
1136 ReportIncompatible(cx, args);
1137 return false;
1138 }
1139
1140 if (!BoxNonStrictThis(cx, thisv, &thisv))
1141 return false;
1142 }
1143
1144 RootedObject obj(cx, &thisv.toObject());
1145 RootedObject proto(cx);
1146 if (!GetPrototype(cx, obj, &proto))
1147 return false;
1148
1149 args.rval().setObjectOrNull(proto);
1150 return true;
1151 }
1152
1153 namespace js {
1154 size_t sSetProtoCalled = 0;
1155 } // namespace js
1156
1157 static bool
ProtoSetter(JSContext * cx,unsigned argc,Value * vp)1158 ProtoSetter(JSContext* cx, unsigned argc, Value* vp)
1159 {
1160 CallArgs args = CallArgsFromVp(argc, vp);
1161
1162 HandleValue thisv = args.thisv();
1163 if (thisv.isNullOrUndefined()) {
1164 ReportIncompatible(cx, args);
1165 return false;
1166 }
1167 if (thisv.isPrimitive()) {
1168 // Mutating a boxed primitive's [[Prototype]] has no side effects.
1169 args.rval().setUndefined();
1170 return true;
1171 }
1172
1173 if (!cx->runningWithTrustedPrincipals())
1174 ++sSetProtoCalled;
1175
1176 Rooted<JSObject*> obj(cx, &args.thisv().toObject());
1177
1178 /* Do nothing if __proto__ isn't being set to an object or null. */
1179 if (args.length() == 0 || !args[0].isObjectOrNull()) {
1180 args.rval().setUndefined();
1181 return true;
1182 }
1183
1184 Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull());
1185 if (!SetPrototype(cx, obj, newProto))
1186 return false;
1187
1188 args.rval().setUndefined();
1189 return true;
1190 }
1191
1192 static const JSFunctionSpec object_methods[] = {
1193 #if JS_HAS_TOSOURCE
1194 JS_FN(js_toSource_str, obj_toSource, 0,0),
1195 #endif
1196 JS_FN(js_toString_str, obj_toString, 0,0),
1197 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0),
1198 JS_FN(js_valueOf_str, obj_valueOf, 0,0),
1199 #if JS_HAS_OBJ_WATCHPOINT
1200 JS_FN(js_watch_str, obj_watch, 2,0),
1201 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
1202 #endif
1203 JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
1204 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
1205 JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
1206 #if JS_OLD_GETTER_SETTER_METHODS
1207 JS_SELF_HOSTED_FN(js_defineGetter_str, "ObjectDefineGetter", 2,0),
1208 JS_SELF_HOSTED_FN(js_defineSetter_str, "ObjectDefineSetter", 2,0),
1209 JS_SELF_HOSTED_FN(js_lookupGetter_str, "ObjectLookupGetter", 1,0),
1210 JS_SELF_HOSTED_FN(js_lookupSetter_str, "ObjectLookupSetter", 1,0),
1211 #endif
1212 JS_FS_END
1213 };
1214
1215 static const JSPropertySpec object_properties[] = {
1216 #if JS_HAS_OBJ_PROTO_PROP
1217 JS_PSGS("__proto__", ProtoGetter, ProtoSetter, 0),
1218 #endif
1219 JS_PS_END
1220 };
1221
1222 static const JSFunctionSpec object_static_methods[] = {
1223 JS_SELF_HOSTED_FN("assign", "ObjectStaticAssign", 2, 0),
1224 JS_SELF_HOSTED_FN("getPrototypeOf", "ObjectGetPrototypeOf", 1, 0),
1225 JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0),
1226 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2, 0),
1227 JS_SELF_HOSTED_FN("getOwnPropertyDescriptors", "ObjectGetOwnPropertyDescriptors", 1, 0),
1228 JS_FN("keys", obj_keys, 1, 0),
1229 JS_FN("values", obj_values, 1, 0),
1230 JS_FN("entries", obj_entries, 1, 0),
1231 JS_FN("is", obj_is, 2, 0),
1232 JS_FN("defineProperty", obj_defineProperty, 3, 0),
1233 JS_FN("defineProperties", obj_defineProperties, 2, 0),
1234 JS_INLINABLE_FN("create", obj_create, 2, 0, ObjectCreate),
1235 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1, 0),
1236 JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1, 0),
1237 JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, 0),
1238 JS_FN("preventExtensions", obj_preventExtensions, 1, 0),
1239 JS_FN("freeze", obj_freeze, 1, 0),
1240 JS_FN("isFrozen", obj_isFrozen, 1, 0),
1241 JS_FN("seal", obj_seal, 1, 0),
1242 JS_FN("isSealed", obj_isSealed, 1, 0),
1243 JS_FS_END
1244 };
1245
1246 static JSObject*
CreateObjectConstructor(JSContext * cx,JSProtoKey key)1247 CreateObjectConstructor(JSContext* cx, JSProtoKey key)
1248 {
1249 Rooted<GlobalObject*> self(cx, cx->global());
1250 if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
1251 return nullptr;
1252
1253 /* Create the Object function now that we have a [[Prototype]] for it. */
1254 return NewNativeConstructor(cx, obj_construct, 1, HandlePropertyName(cx->names().Object),
1255 gc::AllocKind::FUNCTION, SingletonObject);
1256 }
1257
1258 static JSObject*
CreateObjectPrototype(JSContext * cx,JSProtoKey key)1259 CreateObjectPrototype(JSContext* cx, JSProtoKey key)
1260 {
1261 MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
1262 MOZ_ASSERT(cx->global()->isNative());
1263
1264 /*
1265 * Create |Object.prototype| first, mirroring CreateBlankProto but for the
1266 * prototype of the created object.
1267 */
1268 RootedPlainObject objectProto(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr,
1269 SingletonObject));
1270 if (!objectProto)
1271 return nullptr;
1272
1273 bool succeeded;
1274 if (!SetImmutablePrototype(cx, objectProto, &succeeded))
1275 return nullptr;
1276 MOZ_ASSERT(succeeded,
1277 "should have been able to make a fresh Object.prototype's "
1278 "[[Prototype]] immutable");
1279
1280 /*
1281 * The default 'new' type of Object.prototype is required by type inference
1282 * to have unknown properties, to simplify handling of e.g. heterogenous
1283 * objects in JSON and script literals.
1284 */
1285 if (!JSObject::setNewGroupUnknown(cx, &PlainObject::class_, objectProto))
1286 return nullptr;
1287
1288 return objectProto;
1289 }
1290
1291 static bool
FinishObjectClassInit(JSContext * cx,JS::HandleObject ctor,JS::HandleObject proto)1292 FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
1293 {
1294 Rooted<GlobalObject*> global(cx, cx->global());
1295
1296 /* ES5 15.1.2.1. */
1297 RootedId evalId(cx, NameToId(cx->names().eval));
1298 JSObject* evalobj = DefineFunction(cx, global, evalId, IndirectEval, 1,
1299 JSFUN_STUB_GSOPS | JSPROP_RESOLVING);
1300 if (!evalobj)
1301 return false;
1302 global->setOriginalEval(evalobj);
1303
1304 Rooted<NativeObject*> holder(cx, GlobalObject::getIntrinsicsHolder(cx, global));
1305 if (!holder)
1306 return false;
1307
1308 /*
1309 * The global object should have |Object.prototype| as its [[Prototype]].
1310 * Eventually we'd like to have standard classes be there from the start,
1311 * and thus we would know we were always setting what had previously been a
1312 * null [[Prototype]], but right now some code assumes it can set the
1313 * [[Prototype]] before standard classes have been initialized. For now,
1314 * only set the [[Prototype]] if it hasn't already been set.
1315 */
1316 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
1317 if (global->shouldSplicePrototype(cx)) {
1318 if (!global->splicePrototype(cx, global->getClass(), tagged))
1319 return false;
1320 }
1321 return true;
1322 }
1323
1324 static const ClassSpec PlainObjectClassSpec = {
1325 CreateObjectConstructor,
1326 CreateObjectPrototype,
1327 object_static_methods,
1328 nullptr,
1329 object_methods,
1330 object_properties,
1331 FinishObjectClassInit
1332 };
1333
1334 const Class PlainObject::class_ = {
1335 js_Object_str,
1336 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
1337 JS_NULL_CLASS_OPS,
1338 &PlainObjectClassSpec
1339 };
1340
1341 const Class* const js::ObjectClassPtr = &PlainObject::class_;
1342