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