summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Watchtower.h
blob: ec944ce24202794da4ed6bd39b4db8c2ca04c8bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef vm_Watchtower_h
#define vm_Watchtower_h

#include "js/TypeDecls.h"
#include "vm/NativeObject.h"

namespace js {

// [SMDOC] Watchtower
//
// Watchtower is a framework to hook into changes to certain objects. This gives
// us the ability to, for instance, invalidate caches or purge Warp code on
// object layout changes.
//
// Watchtower is only used for objects with certain ObjectFlags set on the
// Shape. This minimizes performance overhead for most objects.
//
// We currently use Watchtower for:
//
// - Invalidating the shape teleporting optimization. See the "Shape Teleporting
//   Optimization" SMDOC comment in CacheIR.cpp.
//
// - Invalidating the MegamorphicCache, a property lookup cache for megamorphic
//   property accesses. See the SMDOC comment in vm/Caches.h.
//
// There's also a testing mechanism that lets us write tests for Watchtower
// hooks. See setWatchtowerCallback and addWatchtowerTarget defined in
// TestingFunctions.cpp.
class Watchtower {
  static bool watchPropertyAddSlow(JSContext* cx, Handle<NativeObject*> obj,
                                   HandleId id);
  static bool watchPropertyRemoveSlow(JSContext* cx, Handle<NativeObject*> obj,
                                      HandleId id);
  static bool watchPropertyChangeSlow(JSContext* cx, Handle<NativeObject*> obj,
                                      HandleId id, PropertyFlags flags);
  template <AllowGC allowGC>
  static bool watchPropertyModificationSlow(
      JSContext* cx,
      typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
      typename MaybeRooted<PropertyKey, allowGC>::HandleType id);
  static bool watchFreezeOrSealSlow(JSContext* cx, Handle<NativeObject*> obj);
  static bool watchProtoChangeSlow(JSContext* cx, HandleObject obj);
  static bool watchObjectSwapSlow(JSContext* cx, HandleObject a,
                                  HandleObject b);

 public:
  static bool watchesPropertyAdd(NativeObject* obj) {
    return obj->hasAnyFlag(
        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::UseWatchtowerTestingLog});
  }
  static bool watchesPropertyRemove(NativeObject* obj) {
    return obj->hasAnyFlag(
        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::GenerationCountedGlobal,
         ObjectFlag::UseWatchtowerTestingLog, ObjectFlag::HasFuseProperty});
  }
  static bool watchesPropertyChange(NativeObject* obj) {
    return obj->hasAnyFlag(
        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::GenerationCountedGlobal,
         ObjectFlag::HasFuseProperty, ObjectFlag::UseWatchtowerTestingLog});
  }
  static bool watchesPropertyModification(NativeObject* obj) {
    return obj->hasAnyFlag(
        {ObjectFlag::HasFuseProperty, ObjectFlag::UseWatchtowerTestingLog});
  }
  static bool watchesFreezeOrSeal(NativeObject* obj) {
    return obj->hasAnyFlag({ObjectFlag::UseWatchtowerTestingLog});
  }
  static bool watchesProtoChange(JSObject* obj) {
    return obj->hasAnyFlag(
        {ObjectFlag::IsUsedAsPrototype, ObjectFlag::UseWatchtowerTestingLog});
  }
  static bool watchesObjectSwap(JSObject* a, JSObject* b) {
    auto watches = [](JSObject* obj) {
      return obj->hasAnyFlag(
          {ObjectFlag::IsUsedAsPrototype, ObjectFlag::UseWatchtowerTestingLog});
    };
    return watches(a) || watches(b);
  }

  static bool watchPropertyAdd(JSContext* cx, Handle<NativeObject*> obj,
                               HandleId id) {
    if (MOZ_LIKELY(!watchesPropertyAdd(obj))) {
      return true;
    }
    return watchPropertyAddSlow(cx, obj, id);
  }
  static bool watchPropertyRemove(JSContext* cx, Handle<NativeObject*> obj,
                                  HandleId id) {
    if (MOZ_LIKELY(!watchesPropertyRemove(obj))) {
      return true;
    }
    return watchPropertyRemoveSlow(cx, obj, id);
  }
  static bool watchPropertyChange(JSContext* cx, Handle<NativeObject*> obj,
                                  HandleId id, PropertyFlags flags) {
    if (MOZ_LIKELY(!watchesPropertyChange(obj))) {
      return true;
    }
    return watchPropertyChangeSlow(cx, obj, id, flags);
  }

  // Note: We can only watch property modification for regular object slots
  // with an id, not reserved slots.
  template <AllowGC allowGC>
  static bool watchPropertyModification(
      JSContext* cx,
      typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
      typename MaybeRooted<PropertyKey, allowGC>::HandleType id) {
    if (MOZ_LIKELY(!watchesPropertyModification(obj))) {
      return true;
    }
    return watchPropertyModificationSlow<allowGC>(cx, obj, id);
  }
  static bool watchFreezeOrSeal(JSContext* cx, Handle<NativeObject*> obj) {
    if (MOZ_LIKELY(!watchesFreezeOrSeal(obj))) {
      return true;
    }
    return watchFreezeOrSealSlow(cx, obj);
  }
  static bool watchProtoChange(JSContext* cx, HandleObject obj) {
    if (MOZ_LIKELY(!watchesProtoChange(obj))) {
      return true;
    }
    return watchProtoChangeSlow(cx, obj);
  }

  static bool watchObjectSwap(JSContext* cx, HandleObject a, HandleObject b) {
    if (MOZ_LIKELY(!watchesObjectSwap(a, b))) {
      return true;
    }
    return watchObjectSwapSlow(cx, a, b);
  }
};

}  // namespace js

#endif /* vm_Watchtower_h */