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/Id.h"
11 #include "js/TypeDecls.h"
12 #include "vm/JSContext.h"
13 #include "vm/NativeObject.h"
14 
15 namespace js {
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, HandleNativeObject obj,
39                                    HandleId id);
40   static bool watchPropertyRemoveSlow(JSContext* cx, HandleNativeObject obj,
41                                       HandleId id);
42   static bool watchPropertyChangeSlow(JSContext* cx, HandleNativeObject obj,
43                                       HandleId id);
44   static bool watchFreezeOrSealSlow(JSContext* cx, HandleNativeObject obj);
45   static bool watchProtoChangeSlow(JSContext* cx, HandleObject obj);
46   static bool watchObjectSwapSlow(JSContext* cx, HandleObject a,
47                                   HandleObject b);
48 
49  public:
watchesPropertyAdd(NativeObject * obj)50   static bool watchesPropertyAdd(NativeObject* obj) {
51     return obj->hasAnyFlag({ObjectFlag::IsUsedAsPrototype,
52                             ObjectFlag::UseWatchtowerTestingCallback});
53   }
watchesPropertyRemove(NativeObject * obj)54   static bool watchesPropertyRemove(NativeObject* obj) {
55     return obj->hasAnyFlag({ObjectFlag::IsUsedAsPrototype,
56                             ObjectFlag::UseWatchtowerTestingCallback});
57   }
watchesPropertyChange(NativeObject * obj)58   static bool watchesPropertyChange(NativeObject* obj) {
59     return obj->hasAnyFlag({ObjectFlag::IsUsedAsPrototype,
60                             ObjectFlag::UseWatchtowerTestingCallback});
61   }
watchesFreezeOrSeal(NativeObject * obj)62   static bool watchesFreezeOrSeal(NativeObject* obj) {
63     return obj->hasAnyFlag({ObjectFlag::UseWatchtowerTestingCallback});
64   }
watchesProtoChange(JSObject * obj)65   static bool watchesProtoChange(JSObject* obj) {
66     return obj->hasAnyFlag({ObjectFlag::IsUsedAsPrototype,
67                             ObjectFlag::UseWatchtowerTestingCallback});
68   }
watchesObjectSwap(JSObject * a,JSObject * b)69   static bool watchesObjectSwap(JSObject* a, JSObject* b) {
70     auto watches = [](JSObject* obj) {
71       return obj->hasAnyFlag({ObjectFlag::IsUsedAsPrototype,
72                               ObjectFlag::UseWatchtowerTestingCallback});
73     };
74     return watches(a) || watches(b);
75   }
76 
watchPropertyAdd(JSContext * cx,HandleNativeObject obj,HandleId id)77   static bool watchPropertyAdd(JSContext* cx, HandleNativeObject obj,
78                                HandleId id) {
79     if (MOZ_LIKELY(!watchesPropertyAdd(obj))) {
80       return true;
81     }
82     return watchPropertyAddSlow(cx, obj, id);
83   }
watchPropertyRemove(JSContext * cx,HandleNativeObject obj,HandleId id)84   static bool watchPropertyRemove(JSContext* cx, HandleNativeObject obj,
85                                   HandleId id) {
86     if (MOZ_LIKELY(!watchesPropertyRemove(obj))) {
87       return true;
88     }
89     return watchPropertyRemoveSlow(cx, obj, id);
90   }
watchPropertyChange(JSContext * cx,HandleNativeObject obj,HandleId id)91   static bool watchPropertyChange(JSContext* cx, HandleNativeObject obj,
92                                   HandleId id) {
93     if (MOZ_LIKELY(!watchesPropertyChange(obj))) {
94       return true;
95     }
96     return watchPropertyChangeSlow(cx, obj, id);
97   }
watchFreezeOrSeal(JSContext * cx,HandleNativeObject obj)98   static bool watchFreezeOrSeal(JSContext* cx, HandleNativeObject obj) {
99     if (MOZ_LIKELY(!watchesFreezeOrSeal(obj))) {
100       return true;
101     }
102     return watchFreezeOrSealSlow(cx, obj);
103   }
watchProtoChange(JSContext * cx,HandleObject obj)104   static bool watchProtoChange(JSContext* cx, HandleObject obj) {
105     if (MOZ_LIKELY(!watchesProtoChange(obj))) {
106       return true;
107     }
108     return watchProtoChangeSlow(cx, obj);
109   }
watchObjectSwap(JSContext * cx,HandleObject a,HandleObject b)110   static bool watchObjectSwap(JSContext* cx, HandleObject a, HandleObject b) {
111     if (MOZ_LIKELY(!watchesObjectSwap(a, b))) {
112       return true;
113     }
114     return watchObjectSwapSlow(cx, a, b);
115   }
116 };
117 
118 }  // namespace js
119 
120 #endif /* vm_Watchtower_h */
121