Source code

Revision control

Other Tools

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/Marking-inl.h"
8
9
#include "mozilla/ArrayUtils.h"
10
#include "mozilla/DebugOnly.h"
11
#include "mozilla/IntegerRange.h"
12
#include "mozilla/ReentrancyGuard.h"
13
#include "mozilla/ScopeExit.h"
14
#include "mozilla/TypeTraits.h"
15
#include "mozilla/Unused.h"
16
17
#include <algorithm>
18
19
#include "jsfriendapi.h"
20
21
#include "builtin/ModuleObject.h"
22
#include "debugger/DebugAPI.h"
23
#include "gc/GCInternals.h"
24
#include "gc/Policy.h"
25
#include "jit/IonCode.h"
26
#include "js/GCTypeMacros.h" // JS_FOR_EACH_PUBLIC_{,TAGGED_}GC_POINTER_TYPE
27
#include "js/SliceBudget.h"
28
#include "util/DiagnosticAssertions.h"
29
#include "util/Memory.h"
30
#include "util/Poison.h"
31
#include "vm/ArgumentsObject.h"
32
#include "vm/ArrayObject.h"
33
#include "vm/BigIntType.h"
34
#include "vm/EnvironmentObject.h"
35
#include "vm/GeneratorObject.h"
36
#include "vm/RegExpShared.h"
37
#include "vm/Scope.h"
38
#include "vm/Shape.h"
39
#include "vm/SymbolType.h"
40
#include "vm/TypedArrayObject.h"
41
#include "wasm/WasmJS.h"
42
43
#include "gc/GC-inl.h"
44
#include "gc/Nursery-inl.h"
45
#include "gc/PrivateIterators-inl.h"
46
#include "gc/WeakMap-inl.h"
47
#include "gc/Zone-inl.h"
48
#include "vm/GeckoProfiler-inl.h"
49
#include "vm/NativeObject-inl.h"
50
#include "vm/Realm-inl.h"
51
#include "vm/StringType-inl.h"
52
53
using namespace js;
54
using namespace js::gc;
55
56
using JS::MapTypeToTraceKind;
57
58
using mozilla::DebugOnly;
59
using mozilla::IntegerRange;
60
using mozilla::IsSame;
61
using mozilla::PodCopy;
62
63
// [SMDOC] GC Tracing
64
//
65
// Tracing Overview
66
// ================
67
//
68
// Tracing, in this context, refers to an abstract visitation of some or all of
69
// the GC-controlled heap. The effect of tracing an edge of the graph depends
70
// on the subclass of the JSTracer on whose behalf we are tracing.
71
//
72
// Marking
73
// -------
74
//
75
// The primary JSTracer is the GCMarker. The marking tracer causes the target
76
// of each traversed edge to be marked black and the target edge's children to
77
// be marked either gray (in the gc algorithm sense) or immediately black.
78
//
79
// Callback
80
// --------
81
//
82
// The secondary JSTracer is the CallbackTracer. This simply invokes a callback
83
// on each edge in a child.
84
//
85
// The following is a rough outline of the general struture of the tracing
86
// internals.
87
//
88
/* clang-format off */
89
// //
90
// .---------. .---------. .--------------------------. .----------. //
91
// |TraceEdge| |TraceRoot| |TraceManuallyBarrieredEdge| ... |TraceRange| ... etc. //
92
// '---------' '---------' '--------------------------' '----------' //
93
// \ \ / / //
94
// \ \ .-----------------. / / //
95
// o------------->o-|TraceEdgeInternal|-o<----------------------o //
96
// '-----------------' //
97
// / \ //
98
// / \ //
99
// .---------. .----------. .-----------------. //
100
// |DoMarking| |DoCallback|-------> |<JSTraceCallback>|-----------> //
101
// '---------' '----------' '-----------------' //
102
// | //
103
// | //
104
// .-----------. //
105
// o------------->|traverse(T)| . //
106
// /_\ '-----------' ' . //
107
// | . . ' . //
108
// | . . ' . //
109
// | . . ' . //
110
// | .--------------. .--------------. ' . .-----------------------. //
111
// | |markAndScan(T)| |markAndPush(T)| ' - |markAndTraceChildren(T)| //
112
// | '--------------' '--------------' '-----------------------' //
113
// | | \ | //
114
// | | \ | //
115
// | .----------------------. .----------------. .------------------. //
116
// | |eagerlyMarkChildren(T)| |pushMarkStackTop|<===Oo |T::traceChildren()|--> //
117
// | '----------------------' '----------------' || '------------------' //
118
// | | || || //
119
// | | || || //
120
// | | || || //
121
// o<-----------------o<========================OO============Oo //
122
// //
123
// //
124
// Legend: //
125
// ------ Direct calls //
126
// . . . Static dispatch //
127
// ====== Dispatch through a manual stack. //
128
// //
129
/* clang-format on */
130
131
/*** Tracing Invariants *****************************************************/
132
133
#if defined(DEBUG)
134
template <typename T>
135
static inline bool IsThingPoisoned(T* thing) {
136
const uint8_t poisonBytes[] = {
137
JS_FRESH_NURSERY_PATTERN, JS_SWEPT_NURSERY_PATTERN,
138
JS_ALLOCATED_NURSERY_PATTERN, JS_FRESH_TENURED_PATTERN,
139
JS_MOVED_TENURED_PATTERN, JS_SWEPT_TENURED_PATTERN,
140
JS_ALLOCATED_TENURED_PATTERN, JS_FREED_HEAP_PTR_PATTERN,
141
JS_FREED_CHUNK_PATTERN, JS_FREED_ARENA_PATTERN,
142
JS_SWEPT_TI_PATTERN, JS_SWEPT_CODE_PATTERN,
143
JS_RESET_VALUE_PATTERN, JS_POISONED_JSSCRIPT_DATA_PATTERN,
144
JS_OOB_PARSE_NODE_PATTERN, JS_LIFO_UNDEFINED_PATTERN,
145
JS_LIFO_UNINITIALIZED_PATTERN,
146
};
147
const int numPoisonBytes = sizeof(poisonBytes) / sizeof(poisonBytes[0]);
148
uint32_t* p =
149
reinterpret_cast<uint32_t*>(reinterpret_cast<FreeSpan*>(thing) + 1);
150
// Note: all free patterns are odd to make the common, not-poisoned case a
151
// single test.
152
if ((*p & 1) == 0) {
153
return false;
154
}
155
for (int i = 0; i < numPoisonBytes; ++i) {
156
const uint8_t pb = poisonBytes[i];
157
const uint32_t pw = pb | (pb << 8) | (pb << 16) | (pb << 24);
158
if (*p == pw) {
159
return true;
160
}
161
}
162
return false;
163
}
164
165
bool js::IsTracerKind(JSTracer* trc, JS::CallbackTracer::TracerKind kind) {
166
return trc->isCallbackTracer() &&
167
trc->asCallbackTracer()->getTracerKind() == kind;
168
}
169
#endif
170
171
bool ThingIsPermanentAtomOrWellKnownSymbol(JSString* str) {
172
return str->isPermanentAtom();
173
}
174
bool ThingIsPermanentAtomOrWellKnownSymbol(JSLinearString* str) {
175
return str->isPermanentAtom();
176
}
177
bool ThingIsPermanentAtomOrWellKnownSymbol(JSAtom* atom) {
178
return atom->isPermanent();
179
}
180
bool ThingIsPermanentAtomOrWellKnownSymbol(PropertyName* name) {
181
return name->isPermanent();
182
}
183
bool ThingIsPermanentAtomOrWellKnownSymbol(JS::Symbol* sym) {
184
return sym->isWellKnownSymbol();
185
}
186
187
template <typename T>
188
static inline bool IsOwnedByOtherRuntime(JSRuntime* rt, T thing) {
189
bool other = thing->runtimeFromAnyThread() != rt;
190
MOZ_ASSERT_IF(other, ThingIsPermanentAtomOrWellKnownSymbol(thing) ||
191
thing->zoneFromAnyThread()->isSelfHostingZone());
192
return other;
193
}
194
195
template <typename T>
196
void js::CheckTracedThing(JSTracer* trc, T* thing) {
197
#ifdef DEBUG
198
MOZ_ASSERT(trc);
199
MOZ_ASSERT(thing);
200
201
if (!trc->checkEdges()) {
202
return;
203
}
204
205
if (IsForwarded(thing)) {
206
MOZ_ASSERT(IsTracerKind(trc, JS::CallbackTracer::TracerKind::Moving) ||
207
trc->isTenuringTracer());
208
thing = Forwarded(thing);
209
}
210
211
/* This function uses data that's not available in the nursery. */
212
if (IsInsideNursery(thing)) {
213
return;
214
}
215
216
/*
217
* Permanent atoms and things in the self-hosting zone are not associated
218
* with this runtime, but will be ignored during marking.
219
*/
220
if (IsOwnedByOtherRuntime(trc->runtime(), thing)) {
221
return;
222
}
223
224
Zone* zone = thing->zoneFromAnyThread();
225
JSRuntime* rt = trc->runtime();
226
MOZ_ASSERT(zone->runtimeFromAnyThread() == rt);
227
228
bool isGcMarkingTracer = trc->isMarkingTracer();
229
bool isUnmarkGrayTracer =
230
IsTracerKind(trc, JS::CallbackTracer::TracerKind::UnmarkGray);
231
bool isClearEdgesTracer =
232
IsTracerKind(trc, JS::CallbackTracer::TracerKind::ClearEdges);
233
234
if (TlsContext.get()->isMainThreadContext()) {
235
// If we're on the main thread we must have access to the runtime and zone.
236
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
237
MOZ_ASSERT(CurrentThreadCanAccessZone(zone));
238
} else {
239
MOZ_ASSERT(
240
isGcMarkingTracer || isUnmarkGrayTracer || isClearEdgesTracer ||
241
IsTracerKind(trc, JS::CallbackTracer::TracerKind::Moving) ||
242
IsTracerKind(trc, JS::CallbackTracer::TracerKind::GrayBuffering) ||
243
IsTracerKind(trc, JS::CallbackTracer::TracerKind::Sweeping));
244
MOZ_ASSERT_IF(!isClearEdgesTracer, CurrentThreadIsPerformingGC());
245
}
246
247
// It shouldn't be possible to trace into zones used by helper threads, except
248
// for use of ClearEdgesTracer by GCManagedDeletePolicy on a helper thread.
249
MOZ_ASSERT_IF(!isClearEdgesTracer, !zone->usedByHelperThread());
250
251
MOZ_ASSERT(thing->isAligned());
252
MOZ_ASSERT(
253
MapTypeToTraceKind<typename mozilla::RemovePointer<T>::Type>::kind ==
254
thing->getTraceKind());
255
256
if (isGcMarkingTracer) {
257
GCMarker* gcMarker = GCMarker::fromTracer(trc);
258
MOZ_ASSERT(zone->shouldMarkInZone());
259
260
MOZ_ASSERT_IF(gcMarker->shouldCheckCompartments(),
261
zone->isCollectingFromAnyThread() || zone->isAtomsZone());
262
263
MOZ_ASSERT_IF(gcMarker->markColor() == MarkColor::Gray,
264
!zone->isGCMarkingBlackOnly() || zone->isAtomsZone());
265
266
MOZ_ASSERT(!(zone->isGCSweeping() || zone->isGCFinished() ||
267
zone->isGCCompacting()));
268
269
// Check that we don't stray from the current compartment and zone without
270
// using TraceCrossCompartmentEdge.
271
Compartment* comp = thing->maybeCompartment();
272
MOZ_ASSERT_IF(gcMarker->tracingCompartment && comp,
273
gcMarker->tracingCompartment == comp);
274
MOZ_ASSERT_IF(gcMarker->tracingZone,
275
gcMarker->tracingZone == zone || zone->isAtomsZone());
276
}
277
278
/*
279
* Try to assert that the thing is allocated.
280
*
281
* We would like to assert that the thing is not in the free list, but this
282
* check is very slow. Instead we check whether the thing has been poisoned:
283
* if it has not then we assume it is allocated, but if it has then it is
284
* either free or uninitialized in which case we check the free list.
285
*
286
* Further complications are that background sweeping may be running and
287
* concurrently modifiying the free list and that tracing is done off
288
* thread during compacting GC and reading the contents of the thing by
289
* IsThingPoisoned would be racy in this case.
290
*/
291
MOZ_ASSERT_IF(JS::RuntimeHeapIsBusy() && !zone->isGCSweeping() &&
292
!zone->isGCFinished() && !zone->isGCCompacting(),
293
!IsThingPoisoned(thing) ||
294
!InFreeList(thing->asTenured().arena(), thing));
295
#endif
296
}
297
298
template <typename T>
299
void js::CheckTracedThing(JSTracer* trc, T thing) {
300
ApplyGCThingTyped(thing, [](auto t) { CheckTracedThing(t); });
301
}
302
303
namespace js {
304
#define IMPL_CHECK_TRACED_THING(_, type, _1, _2) \
305
template void CheckTracedThing<type>(JSTracer*, type*);
306
JS_FOR_EACH_TRACEKIND(IMPL_CHECK_TRACED_THING);
307
#undef IMPL_CHECK_TRACED_THING
308
} // namespace js
309
310
static bool UnmarkGrayGCThing(JSRuntime* rt, JS::GCCellPtr thing);
311
312
static inline bool ShouldMarkCrossCompartment(GCMarker* marker, JSObject* src,
313
Cell* dstCell) {
314
MarkColor color = marker->markColor();
315
316
if (!dstCell->isTenured()) {
317
MOZ_ASSERT(color == MarkColor::Black);
318
return false;
319
}
320
TenuredCell& dst = dstCell->asTenured();
321
322
JS::Zone* dstZone = dst.zone();
323
if (!src->zone()->isGCMarking() && !dstZone->isGCMarking()) {
324
return false;
325
}
326
327
if (color == MarkColor::Black) {
328
// Check our sweep groups are correct: we should never have to
329
// mark something in a zone that we have started sweeping.
330
MOZ_ASSERT_IF(!dst.isMarkedBlack(), !dstZone->isGCSweeping());
331
332
/*
333
* Having black->gray edges violates our promise to the cycle collector so
334
* we ensure that gray things we encounter when marking black end up getting
335
* marked black.
336
*
337
* This can happen for two reasons:
338
*
339
* 1) If we're collecting a compartment and it has an edge to an uncollected
340
* compartment it's possible that the source and destination of the
341
* cross-compartment edge should be gray, but the source was marked black by
342
* the write barrier.
343
*
344
* 2) If we yield during gray marking and the write barrier marks a gray
345
* thing black.
346
*
347
* We handle the first case before returning whereas the second case happens
348
* as part of normal marking.
349
*/
350
if (dst.isMarkedGray() && !dstZone->isGCMarking()) {
351
UnmarkGrayGCThing(marker->runtime(),
352
JS::GCCellPtr(&dst, dst.getTraceKind()));
353
return false;
354
}
355
356
return dstZone->isGCMarking();
357
} else {
358
// Check our sweep groups are correct as above.
359
MOZ_ASSERT_IF(!dst.isMarkedAny(), !dstZone->isGCSweeping());
360
361
if (dstZone->isGCMarkingBlackOnly()) {
362
/*
363
* The destination compartment is being not being marked gray now,
364
* but it will be later, so record the cell so it can be marked gray
365
* at the appropriate time.
366
*/
367
if (!dst.isMarkedAny()) {
368
DelayCrossCompartmentGrayMarking(src);
369
}
370
return false;
371
}
372
373
return dstZone->isGCMarkingBlackAndGray();
374
}
375
}
376
377
static bool ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src,
378
Cell* dstCell) {
379
if (!trc->isMarkingTracer()) {
380
return true;
381
}
382
383
return ShouldMarkCrossCompartment(GCMarker::fromTracer(trc), src, dstCell);
384
}
385
386
static bool ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src,
387
const Value& val) {
388
return val.isGCThing() &&
389
ShouldTraceCrossCompartment(trc, src, val.toGCThing());
390
}
391
392
static void AssertShouldMarkInZone(Cell* thing) {
393
MOZ_ASSERT(thing->asTenured().zone()->shouldMarkInZone());
394
}
395
396
static void AssertShouldMarkInZone(JSString* str) {
397
#ifdef DEBUG
398
Zone* zone = str->zone();
399
MOZ_ASSERT(zone->shouldMarkInZone() || zone->isAtomsZone());
400
#endif
401
}
402
403
static void AssertShouldMarkInZone(JS::Symbol* sym) {
404
#ifdef DEBUG
405
Zone* zone = sym->asTenured().zone();
406
MOZ_ASSERT(zone->shouldMarkInZone() || zone->isAtomsZone());
407
#endif
408
}
409
410
#ifdef DEBUG
411
void js::gc::AssertRootMarkingPhase(JSTracer* trc) {
412
MOZ_ASSERT_IF(trc->isMarkingTracer(),
413
trc->runtime()->gc.state() == State::NotActive ||
414
trc->runtime()->gc.state() == State::MarkRoots);
415
}
416
#endif
417
418
/*** Tracing Interface ******************************************************/
419
420
template <typename T>
421
bool DoCallback(JS::CallbackTracer* trc, T** thingp, const char* name);
422
template <typename T>
423
bool DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name);
424
template <typename T>
425
void DoMarking(GCMarker* gcmarker, T* thing);
426
template <typename T>
427
void DoMarking(GCMarker* gcmarker, const T& thing);
428
429
template <typename T>
430
static void TraceExternalEdgeHelper(JSTracer* trc, T* thingp,
431
const char* name) {
432
MOZ_ASSERT(InternalBarrierMethods<T>::isMarkable(*thingp));
433
TraceEdgeInternal(trc, ConvertToBase(thingp), name);
434
}
435
436
JS_PUBLIC_API void js::UnsafeTraceManuallyBarrieredEdge(JSTracer* trc,
437
JSObject** thingp,
438
const char* name) {
439
TraceEdgeInternal(trc, ConvertToBase(thingp), name);
440
}
441
442
template <typename T>
443
static void UnsafeTraceRootHelper(JSTracer* trc, T* thingp, const char* name) {
444
MOZ_ASSERT(thingp);
445
js::TraceNullableRoot(trc, thingp, name);
446
}
447
448
namespace js {
449
class AbstractGeneratorObject;
450
class SavedFrame;
451
} // namespace js
452
453
#define DEFINE_TRACE_EXTERNAL_EDGE_FUNCTION(type) \
454
JS_PUBLIC_API void js::gc::TraceExternalEdge(JSTracer* trc, type* thingp, \
455
const char* name) { \
456
TraceExternalEdgeHelper(trc, thingp, name); \
457
}
458
459
// Define TraceExternalEdge for each public GC pointer type.
460
JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(DEFINE_TRACE_EXTERNAL_EDGE_FUNCTION)
461
JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DEFINE_TRACE_EXTERNAL_EDGE_FUNCTION)
462
463
// Also, for the moment, define TraceExternalEdge for internal GC pointer types.
464
DEFINE_TRACE_EXTERNAL_EDGE_FUNCTION(AbstractGeneratorObject*)
465
DEFINE_TRACE_EXTERNAL_EDGE_FUNCTION(SavedFrame*)
466
467
#undef DEFINE_TRACE_EXTERNAL_EDGE_FUNCTION
468
469
#define DEFINE_UNSAFE_TRACE_ROOT_FUNCTION(type) \
470
JS_PUBLIC_API void JS::UnsafeTraceRoot(JSTracer* trc, type* thingp, \
471
const char* name) { \
472
UnsafeTraceRootHelper(trc, thingp, name); \
473
}
474
475
// Define UnsafeTraceRoot for each public GC pointer type.
476
JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(DEFINE_UNSAFE_TRACE_ROOT_FUNCTION)
477
JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DEFINE_UNSAFE_TRACE_ROOT_FUNCTION)
478
479
// Also, for the moment, define UnsafeTraceRoot for internal GC pointer types.
480
DEFINE_UNSAFE_TRACE_ROOT_FUNCTION(AbstractGeneratorObject*)
481
DEFINE_UNSAFE_TRACE_ROOT_FUNCTION(SavedFrame*)
482
483
#undef DEFINE_UNSAFE_TRACE_ROOT_FUNCTION
484
485
namespace js {
486
namespace gc {
487
488
#define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type) \
489
template bool TraceEdgeInternal<type>(JSTracer*, type*, const char*); \
490
template void TraceRangeInternal<type>(JSTracer*, size_t len, type*, \
491
const char*);
492
493
#define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND(_1, type, _2, _3) \
494
INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type*)
495
496
JS_FOR_EACH_TRACEKIND(INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND)
497
JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_INTERNAL_TRACE_FUNCTIONS)
498
499
#undef INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND
500
#undef INSTANTIATE_INTERNAL_TRACE_FUNCTIONS
501
502
} // namespace gc
503
} // namespace js
504
505
// In debug builds, makes a note of the current compartment before calling a
506
// trace hook or traceChildren() method on a GC thing.
507
class MOZ_RAII AutoSetTracingSource {
508
#ifdef DEBUG
509
GCMarker* marker = nullptr;
510
#endif
511
512
public:
513
template <typename T>
514
AutoSetTracingSource(JSTracer* trc, T* thing) {
515
#ifdef DEBUG
516
if (trc->isMarkingTracer() && thing) {
517
marker = GCMarker::fromTracer(trc);
518
MOZ_ASSERT(!marker->tracingZone);
519
marker->tracingZone = thing->asTenured().zone();
520
MOZ_ASSERT(!marker->tracingCompartment);
521
marker->tracingCompartment = thing->maybeCompartment();
522
}
523
#endif
524
}
525
526
~AutoSetTracingSource() {
527
#ifdef DEBUG
528
if (marker) {
529
marker->tracingZone = nullptr;
530
marker->tracingCompartment = nullptr;
531
}
532
#endif
533
}
534
};
535
536
// In debug builds, clear the trace hook compartment. This happens
537
// after the trace hook has called back into one of our trace APIs and we've
538
// checked the traced thing.
539
class MOZ_RAII AutoClearTracingSource {
540
#ifdef DEBUG
541
GCMarker* marker = nullptr;
542
JS::Zone* prevZone = nullptr;
543
Compartment* prevCompartment = nullptr;
544
#endif
545
546
public:
547
explicit AutoClearTracingSource(JSTracer* trc) {
548
#ifdef DEBUG
549
if (trc->isMarkingTracer()) {
550
marker = GCMarker::fromTracer(trc);
551
prevZone = marker->tracingZone;
552
marker->tracingZone = nullptr;
553
prevCompartment = marker->tracingCompartment;
554
marker->tracingCompartment = nullptr;
555
}
556
#endif
557
}
558
559
~AutoClearTracingSource() {
560
#ifdef DEBUG
561
if (marker) {
562
marker->tracingZone = prevZone;
563
marker->tracingCompartment = prevCompartment;
564
}
565
#endif
566
}
567
};
568
569
template <typename T>
570
void js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc,
571
JSObject* src, T* dst,
572
const char* name) {
573
// Clear expected compartment for cross-compartment edge.
574
AutoClearTracingSource acts(trc);
575
576
if (ShouldTraceCrossCompartment(trc, src, *dst)) {
577
TraceEdgeInternal(trc, dst, name);
578
}
579
}
580
template void js::TraceManuallyBarrieredCrossCompartmentEdge<Value>(
581
JSTracer*, JSObject*, Value*, const char*);
582
template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSObject*>(
583
JSTracer*, JSObject*, JSObject**, const char*);
584
template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSScript*>(
585
JSTracer*, JSObject*, JSScript**, const char*);
586
template void js::TraceManuallyBarrieredCrossCompartmentEdge<LazyScript*>(
587
JSTracer*, JSObject*, LazyScript**, const char*);
588
589
template <typename T>
590
void js::TraceWeakMapKeyEdgeInternal(JSTracer* trc, Zone* weakMapZone,
591
T** thingp, const char* name) {
592
// We can't use ShouldTraceCrossCompartment here because that assumes the
593
// source of the edge is a CCW object which could be used to delay gray
594
// marking. Instead, assert that the weak map zone is in the same marking
595
// state as the target thing's zone and therefore we can go ahead and mark it.
596
#ifdef DEBUG
597
auto thing = *thingp;
598
if (trc->isMarkingTracer()) {
599
MOZ_ASSERT(weakMapZone->isGCMarking());
600
MOZ_ASSERT(weakMapZone->gcState() == thing->zone()->gcState());
601
}
602
#endif
603
604
// Clear expected compartment for cross-compartment edge.
605
AutoClearTracingSource acts(trc);
606
607
TraceEdgeInternal(trc, thingp, name);
608
}
609
610
template void js::TraceWeakMapKeyEdgeInternal<JSObject>(JSTracer*, Zone*,
611
JSObject**,
612
const char*);
613
template void js::TraceWeakMapKeyEdgeInternal<JSScript>(JSTracer*, Zone*,
614
JSScript**,
615
const char*);
616
template void js::TraceWeakMapKeyEdgeInternal<LazyScript>(JSTracer*, Zone*,
617
LazyScript**,
618
const char*);
619
620
template <typename T>
621
void js::TraceProcessGlobalRoot(JSTracer* trc, T* thing, const char* name) {
622
AssertRootMarkingPhase(trc);
623
MOZ_ASSERT(ThingIsPermanentAtomOrWellKnownSymbol(thing));
624
625
// We have to mark permanent atoms and well-known symbols through a special
626
// method because the default DoMarking implementation automatically skips
627
// them. Fortunately, atoms (permanent and non) cannot refer to other GC
628
// things so they do not need to go through the mark stack and may simply
629
// be marked directly. Moreover, well-known symbols can refer only to
630
// permanent atoms, so likewise require no subsquent marking.
631
CheckTracedThing(trc, *ConvertToBase(&thing));
632
AutoClearTracingSource acts(trc);
633
if (trc->isMarkingTracer()) {
634
thing->asTenured().markIfUnmarked(gc::MarkColor::Black);
635
} else {
636
DoCallback(trc->asCallbackTracer(), ConvertToBase(&thing), name);
637
}
638
}
639
template void js::TraceProcessGlobalRoot<JSAtom>(JSTracer*, JSAtom*,
640
const char*);
641
template void js::TraceProcessGlobalRoot<JS::Symbol>(JSTracer*, JS::Symbol*,
642
const char*);
643
644
static Cell* TraceGenericPointerRootAndType(JSTracer* trc, Cell* thing,
645
JS::TraceKind kind,
646
const char* name) {
647
return MapGCThingTyped(thing, kind, [trc, name](auto t) -> Cell* {
648
TraceRoot(trc, &t, name);
649
return t;
650
});
651
}
652
653
void js::TraceGenericPointerRoot(JSTracer* trc, Cell** thingp,
654
const char* name) {
655
MOZ_ASSERT(thingp);
656
Cell* thing = *thingp;
657
if (!thing) {
658
return;
659
}
660
661
Cell* traced =
662
TraceGenericPointerRootAndType(trc, thing, thing->getTraceKind(), name);
663
if (traced != thing) {
664
*thingp = traced;
665
}
666
}
667
668
void js::TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, Cell** thingp,
669
const char* name) {
670
MOZ_ASSERT(thingp);
671
Cell* thing = *thingp;
672
if (!*thingp) {
673
return;
674
}
675
676
auto traced = MapGCThingTyped(thing, thing->getTraceKind(),
677
[trc, name](auto t) -> Cell* {
678
TraceManuallyBarrieredEdge(trc, &t, name);
679
return t;
680
});
681
if (traced != thing) {
682
*thingp = traced;
683
}
684
}
685
686
void js::TraceGCCellPtrRoot(JSTracer* trc, JS::GCCellPtr* thingp,
687
const char* name) {
688
Cell* thing = thingp->asCell();
689
if (!thing) {
690
return;
691
}
692
693
Cell* traced =
694
TraceGenericPointerRootAndType(trc, thing, thingp->kind(), name);
695
696
if (!traced) {
697
*thingp = JS::GCCellPtr();
698
} else if (traced != thingp->asCell()) {
699
*thingp = JS::GCCellPtr(traced, thingp->kind());
700
}
701
}
702
703
// This method is responsible for dynamic dispatch to the real tracer
704
// implementation. Consider replacing this choke point with virtual dispatch:
705
// a sufficiently smart C++ compiler may be able to devirtualize some paths.
706
template <typename T>
707
bool js::gc::TraceEdgeInternal(JSTracer* trc, T* thingp, const char* name) {
708
#define IS_SAME_TYPE_OR(name, type, _, _1) mozilla::IsSame<type*, T>::value ||
709
static_assert(JS_FOR_EACH_TRACEKIND(IS_SAME_TYPE_OR)
710
mozilla::IsSame<T, JS::Value>::value ||
711
mozilla::IsSame<T, jsid>::value ||
712
mozilla::IsSame<T, TaggedProto>::value,
713
"Only the base cell layout types are allowed into "
714
"marking/tracing internals");
715
#undef IS_SAME_TYPE_OR
716
if (trc->isMarkingTracer()) {
717
DoMarking(GCMarker::fromTracer(trc), *thingp);
718
return true;
719
}
720
if (trc->isTenuringTracer()) {
721
static_cast<TenuringTracer*>(trc)->traverse(thingp);
722
return true;
723
}
724
MOZ_ASSERT(trc->isCallbackTracer());
725
return DoCallback(trc->asCallbackTracer(), thingp, name);
726
}
727
728
template <typename T>
729
void js::gc::TraceRangeInternal(JSTracer* trc, size_t len, T* vec,
730
const char* name) {
731
JS::AutoTracingIndex index(trc);
732
for (auto i : IntegerRange(len)) {
733
if (InternalBarrierMethods<T>::isMarkable(vec[i])) {
734
TraceEdgeInternal(trc, &vec[i], name);
735
}
736
++index;
737
}
738
}
739
740
/*** GC Marking Interface ***************************************************/
741
742
namespace js {
743
744
typedef bool HasNoImplicitEdgesType;
745
746
template <typename T>
747
struct ImplicitEdgeHolderType {
748
typedef HasNoImplicitEdgesType Type;
749
};
750
751
// For now, we only handle JSObject* and JSScript* keys, but the linear time
752
// algorithm can be easily extended by adding in more types here, then making
753
// GCMarker::traverse<T> call markPotentialEphemeronKey.
754
template <>
755
struct ImplicitEdgeHolderType<JSObject*> {
756
typedef JSObject* Type;
757
};
758
759
template <>
760
struct ImplicitEdgeHolderType<JSScript*> {
761
typedef JSScript* Type;
762
};
763
764
template <>
765
struct ImplicitEdgeHolderType<LazyScript*> {
766
typedef LazyScript* Type;
767
};
768
769
void GCMarker::markEphemeronValues(gc::Cell* markedCell,
770
WeakEntryVector& values) {
771
DebugOnly<size_t> initialLen = values.length();
772
773
for (const auto& markable : values) {
774
markable.weakmap->markKey(this, markedCell, markable.key);
775
}
776
777
// The vector should not be appended to during iteration because the key is
778
// already marked, and even in cases where we have a multipart key, we
779
// should only be inserting entries for the unmarked portions.
780
MOZ_ASSERT(values.length() == initialLen);
781
}
782
783
template <typename T>
784
void GCMarker::markImplicitEdgesHelper(T markedThing) {
785
if (!isWeakMarking()) {
786
return;
787
}
788
789
Zone* zone = markedThing->asTenured().zone();
790
MOZ_ASSERT(zone->isGCMarking());
791
MOZ_ASSERT(!zone->isGCSweeping());
792
793
auto p = zone->gcWeakKeys().get(markedThing);
794
if (!p) {
795
return;
796
}
797
WeakEntryVector& markables = p->value;
798
799
markEphemeronValues(markedThing, markables);
800
markables.clear(); // If key address is reused, it should do nothing
801
}
802
803
template <>
804
void GCMarker::markImplicitEdgesHelper(HasNoImplicitEdgesType) {}
805
806
template <typename T>
807
void GCMarker::markImplicitEdges(T* thing) {
808
markImplicitEdgesHelper<typename ImplicitEdgeHolderType<T*>::Type>(thing);
809
}
810
811
template void GCMarker::markImplicitEdges(JSObject*);
812
template void GCMarker::markImplicitEdges(JSScript*);
813
template void GCMarker::markImplicitEdges(LazyScript*);
814
815
} // namespace js
816
817
template <typename T>
818
static inline bool ShouldMark(GCMarker* gcmarker, T thing) {
819
// Don't trace things that are owned by another runtime.
820
if (IsOwnedByOtherRuntime(gcmarker->runtime(), thing)) {
821
return false;
822
}
823
824
// Don't mark things outside a zone if we are in a per-zone GC.
825
return thing->zone()->shouldMarkInZone();
826
}
827
828
template <>
829
bool ShouldMark<JSObject*>(GCMarker* gcmarker, JSObject* obj) {
830
// Don't trace things that are owned by another runtime.
831
if (IsOwnedByOtherRuntime(gcmarker->runtime(), obj)) {
832
return false;
833
}
834
835
// We may mark a Nursery thing outside the context of the
836
// MinorCollectionTracer because of a pre-barrier. The pre-barrier is not
837
// needed in this case because we perform a minor collection before each
838
// incremental slice.
839
if (IsInsideNursery(obj)) {
840
return false;
841
}
842
843
// Don't mark things outside a zone if we are in a per-zone GC. It is
844
// faster to check our own arena, which we can do since we know that
845
// the object is tenured.
846
return obj->asTenured().zone()->shouldMarkInZone();
847
}
848
849
// JSStrings can also be in the nursery. See ShouldMark<JSObject*> for comments.
850
template <>
851
bool ShouldMark<JSString*>(GCMarker* gcmarker, JSString* str) {
852
if (IsOwnedByOtherRuntime(gcmarker->runtime(), str)) {
853
return false;
854
}
855
if (IsInsideNursery(str)) {
856
return false;
857
}
858
return str->asTenured().zone()->shouldMarkInZone();
859
}
860
861
// BigInts can also be in the nursery. See ShouldMark<JSObject*> for comments.
862
template <>
863
bool ShouldMark<JS::BigInt*>(GCMarker* gcmarker, JS::BigInt* bi) {
864
if (IsOwnedByOtherRuntime(gcmarker->runtime(), bi)) {
865
return false;
866
}
867
if (IsInsideNursery(bi)) {
868
return false;
869
}
870
return bi->asTenured().zone()->shouldMarkInZone();
871
}
872
873
template <typename T>
874
void DoMarking(GCMarker* gcmarker, T* thing) {
875
// Do per-type marking precondition checks.
876
if (!ShouldMark(gcmarker, thing)) {
877
return;
878
}
879
880
CheckTracedThing(gcmarker, thing);
881
AutoClearTracingSource acts(gcmarker);
882
gcmarker->traverse(thing);
883
884
// Mark the compartment as live.
885
SetMaybeAliveFlag(thing);
886
}
887
888
template <typename T>
889
void DoMarking(GCMarker* gcmarker, const T& thing) {
890
ApplyGCThingTyped(thing, [gcmarker](auto t) { DoMarking(gcmarker, t); });
891
}
892
893
JS_PUBLIC_API void js::gc::PerformIncrementalReadBarrier(JS::GCCellPtr thing) {
894
// Optimized marking for read barriers. This is called from
895
// ExposeGCThingToActiveJS which has already checked the prerequisites for
896
// performing a read barrier. This means we can skip a bunch of checks and
897
// call info the tracer directly.
898
899
MOZ_ASSERT(thing);
900
MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting());
901
902
TenuredCell* cell = &thing.asCell()->asTenured();
903
Zone* zone = cell->zone();
904
MOZ_ASSERT(zone->needsIncrementalBarrier());
905
906
// Skip disptaching on known tracer type.
907
GCMarker* gcmarker = GCMarker::fromTracer(zone->barrierTracer());
908
909
// Mark the argument, as DoMarking above.
910
ApplyGCThingTyped(thing, [gcmarker](auto thing) {
911
MOZ_ASSERT(ShouldMark(gcmarker, thing));
912
CheckTracedThing(gcmarker, thing);
913
AutoClearTracingSource acts(gcmarker);
914
gcmarker->traverse(thing);
915
});
916
}
917
918
// The simplest traversal calls out to the fully generic traceChildren function
919
// to visit the child edges. In the absence of other traversal mechanisms, this
920
// function will rapidly grow the stack past its bounds and crash the process.
921
// Thus, this generic tracing should only be used in cases where subsequent
922
// tracing will not recurse.
923
template <typename T>
924
void js::GCMarker::markAndTraceChildren(T* thing) {
925
if (ThingIsPermanentAtomOrWellKnownSymbol(thing)) {
926
return;
927
}
928
if (mark(thing)) {
929
AutoSetTracingSource asts(this, thing);
930
thing->traceChildren(this);
931
}
932
}
933
namespace js {
934
template <>
935
void GCMarker::traverse(BaseShape* thing) {
936
markAndTraceChildren(thing);
937
}
938
template <>
939
void GCMarker::traverse(JS::Symbol* thing) {
940
markAndTraceChildren(thing);
941
}
942
template <>
943
void GCMarker::traverse(JS::BigInt* thing) {
944
markAndTraceChildren(thing);
945
}
946
template <>
947
void GCMarker::traverse(RegExpShared* thing) {
948
markAndTraceChildren(thing);
949
}
950
} // namespace js
951
952
// Strings, LazyScripts, Shapes, and Scopes are extremely common, but have
953
// simple patterns of recursion. We traverse trees of these edges immediately,
954
// with aggressive, manual inlining, implemented by eagerlyTraceChildren.
955
template <typename T>
956
void js::GCMarker::markAndScan(T* thing) {
957
if (ThingIsPermanentAtomOrWellKnownSymbol(thing)) {
958
return;
959
}
960
if (mark(thing)) {
961
eagerlyMarkChildren(thing);
962
}
963
}
964
namespace js {
965
template <>
966
void GCMarker::traverse(JSString* thing) {
967
markAndScan(thing);
968
}
969
template <>
970
void GCMarker::traverse(LazyScript* thing) {
971
markAndScan(thing);
972
}
973
template <>
974
void GCMarker::traverse(Shape* thing) {
975
markAndScan(thing);
976
}
977
template <>
978
void GCMarker::traverse(js::Scope* thing) {
979
markAndScan(thing);
980
}
981
} // namespace js
982
983
// Object and ObjectGroup are extremely common and can contain arbitrarily
984
// nested graphs, so are not trivially inlined. In this case we use a mark
985
// stack to control recursion. JitCode shares none of these properties, but is
986
// included for historical reasons. JSScript normally cannot recurse, but may
987
// be used as a weakmap key and thereby recurse into weakmapped values.
988
template <typename T>
989
void js::GCMarker::markAndPush(T* thing) {
990
if (!mark(thing)) {
991
return;
992
}
993
pushTaggedPtr(thing);
994
}
995
namespace js {
996
template <>
997
void GCMarker::traverse(JSObject* thing) {
998
markAndPush(thing);
999
}
1000
template <>
1001
void GCMarker::traverse(ObjectGroup* thing) {
1002
markAndPush(thing);
1003
}
1004
template <>
1005
void GCMarker::traverse(jit::JitCode* thing) {
1006
markAndPush(thing);
1007
}
1008
template <>
1009
void GCMarker::traverse(JSScript* thing) {
1010
markAndPush(thing);
1011
}
1012
} // namespace js
1013
1014
namespace js {
1015
template <>
1016
void GCMarker::traverse(AccessorShape* thing) {
1017
MOZ_CRASH("AccessorShape must be marked as a Shape");
1018
}
1019
} // namespace js
1020
1021
template <typename S, typename T>
1022
static void CheckTraversedEdge(S source, T* target) {
1023
// Atoms and Symbols do not have or mark their internal pointers,
1024
// respectively.
1025
MOZ_ASSERT(!ThingIsPermanentAtomOrWellKnownSymbol(source));
1026
1027
// The Zones must match, unless the target is an atom.
1028
MOZ_ASSERT_IF(
1029
!ThingIsPermanentAtomOrWellKnownSymbol(target),
1030
target->zone()->isAtomsZone() || target->zone() == source->zone());
1031
1032
// If we are marking an atom, that atom must be marked in the source zone's
1033
// atom bitmap.
1034
MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(target) &&
1035
target->zone()->isAtomsZone() &&
1036
!source->zone()->isAtomsZone(),
1037
target->runtimeFromAnyThread()->gc.atomMarking.atomIsMarked(
1038
source->zone(), reinterpret_cast<TenuredCell*>(target)));
1039
1040
// Atoms and Symbols do not have access to a compartment pointer, or we'd need
1041
// to adjust the subsequent check to catch that case.
1042
MOZ_ASSERT_IF(ThingIsPermanentAtomOrWellKnownSymbol(target),
1043
!target->maybeCompartment());
1044
MOZ_ASSERT_IF(target->zoneFromAnyThread()->isAtomsZone(),
1045
!target->maybeCompartment());
1046
// If we have access to a compartment pointer for both things, they must
1047
// match.
1048
MOZ_ASSERT_IF(source->maybeCompartment() && target->maybeCompartment(),
1049
source->maybeCompartment() == target->maybeCompartment());
1050
}
1051
1052
template <typename S, typename T>
1053
void js::GCMarker::traverseEdge(S source, T* target) {
1054
CheckTraversedEdge(source, target);
1055
traverse(target);
1056
}
1057
1058
template <typename S, typename T>
1059
void js::GCMarker::traverseEdge(S source, const T& thing) {
1060
ApplyGCThingTyped(thing,
1061
[this, source](auto t) { this->traverseEdge(source, t); });
1062
}
1063
1064
namespace {
1065
1066
template <typename T>
1067
struct TraceKindCanBeGray {};
1068
#define EXPAND_TRACEKIND_DEF(_, type, canBeGray, _1) \
1069
template <> \
1070
struct TraceKindCanBeGray<type> { \
1071
static const bool value = canBeGray; \
1072
};
1073
JS_FOR_EACH_TRACEKIND(EXPAND_TRACEKIND_DEF)
1074
#undef EXPAND_TRACEKIND_DEF
1075
1076
} // namespace
1077
1078
struct TraceKindCanBeGrayFunctor {
1079
template <typename T>
1080
bool operator()() {
1081
return TraceKindCanBeGray<T>::value;
1082
}
1083
};
1084
1085
static bool TraceKindCanBeMarkedGray(JS::TraceKind kind) {
1086
return DispatchTraceKindTyped(TraceKindCanBeGrayFunctor(), kind);
1087
}
1088
1089
template <typename T>
1090
bool js::GCMarker::mark(T* thing) {
1091
if (!thing->isTenured()) {
1092
return false;
1093
}
1094
1095
AssertShouldMarkInZone(thing);
1096
TenuredCell* cell = &thing->asTenured();
1097
1098
MarkColor color =
1099
TraceKindCanBeGray<T>::value ? markColor() : MarkColor::Black;
1100
bool marked = cell->markIfUnmarked(color);
1101
if (marked) {
1102
markCount++;
1103
}
1104
1105
return marked;
1106
}
1107
1108
/*** Inline, Eager GC Marking ***********************************************/
1109
1110
// Each of the eager, inline marking paths is directly preceeded by the
1111
// out-of-line, generic tracing code for comparison. Both paths must end up
1112
// traversing equivalent subgraphs.
1113
1114
void BaseScript::traceChildren(JSTracer* trc) {
1115
TraceEdge(trc, &functionOrGlobal_, "function");
1116
TraceEdge(trc, &sourceObject_, "sourceObject");
1117
1118
warmUpData_.trace(trc);
1119
1120
if (data_) {
1121
data_->trace(trc);
1122
}
1123
1124
if (sharedData_) {
1125
sharedData_->traceChildren(trc);
1126
}
1127
1128
// Scripts with bytecode may have optional data stored in per-runtime or
1129
// per-zone maps. Note that a failed compilation must not have entries since
1130
// the script itself will not be marked as having bytecode.
1131
if (hasBytecode()) {
1132
JSScript* script = static_cast<JSScript*>(this);
1133
1134
if (hasDebugScript()) {
1135
DebugAPI::traceDebugScript(trc, script);
1136
}
1137
}
1138
}
1139
1140
void LazyScript::traceChildren(JSTracer* trc) {
1141
BaseScript::traceChildren(trc);
1142
1143
if (trc->traceWeakEdges()) {
1144
TraceNullableEdge(trc, &script_, "script");
1145
}
1146
1147
if (trc->isMarkingTracer()) {
1148
GCMarker::fromTracer(trc)->markImplicitEdges(this);
1149
}
1150
}
1151
inline void js::GCMarker::eagerlyMarkChildren(LazyScript* thing) {
1152
traverseEdge(thing, static_cast<JSObject*>(thing->functionOrGlobal_));
1153
traverseEdge(thing, static_cast<JSObject*>(thing->sourceObject_));
1154
1155
thing->warmUpData_.trace(this);
1156
1157
if (thing->data_) {
1158
// Traverse the PrivateScriptData::gcthings() array.
1159
for (JS::GCCellPtr& elem : thing->data_->gcthings()) {
1160
if (elem.is<JSObject>()) {
1161
traverseEdge(thing, &elem.as<JSObject>());
1162
} else if (elem.is<JSString>()) {
1163
traverseEdge(thing, &elem.as<JSString>());
1164
} else {
1165
MOZ_ASSERT(!elem);
1166
}
1167
}
1168
}
1169
1170
MOZ_ASSERT(thing->sharedData_ == nullptr,
1171
"LazyScript should not have shared data.");
1172
1173
// script_ is weak so is not traced here.
1174
1175
markImplicitEdges(thing);
1176
}
1177
1178
void Shape::traceChildren(JSTracer* trc) {
1179
TraceEdge(trc, &base_, "base");
1180
TraceEdge(trc, &propidRef(), "propid");
1181
if (parent) {
1182
TraceEdge(trc, &parent, "parent");
1183
}
1184
if (dictNext.isObject()) {
1185
JSObject* obj = dictNext.toObject();
1186
TraceManuallyBarrieredEdge(trc, &obj, "dictNext object");
1187
if (obj != dictNext.toObject()) {
1188
dictNext.setObject(obj);
1189
}
1190
}
1191
1192
if (hasGetterObject()) {
1193
TraceManuallyBarrieredEdge(trc, &asAccessorShape().getterObj, "getter");
1194
}
1195
if (hasSetterObject()) {
1196
TraceManuallyBarrieredEdge(trc, &asAccessorShape().setterObj, "setter");
1197
}
1198
}
1199
inline void js::GCMarker::eagerlyMarkChildren(Shape* shape) {
1200
MOZ_ASSERT(shape->isMarked(markColor()));
1201
1202
do {
1203
// Special case: if a base shape has a shape table then all its pointers
1204
// must point to this shape or an anscestor. Since these pointers will
1205
// be traced by this loop they do not need to be traced here as well.
1206
BaseShape* base = shape->base();
1207
CheckTraversedEdge(shape, base);
1208
if (mark(base)) {
1209
MOZ_ASSERT(base->canSkipMarkingShapeCache(shape));
1210
base->traceChildrenSkipShapeCache(this);
1211
}
1212
1213
traverseEdge(shape, shape->propidRef().get());
1214
1215
// Normally only the last shape in a dictionary list can have a pointer to
1216
// an object here, but it's possible that we can see this if we trace
1217
// barriers while removing a shape from a dictionary list.
1218
if (shape->dictNext.isObject()) {
1219
traverseEdge(shape, shape->dictNext.toObject());
1220
}
1221
1222
// When triggered between slices on behalf of a barrier, these
1223
// objects may reside in the nursery, so require an extra check.
1224
// FIXME: Bug 1157967 - remove the isTenured checks.
1225
if (shape->hasGetterObject() && shape->getterObject()->isTenured()) {
1226
traverseEdge(shape, shape->getterObject());
1227
}
1228
if (shape->hasSetterObject() && shape->setterObject()->isTenured()) {
1229
traverseEdge(shape, shape->setterObject());
1230
}
1231
1232
shape = shape->previous();
1233
} while (shape && mark(shape));
1234
}
1235
1236
void JSString::traceChildren(JSTracer* trc) {
1237
if (hasBase()) {
1238
traceBase(trc);
1239
} else if (isRope()) {
1240
asRope().traceChildren(trc);
1241
}
1242
}
1243
inline void GCMarker::eagerlyMarkChildren(JSString* str) {
1244
if (str->isLinear()) {
1245
eagerlyMarkChildren(&str->asLinear());
1246
} else {
1247
eagerlyMarkChildren(&str->asRope());
1248
}
1249
}
1250
1251
void JSString::traceBase(JSTracer* trc) {
1252
MOZ_ASSERT(hasBase());
1253
TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base");
1254
}
1255
inline void js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr) {
1256
AssertShouldMarkInZone(linearStr);
1257
MOZ_ASSERT(linearStr->isMarkedAny());
1258
MOZ_ASSERT(linearStr->JSString::isLinear());
1259
1260
// Use iterative marking to avoid blowing out the stack.
1261
while (linearStr->hasBase()) {
1262
linearStr = linearStr->base();
1263
MOZ_ASSERT(linearStr->JSString::isLinear());
1264
if (linearStr->isPermanentAtom()) {
1265
break;
1266
}
1267
AssertShouldMarkInZone(linearStr);
1268
if (!mark(static_cast<JSString*>(linearStr))) {
1269
break;
1270
}
1271
}
1272
}
1273
1274
void JSRope::traceChildren(JSTracer* trc) {
1275
js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child");
1276
js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child");
1277
}
1278
inline void js::GCMarker::eagerlyMarkChildren(JSRope* rope) {
1279
// This function tries to scan the whole rope tree using the marking stack
1280
// as temporary storage. If that becomes full, the unscanned ropes are
1281
// added to the delayed marking list. When the function returns, the
1282
// marking stack is at the same depth as it was on entry. This way we avoid
1283
// using tags when pushing ropes to the stack as ropes never leak to other
1284
// users of the stack. This also assumes that a rope can only point to
1285
// other ropes or linear strings, it cannot refer to GC things of other
1286
// types.
1287
gc::MarkStack& stack = currentStack();
1288
size_t savedPos = stack.position();
1289
JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String);
1290
#ifdef JS_DEBUG
1291
static const size_t DEEP_ROPE_THRESHOLD = 100000;
1292
static const size_t ROPE_CYCLE_HISTORY = 100;
1293
DebugOnly<size_t> ropeDepth = 0;
1294
JSRope* history[ROPE_CYCLE_HISTORY];
1295
#endif
1296
while (true) {
1297
#ifdef JS_DEBUG
1298
if (++ropeDepth >= DEEP_ROPE_THRESHOLD) {
1299
// Bug 1011786 comment 294 - detect cyclic ropes. There are some
1300
// legitimate deep ropes, at least in tests. So if we hit a deep
1301
// rope, start recording the nodes we visit and check whether we
1302
// repeat. But do it on a finite window size W so that we're not
1303
// scanning the full history for every node. And only check every
1304
// Wth push, to add only constant overhead per node. This will only
1305
// catch cycles of size up to W (but it seems most likely that any
1306
// cycles will be size 1 or maybe 2.)
1307
if ((ropeDepth > DEEP_ROPE_THRESHOLD + ROPE_CYCLE_HISTORY) &&
1308
(ropeDepth % ROPE_CYCLE_HISTORY) == 0) {
1309
for (size_t i = 0; i < ROPE_CYCLE_HISTORY; i++) {
1310
MOZ_ASSERT(history[i] != rope, "cycle detected in rope");
1311
}
1312
}
1313
history[ropeDepth % ROPE_CYCLE_HISTORY] = rope;
1314
}
1315
#endif
1316
1317
JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String);
1318
JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
1319
AssertShouldMarkInZone(rope);
1320
MOZ_ASSERT(rope->isMarkedAny());
1321
JSRope* next = nullptr;
1322
1323
JSString* right = rope->rightChild();
1324
if (!right->isPermanentAtom() && mark(right)) {
1325
if (right->isLinear()) {
1326
eagerlyMarkChildren(&right->asLinear());
1327
} else {
1328
next = &right->asRope();
1329
}
1330
}
1331
1332
JSString* left = rope->leftChild();
1333
if (!left->isPermanentAtom() && mark(left)) {
1334
if (left->isLinear()) {
1335
eagerlyMarkChildren(&left->asLinear());
1336
} else {
1337
// When both children are ropes, set aside the right one to
1338
// scan it later.
1339
if (next && !stack.pushTempRope(next)) {
1340
delayMarkingChildren(next);
1341
}
1342
next = &left->asRope();
1343
}
1344
}
1345
if (next) {
1346
rope = next;
1347
} else if (savedPos != stack.position()) {
1348
MOZ_ASSERT(savedPos < stack.position());
1349
rope = stack.popPtr().asTempRope();
1350
} else {
1351
break;
1352
}
1353
}
1354
MOZ_ASSERT(savedPos == stack.position());
1355
}
1356
1357
static inline void TraceBindingNames(JSTracer* trc, BindingName* names,
1358
uint32_t length) {
1359
for (uint32_t i = 0; i < length; i++) {
1360
JSAtom* name = names[i].name();
1361
MOZ_ASSERT(name);
1362
TraceManuallyBarrieredEdge(trc, &name, "scope name");
1363
}
1364
};
1365
static inline void TraceNullableBindingNames(JSTracer* trc, BindingName* names,
1366
uint32_t length) {
1367
for (uint32_t i = 0; i < length; i++) {
1368
if (JSAtom* name = names[i].name()) {
1369
TraceManuallyBarrieredEdge(trc, &name, "scope name");
1370
}
1371
}
1372
};
1373
void BindingName::trace(JSTracer* trc) {
1374
if (JSAtom* atom = name()) {
1375
TraceManuallyBarrieredEdge(trc, &atom, "binding name");
1376
}
1377
}
1378
void BindingIter::trace(JSTracer* trc) {
1379
TraceNullableBindingNames(trc, names_, length_);
1380
}
1381
void LexicalScope::Data::trace(JSTracer* trc) {
1382
TraceBindingNames(trc, trailingNames.start(), length);
1383
}
1384
void FunctionScope::Data::trace(JSTracer* trc) {
1385
TraceNullableEdge(trc, &canonicalFunction, "scope canonical function");
1386
TraceNullableBindingNames(trc, trailingNames.start(), length);
1387
}
1388
void VarScope::Data::trace(JSTracer* trc) {
1389
TraceBindingNames(trc, trailingNames.start(), length);
1390
}
1391
void GlobalScope::Data::trace(JSTracer* trc) {
1392
TraceBindingNames(trc, trailingNames.start(), length);
1393
}
1394
void EvalScope::Data::trace(JSTracer* trc) {
1395
TraceBindingNames(trc, trailingNames.start(), length);
1396
}
1397
void ModuleScope::Data::trace(JSTracer* trc) {
1398
TraceNullableEdge(trc, &module, "scope module");
1399
TraceBindingNames(trc, trailingNames.start(), length);
1400
}
1401
void WasmInstanceScope::Data::trace(JSTracer* trc) {
1402
TraceNullableEdge(trc, &instance, "wasm instance");
1403
TraceBindingNames(trc, trailingNames.start(), length);
1404
}
1405
void WasmFunctionScope::Data::trace(JSTracer* trc) {
1406
TraceBindingNames(trc, trailingNames.start(), length);
1407
}
1408
void Scope::traceChildren(JSTracer* trc) {
1409
TraceNullableEdge(trc, &enclosing_, "scope enclosing");
1410
TraceNullableEdge(trc, &environmentShape_, "scope env shape");
1411
applyScopeDataTyped([trc](auto data) { data->trace(trc); });
1412
}
1413
inline void js::GCMarker::eagerlyMarkChildren(Scope* scope) {
1414
do {
1415
if (scope->environmentShape_) {
1416
traverseEdge(scope, scope->environmentShape_.get());
1417
}
1418
TrailingNamesArray* names = nullptr;
1419
uint32_t length = 0;
1420
switch (scope->kind()) {
1421
case ScopeKind::Function: {
1422
FunctionScope::Data& data = scope->as<FunctionScope>().data();
1423
traverseObjectEdge(scope, data.canonicalFunction);
1424
names = &data.trailingNames;
1425
length = data.length;
1426
break;
1427
}
1428
1429
case ScopeKind::FunctionBodyVar:
1430
case ScopeKind::ParameterExpressionVar: {
1431
VarScope::Data& data = scope->as<VarScope>().data();
1432
names = &data.trailingNames;
1433
length = data.length;
1434
break;
1435
}
1436
1437
case ScopeKind::Lexical:
1438
case ScopeKind::SimpleCatch:
1439
case ScopeKind::Catch:
1440
case ScopeKind::NamedLambda:
1441
case ScopeKind::StrictNamedLambda:
1442
case ScopeKind::FunctionLexical: {
1443
LexicalScope::Data& data = scope->as<LexicalScope>().data();
1444
names = &data.trailingNames;
1445
length = data.length;
1446
break;
1447
}
1448
1449
case ScopeKind::Global:
1450
case ScopeKind::NonSyntactic: {
1451
GlobalScope::Data& data = scope->as<GlobalScope>().data();
1452
names = &data.trailingNames;
1453
length = data.length;
1454
break;
1455
}
1456
1457
case ScopeKind::Eval:
1458
case ScopeKind::StrictEval: {
1459
EvalScope::Data& data = scope->as<EvalScope>().data();
1460
names = &data.trailingNames;
1461
length = data.length;
1462
break;
1463
}
1464
1465
case ScopeKind::Module: {
1466
ModuleScope::Data& data = scope->as<ModuleScope>().data();
1467
traverseObjectEdge(scope, data.module);
1468
names = &data.trailingNames;
1469
length = data.length;
1470
break;
1471
}
1472
1473
case ScopeKind::With:
1474
break;
1475
1476
case ScopeKind::WasmInstance: {
1477
WasmInstanceScope::Data& data = scope->as<WasmInstanceScope>().data();
1478
traverseObjectEdge(scope, data.instance);
1479
names = &data.trailingNames;
1480
length = data.length;
1481
break;
1482
}
1483
1484
case ScopeKind::WasmFunction: {
1485
WasmFunctionScope::Data& data = scope->as<WasmFunctionScope>().data();
1486
names = &data.trailingNames;
1487
length = data.length;
1488
break;
1489
}
1490
}
1491
if (scope->kind_ == ScopeKind::Function) {
1492
for (uint32_t i = 0; i < length; i++) {
1493
if (JSAtom* name = names->get(i).name()) {
1494
traverseStringEdge(scope, name);
1495
}
1496
}
1497
} else {
1498
for (uint32_t i = 0; i < length; i++) {
1499
traverseStringEdge(scope, names->get(i).name());
1500
}
1501
}
1502
scope = scope->enclosing_;
1503
} while (scope && mark(scope));
1504
}
1505
1506
void js::ObjectGroup::traceChildren(JSTracer* trc) {
1507
AutoSweepObjectGroup sweep(this);
1508
1509
if (!trc->canSkipJsids()) {
1510
unsigned count = getPropertyCount(sweep);
1511
for (unsigned i = 0; i < count; i++) {
1512
if (ObjectGroup::Property* prop = getProperty(sweep, i)) {
1513
TraceEdge(trc, &prop->id, "group_property");
1514
}
1515
}
1516
}
1517
1518
if (proto().isObject()) {
1519
TraceEdge(trc, &proto(), "group_proto");
1520
}
1521
1522
// Note: the realm's global can be nullptr if we GC while creating the global.
1523
if (JSObject* global = realm()->unsafeUnbarrieredMaybeGlobal()) {
1524
TraceManuallyBarrieredEdge(trc, &global, "group_global");
1525
}
1526
1527
if (newScript(sweep)) {
1528
newScript(sweep)->trace(trc);
1529
}
1530
1531
if (maybePreliminaryObjects(sweep)) {
1532
maybePreliminaryObjects(sweep)->trace(trc);
1533
}
1534
1535
if (JSObject* descr = maybeTypeDescr()) {
1536
TraceManuallyBarrieredEdge(trc, &descr, "group_type_descr");
1537
setTypeDescr(&descr->as<TypeDescr>());
1538
}
1539
1540
if (JSObject* fun = maybeInterpretedFunction()) {
1541
TraceManuallyBarrieredEdge(trc, &fun, "group_function");
1542
setInterpretedFunction(&fun->as<JSFunction>());
1543
}
1544
}
1545
void js::GCMarker::lazilyMarkChildren(ObjectGroup* group) {
1546
AutoSweepObjectGroup sweep(group);
1547
unsigned count = group->getPropertyCount(sweep);
1548
for (unsigned i = 0; i < count; i++) {
1549
if (ObjectGroup::Property* prop = group->getProperty(sweep, i)) {
1550
traverseEdge(group, prop->id.get());
1551
}
1552
}
1553
1554
if (group->proto().isObject()) {
1555
traverseEdge(group, group->proto().toObject());
1556
}
1557
1558
// Note: the realm's global can be nullptr if we GC while creating the global.
1559
if (GlobalObject* global = group->realm()->unsafeUnbarrieredMaybeGlobal()) {
1560
traverseEdge(group, static_cast<JSObject*>(global));
1561
}
1562
1563
if (group->newScript(sweep)) {
1564
group->newScript(sweep)->trace(this);
1565
}
1566
1567
if (group->maybePreliminaryObjects(sweep)) {
1568
group->maybePreliminaryObjects(sweep)->trace(this);
1569
}
1570
1571
if (TypeDescr* descr = group->maybeTypeDescr()) {
1572
traverseEdge(group, static_cast<JSObject*>(descr));
1573
}
1574
1575
if (JSFunction* fun = group->maybeInterpretedFunction()) {
1576
traverseEdge(group, static_cast<JSObject*>(fun));
1577
}
1578
}
1579
1580
void JS::BigInt::traceChildren(JSTracer* trc) { return; }
1581
1582
template <typename Functor>
1583
static void VisitTraceList(const Functor& f, const uint32_t* traceList,
1584
uint8_t* memory);
1585
1586
// Call the trace hook set on the object, if present. If further tracing of
1587
// NativeObject fields is required, this will return the native object.
1588
enum class CheckGeneration { DoChecks, NoChecks };
1589
template <typename Functor>
1590
static inline NativeObject* CallTraceHook(Functor&& f, JSTracer* trc,
1591
JSObject* obj,
1592
CheckGeneration check) {
1593
const JSClass* clasp = obj->getClass();
1594
MOZ_ASSERT(clasp);
1595
MOZ_ASSERT(obj->isNative() == clasp->isNative());
1596
1597
if (!clasp->hasTrace()) {
1598
return &obj->as<NativeObject>();
1599
}
1600
1601
if (clasp->isTrace(InlineTypedObject::obj_trace)) {
1602
Shape** pshape = obj->as<InlineTypedObject>().addressOfShapeFromGC();
1603
f(pshape);
1604
1605
InlineTypedObject& tobj = obj->as<InlineTypedObject>();
1606
if (tobj.typeDescr().hasTraceList()) {
1607
VisitTraceList(f, tobj.typeDescr().traceList(),
1608
tobj.inlineTypedMemForGC());
1609
}
1610
1611
return nullptr;
1612
}
1613
1614
AutoSetTracingSource asts(trc, obj);
1615
clasp->doTrace(trc, obj);
1616
1617
if (!clasp->isNative()) {
1618
return nullptr;
1619
}
1620
return &obj->as<NativeObject>();
1621
}
1622
1623
template <typename Functor>
1624
static void VisitTraceList(const Functor& f, const uint32_t* traceList,
1625
uint8_t* memory) {
1626
size_t stringCount = *traceList++;
1627
size_t objectCount = *traceList++;
1628
size_t valueCount = *traceList++;
1629
for (size_t i = 0; i < stringCount; i++) {
1630
f(reinterpret_cast<JSString**>(memory + *traceList));
1631
traceList++;
1632
}
1633
for (size_t i = 0; i < objectCount; i++) {
1634
JSObject** objp = reinterpret_cast<JSObject**>(memory + *traceList);
1635
if (*objp) {
1636
f(objp);
1637
}
1638
traceList++;
1639
}
1640
for (size_t i = 0; i < valueCount; i++) {
1641
f(reinterpret_cast<Value*>(memory + *traceList));
1642
traceList++;
1643
}
1644
}
1645
1646
/*** Mark-stack Marking *****************************************************/
1647
1648
GCMarker::MarkQueueProgress GCMarker::processMarkQueue() {
1649
#ifdef DEBUG
1650
if (markQueue.empty()) {
1651
return QueueComplete;
1652
}
1653
1654
GCRuntime& gcrt = runtime()->gc;
1655
if (queueMarkColor == mozilla::Some(MarkColor::Gray) &&
1656
gcrt.state() != State::Sweep) {
1657
return QueueSuspended;
1658
}
1659
1660
// If the queue wants to be gray marking, but we've pushed a black object
1661
// since set-color-gray was processed, then we can't switch to gray and must
1662
// again wait until gray marking is possible.
1663
//
1664
// Remove this code if the restriction against marking gray during black is
1665
// relaxed.
1666
if (queueMarkColor == mozilla::Some(MarkColor::Gray) && hasBlackEntries()) {
1667
return QueueSuspended;
1668
}
1669
1670
// If the queue wants to be marking a particular color, switch to that color.
1671
// In any case, restore the mark color to whatever it was when we entered
1672
// this function.
1673
AutoSetMarkColor autoRevertColor(*this, queueMarkColor.valueOr(markColor()));
1674
1675
// Process the mark queue by taking each object in turn, pushing it onto the
1676
// mark stack, and processing just the top element with processMarkStackTop
1677
// without recursing into reachable objects.
1678
while (queuePos < markQueue.length()) {
1679
Value val = markQueue[queuePos++].get().unbarrieredGet();
1680
if (val.isObject()) {
1681
JSObject* obj = &val.toObject();
1682
JS::Zone* zone = obj->zone();
1683
if (!zone->isGCMarking() || obj->isMarkedAtLeast(markColor())) {
1684
continue;
1685
}
1686
1687
// If we have started sweeping, obey sweep group ordering. But note that
1688
// we will first be called during the initial sweep slice, when the sweep
1689
// group indexes have not yet been computed. In that case, we can mark
1690
// freely.
1691
if (gcrt.state() == State::Sweep && gcrt.initialState != State::Sweep) {
1692
if (zone->gcSweepGroupIndex < gcrt.getCurrentSweepGroupIndex()) {
1693
// Too late. This must have been added after we started collecting,
1694
// and we've already processed its sweep group. Skip it.
1695
continue;
1696
}
1697
if (zone->gcSweepGroupIndex > gcrt.getCurrentSweepGroupIndex()) {
1698
// Not ready yet. Wait until we reach the object's sweep group.
1699
queuePos--;
1700
return QueueSuspended;
1701
}
1702
}
1703
1704
if (markColor() == MarkColor::Gray && zone->isGCMarkingBlackOnly()) {
1705
// Have not yet reached the point where we can mark this object, so
1706
// continue with the GC.
1707
queuePos--;
1708
return QueueSuspended;
1709
}
1710
1711
// Mark the object and push it onto the stack.
1712
traverse(obj);
1713
1714
if (isMarkStackEmpty()) {
1715
if (obj->asTenured().arena()->onDelayedMarkingList()) {
1716
AutoEnterOOMUnsafeRegion oomUnsafe;
1717
oomUnsafe.crash("mark queue OOM");
1718
}
1719
}
1720
1721
// Process just the one object that is now on top of the mark stack,
1722
// possibly pushing more stuff onto the stack.
1723
if (isMarkStackEmpty()) {
1724
MOZ_ASSERT(obj->asTenured().arena()->onDelayedMarkingList());
1725
// If we overflow the stack here and delay marking, then we won't be
1726
// testing what we think we're testing.
1727
AutoEnterOOMUnsafeRegion oomUnsafe;
1728
oomUnsafe.crash("Overflowed stack while marking test queue");
1729
}
1730
1731
SliceBudget unlimited = SliceBudget::unlimited();
1732
processMarkStackTop(unlimited);
1733
} else if (val.isString()) {
1734
JSLinearString* str = &val.toString()->asLinear();
1735
if (js::StringEqualsLiteral(str, "yield") && gcrt.isIncrementalGc()) {
1736
return QueueYielded;
1737
} else if (js::StringEqualsLiteral(str, "enter-weak-marking-mode") ||
1738
js::StringEqualsLiteral(str, "abort-weak-marking-mode")) {
1739
if (state == MarkingState::RegularMarking) {
1740
// We can't enter weak marking mode at just any time, so instead
1741
// we'll stop processing the queue and continue on with the GC. Once
1742
// we enter weak marking mode, we can continue to the rest of the
1743
// queue. Note that we will also suspend for aborting, and then abort
1744
// the earliest following weak marking mode.
1745
queuePos--;
1746
return QueueSuspended;
1747
}
1748
if (js::StringEqualsLiteral(str, "abort-weak-marking-mode")) {
1749
abortLinearWeakMarking();
1750
}
1751
} else if (js::StringEqualsLiteral(str, "drain")) {
1752
auto unlimited = SliceBudget::unlimited();
1753
MOZ_RELEASE_ASSERT(markUntilBudgetExhausted(unlimited));
1754
} else if (js::StringEqualsLiteral(str, "set-color-gray")) {
1755
queueMarkColor = mozilla::Some(MarkColor::Gray);
1756
if (gcrt.state() != State::Sweep) {
1757
// Cannot mark gray yet, so continue with the GC.
1758
queuePos--;
1759
return QueueSuspended;
1760
}
1761
setMarkColor(MarkColor::Gray);
1762
} else if (js::StringEqualsLiteral(str, "set-color-black")) {
1763
queueMarkColor = mozilla::Some(MarkColor::Black);
1764
setMarkColor(MarkColor::Black);
1765
} else if (js::StringEqualsLiteral(str, "unset-color")) {
1766
queueMarkColor.reset();
1767
}
1768
}
1769
}
1770
#endif
1771
1772
return QueueComplete;
1773
}
1774
1775
bool GCMarker::markUntilBudgetExhausted(SliceBudget& budget) {
1776
#ifdef DEBUG
1777
MOZ_ASSERT(!strictCompartmentChecking);
1778
strictCompartmentChecking = true;
1779
auto acc = mozilla::MakeScopeExit([&] { strictCompartmentChecking = false; });
1780
#endif
1781
1782
if (budget.isOverBudget()) {
1783
return false;
1784
}
1785
1786
// This method leaves the mark color as it found it.
1787
AutoSetMarkColor autoSetBlack(*this, MarkColor::Black);
1788
1789
// Change representation of value arrays on the stack while the mutator
1790
// runs.
1791
auto svr = mozilla::MakeScopeExit([&] { saveValueRanges(); });
1792
1793
for (;;) {
1794
while (hasBlackEntries()) {
1795
MOZ_ASSERT(markColor() == MarkColor::Black);
1796
processMarkStackTop(budget);
1797
if (budget.isOverBudget()) {
1798
return false;
1799
}
1800
}
1801
1802
if (hasGrayEntries()) {
1803
AutoSetMarkColor autoSetGray(*this, MarkColor::Gray);
1804
do {
1805
processMarkStackTop(budget);
1806
if (budget.isOverBudget()) {
1807
return false;
1808
}
1809
} while (hasGrayEntries());
1810
}
1811
1812
if (hasBlackEntries()) {
1813
// We can end up marking black during gray marking in the following case:
1814
// a WeakMap has a CCW key whose delegate (target) is black, and during
1815
// gray marking we mark the map (gray). The delegate's color will be
1816
// propagated to the key. (And we can't avoid this by marking the key
1817
// gray, because even though the value will end up gray in either case,
1818
// the WeakMap entry must be preserved because the CCW could get
1819
// collected and then we could re-wrap the delegate and look it up in the
1820
// map again, and need to get back the original value.)
1821
continue;
1822
}
1823
1824
if (!hasDelayedChildren()) {
1825
break;
1826
}
1827
1828
/*
1829
* Mark children of things that caused too deep recursion during the
1830
* above tracing. Don't do this until we're done with everything
1831
* else.
1832
*/
1833
if (!markAllDelayedChildren(budget)) {
1834
return false;
1835
}
<