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