/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Trace methods for all GC things, defined in a separate header to allow * inlining. * * This also includes eager inline marking versions. Both paths must end up * traversing equivalent subgraphs. */ #ifndef gc_TraceMethods_inl_h #define gc_TraceMethods_inl_h #include "gc/GCMarker.h" #include "gc/Tracer.h" #include "jit/JitCode.h" #include "vm/BigIntType.h" #include "vm/GetterSetter.h" #include "vm/GlobalObject.h" #include "vm/JSScript.h" #include "vm/PropMap.h" #include "vm/Realm.h" #include "vm/Scope.h" #include "vm/Shape.h" #include "vm/StringType.h" #include "vm/SymbolType.h" #include "wasm/WasmJS.h" inline void js::BaseScript::traceChildren(JSTracer* trc) { TraceNullableEdge(trc, &function_, "function"); TraceEdge(trc, &sourceObject_, "sourceObject"); warmUpData_.trace(trc); if (data_) { data_->trace(trc); } } inline void js::Shape::traceChildren(JSTracer* trc) { TraceCellHeaderEdge(trc, this, "base"); if (isNative()) { asNative().traceChildren(trc); } } inline void js::NativeShape::traceChildren(JSTracer* trc) { TraceNullableEdge(trc, &propMap_, "propertymap"); } template void js::GCMarker::eagerlyMarkChildren(Shape* shape) { MOZ_ASSERT(shape->isMarked(markColor())); BaseShape* base = shape->base(); checkTraversedEdge(shape, base); if (mark(base)) { base->traceChildren(tracer()); } if (shape->isNative()) { if (PropMap* map = shape->asNative().propMap()) { markAndTraverseEdge(shape, map); } } } inline void JSString::traceChildren(JSTracer* trc) { if (hasBase()) { traceBase(trc); } else if (isRope()) { asRope().traceChildren(trc); } } template void js::GCMarker::eagerlyMarkChildren(JSString* str) { if (str->isLinear()) { eagerlyMarkChildren(&str->asLinear()); } else { eagerlyMarkChildren(&str->asRope()); } } inline void JSString::traceBase(JSTracer* trc) { MOZ_ASSERT(hasBase()); js::TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base"); } template void js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr) { gc::AssertShouldMarkInZone(this, linearStr); MOZ_ASSERT(linearStr->isMarkedAny()); MOZ_ASSERT(linearStr->JSString::isLinear()); // Use iterative marking to avoid blowing out the stack. while (linearStr->hasBase()) { linearStr = linearStr->base(); // It's possible to observe a rope as the base of a linear string if we // process barriers during rope flattening. See the assignment of base in // JSRope::flattenInternal's finish_node section. if (static_cast(linearStr)->isRope()) { MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting()); break; } MOZ_ASSERT(linearStr->JSString::isLinear()); MOZ_ASSERT(!linearStr->isPermanentAtom()); gc::AssertShouldMarkInZone(this, linearStr); if (!mark(static_cast(linearStr))) { break; } } } inline void JSRope::traceChildren(JSTracer* trc) { js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child"); js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child"); } template void js::GCMarker::eagerlyMarkChildren(JSRope* rope) { // This function tries to scan the whole rope tree using the marking stack // as temporary storage. If that becomes full, the unscanned ropes are // added to the delayed marking list. When the function returns, the // marking stack is at the same depth as it was on entry. This way we avoid // using tags when pushing ropes to the stack as ropes never leak to other // users of the stack. This also assumes that a rope can only point to // other ropes or linear strings, it cannot refer to GC things of other // types. size_t savedPos = stack.position(); MOZ_DIAGNOSTIC_ASSERT(rope->getTraceKind() == JS::TraceKind::String); while (true) { MOZ_DIAGNOSTIC_ASSERT(rope->getTraceKind() == JS::TraceKind::String); MOZ_DIAGNOSTIC_ASSERT(rope->JSString::isRope()); gc::AssertShouldMarkInZone(this, rope); MOZ_ASSERT(rope->isMarkedAny()); JSRope* next = nullptr; JSString* right = rope->rightChild(); if (mark(right)) { MOZ_ASSERT(!right->isPermanentAtom()); if (right->isLinear()) { eagerlyMarkChildren(&right->asLinear()); } else { next = &right->asRope(); } } JSString* left = rope->leftChild(); if (mark(left)) { MOZ_ASSERT(!left->isPermanentAtom()); if (left->isLinear()) { eagerlyMarkChildren(&left->asLinear()); } else { // When both children are ropes, set aside the right one to // scan it later. if (next && !stack.pushTempRope(next)) { delayMarkingChildrenOnOOM(next); } next = &left->asRope(); } } if (next) { rope = next; } else if (savedPos != stack.position()) { MOZ_ASSERT(savedPos < stack.position()); rope = stack.popPtr().asTempRope(); } else { break; } } MOZ_ASSERT(savedPos == stack.position()); } inline void JS::Symbol::traceChildren(JSTracer* trc) { js::TraceNullableCellHeaderEdge(trc, this, "symbol description"); } template void js::RuntimeScopeData::trace(JSTracer* trc) { TraceBindingNames(trc, GetScopeDataTrailingNamesPointer(this), length); } inline void js::FunctionScope::RuntimeData::trace(JSTracer* trc) { TraceNullableEdge(trc, &canonicalFunction, "scope canonical function"); TraceNullableBindingNames(trc, GetScopeDataTrailingNamesPointer(this), length); } inline void js::ModuleScope::RuntimeData::trace(JSTracer* trc) { TraceNullableEdge(trc, &module, "scope module"); TraceBindingNames(trc, GetScopeDataTrailingNamesPointer(this), length); } inline void js::WasmInstanceScope::RuntimeData::trace(JSTracer* trc) { TraceNullableEdge(trc, &instance, "wasm instance"); TraceBindingNames(trc, GetScopeDataTrailingNamesPointer(this), length); } inline void js::Scope::traceChildren(JSTracer* trc) { TraceNullableEdge(trc, &environmentShape_, "scope env shape"); TraceNullableEdge(trc, &enclosingScope_, "scope enclosing"); applyScopeDataTyped([trc](auto data) { data->trace(trc); }); } template void js::GCMarker::eagerlyMarkChildren(Scope* scope) { do { if (Shape* shape = scope->environmentShape()) { markAndTraverseEdge(scope, shape); } mozilla::Span> names; switch (scope->kind()) { case ScopeKind::Function: { FunctionScope::RuntimeData& data = scope->as().data(); if (data.canonicalFunction) { markAndTraverseObjectEdge(scope, data.canonicalFunction); } names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::FunctionBodyVar: { VarScope::RuntimeData& data = scope->as().data(); names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::Lexical: case ScopeKind::SimpleCatch: case ScopeKind::Catch: case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: case ScopeKind::FunctionLexical: { LexicalScope::RuntimeData& data = scope->as().data(); names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::ClassBody: { ClassBodyScope::RuntimeData& data = scope->as().data(); names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::Global: case ScopeKind::NonSyntactic: { GlobalScope::RuntimeData& data = scope->as().data(); names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::Eval: case ScopeKind::StrictEval: { EvalScope::RuntimeData& data = scope->as().data(); names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::Module: { ModuleScope::RuntimeData& data = scope->as().data(); if (data.module) { markAndTraverseObjectEdge(scope, data.module); } names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::With: break; case ScopeKind::WasmInstance: { WasmInstanceScope::RuntimeData& data = scope->as().data(); markAndTraverseObjectEdge(scope, data.instance); names = GetScopeDataTrailingNames(&data); break; } case ScopeKind::WasmFunction: { WasmFunctionScope::RuntimeData& data = scope->as().data(); names = GetScopeDataTrailingNames(&data); break; } } if (scope->kind_ == ScopeKind::Function) { for (auto& binding : names) { if (JSAtom* name = binding.name()) { markAndTraverseStringEdge(scope, name); } } } else { for (auto& binding : names) { markAndTraverseStringEdge(scope, binding.name()); } } scope = scope->enclosing(); } while (scope && mark(scope)); } inline void js::BaseShape::traceChildren(JSTracer* trc) { // Note: the realm's global can be nullptr if we GC while creating the global. if (JSObject* global = realm()->unsafeUnbarrieredMaybeGlobal()) { TraceManuallyBarrieredEdge(trc, &global, "baseshape_global"); } if (proto_.isObject()) { TraceEdge(trc, &proto_, "baseshape_proto"); } } inline void js::GetterSetter::traceChildren(JSTracer* trc) { if (getter()) { TraceCellHeaderEdge(trc, this, "gettersetter_getter"); } if (setter()) { TraceEdge(trc, &setter_, "gettersetter_setter"); } } inline void js::PropMap::traceChildren(JSTracer* trc) { if (hasPrevious()) { TraceEdge(trc, &asLinked()->data_.previous, "propmap_previous"); } if (isShared()) { SharedPropMap::TreeData& treeData = asShared()->treeDataRef(); if (SharedPropMap* parent = treeData.parent.maybeMap()) { TraceManuallyBarrieredEdge(trc, &parent, "propmap_parent"); if (parent != treeData.parent.map()) { treeData.setParent(parent, treeData.parent.index()); } } } for (uint32_t i = 0; i < PropMap::Capacity; i++) { if (hasKey(i)) { TraceEdge(trc, &keys_[i], "propmap_key"); } } if (canHaveTable() && asLinked()->hasTable()) { asLinked()->data_.table->trace(trc); } } template void js::GCMarker::eagerlyMarkChildren(PropMap* map) { MOZ_ASSERT(map->isMarkedAny()); do { for (uint32_t i = 0; i < PropMap::Capacity; i++) { if (map->hasKey(i)) { markAndTraverseEdge(map, map->getKey(i)); } } if (map->canHaveTable()) { // Special case: if a map has a table then all its pointers must point to // this map or an ancestor. Since these pointers will be traced by this // loop they do not need to be traced here as well. MOZ_ASSERT(map->asLinked()->canSkipMarkingTable()); } if (map->isDictionary()) { map = map->asDictionary()->previous(); } else { // For shared maps follow the |parent| link and not the |previous| link. // They're different when a map had a branch that wasn't at the end of the // map, but in this case they must have the same |previous| map. This is // asserted in SharedPropMap::addChild. In other words, marking all // |parent| maps will also mark all |previous| maps. map = map->asShared()->treeDataRef().parent.maybeMap(); } } while (map && mark(map)); } inline void JS::BigInt::traceChildren(JSTracer* trc) {} // JitCode::traceChildren is not defined inline due to its dependence on // MacroAssembler. #endif // gc_TraceMethods_inl_h