1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
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/MaybeOneOf.h"
10 #include "mozilla/Range.h"
11 #include "mozilla/RangedPtr.h"
12 
13 #include <algorithm>
14 
15 #include "builtin/BigInt.h"
16 #include "builtin/Eval.h"
17 #include "builtin/SelfHostingDefines.h"
18 #include "frontend/BytecodeCompiler.h"
19 #include "jit/InlinableNatives.h"
20 #include "js/PropertySpec.h"
21 #include "js/UniquePtr.h"
22 #include "util/StringBuffer.h"
23 #include "util/Text.h"
24 #include "vm/AsyncFunction.h"
25 #include "vm/DateObject.h"
26 #include "vm/EqualityOperations.h"  // js::SameValue
27 #include "vm/ErrorObject.h"
28 #include "vm/JSContext.h"
29 #include "vm/PlainObject.h"  // js::PlainObject
30 #include "vm/RegExpObject.h"
31 #include "vm/ToSource.h"  // js::ValueToSource
32 
33 #include "vm/JSObject-inl.h"
34 #include "vm/NativeObject-inl.h"
35 #include "vm/Shape-inl.h"
36 
37 #ifdef FUZZING
38 #  include "builtin/TestingFunctions.h"
39 #endif
40 
41 using namespace js;
42 
43 using js::frontend::IsIdentifier;
44 
45 using mozilla::Range;
46 using mozilla::RangedPtr;
47 
obj_construct(JSContext * cx,unsigned argc,Value * vp)48 bool js::obj_construct(JSContext* cx, unsigned argc, Value* vp) {
49   CallArgs args = CallArgsFromVp(argc, vp);
50 
51   RootedObject obj(cx, nullptr);
52   if (args.isConstructing() &&
53       (&args.newTarget().toObject() != &args.callee())) {
54     RootedObject newTarget(cx, &args.newTarget().toObject());
55     obj = CreateThis(cx, &PlainObject::class_, newTarget);
56     if (!obj) {
57       return false;
58     }
59   } else if (args.length() > 0 && !args[0].isNullOrUndefined()) {
60     obj = ToObject(cx, args[0]);
61     if (!obj) {
62       return false;
63     }
64   } else {
65     /* Make an object whether this was called with 'new' or not. */
66     if (!NewObjectScriptedCall(cx, &obj)) {
67       return false;
68     }
69   }
70 
71   args.rval().setObject(*obj);
72   return true;
73 }
74 
75 /* ES5 15.2.4.7. */
obj_propertyIsEnumerable(JSContext * cx,unsigned argc,Value * vp)76 bool js::obj_propertyIsEnumerable(JSContext* cx, unsigned argc, Value* vp) {
77   CallArgs args = CallArgsFromVp(argc, vp);
78 
79   HandleValue idValue = args.get(0);
80 
81   // As an optimization, provide a fast path when rooting is not necessary and
82   // we can safely retrieve the attributes from the object's shape.
83 
84   /* Steps 1-2. */
85   jsid id;
86   if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
87     JSObject* obj = &args.thisv().toObject();
88 
89     /* Step 3. */
90     PropertyResult prop;
91     if (obj->isNative() && NativeLookupOwnProperty<NoGC>(
92                                cx, &obj->as<NativeObject>(), id, &prop)) {
93       /* Step 4. */
94       if (!prop) {
95         args.rval().setBoolean(false);
96         return true;
97       }
98 
99       /* Step 5. */
100       unsigned attrs = GetPropertyAttributes(obj, prop);
101       args.rval().setBoolean((attrs & JSPROP_ENUMERATE) != 0);
102       return true;
103     }
104   }
105 
106   /* Step 1. */
107   RootedId idRoot(cx);
108   if (!ToPropertyKey(cx, idValue, &idRoot)) {
109     return false;
110   }
111 
112   /* Step 2. */
113   RootedObject obj(cx, ToObject(cx, args.thisv()));
114   if (!obj) {
115     return false;
116   }
117 
118   /* Step 3. */
119   Rooted<PropertyDescriptor> desc(cx);
120   if (!GetOwnPropertyDescriptor(cx, obj, idRoot, &desc)) {
121     return false;
122   }
123 
124   /* Steps 4-5. */
125   args.rval().setBoolean(desc.object() && desc.enumerable());
126   return true;
127 }
128 
obj_toSource(JSContext * cx,unsigned argc,Value * vp)129 static bool obj_toSource(JSContext* cx, unsigned argc, Value* vp) {
130   CallArgs args = CallArgsFromVp(argc, vp);
131 
132   if (!CheckRecursionLimit(cx)) {
133     return false;
134   }
135 
136   RootedObject obj(cx, ToObject(cx, args.thisv()));
137   if (!obj) {
138     return false;
139   }
140 
141   JSString* str = ObjectToSource(cx, obj);
142   if (!str) {
143     return false;
144   }
145 
146   args.rval().setString(str);
147   return true;
148 }
149 
150 template <typename CharT>
Consume(RangedPtr<const CharT> & s,RangedPtr<const CharT> e,const char * chars)151 static bool Consume(RangedPtr<const CharT>& s, RangedPtr<const CharT> e,
152                     const char* chars) {
153   MOZ_ASSERT(s <= e);
154   size_t len = strlen(chars);
155   if (e - s < len) {
156     return false;
157   }
158   if (!EqualChars(s.get(), chars, len)) {
159     return false;
160   }
161   s += len;
162   return true;
163 }
164 
165 template <typename CharT>
ConsumeUntil(RangedPtr<const CharT> & s,RangedPtr<const CharT> e,char16_t ch)166 static bool ConsumeUntil(RangedPtr<const CharT>& s, RangedPtr<const CharT> e,
167                          char16_t ch) {
168   MOZ_ASSERT(s <= e);
169   const CharT* result = js_strchr_limit(s.get(), ch, e.get());
170   if (!result) {
171     return false;
172   }
173   s += result - s.get();
174   MOZ_ASSERT(*s == ch);
175   return true;
176 }
177 
178 template <typename CharT>
ConsumeSpaces(RangedPtr<const CharT> & s,RangedPtr<const CharT> e)179 static void ConsumeSpaces(RangedPtr<const CharT>& s, RangedPtr<const CharT> e) {
180   while (s < e && *s == ' ') {
181     s++;
182   }
183 }
184 
185 /*
186  * Given a function source string, return the offset and length of the part
187  * between '(function $name' and ')'.
188  */
189 template <typename CharT>
ArgsAndBodySubstring(Range<const CharT> chars,size_t * outOffset,size_t * outLen)190 static bool ArgsAndBodySubstring(Range<const CharT> chars, size_t* outOffset,
191                                  size_t* outLen) {
192   const RangedPtr<const CharT> start = chars.begin();
193   RangedPtr<const CharT> s = start;
194   RangedPtr<const CharT> e = chars.end();
195 
196   if (s == e) {
197     return false;
198   }
199 
200   // Remove enclosing parentheses.
201   if (*s == '(' && *(e - 1) == ')') {
202     s++;
203     e--;
204   }
205 
206   // Support the following cases, with spaces between tokens:
207   //
208   //   -+---------+-+------------+-+-----+-+- [ - <any> - ] - ( -+-
209   //    |         | |            | |     | |                     |
210   //    +- async -+ +- function -+ +- * -+ +- <any> - ( ---------+
211   //                |            |
212   //                +- get ------+
213   //                |            |
214   //                +- set ------+
215   //
216   // This accepts some invalid syntax, but we don't care, since it's only
217   // used by the non-standard toSource, and we're doing a best-effort attempt
218   // here.
219 
220   (void)Consume(s, e, "async");
221   ConsumeSpaces(s, e);
222   (void)(Consume(s, e, "function") || Consume(s, e, "get") ||
223          Consume(s, e, "set"));
224   ConsumeSpaces(s, e);
225   (void)Consume(s, e, "*");
226   ConsumeSpaces(s, e);
227 
228   // Jump over the function's name.
229   if (Consume(s, e, "[")) {
230     if (!ConsumeUntil(s, e, ']')) {
231       return false;
232     }
233     s++;  // Skip ']'.
234     ConsumeSpaces(s, e);
235     if (s >= e || *s != '(') {
236       return false;
237     }
238   } else {
239     if (!ConsumeUntil(s, e, '(')) {
240       return false;
241     }
242   }
243 
244   MOZ_ASSERT(*s == '(');
245 
246   *outOffset = s - start;
247   *outLen = e - s;
248   MOZ_ASSERT(*outOffset + *outLen <= chars.length());
249   return true;
250 }
251 
252 enum class PropertyKind { Getter, Setter, Method, Normal };
253 
ObjectToSource(JSContext * cx,HandleObject obj)254 JSString* js::ObjectToSource(JSContext* cx, HandleObject obj) {
255   /* If outermost, we need parentheses to be an expression, not a block. */
256   bool outermost = cx->cycleDetectorVector().empty();
257 
258   AutoCycleDetector detector(cx, obj);
259   if (!detector.init()) {
260     return nullptr;
261   }
262   if (detector.foundCycle()) {
263     return NewStringCopyZ<CanGC>(cx, "{}");
264   }
265 
266   JSStringBuilder buf(cx);
267   if (outermost && !buf.append('(')) {
268     return nullptr;
269   }
270   if (!buf.append('{')) {
271     return nullptr;
272   }
273 
274   RootedIdVector idv(cx);
275   if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_SYMBOLS, &idv)) {
276     return nullptr;
277   }
278 
279   bool comma = false;
280 
281   auto AddProperty = [cx, &comma, &buf](HandleId id, HandleValue val,
282                                         PropertyKind kind) -> bool {
283     /* Convert id to a string. */
284     RootedString idstr(cx);
285     if (JSID_IS_SYMBOL(id)) {
286       RootedValue v(cx, SymbolValue(JSID_TO_SYMBOL(id)));
287       idstr = ValueToSource(cx, v);
288       if (!idstr) {
289         return false;
290       }
291     } else {
292       RootedValue idv(cx, IdToValue(id));
293       idstr = ToString<CanGC>(cx, idv);
294       if (!idstr) {
295         return false;
296       }
297 
298       /*
299        * If id is a string that's not an identifier, or if it's a
300        * negative integer, then it must be quoted.
301        */
302       if (JSID_IS_ATOM(id) ? !IsIdentifier(JSID_TO_ATOM(id))
303                            : JSID_TO_INT(id) < 0) {
304         UniqueChars quotedId = QuoteString(cx, idstr, '\'');
305         if (!quotedId) {
306           return false;
307         }
308         idstr = NewStringCopyZ<CanGC>(cx, quotedId.get());
309         if (!idstr) {
310           return false;
311         }
312       }
313     }
314 
315     RootedString valsource(cx, ValueToSource(cx, val));
316     if (!valsource) {
317       return false;
318     }
319 
320     RootedLinearString valstr(cx, valsource->ensureLinear(cx));
321     if (!valstr) {
322       return false;
323     }
324 
325     if (comma && !buf.append(", ")) {
326       return false;
327     }
328     comma = true;
329 
330     size_t voffset, vlength;
331 
332     // Methods and accessors can return exact syntax of source, that fits
333     // into property without adding property name or "get"/"set" prefix.
334     // Use the exact syntax when the following conditions are met:
335     //
336     //   * It's a function object
337     //     (exclude proxies)
338     //   * Function's kind and property's kind are same
339     //     (this can be false for dynamically defined properties)
340     //   * Function has explicit name
341     //     (this can be false for computed property and dynamically defined
342     //      properties)
343     //   * Function's name and property's name are same
344     //     (this can be false for dynamically defined properties)
345     if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
346         kind == PropertyKind::Method) {
347       RootedFunction fun(cx);
348       if (val.toObject().is<JSFunction>()) {
349         fun = &val.toObject().as<JSFunction>();
350         // Method's case should be checked on caller.
351         if (((fun->isGetter() && kind == PropertyKind::Getter) ||
352              (fun->isSetter() && kind == PropertyKind::Setter) ||
353              kind == PropertyKind::Method) &&
354             fun->explicitName()) {
355           bool result;
356           if (!EqualStrings(cx, fun->explicitName(), idstr, &result)) {
357             return false;
358           }
359 
360           if (result) {
361             if (!buf.append(valstr)) {
362               return false;
363             }
364             return true;
365           }
366         }
367       }
368 
369       {
370         // When falling back try to generate a better string
371         // representation by skipping the prelude, and also removing
372         // the enclosing parentheses.
373         bool success;
374         JS::AutoCheckCannotGC nogc;
375         if (valstr->hasLatin1Chars()) {
376           success = ArgsAndBodySubstring(valstr->latin1Range(nogc), &voffset,
377                                          &vlength);
378         } else {
379           success = ArgsAndBodySubstring(valstr->twoByteRange(nogc), &voffset,
380                                          &vlength);
381         }
382         if (!success) {
383           kind = PropertyKind::Normal;
384         }
385       }
386 
387       if (kind == PropertyKind::Getter) {
388         if (!buf.append("get ")) {
389           return false;
390         }
391       } else if (kind == PropertyKind::Setter) {
392         if (!buf.append("set ")) {
393           return false;
394         }
395       } else if (kind == PropertyKind::Method && fun) {
396         if (fun->isAsync()) {
397           if (!buf.append("async ")) {
398             return false;
399           }
400         }
401 
402         if (fun->isGenerator()) {
403           if (!buf.append('*')) {
404             return false;
405           }
406         }
407       }
408     }
409 
410     bool needsBracket = JSID_IS_SYMBOL(id);
411     if (needsBracket && !buf.append('[')) {
412       return false;
413     }
414     if (!buf.append(idstr)) {
415       return false;
416     }
417     if (needsBracket && !buf.append(']')) {
418       return false;
419     }
420 
421     if (kind == PropertyKind::Getter || kind == PropertyKind::Setter ||
422         kind == PropertyKind::Method) {
423       if (!buf.appendSubstring(valstr, voffset, vlength)) {
424         return false;
425       }
426     } else {
427       if (!buf.append(':')) {
428         return false;
429       }
430       if (!buf.append(valstr)) {
431         return false;
432       }
433     }
434     return true;
435   };
436 
437   RootedId id(cx);
438   Rooted<PropertyDescriptor> desc(cx);
439   RootedValue val(cx);
440   for (size_t i = 0; i < idv.length(); ++i) {
441     id = idv[i];
442     if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
443       return nullptr;
444     }
445 
446     if (!desc.object()) {
447       continue;
448     }
449 
450     if (desc.isAccessorDescriptor()) {
451       if (desc.hasGetterObject() && desc.getterObject()) {
452         val.setObject(*desc.getterObject());
453         if (!AddProperty(id, val, PropertyKind::Getter)) {
454           return nullptr;
455         }
456       }
457       if (desc.hasSetterObject() && desc.setterObject()) {
458         val.setObject(*desc.setterObject());
459         if (!AddProperty(id, val, PropertyKind::Setter)) {
460           return nullptr;
461         }
462       }
463       continue;
464     }
465 
466     val.set(desc.value());
467 
468     JSFunction* fun;
469     if (IsFunctionObject(val, &fun) && fun->isMethod()) {
470       if (!AddProperty(id, val, PropertyKind::Method)) {
471         return nullptr;
472       }
473       continue;
474     }
475 
476     if (!AddProperty(id, val, PropertyKind::Normal)) {
477       return nullptr;
478     }
479   }
480 
481   if (!buf.append('}')) {
482     return nullptr;
483   }
484   if (outermost && !buf.append(')')) {
485     return nullptr;
486   }
487 
488   return buf.finishString();
489 }
490 
GetBuiltinTagSlow(JSContext * cx,HandleObject obj,MutableHandleString builtinTag)491 static bool GetBuiltinTagSlow(JSContext* cx, HandleObject obj,
492                               MutableHandleString builtinTag) {
493   // Step 4.
494   bool isArray;
495   if (!IsArray(cx, obj, &isArray)) {
496     return false;
497   }
498 
499   // Step 5.
500   if (isArray) {
501     builtinTag.set(cx->names().objectArray);
502     return true;
503   }
504 
505   // Steps 6-13.
506   ESClass cls;
507   if (!GetBuiltinClass(cx, obj, &cls)) {
508     return false;
509   }
510 
511   switch (cls) {
512     case ESClass::String:
513       builtinTag.set(cx->names().objectString);
514       return true;
515     case ESClass::Arguments:
516       builtinTag.set(cx->names().objectArguments);
517       return true;
518     case ESClass::Error:
519       builtinTag.set(cx->names().objectError);
520       return true;
521     case ESClass::Boolean:
522       builtinTag.set(cx->names().objectBoolean);
523       return true;
524     case ESClass::Number:
525       builtinTag.set(cx->names().objectNumber);
526       return true;
527     case ESClass::Date:
528       builtinTag.set(cx->names().objectDate);
529       return true;
530     case ESClass::RegExp:
531       builtinTag.set(cx->names().objectRegExp);
532       return true;
533     default:
534       if (obj->isCallable()) {
535         // Non-standard: Prevent <object> from showing up as Function.
536         RootedObject unwrapped(cx, CheckedUnwrapDynamic(obj, cx));
537         if (!unwrapped || !unwrapped->getClass()->isDOMClass()) {
538           builtinTag.set(cx->names().objectFunction);
539           return true;
540         }
541       }
542       builtinTag.set(nullptr);
543       return true;
544   }
545 }
546 
GetBuiltinTagFast(JSObject * obj,const JSClass * clasp,JSContext * cx)547 static MOZ_ALWAYS_INLINE JSString* GetBuiltinTagFast(JSObject* obj,
548                                                      const JSClass* clasp,
549                                                      JSContext* cx) {
550   MOZ_ASSERT(clasp == obj->getClass());
551   MOZ_ASSERT(!clasp->isProxy());
552 
553   // Optimize the non-proxy case to bypass GetBuiltinClass.
554   if (clasp == &PlainObject::class_) {
555     // This is not handled by GetBuiltinTagSlow, but this case is by far
556     // the most common so we optimize it here.
557     return cx->names().objectObject;
558   }
559 
560   if (clasp == &ArrayObject::class_) {
561     return cx->names().objectArray;
562   }
563 
564   if (clasp == &JSFunction::class_) {
565     return cx->names().objectFunction;
566   }
567 
568   if (clasp == &StringObject::class_) {
569     return cx->names().objectString;
570   }
571 
572   if (clasp == &NumberObject::class_) {
573     return cx->names().objectNumber;
574   }
575 
576   if (clasp == &BooleanObject::class_) {
577     return cx->names().objectBoolean;
578   }
579 
580   if (clasp == &DateObject::class_) {
581     return cx->names().objectDate;
582   }
583 
584   if (clasp == &RegExpObject::class_) {
585     return cx->names().objectRegExp;
586   }
587 
588   if (obj->is<ArgumentsObject>()) {
589     return cx->names().objectArguments;
590   }
591 
592   if (obj->is<ErrorObject>()) {
593     return cx->names().objectError;
594   }
595 
596   if (obj->isCallable() && !obj->getClass()->isDOMClass()) {
597     // Non-standard: Prevent <object> from showing up as Function.
598     return cx->names().objectFunction;
599   }
600 
601   return nullptr;
602 }
603 
604 // ES6 19.1.3.6
obj_toString(JSContext * cx,unsigned argc,Value * vp)605 bool js::obj_toString(JSContext* cx, unsigned argc, Value* vp) {
606   CallArgs args = CallArgsFromVp(argc, vp);
607 
608   // Step 1.
609   if (args.thisv().isUndefined()) {
610     args.rval().setString(cx->names().objectUndefined);
611     return true;
612   }
613 
614   // Step 2.
615   if (args.thisv().isNull()) {
616     args.rval().setString(cx->names().objectNull);
617     return true;
618   }
619 
620   // Step 3.
621   RootedObject obj(cx, ToObject(cx, args.thisv()));
622   if (!obj) {
623     return false;
624   }
625 
626   RootedString builtinTag(cx);
627   const JSClass* clasp = obj->getClass();
628   if (MOZ_UNLIKELY(clasp->isProxy())) {
629     if (!GetBuiltinTagSlow(cx, obj, &builtinTag)) {
630       return false;
631     }
632   } else {
633     builtinTag = GetBuiltinTagFast(obj, clasp, cx);
634 #ifdef DEBUG
635     // Assert this fast path is correct and matches BuiltinTagSlow. The
636     // only exception is the PlainObject case: we special-case it here
637     // because it's so common, but BuiltinTagSlow doesn't handle this.
638     RootedString builtinTagSlow(cx);
639     if (!GetBuiltinTagSlow(cx, obj, &builtinTagSlow)) {
640       return false;
641     }
642     if (clasp == &PlainObject::class_) {
643       MOZ_ASSERT(!builtinTagSlow);
644     } else {
645       MOZ_ASSERT(builtinTagSlow == builtinTag);
646     }
647 #endif
648   }
649 
650   // Step 14.
651   if (!builtinTag) {
652     builtinTag = cx->names().objectObject;
653   }
654 
655   // Step 15.
656   RootedValue tag(cx);
657   if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toStringTag,
658                                     &tag)) {
659     return false;
660   }
661 
662   // Step 16.
663   if (!tag.isString()) {
664     args.rval().setString(builtinTag);
665     return true;
666   }
667 
668   // Step 17.
669   StringBuffer sb(cx);
670   if (!sb.append("[object ") || !sb.append(tag.toString()) || !sb.append(']')) {
671     return false;
672   }
673 
674   JSString* str = sb.finishAtom();
675   if (!str) {
676     return false;
677   }
678 
679   args.rval().setString(str);
680   return true;
681 }
682 
ObjectClassToString(JSContext * cx,HandleObject obj)683 JSString* js::ObjectClassToString(JSContext* cx, HandleObject obj) {
684   const JSClass* clasp = obj->getClass();
685 
686   if (JSString* tag = GetBuiltinTagFast(obj, clasp, cx)) {
687     return tag;
688   }
689 
690   const char* className = clasp->name;
691   StringBuffer sb(cx);
692   if (!sb.append("[object ") || !sb.append(className, strlen(className)) ||
693       !sb.append(']')) {
694     return nullptr;
695   }
696 
697   return sb.finishAtom();
698 }
699 
obj_setPrototypeOf(JSContext * cx,unsigned argc,Value * vp)700 static bool obj_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
701   CallArgs args = CallArgsFromVp(argc, vp);
702 
703   if (!args.requireAtLeast(cx, "Object.setPrototypeOf", 2)) {
704     return false;
705   }
706 
707   /* Step 1-2. */
708   if (args[0].isNullOrUndefined()) {
709     JS_ReportErrorNumberASCII(
710         cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
711         args[0].isNull() ? "null" : "undefined", "object");
712     return false;
713   }
714 
715   /* Step 3. */
716   if (!args[1].isObjectOrNull()) {
717     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
718                               JSMSG_NOT_EXPECTED_TYPE, "Object.setPrototypeOf",
719                               "an object or null",
720                               InformalValueTypeName(args[1]));
721     return false;
722   }
723 
724   /* Step 4. */
725   if (!args[0].isObject()) {
726     args.rval().set(args[0]);
727     return true;
728   }
729 
730   /* Step 5-7. */
731   RootedObject obj(cx, &args[0].toObject());
732   RootedObject newProto(cx, args[1].toObjectOrNull());
733   if (!SetPrototype(cx, obj, newProto)) {
734     return false;
735   }
736 
737   /* Step 8. */
738   args.rval().set(args[0]);
739   return true;
740 }
741 
PropertyIsEnumerable(JSContext * cx,HandleObject obj,HandleId id,bool * enumerable)742 static bool PropertyIsEnumerable(JSContext* cx, HandleObject obj, HandleId id,
743                                  bool* enumerable) {
744   PropertyResult prop;
745   if (obj->isNative() &&
746       NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop)) {
747     if (!prop) {
748       *enumerable = false;
749       return true;
750     }
751 
752     unsigned attrs = GetPropertyAttributes(obj, prop);
753     *enumerable = (attrs & JSPROP_ENUMERATE) != 0;
754     return true;
755   }
756 
757   Rooted<PropertyDescriptor> desc(cx);
758   if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
759     return false;
760   }
761 
762   *enumerable = desc.object() && desc.enumerable();
763   return true;
764 }
765 
TryAssignNative(JSContext * cx,HandleObject to,HandleObject from,bool * optimized)766 static bool TryAssignNative(JSContext* cx, HandleObject to, HandleObject from,
767                             bool* optimized) {
768   *optimized = false;
769 
770   if (!from->isNative() || !to->isNative()) {
771     return true;
772   }
773 
774   // Don't use the fast path if |from| may have extra indexed or lazy
775   // properties.
776   NativeObject* fromNative = &from->as<NativeObject>();
777   if (fromNative->getDenseInitializedLength() > 0 || fromNative->isIndexed() ||
778       fromNative->is<TypedArrayObject>() ||
779       fromNative->getClass()->getNewEnumerate() ||
780       fromNative->getClass()->getEnumerate()) {
781     return true;
782   }
783 
784   // Get a list of |from| shapes. As long as from->lastProperty() == fromShape
785   // we can use this to speed up both the enumerability check and the GetProp.
786 
787   using ShapeVector = GCVector<Shape*, 8>;
788   Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
789 
790   RootedShape fromShape(cx, fromNative->lastProperty());
791   for (Shape::Range<NoGC> r(fromShape); !r.empty(); r.popFront()) {
792     // Symbol properties need to be assigned last. For now fall back to the
793     // slow path if we see a symbol property.
794     if (MOZ_UNLIKELY(JSID_IS_SYMBOL(r.front().propidRaw()))) {
795       return true;
796     }
797     if (MOZ_UNLIKELY(!shapes.append(&r.front()))) {
798       return false;
799     }
800   }
801 
802   *optimized = true;
803 
804   RootedShape shape(cx);
805   RootedValue propValue(cx);
806   RootedId nextKey(cx);
807   RootedValue toReceiver(cx, ObjectValue(*to));
808 
809   for (size_t i = shapes.length(); i > 0; i--) {
810     shape = shapes[i - 1];
811     nextKey = shape->propid();
812 
813     // Ensure |from| is still native: a getter/setter might have been swapped
814     // with a non-native object.
815     if (MOZ_LIKELY(from->isNative() &&
816                    from->as<NativeObject>().lastProperty() == fromShape &&
817                    shape->isDataProperty())) {
818       if (!shape->enumerable()) {
819         continue;
820       }
821       propValue = from->as<NativeObject>().getSlot(shape->slot());
822     } else {
823       // |from| changed shape or the property is not a data property, so
824       // we have to do the slower enumerability check and GetProp.
825       bool enumerable;
826       if (!PropertyIsEnumerable(cx, from, nextKey, &enumerable)) {
827         return false;
828       }
829       if (!enumerable) {
830         continue;
831       }
832       if (!GetProperty(cx, from, from, nextKey, &propValue)) {
833         return false;
834       }
835     }
836 
837     ObjectOpResult result;
838     if (MOZ_UNLIKELY(
839             !SetProperty(cx, to, nextKey, propValue, toReceiver, result))) {
840       return false;
841     }
842     if (MOZ_UNLIKELY(!result.checkStrict(cx, to, nextKey))) {
843       return false;
844     }
845   }
846 
847   return true;
848 }
849 
AssignSlow(JSContext * cx,HandleObject to,HandleObject from)850 static bool AssignSlow(JSContext* cx, HandleObject to, HandleObject from) {
851   // Step 4.b.ii.
852   RootedIdVector keys(cx);
853   if (!GetPropertyKeys(
854           cx, from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &keys)) {
855     return false;
856   }
857 
858   // Step 4.c.
859   RootedId nextKey(cx);
860   RootedValue propValue(cx);
861   for (size_t i = 0, len = keys.length(); i < len; i++) {
862     nextKey = keys[i];
863 
864     // Step 4.c.i.
865     bool enumerable;
866     if (MOZ_UNLIKELY(!PropertyIsEnumerable(cx, from, nextKey, &enumerable))) {
867       return false;
868     }
869     if (!enumerable) {
870       continue;
871     }
872 
873     // Step 4.c.ii.1.
874     if (MOZ_UNLIKELY(!GetProperty(cx, from, from, nextKey, &propValue))) {
875       return false;
876     }
877 
878     // Step 4.c.ii.2.
879     if (MOZ_UNLIKELY(!SetProperty(cx, to, nextKey, propValue))) {
880       return false;
881     }
882   }
883 
884   return true;
885 }
886 
JS_AssignObject(JSContext * cx,JS::HandleObject target,JS::HandleObject src)887 JS_PUBLIC_API bool JS_AssignObject(JSContext* cx, JS::HandleObject target,
888                                    JS::HandleObject src) {
889   bool optimized;
890   if (!TryAssignNative(cx, target, src, &optimized)) {
891     return false;
892   }
893   if (optimized) {
894     return true;
895   }
896 
897   return AssignSlow(cx, target, src);
898 }
899 
900 // ES2018 draft rev 48ad2688d8f964da3ea8c11163ef20eb126fb8a4
901 // 19.1.2.1 Object.assign(target, ...sources)
obj_assign(JSContext * cx,unsigned argc,Value * vp)902 static bool obj_assign(JSContext* cx, unsigned argc, Value* vp) {
903   CallArgs args = CallArgsFromVp(argc, vp);
904 
905   // Step 1.
906   RootedObject to(cx, ToObject(cx, args.get(0)));
907   if (!to) {
908     return false;
909   }
910 
911   // Note: step 2 is implicit. If there are 0 arguments, ToObject throws. If
912   // there's 1 argument, the loop below is a no-op.
913 
914   // Step 4.
915   RootedObject from(cx);
916   for (size_t i = 1; i < args.length(); i++) {
917     // Step 4.a.
918     if (args[i].isNullOrUndefined()) {
919       continue;
920     }
921 
922     // Step 4.b.i.
923     from = ToObject(cx, args[i]);
924     if (!from) {
925       return false;
926     }
927 
928     // Steps 4.b.ii, 4.c.
929     if (!JS_AssignObject(cx, to, from)) {
930       return false;
931     }
932   }
933 
934   // Step 5.
935   args.rval().setObject(*to);
936   return true;
937 }
938 
939 /* ES5 15.2.4.6. */
obj_isPrototypeOf(JSContext * cx,unsigned argc,Value * vp)940 static bool obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
941   CallArgs args = CallArgsFromVp(argc, vp);
942 
943   /* Step 1. */
944   if (args.length() < 1 || !args[0].isObject()) {
945     args.rval().setBoolean(false);
946     return true;
947   }
948 
949   /* Step 2. */
950   RootedObject obj(cx, ToObject(cx, args.thisv()));
951   if (!obj) {
952     return false;
953   }
954 
955   /* Step 3. */
956   bool isPrototype;
957   if (!IsPrototypeOf(cx, obj, &args[0].toObject(), &isPrototype)) {
958     return false;
959   }
960   args.rval().setBoolean(isPrototype);
961   return true;
962 }
963 
ObjectCreateImpl(JSContext * cx,HandleObject proto,NewObjectKind newKind,HandleObjectGroup group)964 PlainObject* js::ObjectCreateImpl(JSContext* cx, HandleObject proto,
965                                   NewObjectKind newKind,
966                                   HandleObjectGroup group) {
967   // Give the new object a small number of fixed slots, like we do for empty
968   // object literals ({}).
969   gc::AllocKind allocKind = GuessObjectGCKind(0);
970 
971   if (!proto) {
972     // Object.create(null) is common, optimize it by using an allocation
973     // site specific ObjectGroup. Because GetCallerInitGroup is pretty
974     // slow, the caller can pass in the group if it's known and we use that
975     // instead.
976     RootedObjectGroup ngroup(cx, group);
977     if (!ngroup) {
978       ngroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Null);
979       if (!ngroup) {
980         return nullptr;
981       }
982     }
983 
984     MOZ_ASSERT(!ngroup->proto().toObjectOrNull());
985 
986     return NewObjectWithGroup<PlainObject>(cx, ngroup, allocKind, newKind);
987   }
988 
989   return NewObjectWithGivenProtoAndKinds<PlainObject>(cx, proto, allocKind,
990                                                       newKind);
991 }
992 
ObjectCreateWithTemplate(JSContext * cx,HandlePlainObject templateObj)993 PlainObject* js::ObjectCreateWithTemplate(JSContext* cx,
994                                           HandlePlainObject templateObj) {
995   RootedObject proto(cx, templateObj->staticPrototype());
996   RootedObjectGroup group(cx, templateObj->group());
997   return ObjectCreateImpl(cx, proto, GenericObject, group);
998 }
999 
1000 // ES 2017 draft 19.1.2.3.1
ObjectDefineProperties(JSContext * cx,HandleObject obj,HandleValue properties,bool * failedOnWindowProxy)1001 static bool ObjectDefineProperties(JSContext* cx, HandleObject obj,
1002                                    HandleValue properties,
1003                                    bool* failedOnWindowProxy) {
1004   // Step 1. implicit
1005   // Step 2.
1006   RootedObject props(cx, ToObject(cx, properties));
1007   if (!props) {
1008     return false;
1009   }
1010 
1011   // Step 3.
1012   RootedIdVector keys(cx);
1013   if (!GetPropertyKeys(
1014           cx, props, JSITER_OWNONLY | JSITER_SYMBOLS | JSITER_HIDDEN, &keys)) {
1015     return false;
1016   }
1017 
1018   RootedId nextKey(cx);
1019   Rooted<PropertyDescriptor> desc(cx);
1020   RootedValue descObj(cx);
1021 
1022   // Step 4.
1023   Rooted<PropertyDescriptorVector> descriptors(cx,
1024                                                PropertyDescriptorVector(cx));
1025   RootedIdVector descriptorKeys(cx);
1026 
1027   // Step 5.
1028   for (size_t i = 0, len = keys.length(); i < len; i++) {
1029     nextKey = keys[i];
1030 
1031     // Step 5.a.
1032     if (!GetOwnPropertyDescriptor(cx, props, nextKey, &desc)) {
1033       return false;
1034     }
1035 
1036     // Step 5.b.
1037     if (desc.object() && desc.enumerable()) {
1038       if (!GetProperty(cx, props, props, nextKey, &descObj) ||
1039           !ToPropertyDescriptor(cx, descObj, true, &desc) ||
1040           !descriptors.append(desc) || !descriptorKeys.append(nextKey)) {
1041         return false;
1042       }
1043     }
1044   }
1045 
1046   // Step 6.
1047   *failedOnWindowProxy = false;
1048   for (size_t i = 0, len = descriptors.length(); i < len; i++) {
1049     ObjectOpResult result;
1050     if (!DefineProperty(cx, obj, descriptorKeys[i], descriptors[i], result)) {
1051       return false;
1052     }
1053 
1054     if (!result.ok()) {
1055       if (result.failureCode() == JSMSG_CANT_DEFINE_WINDOW_NC) {
1056         *failedOnWindowProxy = true;
1057       } else if (!result.checkStrict(cx, obj, descriptorKeys[i])) {
1058         return false;
1059       }
1060     }
1061   }
1062 
1063   return true;
1064 }
1065 
1066 // ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
obj_create(JSContext * cx,unsigned argc,Value * vp)1067 bool js::obj_create(JSContext* cx, unsigned argc, Value* vp) {
1068   CallArgs args = CallArgsFromVp(argc, vp);
1069 
1070   // Step 1.
1071   if (!args.requireAtLeast(cx, "Object.create", 1)) {
1072     return false;
1073   }
1074 
1075   if (!args[0].isObjectOrNull()) {
1076     UniqueChars bytes =
1077         DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args[0], nullptr);
1078     if (!bytes) {
1079       return false;
1080     }
1081 
1082     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1083                              JSMSG_UNEXPECTED_TYPE, bytes.get(),
1084                              "not an object or null");
1085     return false;
1086   }
1087 
1088   // Step 2.
1089   RootedObject proto(cx, args[0].toObjectOrNull());
1090   RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
1091   if (!obj) {
1092     return false;
1093   }
1094 
1095   // Step 3.
1096   if (args.hasDefined(1)) {
1097     // we can't ever end up with failures to define on a WindowProxy
1098     // here, because "obj" is never a WindowProxy.
1099     bool failedOnWindowProxy = false;
1100     if (!ObjectDefineProperties(cx, obj, args[1], &failedOnWindowProxy)) {
1101       return false;
1102     }
1103     MOZ_ASSERT(!failedOnWindowProxy, "How did we get a WindowProxy here?");
1104   }
1105 
1106   // Step 4.
1107   args.rval().setObject(*obj);
1108   return true;
1109 }
1110 
1111 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1112 // 6.2.4.4 FromPropertyDescriptor ( Desc )
FromPropertyDescriptorToArray(JSContext * cx,Handle<PropertyDescriptor> desc,MutableHandleValue vp)1113 static bool FromPropertyDescriptorToArray(JSContext* cx,
1114                                           Handle<PropertyDescriptor> desc,
1115                                           MutableHandleValue vp) {
1116   // Step 1.
1117   if (!desc.object()) {
1118     vp.setUndefined();
1119     return true;
1120   }
1121 
1122   // Steps 2-11.
1123   // Retrieve all property descriptor fields and place them into the result
1124   // array. The actual return object is created in self-hosted code for
1125   // performance reasons.
1126 
1127   int32_t attrsAndKind = 0;
1128   if (desc.enumerable()) {
1129     attrsAndKind |= ATTR_ENUMERABLE;
1130   }
1131   if (desc.configurable()) {
1132     attrsAndKind |= ATTR_CONFIGURABLE;
1133   }
1134   if (!desc.isAccessorDescriptor()) {
1135     if (desc.writable()) {
1136       attrsAndKind |= ATTR_WRITABLE;
1137     }
1138     attrsAndKind |= DATA_DESCRIPTOR_KIND;
1139   } else {
1140     attrsAndKind |= ACCESSOR_DESCRIPTOR_KIND;
1141   }
1142 
1143   RootedArrayObject result(cx);
1144   if (!desc.isAccessorDescriptor()) {
1145     result = NewDenseFullyAllocatedArray(cx, 2);
1146     if (!result) {
1147       return false;
1148     }
1149     result->setDenseInitializedLength(2);
1150 
1151     result->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX,
1152                              Int32Value(attrsAndKind));
1153     result->initDenseElement(PROP_DESC_VALUE_INDEX, desc.value());
1154   } else {
1155     result = NewDenseFullyAllocatedArray(cx, 3);
1156     if (!result) {
1157       return false;
1158     }
1159     result->setDenseInitializedLength(3);
1160 
1161     result->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX,
1162                              Int32Value(attrsAndKind));
1163 
1164     if (JSObject* get = desc.getterObject()) {
1165       result->initDenseElement(PROP_DESC_GETTER_INDEX, ObjectValue(*get));
1166     } else {
1167       result->initDenseElement(PROP_DESC_GETTER_INDEX, UndefinedValue());
1168     }
1169 
1170     if (JSObject* set = desc.setterObject()) {
1171       result->initDenseElement(PROP_DESC_SETTER_INDEX, ObjectValue(*set));
1172     } else {
1173       result->initDenseElement(PROP_DESC_SETTER_INDEX, UndefinedValue());
1174     }
1175   }
1176 
1177   vp.setObject(*result);
1178   return true;
1179 }
1180 
1181 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1182 // 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P )
GetOwnPropertyDescriptorToArray(JSContext * cx,unsigned argc,Value * vp)1183 bool js::GetOwnPropertyDescriptorToArray(JSContext* cx, unsigned argc,
1184                                          Value* vp) {
1185   CallArgs args = CallArgsFromVp(argc, vp);
1186   MOZ_ASSERT(args.length() == 2);
1187 
1188   // Step 1.
1189   RootedObject obj(cx, ToObject(cx, args[0]));
1190   if (!obj) {
1191     return false;
1192   }
1193 
1194   // Step 2.
1195   RootedId id(cx);
1196   if (!ToPropertyKey(cx, args[1], &id)) {
1197     return false;
1198   }
1199 
1200   // Step 3.
1201   Rooted<PropertyDescriptor> desc(cx);
1202   if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
1203     return false;
1204   }
1205 
1206   // [[GetOwnProperty]] is spec'ed to always return a complete property
1207   // descriptor record (ES2017, 6.1.7.3, invariants of [[GetOwnProperty]]).
1208   desc.assertCompleteIfFound();
1209 
1210   // Step 4.
1211   return FromPropertyDescriptorToArray(cx, desc, args.rval());
1212 }
1213 
NewValuePair(JSContext * cx,HandleValue val1,HandleValue val2,MutableHandleValue rval)1214 static bool NewValuePair(JSContext* cx, HandleValue val1, HandleValue val2,
1215                          MutableHandleValue rval) {
1216   ArrayObject* array = NewDenseFullyAllocatedArray(cx, 2);
1217   if (!array) {
1218     return false;
1219   }
1220 
1221   array->setDenseInitializedLength(2);
1222   array->initDenseElement(0, val1);
1223   array->initDenseElement(1, val2);
1224 
1225   rval.setObject(*array);
1226   return true;
1227 }
1228 
1229 enum class EnumerableOwnPropertiesKind { Keys, Values, KeysAndValues, Names };
1230 
HasEnumerableStringNonDataProperties(NativeObject * obj)1231 static bool HasEnumerableStringNonDataProperties(NativeObject* obj) {
1232   // We also check for enumerability and symbol properties, so uninteresting
1233   // non-data properties like |array.length| don't let us fall into the slow
1234   // path.
1235   for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
1236     Shape* shape = &r.front();
1237     if (!shape->isDataProperty() && shape->enumerable() &&
1238         !JSID_IS_SYMBOL(shape->propid())) {
1239       return true;
1240     }
1241   }
1242   return false;
1243 }
1244 
1245 template <EnumerableOwnPropertiesKind kind>
TryEnumerableOwnPropertiesNative(JSContext * cx,HandleObject obj,MutableHandleValue rval,bool * optimized)1246 static bool TryEnumerableOwnPropertiesNative(JSContext* cx, HandleObject obj,
1247                                              MutableHandleValue rval,
1248                                              bool* optimized) {
1249   *optimized = false;
1250 
1251   // Use the fast path if |obj| has neither extra indexed properties nor a
1252   // newEnumerate hook. String objects need to be special-cased, because
1253   // they're only marked as indexed after their enumerate hook ran. And
1254   // because their enumerate hook is slowish, it's more performant to
1255   // exclude them directly instead of executing the hook first.
1256   if (!obj->isNative() || obj->as<NativeObject>().isIndexed() ||
1257       obj->getClass()->getNewEnumerate() || obj->is<StringObject>()) {
1258     return true;
1259   }
1260 
1261   HandleNativeObject nobj = obj.as<NativeObject>();
1262 
1263   // Resolve lazy properties on |nobj|.
1264   if (JSEnumerateOp enumerate = nobj->getClass()->getEnumerate()) {
1265     if (!enumerate(cx, nobj)) {
1266       return false;
1267     }
1268 
1269     // Ensure no extra indexed properties were added through enumerate().
1270     if (nobj->isIndexed()) {
1271       return true;
1272     }
1273   }
1274 
1275   *optimized = true;
1276 
1277   RootedValueVector properties(cx);
1278   RootedValue key(cx);
1279   RootedValue value(cx);
1280 
1281   // We have ensured |nobj| contains no extra indexed properties, so the
1282   // only indexed properties we need to handle here are dense and typed
1283   // array elements.
1284 
1285   for (uint32_t i = 0, len = nobj->getDenseInitializedLength(); i < len; i++) {
1286     value.set(nobj->getDenseElement(i));
1287     if (value.isMagic(JS_ELEMENTS_HOLE)) {
1288       continue;
1289     }
1290 
1291     JSString* str;
1292     if (kind != EnumerableOwnPropertiesKind::Values) {
1293       static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= JSID_INT_MAX,
1294                     "dense elements don't exceed JSID_INT_MAX");
1295       str = Int32ToString<CanGC>(cx, i);
1296       if (!str) {
1297         return false;
1298       }
1299     }
1300 
1301     if (kind == EnumerableOwnPropertiesKind::Keys ||
1302         kind == EnumerableOwnPropertiesKind::Names) {
1303       value.setString(str);
1304     } else if (kind == EnumerableOwnPropertiesKind::KeysAndValues) {
1305       key.setString(str);
1306       if (!NewValuePair(cx, key, value, &value)) {
1307         return false;
1308       }
1309     }
1310 
1311     if (!properties.append(value)) {
1312       return false;
1313     }
1314   }
1315 
1316   if (obj->is<TypedArrayObject>()) {
1317     Handle<TypedArrayObject*> tobj = obj.as<TypedArrayObject>();
1318     uint32_t len = tobj->length();
1319 
1320     // Fail early if the typed array contains too many elements for a
1321     // dense array, because we likely OOM anyway when trying to allocate
1322     // more than 2GB for the properties vector. This also means we don't
1323     // need to handle indices greater than MAX_INT32 in the loop below.
1324     if (len > NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
1325       ReportOutOfMemory(cx);
1326       return false;
1327     }
1328 
1329     MOZ_ASSERT(properties.empty(), "typed arrays cannot have dense elements");
1330     if (!properties.resize(len)) {
1331       return false;
1332     }
1333 
1334     for (uint32_t i = 0; i < len; i++) {
1335       JSString* str;
1336       if (kind != EnumerableOwnPropertiesKind::Values) {
1337         static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= JSID_INT_MAX,
1338                       "dense elements don't exceed JSID_INT_MAX");
1339         str = Int32ToString<CanGC>(cx, i);
1340         if (!str) {
1341           return false;
1342         }
1343       }
1344 
1345       if (kind == EnumerableOwnPropertiesKind::Keys ||
1346           kind == EnumerableOwnPropertiesKind::Names) {
1347         value.setString(str);
1348       } else if (kind == EnumerableOwnPropertiesKind::Values) {
1349         if (!tobj->getElement<CanGC>(cx, i, &value)) {
1350           return false;
1351         }
1352       } else {
1353         key.setString(str);
1354         if (!tobj->getElement<CanGC>(cx, i, &value)) {
1355           return false;
1356         }
1357         if (!NewValuePair(cx, key, value, &value)) {
1358           return false;
1359         }
1360       }
1361 
1362       properties[i].set(value);
1363     }
1364   }
1365 
1366   // Up to this point no side-effects through accessor properties are
1367   // possible which could have replaced |obj| with a non-native object.
1368   MOZ_ASSERT(obj->isNative());
1369 
1370   if (kind == EnumerableOwnPropertiesKind::Keys ||
1371       kind == EnumerableOwnPropertiesKind::Names ||
1372       !HasEnumerableStringNonDataProperties(nobj)) {
1373     // If |kind == Values| or |kind == KeysAndValues|:
1374     // All enumerable properties with string property keys are data
1375     // properties. This allows us to collect the property values while
1376     // iterating over the shape hierarchy without worrying over accessors
1377     // modifying any state.
1378 
1379     size_t elements = properties.length();
1380     constexpr bool onlyEnumerable = kind != EnumerableOwnPropertiesKind::Names;
1381     constexpr AllowGC allowGC =
1382         kind != EnumerableOwnPropertiesKind::KeysAndValues ? AllowGC::NoGC
1383                                                            : AllowGC::CanGC;
1384     mozilla::MaybeOneOf<Shape::Range<NoGC>, Shape::Range<CanGC>> m;
1385     if (allowGC == AllowGC::NoGC) {
1386       m.construct<Shape::Range<NoGC>>(nobj->lastProperty());
1387     } else {
1388       m.construct<Shape::Range<CanGC>>(cx, nobj->lastProperty());
1389     }
1390     for (Shape::Range<allowGC>& r = m.ref<Shape::Range<allowGC>>(); !r.empty();
1391          r.popFront()) {
1392       Shape* shape = &r.front();
1393       jsid id = shape->propid();
1394       if ((onlyEnumerable && !shape->enumerable()) || JSID_IS_SYMBOL(id)) {
1395         continue;
1396       }
1397       MOZ_ASSERT(!JSID_IS_INT(id), "Unexpected indexed property");
1398       MOZ_ASSERT_IF(kind == EnumerableOwnPropertiesKind::Values ||
1399                         kind == EnumerableOwnPropertiesKind::KeysAndValues,
1400                     shape->isDataProperty());
1401 
1402       if (kind == EnumerableOwnPropertiesKind::Keys ||
1403           kind == EnumerableOwnPropertiesKind::Names) {
1404         value.setString(JSID_TO_STRING(id));
1405       } else if (kind == EnumerableOwnPropertiesKind::Values) {
1406         value.set(nobj->getSlot(shape->slot()));
1407       } else {
1408         key.setString(JSID_TO_STRING(id));
1409         value.set(nobj->getSlot(shape->slot()));
1410         if (!NewValuePair(cx, key, value, &value)) {
1411           return false;
1412         }
1413       }
1414 
1415       if (!properties.append(value)) {
1416         return false;
1417       }
1418     }
1419 
1420     // The (non-indexed) properties were visited in reverse iteration order,
1421     // call std::reverse() to ensure they appear in iteration order.
1422     std::reverse(properties.begin() + elements, properties.end());
1423   } else {
1424     MOZ_ASSERT(kind == EnumerableOwnPropertiesKind::Values ||
1425                kind == EnumerableOwnPropertiesKind::KeysAndValues);
1426 
1427     // Get a list of all |obj| shapes. As long as obj->lastProperty()
1428     // is equal to |objShape|, we can use this to speed up both the
1429     // enumerability check and GetProperty.
1430     using ShapeVector = GCVector<Shape*, 8>;
1431     Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
1432 
1433     // Collect all non-symbol properties.
1434     RootedShape objShape(cx, nobj->lastProperty());
1435     for (Shape::Range<NoGC> r(objShape); !r.empty(); r.popFront()) {
1436       Shape* shape = &r.front();
1437       if (JSID_IS_SYMBOL(shape->propid())) {
1438         continue;
1439       }
1440       MOZ_ASSERT(!JSID_IS_INT(shape->propid()), "Unexpected indexed property");
1441 
1442       if (!shapes.append(shape)) {
1443         return false;
1444       }
1445     }
1446 
1447     RootedId id(cx);
1448     for (size_t i = shapes.length(); i > 0; i--) {
1449       Shape* shape = shapes[i - 1];
1450       id = shape->propid();
1451 
1452       // Ensure |obj| is still native: a getter might have been swapped with a
1453       // non-native object.
1454       if (obj->isNative() &&
1455           obj->as<NativeObject>().lastProperty() == objShape &&
1456           shape->isDataProperty()) {
1457         if (!shape->enumerable()) {
1458           continue;
1459         }
1460         value = obj->as<NativeObject>().getSlot(shape->slot());
1461       } else {
1462         // |obj| changed shape or the property is not a data property,
1463         // so we have to do the slower enumerability check and
1464         // GetProperty.
1465         bool enumerable;
1466         if (!PropertyIsEnumerable(cx, obj, id, &enumerable)) {
1467           return false;
1468         }
1469         if (!enumerable) {
1470           continue;
1471         }
1472         if (!GetProperty(cx, obj, obj, id, &value)) {
1473           return false;
1474         }
1475       }
1476 
1477       if (kind == EnumerableOwnPropertiesKind::KeysAndValues) {
1478         key.setString(JSID_TO_STRING(id));
1479         if (!NewValuePair(cx, key, value, &value)) {
1480           return false;
1481         }
1482       }
1483 
1484       if (!properties.append(value)) {
1485         return false;
1486       }
1487     }
1488   }
1489 
1490   JSObject* array =
1491       NewDenseCopiedArray(cx, properties.length(), properties.begin());
1492   if (!array) {
1493     return false;
1494   }
1495 
1496   rval.setObject(*array);
1497   return true;
1498 }
1499 
1500 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1501 // 7.3.21 EnumerableOwnProperties ( O, kind )
1502 template <EnumerableOwnPropertiesKind kind>
EnumerableOwnProperties(JSContext * cx,const JS::CallArgs & args)1503 static bool EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args) {
1504   static_assert(kind == EnumerableOwnPropertiesKind::Values ||
1505                     kind == EnumerableOwnPropertiesKind::KeysAndValues,
1506                 "Only implemented for Object.keys and Object.entries");
1507 
1508   // Step 1. (Step 1 of Object.{keys,values,entries}, really.)
1509   RootedObject obj(cx, ToObject(cx, args.get(0)));
1510   if (!obj) {
1511     return false;
1512   }
1513 
1514   bool optimized;
1515   if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
1516                                               &optimized)) {
1517     return false;
1518   }
1519   if (optimized) {
1520     return true;
1521   }
1522 
1523   // Typed arrays are always handled in the fast path.
1524   MOZ_ASSERT(!obj->is<TypedArrayObject>());
1525 
1526   // Step 2.
1527   RootedIdVector ids(cx);
1528   if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &ids)) {
1529     return false;
1530   }
1531 
1532   // Step 3.
1533   RootedValueVector properties(cx);
1534   size_t len = ids.length();
1535   if (!properties.resize(len)) {
1536     return false;
1537   }
1538 
1539   RootedId id(cx);
1540   RootedValue key(cx);
1541   RootedValue value(cx);
1542   RootedShape shape(cx);
1543   Rooted<PropertyDescriptor> desc(cx);
1544   // Step 4.
1545   size_t out = 0;
1546   for (size_t i = 0; i < len; i++) {
1547     id = ids[i];
1548 
1549     // Step 4.a. (Symbols were filtered out in step 2.)
1550     MOZ_ASSERT(!JSID_IS_SYMBOL(id));
1551 
1552     if (kind != EnumerableOwnPropertiesKind::Values) {
1553       if (!IdToStringOrSymbol(cx, id, &key)) {
1554         return false;
1555       }
1556     }
1557 
1558     // Step 4.a.i.
1559     if (obj->is<NativeObject>()) {
1560       HandleNativeObject nobj = obj.as<NativeObject>();
1561       if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) {
1562         if (!nobj->getDenseOrTypedArrayElement<CanGC>(cx, JSID_TO_INT(id),
1563                                                       &value)) {
1564           return false;
1565         }
1566       } else {
1567         shape = nobj->lookup(cx, id);
1568         if (!shape || !shape->enumerable()) {
1569           continue;
1570         }
1571         if (!shape->isAccessorShape()) {
1572           if (!NativeGetExistingProperty(cx, nobj, nobj, shape, &value)) {
1573             return false;
1574           }
1575         } else if (!GetProperty(cx, obj, obj, id, &value)) {
1576           return false;
1577         }
1578       }
1579     } else {
1580       if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) {
1581         return false;
1582       }
1583 
1584       // Step 4.a.ii. (inverted.)
1585       if (!desc.object() || !desc.enumerable()) {
1586         continue;
1587       }
1588 
1589       // Step 4.a.ii.1.
1590       // (Omitted because Object.keys doesn't use this implementation.)
1591 
1592       // Step 4.a.ii.2.a.
1593       if (!GetProperty(cx, obj, obj, id, &value)) {
1594         return false;
1595       }
1596     }
1597 
1598     // Steps 4.a.ii.2.b-c.
1599     if (kind == EnumerableOwnPropertiesKind::Values) {
1600       properties[out++].set(value);
1601     } else if (!NewValuePair(cx, key, value, properties[out++])) {
1602       return false;
1603     }
1604   }
1605 
1606   // Step 5.
1607   // (Implemented in step 2.)
1608 
1609   // Step 3 of Object.{keys,values,entries}
1610   JSObject* aobj = NewDenseCopiedArray(cx, out, properties.begin());
1611   if (!aobj) {
1612     return false;
1613   }
1614 
1615   args.rval().setObject(*aobj);
1616   return true;
1617 }
1618 
1619 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1620 // 19.1.2.16 Object.keys ( O )
obj_keys(JSContext * cx,unsigned argc,Value * vp)1621 static bool obj_keys(JSContext* cx, unsigned argc, Value* vp) {
1622   CallArgs args = CallArgsFromVp(argc, vp);
1623 
1624   // Step 1.
1625   RootedObject obj(cx, ToObject(cx, args.get(0)));
1626   if (!obj) {
1627     return false;
1628   }
1629 
1630   bool optimized;
1631   static constexpr EnumerableOwnPropertiesKind kind =
1632       EnumerableOwnPropertiesKind::Keys;
1633   if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
1634                                               &optimized)) {
1635     return false;
1636   }
1637   if (optimized) {
1638     return true;
1639   }
1640 
1641   // Steps 2-3.
1642   return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY, args.rval());
1643 }
1644 
1645 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1646 // 19.1.2.21 Object.values ( O )
obj_values(JSContext * cx,unsigned argc,Value * vp)1647 static bool obj_values(JSContext* cx, unsigned argc, Value* vp) {
1648   CallArgs args = CallArgsFromVp(argc, vp);
1649 
1650   // Steps 1-3.
1651   return EnumerableOwnProperties<EnumerableOwnPropertiesKind::Values>(cx, args);
1652 }
1653 
1654 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1655 // 19.1.2.5 Object.entries ( O )
obj_entries(JSContext * cx,unsigned argc,Value * vp)1656 static bool obj_entries(JSContext* cx, unsigned argc, Value* vp) {
1657   CallArgs args = CallArgsFromVp(argc, vp);
1658 
1659   // Steps 1-3.
1660   return EnumerableOwnProperties<EnumerableOwnPropertiesKind::KeysAndValues>(
1661       cx, args);
1662 }
1663 
1664 /* ES6 draft 15.2.3.16 */
obj_is(JSContext * cx,unsigned argc,Value * vp)1665 bool js::obj_is(JSContext* cx, unsigned argc, Value* vp) {
1666   CallArgs args = CallArgsFromVp(argc, vp);
1667 
1668   bool same;
1669   if (!SameValue(cx, args.get(0), args.get(1), &same)) {
1670     return false;
1671   }
1672 
1673   args.rval().setBoolean(same);
1674   return true;
1675 }
1676 
IdToStringOrSymbol(JSContext * cx,HandleId id,MutableHandleValue result)1677 bool js::IdToStringOrSymbol(JSContext* cx, HandleId id,
1678                             MutableHandleValue result) {
1679   if (JSID_IS_INT(id)) {
1680     JSString* str = Int32ToString<CanGC>(cx, JSID_TO_INT(id));
1681     if (!str) {
1682       return false;
1683     }
1684     result.setString(str);
1685   } else if (JSID_IS_ATOM(id)) {
1686     result.setString(JSID_TO_STRING(id));
1687   } else {
1688     result.setSymbol(JSID_TO_SYMBOL(id));
1689   }
1690   return true;
1691 }
1692 
1693 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1694 // 19.1.2.10.1 Runtime Semantics: GetOwnPropertyKeys ( O, Type )
GetOwnPropertyKeys(JSContext * cx,HandleObject obj,unsigned flags,MutableHandleValue rval)1695 bool js::GetOwnPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags,
1696                             MutableHandleValue rval) {
1697   // Step 1 (Performed in caller).
1698 
1699   // Steps 2-4.
1700   RootedIdVector keys(cx);
1701   if (!GetPropertyKeys(cx, obj, flags, &keys)) {
1702     return false;
1703   }
1704 
1705   // Step 5 (Inlined CreateArrayFromList).
1706   RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, keys.length()));
1707   if (!array) {
1708     return false;
1709   }
1710 
1711   array->ensureDenseInitializedLength(cx, 0, keys.length());
1712 
1713   RootedValue val(cx);
1714   for (size_t i = 0, len = keys.length(); i < len; i++) {
1715     MOZ_ASSERT_IF(JSID_IS_SYMBOL(keys[i]), flags & JSITER_SYMBOLS);
1716     MOZ_ASSERT_IF(!JSID_IS_SYMBOL(keys[i]), !(flags & JSITER_SYMBOLSONLY));
1717     if (!IdToStringOrSymbol(cx, keys[i], &val)) {
1718       return false;
1719     }
1720     array->initDenseElement(i, val);
1721   }
1722 
1723   rval.setObject(*array);
1724   return true;
1725 }
1726 
1727 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1728 // 19.1.2.9 Object.getOwnPropertyNames ( O )
obj_getOwnPropertyNames(JSContext * cx,unsigned argc,Value * vp)1729 static bool obj_getOwnPropertyNames(JSContext* cx, unsigned argc, Value* vp) {
1730   CallArgs args = CallArgsFromVp(argc, vp);
1731 
1732   RootedObject obj(cx, ToObject(cx, args.get(0)));
1733   if (!obj) {
1734     return false;
1735   }
1736 
1737   bool optimized;
1738   static constexpr EnumerableOwnPropertiesKind kind =
1739       EnumerableOwnPropertiesKind::Names;
1740   if (!TryEnumerableOwnPropertiesNative<kind>(cx, obj, args.rval(),
1741                                               &optimized)) {
1742     return false;
1743   }
1744   if (optimized) {
1745     return true;
1746   }
1747 
1748   return GetOwnPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN,
1749                             args.rval());
1750 }
1751 
1752 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
1753 // 19.1.2.10 Object.getOwnPropertySymbols ( O )
obj_getOwnPropertySymbols(JSContext * cx,unsigned argc,Value * vp)1754 static bool obj_getOwnPropertySymbols(JSContext* cx, unsigned argc, Value* vp) {
1755   CallArgs args = CallArgsFromVp(argc, vp);
1756 
1757   RootedObject obj(cx, ToObject(cx, args.get(0)));
1758   if (!obj) {
1759     return false;
1760   }
1761 
1762   return GetOwnPropertyKeys(
1763       cx, obj,
1764       JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY,
1765       args.rval());
1766 }
1767 
1768 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
obj_defineProperties(JSContext * cx,unsigned argc,Value * vp)1769 static bool obj_defineProperties(JSContext* cx, unsigned argc, Value* vp) {
1770   CallArgs args = CallArgsFromVp(argc, vp);
1771 
1772   /* Step 1. */
1773   RootedObject obj(cx);
1774   if (!GetFirstArgumentAsObject(cx, args, "Object.defineProperties", &obj)) {
1775     return false;
1776   }
1777 
1778   /* Step 2. */
1779   if (!args.requireAtLeast(cx, "Object.defineProperties", 2)) {
1780     return false;
1781   }
1782 
1783   /* Steps 3-6. */
1784   bool failedOnWindowProxy = false;
1785   if (!ObjectDefineProperties(cx, obj, args[1], &failedOnWindowProxy)) {
1786     return false;
1787   }
1788 
1789   /* Step 7, but modified to deal with WindowProxy mess */
1790   if (failedOnWindowProxy) {
1791     args.rval().setNull();
1792   } else {
1793     args.rval().setObject(*obj);
1794   }
1795   return true;
1796 }
1797 
1798 // ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
obj_preventExtensions(JSContext * cx,unsigned argc,Value * vp)1799 static bool obj_preventExtensions(JSContext* cx, unsigned argc, Value* vp) {
1800   CallArgs args = CallArgsFromVp(argc, vp);
1801   args.rval().set(args.get(0));
1802 
1803   // Step 1.
1804   if (!args.get(0).isObject()) {
1805     return true;
1806   }
1807 
1808   // Steps 2-5.
1809   RootedObject obj(cx, &args.get(0).toObject());
1810   return PreventExtensions(cx, obj);
1811 }
1812 
1813 // ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
obj_freeze(JSContext * cx,unsigned argc,Value * vp)1814 static bool obj_freeze(JSContext* cx, unsigned argc, Value* vp) {
1815   CallArgs args = CallArgsFromVp(argc, vp);
1816   args.rval().set(args.get(0));
1817 
1818   // Step 1.
1819   if (!args.get(0).isObject()) {
1820     return true;
1821   }
1822 
1823   // Steps 2-5.
1824   RootedObject obj(cx, &args.get(0).toObject());
1825   return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen);
1826 }
1827 
1828 // ES6 draft rev27 (2014/08/24) 19.1.2.12 Object.isFrozen(O)
obj_isFrozen(JSContext * cx,unsigned argc,Value * vp)1829 static bool obj_isFrozen(JSContext* cx, unsigned argc, Value* vp) {
1830   CallArgs args = CallArgsFromVp(argc, vp);
1831 
1832   // Step 1.
1833   bool frozen = true;
1834 
1835   // Step 2.
1836   if (args.get(0).isObject()) {
1837     RootedObject obj(cx, &args.get(0).toObject());
1838     if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Frozen, &frozen)) {
1839       return false;
1840     }
1841   }
1842   args.rval().setBoolean(frozen);
1843   return true;
1844 }
1845 
1846 // ES6 draft rev27 (2014/08/24) 19.1.2.17 Object.seal(O)
obj_seal(JSContext * cx,unsigned argc,Value * vp)1847 static bool obj_seal(JSContext* cx, unsigned argc, Value* vp) {
1848   CallArgs args = CallArgsFromVp(argc, vp);
1849   args.rval().set(args.get(0));
1850 
1851   // Step 1.
1852   if (!args.get(0).isObject()) {
1853     return true;
1854   }
1855 
1856   // Steps 2-5.
1857   RootedObject obj(cx, &args.get(0).toObject());
1858   return SetIntegrityLevel(cx, obj, IntegrityLevel::Sealed);
1859 }
1860 
1861 // ES6 draft rev27 (2014/08/24) 19.1.2.13 Object.isSealed(O)
obj_isSealed(JSContext * cx,unsigned argc,Value * vp)1862 static bool obj_isSealed(JSContext* cx, unsigned argc, Value* vp) {
1863   CallArgs args = CallArgsFromVp(argc, vp);
1864 
1865   // Step 1.
1866   bool sealed = true;
1867 
1868   // Step 2.
1869   if (args.get(0).isObject()) {
1870     RootedObject obj(cx, &args.get(0).toObject());
1871     if (!TestIntegrityLevel(cx, obj, IntegrityLevel::Sealed, &sealed)) {
1872       return false;
1873     }
1874   }
1875   args.rval().setBoolean(sealed);
1876   return true;
1877 }
1878 
obj_setProto(JSContext * cx,unsigned argc,Value * vp)1879 bool js::obj_setProto(JSContext* cx, unsigned argc, Value* vp) {
1880   CallArgs args = CallArgsFromVp(argc, vp);
1881   MOZ_ASSERT(args.length() == 1);
1882 
1883   HandleValue thisv = args.thisv();
1884   if (thisv.isNullOrUndefined()) {
1885     ReportIncompatible(cx, args);
1886     return false;
1887   }
1888   if (thisv.isPrimitive()) {
1889     // Mutating a boxed primitive's [[Prototype]] has no side effects.
1890     args.rval().setUndefined();
1891     return true;
1892   }
1893 
1894   /* Do nothing if __proto__ isn't being set to an object or null. */
1895   if (!args[0].isObjectOrNull()) {
1896     args.rval().setUndefined();
1897     return true;
1898   }
1899 
1900   Rooted<JSObject*> obj(cx, &args.thisv().toObject());
1901   Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull());
1902   if (!SetPrototype(cx, obj, newProto)) {
1903     return false;
1904   }
1905 
1906   args.rval().setUndefined();
1907   return true;
1908 }
1909 
1910 static const JSFunctionSpec object_methods[] = {
1911     JS_FN(js_toSource_str, obj_toSource, 0, 0),
1912     JS_INLINABLE_FN(js_toString_str, obj_toString, 0, 0, ObjectToString),
1913     JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0),
1914     JS_SELF_HOSTED_FN(js_valueOf_str, "Object_valueOf", 0, 0),
1915     JS_SELF_HOSTED_FN(js_hasOwnProperty_str, "Object_hasOwnProperty", 1, 0),
1916     JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1, 0),
1917     JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1, 0),
1918     JS_SELF_HOSTED_FN(js_defineGetter_str, "ObjectDefineGetter", 2, 0),
1919     JS_SELF_HOSTED_FN(js_defineSetter_str, "ObjectDefineSetter", 2, 0),
1920     JS_SELF_HOSTED_FN(js_lookupGetter_str, "ObjectLookupGetter", 1, 0),
1921     JS_SELF_HOSTED_FN(js_lookupSetter_str, "ObjectLookupSetter", 1, 0),
1922     JS_FS_END};
1923 
1924 static const JSPropertySpec object_properties[] = {
1925     JS_SELF_HOSTED_GETSET("__proto__", "$ObjectProtoGetter",
1926                           "$ObjectProtoSetter", 0),
1927     JS_PS_END};
1928 
1929 static const JSFunctionSpec object_static_methods[] = {
1930     JS_FN("assign", obj_assign, 2, 0),
1931     JS_SELF_HOSTED_FN("getPrototypeOf", "ObjectGetPrototypeOf", 1, 0),
1932     JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0),
1933     JS_SELF_HOSTED_FN("getOwnPropertyDescriptor",
1934                       "ObjectGetOwnPropertyDescriptor", 2, 0),
1935     JS_SELF_HOSTED_FN("getOwnPropertyDescriptors",
1936                       "ObjectGetOwnPropertyDescriptors", 1, 0),
1937     JS_FN("keys", obj_keys, 1, 0),
1938     JS_FN("values", obj_values, 1, 0),
1939     JS_FN("entries", obj_entries, 1, 0),
1940     JS_INLINABLE_FN("is", obj_is, 2, 0, ObjectIs),
1941     JS_SELF_HOSTED_FN("defineProperty", "ObjectDefineProperty", 3, 0),
1942     JS_FN("defineProperties", obj_defineProperties, 2, 0),
1943     JS_INLINABLE_FN("create", obj_create, 2, 0, ObjectCreate),
1944     JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1, 0),
1945     JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols, 1, 0),
1946     JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, 0),
1947     JS_FN("preventExtensions", obj_preventExtensions, 1, 0),
1948     JS_FN("freeze", obj_freeze, 1, 0),
1949     JS_FN("isFrozen", obj_isFrozen, 1, 0),
1950     JS_FN("seal", obj_seal, 1, 0),
1951     JS_FN("isSealed", obj_isSealed, 1, 0),
1952     JS_SELF_HOSTED_FN("fromEntries", "ObjectFromEntries", 1, 0),
1953     JS_FS_END};
1954 
CreateObjectConstructor(JSContext * cx,JSProtoKey key)1955 static JSObject* CreateObjectConstructor(JSContext* cx, JSProtoKey key) {
1956   Rooted<GlobalObject*> self(cx, cx->global());
1957   if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function)) {
1958     return nullptr;
1959   }
1960 
1961   /* Create the Object function now that we have a [[Prototype]] for it. */
1962   JSFunction* fun = NewNativeConstructor(
1963       cx, obj_construct, 1, HandlePropertyName(cx->names().Object),
1964       gc::AllocKind::FUNCTION, SingletonObject);
1965   if (!fun) {
1966     return nullptr;
1967   }
1968 
1969   fun->setJitInfo(&jit::JitInfo_Object);
1970   return fun;
1971 }
1972 
CreateObjectPrototype(JSContext * cx,JSProtoKey key)1973 static JSObject* CreateObjectPrototype(JSContext* cx, JSProtoKey key) {
1974   MOZ_ASSERT(!cx->zone()->isAtomsZone());
1975   MOZ_ASSERT(cx->global()->isNative());
1976 
1977   /*
1978    * Create |Object.prototype| first, mirroring CreateBlankProto but for the
1979    * prototype of the created object.
1980    */
1981   RootedPlainObject objectProto(
1982       cx, NewSingletonObjectWithGivenProto<PlainObject>(cx, nullptr));
1983   if (!objectProto) {
1984     return nullptr;
1985   }
1986 
1987   bool succeeded;
1988   if (!SetImmutablePrototype(cx, objectProto, &succeeded)) {
1989     return nullptr;
1990   }
1991   MOZ_ASSERT(succeeded,
1992              "should have been able to make a fresh Object.prototype's "
1993              "[[Prototype]] immutable");
1994 
1995   /*
1996    * The default 'new' type of Object.prototype is required by type inference
1997    * to have unknown properties, to simplify handling of e.g. heterogenous
1998    * objects in JSON and script literals.
1999    */
2000   ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
2001   if (!JSObject::setNewGroupUnknown(cx, realm, &PlainObject::class_,
2002                                     objectProto)) {
2003     return nullptr;
2004   }
2005 
2006   return objectProto;
2007 }
2008 
FinishObjectClassInit(JSContext * cx,JS::HandleObject ctor,JS::HandleObject proto)2009 static bool FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor,
2010                                   JS::HandleObject proto) {
2011   Rooted<GlobalObject*> global(cx, cx->global());
2012 
2013   /* ES5 15.1.2.1. */
2014   RootedId evalId(cx, NameToId(cx->names().eval));
2015   JSObject* evalobj =
2016       DefineFunction(cx, global, evalId, IndirectEval, 1, JSPROP_RESOLVING);
2017   if (!evalobj) {
2018     return false;
2019   }
2020   global->setOriginalEval(evalobj);
2021 
2022 #ifdef FUZZING
2023   if (cx->options().fuzzing()) {
2024     if (!DefineTestingFunctions(cx, global, /* fuzzingSafe = */ true,
2025                                 /* disableOOMFunctions = */ false)) {
2026       return false;
2027     }
2028   }
2029 #endif
2030 
2031   Rooted<NativeObject*> holder(cx,
2032                                GlobalObject::getIntrinsicsHolder(cx, global));
2033   if (!holder) {
2034     return false;
2035   }
2036 
2037   /*
2038    * The global object should have |Object.prototype| as its [[Prototype]].
2039    * Eventually we'd like to have standard classes be there from the start,
2040    * and thus we would know we were always setting what had previously been a
2041    * null [[Prototype]], but right now some code assumes it can set the
2042    * [[Prototype]] before standard classes have been initialized.  For now,
2043    * only set the [[Prototype]] if it hasn't already been set.
2044    */
2045   Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
2046   if (global->shouldSplicePrototype()) {
2047     if (!JSObject::splicePrototype(cx, global, tagged)) {
2048       return false;
2049     }
2050   }
2051   return true;
2052 }
2053 
2054 static const ClassSpec PlainObjectClassSpec = {
2055     CreateObjectConstructor, CreateObjectPrototype,
2056     object_static_methods,   nullptr,
2057     object_methods,          object_properties,
2058     FinishObjectClassInit};
2059 
2060 const JSClass PlainObject::class_ = {js_Object_str,
2061                                      JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
2062                                      JS_NULL_CLASS_OPS, &PlainObjectClassSpec};
2063 
2064 const JSClass* const js::ObjectClassPtr = &PlainObject::class_;
2065