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