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