• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #717

17 Jan 2025 09:06PM UTC coverage: 85.373% (-0.1%) from 85.479%
#717

push

raphw
Fix build.

0 of 4 new or added lines in 1 file covered. (0.0%)

572 existing lines in 8 files now uncovered.

28973 of 33937 relevant lines covered (85.37%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.26
/byte-buddy-dep/src/main/java/net/bytebuddy/utility/AsmClassWriter.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy.utility;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ClassFileVersion;
20
import net.bytebuddy.build.AccessControllerPlugin;
21
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
22
import net.bytebuddy.description.type.TypeDescription;
23
import net.bytebuddy.pool.TypePool;
24
import net.bytebuddy.utility.dispatcher.JavaDispatcher;
25
import net.bytebuddy.utility.nullability.AlwaysNull;
26
import net.bytebuddy.utility.nullability.MaybeNull;
27
import net.bytebuddy.utility.privilege.GetSystemPropertyAction;
28
import org.objectweb.asm.ClassReader;
29
import org.objectweb.asm.ClassVisitor;
30
import org.objectweb.asm.ClassWriter;
31

32
import java.lang.reflect.Method;
33
import java.security.PrivilegedAction;
34

35
/**
36
 * A facade for creating a {@link ClassVisitor} that writes a class file.
37
 */
38
public interface AsmClassWriter {
39

40
    /**
41
     * Returns the {@link ClassVisitor} to use for writing the class file.
42
     *
43
     * @return An appropriate class visitor.
44
     */
45
    ClassVisitor getVisitor();
46

47
    /**
48
     * Returns the binary representation of the created class file.
49
     *
50
     * @return The binary representation of the created class file.
51
     */
52
    byte[] getBinaryRepresentation();
53

54
    /**
55
     * A factory for creating an {@link AsmClassWriter}.
56
     */
57
    interface Factory {
58

59
        /**
60
         * Creates a new class writer for the given flags.
61
         *
62
         * @param flags The flags to consider while writing a class file.
63
         * @return An appropriate class writer.
64
         */
65
        AsmClassWriter make(int flags);
66

67
        /**
68
         * Creates a new class writer for the given flags, possibly based on a previous class file representation.
69
         *
70
         * @param flags       The flags to consider while writing a class file.
71
         * @param classReader A class reader to consider for writing a class file.
72
         * @return An appropriate class writer.
73
         */
74
        AsmClassWriter make(int flags, AsmClassReader classReader);
75

76
        /**
77
         * Creates a new class writer for the given flags.
78
         *
79
         * @param flags    The flags to consider while writing a class file.
80
         * @param typePool A type pool to use for resolving type information for frame generation.
81
         * @return An appropriate class writer.
82
         */
83
        AsmClassWriter make(int flags, TypePool typePool);
84

85
        /**
86
         * Creates a new class writer for the given flags, possibly based on a previous class file representation.
87
         *
88
         * @param flags       The flags to consider while writing a class file.
89
         * @param classReader A class reader to consider for writing a class file.
90
         * @param typePool    A type pool to use for resolving type information for frame generation.
91
         * @return An appropriate class writer.
92
         */
93
        AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool);
94

95
        /**
96
         * Default implementations for factories of {@link AsmClassWriter}s.
97
         */
98
        enum Default implements Factory {
1✔
99

100
            /**
101
             * Uses a processor as it is configured by {@link OpenedClassReader#PROCESSOR_PROPERTY},
102
             * or {@link Default#ASM_FIRST} if no implicit processor is defined.
103
             */
104
            IMPLICIT {
1✔
105
                /**
106
                 * {@inheritDoc}
107
                 */
108
                public AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool) {
109
                    return FACTORY.make(flags, classReader, typePool);
1✔
110
                }
111
            },
112

113
            /**
114
             * A factory for a class reader that uses ASM's internal implementation whenever possible.
115
             */
116
            ASM_FIRST {
1✔
117
                /**
118
                 * {@inheritDoc}
119
                 */
120
                public AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool) {
121
                    return ClassFileVersion.ofThisVm().isGreaterThan(ClassFileVersion.latest())
1✔
122
                            ? CLASS_FILE_API_ONLY.make(flags, classReader, typePool)
1✔
123
                            : ASM_ONLY.make(flags, classReader, typePool);
1✔
124
                }
125
            },
126

127
            /**
128
             * A factory for a class writer that uses the class file API whenever possible.
129
             */
130
            CLASS_FILE_API_FIRST {
1✔
131
                /**
132
                 * {@inheritDoc}
133
                 */
134
                public AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool) {
UNCOV
135
                    return ClassFileVersion.ofThisVm().isAtLeast(ClassFileVersion.JAVA_V24)
×
UNCOV
136
                            ? CLASS_FILE_API_ONLY.make(flags, classReader, typePool)
×
UNCOV
137
                            : ASM_ONLY.make(flags, classReader, typePool);
×
138
                }
139
            },
140

141
            /**
142
             * A factory that will always use ASM's internal implementation.
143
             */
144
            ASM_ONLY {
1✔
145
                /**
146
                 * {@inheritDoc}
147
                 */
148
                public AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool) {
149
                    ClassReader unwrapped = classReader.unwrap(ClassReader.class);
1✔
150
                    return new ForAsm(unwrapped == null
1✔
151
                            ? new FrameComputingClassWriter(flags, typePool)
152
                            : new FrameComputingClassWriter(unwrapped, flags, typePool));
153
                }
154
            },
155

156
            /**
157
             * A factory that will always use the Class File API.
158
             */
159
            CLASS_FILE_API_ONLY {
1✔
160
                /**
161
                 * {@inheritDoc}
162
                 */
163
                @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "False positive in FindBugs.")
164
                public AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool) {
NEW
165
                    Object jdkClassReader = JDK_CLASS_READER == null ? null : classReader.unwrap(JDK_CLASS_READER);
×
NEW
166
                    if (jdkClassReader == null) {
×
NEW
167
                        return new ForClassFileApi(ForClassFileApi.DISPATCHER.make(flags,
×
168
                                SuperClassResolvingJdkClassWriter.GET_SUPER_CLASS,
169
                                new SuperClassResolvingJdkClassWriter(typePool)));
170
                    } else {
NEW
171
                        return new ForClassFileApi(ForClassFileApi.DISPATCHER.make(flags,
×
172
                                jdkClassReader,
173
                                SuperClassResolvingJdkClassWriter.GET_SUPER_CLASS,
174
                                new SuperClassResolvingJdkClassWriter(typePool)));
175
                    }
176
                }
177
            };
178

179
            /**
180
             * The {@code codes.rafael.asmjdkbridge.JdkClassReader} type or {@code null} if not available.
181
             */
182
            @MaybeNull
183
            private static final Class<?> JDK_CLASS_READER;
184

185
            /**
186
             * The implicit factory to use for writing class files.
187
             */
188
            private static final Factory FACTORY;
189

190
            /*
191
             * Resolves the implicit writer factory, if any and locates a possible {@code JdkClassReader} type.
192
             */
193
            static {
194
                String processor;
195
                try {
196
                    processor = doPrivileged(new GetSystemPropertyAction(OpenedClassReader.PROCESSOR_PROPERTY));
1✔
UNCOV
197
                } catch (Throwable ignored) {
×
UNCOV
198
                    processor = null;
×
199
                }
1✔
200
                FACTORY = processor == null ? Default.ASM_FIRST : Default.valueOf(processor);
1✔
201
                Class<?> type;
202
                try {
203
                    type = Class.forName("codes.rafael.asmjdkbridge.JdkClassReader");
1✔
UNCOV
204
                } catch (ClassNotFoundException ignored) {
×
UNCOV
205
                    type = null;
×
206
                }
1✔
207
                JDK_CLASS_READER = type;
1✔
208
            }
1✔
209

210
            /**
211
             * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
212
             *
213
             * @param action The action to execute from a privileged context.
214
             * @param <T>    The type of the action's resolved value.
215
             * @return The action's resolved value.
216
             */
217
            @MaybeNull
218
            @AccessControllerPlugin.Enhance
219
            private static <T> T doPrivileged(PrivilegedAction<T> action) {
UNCOV
220
                return action.run();
×
221
            }
222

223
            /**
224
             * {@inheritDoc}
225
             */
226
            public AsmClassWriter make(int flags) {
227
                return make(flags, TypePool.Empty.INSTANCE);
1✔
228
            }
229

230
            /**
231
             * {@inheritDoc}
232
             */
233
            public AsmClassWriter make(int flags, AsmClassReader classReader) {
234
                return make(flags, classReader, TypePool.Empty.INSTANCE);
1✔
235
            }
236

237
            /**
238
             * {@inheritDoc}
239
             */
240
            public AsmClassWriter make(int flags, TypePool typePool) {
241
                return make(flags, EmptyAsmClassReader.INSTANCE, typePool);
1✔
242
            }
243

244
            /**
245
             * An empty class reader for ASM that never unwraps an underlying implementation.
246
             */
247
            protected enum EmptyAsmClassReader implements AsmClassReader {
1✔
248

249
                /**
250
                 * The singleton instance.
251
                 */
252
                INSTANCE;
1✔
253

254
                /**
255
                 * {@inheritDoc}
256
                 */
257
                @AlwaysNull
258
                public <T> T unwrap(Class<T> type) {
259
                    return null;
1✔
260
                }
261

262
                /**
263
                 * {@inheritDoc}
264
                 */
265
                public void accept(ClassVisitor classVisitor, int flags) {
UNCOV
266
                    throw new UnsupportedOperationException();
×
267
                }
268
            }
269
        }
270

271
        /**
272
         * A class writer factory that suppresses any class reader implementation that might be provided
273
         * upon constructing a class writer.
274
         */
275
        @HashCodeAndEqualsPlugin.Enhance
276
        class Suppressing implements Factory {
277

278
            /**
279
             * The factory to delegate to.
280
             */
281
            private final Factory delegate;
282

283
            /**
284
             * Creates a suppressing class writer factory.
285
             *
286
             * @param delegate The factory to delegate to.
287
             */
UNCOV
288
            public Suppressing(Factory delegate) {
×
UNCOV
289
                this.delegate = delegate;
×
UNCOV
290
            }
×
291

292
            /**
293
             * {@inheritDoc}
294
             */
295
            public AsmClassWriter make(int flags) {
UNCOV
296
                return delegate.make(flags);
×
297
            }
298

299
            /**
300
             * {@inheritDoc}
301
             */
302
            public AsmClassWriter make(int flags, AsmClassReader classReader) {
UNCOV
303
                return delegate.make(flags);
×
304
            }
305

306
            /**
307
             * {@inheritDoc}
308
             */
309
            public AsmClassWriter make(int flags, TypePool typePool) {
UNCOV
310
                return delegate.make(flags, typePool);
×
311
            }
312

313
            /**
314
             * {@inheritDoc}
315
             */
316
            public AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool) {
UNCOV
317
                return delegate.make(flags, typePool);
×
318
            }
319
        }
320
    }
321

322
    /**
323
     * Am implementation that uses ASM's internal {@link ClassWriter}.
324
     */
325
    class ForAsm implements AsmClassWriter {
326

327
        /**
328
         * The represented class writer.
329
         */
330
        private final ClassWriter classWriter;
331

332
        /**
333
         * Creates a new class writer based upon ASM's own implementation.
334
         *
335
         * @param classWriter The represented class writer.
336
         */
337
        public ForAsm(ClassWriter classWriter) {
1✔
338
            this.classWriter = classWriter;
1✔
339
        }
1✔
340

341
        /**
342
         * {@inheritDoc}
343
         */
344
        public ClassVisitor getVisitor() {
345
            return classWriter;
1✔
346
        }
347

348
        /**
349
         * {@inheritDoc}
350
         */
351
        public byte[] getBinaryRepresentation() {
352
            return classWriter.toByteArray();
1✔
353
        }
354
    }
355

356
    /**
357
     * A Class File API-based implementation for a class writer.
358
     */
359
    class ForClassFileApi implements AsmClassWriter {
360

361
        /**
362
         * The dispatcher for interacting with a {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
363
         */
UNCOV
364
        private static final JdkClassWriter DISPATCHER = doPrivileged(JavaDispatcher.of(
×
365
                JdkClassWriter.class,
UNCOV
366
                ForClassFileApi.class.getClassLoader()));
×
367

368
        /**
369
         * The represented class writer.
370
         */
371
        private final ClassVisitor classWriter;
372

373
        /**
374
         * Creates a new class file API-based class writer.
375
         *
376
         * @param classWriter The represented class writer.
377
         */
UNCOV
378
        public ForClassFileApi(ClassVisitor classWriter) {
×
UNCOV
379
            if (!DISPATCHER.isInstance(classWriter)) {
×
UNCOV
380
                throw new IllegalArgumentException("Not a JDK class writer: " + classWriter);
×
381
            }
UNCOV
382
            this.classWriter = classWriter;
×
UNCOV
383
        }
×
384

385
        /**
386
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
387
         *
388
         * @param action The action to execute from a privileged context.
389
         * @param <T>    The type of the action's resolved value.
390
         * @return The action's resolved value.
391
         */
392
        @AccessControllerPlugin.Enhance
393
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
UNCOV
394
            return action.run();
×
395
        }
396

397
        /**
398
         * {@inheritDoc}
399
         */
400
        public ClassVisitor getVisitor() {
UNCOV
401
            return classWriter;
×
402
        }
403

404
        /**
405
         * {@inheritDoc}
406
         */
407
        public byte[] getBinaryRepresentation() {
UNCOV
408
            return DISPATCHER.toByteArray(classWriter);
×
409
        }
410

411
        /**
412
         * An API to interact with {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
413
         */
414
        @JavaDispatcher.Proxied("codes.rafael.asmjdkbridge.JdkClassWriter")
415
        protected interface JdkClassWriter {
416

417
            /**
418
             * Checks if the supplied instance is a {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
419
             *
420
             * @param value The value to evaluate.
421
             * @return {@code true} if the supplied instance is a  {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
422
             */
423
            @JavaDispatcher.Instance
424
            boolean isInstance(ClassVisitor value);
425

426
            /**
427
             * Create a new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
428
             *
429
             * @param flags         The flags to consider.
430
             * @param getSuperClass A resolver for the super class.
431
             * @param target        The target to invoke the super class resolver upon.
432
             * @return A new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
433
             */
434
            @JavaDispatcher.IsConstructor
435
            ClassVisitor make(int flags, Method getSuperClass, Object target);
436

437
            /**
438
             * Create a new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
439
             *
440
             * @param flags         The flags to consider.
441
             * @param classReader   The class reader of which to reuse the constant pool.
442
             * @param getSuperClass A resolver for the super class.
443
             * @param target        The target to invoke the super class resolver upon.
444
             * @return A new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
445
             */
446
            @JavaDispatcher.IsConstructor
447
            ClassVisitor make(int flags,
448
                              @JavaDispatcher.Proxied("codes.rafael.asmjdkbridge.JdkClassReader") Object classReader,
449
                              Method getSuperClass,
450
                              Object target);
451

452
            /**
453
             * Reads the created class file byte array from a given {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
454
             *
455
             * @param value The {@code codes.rafael.asmjdkbridge.JdkClassWriter} to read from.
456
             * @return The generated class file.
457
             */
458
            byte[] toByteArray(ClassVisitor value);
459
        }
460
    }
461

462
    /**
463
     * A class writer that piggy-backs on Byte Buddy's {@link TypePool} to avoid class loading or look-up errors when redefining a class.
464
     * This is not available when creating a new class where automatic frame computation is however not normally a requirement.
465
     */
466
    class FrameComputingClassWriter extends ClassWriter {
467

468
        /**
469
         * The type pool to use for computing stack map frames, if required.
470
         */
471
        private final TypePool typePool;
472

473
        /**
474
         * Creates a new frame computing class writer.
475
         *
476
         * @param flags    The flags to be handed to the writer.
477
         * @param typePool The type pool to use for computing stack map frames, if required.
478
         */
479
        public FrameComputingClassWriter(int flags, TypePool typePool) {
480
            super(flags);
1✔
481
            this.typePool = typePool;
1✔
482
        }
1✔
483

484
        /**
485
         * Creates a new frame computing class writer.
486
         *
487
         * @param classReader The class reader from which the original class is read.
488
         * @param flags       The flags to be handed to the writer.
489
         * @param typePool    The type pool to use for computing stack map frames, if required.
490
         */
491
        public FrameComputingClassWriter(ClassReader classReader, int flags, TypePool typePool) {
492
            super(classReader, flags);
1✔
493
            this.typePool = typePool;
1✔
494
        }
1✔
495

496
        /**
497
         * {@inheritDoc}
498
         */
499
        protected String getCommonSuperClass(String leftTypeName, String rightTypeName) {
500
            TypeDescription leftType = typePool.describe(leftTypeName.replace('/', '.')).resolve();
1✔
501
            TypeDescription rightType = typePool.describe(rightTypeName.replace('/', '.')).resolve();
1✔
502
            if (leftType.isAssignableFrom(rightType)) {
1✔
503
                return leftType.getInternalName();
1✔
504
            } else if (leftType.isAssignableTo(rightType)) {
1✔
505
                return rightType.getInternalName();
1✔
506
            } else if (leftType.isInterface() || rightType.isInterface()) {
1✔
507
                return TypeDescription.ForLoadedType.of(Object.class).getInternalName();
1✔
508
            } else {
509
                do {
510
                    TypeDescription.Generic superClass = leftType.getSuperClass();
1✔
511
                    if (superClass == null) {
1✔
UNCOV
512
                        return TypeDescription.ForLoadedType.of(Object.class).getInternalName();
×
513
                    }
514
                    leftType = superClass.asErasure();
1✔
515
                } while (!leftType.isAssignableFrom(rightType));
1✔
516
                return leftType.getInternalName();
1✔
517
            }
518
        }
519
    }
520

521
    /**
522
     * A pseudo-JDK class writer that resolves super classes using a {@link TypePool}, to pass in the constructor.
523
     */
524
    class SuperClassResolvingJdkClassWriter {
525

526
        /**
527
         * The {@link SuperClassResolvingJdkClassWriter#getSuperClass(String)} method.
528
         */
529
        protected static final Method GET_SUPER_CLASS;
530

531
        /*
532
         * Resolve the method instance to use for reflection.
533
         */
534
        static {
535
            Method getSuperClass;
536
            try {
UNCOV
537
                getSuperClass = SuperClassResolvingJdkClassWriter.class.getMethod("getSuperClass", String.class);
×
UNCOV
538
            } catch (NoSuchMethodException e) {
×
UNCOV
539
                throw new IllegalStateException("Failed to resolve own method", e);
×
UNCOV
540
            }
×
UNCOV
541
            GET_SUPER_CLASS = getSuperClass;
×
UNCOV
542
        }
×
543

544
        /**
545
         * The {@link TypePool} to use.
546
         */
547
        private final TypePool typePool;
548

549
        /**
550
         * Creates a super class resolving JDK class writer.
551
         *
552
         * @param typePool The {@link TypePool} to use.
553
         */
UNCOV
554
        public SuperClassResolvingJdkClassWriter(TypePool typePool) {
×
UNCOV
555
            this.typePool = typePool;
×
UNCOV
556
        }
×
557

558
        /**
559
         * Resolves the super class for a given internal class name, or {@code null} if a given class
560
         * represents an interface. The provided class name will never be {@link Object}.
561
         *
562
         * @param internalName The internal name of the class or interface of which to return a super type.
563
         * @return The internal name of the super class or {@code null} if the provided type name represents
564
         * an interface.
565
         */
566
        @MaybeNull
567
        @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Object class can never be passed.")
568
        public String getSuperClass(String internalName) {
UNCOV
569
            TypeDescription typeDescription = typePool.describe(internalName.replace('/', '.')).resolve();
×
UNCOV
570
            return typeDescription.isInterface()
×
571
                    ? null
UNCOV
572
                    : typeDescription.getSuperClass().asErasure().getInternalName();
×
573
        }
574
    }
575
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc