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