tor-browser

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

Watchtower.h (7264B)


      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 vm_Watchtower_h
      8 #define vm_Watchtower_h
      9 
     10 #include "js/TypeDecls.h"
     11 #include "vm/NativeObject.h"
     12 
     13 namespace js {
     14 
     15 enum class SetSlotOptimizable { Yes, No, NotYet };
     16 
     17 // [SMDOC] Watchtower
     18 //
     19 // Watchtower is a framework to hook into changes to certain objects. This gives
     20 // us the ability to, for instance, invalidate caches or purge Warp code on
     21 // object layout changes.
     22 //
     23 // Watchtower is only used for objects with certain ObjectFlags set on the
     24 // Shape. This minimizes performance overhead for most objects.
     25 //
     26 // We currently use Watchtower for:
     27 //
     28 // - Invalidating the shape teleporting optimization. See the "Shape Teleporting
     29 //   Optimization" SMDOC comment in CacheIR.cpp.
     30 //
     31 // - Invalidating the MegamorphicCache, a property lookup cache for megamorphic
     32 //   property accesses. See the SMDOC comment in vm/Caches.h.
     33 //
     34 // There's also a testing mechanism that lets us write tests for Watchtower
     35 // hooks. See setWatchtowerCallback and addWatchtowerTarget defined in
     36 // TestingFunctions.cpp.
     37 class Watchtower {
     38  static bool watchPropertyAddSlow(JSContext* cx, Handle<NativeObject*> obj,
     39                                   HandleId id);
     40  static bool watchPropertyRemoveSlow(JSContext* cx, Handle<NativeObject*> obj,
     41                                      HandleId id, PropertyInfo propInfo,
     42                                      bool* wasTrackedObjectFuseProp);
     43  static bool watchPropertyFlagsChangeSlow(JSContext* cx,
     44                                           Handle<NativeObject*> obj,
     45                                           HandleId id, PropertyInfo propInfo,
     46                                           PropertyFlags newFlags);
     47  template <AllowGC allowGC>
     48  static void watchPropertyValueChangeSlow(
     49      JSContext* cx,
     50      typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
     51      typename MaybeRooted<PropertyKey, allowGC>::HandleType id,
     52      typename MaybeRooted<Value, allowGC>::HandleType value,
     53      PropertyInfo propInfo);
     54  static bool watchFreezeOrSealSlow(JSContext* cx, Handle<NativeObject*> obj,
     55                                    IntegrityLevel level);
     56  static bool watchProtoChangeSlow(JSContext* cx, HandleObject obj);
     57  static bool watchObjectSwapSlow(JSContext* cx, HandleObject a,
     58                                  HandleObject b);
     59  static SetSlotOptimizable canOptimizeSetSlotSlow(JSContext* cx,
     60                                                   NativeObject* obj,
     61                                                   PropertyInfo prop);
     62 
     63 public:
     64  static bool watchesPropertyAdd(NativeObject* obj) {
     65    return obj->hasAnyFlag(
     66        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::UseWatchtowerTestingLog});
     67  }
     68  static bool watchesPropertyRemove(NativeObject* obj) {
     69    return obj->hasAnyFlag(
     70        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::GenerationCountedGlobal,
     71         ObjectFlag::UseWatchtowerTestingLog, ObjectFlag::HasRealmFuseProperty,
     72         ObjectFlag::HasObjectFuse});
     73  }
     74  static bool watchesPropertyFlagsChange(NativeObject* obj) {
     75    return obj->hasAnyFlag({ObjectFlag::IsUsedAsPrototype,
     76                            ObjectFlag::GenerationCountedGlobal,
     77                            ObjectFlag::UseWatchtowerTestingLog});
     78  }
     79  static bool watchesPropertyValueChange(NativeObject* obj) {
     80    return obj->hasAnyFlag({ObjectFlag::HasRealmFuseProperty,
     81                            ObjectFlag::UseWatchtowerTestingLog,
     82                            ObjectFlag::HasObjectFuse});
     83  }
     84  static bool watchesFreezeOrSeal(NativeObject* obj) {
     85    return obj->hasAnyFlag(
     86        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::UseWatchtowerTestingLog});
     87  }
     88  static bool watchesProtoChange(JSObject* obj) {
     89    return obj->hasAnyFlag(
     90        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::UseWatchtowerTestingLog});
     91  }
     92  static bool watchesObjectSwap(JSObject* a, JSObject* b) {
     93    auto watches = [](JSObject* obj) {
     94      return obj->hasAnyFlag({ObjectFlag::IsUsedAsPrototype,
     95                              ObjectFlag::UseWatchtowerTestingLog,
     96                              ObjectFlag::HasObjectFuse});
     97    };
     98    return watches(a) || watches(b);
     99  }
    100  static SetSlotOptimizable canOptimizeSetSlot(JSContext* cx, NativeObject* obj,
    101                                               PropertyInfo prop) {
    102    if (obj->hasAnyFlag({ObjectFlag::HasRealmFuseProperty,
    103                         ObjectFlag::UseWatchtowerTestingLog})) {
    104      return SetSlotOptimizable::No;
    105    }
    106    if (!obj->hasObjectFuse()) {
    107      return SetSlotOptimizable::Yes;
    108    }
    109    return canOptimizeSetSlotSlow(cx, obj, prop);
    110  }
    111 
    112  static bool watchPropertyAdd(JSContext* cx, Handle<NativeObject*> obj,
    113                               HandleId id) {
    114    if (MOZ_LIKELY(!watchesPropertyAdd(obj))) {
    115      return true;
    116    }
    117    return watchPropertyAddSlow(cx, obj, id);
    118  }
    119  static bool watchPropertyRemove(JSContext* cx, Handle<NativeObject*> obj,
    120                                  HandleId id, PropertyInfo propInfo,
    121                                  bool* wasTrackedObjectFuseProp) {
    122    MOZ_ASSERT(!*wasTrackedObjectFuseProp);
    123    if (MOZ_LIKELY(!watchesPropertyRemove(obj))) {
    124      return true;
    125    }
    126    return watchPropertyRemoveSlow(cx, obj, id, propInfo,
    127                                   wasTrackedObjectFuseProp);
    128  }
    129  static bool watchPropertyFlagsChange(JSContext* cx, Handle<NativeObject*> obj,
    130                                       HandleId id, PropertyInfo propInfo,
    131                                       PropertyFlags newFlags) {
    132    if (MOZ_LIKELY(!watchesPropertyFlagsChange(obj))) {
    133      return true;
    134    }
    135    return watchPropertyFlagsChangeSlow(cx, obj, id, propInfo, newFlags);
    136  }
    137 
    138  // Note: We can only watch property value changes for regular object slots
    139  // with an id, not reserved slots.
    140  template <AllowGC allowGC>
    141  static void watchPropertyValueChange(
    142      JSContext* cx,
    143      typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    144      typename MaybeRooted<PropertyKey, allowGC>::HandleType id,
    145      typename MaybeRooted<Value, allowGC>::HandleType value,
    146      PropertyInfo propInfo) {
    147    if (MOZ_LIKELY(!watchesPropertyValueChange(obj))) {
    148      return;
    149    }
    150    watchPropertyValueChangeSlow<allowGC>(cx, obj, id, value, propInfo);
    151  }
    152  static bool watchFreezeOrSeal(JSContext* cx, Handle<NativeObject*> obj,
    153                                IntegrityLevel level) {
    154    if (MOZ_LIKELY(!watchesFreezeOrSeal(obj))) {
    155      return true;
    156    }
    157    return watchFreezeOrSealSlow(cx, obj, level);
    158  }
    159  static bool watchProtoChange(JSContext* cx, HandleObject obj) {
    160    if (MOZ_LIKELY(!watchesProtoChange(obj))) {
    161      return true;
    162    }
    163    return watchProtoChangeSlow(cx, obj);
    164  }
    165 
    166  static bool watchObjectSwap(JSContext* cx, HandleObject a, HandleObject b) {
    167    if (MOZ_LIKELY(!watchesObjectSwap(a, b))) {
    168      return true;
    169    }
    170    return watchObjectSwapSlow(cx, a, b);
    171  }
    172 };
    173 
    174 }  // namespace js
    175 
    176 #endif /* vm_Watchtower_h */