tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */