Skip to content

Commit d3947c3

Browse files
committed
more
1 parent 196fe9b commit d3947c3

File tree

2 files changed

+32
-35
lines changed

2 files changed

+32
-35
lines changed

core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarTreeShaker.java

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,6 @@ JarTreeShakeBuildItem run() {
119119
* during static initialization or construction of reachable classes.
120120
*/
121121
private Set<String> analyzeClassLoadingChains(Set<String> reachable) {
122-
// Build combined bytecode map. Order matters: app bytecode first, then dep bytecode
123-
// (which includes transformed versions), then generated. This ensures transformed
124-
// app class bytecode takes priority over the original, matching the lookup order
125-
// in lookupBytecode().
126-
Map<String, Supplier<byte[]>> allBytecode = new HashMap<>();
127-
allBytecode.putAll(input.appBytecode);
128-
allBytecode.putAll(input.depBytecode);
129-
allBytecode.putAll(input.generatedBytecode);
130-
131122
// Build the transformed bytecode map once (optimization B)
132123
Map<String, Supplier<byte[]>> transformedBytecode = new HashMap<>();
133124
for (String name : input.transformedClassNames) {
@@ -137,7 +128,7 @@ private Set<String> analyzeClassLoadingChains(Set<String> reachable) {
137128
}
138129
}
139130

140-
ClassLoadingChainAnalyzer analyzer = new ClassLoadingChainAnalyzer(allBytecode, input.classToDep.keySet());
131+
ClassLoadingChainAnalyzer analyzer = new ClassLoadingChainAnalyzer(input.allBytecode, input.classToDep.keySet());
141132
Set<String> executedEntryPoints = new HashSet<>();
142133
Set<String> allPhase3Discovered = new HashSet<>();
143134
Set<String> classesToScan = new HashSet<>(reachable);
@@ -198,6 +189,7 @@ private Set<String> traceReachableClasses(Set<String> startingRoots, Set<String>
198189
}
199190
boolean sisuActivated = false;
200191
final Map<ArtifactKey, Integer> depDeserializationFlags = new HashMap<>();
192+
final BfsScanResult scan = new BfsScanResult();
201193

202194
while (!queue.isEmpty()) {
203195
String name = queue.poll();
@@ -212,12 +204,15 @@ private Set<String> traceReachableClasses(Set<String> startingRoots, Set<String>
212204

213205
// Single-pass ASM scan: extracts all class references, string class refs,
214206
// ServiceLoader calls, sisu detection, and resource/OIS detection in one pass
207+
scan.clear();
215208
boolean isGenerated = input.generatedBytecode.containsKey(name);
216-
BfsScanResult scan = scanBytecode(name, bytecode, allKnownClasses, isGenerated, sisuActivated);
209+
scanBytecode(name, bytecode, allKnownClasses, isGenerated, sisuActivated, scan);
217210

218-
// Enqueue discovered class references
211+
// Enqueue discovered class references — only those we have bytecode for.
212+
// Skips JDK/platform classes that we'll never find, avoiding thousands of
213+
// no-op queue iterations and wasted lookups.
219214
for (String ref : scan.refs) {
220-
if (ref != null && visited.add(ref)) {
215+
if (ref != null && input.allBytecode.containsKey(ref) && visited.add(ref)) {
221216
queue.add(ref);
222217
}
223218
}
@@ -334,27 +329,11 @@ private void includeServiceProviders(String name, Set<String> visited, Queue<Str
334329
}
335330

336331
/**
337-
* Looks up bytecode for a class: generated classes first, then dependency classes
338-
* (which include transformed versions), then app classes.
339-
* Dependency bytecode is checked before app bytecode because
340-
* {@link JarTreeShakerInput#collectTransformedClasses} places transformed app class
341-
* bytecode in {@code depBytecode}, and the transformed version may contain additional
342-
* references (e.g., converter classes added by bytecode transformers) that the original
343-
* app bytecode does not have.
332+
* Looks up bytecode for a class from the merged bytecode map.
344333
* Returns null if the class is a JDK class or not available.
345334
*/
346335
private byte[] lookupBytecode(String name) {
347-
byte[] bytecode = getBytecodeOrNull(input.generatedBytecode.get(name));
348-
if (bytecode == null) {
349-
bytecode = getBytecodeOrNull(input.depBytecode.get(name));
350-
if (bytecode == null) {
351-
bytecode = getBytecodeOrNull(input.appBytecode.get(name));
352-
}
353-
}
354-
return bytecode;
355-
}
356-
357-
private static byte[] getBytecodeOrNull(Supplier<byte[]> supplier) {
336+
Supplier<byte[]> supplier = input.allBytecode.get(name);
358337
return supplier == null ? null : supplier.get();
359338
}
360339

@@ -371,16 +350,23 @@ private static class BfsScanResult {
371350
boolean sisuDetected;
372351
/** Flags for resource access and ObjectInputStream usage detection */
373352
int deserializationFlags;
353+
354+
/** Resets all fields for reuse with the next class, avoiding per-class allocation. */
355+
void clear() {
356+
refs.clear();
357+
serviceLoaderServices.clear();
358+
sisuDetected = false;
359+
deserializationFlags = 0;
360+
}
374361
}
375362

376363
/**
377364
* Single-pass ASM scan that combines reference extraction, string class matching,
378365
* ServiceLoader detection, sisu detection, and resource/ObjectInputStream detection.
379366
* Replaces 4-5 separate ClassReader.accept() calls with one.
380367
*/
381-
private BfsScanResult scanBytecode(String className, byte[] bytecode, Set<String> allKnownClasses,
382-
boolean isGenerated, boolean sisuAlreadyActivated) {
383-
BfsScanResult result = new BfsScanResult();
368+
private void scanBytecode(String className, byte[] bytecode, Set<String> allKnownClasses,
369+
boolean isGenerated, boolean sisuAlreadyActivated, BfsScanResult result) {
384370
ClassReader reader = new ClassReader(bytecode);
385371
reader.accept(new ClassVisitor(Opcodes.ASM9) {
386372

@@ -625,7 +611,6 @@ public void visitEnd() {
625611
}
626612
}
627613
}, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
628-
return result;
629614
}
630615

631616
/**

core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarTreeShakerInput.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ class JarTreeShakerInput implements AutoCloseable {
6868
final List<Path> depJarPaths;
6969
final List<Path> appPaths;
7070
final Set<String> transformedClassNames;
71+
/**
72+
* Merged bytecode map with priority: app → dep (includes transformed) → generated.
73+
* Provides single-lookup access for BFS and call graph analysis.
74+
*/
75+
final Map<String, Supplier<byte[]>> allBytecode;
7176

7277
private final List<OpenPathTree> openTrees;
7378

@@ -107,6 +112,13 @@ class JarTreeShakerInput implements AutoCloseable {
107112
this.depJarPaths = depJarPaths;
108113
this.appPaths = appPaths;
109114
this.transformedClassNames = transformedClassNames;
115+
// Build merged bytecode map: app first, then dep (overwrites), then generated (overwrites)
116+
Map<String, Supplier<byte[]>> merged = new HashMap<>(
117+
appBytecode.size() + depBytecode.size() + generatedBytecode.size());
118+
merged.putAll(appBytecode);
119+
merged.putAll(depBytecode);
120+
merged.putAll(generatedBytecode);
121+
this.allBytecode = merged;
110122
this.openTrees = openTrees;
111123
}
112124

0 commit comments

Comments
 (0)