summaryrefslogtreecommitdiffstats
path: root/chromium/docs/website/site/developers/blink-gc-plugin-errors/index.md
blob: 37c608fdc2be04a877be8225dd7d0571266c6cae (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
---
breadcrumbs:
- - /developers
  - For Developers
page_name: blink-gc-plugin-errors
title: Blink GC Plugin Errors
---

## Introduction

With the addition of the new Blink garbage collection infrastructure we have
created a clang compiler plugin to statically check various conditions are met.
This page documents what conditions are checked, which error a failed condition
will raise and how the error might be fixed.

All diagnostics produced by the checker are prefixed with \[blink-gc\].

## Tracing Errors

All members of a class must be traced in order to ensure that the GC system does
not reclaim their storage space. Thus, if a class contains any fields that need
to be traced (ie, the field is either a GC managed object or it also contains
fields that need to be traced), then the class must implement a trace method to
do so.

If you get the error:

*   Class 'Foo' requires a trace method because it contains fields that
            require tracing.

then your class/struct contains fields that need to be traced and you must
define a trace method doing so. The plugin will provide a note describing which
fields that need to be traced. A problematic class might look like:

class Foo : public GarbageCollected<Foo> {

public:

Baz\* getTheBaz() { return m_baz; }

private:

Member<Baz> m_baz;

};

This is fixed by defining a trace method and tracing the field m_baz:

class Foo : public GarbageCollected<Foo> {

public:

Baz\* getTheBaz() { return m_baz; }

virtual void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};

If you get the error:

*   Class 'Foo' overrides non-virtual trace of base class 'Bar'.

then your class/struct has overridden a non-virtual trace method of a base class
and the base class should either be virtual or the hierarchy needs to implement
manual trace dispatching (see the section on Dispatch Errors below).

If you get the errors:

*   Base class 'Foo' of derived class 'Bar' requires tracing.
*   Class 'Bar' has untraced fields that require tracing.

then your class/struct has a trace method, but it does not trace all of the
required parts. The base class tracing ensures that fields declared in base
classes are traced. The plugin will provide a note describing which fields that
need to be traced. A problematic class might look like:

class Bar : public Foo {

public:

Baz\* getTheOtherBaz() { return m_otherBaz; }

void trace(Visitor\* visitor) { }

private:

Member<Baz> m_otherBaz;

};

This is fixed by tracing Foo and m_otherBaz:

class Bar : public Foo {

public:

Baz\* getTheOtherBaz() { return m_otherBaz; }

void trace(Visitor\* visitor)

{

visitor->trace(m_otherBaz);

Foo::trace(visitor);

}

private:

Member<Baz> m_otherBaz;

};

## Field Errors

In order to ensure proper tracing, each field of a class is checked against some
correctness requirements described below.

If you get the errors:

*   Class 'Foo' contains invalid fields.
*   Class 'Foo' contains GC root in field 'm_bar'.

then your class/struct contains problematic fields. If a field points to a GC
allocated object then it should be in a Member, so if the field is currently a
raw pointer, RefPtr or OwnPtr, it should be changed to a Member or WeakMember
and properly traced. A problematic class might look like:

class Foo : public GarbageCollected<Foo> {

public:

Baz\* getTheBaz() { return m_baz; }

private:

OwnPtr<Baz> m_baz;

};

Here Baz is a GC allocated type and the issue is fixed by replacing the OwnPtr
by Member and tracing the field:

class Foo : public GarbageCollected<Foo> {

public:

Baz\* getTheBaz() { return m_baz; }

virtual void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};

Another type of invalid field is the embedding of or a pointer to a
stack-allocated object from within A GC allocated object. This is simply not
permitted and the containing class must also be stack allocated or the
stack-allocated object must become GC allocated.

If a field inside a GC allocated object either defines a GC root (ie, Persistent
or PersistentHeapXXX collection) or embeds a GC root via a part object, then we
likely have a memory leak. In this case the Persistent should be replaced by a
properly traced Member. A problematic case might look like:

class Part {

DISALLOW_ALLOCATION();

public:

Baz\* getTheBaz() { return m_baz; }

private:

Persistent<Baz> m_baz;

};

class Foo : public GarbageCollected<Foo> {

public:

Baz\* getTheBaz() { return m_part.getTheBaz(); }

private:

Part m_part;

};

Here Part is used as a part object of Foo and it contains a Persistent pointer
to Baz. To fix the issue the Persistent should be replaced by Member and Part
should be traced:

class Part {

DISALLOW_ALLOCATION();

public:

Baz\* getTheBaz() { return m_baz; }

void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};

class Foo : public GarbageCollected<Foo> {

public:

Baz\* getTheBaz() { return m_part.getTheBaz(); }

virtual void trace(Visitor\* visitor) { visitor->trace(m_part); }

private:

Part m_part;

};

## Finalization Errors

When an object is allocated on the GC managed heap it might need finalization
support. Objects that derive a GC base with finalization support will be
finalized on the first GC where they have become unreachable. Thus the
finalization time (ie, time of destruction) is not known in advance. By default
finalization of an object will call the objects destructor. If a class does not
have finalization support its destructor will never be called. The existence of
destructors that are never called can hide subtle bugs and so we check agains
this with the GC plugin. The basic rule is that a class with a "non-trivial
destructor" must have finalization support. A class has a trivial destructor if
and only if the destructor is the default generated destructor and all of the
class bases and members have trivial destructors.

If you get the error:

*   Class 'Foo' requires finalization.

then your class/struct has a "non-trivial finalizer" and therefore needs to
inherit from a finalized GC base class, such as, GarbageCollectedFinalized. The
plugin will try to emit notes about which bases, members and destructors are
causing the class to have a non-trivial finalizer. For example:

class Foo : public GarbageCollected<Foo> {

public:

~Foo();

};

will issue a note about a "user-declared destructor". The destructor can then
either be removed or finalization support can be added to the class:

class Foo : public GarbageCollectedFinalized<Foo> {

public:

~Foo();

};

If you get the error:

*   Finalizer '~Foo' accesses potentially finalized field 'm_baz'.

then your finalizer is accessing a field of Foo that might be finalized in the
same GC round. In other words, the object pointed to by m_baz might already be
reclaimed by the GC. For example:

class Foo : public GarbageCollected<Foo> {

public:

~Foo { m_baz->doSomeCleanup(); }

void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};

needs to be changed to perform the cleanup of baz in some other way dependent on
the concrete situation. Maybe the cleanup code can be moved into the
finalization of Baz.

## Dispatch Errors

Some places in the Blink code base avoid introducing a vtable for a class
hierarchy, such as the CSSValue class hierarchy. In such cases the programmer
needs to define the dispatch methods manually.

If you get the errors:

*   Class 'Foo' is missing manual trace dispatch.
*   Class 'Foo' is missing manual finalization dispatch.

your class needs to define a trace method and/or a
finalizeGarbageCollectedObject method to perform the manual dispatch.

If you get the errors:

*   Class 'Foo' contains or inherits virtual methods but implements
            manual dispatching.

your class has implemented manual dispatch but might be able to simply have a
virtual trace and destructor.

If you get the errors:

*   Missing dispatch to class 'Bar' in manual trace dispatch.
*   Missing dispatch to class 'Bar' in manual finalize dispatch.

then the derived class 'Bar' is not dispatched to from the manually implemented
dispatch.

## Derived Class Errors

If you get the error:

*   Class 'Foo' must derive its GC base in the left-most position.

then the class Foo inherits from a GC base, but does so incorrectly. To ensure
that the vtable entry and GC meta data of a class is mapped to a reliable
location in memory, we require that the GC base is always inherited in the
left-most position in the super-class declarations. For example:

class Foo : public Bar, public GarbageCollected<Foo> {

public:

void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};

needs to swap the position of Bar and the GC base:

class Foo : public GarbageCollected<Foo>, public Bar {

public:

void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};

The same is true if the GC base is inherited from some other derived class, say
Baz, then the super-class declaration of Baz must be in the left-most position.

If you get the error:

*   Left-most base class 'Bar' of derived class 'Foo' must be
            polymorphic.
*   Left-most base class 'Bar' of derived class 'Foo' must define a
            virtual trace method.

then the base class 'Bar' needs to be polymorphic and if 'Foo' has a virtual
trace method then it must be declared virtual in 'Bar'. The requirement is to
ensure that the GC have a consistent view of a vtable/virtual-trace even if a GC
happens while an object is only partially constructed. In both cases, the
typical fix will simply be to define a virtual trace in 'Bar', override it in
'Foo' and trace the super class there. For example:

class Bar : public GarbageCollected<Bar> {

public:

Bar(); // could potentially trigger a GC.

};

class Foo : public Bar {

public:

virtual void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};

needs to define trace in 'Bar' and trace it in 'Foo':

class Bar : public GarbageCollected<Bar> {

public:

Bar();

virtual void trace(Visitor\*) { }

};

class Foo : public Bar {

public:

void trace(Visitor\* visitor)

{

visitor->trace(m_baz);

Bar::trace(visitor);

}

private:

Member<Baz> m_baz;

};

If you get the error:

*   Stack-allocated class 'Foo' derives class 'Bar' which is not stack
            allocated

then either class Foo can not be annotated as "stack allocated", or class Bar
must be annotated "stack allocated" and used as such. A stack allocated class
cannot be new'ed, ie, allocated on the managed heap or using any other kind of
allocator. For example:

class Bar {

public:

void someMethod();

};

class Foo : public Bar {

STACK_ALLOCATED();

private:

Member<Baz> m_baz;

};

needs to either make Bar stack allocated:

class Bar {

STACK_ALLOCATED();

public:

void someMethod();

};

class Foo : public Bar {

private:

Member<Baz> m_baz;

};

Notice that the stack allocated annotation is inherited and that a stack
allocated object does not need to define a trace method: its pointers are found
conservatively on the stack.

Alternatively, Foo can be made GC allocated:

class Bar {

public:

void someMethod();

};

class Foo : public GarbageCollected<Foo>, public Bar {

public:

void trace(Visitor\* visitor) { visitor->trace(m_baz); }

private:

Member<Baz> m_baz;

};