no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / js / src / gc / TraceMethods-inl.h
blob37da24fe6edfdf418c770ff6f61cb4937c0b4908
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/. */
7 /*
8 * Trace methods for all GC things, defined in a separate header to allow
9 * inlining.
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"
26 #include "vm/Realm.h"
27 #include "vm/Scope.h"
28 #include "vm/Shape.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);
39 if (data_) {
40 data_->trace(trc);
44 inline void js::Shape::traceChildren(JSTracer* trc) {
45 TraceCellHeaderEdge(trc, this, "base");
46 if (isNative()) {
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) {
73 if (hasBase()) {
74 traceBase(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());
83 } else {
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());
107 break;
110 MOZ_ASSERT(linearStr->JSString::isLinear());
111 gc::AssertShouldMarkInZone(this, linearStr);
112 if (!mark<opts>(static_cast<JSString*>(linearStr))) {
113 break;
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
131 // types.
132 size_t savedPos = stack.position();
133 MOZ_DIAGNOSTIC_ASSERT(rope->getTraceKind() == JS::TraceKind::String);
134 while (true) {
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());
146 } else {
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());
156 } else {
157 // When both children are ropes, set aside the right one to
158 // scan it later.
159 if (next && !stack.pushTempRope(next)) {
160 delayMarkingChildrenOnOOM(next);
162 next = &left->asRope();
165 if (next) {
166 rope = next;
167 } else if (savedPos != stack.position()) {
168 MOZ_ASSERT(savedPos < stack.position());
169 rope = stack.popPtr().asTempRope();
170 } else {
171 break;
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),
189 length);
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) {
208 do {
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);
220 break;
223 case ScopeKind::FunctionBodyVar: {
224 VarScope::RuntimeData& data = scope->as<VarScope>().data();
225 names = GetScopeDataTrailingNames(&data);
226 break;
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);
237 break;
240 case ScopeKind::ClassBody: {
241 ClassBodyScope::RuntimeData& data = scope->as<ClassBodyScope>().data();
242 names = GetScopeDataTrailingNames(&data);
243 break;
246 case ScopeKind::Global:
247 case ScopeKind::NonSyntactic: {
248 GlobalScope::RuntimeData& data = scope->as<GlobalScope>().data();
249 names = GetScopeDataTrailingNames(&data);
250 break;
253 case ScopeKind::Eval:
254 case ScopeKind::StrictEval: {
255 EvalScope::RuntimeData& data = scope->as<EvalScope>().data();
256 names = GetScopeDataTrailingNames(&data);
257 break;
260 case ScopeKind::Module: {
261 ModuleScope::RuntimeData& data = scope->as<ModuleScope>().data();
262 if (data.module) {
263 markAndTraverseObjectEdge<opts>(scope, data.module);
265 names = GetScopeDataTrailingNames(&data);
266 break;
269 case ScopeKind::With:
270 break;
272 case ScopeKind::WasmInstance: {
273 WasmInstanceScope::RuntimeData& data =
274 scope->as<WasmInstanceScope>().data();
275 markAndTraverseObjectEdge<opts>(scope, data.instance);
276 names = GetScopeDataTrailingNames(&data);
277 break;
280 case ScopeKind::WasmFunction: {
281 WasmFunctionScope::RuntimeData& data =
282 scope->as<WasmFunctionScope>().data();
283 names = GetScopeDataTrailingNames(&data);
284 break;
287 if (scope->kind_ == ScopeKind::Function) {
288 for (auto& binding : names) {
289 if (JSAtom* name = binding.name()) {
290 markAndTraverseStringEdge<opts>(scope, name);
293 } else {
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) {
314 if (getter()) {
315 TraceCellHeaderEdge(trc, this, "gettersetter_getter");
317 if (setter()) {
318 TraceEdge(trc, &setter_, "gettersetter_setter");
322 inline void js::PropMap::traceChildren(JSTracer* trc) {
323 if (hasPrevious()) {
324 TraceEdge(trc, &asLinked()->data_.previous, "propmap_previous");
327 if (isShared()) {
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++) {
338 if (hasKey(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());
351 do {
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();
367 } else {
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
381 // MacroAssembler.
383 #endif // gc_TraceMethods_inl_h