tor-browser

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

ObjectFuse.cpp (7564B)


      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 "vm/ObjectFuse.h"
      8 
      9 #include "gc/Barrier.h"
     10 #include "gc/StableCellHasher.h"
     11 #include "jit/InvalidationScriptSet.h"
     12 #include "js/SweepingAPI.h"
     13 #include "vm/JSScript.h"
     14 #include "vm/NativeObject.h"
     15 #include "vm/PropertyInfo.h"
     16 
     17 #include "gc/StableCellHasher-inl.h"
     18 #include "vm/JSScript-inl.h"
     19 
     20 using namespace js;
     21 
     22 bool ObjectFuse::ensurePropertyStateLength(uint32_t length) {
     23  MOZ_ASSERT(length > 0);
     24 
     25  if (length <= propertyStateLength_) {
     26    MOZ_ASSERT(propertyStateBits_);
     27    return true;
     28  }
     29 
     30  if (!propertyStateBits_) {
     31    propertyStateBits_.reset(js_pod_malloc<uint32_t>(length));
     32    if (!propertyStateBits_) {
     33      return false;
     34    }
     35  } else {
     36    uint32_t* bitsNew = js_pod_realloc<uint32_t>(propertyStateBits_.get(),
     37                                                 propertyStateLength_, length);
     38    if (!bitsNew) {
     39      return false;
     40    }
     41    (void)propertyStateBits_.release();
     42    propertyStateBits_.reset(bitsNew);
     43  }
     44 
     45  // Initialize the new words to 0.
     46  static_assert(uint32_t(PropertyState::Untracked) == 0);
     47  std::uninitialized_fill_n(propertyStateBits_.get() + propertyStateLength_,
     48                            length - propertyStateLength_, 0);
     49  propertyStateLength_ = length;
     50  return true;
     51 }
     52 
     53 bool ObjectFuse::addDependency(uint32_t propSlot,
     54                               const jit::IonScriptKey& ionScript) {
     55  MOZ_ASSERT(getPropertyState(propSlot) == PropertyState::Constant);
     56 
     57  auto p = dependencies_.lookupForAdd(propSlot);
     58  if (!p) {
     59    if (!dependencies_.add(p, propSlot, jit::DependentIonScriptSet())) {
     60      return false;
     61    }
     62  }
     63  return p->value().addToSet(ionScript);
     64 }
     65 
     66 void ObjectFuse::invalidateDependentIonScriptsForProperty(JSContext* cx,
     67                                                          PropertyInfo prop,
     68                                                          const char* reason) {
     69  if (auto p = dependencies_.lookup(prop.slot())) {
     70    p->value().invalidateAndClear(cx, reason);
     71    dependencies_.remove(p);
     72  }
     73 }
     74 
     75 void ObjectFuse::invalidateAllDependentIonScripts(JSContext* cx,
     76                                                  const char* reason) {
     77  for (auto r = dependencies_.all(); !r.empty(); r.popFront()) {
     78    r.front().value().invalidateAndClear(cx, reason);
     79  }
     80  dependencies_.clear();
     81 }
     82 
     83 bool ObjectFuse::markPropertyConstant(PropertyInfo prop) {
     84  MOZ_ASSERT(getPropertyState(prop) == PropertyState::Untracked);
     85  uint32_t index = prop.slot() / NumPropsPerWord;
     86  if (!ensurePropertyStateLength(index + 1)) {
     87    return false;
     88  }
     89  setPropertyState(prop, PropertyState::Constant);
     90  return true;
     91 }
     92 
     93 bool ObjectFuse::tryOptimizeConstantProperty(PropertyInfo prop) {
     94  if (MOZ_UNLIKELY(!generation_.isValid())) {
     95    return false;
     96  }
     97  PropertyState state = getPropertyState(prop);
     98  switch (state) {
     99    case PropertyState::Untracked:
    100      if (!markPropertyConstant(prop)) {
    101        return false;
    102      }
    103      MOZ_ASSERT(isConstantProperty(prop));
    104      return true;
    105    case PropertyState::Constant:
    106      return true;
    107    case PropertyState::NotConstant:
    108      return false;
    109  }
    110  MOZ_CRASH("Unreachable");
    111 }
    112 
    113 void ObjectFuse::handlePropertyValueChange(JSContext* cx, PropertyInfo prop) {
    114  // Custom data properties aren't optimized with object fuses.
    115  if (!prop.hasSlot()) {
    116    return;
    117  }
    118 
    119  PropertyState state = getPropertyState(prop);
    120  switch (state) {
    121    case PropertyState::Untracked:
    122      // Mark the property as Constant. IC code for SetProp operations relies on
    123      // properties getting marked NotConstant after a few sets, because we can
    124      // only optimize stores to NotConstant properties. We can ignore OOM here.
    125      (void)markPropertyConstant(prop);
    126      break;
    127    case PropertyState::Constant:
    128      invalidatedConstantProperty_ = 1;
    129      setPropertyState(prop, PropertyState::NotConstant);
    130      invalidateDependentIonScriptsForProperty(cx, prop,
    131                                               "changed constant property");
    132      break;
    133    case PropertyState::NotConstant:
    134      break;
    135  }
    136 }
    137 
    138 void ObjectFuse::handlePropertyRemove(JSContext* cx, PropertyInfo prop,
    139                                      bool* wasTrackedProp) {
    140  if (!prop.hasSlot() || isUntrackedProperty(prop)) {
    141    MOZ_ASSERT(!*wasTrackedProp);
    142    return;
    143  }
    144 
    145  *wasTrackedProp = true;
    146  bumpGeneration();
    147  invalidateDependentIonScriptsForProperty(cx, prop, "removed property");
    148 
    149  // Ensure a new property with this slot number will have the correct initial
    150  // state.
    151  setPropertyState(prop, PropertyState::Untracked);
    152 }
    153 
    154 void ObjectFuse::handleTeleportingShadowedProperty(JSContext* cx,
    155                                                   PropertyInfo prop) {
    156  if (!prop.hasSlot() || isUntrackedProperty(prop)) {
    157    return;
    158  }
    159  bumpGeneration();
    160  invalidateDependentIonScriptsForProperty(cx, prop,
    161                                           "teleporting shadowed property");
    162 }
    163 
    164 void ObjectFuse::handleTeleportingProtoMutation(JSContext* cx) {
    165  bumpGeneration();
    166  invalidateAllDependentIonScripts(cx, "proto mutation");
    167 }
    168 
    169 void ObjectFuse::handleObjectSwap(JSContext* cx) {
    170  bumpGeneration();
    171 
    172  // Reset state for all properties.
    173  propertyStateLength_ = 0;
    174  propertyStateBits_.reset();
    175 
    176  invalidateAllDependentIonScripts(cx, "object swap");
    177 }
    178 
    179 void ObjectFuse::handleShadowedGlobalProperty(JSContext* cx,
    180                                              PropertyInfo prop) {
    181  if (isUntrackedProperty(prop)) {
    182    return;
    183  }
    184  bumpGeneration();
    185  invalidateDependentIonScriptsForProperty(cx, prop,
    186                                           "shadowed global property");
    187 }
    188 
    189 const char* ObjectFuse::getPropertyStateString(PropertyInfo prop) const {
    190  PropertyState state = getPropertyState(prop);
    191  switch (state) {
    192    case PropertyState::Untracked:
    193      return "Untracked";
    194    case PropertyState::Constant:
    195      return "Constant";
    196    case PropertyState::NotConstant:
    197      return "NotConstant";
    198  }
    199  MOZ_CRASH("Unreachable");
    200 }
    201 
    202 size_t ObjectFuse::sizeOfIncludingThis(
    203    mozilla::MallocSizeOf mallocSizeOf) const {
    204  size_t result = mallocSizeOf(this);
    205  if (propertyStateBits_) {
    206    result += mallocSizeOf(propertyStateBits_.get());
    207  }
    208  result += dependencies_.shallowSizeOfExcludingThis(mallocSizeOf);
    209  for (auto r = dependencies_.all(); !r.empty(); r.popFront()) {
    210    result += r.front().value().sizeOfExcludingThis(mallocSizeOf);
    211  }
    212  return result;
    213 }
    214 
    215 ObjectFuse* ObjectFuseMap::getOrCreate(JSContext* cx, NativeObject* obj) {
    216  MOZ_ASSERT(obj->hasObjectFuse());
    217  auto p = objectFuses_.lookupForAdd(obj);
    218  if (!p) {
    219    auto fuse = MakeUnique<ObjectFuse>();
    220    if (!fuse || !objectFuses_.add(p, obj, std::move(fuse))) {
    221      ReportOutOfMemory(cx);
    222      return nullptr;
    223    }
    224  }
    225  return p->value().get();
    226 }
    227 
    228 ObjectFuse* ObjectFuseMap::get(NativeObject* obj) {
    229  MOZ_ASSERT(obj->hasObjectFuse());
    230  auto p = objectFuses_.lookup(obj);
    231  return p ? p->value().get() : nullptr;
    232 }
    233 
    234 size_t ObjectFuseMap::sizeOfExcludingThis(
    235    mozilla::MallocSizeOf mallocSizeOf) const {
    236  size_t result = objectFuses_.sizeOfExcludingThis(mallocSizeOf);
    237  for (auto r = objectFuses_.all(); !r.empty(); r.popFront()) {
    238    result += r.front().value()->sizeOfIncludingThis(mallocSizeOf);
    239  }
    240  return result;
    241 }