Tracer.cpp (8263B)
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 #include "gc/Tracer.h" 8 9 #include "mozilla/DebugOnly.h" 10 11 #include "NamespaceImports.h" 12 13 #include "gc/PublicIterators.h" 14 #include "jit/JitCode.h" 15 #include "util/Memory.h" 16 #include "util/Text.h" 17 #include "vm/BigIntType.h" 18 #include "vm/JSContext.h" 19 #include "vm/JSFunction.h" 20 #include "vm/JSScript.h" 21 #include "vm/RegExpShared.h" 22 #include "vm/Scope.h" 23 #include "vm/Shape.h" 24 #include "vm/StringType.h" 25 #include "vm/SymbolType.h" 26 27 #include "gc/TraceMethods-inl.h" 28 #include "vm/Shape-inl.h" 29 30 using namespace js; 31 using namespace js::gc; 32 33 template void RuntimeScopeData<LexicalScope::SlotInfo>::trace(JSTracer* trc); 34 template void RuntimeScopeData<ClassBodyScope::SlotInfo>::trace(JSTracer* trc); 35 template void RuntimeScopeData<VarScope::SlotInfo>::trace(JSTracer* trc); 36 template void RuntimeScopeData<GlobalScope::SlotInfo>::trace(JSTracer* trc); 37 template void RuntimeScopeData<EvalScope::SlotInfo>::trace(JSTracer* trc); 38 template void RuntimeScopeData<WasmFunctionScope::SlotInfo>::trace( 39 JSTracer* trc); 40 41 void JS::TracingContext::getEdgeName(const char* name, char* buffer, 42 size_t bufferSize) { 43 MOZ_ASSERT(bufferSize > 0); 44 if (functor_) { 45 (*functor_)(this, name, buffer, bufferSize); 46 return; 47 } 48 if (index_ != InvalidIndex) { 49 snprintf(buffer, bufferSize, "%s[%zu]", name, index_); 50 return; 51 } 52 snprintf(buffer, bufferSize, "%s", name); 53 } 54 55 /*** Public Tracing API *****************************************************/ 56 57 JS_PUBLIC_API void JS::TraceChildren(JSTracer* trc, GCCellPtr thing) { 58 ApplyGCThingTyped(thing.asCell(), thing.kind(), [trc](auto t) { 59 MOZ_ASSERT_IF(t->runtimeFromAnyThread() != trc->runtime(), 60 t->isPermanentAndMayBeShared()); 61 t->traceChildren(trc); 62 }); 63 } 64 65 void js::gc::TraceIncomingCCWs(JSTracer* trc, 66 const JS::CompartmentSet& compartments) { 67 for (CompartmentsIter source(trc->runtime()); !source.done(); source.next()) { 68 if (compartments.has(source)) { 69 continue; 70 } 71 // Iterate over all compartments that |source| has wrappers for. 72 for (Compartment::WrappedObjectCompartmentEnum dest(source); !dest.empty(); 73 dest.popFront()) { 74 if (!compartments.has(dest)) { 75 continue; 76 } 77 // Iterate over all wrappers from |source| to |dest| compartments. 78 for (Compartment::ObjectWrapperEnum e(source, dest); !e.empty(); 79 e.popFront()) { 80 JSObject* obj = e.front().key(); 81 MOZ_ASSERT(compartments.has(obj->compartment())); 82 mozilla::DebugOnly<JSObject*> prior = obj; 83 TraceManuallyBarrieredEdge(trc, &obj, 84 "cross-compartment wrapper target"); 85 MOZ_ASSERT(obj == prior); 86 } 87 } 88 } 89 } 90 91 /*** Cycle Collector Helpers ************************************************/ 92 93 // This function is used by the Cycle Collector (CC) to trace through -- or in 94 // CC parlance, traverse -- a Shape. The CC does not care about Shapes, 95 // BaseShapes or PropMaps themselves, only things held live by them that can 96 // participate in cycles. 97 void gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape) { 98 shape->base()->traceChildren(trc); 99 100 // TODO: Trace symbols reachable from |shape|. Shapes can entrain symbols via 101 // their propmaps, and these can now participate in cycles. Not doing this 102 // means there are some cycles that the CC will not be able to collect. 103 } 104 105 /*** Traced Edge Printer ****************************************************/ 106 107 static size_t CountDecimalDigits(size_t num) { 108 size_t numDigits = 0; 109 do { 110 num /= 10; 111 numDigits++; 112 } while (num > 0); 113 114 return numDigits; 115 } 116 117 static const char* StringKindHeader(JSString* str) { 118 MOZ_ASSERT(str->isLinear()); 119 120 if (str->isAtom()) { 121 if (str->isPermanentAtom()) { 122 return "permanent atom: "; 123 } 124 return "atom: "; 125 } 126 127 if (str->isExtensible()) { 128 return "extensible: "; 129 } 130 131 if (str->isInline()) { 132 if (str->isFatInline()) { 133 return "fat inline: "; 134 } 135 return "inline: "; 136 } 137 138 if (str->isDependent()) { 139 return "dependent: "; 140 } 141 142 if (str->isExternal()) { 143 return "external: "; 144 } 145 146 return "linear: "; 147 } 148 149 void js::gc::GetTraceThingInfo(char* buf, size_t bufsize, void* thing, 150 JS::TraceKind kind, bool details) { 151 const char* name = nullptr; /* silence uninitialized warning */ 152 size_t n; 153 154 if (bufsize == 0) { 155 return; 156 } 157 158 switch (kind) { 159 case JS::TraceKind::BaseShape: 160 name = "base_shape"; 161 break; 162 163 case JS::TraceKind::GetterSetter: 164 name = "getter_setter"; 165 break; 166 167 case JS::TraceKind::PropMap: 168 name = "prop_map"; 169 break; 170 171 case JS::TraceKind::JitCode: 172 name = "jitcode"; 173 break; 174 175 case JS::TraceKind::Null: 176 name = "null_pointer"; 177 break; 178 179 case JS::TraceKind::Object: { 180 name = static_cast<JSObject*>(thing)->getClass()->name; 181 break; 182 } 183 184 case JS::TraceKind::RegExpShared: 185 name = "reg_exp_shared"; 186 break; 187 188 case JS::TraceKind::Scope: 189 name = "scope"; 190 break; 191 192 case JS::TraceKind::Script: 193 name = "script"; 194 break; 195 196 case JS::TraceKind::Shape: 197 name = "shape"; 198 break; 199 200 case JS::TraceKind::String: 201 name = ((JSString*)thing)->isDependent() ? "substring" : "string"; 202 break; 203 204 case JS::TraceKind::Symbol: 205 name = "symbol"; 206 break; 207 208 case JS::TraceKind::BigInt: 209 name = "BigInt"; 210 break; 211 212 default: 213 name = "INVALID"; 214 break; 215 } 216 217 n = strlen(name); 218 if (n > bufsize - 1) { 219 n = bufsize - 1; 220 } 221 js_memcpy(buf, name, n + 1); 222 buf += n; 223 bufsize -= n; 224 *buf = '\0'; 225 226 if (details && bufsize > 2) { 227 switch (kind) { 228 case JS::TraceKind::Object: { 229 JSObject* obj = (JSObject*)thing; 230 if (obj->is<JSFunction>()) { 231 JSFunction* fun = &obj->as<JSFunction>(); 232 if (fun->maybePartialDisplayAtom()) { 233 *buf++ = ' '; 234 bufsize--; 235 PutEscapedString(buf, bufsize, fun->maybePartialDisplayAtom(), 0); 236 } 237 } else { 238 snprintf(buf, bufsize, " <unknown object>"); 239 } 240 break; 241 } 242 243 case JS::TraceKind::Script: { 244 auto* script = static_cast<js::BaseScript*>(thing); 245 snprintf(buf, bufsize, " %s:%u", script->filename(), script->lineno()); 246 break; 247 } 248 249 case JS::TraceKind::String: { 250 *buf++ = ' '; 251 bufsize--; 252 JSString* str = (JSString*)thing; 253 254 if (str->isLinear()) { 255 const char* header = StringKindHeader(str); 256 bool willFit = str->length() + strlen("<length > ") + strlen(header) + 257 CountDecimalDigits(str->length()) < 258 bufsize; 259 260 n = snprintf(buf, bufsize, "<%slength %zu%s> ", header, str->length(), 261 willFit ? "" : " (truncated)"); 262 buf += n; 263 bufsize -= n; 264 265 PutEscapedString(buf, bufsize, &str->asLinear(), 0); 266 } else { 267 snprintf(buf, bufsize, "<rope: length %zu>", str->length()); 268 } 269 break; 270 } 271 272 case JS::TraceKind::Symbol: { 273 *buf++ = ' '; 274 bufsize--; 275 auto* sym = static_cast<JS::Symbol*>(thing); 276 if (JSAtom* desc = sym->description()) { 277 PutEscapedString(buf, bufsize, desc, 0); 278 } else { 279 snprintf(buf, bufsize, "<null>"); 280 } 281 break; 282 } 283 284 case JS::TraceKind::Scope: { 285 auto* scope = static_cast<js::Scope*>(thing); 286 snprintf(buf, bufsize, " %s", js::ScopeKindString(scope->kind())); 287 break; 288 } 289 290 default: 291 break; 292 } 293 } 294 buf[bufsize - 1] = '\0'; 295 } 296 297 JS::CallbackTracer::CallbackTracer(JSContext* cx, JS::TracerKind kind, 298 JS::TraceOptions options) 299 : CallbackTracer(cx->runtime(), kind, options) {}