- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 98 %
- : 97 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 97 %
- : 97 %
- : 98 %
- : 98 %
- : 97 %
- : 96 %
- : 98 %
- : 96 %
- : 96 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 98 %
- : 96 %
- : 98 %
- : 98 %
- : 98 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 98 %
- : 96 %
- : 96 %
- : 98 %
- : 96 %
- : 96 %
- : 98 %
- : 98 %
- : 96 %
- : 98 %
- : 96 %
- : 98 %
- : 96 %
- : 98 %
- : 96 %
- : 98 %
- : 96 %
- : 96 %
- : 98 %
- : 96 %
- : 98 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 98 %
- : 96 %
- : 98 %
- : 96 %
- : 98 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 91 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 100 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 100 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
- : 96 %
Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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 "mozilla/Likely.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"
#include "gc/BufferAllocator-inl.h"
#include "gc/Marking-inl.h"
#include "vm/StringType-inl.h"
inline void js::BaseScript::traceChildren(JSTracer* trc) {
TraceNullableEdge(trc, &function_, "function");
TraceEdge(trc, &sourceObject_, "sourceObject");
if (data_) {
TraceBufferEdge(trc, this, &data_, "PrivateScriptData");
data_->trace(trc);
}
warmUpData_.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 <uint32_t opts>
void js::GCMarker::eagerlyMarkChildren(Shape* shape) {
MOZ_ASSERT(shape->isMarked(markColor()));
BaseShape* base = shape->base();
markAndTraverseEdge<opts>(shape, base);
if (shape->isNative()) {
if (PropMap* map = shape->asNative().propMap()) {
markAndTraverseEdge<opts>(shape, map);
}
}
}
inline void js::BaseShape::traceChildren(JSTracer* trc) {
// Note: the realm's global can be nullptr if we GC while creating the global.
JSObject* global = realm()->unsafeUnbarrieredMaybeGlobal();
if (MOZ_LIKELY(global)) {
TraceManuallyBarrieredEdge(trc, &global, "baseshape_global");
}
if (proto_.isObject()) {
TraceEdge(trc, &proto_, "baseshape_proto");
}
}
template <uint32_t opts>
void js::GCMarker::eagerlyMarkChildren(BaseShape* base) {
JSObject* global = base->realm()->unsafeUnbarrieredMaybeGlobal();
if (MOZ_LIKELY(global)) {
markAndTraverseEdge<opts>(base, global);
}
TaggedProto proto = base->proto();
if (proto.isObject()) {
markAndTraverseEdge<opts>(base, proto.toObject());
}
}
inline void JSString::traceChildren(JSTracer* trc) {
// Concurrent marking uses the other path.
MOZ_ASSERT(!js::IsConcurrentMarkingTracer(trc));
if (hasBase()) {
traceBase(trc);
} else if (isRope()) {
asRope().traceChildren(trc);
}
}
template <uint32_t opts>
void js::GCMarker::eagerlyMarkChildren(JSString* str) {
uint32_t flags = str->flags();
if (flags & JSString::LINEAR_BIT) {
eagerlyMarkChildren<opts>(static_cast<JSLinearString*>(str));
} else {
eagerlyMarkChildren<opts>(static_cast<JSRope*>(str));
}
}
inline void JSString::traceBase(JSTracer* trc) {
MOZ_ASSERT(hasBase());
js::TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base");
}
template <uint32_t opts>
void js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr) {
gc::AssertShouldMarkInZone(this, linearStr);
MOZ_ASSERT(linearStr->isMarkedAny());
// 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<JSString*>(linearStr)->isRope()) {
MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting());
break;
}
MOZ_ASSERT(linearStr->JSString::isLinear());
gc::AssertShouldMarkInZone(this, linearStr);
if (!mark<opts>(static_cast<JSString*>(linearStr))) {
break;
}
}
}
inline void JSRope::traceChildren(JSTracer* trc) {
// Concurrent marking uses the other path.
MOZ_ASSERT(!js::IsConcurrentMarkingTracer(trc));
js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child");
js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child");
}
template <uint32_t opts>
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 and 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);
gc::AssertShouldMarkInZone(this, rope);
MOZ_ASSERT(rope->isMarkedAny());
JSRope* next = nullptr;
JSString* left = rope->leftChild();
JSString* right = rope->rightChild();
#ifdef JS_GC_CONCURRENT_MARKING
// Check for a change of type performed by the main thread. This uses
// fence/fence synchronisation with the atomic operation being the update to
// the string flags.
//
// If we observe a change we skip marking the string here. This string will
// be marked by main thread.
//
// We care about the following transitions here:
// - rope => dependent string (rope flattening)
// - rope => extensible string (rope flattening)
// - rope => atom ref
//
// The order of operations is important here: we read the child fields,
// perform the memory fence and then check the flags. When changing the type
// on the main thread these happen in the reverse order. This ensures that
// we can't observe updated children with the old type. Observing the new
// type with old children is possible but benign.
if constexpr (bool(opts & gc::MarkingOptions::ConcurrentMarking)) {
gc::MemoryAcquireFence<opts>(rope->runtimeFromAnyThread());
if (!rope->isRopeAtomic()) {
continue;
}
}
#else
MOZ_DIAGNOSTIC_ASSERT(rope->JSString::isRope());
#endif
if (mark<opts>(right)) {
MOZ_ASSERT(!right->isPermanentAtom());
if (right->isLinear()) {
eagerlyMarkChildren<opts>(static_cast<JSLinearString*>(right));
} else {
next = static_cast<JSRope*>(right);
}
}
if (mark<opts>(left)) {
MOZ_ASSERT(!left->isPermanentAtom());
if (left->isLinear()) {
eagerlyMarkChildren<opts>(static_cast<JSLinearString*>(left));
} else {
// When both children are ropes, set aside the right one to
// scan it later.
if (next && !stack.pushTempRope(next)) {
delayMarkingChildrenOnOOM(next);
}
next = static_cast<JSRope*>(left);
}
}
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 <typename SlotInfo>
void js::RuntimeScopeData<SlotInfo>::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");
BaseScopeData* data = rawData();
if (data) {
TraceBufferEdge(trc, this, &data, "Scope data");
if (data != rawData()) {
setHeaderPtr(data);
}
applyScopeDataTyped([trc](auto data) { data->trace(trc); });
}
}
template <uint32_t opts>
void js::GCMarker::eagerlyMarkChildren(Scope* scope) {
do {
if (Shape* shape = scope->environmentShape()) {
markAndTraverseEdge<opts>(scope, shape);
}
if (BaseScopeData* data = scope->rawData()) {
MarkTenuredBuffer(scope->zone(), data);
}
mozilla::Span<AbstractBindingName<JSAtom>> names;
switch (scope->kind()) {
case ScopeKind::Function: {
FunctionScope::RuntimeData& data = scope->as<FunctionScope>().data();
if (data.canonicalFunction) {
markAndTraverseObjectEdge<opts>(scope, data.canonicalFunction);
}
names = GetScopeDataTrailingNames(&data);
break;
}
case ScopeKind::FunctionBodyVar: {
VarScope::RuntimeData& data = scope->as<VarScope>().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<LexicalScope>().data();
names = GetScopeDataTrailingNames(&data);
break;
}
case ScopeKind::ClassBody: {
ClassBodyScope::RuntimeData& data = scope->as<ClassBodyScope>().data();
names = GetScopeDataTrailingNames(&data);
break;
}
case ScopeKind::Global:
case ScopeKind::NonSyntactic: {
GlobalScope::RuntimeData& data = scope->as<GlobalScope>().data();
names = GetScopeDataTrailingNames(&data);
break;
}
case ScopeKind::Eval:
case ScopeKind::StrictEval: {
EvalScope::RuntimeData& data = scope->as<EvalScope>().data();
names = GetScopeDataTrailingNames(&data);
break;
}
case ScopeKind::Module: {
ModuleScope::RuntimeData& data = scope->as<ModuleScope>().data();
if (data.module) {
markAndTraverseObjectEdge<opts>(scope, data.module);
}
names = GetScopeDataTrailingNames(&data);
break;
}
case ScopeKind::With:
break;
case ScopeKind::WasmInstance: {
WasmInstanceScope::RuntimeData& data =
scope->as<WasmInstanceScope>().data();
markAndTraverseObjectEdge<opts>(scope, data.instance);
names = GetScopeDataTrailingNames(&data);
break;
}
case ScopeKind::WasmFunction: {
WasmFunctionScope::RuntimeData& data =
scope->as<WasmFunctionScope>().data();
names = GetScopeDataTrailingNames(&data);
break;
}
}
if (scope->kind_ == ScopeKind::Function) {
for (auto& binding : names) {
if (JSAtom* name = binding.name()) {
markAndTraverseStringEdge<opts>(scope, name);
}
}
} else {
for (auto& binding : names) {
markAndTraverseStringEdge<opts>(scope, binding.name());
}
}
scope = scope->enclosing();
} while (scope && mark<opts>(scope));
}
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 <uint32_t opts>
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<opts>(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_IF(state != MarkingState::ConcurrentMarking,
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<opts>(map));
}
inline void JS::BigInt::traceChildren(JSTracer* trc) {
if (!hasInlineDigits()) {
js::TraceBufferEdge(trc, this, &heapDigits_, "BigInt::heapDigits_");
}
}
// JitCode::traceChildren is not defined inline due to its dependence on
// MacroAssembler.
#endif // gc_TraceMethods_inl_h