1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * Trace methods for all GC things, defined in a separate header to allow
11 * This also includes eager inline marking versions. Both paths must end up
12 * traversing equivalent subgraphs.
15 #ifndef gc_TraceMethods_inl_h
16 #define gc_TraceMethods_inl_h
18 #include "gc/GCMarker.h"
19 #include "gc/Tracer.h"
20 #include "jit/JitCode.h"
21 #include "vm/BigIntType.h"
22 #include "vm/GetterSetter.h"
23 #include "vm/GlobalObject.h"
24 #include "vm/JSScript.h"
25 #include "vm/PropMap.h"
29 #include "vm/StringType.h"
30 #include "vm/SymbolType.h"
31 #include "wasm/WasmJS.h"
33 inline void js::BaseScript::traceChildren(JSTracer
* trc
) {
34 TraceNullableEdge(trc
, &function_
, "function");
35 TraceEdge(trc
, &sourceObject_
, "sourceObject");
37 warmUpData_
.trace(trc
);
44 inline void js::Shape::traceChildren(JSTracer
* trc
) {
45 TraceCellHeaderEdge(trc
, this, "base");
47 asNative().traceChildren(trc
);
51 inline void js::NativeShape::traceChildren(JSTracer
* trc
) {
52 TraceNullableEdge(trc
, &propMap_
, "propertymap");
55 template <uint32_t opts
>
56 void js::GCMarker::eagerlyMarkChildren(Shape
* shape
) {
57 MOZ_ASSERT(shape
->isMarked(markColor()));
59 BaseShape
* base
= shape
->base();
60 checkTraversedEdge(shape
, base
);
61 if (mark
<opts
>(base
)) {
62 base
->traceChildren(tracer());
65 if (shape
->isNative()) {
66 if (PropMap
* map
= shape
->asNative().propMap()) {
67 markAndTraverseEdge
<opts
>(shape
, map
);
72 inline void JSString::traceChildren(JSTracer
* trc
) {
75 } else if (isRope()) {
76 asRope().traceChildren(trc
);
79 template <uint32_t opts
>
80 void js::GCMarker::eagerlyMarkChildren(JSString
* str
) {
81 if (str
->isLinear()) {
82 eagerlyMarkChildren
<opts
>(&str
->asLinear());
84 eagerlyMarkChildren
<opts
>(&str
->asRope());
88 inline void JSString::traceBase(JSTracer
* trc
) {
89 MOZ_ASSERT(hasBase());
90 js::TraceManuallyBarrieredEdge(trc
, &d
.s
.u3
.base
, "base");
92 template <uint32_t opts
>
93 void js::GCMarker::eagerlyMarkChildren(JSLinearString
* linearStr
) {
94 gc::AssertShouldMarkInZone(this, linearStr
);
95 MOZ_ASSERT(linearStr
->isMarkedAny());
96 MOZ_ASSERT(linearStr
->JSString::isLinear());
98 // Use iterative marking to avoid blowing out the stack.
99 while (linearStr
->hasBase()) {
100 linearStr
= linearStr
->base();
102 // It's possible to observe a rope as the base of a linear string if we
103 // process barriers during rope flattening. See the assignment of base in
104 // JSRope::flattenInternal's finish_node section.
105 if (static_cast<JSString
*>(linearStr
)->isRope()) {
106 MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting());
110 MOZ_ASSERT(linearStr
->JSString::isLinear());
111 gc::AssertShouldMarkInZone(this, linearStr
);
112 if (!mark
<opts
>(static_cast<JSString
*>(linearStr
))) {
118 inline void JSRope::traceChildren(JSTracer
* trc
) {
119 js::TraceManuallyBarrieredEdge(trc
, &d
.s
.u2
.left
, "left child");
120 js::TraceManuallyBarrieredEdge(trc
, &d
.s
.u3
.right
, "right child");
122 template <uint32_t opts
>
123 void js::GCMarker::eagerlyMarkChildren(JSRope
* rope
) {
124 // This function tries to scan the whole rope tree using the marking stack
125 // as temporary storage. If that becomes full, the unscanned ropes are
126 // added to the delayed marking list. When the function returns, the
127 // marking stack is at the same depth as it was on entry. This way we avoid
128 // using tags when pushing ropes to the stack as ropes never leak to other
129 // users of the stack. This also assumes that a rope can only point to
130 // other ropes or linear strings, it cannot refer to GC things of other
132 size_t savedPos
= stack
.position();
133 MOZ_DIAGNOSTIC_ASSERT(rope
->getTraceKind() == JS::TraceKind::String
);
135 MOZ_DIAGNOSTIC_ASSERT(rope
->getTraceKind() == JS::TraceKind::String
);
136 MOZ_DIAGNOSTIC_ASSERT(rope
->JSString::isRope());
137 gc::AssertShouldMarkInZone(this, rope
);
138 MOZ_ASSERT(rope
->isMarkedAny());
139 JSRope
* next
= nullptr;
141 JSString
* right
= rope
->rightChild();
142 if (mark
<opts
>(right
)) {
143 MOZ_ASSERT(!right
->isPermanentAtom());
144 if (right
->isLinear()) {
145 eagerlyMarkChildren
<opts
>(&right
->asLinear());
147 next
= &right
->asRope();
151 JSString
* left
= rope
->leftChild();
152 if (mark
<opts
>(left
)) {
153 MOZ_ASSERT(!left
->isPermanentAtom());
154 if (left
->isLinear()) {
155 eagerlyMarkChildren
<opts
>(&left
->asLinear());
157 // When both children are ropes, set aside the right one to
159 if (next
&& !stack
.pushTempRope(next
)) {
160 delayMarkingChildrenOnOOM(next
);
162 next
= &left
->asRope();
167 } else if (savedPos
!= stack
.position()) {
168 MOZ_ASSERT(savedPos
< stack
.position());
169 rope
= stack
.popPtr().asTempRope();
174 MOZ_ASSERT(savedPos
== stack
.position());
177 inline void JS::Symbol::traceChildren(JSTracer
* trc
) {
178 js::TraceNullableCellHeaderEdge(trc
, this, "symbol description");
181 template <typename SlotInfo
>
182 void js::RuntimeScopeData
<SlotInfo
>::trace(JSTracer
* trc
) {
183 TraceBindingNames(trc
, GetScopeDataTrailingNamesPointer(this), length
);
186 inline void js::FunctionScope::RuntimeData::trace(JSTracer
* trc
) {
187 TraceNullableEdge(trc
, &canonicalFunction
, "scope canonical function");
188 TraceNullableBindingNames(trc
, GetScopeDataTrailingNamesPointer(this),
191 inline void js::ModuleScope::RuntimeData::trace(JSTracer
* trc
) {
192 TraceNullableEdge(trc
, &module
, "scope module");
193 TraceBindingNames(trc
, GetScopeDataTrailingNamesPointer(this), length
);
195 inline void js::WasmInstanceScope::RuntimeData::trace(JSTracer
* trc
) {
196 TraceNullableEdge(trc
, &instance
, "wasm instance");
197 TraceBindingNames(trc
, GetScopeDataTrailingNamesPointer(this), length
);
200 inline void js::Scope::traceChildren(JSTracer
* trc
) {
201 TraceNullableEdge(trc
, &environmentShape_
, "scope env shape");
202 TraceNullableEdge(trc
, &enclosingScope_
, "scope enclosing");
203 applyScopeDataTyped([trc
](auto data
) { data
->trace(trc
); });
206 template <uint32_t opts
>
207 void js::GCMarker::eagerlyMarkChildren(Scope
* scope
) {
209 if (Shape
* shape
= scope
->environmentShape()) {
210 markAndTraverseEdge
<opts
>(scope
, shape
);
212 mozilla::Span
<AbstractBindingName
<JSAtom
>> names
;
213 switch (scope
->kind()) {
214 case ScopeKind::Function
: {
215 FunctionScope::RuntimeData
& data
= scope
->as
<FunctionScope
>().data();
216 if (data
.canonicalFunction
) {
217 markAndTraverseObjectEdge
<opts
>(scope
, data
.canonicalFunction
);
219 names
= GetScopeDataTrailingNames(&data
);
223 case ScopeKind::FunctionBodyVar
: {
224 VarScope::RuntimeData
& data
= scope
->as
<VarScope
>().data();
225 names
= GetScopeDataTrailingNames(&data
);
229 case ScopeKind::Lexical
:
230 case ScopeKind::SimpleCatch
:
231 case ScopeKind::Catch
:
232 case ScopeKind::NamedLambda
:
233 case ScopeKind::StrictNamedLambda
:
234 case ScopeKind::FunctionLexical
: {
235 LexicalScope::RuntimeData
& data
= scope
->as
<LexicalScope
>().data();
236 names
= GetScopeDataTrailingNames(&data
);
240 case ScopeKind::ClassBody
: {
241 ClassBodyScope::RuntimeData
& data
= scope
->as
<ClassBodyScope
>().data();
242 names
= GetScopeDataTrailingNames(&data
);
246 case ScopeKind::Global
:
247 case ScopeKind::NonSyntactic
: {
248 GlobalScope::RuntimeData
& data
= scope
->as
<GlobalScope
>().data();
249 names
= GetScopeDataTrailingNames(&data
);
253 case ScopeKind::Eval
:
254 case ScopeKind::StrictEval
: {
255 EvalScope::RuntimeData
& data
= scope
->as
<EvalScope
>().data();
256 names
= GetScopeDataTrailingNames(&data
);
260 case ScopeKind::Module
: {
261 ModuleScope::RuntimeData
& data
= scope
->as
<ModuleScope
>().data();
263 markAndTraverseObjectEdge
<opts
>(scope
, data
.module
);
265 names
= GetScopeDataTrailingNames(&data
);
269 case ScopeKind::With
:
272 case ScopeKind::WasmInstance
: {
273 WasmInstanceScope::RuntimeData
& data
=
274 scope
->as
<WasmInstanceScope
>().data();
275 markAndTraverseObjectEdge
<opts
>(scope
, data
.instance
);
276 names
= GetScopeDataTrailingNames(&data
);
280 case ScopeKind::WasmFunction
: {
281 WasmFunctionScope::RuntimeData
& data
=
282 scope
->as
<WasmFunctionScope
>().data();
283 names
= GetScopeDataTrailingNames(&data
);
287 if (scope
->kind_
== ScopeKind::Function
) {
288 for (auto& binding
: names
) {
289 if (JSAtom
* name
= binding
.name()) {
290 markAndTraverseStringEdge
<opts
>(scope
, name
);
294 for (auto& binding
: names
) {
295 markAndTraverseStringEdge
<opts
>(scope
, binding
.name());
298 scope
= scope
->enclosing();
299 } while (scope
&& mark
<opts
>(scope
));
302 inline void js::BaseShape::traceChildren(JSTracer
* trc
) {
303 // Note: the realm's global can be nullptr if we GC while creating the global.
304 if (JSObject
* global
= realm()->unsafeUnbarrieredMaybeGlobal()) {
305 TraceManuallyBarrieredEdge(trc
, &global
, "baseshape_global");
308 if (proto_
.isObject()) {
309 TraceEdge(trc
, &proto_
, "baseshape_proto");
313 inline void js::GetterSetter::traceChildren(JSTracer
* trc
) {
315 TraceCellHeaderEdge(trc
, this, "gettersetter_getter");
318 TraceEdge(trc
, &setter_
, "gettersetter_setter");
322 inline void js::PropMap::traceChildren(JSTracer
* trc
) {
324 TraceEdge(trc
, &asLinked()->data_
.previous
, "propmap_previous");
328 SharedPropMap::TreeData
& treeData
= asShared()->treeDataRef();
329 if (SharedPropMap
* parent
= treeData
.parent
.maybeMap()) {
330 TraceManuallyBarrieredEdge(trc
, &parent
, "propmap_parent");
331 if (parent
!= treeData
.parent
.map()) {
332 treeData
.setParent(parent
, treeData
.parent
.index());
337 for (uint32_t i
= 0; i
< PropMap::Capacity
; i
++) {
339 TraceEdge(trc
, &keys_
[i
], "propmap_key");
343 if (canHaveTable() && asLinked()->hasTable()) {
344 asLinked()->data_
.table
->trace(trc
);
348 template <uint32_t opts
>
349 void js::GCMarker::eagerlyMarkChildren(PropMap
* map
) {
350 MOZ_ASSERT(map
->isMarkedAny());
352 for (uint32_t i
= 0; i
< PropMap::Capacity
; i
++) {
353 if (map
->hasKey(i
)) {
354 markAndTraverseEdge
<opts
>(map
, map
->getKey(i
));
358 if (map
->canHaveTable()) {
359 // Special case: if a map has a table then all its pointers must point to
360 // this map or an ancestor. Since these pointers will be traced by this
361 // loop they do not need to be traced here as well.
362 MOZ_ASSERT(map
->asLinked()->canSkipMarkingTable());
365 if (map
->isDictionary()) {
366 map
= map
->asDictionary()->previous();
368 // For shared maps follow the |parent| link and not the |previous| link.
369 // They're different when a map had a branch that wasn't at the end of the
370 // map, but in this case they must have the same |previous| map. This is
371 // asserted in SharedPropMap::addChild. In other words, marking all
372 // |parent| maps will also mark all |previous| maps.
373 map
= map
->asShared()->treeDataRef().parent
.maybeMap();
375 } while (map
&& mark
<opts
>(map
));
378 inline void JS::BigInt::traceChildren(JSTracer
* trc
) {}
380 // JitCode::traceChildren is not defined inline due to its dependence on
383 #endif // gc_TraceMethods_inl_h