TraceMethods-inl.h (12636B)
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/. */ 6 7 /* 8 * Trace methods for all GC things, defined in a separate header to allow 9 * inlining. 10 * 11 * This also includes eager inline marking versions. Both paths must end up 12 * traversing equivalent subgraphs. 13 */ 14 15 #ifndef gc_TraceMethods_inl_h 16 #define gc_TraceMethods_inl_h 17 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" 32 33 #include "gc/BufferAllocator-inl.h" 34 #include "gc/Marking-inl.h" 35 #include "vm/StringType-inl.h" 36 37 inline void js::BaseScript::traceChildren(JSTracer* trc) { 38 TraceNullableEdge(trc, &function_, "function"); 39 TraceEdge(trc, &sourceObject_, "sourceObject"); 40 41 warmUpData_.trace(trc); 42 43 if (data_) { 44 data_->trace(trc); 45 } 46 } 47 48 inline void js::Shape::traceChildren(JSTracer* trc) { 49 TraceCellHeaderEdge(trc, this, "base"); 50 if (isNative()) { 51 asNative().traceChildren(trc); 52 } 53 } 54 55 inline void js::NativeShape::traceChildren(JSTracer* trc) { 56 TraceNullableEdge(trc, &propMap_, "propertymap"); 57 } 58 59 template <uint32_t opts> 60 void js::GCMarker::eagerlyMarkChildren(Shape* shape) { 61 MOZ_ASSERT(shape->isMarked(markColor())); 62 63 BaseShape* base = shape->base(); 64 checkTraversedEdge(shape, base); 65 if (mark<opts>(base)) { 66 base->traceChildren(tracer()); 67 } 68 69 if (shape->isNative()) { 70 if (PropMap* map = shape->asNative().propMap()) { 71 markAndTraverseEdge<opts>(shape, map); 72 } 73 } 74 } 75 76 inline void JSString::traceChildren(JSTracer* trc) { 77 if (hasBase()) { 78 traceBase(trc); 79 } else if (isRope()) { 80 asRope().traceChildren(trc); 81 } 82 } 83 template <uint32_t opts> 84 void js::GCMarker::eagerlyMarkChildren(JSString* str) { 85 if (str->isLinear()) { 86 eagerlyMarkChildren<opts>(&str->asLinear()); 87 } else { 88 eagerlyMarkChildren<opts>(&str->asRope()); 89 } 90 } 91 92 inline void JSString::traceBase(JSTracer* trc) { 93 MOZ_ASSERT(hasBase()); 94 js::TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base"); 95 } 96 template <uint32_t opts> 97 void js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr) { 98 gc::AssertShouldMarkInZone(this, linearStr); 99 MOZ_ASSERT(linearStr->isMarkedAny()); 100 MOZ_ASSERT(linearStr->JSString::isLinear()); 101 102 // Use iterative marking to avoid blowing out the stack. 103 while (linearStr->hasBase()) { 104 linearStr = linearStr->base(); 105 106 // It's possible to observe a rope as the base of a linear string if we 107 // process barriers during rope flattening. See the assignment of base in 108 // JSRope::flattenInternal's finish_node section. 109 if (static_cast<JSString*>(linearStr)->isRope()) { 110 MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting()); 111 break; 112 } 113 114 MOZ_ASSERT(linearStr->JSString::isLinear()); 115 gc::AssertShouldMarkInZone(this, linearStr); 116 if (!mark<opts>(static_cast<JSString*>(linearStr))) { 117 break; 118 } 119 } 120 } 121 122 inline void JSRope::traceChildren(JSTracer* trc) { 123 js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child"); 124 js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child"); 125 } 126 template <uint32_t opts> 127 void js::GCMarker::eagerlyMarkChildren(JSRope* rope) { 128 // This function tries to scan the whole rope tree using the marking stack 129 // as temporary storage. If that becomes full, the unscanned ropes are 130 // added to the delayed marking list. When the function returns, the 131 // marking stack is at the same depth as it was on entry. This way we avoid 132 // using tags when pushing ropes to the stack as ropes never leak to other 133 // users of the stack. This also assumes that a rope can only point to 134 // other ropes or linear strings, it cannot refer to GC things of other 135 // types. 136 size_t savedPos = stack.position(); 137 MOZ_DIAGNOSTIC_ASSERT(rope->getTraceKind() == JS::TraceKind::String); 138 while (true) { 139 MOZ_DIAGNOSTIC_ASSERT(rope->getTraceKind() == JS::TraceKind::String); 140 MOZ_DIAGNOSTIC_ASSERT(rope->JSString::isRope()); 141 gc::AssertShouldMarkInZone(this, rope); 142 MOZ_ASSERT(rope->isMarkedAny()); 143 JSRope* next = nullptr; 144 145 JSString* right = rope->rightChild(); 146 if (mark<opts>(right)) { 147 MOZ_ASSERT(!right->isPermanentAtom()); 148 if (right->isLinear()) { 149 eagerlyMarkChildren<opts>(&right->asLinear()); 150 } else { 151 next = &right->asRope(); 152 } 153 } 154 155 JSString* left = rope->leftChild(); 156 if (mark<opts>(left)) { 157 MOZ_ASSERT(!left->isPermanentAtom()); 158 if (left->isLinear()) { 159 eagerlyMarkChildren<opts>(&left->asLinear()); 160 } else { 161 // When both children are ropes, set aside the right one to 162 // scan it later. 163 if (next && !stack.pushTempRope(next)) { 164 delayMarkingChildrenOnOOM(next); 165 } 166 next = &left->asRope(); 167 } 168 } 169 if (next) { 170 rope = next; 171 } else if (savedPos != stack.position()) { 172 MOZ_ASSERT(savedPos < stack.position()); 173 rope = stack.popPtr().asTempRope(); 174 } else { 175 break; 176 } 177 } 178 MOZ_ASSERT(savedPos == stack.position()); 179 } 180 181 inline void JS::Symbol::traceChildren(JSTracer* trc) { 182 js::TraceNullableCellHeaderEdge(trc, this, "symbol description"); 183 } 184 185 template <typename SlotInfo> 186 void js::RuntimeScopeData<SlotInfo>::trace(JSTracer* trc) { 187 TraceBindingNames(trc, GetScopeDataTrailingNamesPointer(this), length); 188 } 189 190 inline void js::FunctionScope::RuntimeData::trace(JSTracer* trc) { 191 TraceNullableEdge(trc, &canonicalFunction, "scope canonical function"); 192 TraceNullableBindingNames(trc, GetScopeDataTrailingNamesPointer(this), 193 length); 194 } 195 inline void js::ModuleScope::RuntimeData::trace(JSTracer* trc) { 196 TraceNullableEdge(trc, &module, "scope module"); 197 TraceBindingNames(trc, GetScopeDataTrailingNamesPointer(this), length); 198 } 199 inline void js::WasmInstanceScope::RuntimeData::trace(JSTracer* trc) { 200 TraceNullableEdge(trc, &instance, "wasm instance"); 201 TraceBindingNames(trc, GetScopeDataTrailingNamesPointer(this), length); 202 } 203 204 inline void js::Scope::traceChildren(JSTracer* trc) { 205 TraceNullableEdge(trc, &environmentShape_, "scope env shape"); 206 TraceNullableEdge(trc, &enclosingScope_, "scope enclosing"); 207 BaseScopeData* data = rawData(); 208 if (data) { 209 TraceBufferEdge(trc, this, &data, "Scope data"); 210 if (data != rawData()) { 211 setHeaderPtr(data); 212 } 213 } 214 applyScopeDataTyped([trc](auto data) { data->trace(trc); }); 215 } 216 217 template <uint32_t opts> 218 void js::GCMarker::eagerlyMarkChildren(Scope* scope) { 219 do { 220 if (Shape* shape = scope->environmentShape()) { 221 markAndTraverseEdge<opts>(scope, shape); 222 } 223 if (BaseScopeData* data = scope->rawData()) { 224 MarkTenuredBuffer(scope->zone(), data); 225 } 226 mozilla::Span<AbstractBindingName<JSAtom>> names; 227 switch (scope->kind()) { 228 case ScopeKind::Function: { 229 FunctionScope::RuntimeData& data = scope->as<FunctionScope>().data(); 230 if (data.canonicalFunction) { 231 markAndTraverseObjectEdge<opts>(scope, data.canonicalFunction); 232 } 233 names = GetScopeDataTrailingNames(&data); 234 break; 235 } 236 237 case ScopeKind::FunctionBodyVar: { 238 VarScope::RuntimeData& data = scope->as<VarScope>().data(); 239 names = GetScopeDataTrailingNames(&data); 240 break; 241 } 242 243 case ScopeKind::Lexical: 244 case ScopeKind::SimpleCatch: 245 case ScopeKind::Catch: 246 case ScopeKind::NamedLambda: 247 case ScopeKind::StrictNamedLambda: 248 case ScopeKind::FunctionLexical: { 249 LexicalScope::RuntimeData& data = scope->as<LexicalScope>().data(); 250 names = GetScopeDataTrailingNames(&data); 251 break; 252 } 253 254 case ScopeKind::ClassBody: { 255 ClassBodyScope::RuntimeData& data = scope->as<ClassBodyScope>().data(); 256 names = GetScopeDataTrailingNames(&data); 257 break; 258 } 259 260 case ScopeKind::Global: 261 case ScopeKind::NonSyntactic: { 262 GlobalScope::RuntimeData& data = scope->as<GlobalScope>().data(); 263 names = GetScopeDataTrailingNames(&data); 264 break; 265 } 266 267 case ScopeKind::Eval: 268 case ScopeKind::StrictEval: { 269 EvalScope::RuntimeData& data = scope->as<EvalScope>().data(); 270 names = GetScopeDataTrailingNames(&data); 271 break; 272 } 273 274 case ScopeKind::Module: { 275 ModuleScope::RuntimeData& data = scope->as<ModuleScope>().data(); 276 if (data.module) { 277 markAndTraverseObjectEdge<opts>(scope, data.module); 278 } 279 names = GetScopeDataTrailingNames(&data); 280 break; 281 } 282 283 case ScopeKind::With: 284 break; 285 286 case ScopeKind::WasmInstance: { 287 WasmInstanceScope::RuntimeData& data = 288 scope->as<WasmInstanceScope>().data(); 289 markAndTraverseObjectEdge<opts>(scope, data.instance); 290 names = GetScopeDataTrailingNames(&data); 291 break; 292 } 293 294 case ScopeKind::WasmFunction: { 295 WasmFunctionScope::RuntimeData& data = 296 scope->as<WasmFunctionScope>().data(); 297 names = GetScopeDataTrailingNames(&data); 298 break; 299 } 300 } 301 if (scope->kind_ == ScopeKind::Function) { 302 for (auto& binding : names) { 303 if (JSAtom* name = binding.name()) { 304 markAndTraverseStringEdge<opts>(scope, name); 305 } 306 } 307 } else { 308 for (auto& binding : names) { 309 markAndTraverseStringEdge<opts>(scope, binding.name()); 310 } 311 } 312 scope = scope->enclosing(); 313 } while (scope && mark<opts>(scope)); 314 } 315 316 inline void js::BaseShape::traceChildren(JSTracer* trc) { 317 // Note: the realm's global can be nullptr if we GC while creating the global. 318 if (JSObject* global = realm()->unsafeUnbarrieredMaybeGlobal()) { 319 TraceManuallyBarrieredEdge(trc, &global, "baseshape_global"); 320 } 321 322 if (proto_.isObject()) { 323 TraceEdge(trc, &proto_, "baseshape_proto"); 324 } 325 } 326 327 inline void js::GetterSetter::traceChildren(JSTracer* trc) { 328 if (getter()) { 329 TraceCellHeaderEdge(trc, this, "gettersetter_getter"); 330 } 331 if (setter()) { 332 TraceEdge(trc, &setter_, "gettersetter_setter"); 333 } 334 } 335 336 inline void js::PropMap::traceChildren(JSTracer* trc) { 337 if (hasPrevious()) { 338 TraceEdge(trc, &asLinked()->data_.previous, "propmap_previous"); 339 } 340 341 if (isShared()) { 342 SharedPropMap::TreeData& treeData = asShared()->treeDataRef(); 343 if (SharedPropMap* parent = treeData.parent.maybeMap()) { 344 TraceManuallyBarrieredEdge(trc, &parent, "propmap_parent"); 345 if (parent != treeData.parent.map()) { 346 treeData.setParent(parent, treeData.parent.index()); 347 } 348 } 349 } 350 351 for (uint32_t i = 0; i < PropMap::Capacity; i++) { 352 if (hasKey(i)) { 353 TraceEdge(trc, &keys_[i], "propmap_key"); 354 } 355 } 356 357 if (canHaveTable() && asLinked()->hasTable()) { 358 asLinked()->data_.table->trace(trc); 359 } 360 } 361 362 template <uint32_t opts> 363 void js::GCMarker::eagerlyMarkChildren(PropMap* map) { 364 MOZ_ASSERT(map->isMarkedAny()); 365 do { 366 for (uint32_t i = 0; i < PropMap::Capacity; i++) { 367 if (map->hasKey(i)) { 368 markAndTraverseEdge<opts>(map, map->getKey(i)); 369 } 370 } 371 372 if (map->canHaveTable()) { 373 // Special case: if a map has a table then all its pointers must point to 374 // this map or an ancestor. Since these pointers will be traced by this 375 // loop they do not need to be traced here as well. 376 MOZ_ASSERT(map->asLinked()->canSkipMarkingTable()); 377 } 378 379 if (map->isDictionary()) { 380 map = map->asDictionary()->previous(); 381 } else { 382 // For shared maps follow the |parent| link and not the |previous| link. 383 // They're different when a map had a branch that wasn't at the end of the 384 // map, but in this case they must have the same |previous| map. This is 385 // asserted in SharedPropMap::addChild. In other words, marking all 386 // |parent| maps will also mark all |previous| maps. 387 map = map->asShared()->treeDataRef().parent.maybeMap(); 388 } 389 } while (map && mark<opts>(map)); 390 } 391 392 inline void JS::BigInt::traceChildren(JSTracer* trc) { 393 if (!hasInlineDigits()) { 394 js::TraceBufferEdge(trc, this, &heapDigits_, "BigInt::heapDigits_"); 395 } 396 } 397 398 // JitCode::traceChildren is not defined inline due to its dependence on 399 // MacroAssembler. 400 401 #endif // gc_TraceMethods_inl_h