MapObject-inl.h (3678B)
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 #ifndef builtin_MapObject_inl_h 8 #define builtin_MapObject_inl_h 9 10 #include "builtin/MapObject.h" 11 12 #include "vm/JSObject.h" 13 #include "vm/NativeObject.h" 14 15 #include "vm/JSObject-inl.h" 16 #include "vm/NativeObject-inl.h" 17 18 namespace js { 19 20 enum class MapOrSet { Map, Set }; 21 22 template <MapOrSet IsMapOrSet> 23 [[nodiscard]] static bool IsOptimizableArrayForMapOrSetCtor(JSObject* iterable, 24 JSContext* cx) { 25 if (!IsArrayWithDefaultIterator<MustBePacked::Yes>(iterable, cx)) { 26 return false; 27 } 28 29 // For the Map and WeakMap constructors, ensure the elements are also packed 30 // arrays with at least two elements (key and value). 31 // 32 // Limit this to relatively short arrays to avoid adding overhead for large 33 // arrays in the worst case, when this check fails for one of the last 34 // elements. 35 if constexpr (IsMapOrSet == MapOrSet::Map) { 36 ArrayObject* array = &iterable->as<ArrayObject>(); 37 size_t len = array->length(); 38 static constexpr size_t MaxLength = 100; 39 if (len > MaxLength) { 40 return false; 41 } 42 for (size_t i = 0; i < len; i++) { 43 Value elem = array->getDenseElement(i); 44 if (!elem.isObject()) { 45 return false; 46 } 47 JSObject* obj = &elem.toObject(); 48 if (!IsPackedArray(obj) || obj->as<ArrayObject>().length() < 2) { 49 return false; 50 } 51 } 52 } 53 54 return true; 55 } 56 57 template <JSProtoKey ProtoKey> 58 [[nodiscard]] static bool CanOptimizeMapOrSetCtorWithIterable( 59 JSNative addOrSetNative, NativeObject* mapOrSetObject, JSContext* cx) { 60 constexpr bool isMap = ProtoKey == JSProto_Map || ProtoKey == JSProto_WeakMap; 61 constexpr bool isSet = ProtoKey == JSProto_Set || ProtoKey == JSProto_WeakSet; 62 static_assert(isMap != isSet, "must be either a Map or a Set"); 63 64 // Ensures mapOrSetObject's prototype is the canonical prototype. 65 JSObject* proto = mapOrSetObject->staticPrototype(); 66 MOZ_ASSERT(proto); 67 if (proto != cx->global()->maybeGetPrototype(ProtoKey)) { 68 return false; 69 } 70 71 // Ensure the 'add' or 'set' method on the prototype is unchanged. Use a fast 72 // path based on fuses. 73 switch (ProtoKey) { 74 case JSProto_Map: 75 if (cx->realm()->realmFuses.optimizeMapPrototypeSetFuse.intact()) { 76 return true; 77 } 78 break; 79 case JSProto_Set: 80 if (cx->realm()->realmFuses.optimizeSetPrototypeAddFuse.intact()) { 81 return true; 82 } 83 break; 84 case JSProto_WeakMap: 85 if (cx->realm()->realmFuses.optimizeWeakMapPrototypeSetFuse.intact()) { 86 return true; 87 } 88 break; 89 case JSProto_WeakSet: 90 if (cx->realm()->realmFuses.optimizeWeakSetPrototypeAddFuse.intact()) { 91 return true; 92 } 93 break; 94 default: 95 MOZ_CRASH("Unexpected ProtoKey"); 96 } 97 98 // Look up the 'add' or 'set' property on the prototype object. 99 auto* nproto = &proto->as<NativeObject>(); 100 PropertyName* propName = isSet ? cx->names().add : cx->names().set; 101 mozilla::Maybe<PropertyInfo> prop = nproto->lookup(cx, propName); 102 if (prop.isNothing() || !prop->isDataProperty()) { 103 return false; 104 } 105 106 // Get the property value and ensure it's the canonical 'add' or 'set' 107 // function. 108 Value propVal = nproto->getSlot(prop->slot()); 109 return IsNativeFunction(propVal, addOrSetNative); 110 } 111 112 } // namespace js 113 114 #endif /* builtin_MapObject_inl_h */