blob: 0d0ad337dac607b07d68e923499de166fbdbb1be [file] [log] [blame] [view]
# Wrapper Tracing Reference
This document describes wrapper tracing and how its API is supposed to be used.
[TOC]
## Quickstart guide
Wrapper tracing is used to represent reachability across V8 and Blink. The
following checklist highlights the modifications needed to make a class
participate in wrapper tracing.
1. Make sure that objects participating in tracing either inherit from
`ScriptWrappable` (if they can reference wrappers) or `TraceWrapperBase`
(transitively holding wrappers alive).
2. Use `TraceWrapperMember<T>` to annotate fields that need to be followed to
find other wrappers that this object should keep alive.
3. Use `TraceWrapperV8Reference<T>` to annotate references to V8 that this
object should keep alive.
4. Declare a `virtual void TraceWrappers(const ScriptWrappableVisitor*) const`
method to trace other wrappers.
5. Define the method and trace all fields that received a wrapper tracing type
in (1) and (2) using `visitor->TraceWrappers(<field_>)` in the body.
The following example illustrates these steps:
```c++
#include "platform/bindings/ScriptWrappable.h"
#include "platform/bindings/TraceWrapperMember.h"
#include "platform/bindings/TraceWrapperV8Reference.h"
class SomeDOMObject : public ScriptWrappable { // (1)
DEFINE_WRAPPERTYPEINFO();
public:
virtual void TraceWrappers(
const ScriptWrappableVisitor*) const; // (4)
private:
TraceWrapperMember<OtherWrappable> other_wrappable_; // (2)
Member<NonWrappable> non_wrappable_;
TraceWrapperV8Reference<v8::Value> v8object_; // (3)
// ...
};
void SomeDOMObject::TraceWrappers(
const ScriptWrappableVisitor* visitor) const { // (5)
visitor->TraceWrappers(other_wrappable_); // (5)
visitor->TraceWrappers(v8object_); // (5)
}
```
For more in-depth information and how to deal with corner cases continue on reading.
## Background
Blink and V8 need to cooperate to collect JavaScript *wrappers*. Each V8
*wrapper* object (*W*) in JavaScript is assigned a C++ *DOM object* (*D*) in
Blink. A single C++ *DOM object* can hold onto one or many *wrapper* objects.
During a garbage collection initiated from JavaScript, a *wrapper* then keeps
the C++ *DOM object* and all its transitive dependencies, including other
*wrappers*, alive, effectively tracing paths like
*W<sub>x</sub> -> D<sub>1</sub> -> -> D<sub>n</sub> -> W<sub>y</sub>*.
Previously, this relationship was expressed using so-called object groups,
effectively assigning all C++ *DOM objects* in a given DOM tree the same group.
The group would only die if all objects belonging to the same group die. A brief
introduction on the limitations on this approach can be found in
[this slide deck][object-grouping-slides].
Wrapper tracing solves this problem by determining liveness based on
reachability by tracing through the C++ *DOM objects*. The rest of this document
describes how the API is used to keep JavaScript wrapper objects alive.
Note that *wrappables* have to be *on-heap objects* and thus all
[Oilpan-related rules][oilpan-docs] apply.
[object-grouping-slides]: https://docs.google.com/presentation/d/1I6leiRm0ysSTqy7QWh33Gfp7_y4ngygyM2tDAqdF0fI/
[oilpan-docs]: https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/platform/heap/BlinkGCAPIReference.md
## Basic usage
The annotations that are required can be found in the following header files.
Pick the header file depending on what types are needed.
```c++
#include "platform/bindings/ScriptWrappable.h"
#include "platform/bindings/TraceWrapperV8Reference.h"
```
The following example will guide through the modifications that are needed to
adjust a given class `SomeDOMObject` to participate in wrapper tracing.
```c++
class SomeDOMObject : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
// ...
private:
Member<OtherWrappable /* extending ScriptWrappable */> other_wrappable_;
Member<NonWrappable> non_wrappable_;
};
```
In this scenario `SomeDOMObject` is the object that is wrapped by an object on
the JavaScript side. The next step is to identify the paths that lead to other
wrappables. In this case, only `other_wrappable_` needs to be traced to find
other *wrappers* in V8.
```c++
class SomeDOMObject : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
private:
Member<OtherWrappable> other_wrappable_;
Member<NonWrappable> non_wrappable_;
};
void SomeDOMObject::TraceWrappers(
const ScriptWrappableVisitor* visitor) const {
visitor->TraceWrappers(other_wrappable_);
}
```
Blink and V8 implement *incremental* wrapper tracing, which means that marking
can be interleaved with JavaScript or even DOM operations. This poses a
challenge, because already marked objects will not be considered again if they
are reached through some other path.
For example, consider an object `A` that has already been marked and a write
to a field `A.x` setting `x` to an unmarked object `Y`. Since `A.x` is
the only reference keeping `Y`, and `A` has already been marked, the garbage
collector will not find `Y` and reclaim it.
To overcome this problem we require all writes of interesting objects, i.e.,
writes to traced fields, to go through a write barrier.
The write barrier will check for the problem case above and make sure
`Y` will be marked. In order to automatically issue a write barrier
`other_wrappable_` needs `TraceWrapperMember` type.
```c++
class SomeDOMObject : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
private:
TraceWrapperMember<OtherWrappable> other_wrappable_;
Member<NonWrappable> non_wrappable_;
};
void SomeDOMObject::TraceWrappers(
const ScriptWrappableVisitor* visitor) const {
visitor->TraceWrappers(other_wrappable_);
}
```
`TraceWrapperMember` makes sure that any write to `other_wrappable_` will
consider doing a write barrier. Using the proper type, the write barrier is
correct by construction, i.e., it will never be missed.
## Heap collections
The proper type usage for collections, e.g. `HeapVector` looks like the
following.
```c++
class SomeDOMObject : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
// ...
void AppendNewValue(OtherWrappable* newValue);
virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
private:
HeapVector<TraceWrapperMember<OtherWrappable>> other_wrappables_;
};
void SomeDOMObject::TraceWrappers(
const ScriptWrappableVisitor* visitor) const {
for (auto other : other_wrappables_)
visitor->TraceWrappers(other);
}
```
Note that this is different to Oilpan which can just trace the whole collection.
`TraceWrapperMember` can be constructed in place, so using `append` and
friends will work out of the box, e.g.
```c++
void SomeDOMObject::AppendNewValue(OtherWrappable* newValue) {
other_wrappables_.append(newValue);
}
```
The compiler will throw an error for each omitted `TraceWrapperMember`
construction.
### Swapping `HeapVector` containing `TraceWrapperMember` and `Member`
It is possible to swap two `HeapVectors` containing `TraceWrapperMember` and
`Member` by using `blink::swap`. The underlying swap will avoid copies and
write barriers if possible.
```c++
// Swap two wrapper traced heap vectors.
HeapVector<TraceWrapperMember<Wrappable>> a;
HeapVector<TraceWrapperMember<Wrappable>> b;
blink::swap(a, b);
// Swap in a non-traced heap vector into a wrapper traced one.
HeapVector<TraceWrapperMember<Wrappable>> c;
HeapVector<Member<Wrappable>> temporary;
blink::swap(c, temporary);
```
## Tracing through non-`ScriptWrappable` types
Sometimes it is necessary to trace through types that do not inherit from
`ScriptWrappable`. For example, consider the object graph
`A -> B -> C` where both `A` and `C` are `ScriptWrappable`s that
need to be traced.
In this case, the same rules as with `ScriptWrappables` apply, except for the
difference that these classes need to inherit from `TraceWrapperBase`.
### Memory-footprint critical uses
In the case we cannot afford inheriting from `TraceWrapperBase`, which will
add a vtable pointer for tracing wrappers, use
`DEFINE_TRAIT_FOR_TRACE_WRAPPERS(ClassName)` after defining
`ClassName` to define the proper tracing specializations.
## Explicit write barriers
Sometimes it is necessary to stick with the regular types and issue the write
barriers explicitly. In this case, tracing needs to be adjusted to tell the
system that all barriers will be done manually.
```c++
class ManualWrappable : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
void setNew(OtherWrappable* newValue) {
other_wrappable_ = newValue;
SriptWrappableVisitor::WriteBarrier(other_wrappable_);
}
virtual void TraceWrappers(const ScriptWrappableVisitor*) const;
private:
Member<OtherWrappable>> other_wrappable_;
};
void ManualWrappable::TraceWrappers(
const ScriptWrappableVisitor* visitor) const {
visitor->TraceWrappersWithManualWriteBarrier(other_wrappable_);
}
```