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/. */
7 #include "builtin/Object.h"
8 #include "js/Object.h" // JS::GetBuiltinClass
10 #include "mozilla/Maybe.h"
11 #include "mozilla/Range.h"
12 #include "mozilla/RangedPtr.h"
15 #include <string_view>
19 #include "builtin/Eval.h"
20 #include "builtin/SelfHostingDefines.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/Identifier.h" // js::IsIdentifier
28 #include "util/StringBuffer.h"
29 #include "util/Text.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/Iteration.h"
35 #include "vm/JSContext.h"
36 #include "vm/NumberObject.h"
37 #include "vm/PlainObject.h" // js::PlainObject
38 #include "vm/RegExpObject.h"
39 #include "vm/StringObject.h"
40 #include "vm/StringType.h"
41 #include "vm/ToSource.h" // js::ValueToSource
42 #include "vm/Watchtower.h"
44 #ifdef ENABLE_RECORD_TUPLE
45 # include "builtin/RecordObject.h"
46 # include "builtin/TupleObject.h"
49 #include "vm/GeckoProfiler-inl.h"
50 #include "vm/JSObject-inl.h"
51 #include "vm/NativeObject-inl.h"
52 #include "vm/Shape-inl.h"
55 # include "builtin/TestingFunctions.h"
62 using mozilla::RangedPtr
;
64 static PlainObject
* CreateThis(JSContext
* cx
, HandleObject newTarget
) {
65 RootedObject
proto(cx
);
66 if (!GetPrototypeFromConstructor(cx
, newTarget
, JSProto_Object
, &proto
)) {
70 gc::AllocKind allocKind
= NewObjectGCKind();
73 return NewPlainObjectWithProtoAndAllocKind(cx
, proto
, allocKind
);
75 return NewPlainObjectWithAllocKind(cx
, allocKind
);
78 bool js::obj_construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
79 CallArgs args
= CallArgsFromVp(argc
, vp
);
82 if (args
.isConstructing() &&
83 (&args
.newTarget().toObject() != &args
.callee())) {
84 RootedObject
newTarget(cx
, &args
.newTarget().toObject());
85 obj
= CreateThis(cx
, newTarget
);
86 } else if (args
.length() > 0 && !args
[0].isNullOrUndefined()) {
87 obj
= ToObject(cx
, args
[0]);
89 /* Make an object whether this was called with 'new' or not. */
90 gc::AllocKind allocKind
= NewObjectGCKind();
91 obj
= NewPlainObjectWithAllocKind(cx
, allocKind
);
97 args
.rval().setObject(*obj
);
102 bool js::obj_propertyIsEnumerable(JSContext
* cx
, unsigned argc
, Value
* vp
) {
103 CallArgs args
= CallArgsFromVp(argc
, vp
);
105 HandleValue idValue
= args
.get(0);
107 // As an optimization, provide a fast path when rooting is not necessary and
108 // we can safely retrieve the attributes from the object's shape.
112 if (args
.thisv().isObject() && idValue
.isPrimitive() &&
113 PrimitiveValueToId
<NoGC
>(cx
, idValue
, &id
)) {
114 JSObject
* obj
= &args
.thisv().toObject();
118 if (obj
->is
<NativeObject
>() &&
119 NativeLookupOwnProperty
<NoGC
>(cx
, &obj
->as
<NativeObject
>(), id
,
122 if (prop
.isNotFound()) {
123 args
.rval().setBoolean(false);
128 JS::PropertyAttributes attrs
= GetPropertyAttributes(obj
, prop
);
129 args
.rval().setBoolean(attrs
.enumerable());
136 if (!ToPropertyKey(cx
, idValue
, &idRoot
)) {
141 RootedObject
obj(cx
, ToObject(cx
, args
.thisv()));
147 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx
);
148 if (!GetOwnPropertyDescriptor(cx
, obj
, idRoot
, &desc
)) {
153 if (desc
.isNothing()) {
154 args
.rval().setBoolean(false);
159 args
.rval().setBoolean(desc
->enumerable());
163 static bool obj_toSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
164 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object.prototype", "toSource");
165 CallArgs args
= CallArgsFromVp(argc
, vp
);
167 AutoCheckRecursionLimit
recursion(cx
);
168 if (!recursion
.check(cx
)) {
172 RootedObject
obj(cx
, ToObject(cx
, args
.thisv()));
177 JSString
* str
= ObjectToSource(cx
, obj
);
182 args
.rval().setString(str
);
186 template <typename CharT
>
187 static bool Consume(RangedPtr
<const CharT
>& s
, RangedPtr
<const CharT
> e
,
188 std::string_view chars
) {
190 size_t len
= chars
.length();
194 if (!EqualChars(s
.get(), chars
.data(), len
)) {
201 template <typename CharT
>
202 static bool ConsumeUntil(RangedPtr
<const CharT
>& s
, RangedPtr
<const CharT
> e
,
205 const CharT
* result
= js_strchr_limit(s
.get(), ch
, e
.get());
209 s
+= result
- s
.get();
210 MOZ_ASSERT(*s
== ch
);
214 template <typename CharT
>
215 static void ConsumeSpaces(RangedPtr
<const CharT
>& s
, RangedPtr
<const CharT
> e
) {
216 while (s
< e
&& *s
== ' ') {
222 * Given a function source string, return the offset and length of the part
223 * between '(function $name' and ')'.
225 template <typename CharT
>
226 static bool ArgsAndBodySubstring(Range
<const CharT
> chars
, size_t* outOffset
,
228 const RangedPtr
<const CharT
> start
= chars
.begin();
229 RangedPtr
<const CharT
> s
= start
;
230 RangedPtr
<const CharT
> e
= chars
.end();
236 // Remove enclosing parentheses.
237 if (*s
== '(' && *(e
- 1) == ')') {
242 // Support the following cases, with spaces between tokens:
244 // -+---------+-+------------+-+-----+-+- [ - <any> - ] - ( -+-
246 // +- async -+ +- function -+ +- * -+ +- <any> - ( ---------+
252 // This accepts some invalid syntax, but we don't care, since it's only
253 // used by the non-standard toSource, and we're doing a best-effort attempt
256 (void)Consume(s
, e
, "async");
258 (void)(Consume(s
, e
, "function") || Consume(s
, e
, "get") ||
259 Consume(s
, e
, "set"));
261 (void)Consume(s
, e
, "*");
264 // Jump over the function's name.
265 if (Consume(s
, e
, "[")) {
266 if (!ConsumeUntil(s
, e
, ']')) {
271 if (s
>= e
|| *s
!= '(') {
275 if (!ConsumeUntil(s
, e
, '(')) {
280 MOZ_ASSERT(*s
== '(');
282 *outOffset
= s
- start
;
284 MOZ_ASSERT(*outOffset
+ *outLen
<= chars
.length());
288 enum class PropertyKind
{ Getter
, Setter
, Method
, Normal
};
290 JSString
* js::ObjectToSource(JSContext
* cx
, HandleObject obj
) {
291 /* If outermost, we need parentheses to be an expression, not a block. */
292 bool outermost
= cx
->cycleDetectorVector().empty();
294 AutoCycleDetector
detector(cx
, obj
);
295 if (!detector
.init()) {
298 if (detector
.foundCycle()) {
299 return NewStringCopyZ
<CanGC
>(cx
, "{}");
302 JSStringBuilder
buf(cx
);
303 if (outermost
&& !buf
.append('(')) {
306 if (!buf
.append('{')) {
310 RootedIdVector
idv(cx
);
311 if (!GetPropertyKeys(cx
, obj
, JSITER_OWNONLY
| JSITER_SYMBOLS
, &idv
)) {
315 #ifdef ENABLE_RECORD_TUPLE
316 if (IsExtendedPrimitiveWrapper(*obj
)) {
317 if (obj
->is
<TupleObject
>()) {
318 Rooted
<TupleType
*> tup(cx
, &obj
->as
<TupleObject
>().unbox());
319 return TupleToSource(cx
, tup
);
321 MOZ_ASSERT(obj
->is
<RecordObject
>());
322 return RecordToSource(cx
, obj
->as
<RecordObject
>().unbox());
328 auto AddProperty
= [cx
, &comma
, &buf
](HandleId id
, HandleValue val
,
329 PropertyKind kind
) -> bool {
330 /* Convert id to a string. */
331 RootedString
idstr(cx
);
333 RootedValue
v(cx
, SymbolValue(id
.toSymbol()));
334 idstr
= ValueToSource(cx
, v
);
339 RootedValue
idv(cx
, IdToValue(id
));
340 idstr
= ToString
<CanGC
>(cx
, idv
);
346 * If id is a string that's not an identifier, or if it's a
347 * negative integer, then it must be quoted.
349 if (id
.isAtom() ? !IsIdentifier(id
.toAtom()) : id
.toInt() < 0) {
350 UniqueChars quotedId
= QuoteString(cx
, idstr
, '\'');
354 idstr
= NewStringCopyZ
<CanGC
>(cx
, quotedId
.get());
361 RootedString
valsource(cx
, ValueToSource(cx
, val
));
366 Rooted
<JSLinearString
*> valstr(cx
, valsource
->ensureLinear(cx
));
371 if (comma
&& !buf
.append(", ")) {
376 size_t voffset
, vlength
;
378 // Methods and accessors can return exact syntax of source, that fits
379 // into property without adding property name or "get"/"set" prefix.
380 // Use the exact syntax when the following conditions are met:
382 // * It's a function object
384 // * Function's kind and property's kind are same
385 // (this can be false for dynamically defined properties)
386 // * Function has explicit name
387 // (this can be false for computed property and dynamically defined
389 // * Function's name and property's name are same
390 // (this can be false for dynamically defined properties)
391 if (kind
== PropertyKind::Getter
|| kind
== PropertyKind::Setter
||
392 kind
== PropertyKind::Method
) {
393 RootedFunction
fun(cx
);
394 if (val
.toObject().is
<JSFunction
>()) {
395 fun
= &val
.toObject().as
<JSFunction
>();
396 // Method's case should be checked on caller.
397 if (((fun
->isGetter() && kind
== PropertyKind::Getter
&&
398 !fun
->isAccessorWithLazyName()) ||
399 (fun
->isSetter() && kind
== PropertyKind::Setter
&&
400 !fun
->isAccessorWithLazyName()) ||
401 kind
== PropertyKind::Method
) &&
402 fun
->fullExplicitName()) {
404 if (!EqualStrings(cx
, fun
->fullExplicitName(), idstr
, &result
)) {
409 if (!buf
.append(valstr
)) {
418 // When falling back try to generate a better string
419 // representation by skipping the prelude, and also removing
420 // the enclosing parentheses.
422 JS::AutoCheckCannotGC nogc
;
423 if (valstr
->hasLatin1Chars()) {
424 success
= ArgsAndBodySubstring(valstr
->latin1Range(nogc
), &voffset
,
427 success
= ArgsAndBodySubstring(valstr
->twoByteRange(nogc
), &voffset
,
431 kind
= PropertyKind::Normal
;
435 if (kind
== PropertyKind::Getter
) {
436 if (!buf
.append("get ")) {
439 } else if (kind
== PropertyKind::Setter
) {
440 if (!buf
.append("set ")) {
443 } else if (kind
== PropertyKind::Method
&& fun
) {
444 if (fun
->isAsync()) {
445 if (!buf
.append("async ")) {
450 if (fun
->isGenerator()) {
451 if (!buf
.append('*')) {
458 bool needsBracket
= id
.isSymbol();
459 if (needsBracket
&& !buf
.append('[')) {
462 if (!buf
.append(idstr
)) {
465 if (needsBracket
&& !buf
.append(']')) {
469 if (kind
== PropertyKind::Getter
|| kind
== PropertyKind::Setter
||
470 kind
== PropertyKind::Method
) {
471 if (!buf
.appendSubstring(valstr
, voffset
, vlength
)) {
475 if (!buf
.append(':')) {
478 if (!buf
.append(valstr
)) {
486 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx
);
488 for (size_t i
= 0; i
< idv
.length(); ++i
) {
490 if (!GetOwnPropertyDescriptor(cx
, obj
, id
, &desc
)) {
494 if (desc
.isNothing()) {
498 if (desc
->isAccessorDescriptor()) {
499 if (desc
->hasGetter() && desc
->getter()) {
500 val
.setObject(*desc
->getter());
501 if (!AddProperty(id
, val
, PropertyKind::Getter
)) {
505 if (desc
->hasSetter() && desc
->setter()) {
506 val
.setObject(*desc
->setter());
507 if (!AddProperty(id
, val
, PropertyKind::Setter
)) {
514 val
.set(desc
->value());
516 JSFunction
* fun
= nullptr;
517 if (IsFunctionObject(val
, &fun
) && fun
->isMethod()) {
518 if (!AddProperty(id
, val
, PropertyKind::Method
)) {
524 if (!AddProperty(id
, val
, PropertyKind::Normal
)) {
529 if (!buf
.append('}')) {
532 if (outermost
&& !buf
.append(')')) {
536 return buf
.finishString();
539 static JSString
* GetBuiltinTagSlow(JSContext
* cx
, HandleObject obj
) {
542 if (!IsArray(cx
, obj
, &isArray
)) {
548 return cx
->names().object_Array_
;
553 if (!JS::GetBuiltinClass(cx
, obj
, &cls
)) {
558 case ESClass::String
:
559 return cx
->names().object_String_
;
560 case ESClass::Arguments
:
561 return cx
->names().object_Arguments_
;
563 return cx
->names().object_Error_
;
564 case ESClass::Boolean
:
565 return cx
->names().object_Boolean_
;
566 case ESClass::Number
:
567 return cx
->names().object_Number_
;
569 return cx
->names().object_Date_
;
570 case ESClass::RegExp
:
571 return cx
->names().object_RegExp_
;
573 if (obj
->isCallable()) {
574 // Non-standard: Prevent <object> from showing up as Function.
575 JSObject
* unwrapped
= CheckedUnwrapDynamic(obj
, cx
);
576 if (!unwrapped
|| !unwrapped
->getClass()->isDOMClass()) {
577 return cx
->names().object_Function_
;
580 return cx
->names().object_Object_
;
584 static MOZ_ALWAYS_INLINE JSString
* GetBuiltinTagFast(JSObject
* obj
,
586 const JSClass
* clasp
= obj
->getClass();
587 MOZ_ASSERT(!clasp
->isProxyObject());
589 // Optimize the non-proxy case to bypass GetBuiltinClass.
590 if (clasp
== &PlainObject::class_
) {
591 // This case is by far the most common so we handle it first.
592 return cx
->names().object_Object_
;
595 if (clasp
== &ArrayObject::class_
) {
596 return cx
->names().object_Array_
;
599 if (clasp
->isJSFunction()) {
600 return cx
->names().object_Function_
;
603 if (clasp
== &StringObject::class_
) {
604 return cx
->names().object_String_
;
607 if (clasp
== &NumberObject::class_
) {
608 return cx
->names().object_Number_
;
611 if (clasp
== &BooleanObject::class_
) {
612 return cx
->names().object_Boolean_
;
615 if (clasp
== &DateObject::class_
) {
616 return cx
->names().object_Date_
;
619 if (clasp
== &RegExpObject::class_
) {
620 return cx
->names().object_RegExp_
;
623 if (obj
->is
<ArgumentsObject
>()) {
624 return cx
->names().object_Arguments_
;
627 if (obj
->is
<ErrorObject
>()) {
628 return cx
->names().object_Error_
;
631 if (obj
->isCallable() && !obj
->getClass()->isDOMClass()) {
632 // Non-standard: Prevent <object> from showing up as Function.
633 return cx
->names().object_Function_
;
636 return cx
->names().object_Object_
;
639 // For primitive values we try to avoid allocating the object if we can
640 // determine that the prototype it would use does not define Symbol.toStringTag.
641 static JSAtom
* MaybeObjectToStringPrimitive(JSContext
* cx
, const Value
& v
) {
642 JSProtoKey protoKey
= js::PrimitiveToProtoKey(cx
, v
);
644 // If prototype doesn't exist yet, just fall through.
645 JSObject
* proto
= cx
->global()->maybeGetPrototype(protoKey
);
650 // If determining this may have side-effects, we must instead create the
651 // object normally since it is the receiver while looking up
652 // Symbol.toStringTag.
653 if (MaybeHasInterestingSymbolProperty(
654 cx
, proto
, cx
->wellKnownSymbols().toStringTag
, nullptr)) {
658 // Return the direct result.
661 return cx
->names().object_String_
;
663 return cx
->names().object_Number_
;
664 case JSProto_Boolean
:
665 return cx
->names().object_Boolean_
;
667 return cx
->names().object_Symbol_
;
669 return cx
->names().object_BigInt_
;
678 bool js::obj_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
679 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object.prototype", "toString");
680 CallArgs args
= CallArgsFromVp(argc
, vp
);
681 RootedObject
obj(cx
);
683 if (args
.thisv().isPrimitive()) {
685 if (args
.thisv().isUndefined()) {
686 args
.rval().setString(cx
->names().object_Undefined_
);
691 if (args
.thisv().isNull()) {
692 args
.rval().setString(cx
->names().object_Null_
);
696 // Try fast-path for primitives. This is unusual but we encounter code like
698 JSAtom
* result
= MaybeObjectToStringPrimitive(cx
, args
.thisv());
700 args
.rval().setString(result
);
705 obj
= ToObject(cx
, args
.thisv());
710 obj
= &args
.thisv().toObject();
713 // When |obj| is a non-proxy object, compute |builtinTag| only when needed.
714 RootedString
builtinTag(cx
);
715 if (MOZ_UNLIKELY(obj
->is
<ProxyObject
>())) {
716 builtinTag
= GetBuiltinTagSlow(cx
, obj
);
724 if (!GetInterestingSymbolProperty(cx
, obj
, cx
->wellKnownSymbols().toStringTag
,
730 if (!tag
.isString()) {
732 builtinTag
= GetBuiltinTagFast(obj
, cx
);
734 // Assert this fast path is correct and matches BuiltinTagSlow.
735 JSString
* builtinTagSlow
= GetBuiltinTagSlow(cx
, obj
);
736 if (!builtinTagSlow
) {
739 MOZ_ASSERT(builtinTagSlow
== builtinTag
);
743 args
.rval().setString(builtinTag
);
749 if (!sb
.append("[object ") || !sb
.append(tag
.toString()) || !sb
.append(']')) {
753 JSString
* str
= sb
.finishAtom();
758 args
.rval().setString(str
);
762 JSString
* js::ObjectClassToString(JSContext
* cx
, JSObject
* obj
) {
763 AutoUnsafeCallWithABI unsafe
;
765 if (MaybeHasInterestingSymbolProperty(cx
, obj
,
766 cx
->wellKnownSymbols().toStringTag
)) {
769 return GetBuiltinTagFast(obj
, cx
);
772 static bool obj_setPrototypeOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
773 CallArgs args
= CallArgsFromVp(argc
, vp
);
775 if (!args
.requireAtLeast(cx
, "Object.setPrototypeOf", 2)) {
780 if (args
[0].isNullOrUndefined()) {
781 JS_ReportErrorNumberASCII(
782 cx
, GetErrorMessage
, nullptr, JSMSG_CANT_CONVERT_TO
,
783 args
[0].isNull() ? "null" : "undefined", "object");
788 if (!args
[1].isObjectOrNull()) {
789 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
790 JSMSG_NOT_EXPECTED_TYPE
, "Object.setPrototypeOf",
792 InformalValueTypeName(args
[1]));
797 if (!args
[0].isObject()) {
798 args
.rval().set(args
[0]);
803 RootedObject
obj(cx
, &args
[0].toObject());
804 RootedObject
newProto(cx
, args
[1].toObjectOrNull());
805 if (!SetPrototype(cx
, obj
, newProto
)) {
810 args
.rval().set(args
[0]);
814 static bool PropertyIsEnumerable(JSContext
* cx
, HandleObject obj
, HandleId id
,
817 if (obj
->is
<NativeObject
>() &&
818 NativeLookupOwnProperty
<NoGC
>(cx
, &obj
->as
<NativeObject
>(), id
, &prop
)) {
819 if (prop
.isNotFound()) {
824 JS::PropertyAttributes attrs
= GetPropertyAttributes(obj
, prop
);
825 *enumerable
= attrs
.enumerable();
829 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx
);
830 if (!GetOwnPropertyDescriptor(cx
, obj
, id
, &desc
)) {
834 *enumerable
= desc
.isSome() && desc
->enumerable();
838 // Returns true if properties not named "__proto__" can be added to |obj|
839 // with a fast path that doesn't check any properties on the prototype chain.
840 static bool CanAddNewPropertyExcludingProtoFast(PlainObject
* obj
) {
841 if (!obj
->isExtensible() || obj
->isUsedAsPrototype()) {
845 // Don't fastpath assign if we're watching for property modification.
846 if (Watchtower::watchesPropertyModification(obj
)) {
850 // Ensure the object has no non-writable properties or getters/setters.
851 // For now only support PlainObjects so that we don't have to worry about
852 // resolve hooks and other JSClass hooks.
854 if (obj
->hasNonWritableOrAccessorPropExclProto()) {
858 JSObject
* proto
= obj
->staticPrototype();
862 if (!proto
->is
<PlainObject
>()) {
865 obj
= &proto
->as
<PlainObject
>();
870 void PlainObjectAssignCache::assertValid() const {
871 MOZ_ASSERT(emptyToShape_
);
872 MOZ_ASSERT(fromShape_
);
873 MOZ_ASSERT(newToShape_
);
875 MOZ_ASSERT(emptyToShape_
->propMapLength() == 0);
876 MOZ_ASSERT(emptyToShape_
->base() == newToShape_
->base());
877 MOZ_ASSERT(emptyToShape_
->numFixedSlots() == newToShape_
->numFixedSlots());
879 MOZ_ASSERT(emptyToShape_
->getObjectClass() == &PlainObject::class_
);
880 MOZ_ASSERT(fromShape_
->getObjectClass() == &PlainObject::class_
);
882 MOZ_ASSERT(fromShape_
->slotSpan() == newToShape_
->slotSpan());
886 [[nodiscard
]] static bool TryAssignPlain(JSContext
* cx
, HandleObject to
,
887 HandleObject from
, bool* optimized
) {
888 // Object.assign is used with PlainObjects most of the time. This is a fast
889 // path to optimize that case. This lets us avoid checks that are only
890 // relevant for other JSClasses.
892 MOZ_ASSERT(*optimized
== false);
894 if (!from
->is
<PlainObject
>() || !to
->is
<PlainObject
>()) {
898 // Don't use the fast path if |from| may have extra indexed properties.
899 Handle
<PlainObject
*> fromPlain
= from
.as
<PlainObject
>();
900 if (fromPlain
->getDenseInitializedLength() > 0 || fromPlain
->isIndexed()) {
903 MOZ_ASSERT(!fromPlain
->getClass()->getNewEnumerate());
904 MOZ_ASSERT(!fromPlain
->getClass()->getEnumerate());
906 // Empty |from| objects are common, so check for this first.
907 if (fromPlain
->empty()) {
912 Handle
<PlainObject
*> toPlain
= to
.as
<PlainObject
>();
913 if (!CanAddNewPropertyExcludingProtoFast(toPlain
)) {
917 const bool toWasEmpty
= toPlain
->empty();
919 const PlainObjectAssignCache
& cache
= cx
->realm()->plainObjectAssignCache
;
920 SharedShape
* newShape
= cache
.lookup(toPlain
->shape(), fromPlain
->shape());
923 uint32_t oldSpan
= 0;
924 uint32_t newSpan
= newShape
->slotSpan();
925 if (!toPlain
->setShapeAndAddNewSlots(cx
, newShape
, oldSpan
, newSpan
)) {
928 MOZ_ASSERT(fromPlain
->slotSpan() == newSpan
);
929 for (size_t i
= 0; i
< newSpan
; i
++) {
930 toPlain
->initSlot(i
, fromPlain
->getSlot(i
));
936 // Get a list of all enumerable |from| properties.
938 Rooted
<PropertyInfoWithKeyVector
> props(cx
, PropertyInfoWithKeyVector(cx
));
941 Rooted
<Shape
*> fromShape(cx
, fromPlain
->shape());
944 bool hasPropsWithNonDefaultAttrs
= false;
945 bool hasOnlyEnumerableProps
= true;
946 for (ShapePropertyIter
<NoGC
> iter(fromPlain
->shape()); !iter
.done(); iter
++) {
947 // Symbol properties need to be assigned last. For now fall back to the
948 // slow path if we see a symbol property.
949 jsid id
= iter
->key();
950 if (MOZ_UNLIKELY(id
.isSymbol())) {
953 // __proto__ is not supported by CanAddNewPropertyExcludingProtoFast.
954 if (MOZ_UNLIKELY(id
.isAtom(cx
->names().proto_
))) {
957 if (MOZ_UNLIKELY(!iter
->isDataProperty())) {
960 if (iter
->flags() != PropertyFlags::defaultDataPropFlags
) {
961 hasPropsWithNonDefaultAttrs
= true;
962 if (!iter
->enumerable()) {
963 hasOnlyEnumerableProps
= false;
967 if (MOZ_UNLIKELY(!props
.append(*iter
))) {
972 MOZ_ASSERT_IF(hasOnlyEnumerableProps
&& !fromPlain
->inDictionaryMode(),
973 fromPlain
->slotSpan() == props
.length());
977 Rooted
<Shape
*> origToShape(cx
, toPlain
->shape());
979 // If the |to| object has no properties and the |from| object only has plain
980 // enumerable/writable/configurable data properties, try to use its shape or
982 if (toWasEmpty
&& !hasPropsWithNonDefaultAttrs
) {
983 CanReuseShape canReuse
=
984 toPlain
->canReuseShapeForNewProperties(fromPlain
->shape());
985 if (canReuse
!= CanReuseShape::NoReuse
) {
986 SharedShape
* newShape
;
987 if (canReuse
== CanReuseShape::CanReuseShape
) {
988 newShape
= fromPlain
->sharedShape();
990 // Get a shape with fromPlain's PropMap and ObjectFlags (because we need
991 // the HasEnumerable flag checked in canReuseShapeForNewProperties) and
992 // the other fields (BaseShape, numFixedSlots) unchanged.
993 MOZ_ASSERT(canReuse
== CanReuseShape::CanReusePropMap
);
994 ObjectFlags objectFlags
= fromPlain
->sharedShape()->objectFlags();
995 Rooted
<SharedPropMap
*> map(cx
, fromPlain
->sharedShape()->propMap());
996 uint32_t mapLength
= fromPlain
->sharedShape()->propMapLength();
997 BaseShape
* base
= toPlain
->sharedShape()->base();
998 uint32_t nfixed
= toPlain
->sharedShape()->numFixedSlots();
999 newShape
= SharedShape::getPropMapShape(cx
, base
, nfixed
, map
,
1000 mapLength
, objectFlags
);
1005 uint32_t oldSpan
= 0;
1006 uint32_t newSpan
= props
.length();
1007 if (!toPlain
->setShapeAndAddNewSlots(cx
, newShape
, oldSpan
, newSpan
)) {
1010 MOZ_ASSERT(fromPlain
->slotSpan() == newSpan
);
1011 MOZ_ASSERT(toPlain
->slotSpan() == newSpan
);
1012 for (size_t i
= 0; i
< newSpan
; i
++) {
1013 toPlain
->initSlot(i
, fromPlain
->getSlot(i
));
1015 PlainObjectAssignCache
& cache
= cx
->realm()->plainObjectAssignCache
;
1016 cache
.fill(&origToShape
->asShared(), fromPlain
->sharedShape(), newShape
);
1021 RootedValue
propValue(cx
);
1022 RootedId
nextKey(cx
);
1024 for (size_t i
= props
.length(); i
> 0; i
--) {
1025 // Assert |from| still has the same properties.
1026 MOZ_ASSERT(fromPlain
->shape() == fromShape
);
1028 PropertyInfoWithKey fromProp
= props
[i
- 1];
1029 MOZ_ASSERT(fromProp
.isDataProperty());
1030 MOZ_ASSERT(fromProp
.enumerable());
1032 nextKey
= fromProp
.key();
1033 propValue
= fromPlain
->getSlot(fromProp
.slot());
1036 if (Maybe
<PropertyInfo
> toProp
= toPlain
->lookup(cx
, nextKey
)) {
1037 MOZ_ASSERT(toProp
->isDataProperty());
1038 MOZ_ASSERT(toProp
->writable());
1039 toPlain
->setSlot(toProp
->slot(), propValue
);
1044 MOZ_ASSERT(!toPlain
->containsPure(nextKey
));
1046 if (!AddDataPropertyToPlainObject(cx
, toPlain
, nextKey
, propValue
)) {
1051 // Note: dictionary shapes are not supported by the cache because they have a
1052 // more complicated slot layout (the slot numbers may not match the property
1053 // definition order and the slots may contain holes).
1054 if (toWasEmpty
&& hasOnlyEnumerableProps
&& !fromPlain
->inDictionaryMode() &&
1055 !toPlain
->inDictionaryMode()) {
1056 PlainObjectAssignCache
& cache
= cx
->realm()->plainObjectAssignCache
;
1057 cache
.fill(&origToShape
->asShared(), fromPlain
->sharedShape(),
1058 toPlain
->sharedShape());
1064 static bool TryAssignNative(JSContext
* cx
, HandleObject to
, HandleObject from
,
1066 MOZ_ASSERT(*optimized
== false);
1068 if (!from
->is
<NativeObject
>() || !to
->is
<NativeObject
>()) {
1072 // Don't use the fast path if |from| may have extra indexed or lazy
1074 NativeObject
* fromNative
= &from
->as
<NativeObject
>();
1075 if (fromNative
->getDenseInitializedLength() > 0 || fromNative
->isIndexed() ||
1076 fromNative
->is
<TypedArrayObject
>() ||
1077 fromNative
->getClass()->getNewEnumerate() ||
1078 fromNative
->getClass()->getEnumerate()) {
1082 // Get a list of |from| properties. As long as from->shape() == fromShape
1083 // we can use this to speed up both the enumerability check and the GetProp.
1085 Rooted
<PropertyInfoWithKeyVector
> props(cx
, PropertyInfoWithKeyVector(cx
));
1087 Rooted
<NativeShape
*> fromShape(cx
, fromNative
->shape());
1088 for (ShapePropertyIter
<NoGC
> iter(fromShape
); !iter
.done(); iter
++) {
1089 // Symbol properties need to be assigned last. For now fall back to the
1090 // slow path if we see a symbol property.
1091 if (MOZ_UNLIKELY(iter
->key().isSymbol())) {
1094 if (MOZ_UNLIKELY(!props
.append(*iter
))) {
1101 RootedValue
propValue(cx
);
1102 RootedId
nextKey(cx
);
1103 RootedValue
toReceiver(cx
, ObjectValue(*to
));
1105 for (size_t i
= props
.length(); i
> 0; i
--) {
1106 PropertyInfoWithKey prop
= props
[i
- 1];
1107 nextKey
= prop
.key();
1109 // If |from| still has the same shape, it must still be a NativeObject with
1110 // the properties in |props|.
1111 if (MOZ_LIKELY(from
->shape() == fromShape
&& prop
.isDataProperty())) {
1112 if (!prop
.enumerable()) {
1115 propValue
= from
->as
<NativeObject
>().getSlot(prop
.slot());
1117 // |from| changed shape or the property is not a data property, so
1118 // we have to do the slower enumerability check and GetProp.
1120 if (!PropertyIsEnumerable(cx
, from
, nextKey
, &enumerable
)) {
1126 if (!GetProperty(cx
, from
, from
, nextKey
, &propValue
)) {
1131 ObjectOpResult result
;
1133 !SetProperty(cx
, to
, nextKey
, propValue
, toReceiver
, result
))) {
1136 if (MOZ_UNLIKELY(!result
.checkStrict(cx
, to
, nextKey
))) {
1144 static bool AssignSlow(JSContext
* cx
, HandleObject to
, HandleObject from
) {
1146 RootedIdVector
keys(cx
);
1147 if (!GetPropertyKeys(
1148 cx
, from
, JSITER_OWNONLY
| JSITER_HIDDEN
| JSITER_SYMBOLS
, &keys
)) {
1153 RootedId
nextKey(cx
);
1154 RootedValue
propValue(cx
);
1155 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
1160 if (MOZ_UNLIKELY(!PropertyIsEnumerable(cx
, from
, nextKey
, &enumerable
))) {
1168 if (MOZ_UNLIKELY(!GetProperty(cx
, from
, from
, nextKey
, &propValue
))) {
1173 if (MOZ_UNLIKELY(!SetProperty(cx
, to
, nextKey
, propValue
))) {
1181 JS_PUBLIC_API
bool JS_AssignObject(JSContext
* cx
, JS::HandleObject target
,
1182 JS::HandleObject src
) {
1183 bool optimized
= false;
1185 if (!TryAssignPlain(cx
, target
, src
, &optimized
)) {
1192 if (!TryAssignNative(cx
, target
, src
, &optimized
)) {
1199 return AssignSlow(cx
, target
, src
);
1202 // ES2018 draft rev 48ad2688d8f964da3ea8c11163ef20eb126fb8a4
1203 // 19.1.2.1 Object.assign(target, ...sources)
1204 static bool obj_assign(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1205 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object", "assign");
1206 CallArgs args
= CallArgsFromVp(argc
, vp
);
1209 RootedObject
to(cx
, ToObject(cx
, args
.get(0)));
1214 // Note: step 2 is implicit. If there are 0 arguments, ToObject throws. If
1215 // there's 1 argument, the loop below is a no-op.
1218 RootedObject
from(cx
);
1219 for (size_t i
= 1; i
< args
.length(); i
++) {
1221 if (args
[i
].isNullOrUndefined()) {
1226 from
= ToObject(cx
, args
[i
]);
1231 // Steps 4.b.ii, 4.c.
1232 if (!JS_AssignObject(cx
, to
, from
)) {
1238 args
.rval().setObject(*to
);
1243 bool js::obj_isPrototypeOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1244 CallArgs args
= CallArgsFromVp(argc
, vp
);
1247 if (args
.length() < 1 || !args
[0].isObject()) {
1248 args
.rval().setBoolean(false);
1253 RootedObject
obj(cx
, ToObject(cx
, args
.thisv()));
1260 if (!IsPrototypeOf(cx
, obj
, &args
[0].toObject(), &isPrototype
)) {
1263 args
.rval().setBoolean(isPrototype
);
1267 PlainObject
* js::ObjectCreateImpl(JSContext
* cx
, HandleObject proto
,
1268 NewObjectKind newKind
) {
1269 // Give the new object a small number of fixed slots, like we do for empty
1270 // object literals ({}).
1271 gc::AllocKind allocKind
= NewObjectGCKind();
1272 return NewPlainObjectWithProtoAndAllocKind(cx
, proto
, allocKind
, newKind
);
1275 PlainObject
* js::ObjectCreateWithTemplate(JSContext
* cx
,
1276 Handle
<PlainObject
*> templateObj
) {
1277 RootedObject
proto(cx
, templateObj
->staticPrototype());
1278 return ObjectCreateImpl(cx
, proto
, GenericObject
);
1281 // ES 2017 draft 19.1.2.3.1
1282 static bool ObjectDefineProperties(JSContext
* cx
, HandleObject obj
,
1283 HandleValue properties
,
1284 bool* failedOnWindowProxy
) {
1287 RootedObject
props(cx
, ToObject(cx
, properties
));
1293 RootedIdVector
keys(cx
);
1294 if (!GetPropertyKeys(
1295 cx
, props
, JSITER_OWNONLY
| JSITER_SYMBOLS
| JSITER_HIDDEN
, &keys
)) {
1299 RootedId
nextKey(cx
);
1300 Rooted
<Maybe
<PropertyDescriptor
>> keyDesc(cx
);
1301 Rooted
<PropertyDescriptor
> desc(cx
);
1302 RootedValue
descObj(cx
);
1305 Rooted
<PropertyDescriptorVector
> descriptors(cx
,
1306 PropertyDescriptorVector(cx
));
1307 RootedIdVector
descriptorKeys(cx
);
1310 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
1314 if (!GetOwnPropertyDescriptor(cx
, props
, nextKey
, &keyDesc
)) {
1319 if (keyDesc
.isSome() && keyDesc
->enumerable()) {
1320 if (!GetProperty(cx
, props
, props
, nextKey
, &descObj
) ||
1321 !ToPropertyDescriptor(cx
, descObj
, true, &desc
) ||
1322 !descriptors
.append(desc
) || !descriptorKeys
.append(nextKey
)) {
1329 *failedOnWindowProxy
= false;
1330 for (size_t i
= 0, len
= descriptors
.length(); i
< len
; i
++) {
1331 ObjectOpResult result
;
1332 if (!DefineProperty(cx
, obj
, descriptorKeys
[i
], descriptors
[i
], result
)) {
1337 if (result
.failureCode() == JSMSG_CANT_DEFINE_WINDOW_NC
) {
1338 *failedOnWindowProxy
= true;
1339 } else if (!result
.checkStrict(cx
, obj
, descriptorKeys
[i
])) {
1348 // ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties])
1349 bool js::obj_create(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1350 CallArgs args
= CallArgsFromVp(argc
, vp
);
1353 if (!args
.requireAtLeast(cx
, "Object.create", 1)) {
1357 if (!args
[0].isObjectOrNull()) {
1359 DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, args
[0], nullptr);
1364 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1365 JSMSG_UNEXPECTED_TYPE
, bytes
.get(),
1366 "not an object or null");
1371 RootedObject
proto(cx
, args
[0].toObjectOrNull());
1372 Rooted
<PlainObject
*> obj(cx
, ObjectCreateImpl(cx
, proto
));
1378 if (args
.hasDefined(1)) {
1379 // we can't ever end up with failures to define on a WindowProxy
1380 // here, because "obj" is never a WindowProxy.
1381 bool failedOnWindowProxy
= false;
1382 if (!ObjectDefineProperties(cx
, obj
, args
[1], &failedOnWindowProxy
)) {
1385 MOZ_ASSERT(!failedOnWindowProxy
, "How did we get a WindowProxy here?");
1389 args
.rval().setObject(*obj
);
1393 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1394 // 6.2.4.4 FromPropertyDescriptor ( Desc )
1395 static bool FromPropertyDescriptorToArray(
1396 JSContext
* cx
, Handle
<Maybe
<PropertyDescriptor
>> desc
,
1397 MutableHandleValue vp
) {
1399 if (desc
.isNothing()) {
1405 // Retrieve all property descriptor fields and place them into the result
1406 // array. The actual return object is created in self-hosted code for
1407 // performance reasons.
1409 int32_t attrsAndKind
= 0;
1410 if (desc
->enumerable()) {
1411 attrsAndKind
|= ATTR_ENUMERABLE
;
1413 if (desc
->configurable()) {
1414 attrsAndKind
|= ATTR_CONFIGURABLE
;
1416 if (!desc
->isAccessorDescriptor()) {
1417 if (desc
->writable()) {
1418 attrsAndKind
|= ATTR_WRITABLE
;
1420 attrsAndKind
|= DATA_DESCRIPTOR_KIND
;
1422 attrsAndKind
|= ACCESSOR_DESCRIPTOR_KIND
;
1425 Rooted
<ArrayObject
*> result(cx
);
1426 if (!desc
->isAccessorDescriptor()) {
1427 result
= NewDenseFullyAllocatedArray(cx
, 2);
1431 result
->setDenseInitializedLength(2);
1433 result
->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX
,
1434 Int32Value(attrsAndKind
));
1435 result
->initDenseElement(PROP_DESC_VALUE_INDEX
, desc
->value());
1437 result
= NewDenseFullyAllocatedArray(cx
, 3);
1441 result
->setDenseInitializedLength(3);
1443 result
->initDenseElement(PROP_DESC_ATTRS_AND_KIND_INDEX
,
1444 Int32Value(attrsAndKind
));
1446 if (JSObject
* get
= desc
->getter()) {
1447 result
->initDenseElement(PROP_DESC_GETTER_INDEX
, ObjectValue(*get
));
1449 result
->initDenseElement(PROP_DESC_GETTER_INDEX
, UndefinedValue());
1452 if (JSObject
* set
= desc
->setter()) {
1453 result
->initDenseElement(PROP_DESC_SETTER_INDEX
, ObjectValue(*set
));
1455 result
->initDenseElement(PROP_DESC_SETTER_INDEX
, UndefinedValue());
1459 vp
.setObject(*result
);
1463 // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1464 // 19.1.2.6 Object.getOwnPropertyDescriptor ( O, P )
1465 bool js::GetOwnPropertyDescriptorToArray(JSContext
* cx
, unsigned argc
,
1467 CallArgs args
= CallArgsFromVp(argc
, vp
);
1468 MOZ_ASSERT(args
.length() == 2);
1471 RootedObject
obj(cx
, ToObject(cx
, args
[0]));
1478 if (!ToPropertyKey(cx
, args
[1], &id
)) {
1483 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx
);
1484 if (!GetOwnPropertyDescriptor(cx
, obj
, id
, &desc
)) {
1489 return FromPropertyDescriptorToArray(cx
, desc
, args
.rval());
1492 static bool NewValuePair(JSContext
* cx
, HandleValue val1
, HandleValue val2
,
1493 MutableHandleValue rval
,
1494 gc::Heap heap
= gc::Heap::Default
) {
1495 NewObjectKind kind
=
1496 heap
== gc::Heap::Tenured
? TenuredObject
: GenericObject
;
1497 ArrayObject
* array
= NewDenseFullyAllocatedArray(cx
, 2, kind
);
1502 array
->setDenseInitializedLength(2);
1503 array
->initDenseElement(0, val1
);
1504 array
->initDenseElement(1, val2
);
1506 rval
.setObject(*array
);
1510 enum class EnumerableOwnPropertiesKind
{ Keys
, Values
, KeysAndValues
, Names
};
1512 static bool HasEnumerableStringNonDataProperties(NativeObject
* obj
) {
1513 // We also check for enumerability and symbol properties, so uninteresting
1514 // non-data properties like |array.length| don't let us fall into the slow
1516 if (!obj
->hasEnumerableProperty()) {
1519 for (ShapePropertyIter
<NoGC
> iter(obj
->shape()); !iter
.done(); iter
++) {
1520 if (!iter
->isDataProperty() && iter
->enumerable() &&
1521 !iter
->key().isSymbol()) {
1528 template <EnumerableOwnPropertiesKind kind
>
1529 static bool TryEnumerableOwnPropertiesNative(JSContext
* cx
, HandleObject obj
,
1530 MutableHandleValue rval
,
1534 // Use the fast path if |obj| has neither extra indexed properties nor a
1535 // newEnumerate hook. String objects need to be special-cased, because
1536 // they're only marked as indexed after their enumerate hook ran. And
1537 // because their enumerate hook is slowish, it's more performant to
1538 // exclude them directly instead of executing the hook first.
1539 if (!obj
->is
<NativeObject
>() || obj
->as
<NativeObject
>().isIndexed() ||
1540 obj
->getClass()->getNewEnumerate() || obj
->is
<StringObject
>()) {
1544 #ifdef ENABLE_RECORD_TUPLE
1545 if (obj
->is
<TupleObject
>()) {
1546 Rooted
<TupleType
*> tup(cx
, &obj
->as
<TupleObject
>().unbox());
1547 return TryEnumerableOwnPropertiesNative
<kind
>(cx
, tup
, rval
, optimized
);
1548 } else if (obj
->is
<RecordObject
>()) {
1549 Rooted
<RecordType
*> tup(cx
, obj
->as
<RecordObject
>().unbox());
1550 return TryEnumerableOwnPropertiesNative
<kind
>(cx
, tup
, rval
, optimized
);
1554 Handle
<NativeObject
*> nobj
= obj
.as
<NativeObject
>();
1556 // Resolve lazy properties on |nobj|.
1557 if (JSEnumerateOp enumerate
= nobj
->getClass()->getEnumerate()) {
1558 if (!enumerate(cx
, nobj
)) {
1562 // Ensure no extra indexed properties were added through enumerate().
1563 if (nobj
->isIndexed()) {
1570 RootedValueVector
properties(cx
);
1571 RootedValue
key(cx
);
1572 RootedValue
value(cx
);
1574 if (kind
== EnumerableOwnPropertiesKind::Keys
) {
1575 // If possible, attempt to use the shape's iterator cache.
1576 Rooted
<PropertyIteratorObject
*> piter(cx
,
1577 LookupInShapeIteratorCache(cx
, nobj
));
1580 NativeIterator
* ni
= piter
->getNativeIterator();
1581 MOZ_ASSERT(ni
->isReusable());
1583 // Guard against indexes.
1584 if (ni
->mayHavePrototypeProperties()) {
1588 JSLinearString
** properties
=
1589 ni
->propertiesBegin()->unbarrieredAddress();
1590 JSObject
* array
= NewDenseCopiedArray(cx
, ni
->numKeys(), properties
);
1595 rval
.setObject(*array
);
1602 // Switch to allocating in the tenured heap if necessary to avoid possible
1603 // quadratic behaviour marking stack rooted |properties| vector.
1604 AutoSelectGCHeap
gcHeap(cx
, 1);
1606 // We have ensured |nobj| contains no extra indexed properties, so the
1607 // only indexed properties we need to handle here are dense and typed
1610 // Pre-reserve to avoid reallocating the properties vector frequently.
1611 if (nobj
->getDenseInitializedLength() > 0 &&
1612 !properties
.reserve(nobj
->getDenseInitializedLength())) {
1615 for (uint32_t i
= 0, len
= nobj
->getDenseInitializedLength(); i
< len
; i
++) {
1616 value
.set(nobj
->getDenseElement(i
));
1617 if (value
.isMagic(JS_ELEMENTS_HOLE
)) {
1622 if (kind
!= EnumerableOwnPropertiesKind::Values
) {
1624 NativeObject::MAX_DENSE_ELEMENTS_COUNT
<= PropertyKey::IntMax
,
1625 "dense elements don't exceed PropertyKey::IntMax");
1626 str
= Int32ToStringWithHeap
<CanGC
>(cx
, i
, gcHeap
);
1632 if (kind
== EnumerableOwnPropertiesKind::Keys
||
1633 kind
== EnumerableOwnPropertiesKind::Names
) {
1634 value
.setString(str
);
1635 } else if (kind
== EnumerableOwnPropertiesKind::KeysAndValues
) {
1637 if (!NewValuePair(cx
, key
, value
, &value
, gcHeap
)) {
1642 if (!properties
.append(value
)) {
1647 if (obj
->is
<TypedArrayObject
>()) {
1648 Handle
<TypedArrayObject
*> tobj
= obj
.as
<TypedArrayObject
>();
1649 size_t len
= tobj
->length().valueOr(0);
1651 // Fail early if the typed array contains too many elements for a
1652 // dense array, because we likely OOM anyway when trying to allocate
1653 // more than 2GB for the properties vector. This also means we don't
1654 // need to handle indices greater than MAX_INT32 in the loop below.
1655 if (len
> NativeObject::MAX_DENSE_ELEMENTS_COUNT
) {
1656 ReportOversizedAllocation(cx
, JSMSG_ALLOC_OVERFLOW
);
1660 MOZ_ASSERT(properties
.empty(), "typed arrays cannot have dense elements");
1661 if (!properties
.resize(len
)) {
1665 for (uint32_t i
= 0; i
< len
; i
++) {
1667 if (kind
!= EnumerableOwnPropertiesKind::Values
) {
1669 NativeObject::MAX_DENSE_ELEMENTS_COUNT
<= PropertyKey::IntMax
,
1670 "dense elements don't exceed PropertyKey::IntMax");
1671 str
= Int32ToStringWithHeap
<CanGC
>(cx
, i
, gcHeap
);
1677 if (kind
== EnumerableOwnPropertiesKind::Keys
||
1678 kind
== EnumerableOwnPropertiesKind::Names
) {
1679 value
.setString(str
);
1680 } else if (kind
== EnumerableOwnPropertiesKind::Values
) {
1681 if (!tobj
->getElement
<CanGC
>(cx
, i
, &value
)) {
1686 if (!tobj
->getElement
<CanGC
>(cx
, i
, &value
)) {
1689 if (!NewValuePair(cx
, key
, value
, &value
, gcHeap
)) {
1694 properties
[i
].set(value
);
1697 #ifdef ENABLE_RECORD_TUPLE
1698 else if (obj
->is
<RecordType
>()) {
1699 RecordType
* rec
= &obj
->as
<RecordType
>();
1700 Rooted
<ArrayObject
*> keys(cx
, rec
->keys());
1702 RootedString
keyStr(cx
);
1704 MOZ_ASSERT(properties
.empty(), "records cannot have dense elements");
1705 if (!properties
.resize(keys
->length())) {
1709 for (size_t i
= 0; i
< keys
->length(); i
++) {
1710 MOZ_ASSERT(keys
->getDenseElement(i
).isString());
1711 if (kind
== EnumerableOwnPropertiesKind::Keys
||
1712 kind
== EnumerableOwnPropertiesKind::Names
) {
1713 value
.set(keys
->getDenseElement(i
));
1714 } else if (kind
== EnumerableOwnPropertiesKind::Values
) {
1715 keyStr
.set(keys
->getDenseElement(i
).toString());
1717 if (!JS_StringToId(cx
, keyStr
, &keyId
)) {
1720 MOZ_ALWAYS_TRUE(rec
->getOwnProperty(cx
, keyId
, &value
));
1722 MOZ_ASSERT(kind
== EnumerableOwnPropertiesKind::KeysAndValues
);
1724 key
.set(keys
->getDenseElement(i
));
1725 keyStr
.set(key
.toString());
1727 if (!JS_StringToId(cx
, keyStr
, &keyId
)) {
1730 MOZ_ALWAYS_TRUE(rec
->getOwnProperty(cx
, keyId
, &value
));
1732 if (!NewValuePair(cx
, key
, value
, &value
, gcHeap
)) {
1737 properties
[i
].set(value
);
1740 // Uh, goto... When using records, we already get the (sorted) properties
1741 // from its sorted keys, so we don't read them again as "own properties".
1742 // We could use an `if` or some refactoring to skip the next logic, but
1743 // goto makes it easer to keep the logic separated in
1744 // "#ifdef ENABLE_RECORD_TUPLE" blocks.
1745 // This should be refactored when the #ifdefs are removed.
1750 // Up to this point no side-effects through accessor properties are
1751 // possible which could have replaced |obj| with a non-native object.
1752 MOZ_ASSERT(obj
->is
<NativeObject
>());
1753 MOZ_ASSERT(obj
.as
<NativeObject
>() == nobj
);
1756 // This new scope exists to support the goto end used by
1757 // ENABLE_RECORD_TUPLE builds, and can be removed when said goto goes away.
1758 size_t approximatePropertyCount
=
1759 nobj
->shape()->propMap()
1760 ? nobj
->shape()->propMap()->approximateEntryCount()
1762 if (!properties
.reserve(properties
.length() + approximatePropertyCount
)) {
1767 if (kind
== EnumerableOwnPropertiesKind::Keys
||
1768 kind
== EnumerableOwnPropertiesKind::Names
||
1769 !HasEnumerableStringNonDataProperties(nobj
)) {
1770 // If |kind == Values| or |kind == KeysAndValues|:
1771 // All enumerable properties with string property keys are data
1772 // properties. This allows us to collect the property values while
1773 // iterating over the shape hierarchy without worrying over accessors
1774 // modifying any state.
1776 constexpr bool onlyEnumerable
= kind
!= EnumerableOwnPropertiesKind::Names
;
1777 if (!onlyEnumerable
|| nobj
->hasEnumerableProperty()) {
1778 size_t elements
= properties
.length();
1779 constexpr AllowGC allowGC
=
1780 kind
!= EnumerableOwnPropertiesKind::KeysAndValues
? AllowGC::NoGC
1782 mozilla::Maybe
<ShapePropertyIter
<allowGC
>> m
;
1783 if constexpr (allowGC
== AllowGC::NoGC
) {
1784 m
.emplace(nobj
->shape());
1786 m
.emplace(cx
, nobj
->shape());
1788 for (auto& iter
= m
.ref(); !iter
.done(); iter
++) {
1789 jsid id
= iter
->key();
1790 if ((onlyEnumerable
&& !iter
->enumerable()) || id
.isSymbol()) {
1793 MOZ_ASSERT(!id
.isInt(), "Unexpected indexed property");
1794 MOZ_ASSERT_IF(kind
== EnumerableOwnPropertiesKind::Values
||
1795 kind
== EnumerableOwnPropertiesKind::KeysAndValues
,
1796 iter
->isDataProperty());
1798 if constexpr (kind
== EnumerableOwnPropertiesKind::Keys
||
1799 kind
== EnumerableOwnPropertiesKind::Names
) {
1800 value
.setString(id
.toString());
1801 } else if constexpr (kind
== EnumerableOwnPropertiesKind::Values
) {
1802 value
.set(nobj
->getSlot(iter
->slot()));
1804 key
.setString(id
.toString());
1805 value
.set(nobj
->getSlot(iter
->slot()));
1806 if (!NewValuePair(cx
, key
, value
, &value
, gcHeap
)) {
1811 if (!properties
.append(value
)) {
1816 // The (non-indexed) properties were visited in reverse iteration order,
1817 // call std::reverse() to ensure they appear in iteration order.
1818 std::reverse(properties
.begin() + elements
, properties
.end());
1821 MOZ_ASSERT(kind
== EnumerableOwnPropertiesKind::Values
||
1822 kind
== EnumerableOwnPropertiesKind::KeysAndValues
);
1824 // Get a list of all |obj| properties. As long as obj->shape()
1825 // is equal to |objShape|, we can use this to speed up both the
1826 // enumerability check and GetProperty.
1827 Rooted
<PropertyInfoWithKeyVector
> props(cx
, PropertyInfoWithKeyVector(cx
));
1829 // Collect all non-symbol properties.
1830 Rooted
<NativeShape
*> objShape(cx
, nobj
->shape());
1831 for (ShapePropertyIter
<NoGC
> iter(objShape
); !iter
.done(); iter
++) {
1832 if (iter
->key().isSymbol()) {
1835 MOZ_ASSERT(!iter
->key().isInt(), "Unexpected indexed property");
1837 if (!props
.append(*iter
)) {
1843 for (size_t i
= props
.length(); i
> 0; i
--) {
1844 PropertyInfoWithKey prop
= props
[i
- 1];
1847 // If |obj| still has the same shape, it must still be a NativeObject with
1848 // the properties in |props|.
1849 if (obj
->shape() == objShape
&& prop
.isDataProperty()) {
1850 if (!prop
.enumerable()) {
1853 value
= obj
->as
<NativeObject
>().getSlot(prop
.slot());
1855 // |obj| changed shape or the property is not a data property,
1856 // so we have to do the slower enumerability check and
1859 if (!PropertyIsEnumerable(cx
, obj
, id
, &enumerable
)) {
1865 if (!GetProperty(cx
, obj
, obj
, id
, &value
)) {
1870 if (kind
== EnumerableOwnPropertiesKind::KeysAndValues
) {
1871 key
.setString(id
.toString());
1872 if (!NewValuePair(cx
, key
, value
, &value
, gcHeap
)) {
1877 if (!properties
.append(value
)) {
1883 #ifdef ENABLE_RECORD_TUPLE
1888 NewDenseCopiedArray(cx
, properties
.length(), properties
.begin());
1893 rval
.setObject(*array
);
1897 // Optimization dedicated for `Object.keys(..).length` JS pattern. This function
1898 // replicates TryEnumerableOwnPropertiesNative code, except that instead of
1899 // generating an array we only return the length of the array that would have
1902 // As opposed to TryEnumerableOwnPropertiesNative, this function only support
1903 // EnumerableOwnPropertiesKind::Keys variant.
1904 static bool CountEnumerableOwnPropertiesNative(JSContext
* cx
, HandleObject obj
,
1905 int32_t& rval
, bool* optimized
) {
1908 // Use the fast path if |obj| has neither extra indexed properties nor a
1909 // newEnumerate hook. String objects need to be special-cased, because
1910 // they're only marked as indexed after their enumerate hook ran. And
1911 // because their enumerate hook is slowish, it's more performant to
1912 // exclude them directly instead of executing the hook first.
1913 if (!obj
->is
<NativeObject
>() || obj
->as
<NativeObject
>().isIndexed() ||
1914 obj
->getClass()->getNewEnumerate() || obj
->is
<StringObject
>()) {
1918 #ifdef ENABLE_RECORD_TUPLE
1919 // Skip the optimized path in case of record and tuples.
1920 if (obj
->is
<TupleObject
>() || obj
->is
<RecordObject
>()) {
1925 Handle
<NativeObject
*> nobj
= obj
.as
<NativeObject
>();
1927 // Resolve lazy properties on |nobj|.
1928 if (JSEnumerateOp enumerate
= nobj
->getClass()->getEnumerate()) {
1929 if (!enumerate(cx
, nobj
)) {
1933 // Ensure no extra indexed properties were added through enumerate().
1934 if (nobj
->isIndexed()) {
1941 int32_t num_properties
= 0;
1943 // If possible, attempt to use the shape's iterator cache.
1944 Rooted
<PropertyIteratorObject
*> piter(cx
,
1945 LookupInShapeIteratorCache(cx
, nobj
));
1947 NativeIterator
* ni
= piter
->getNativeIterator();
1948 MOZ_ASSERT(ni
->isReusable());
1950 // Guard against indexes.
1951 if (!ni
->mayHavePrototypeProperties()) {
1952 rval
= ni
->numKeys();
1957 for (uint32_t i
= 0, len
= nobj
->getDenseInitializedLength(); i
< len
; i
++) {
1958 if (nobj
->getDenseElement(i
).isMagic(JS_ELEMENTS_HOLE
)) {
1962 num_properties
+= 1;
1965 if (obj
->is
<TypedArrayObject
>()) {
1966 Handle
<TypedArrayObject
*> tobj
= obj
.as
<TypedArrayObject
>();
1967 size_t len
= tobj
->length().valueOr(0);
1969 // Fail early if the typed array contains too many elements for a
1970 // dense array, because we likely OOM anyway when trying to allocate
1971 // more than 2GB for the properties vector. This also means we don't
1972 // need to handle indices greater than MAX_INT32 in the loop below.
1973 if (len
> NativeObject::MAX_DENSE_ELEMENTS_COUNT
) {
1974 ReportOversizedAllocation(cx
, JSMSG_ALLOC_OVERFLOW
);
1978 MOZ_ASSERT(num_properties
== 0, "typed arrays cannot have dense elements");
1979 num_properties
= len
;
1982 // All enumerable properties with string property keys are data
1983 // properties. This allows us to collect the property values while
1984 // iterating over the shape hierarchy without worrying over accessors
1985 // modifying any state.
1987 if (nobj
->hasEnumerableProperty()) {
1988 for (ShapePropertyIter
<AllowGC::NoGC
> iter(obj
.as
<NativeObject
>()->shape());
1989 !iter
.done(); iter
++) {
1990 jsid id
= iter
->key();
1991 if (!iter
->enumerable() || id
.isSymbol()) {
1994 MOZ_ASSERT(!id
.isInt(), "Unexpected indexed property");
1995 num_properties
+= 1;
1999 rval
= num_properties
;
2003 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
2004 // 7.3.21 EnumerableOwnProperties ( O, kind )
2005 template <EnumerableOwnPropertiesKind kind
>
2006 static bool EnumerableOwnProperties(JSContext
* cx
, const JS::CallArgs
& args
) {
2007 static_assert(kind
== EnumerableOwnPropertiesKind::Values
||
2008 kind
== EnumerableOwnPropertiesKind::KeysAndValues
,
2009 "Only implemented for Object.keys and Object.entries");
2011 // Step 1. (Step 1 of Object.{keys,values,entries}, really.)
2012 RootedObject
obj(cx
, IF_RECORD_TUPLE(ToObjectOrGetObjectPayload
, ToObject
)(
2019 if (!TryEnumerableOwnPropertiesNative
<kind
>(cx
, obj
, args
.rval(),
2027 // Typed arrays are always handled in the fast path.
2028 MOZ_ASSERT(!obj
->is
<TypedArrayObject
>());
2031 RootedIdVector
ids(cx
);
2032 if (!GetPropertyKeys(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, &ids
)) {
2037 RootedValueVector
properties(cx
);
2038 size_t len
= ids
.length();
2039 if (!properties
.resize(len
)) {
2044 RootedValue
key(cx
);
2045 RootedValue
value(cx
);
2046 Rooted
<Shape
*> shape(cx
);
2047 Rooted
<Maybe
<PropertyDescriptor
>> desc(cx
);
2050 for (size_t i
= 0; i
< len
; i
++) {
2053 // Step 4.a. (Symbols were filtered out in step 2.)
2054 MOZ_ASSERT(!id
.isSymbol());
2056 if (kind
!= EnumerableOwnPropertiesKind::Values
) {
2057 if (!IdToStringOrSymbol(cx
, id
, &key
)) {
2063 if (obj
->is
<NativeObject
>()) {
2064 Handle
<NativeObject
*> nobj
= obj
.as
<NativeObject
>();
2065 if (id
.isInt() && nobj
->containsDenseElement(id
.toInt())) {
2066 value
.set(nobj
->getDenseElement(id
.toInt()));
2068 Maybe
<PropertyInfo
> prop
= nobj
->lookup(cx
, id
);
2069 if (prop
.isNothing() || !prop
->enumerable()) {
2072 if (prop
->isDataProperty()) {
2073 value
= nobj
->getSlot(prop
->slot());
2074 } else if (!GetProperty(cx
, obj
, obj
, id
, &value
)) {
2079 if (!GetOwnPropertyDescriptor(cx
, obj
, id
, &desc
)) {
2083 // Step 4.a.ii. (inverted.)
2084 if (desc
.isNothing() || !desc
->enumerable()) {
2089 // (Omitted because Object.keys doesn't use this implementation.)
2092 if (!GetProperty(cx
, obj
, obj
, id
, &value
)) {
2097 // Steps 4.a.ii.2.b-c.
2098 if (kind
== EnumerableOwnPropertiesKind::Values
) {
2099 properties
[out
++].set(value
);
2100 } else if (!NewValuePair(cx
, key
, value
, properties
[out
++])) {
2106 // (Implemented in step 2.)
2108 // Step 3 of Object.{keys,values,entries}
2109 JSObject
* aobj
= NewDenseCopiedArray(cx
, out
, properties
.begin());
2114 args
.rval().setObject(*aobj
);
2118 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
2119 // 19.1.2.16 Object.keys ( O )
2120 bool js::obj_keys(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2121 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object", "keys");
2122 CallArgs args
= CallArgsFromVp(argc
, vp
);
2125 RootedObject
obj(cx
, IF_RECORD_TUPLE(ToObjectOrGetObjectPayload
, ToObject
)(
2132 static constexpr EnumerableOwnPropertiesKind kind
=
2133 EnumerableOwnPropertiesKind::Keys
;
2134 if (!TryEnumerableOwnPropertiesNative
<kind
>(cx
, obj
, args
.rval(),
2143 return GetOwnPropertyKeys(cx
, obj
, JSITER_OWNONLY
, args
.rval());
2146 bool js::obj_keys_length(JSContext
* cx
, HandleObject obj
, int32_t& length
) {
2148 if (!CountEnumerableOwnPropertiesNative(cx
, obj
, length
, &optimized
)) {
2155 // Object.keys: Steps 2-3.
2156 // (GetOwnPropertyKeys / CountOwnPropertyKeys)
2157 RootedIdVector
keys(cx
);
2158 if (!GetPropertyKeys(cx
, obj
, JSITER_OWNONLY
, &keys
)) {
2162 length
= keys
.length();
2166 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
2167 // 19.1.2.21 Object.values ( O )
2168 static bool obj_values(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2169 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object", "values");
2170 CallArgs args
= CallArgsFromVp(argc
, vp
);
2173 return EnumerableOwnProperties
<EnumerableOwnPropertiesKind::Values
>(cx
, args
);
2176 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
2177 // 19.1.2.5 Object.entries ( O )
2178 static bool obj_entries(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2179 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object", "entries");
2180 CallArgs args
= CallArgsFromVp(argc
, vp
);
2183 return EnumerableOwnProperties
<EnumerableOwnPropertiesKind::KeysAndValues
>(
2187 /* ES6 draft 15.2.3.16 */
2188 bool js::obj_is(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2189 CallArgs args
= CallArgsFromVp(argc
, vp
);
2192 if (!SameValue(cx
, args
.get(0), args
.get(1), &same
)) {
2196 args
.rval().setBoolean(same
);
2200 bool js::IdToStringOrSymbol(JSContext
* cx
, HandleId id
,
2201 MutableHandleValue result
) {
2203 JSString
* str
= Int32ToString
<CanGC
>(cx
, id
.toInt());
2207 result
.setString(str
);
2208 } else if (id
.isAtom()) {
2209 result
.setString(id
.toAtom());
2211 result
.setSymbol(id
.toSymbol());
2216 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
2217 // 19.1.2.10.1 Runtime Semantics: GetOwnPropertyKeys ( O, Type )
2218 bool js::GetOwnPropertyKeys(JSContext
* cx
, HandleObject obj
, unsigned flags
,
2219 MutableHandleValue rval
) {
2220 // Step 1 (Performed in caller).
2223 RootedIdVector
keys(cx
);
2224 if (!GetPropertyKeys(cx
, obj
, flags
, &keys
)) {
2228 // Step 5 (Inlined CreateArrayFromList).
2229 Rooted
<ArrayObject
*> array(cx
,
2230 NewDenseFullyAllocatedArray(cx
, keys
.length()));
2235 array
->ensureDenseInitializedLength(0, keys
.length());
2237 RootedValue
val(cx
);
2238 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
2239 MOZ_ASSERT_IF(keys
[i
].isSymbol(), flags
& JSITER_SYMBOLS
);
2240 MOZ_ASSERT_IF(!keys
[i
].isSymbol(), !(flags
& JSITER_SYMBOLSONLY
));
2241 if (!IdToStringOrSymbol(cx
, keys
[i
], &val
)) {
2244 array
->initDenseElement(i
, val
);
2247 rval
.setObject(*array
);
2251 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
2252 // 19.1.2.9 Object.getOwnPropertyNames ( O )
2253 static bool obj_getOwnPropertyNames(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2254 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object", "getOwnPropertyNames");
2255 CallArgs args
= CallArgsFromVp(argc
, vp
);
2257 RootedObject
obj(cx
, ToObject(cx
, args
.get(0)));
2263 static constexpr EnumerableOwnPropertiesKind kind
=
2264 EnumerableOwnPropertiesKind::Names
;
2265 if (!TryEnumerableOwnPropertiesNative
<kind
>(cx
, obj
, args
.rval(),
2273 return GetOwnPropertyKeys(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
,
2277 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
2278 // 19.1.2.10 Object.getOwnPropertySymbols ( O )
2279 static bool obj_getOwnPropertySymbols(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2280 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object", "getOwnPropertySymbols");
2281 CallArgs args
= CallArgsFromVp(argc
, vp
);
2283 RootedObject
obj(cx
, ToObject(cx
, args
.get(0)));
2288 return GetOwnPropertyKeys(
2290 JSITER_OWNONLY
| JSITER_HIDDEN
| JSITER_SYMBOLS
| JSITER_SYMBOLSONLY
,
2294 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2295 static bool obj_defineProperties(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2296 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Object", "defineProperties");
2297 CallArgs args
= CallArgsFromVp(argc
, vp
);
2300 RootedObject
obj(cx
);
2301 if (!GetFirstArgumentAsObject(cx
, args
, "Object.defineProperties", &obj
)) {
2306 if (!args
.requireAtLeast(cx
, "Object.defineProperties", 2)) {
2311 bool failedOnWindowProxy
= false;
2312 if (!ObjectDefineProperties(cx
, obj
, args
[1], &failedOnWindowProxy
)) {
2316 /* Step 7, but modified to deal with WindowProxy mess */
2317 if (failedOnWindowProxy
) {
2318 args
.rval().setNull();
2320 args
.rval().setObject(*obj
);
2325 // ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
2326 static bool obj_preventExtensions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2327 CallArgs args
= CallArgsFromVp(argc
, vp
);
2328 args
.rval().set(args
.get(0));
2331 if (!args
.get(0).isObject()) {
2336 RootedObject
obj(cx
, &args
.get(0).toObject());
2337 return PreventExtensions(cx
, obj
);
2340 // ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
2341 static bool obj_freeze(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2342 CallArgs args
= CallArgsFromVp(argc
, vp
);
2343 args
.rval().set(args
.get(0));
2346 if (!args
.get(0).isObject()) {
2351 RootedObject
obj(cx
, &args
.get(0).toObject());
2352 return SetIntegrityLevel(cx
, obj
, IntegrityLevel::Frozen
);
2355 // ES6 draft rev27 (2014/08/24) 19.1.2.12 Object.isFrozen(O)
2356 static bool obj_isFrozen(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2357 CallArgs args
= CallArgsFromVp(argc
, vp
);
2363 if (args
.get(0).isObject()) {
2364 RootedObject
obj(cx
, &args
.get(0).toObject());
2365 if (!TestIntegrityLevel(cx
, obj
, IntegrityLevel::Frozen
, &frozen
)) {
2369 args
.rval().setBoolean(frozen
);
2373 // ES6 draft rev27 (2014/08/24) 19.1.2.17 Object.seal(O)
2374 static bool obj_seal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2375 CallArgs args
= CallArgsFromVp(argc
, vp
);
2376 args
.rval().set(args
.get(0));
2379 if (!args
.get(0).isObject()) {
2384 RootedObject
obj(cx
, &args
.get(0).toObject());
2385 return SetIntegrityLevel(cx
, obj
, IntegrityLevel::Sealed
);
2388 // ES6 draft rev27 (2014/08/24) 19.1.2.13 Object.isSealed(O)
2389 static bool obj_isSealed(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2390 CallArgs args
= CallArgsFromVp(argc
, vp
);
2396 if (args
.get(0).isObject()) {
2397 RootedObject
obj(cx
, &args
.get(0).toObject());
2398 if (!TestIntegrityLevel(cx
, obj
, IntegrityLevel::Sealed
, &sealed
)) {
2402 args
.rval().setBoolean(sealed
);
2406 bool js::obj_setProto(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2407 CallArgs args
= CallArgsFromVp(argc
, vp
);
2408 MOZ_ASSERT(args
.length() == 1);
2410 HandleValue thisv
= args
.thisv();
2411 if (thisv
.isNullOrUndefined()) {
2412 ReportIncompatible(cx
, args
);
2415 if (thisv
.isPrimitive()) {
2416 // Mutating a boxed primitive's [[Prototype]] has no side effects.
2417 args
.rval().setUndefined();
2421 /* Do nothing if __proto__ isn't being set to an object or null. */
2422 if (!args
[0].isObjectOrNull()) {
2423 args
.rval().setUndefined();
2427 Rooted
<JSObject
*> obj(cx
, &args
.thisv().toObject());
2428 Rooted
<JSObject
*> newProto(cx
, args
[0].toObjectOrNull());
2429 if (!SetPrototype(cx
, obj
, newProto
)) {
2433 args
.rval().setUndefined();
2437 static const JSFunctionSpec object_methods
[] = {
2438 JS_FN("toSource", obj_toSource
, 0, 0),
2439 JS_INLINABLE_FN("toString", obj_toString
, 0, 0, ObjectToString
),
2440 JS_SELF_HOSTED_FN("toLocaleString", "Object_toLocaleString", 0, 0),
2441 JS_SELF_HOSTED_FN("valueOf", "Object_valueOf", 0, 0),
2442 JS_SELF_HOSTED_FN("hasOwnProperty", "Object_hasOwnProperty", 1, 0),
2443 JS_INLINABLE_FN("isPrototypeOf", obj_isPrototypeOf
, 1, 0,
2444 ObjectIsPrototypeOf
),
2445 JS_FN("propertyIsEnumerable", obj_propertyIsEnumerable
, 1, 0),
2446 JS_SELF_HOSTED_FN("__defineGetter__", "ObjectDefineGetter", 2, 0),
2447 JS_SELF_HOSTED_FN("__defineSetter__", "ObjectDefineSetter", 2, 0),
2448 JS_SELF_HOSTED_FN("__lookupGetter__", "ObjectLookupGetter", 1, 0),
2449 JS_SELF_HOSTED_FN("__lookupSetter__", "ObjectLookupSetter", 1, 0),
2452 static const JSPropertySpec object_properties
[] = {
2453 JS_SELF_HOSTED_GETSET("__proto__", "$ObjectProtoGetter",
2454 "$ObjectProtoSetter", 0),
2457 static const JSFunctionSpec object_static_methods
[] = {
2458 JS_FN("assign", obj_assign
, 2, 0),
2459 JS_SELF_HOSTED_FN("getPrototypeOf", "ObjectGetPrototypeOf", 1, 0),
2460 JS_FN("setPrototypeOf", obj_setPrototypeOf
, 2, 0),
2461 JS_SELF_HOSTED_FN("getOwnPropertyDescriptor",
2462 "ObjectGetOwnPropertyDescriptor", 2, 0),
2463 JS_SELF_HOSTED_FN("getOwnPropertyDescriptors",
2464 "ObjectGetOwnPropertyDescriptors", 1, 0),
2465 JS_INLINABLE_FN("keys", obj_keys
, 1, 0, ObjectKeys
),
2466 JS_FN("values", obj_values
, 1, 0),
2467 JS_FN("entries", obj_entries
, 1, 0),
2468 JS_INLINABLE_FN("is", obj_is
, 2, 0, ObjectIs
),
2469 JS_SELF_HOSTED_FN("defineProperty", "ObjectDefineProperty", 3, 0),
2470 JS_FN("defineProperties", obj_defineProperties
, 2, 0),
2471 JS_INLINABLE_FN("create", obj_create
, 2, 0, ObjectCreate
),
2472 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames
, 1, 0),
2473 JS_FN("getOwnPropertySymbols", obj_getOwnPropertySymbols
, 1, 0),
2474 JS_SELF_HOSTED_FN("isExtensible", "ObjectIsExtensible", 1, 0),
2475 JS_FN("preventExtensions", obj_preventExtensions
, 1, 0),
2476 JS_FN("freeze", obj_freeze
, 1, 0),
2477 JS_FN("isFrozen", obj_isFrozen
, 1, 0),
2478 JS_FN("seal", obj_seal
, 1, 0),
2479 JS_FN("isSealed", obj_isSealed
, 1, 0),
2480 JS_SELF_HOSTED_FN("fromEntries", "ObjectFromEntries", 1, 0),
2481 JS_SELF_HOSTED_FN("hasOwn", "ObjectHasOwn", 2, 0),
2482 JS_SELF_HOSTED_FN("groupBy", "ObjectGroupBy", 2, 0),
2485 static JSObject
* CreateObjectConstructor(JSContext
* cx
, JSProtoKey key
) {
2486 Rooted
<GlobalObject
*> self(cx
, cx
->global());
2487 if (!GlobalObject::ensureConstructor(cx
, self
, JSProto_Function
)) {
2491 /* Create the Object function now that we have a [[Prototype]] for it. */
2492 JSFunction
* fun
= NewNativeConstructor(
2493 cx
, obj_construct
, 1, Handle
<PropertyName
*>(cx
->names().Object
),
2494 gc::AllocKind::FUNCTION
, TenuredObject
);
2499 fun
->setJitInfo(&jit::JitInfo_Object
);
2503 static JSObject
* CreateObjectPrototype(JSContext
* cx
, JSProtoKey key
) {
2504 MOZ_ASSERT(!cx
->zone()->isAtomsZone());
2505 MOZ_ASSERT(cx
->global()->is
<NativeObject
>());
2508 * Create |Object.prototype| first, mirroring CreateBlankProto but for the
2509 * prototype of the created object.
2511 Rooted
<PlainObject
*> objectProto(
2512 cx
, NewPlainObjectWithProto(cx
, nullptr, TenuredObject
));
2518 if (!SetImmutablePrototype(cx
, objectProto
, &succeeded
)) {
2521 MOZ_ASSERT(succeeded
,
2522 "should have been able to make a fresh Object.prototype's "
2523 "[[Prototype]] immutable");
2528 static bool FinishObjectClassInit(JSContext
* cx
, JS::HandleObject ctor
,
2529 JS::HandleObject proto
) {
2530 Rooted
<GlobalObject
*> global(cx
, cx
->global());
2533 RootedId
evalId(cx
, NameToId(cx
->names().eval
));
2534 JSFunction
* evalobj
=
2535 DefineFunction(cx
, global
, evalId
, IndirectEval
, 1, JSPROP_RESOLVING
);
2539 global
->setOriginalEval(evalobj
);
2542 if (cx
->options().fuzzing()) {
2543 if (!DefineTestingFunctions(cx
, global
, /* fuzzingSafe = */ true,
2544 /* disableOOMFunctions = */ false)) {
2550 // The global object should have |Object.prototype| as its [[Prototype]].
2551 MOZ_ASSERT(global
->staticPrototype() == nullptr);
2552 MOZ_ASSERT(!global
->staticPrototypeIsImmutable());
2553 return SetPrototype(cx
, global
, proto
);
2556 static const ClassSpec PlainObjectClassSpec
= {
2557 CreateObjectConstructor
, CreateObjectPrototype
,
2558 object_static_methods
, nullptr,
2559 object_methods
, object_properties
,
2560 FinishObjectClassInit
};
2562 const JSClass
PlainObject::class_
= {"Object",
2563 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
2564 JS_NULL_CLASS_OPS
, &PlainObjectClassSpec
};
2566 const JSClass
* const js::ObjectClassPtr
= &PlainObject::class_
;