tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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