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 |