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