| OLD | NEW |
| (Empty) |
| 1 # Wrapper Tracing Reference | |
| 2 | |
| 3 This document describes wrapper tracing and how its API is supposed to be used. | |
| 4 | |
| 5 [TOC] | |
| 6 | |
| 7 ## Quickstart guide | |
| 8 | |
| 9 Wrapper tracing is used to represent reachability across V8 and Blink. The | |
| 10 following checklist highlights the modifications needed to make a class | |
| 11 participate in wrapper tracing. | |
| 12 | |
| 13 1. Make sure that objects participating in tracing either inherit from | |
| 14 ``ScriptWrappable`` (if they can reference wrappers) or ``TraceWrapperBase`` | |
| 15 (transitively holding wrappers alive). | |
| 16 2. Use ``TraceWrapperMember<T>`` to annotate fields that need to be followed to | |
| 17 find other wrappers that this object should keep alive. | |
| 18 3. Use ``TraceWrapperV8Reference<T>`` to annotate references to V8 that this | |
| 19 object should keep alive. | |
| 20 4. Declare a method to trace other wrappers using | |
| 21 ``DECLARE_VIRTUAL_TRACE_WRAPPERS()``. | |
| 22 5. Define the method using ``DEFINE_TRACE_WRAPPERS(ClassName)``. | |
| 23 6. Trace all fields that received a wrapper tracing type in (1) and (2) using | |
| 24 ``visitor->traceWrapers(<m_field>)`` in the body of ``DEFINE_TRACE_WRAPPERS``. | |
| 25 | |
| 26 The following example illustrates these steps: | |
| 27 | |
| 28 ```c++ | |
| 29 #include "bindings/core/v8/ScriptWrappable.h" | |
| 30 #include "bindings/core/v8/TraceWrapperMember.h" | |
| 31 #include "bindings/core/v8/TraceWrapperV8Reference.h" | |
| 32 | |
| 33 class SomeDOMObject : public ScriptWrappable { // (1) | |
| 34 public: | |
| 35 DECLARE_VIRTUAL_TRACE_WRAPPERS(); // (4) | |
| 36 | |
| 37 private: | |
| 38 TraceWrapperMember<OtherWrappable> m_otherWrappable; // (2) | |
| 39 Member<NonWrappable> m_nonWrappable; | |
| 40 TraceWrapperV8Reference<v8::Value> m_v8object; // (3) | |
| 41 // ... | |
| 42 }; | |
| 43 | |
| 44 DEFINE_TRACE_WRAPPERS(SomeDOMObject) { // (5) | |
| 45 visitor->traceWrappers(m_otherWrappable); // (6) | |
| 46 visitor->traceWrappers(m_v8object); // (6) | |
| 47 } | |
| 48 ``` | |
| 49 | |
| 50 For more in-depth information and how to deal with corner cases continue on read
ing. | |
| 51 | |
| 52 ## Background | |
| 53 | |
| 54 Blink and V8 need to cooperate to collect JavaScript *wrappers*. Each V8 | |
| 55 *wrapper* object (*W*) in JavaScript is assigned a C++ *DOM object* (*D*) in | |
| 56 Blink. A single C++ *DOM object* can hold onto one or many *wrapper* objects. | |
| 57 During a garbage collection initiated from JavaScript, a *wrapper* then keeps | |
| 58 the C++ *DOM object* and all its transitive dependencies, including other | |
| 59 *wrappers*, alive, effectively tracing paths like | |
| 60 *W<sub>x</sub> -> D<sub>1</sub> -> ⋯ -> D<sub>n</sub> -> W<sub>y</sub>*. | |
| 61 | |
| 62 Previously, this relationship was expressed using so-called object groups, | |
| 63 effectively assigning all C++ *DOM objects* in a given DOM tree the same group. | |
| 64 The group would only die if all objects belonging to the same group die. A brief | |
| 65 introduction on the limitations on this approach can be found in | |
| 66 [this slide deck][object-grouping-slides]. | |
| 67 | |
| 68 Wrapper tracing solves this problem by determining liveness based on | |
| 69 reachability by tracing through the C++ *DOM objects*. The rest of this document | |
| 70 describes how the API is used to keep JavaScript wrapper objects alive. | |
| 71 | |
| 72 Note that *wrappables* have to be *on-heap objects* and thus all | |
| 73 [Oilpan-related rules][oilpan-docs] apply. | |
| 74 | |
| 75 [object-grouping-slides]: https://docs.google.com/presentation/d/1I6leiRm0ysSTqy
7QWh33Gfp7_y4ngygyM2tDAqdF0fI/ | |
| 76 [oilpan-docs]: https://chromium.googlesource.com/chromium/src/+/master/third_par
ty/WebKit/Source/platform/heap/BlinkGCAPIReference.md | |
| 77 | |
| 78 ## Basic usage | |
| 79 | |
| 80 The annotations that are required can be found in the following header files. | |
| 81 Pick the header file depending on what types are needed. | |
| 82 | |
| 83 ```c++ | |
| 84 #include "bindings/core/v8/ScriptWrappable.h" | |
| 85 #include "bindings/core/v8/TraceWrapperBase.h" | |
| 86 #include "bindings/core/v8/TraceWrapperMember.h" | |
| 87 #include "bindings/core/v8/TraceWrapperV8Reference.h" | |
| 88 ``` | |
| 89 | |
| 90 The following example will guide through the modifications that are needed to | |
| 91 adjust a given class ``SomeDOMObject`` to participate in wrapper tracing. | |
| 92 | |
| 93 ```c++ | |
| 94 class SomeDOMObject : public ScriptWrappable { | |
| 95 // ... | |
| 96 private: | |
| 97 Member<OtherWrappable /* extending ScriptWrappable */> m_otherWrappable; | |
| 98 Member<NonWrappable> m_nonWrappable; | |
| 99 }; | |
| 100 ``` | |
| 101 | |
| 102 In this scenario ``SomeDOMObject`` is the object that is wrapped by an object on | |
| 103 the JavaScript side. The next step is to identify the paths that lead to other | |
| 104 wrappables. In this case, only ``m_otherWrappable`` needs to be traced to find | |
| 105 other *wrappers* in V8. | |
| 106 | |
| 107 As wrapper tracing only traces a subset of members residing on the Oilpan heap, | |
| 108 it requires its own tracing method. The macros are as follows: | |
| 109 | |
| 110 * ``DECLARE_VIRTUAL_TRACE_WRAPPERS();``: Use in the class declaration to declare | |
| 111 the needed wrapper tracing method. | |
| 112 * ``DEFINE_TRACE_WRAPPERS(ClassName)``: Use to define the implementation of | |
| 113 wrapper tracing. | |
| 114 | |
| 115 Many more convenience wrappers, like inline definitions, can be found in | |
| 116 ``platform/heap/WrapperVisitor.h``. | |
| 117 | |
| 118 ```c++ | |
| 119 class SomeDOMObject : public ScriptWrappable { | |
| 120 public: | |
| 121 DECLARE_VIRTUAL_TRACE_WRAPPERS(); | |
| 122 | |
| 123 private: | |
| 124 Member<OtherWrappable> m_otherWrappable; | |
| 125 Member<NonWrappable> m_nonWrappable; | |
| 126 }; | |
| 127 | |
| 128 DEFINE_TRACE_WRAPPERS(SomeDOMObject) { | |
| 129 visitor->traceWrappers(m_otherWrappable); | |
| 130 } | |
| 131 ``` | |
| 132 | |
| 133 | |
| 134 Blink and V8 implement *incremental* wrapper tracing, which means that marking | |
| 135 can be interleaved with JavaScript or even DOM operations. This poses a | |
| 136 challenge, because already marked objects will not be considered again if they | |
| 137 are reached through some other path. | |
| 138 | |
| 139 For example, consider an object ``A`` that has already been marked and a write | |
| 140 to a field ``A.x`` setting ``x`` to an unmarked object ``Y``. Since ``A.x`` is | |
| 141 the only reference keeping ``Y``, and ``A`` has already been marked, the garbage | |
| 142 collector will not find ``Y`` and reclaim it. | |
| 143 | |
| 144 To overcome this problem we require all writes of interesting objects, i.e., | |
| 145 writes to traced fields, to go through a write barrier. Repeat for simpler | |
| 146 readers: The write barrier will check for the problem case above and make sure | |
| 147 ``Y`` will be marked. In order to automatically issue a write barrier | |
| 148 ``m_otherWrappable`` needs ``TraceWrapperMember`` type. | |
| 149 | |
| 150 ```c++ | |
| 151 class SomeDOMObject : public ScriptWrappable { | |
| 152 public: | |
| 153 SomeDOMObject() : m_otherWrappable(this, nullptr) {} | |
| 154 DECLARE_VIRTUAL_TRACE_WRAPPERS(); | |
| 155 | |
| 156 private: | |
| 157 TraceWrapperMember<OtherWrappable> m_otherWrappable; | |
| 158 Member<NonWrappable> m_nonWrappable; | |
| 159 }; | |
| 160 | |
| 161 DEFINE_TRACE_WRAPPERS(SomeDOMObject) { | |
| 162 visitor->traceWrappers(m_otherWrappable); | |
| 163 } | |
| 164 ``` | |
| 165 | |
| 166 ``TraceWrapperMember`` makes sure that any write to ``m_otherWrappable`` will | |
| 167 consider doing a write barrier. Using the proper type, the write barrier is | |
| 168 correct by construction, i.e., it will never be missed. | |
| 169 | |
| 170 ## Heap collections | |
| 171 | |
| 172 The proper type usage for collections, e.g. ``HeapVector`` looks like the | |
| 173 following. | |
| 174 | |
| 175 ```c++ | |
| 176 class SomeDOMObject : public ScriptWrappable { | |
| 177 public: | |
| 178 // ... | |
| 179 void AppendNewValue(OtherWrappable* newValue); | |
| 180 DECLARE_VIRTUAL_TRACE_WRAPPERS(); | |
| 181 | |
| 182 private: | |
| 183 HeapVector<TraceWrapperMember<OtherWrappable>> m_otherWrappables; | |
| 184 }; | |
| 185 | |
| 186 DEFINE_TRACE_WRAPPERS(SomeDOMObject) { | |
| 187 for (auto other : m_otherWrappables) | |
| 188 visitor->traceWrappers(other); | |
| 189 } | |
| 190 ``` | |
| 191 | |
| 192 Note that this is different to Oilpan which can just trace the whole collection. | |
| 193 Whenever an element is added through ``append()`` the value needs to be | |
| 194 constructed using ``TraceWrapperMember``, e.g. | |
| 195 | |
| 196 ```c++ | |
| 197 void SomeDOMObject::AppendNewValue(OtherWrappable* newValue) { | |
| 198 m_otherWrappables.append(TraceWrapperMember(this, newValue)); | |
| 199 } | |
| 200 ``` | |
| 201 | |
| 202 The compiler will throw an error for each ommitted ``TraceWrapperMember`` | |
| 203 construction. | |
| 204 | |
| 205 ### Corner-case: Pre-sized containers | |
| 206 | |
| 207 Containers know how to construct an empty ``TraceWrapperMember`` slot. This | |
| 208 allows simple creation of containers at the cost of loosing the compile-time | |
| 209 check for assigning a raw value. | |
| 210 | |
| 211 ```c++ | |
| 212 class SomeDOMObject : public ScriptWrappable { | |
| 213 public: | |
| 214 SomeDOMObject() { | |
| 215 m_otherWrappables.resize(5); | |
| 216 } | |
| 217 | |
| 218 void writeAt(int i, OtherWrappable* other) { | |
| 219 m_otherWrappables[i] = other; | |
| 220 } | |
| 221 | |
| 222 DECLARE_VIRTUAL_TRACE_WRAPPERS(); | |
| 223 private: | |
| 224 HeapVector<TraceWrapperMember<OtherWrappable>> m_otherWrappables; | |
| 225 }; | |
| 226 | |
| 227 DEFINE_TRACE_WRAPPERS(SomeDOMObject) { | |
| 228 for (auto other : m_otherWrappables) | |
| 229 visitor->traceWrappers(other); | |
| 230 } | |
| 231 ``` | |
| 232 | |
| 233 In this example, the compiler will not warn you on | |
| 234 ``m_otherWrappables[i] = other``, but an assertion will throw at runtime as long | |
| 235 as there exists a test covering that branch. | |
| 236 | |
| 237 The correct assignment looks like | |
| 238 | |
| 239 ```c++ | |
| 240 m_otherWrappables[i] = TraceWrapperMember<OtherWrappable>(this, other); | |
| 241 ``` | |
| 242 | |
| 243 Note that the assertion that triggers when the annotation is not present does | |
| 244 not require wrapper tracing to be enabled. | |
| 245 | |
| 246 ## Tracing through non-``ScriptWrappable`` types | |
| 247 | |
| 248 Sometimes it is necessary to trace through types that do not inherit from | |
| 249 ``ScriptWrappable``. For example, consider the object graph | |
| 250 ``A -> B -> C`` where both ``A`` and ``C`` are ``ScriptWrappable``s that | |
| 251 need to be traced. | |
| 252 | |
| 253 In this case, the same rules as with ``ScriptWrappables`` apply, except for the | |
| 254 difference that these classes need to inherit from ``TraceWrapperBase``. | |
| 255 | |
| 256 ### Memory-footprint critical uses | |
| 257 | |
| 258 In the case we cannot afford inheriting from ``TraceWrapperBase``, which will | |
| 259 add a vtable pointer for tracing wrappers, use | |
| 260 ``DEFINE_TRAIT_FOR_TRACE_WRAPPERS(ClassName)`` after defining | |
| 261 ``ClassName`` to define the proper tracing specializations. | |
| 262 | |
| 263 ## Explicit write barriers | |
| 264 | |
| 265 Sometimes it is necessary to stick with the regular types and issue the write | |
| 266 barriers explicitly. For example, if memory footprint is really important and | |
| 267 it's not possible to use ``TraceWrapperMember`` which adds another pointer | |
| 268 field. In this case, tracing needs to be adjusted to tell the system that all | |
| 269 barriers will be done manually. | |
| 270 | |
| 271 ```c++ | |
| 272 class ManualWrappable : public ScriptWrappable { | |
| 273 public: | |
| 274 void setNew(OtherWrappable* newValue) { | |
| 275 m_otherWrappable = newValue; | |
| 276 SriptWrappableVisitor::writeBarrier(this, m_otherWrappable); | |
| 277 } | |
| 278 | |
| 279 DECLARE_VIRTUAL_TRACE_WRAPPERS(); | |
| 280 private: | |
| 281 Member<OtherWrappable>> m_otherWrappable; | |
| 282 }; | |
| 283 | |
| 284 DEFINE_TRACE_WRAPPERS(ManualWrappable) { | |
| 285 for (auto other : m_otherWrappables) | |
| 286 visitor->traceWrappersWithManualWriteBarrier(other); | |
| 287 } | |
| 288 ``` | |
| OLD | NEW |