Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
33f8b55
feat(moose): Phase A + Phase C-mini — HasCompiler stub + Class::MOP shim
fglock Apr 27, 2026
1388b48
feat(moose): Phase 2 stubs — metaclass / Test::Moose / Moose::Util / …
fglock Apr 27, 2026
2515c51
docs(moose): record lessons learned and detailed Phase 3+ plan
fglock Apr 27, 2026
2399fb7
docs(moose): add realistic ceiling section — shim caps at ~30%, not 100%
fglock Apr 27, 2026
9299881
docs(moose): retarget plan to 477/478 — concrete Phase D breakdown
fglock Apr 27, 2026
9e7400e
feat(moose): Phase 3 — rich _FakeMeta + next batch of stubs
fglock Apr 27, 2026
7744454
feat(moose): Phase 3 follow-ups — module pre-loading + missing methods
fglock Apr 27, 2026
aee01df
fix(core): *GLOB{SCALAR} returns a SCALAR reference, not the value
fglock Apr 27, 2026
708ec64
fix(refcount): auto-sweep keeps weak refs whose referent has refCount…
fglock Apr 27, 2026
616ecbc
docs(moose): document second Phase D blocker — MortalList.flush DESTROY
fglock Apr 27, 2026
369b8cb
docs(moose): refcount investigation findings — bug isolated, fix atte…
fglock Apr 27, 2026
7b28a75
docs(moose): refcount fix — second attempt also reverted
fglock Apr 27, 2026
434a4a4
docs(moose): detailed plan for "make refcount accurate enough"
fglock Apr 27, 2026
df42cd0
fix(refcount): walker-gated destroy resolves Class::MOP bootstrap blo…
fglock Apr 27, 2026
61873bc
Revert "fix(refcount): walker-gated destroy resolves Class::MOP boots…
fglock Apr 27, 2026
703c686
Revert "fix(core): *GLOB{SCALAR} returns a SCALAR reference, not the …
fglock Apr 27, 2026
518edf9
Revert "fix(refcount): auto-sweep keeps weak refs whose referent has …
fglock Apr 27, 2026
c68d1b9
docs(moose): document reverted core fixes and lessons learned
fglock Apr 27, 2026
5508e23
fix(refcount): walker-gated destroy resolves Class::MOP bootstrap blo…
fglock Apr 27, 2026
30f6913
feat(moose-phase-d): bundle pure-Perl Moose 2.4000 (work-in-progress)
fglock Apr 27, 2026
09b939b
feat(moose-phase-d): use Moose now works on bundled upstream Moose 2.…
fglock Apr 27, 2026
b0308bd
fix(refcount): narrow walker-gated destroy back to targeted call sites
fglock Apr 28, 2026
d400851
fix(refcount): two walker oracle improvements + detailed plan update
fglock Apr 28, 2026
5034ed1
test(refcount): add walker_gate_dbic_pattern.t + plan update
fglock Apr 28, 2026
efaadcd
test(refcount): reliable reproducer in dev/sandbox + identified root …
fglock Apr 28, 2026
6aa447a
docs(refcount): D-W1 done, document D-W2 perf fix (RuntimeStash skip)
fglock Apr 28, 2026
9b2d1ae
fix(refcount): D-W2 — skip RuntimeStash in walker BFS
fglock Apr 28, 2026
926991a
fix(parser): wrap `use` arg list in its own scope (matches Perl)
fglock Apr 28, 2026
49456fa
docs(refcount): document D-W2b perf regression + fix candidates
fglock Apr 28, 2026
2351e6a
docs(refcount): D-W2b plan with concrete measurements + lazy live-cou…
fglock Apr 28, 2026
fed551f
fix(refcount): D-W2b — lazy MyVarCleanupStack.liveCounts population
fglock Apr 28, 2026
3c6a2dc
docs(refcount): D-W2b results + D-W2c plan for the 4 remaining failures
fglock Apr 28, 2026
a1f1da0
docs(refcount): D-W2c findings — root cause is stale my-vars
fglock Apr 28, 2026
e410ce9
fix(refcount): D-W2c — restrict walker gate to Class::MOP / Moose
fglock Apr 28, 2026
e00a805
fix(refcount): D-W2c — class-name heuristic + isReachableFromRoots(gl…
fglock Apr 28, 2026
c4db69e
docs(refcount): D-W2c VERIFIED — DBIC PASSES, matches master baseline
fglock Apr 28, 2026
1a221c1
fix(SubUtil): expose _is_renamed for B.pm to honor set_subname
fglock Apr 28, 2026
06764a5
fix(SubUtil): return Package::__ANON__ for anonymous subs
fglock Apr 28, 2026
17fa88c
fix(B): handle set_subname empty name and Package::__ANON__
fglock Apr 28, 2026
358b49f
fix(sort): pass outer @_ to sort BLOCK comparator
fglock Apr 28, 2026
c9a64f2
docs: D-W3 Sub::Util + sort BLOCK fixes; Moose 396/478
fglock Apr 28, 2026
1858e1c
fix(parser): restore listStartIndex/hasEmptyLiteralList from master a…
fglock Apr 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1,648 changes: 1,590 additions & 58 deletions dev/modules/moose_support.md

Large diffs are not rendered by default.

111 changes: 111 additions & 0 deletions dev/sandbox/walker_gate_dbic_minimal.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Minimal failing reproducer for the walker-gate DBIC regression.
# Move from dev/sandbox/ to src/test/resources/unit/refcount/ once it
# passes (i.e. once the underlying refcount/walker bug is fixed).
#
# IMPORTANT: this test deliberately uses raw `print` with TAP rather than
# `use Test::More`. Loading Test::More creates enough additional globals
# / lexicals to mask the bug — the walker's reachable set ends up large
# enough that the @objs my-array is reachable transitively. The bare
# version below reliably fails the walker_gate_dbic_minimal.t pattern.
#
# Pattern:
# - 5 blessed objects in @objs (top-level my array)
# - 5 wrappers in @wrappers (top-level my array)
# - cycle: $obj->{wrapper} = $w (strong)
# - back-ref: weaken($w->{obj}) (weak)
# - many subsequent ref operations to trigger mortal flushes / auto-sweep
#
# Expected: all 5 wrappers should still see their obj.
# Observed (Apr 2026): wrapper #1 (and sometimes others) have undef
# ->{obj} because T::Obj id=1 was DESTROYED by maybeAutoSweep →
# sweepWeakRefs even though @objs still holds it. Same shape of bug as
# DBIC's "source 'Track' is not associated with a schema" failure.
#
# Stack trace from PJ_DESTROY_TRACE=1 confirms:
# at ReachabilityWalker.sweepWeakRefs(...)
# at MortalList.maybeAutoSweep(...)
# at MortalList.flush(...)
#
# The auto-sweep's `walk()` pass produces a `live` set that does NOT
# include @objs's elements, so they are flagged unreachable and DESTROY'd
# even though the named lexical @objs is still in scope.
#
# Root cause hypothesis: `ReachabilityWalker.walk()` seeds from globals
# and ScalarRefRegistry, but the @objs my-array isn't directly seeded.
# The fix likely requires seeding from `MyVarCleanupStack.snapshotLiveVars()`
# in walk() the same way `isReachableFromRoots()` already does.

use strict;
use warnings;
use Scalar::Util qw(weaken);

package T::Obj;
my $count = 0;
sub new { my $c = shift; bless { id => ++$count }, $c }
sub id { $_[0]->{id} }
sub DESTROY { $main::DESTROYED{$_[0]->{id}} = 1 }

package T::Wrapper;
use Scalar::Util qw(weaken);
sub new {
my ($class, $obj) = @_;
my $self = bless { obj => $obj }, $class;
weaken($self->{obj});
$self;
}
sub get {
my $self = shift;
die "no obj" unless defined $self->{obj};
$self->{obj};
}

package main;

%main::DESTROYED = ();

my @objs;
my @wrappers;
for (1..5) {
my $o = T::Obj->new;
my $w = T::Wrapper->new($o);
$o->{wrapper} = $w;
push @objs, $o;
push @wrappers, $w;
}

my $failed = 0;
for my $iter (1..20) {
for my $w (@wrappers) {
my $o = eval { $w->get };
unless (defined $o) {
$failed++;
next;
}
my $id = $o->id;
my @temps = (\$id, [$o], { id => $id });
}
}

# Bare TAP — no Test::More to keep the walker's reachable set small.
my @tests;
push @tests, ['no premature DESTROYs', 0 == scalar keys %main::DESTROYED,
"destroyed ids: " . join(",", sort { $a <=> $b } keys %main::DESTROYED)];
push @tests, ['no get() failures', $failed == 0, "$failed get() failures"];
my $attached = scalar grep { defined $_->{obj} } @wrappers;
push @tests, ['all 5 wrappers attached', $attached == 5, "only $attached/5 attached"];
my $with_wrapper = scalar grep { $_->{wrapper} } @objs;
push @tests, ['all 5 objs have wrapper', $with_wrapper == 5, "only $with_wrapper/5 with wrapper"];

print "1..", scalar @tests, "\n";
for my $i (0..$#tests) {
my ($desc, $ok, $diag) = @{$tests[$i]};
my $n = $i + 1;
if ($ok) {
print "ok $n - $desc\n";
} else {
print "not ok $n - $desc\n";
print "# $diag\n";
}
}

exit(scalar grep { !$_->[1] } @tests);
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,9 @@ public static int executeSort(int[] bytecode, int pc, RuntimeBase[] registers, I
RuntimeList list = listBase.getList();
RuntimeScalar closure = (RuntimeScalar) registers[closureReg];
String packageName = code.stringPool[packageIdx];
RuntimeList result = ListOperators.sort(list, closure, packageName);
// Pass outer @_ (register 1) so sort blocks can access $_[0], $_[1], etc.
RuntimeArray outerArgs = (registers[1] instanceof RuntimeArray) ? (RuntimeArray) registers[1] : null;
RuntimeList result = ListOperators.sort(list, closure, outerArgs, packageName);
registers[rd] = result;
return pc;
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/perlonjava/backend/jvm/EmitOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,9 @@ static void handleMapOperator(EmitterVisitor emitterVisitor, BinaryOperatorNode
node.right.accept(emitterVisitor.with(RuntimeContextType.LIST)); // list
node.left.accept(emitterVisitor.with(RuntimeContextType.SCALAR)); // subroutine
if (operator.equals("sort")) {
// Push outer @_ so sort blocks can access $_[0], $_[1], etc.
// (real Perl's sort BLOCK shares the surrounding sub's @_).
mv.visitVarInsn(Opcodes.ALOAD, 1);
emitterVisitor.pushCurrentPackage();
} else {
// For map, grep, all, any: push the outer @_ so blocks can access $_[0], $_[1] etc.
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/perlonjava/core/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public final class Configuration {
* Automatically populated by Gradle/Maven during build.
* DO NOT EDIT MANUALLY - this value is replaced at build time.
*/
public static final String gitCommitId = "52d34a6f8";
public static final String gitCommitId = "c9a64f290";

/**
* Git commit date of the build (ISO format: YYYY-MM-DD).
Expand All @@ -48,7 +48,7 @@ public final class Configuration {
* Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at"
* DO NOT EDIT MANUALLY - this value is replaced at build time.
*/
public static final String buildTimestamp = "Apr 28 2026 19:44:33";
public static final String buildTimestamp = "Apr 28 2026 20:42:14";

// Prevent instantiation
private Configuration() {
Expand Down
19 changes: 17 additions & 2 deletions src/main/java/org/perlonjava/frontend/parser/StatementParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -748,10 +748,25 @@ public static Node parseUseDeclaration(Parser parser, LexerToken token) {
}
}

// Parse the parameter list
// Parse the parameter list.
// Real Perl wraps the use's arg list in an implicit BEGIN block, so
// `our` declarations inside `use if (not our $x), 'M'` get their own
// lexical scope and do not collide with the surrounding script's
// `our` declarations. Mirror that by entering a new symbol-table
// scope for the parse, otherwise repeated patterns like
// use if (not our $__mx_is_compiled), 'Moose::Meta::Class';
// use if (not our $__mx_is_compiled), metaclass => 'Moose::Meta::Class';
// (idiomatic in Moose/Object.pm) emit spurious "our variable
// redeclared" warnings.
boolean hasParentheses = TokenUtils.peek(parser).text.equals("(");
int listStartIndex = parser.tokenIndex;
Node list = ListParser.parseZeroOrMoreList(parser, 0, false, false, false, false);
int useArgScope = parser.ctx.symbolTable.enterScope();
Node list;
try {
list = ListParser.parseZeroOrMoreList(parser, 0, false, false, false, false);
} finally {
parser.ctx.symbolTable.exitScope(useArgScope);
}
// Detect a syntactically empty list expression after the module name
// (e.g. `use Foo qw()` — `use Foo ()` is already covered by hasParentheses).
// Perl treats this as "skip import", distinct from `use Foo` (no list at all)
Expand Down
26 changes: 21 additions & 5 deletions src/main/java/org/perlonjava/runtime/operators/ListOperators.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ public static RuntimeList map(RuntimeList runtimeList, RuntimeScalar perlMapClos
* @throws RuntimeException If the Perl comparator subroutine throws an exception.
*/
public static RuntimeList sort(RuntimeList runtimeList, RuntimeScalar perlComparatorClosure, String packageName) {
return sort(runtimeList, perlComparatorClosure, null, packageName);
}

/**
* Sorts the elements of this RuntimeArray using a Perl comparator subroutine,
* passing the outer {@code @_} so the comparator block can access {@code $_[0]} etc.
* (Real Perl's sort BLOCK shares the surrounding subroutine's @_.)
*/
public static RuntimeList sort(RuntimeList runtimeList, RuntimeScalar perlComparatorClosure, RuntimeArray outerArgs, String packageName) {

// Check each element to ensure it's not an undefined array reference
runtimeList.validateNoAutovivification();
Expand Down Expand Up @@ -147,11 +156,15 @@ public static RuntimeList sort(RuntimeList runtimeList, RuntimeScalar perlCompar
varA.set(a);
varB.set(b);

// For $$-prototyped comparators, pass elements via @_
RuntimeArray comparatorArgs = new RuntimeArray();
// For $$-prototyped comparators, pass elements via @_;
// otherwise inherit the outer @_ so the block can use $_[N].
RuntimeArray comparatorArgs;
if (stackedComparator) {
comparatorArgs = new RuntimeArray();
comparatorArgs.push(a);
comparatorArgs.push(b);
} else {
comparatorArgs = outerArgs != null ? outerArgs : new RuntimeArray();
}

// Apply the Perl comparator subroutine with the arguments
Expand Down Expand Up @@ -233,9 +246,12 @@ public static RuntimeList grep(RuntimeList runtimeList, RuntimeScalar perlFilter

// Check the result of the filter subroutine
if (result.getFirst().getBoolean()) {
// If the result is non-zero, add the element to the filtered list
// We need to clone, otherwise we would be adding an alias to the original element
filteredElements.add(element.clone());
// Perl semantics: grep returns aliases to the original
// elements (not copies). This is required for patterns
// like `for (grep { !ref } $a, $b) { $_ = ... }` which
// modifies $a and $b. Cloning here would silently
// break that aliasing — see Class::MOP::MiniTrait.
filteredElements.add(element);
}
} catch (PerlNonLocalReturnException e) {
throw e; // Don't wrap non-local returns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ public record OperatorHandler(String className, String methodName, int methodTyp
"(Lorg/perlonjava/runtime/runtimetypes/RuntimeList;Lorg/perlonjava/runtime/runtimetypes/RuntimeScalar;Lorg/perlonjava/runtime/runtimetypes/RuntimeArray;I)Lorg/perlonjava/runtime/runtimetypes/RuntimeList;");
put("sort", "sort",
"org/perlonjava/runtime/operators/ListOperators",
"(Lorg/perlonjava/runtime/runtimetypes/RuntimeList;Lorg/perlonjava/runtime/runtimetypes/RuntimeScalar;Ljava/lang/String;)Lorg/perlonjava/runtime/runtimetypes/RuntimeList;");
"(Lorg/perlonjava/runtime/runtimetypes/RuntimeList;Lorg/perlonjava/runtime/runtimetypes/RuntimeScalar;Lorg/perlonjava/runtime/runtimetypes/RuntimeArray;Ljava/lang/String;)Lorg/perlonjava/runtime/runtimetypes/RuntimeList;");
put("all", "all",
"org/perlonjava/runtime/operators/ListOperators",
"(Lorg/perlonjava/runtime/runtimetypes/RuntimeList;Lorg/perlonjava/runtime/runtimetypes/RuntimeScalar;Lorg/perlonjava/runtime/runtimetypes/RuntimeArray;I)Lorg/perlonjava/runtime/runtimetypes/RuntimeList;");
Expand Down
38 changes: 37 additions & 1 deletion src/main/java/org/perlonjava/runtime/perlmodule/SubUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public static void initialize() {
subUtil.registerMethod("set_prototype", null); // No prototype to allow @_ passing
subUtil.registerMethod("subname", "$");
subUtil.registerMethod("set_subname", null); // No prototype to allow @_ passing
// Phase D-W2c: B.pm consults `Sub::Name::_is_renamed` to know
// whether to honor a Sub::Util::set_subname rename in
// `B::CV->GV->NAME`. Expose `Sub::Util::_is_renamed` (and
// alias `Sub::Name::_is_renamed`) so set_subname is reflected
// by Class::MOP::get_code_info even if the renamed sub was
// never installed into the target package's stash.
subUtil.registerMethod("_is_renamed", "$");
} catch (NoSuchMethodException e) {
System.err.println("Warning: Missing Sub::Util method: " + e.getMessage());
}
Expand Down Expand Up @@ -102,9 +109,23 @@ public static RuntimeList subname(RuntimeArray args, int ctx) {
return new RuntimeScalar().getList(); // undef for non-CODE
}
RuntimeCode code = (RuntimeCode) codeRef.value;
String pkg = code.packageName;
String sub = code.subName;
boolean renamed = code.explicitlyRenamed;
String pkg = code.packageName;
if (renamed) {
// Honor explicit rename; sub may be empty string.
if (sub == null) sub = "";
if (pkg != null && !pkg.isEmpty()) {
return new RuntimeScalar(pkg + "::" + sub).getList();
}
return new RuntimeScalar(sub).getList();
}
if (sub == null || sub.isEmpty()) {
// Anonymous sub: real Perl returns "Package::__ANON__" where Package
// is the compile-time package (CvSTASH).
if (pkg != null && !pkg.isEmpty()) {
return new RuntimeScalar(pkg + "::__ANON__").getList();
}
return new RuntimeScalar("__ANON__").getList();
}
if (pkg != null && !pkg.isEmpty()) {
Expand Down Expand Up @@ -147,4 +168,19 @@ public static RuntimeList set_subname(RuntimeArray args, int ctx) {
code.explicitlyRenamed = true;
return codeRef.getList();
}

/**
* Phase D-W2c: returns true if {@code set_subname} has been called on
* the given coderef. Used by {@code B::CV->_introspect} to decide
* whether to honor the renamed name.
*/
public static RuntimeList _is_renamed(RuntimeArray args, int ctx) {
if (args.size() != 1) {
throw new IllegalStateException("Bad number of arguments for _is_renamed()");
}
RuntimeScalar codeRef = args.get(0);
if (codeRef.type != CODE) return new RuntimeScalar(0).getList();
RuntimeCode code = (RuntimeCode) codeRef.value;
return new RuntimeScalar(code.explicitlyRenamed ? 1 : 0).getList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,44 @@ public static java.util.List<RuntimeBase> snapshotRescuedForWalk() {
* @param className the Perl class name
* @return true if DESTROY (or AUTOLOAD) is defined in the class hierarchy
*/
/**
* Phase D-W2c: walker-gated destroy is restricted to known-needed
* class hierarchies (Class::MOP and Moose / Moo). The gate is
* essential for those modules' bootstrap (their metaclasses and
* %METAS rely on transient refCount drift being absorbed by the
* walker), but it actively breaks DBIC's lazy-cache pattern and
* other CDBI / DBIx::Class flows where rows are MEANT to be
* destroyed at refCount=0 even when stack-local my-vars
* transiently reference them.
*
* The gate applies if and only if the class is in the
* Class::MOP / Moose family. The check is fast: a per-blessId
* BitSet lookup after the first miss-and-resolve.
*
* Patterns outside this family (e.g. user weak-ref cycles
* documented in dev/sandbox/walker_gate_dbic_minimal.t) do NOT
* get the gate; they were already broken on master and need a
* separate fix path.
*/
private static final java.util.BitSet walkerGateClasses = new java.util.BitSet();
private static final java.util.BitSet walkerGateChecked = new java.util.BitSet();

public static boolean classNeedsWalkerGate(int blessId) {
int idx = Math.abs(blessId);
if (walkerGateChecked.get(idx)) return walkerGateClasses.get(idx);
String cn = NameNormalizer.getBlessStr(blessId);
boolean needs = cn != null && (
cn.startsWith("Class::MOP")
|| cn.startsWith("Moose::")
|| cn.equals("Moose")
|| cn.startsWith("Moo::")
|| cn.equals("Moo")
);
walkerGateChecked.set(idx);
if (needs) walkerGateClasses.set(idx);
return needs;
}

public static boolean classHasDestroy(int blessId, String className) {
int idx = Math.abs(blessId);
if (destroyClasses.get(idx)) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,8 @@ public static boolean suppressFlush(boolean suppress) {
private static final long AUTO_SWEEP_MIN_INTERVAL_NS = 5_000_000_000L;
private static final boolean AUTO_GC_DISABLED =
System.getenv("JPERL_NO_AUTO_GC") != null;
private static final boolean AUTO_GC_DEBUG =
System.getenv("JPERL_GC_DEBUG") != null;
private static boolean inAutoSweep = false;

public static void flush() {
Expand All @@ -553,6 +555,30 @@ public static void flush() {
// leak-tracing scenarios; those scenarios now use
// createAnonymousReference() (localBindingExists stays false)
// so the clear is no longer needed and broke #76716.
} else if (base.blessId != 0
&& WeakRefRegistry.hasWeakRefsTo(base)
&& DestroyDispatch.classNeedsWalkerGate(base.blessId)
&& ReachabilityWalker.isReachableFromRoots(base)) {
// Phase D / Step W3-Path 2: blessed object with
// outstanding weak refs whose cooperative refCount
// dipped to 0 under deferred-decrement flush, BUT
// the walker can still reach it from package globals
// or hash/array element seeds. Treat as transient
// refCount drift — leave at 0; the next assignment
// that writes a tracked ref will bump it back up.
//
// Don't fire DESTROY, don't clear weak refs.
//
// The walker correctly distinguishes this case from
// the cycle-break-via-weaken case: an isolated
// cycle has no path to roots, so isReachableFromRoots
// returns false and the cycle is properly destroyed.
//
// The hasWeakRefsTo gate keeps this safeguard cheap
// for the overwhelmingly common case of objects
// without weak refs (no walker call needed).
//
// See dev/modules/moose_support.md (Phase D / Step W).
} else {
base.refCount = Integer.MIN_VALUE;
DestroyDispatch.callDestroy(base);
Expand Down Expand Up @@ -587,7 +613,7 @@ private static void maybeAutoSweep() {
// Explicit Internals::jperl_gc() still fires DESTROY for
// callers that want full cleanup.
int cleared = ReachabilityWalker.sweepWeakRefs(true);
if (System.getenv("JPERL_GC_DEBUG") != null) {
if (AUTO_GC_DEBUG) {
System.err.println("DBG auto-sweep cleared=" + cleared);
}
} finally {
Expand Down
Loading
Loading