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

raphw / byte-buddy / #730

28 Jan 2025 04:19PM UTC coverage: 85.352% (-0.02%) from 85.372%
#730

push

raphw
Adjust abstraction.

4 of 6 new or added lines in 2 files covered. (66.67%)

158 existing lines in 3 files now uncovered.

28977 of 33950 relevant lines covered (85.35%)

0.85 hits per line

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

49.5
/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 == IMPLICIT ? ASM_FIRST : 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) {
135
                    return ClassFileVersion.ofThisVm().isAtLeast(ClassFileVersion.JAVA_V24)
×
136
                            ? CLASS_FILE_API_ONLY.make(flags, classReader, typePool)
×
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) {
165
                    Object jdkClassReader = JDK_CLASS_READER == null ? null : classReader.unwrap(JDK_CLASS_READER);
×
166
                    if (jdkClassReader == null) {
×
167
                        return new ForClassFileApi(ForClassFileApi.DISPATCHER.make(flags,
×
168
                                SuperClassResolvingJdkClassWriter.GET_SUPER_CLASS,
169
                                new SuperClassResolvingJdkClassWriter(typePool)));
170
                    } else {
171
                        return new ForClassFileApi(ForClassFileApi.DISPATCHER.make(jdkClassReader,
×
172
                                flags,
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✔
197
                } catch (Throwable ignored) {
×
198
                    processor = null;
×
199
                }
1✔
200
                FACTORY = processor == null ? Default.ASM_FIRST : Default.valueOf(processor);
1✔
201
                Class<?> type;
202
                try {
203
                    type = ClassFileVersion.ofThisVm().isAtLeast(ClassFileVersion.JAVA_V24)
1✔
204
                        ? Class.forName("codes.rafael.asmjdkbridge.JdkClassReader")
1✔
205
                        : null;
206
                } catch (ClassNotFoundException ignored) {
×
207
                    type = null;
×
208
                }
1✔
209
                JDK_CLASS_READER = type;
1✔
210
            }
1✔
211

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

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

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

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

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

251
                /**
252
                 * The singleton instance.
253
                 */
254
                INSTANCE;
1✔
255

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

264
                /**
265
                 * {@inheritDoc}
266
                 */
267
                @AlwaysNull
268
                public AsmClassWriter toWriter(int flags, TypePool typePool) {
UNCOV
269
                    return null;
×
270
                }
271

272
                /**
273
                 * {@inheritDoc}
274
                 */
275
                public void accept(ClassVisitor classVisitor, int flags) {
UNCOV
276
                    throw new UnsupportedOperationException();
×
277
                }
278
            }
279

280
            /**
281
             * A class reader that does not retain a compatible {@link AsmClassWriter} implementation.
282
             */
283
            public static class NonRetainingAsmClassReader implements AsmClassReader {
284

285
                /**
286
                 * The delegate implementation.
287
                 */
288
                private final AsmClassReader delegate;
289

290
                /**
291
                 * Creates a new non-retaining ASM class writer.
292
                 *
293
                 * @param delegate The delegate implementation.
294
                 */
UNCOV
295
                public NonRetainingAsmClassReader(AsmClassReader delegate) {
×
UNCOV
296
                    this.delegate = delegate;
×
UNCOV
297
                }
×
298

299
                /**
300
                 * {@inheritDoc}
301
                 */
302
                @MaybeNull
303
                public <T> T unwrap(Class<T> type) {
UNCOV
304
                    return delegate.unwrap(type);
×
305
                }
306

307
                /**
308
                 * {@inheritDoc}
309
                 */
310
                @AlwaysNull
311
                public AsmClassWriter toWriter(int flags, TypePool typePool) {
312
                    return null;
×
313
                }
314

315
                /**
316
                 * {@inheritDoc}
317
                 */
318
                public void accept(ClassVisitor classVisitor, int flags) {
319
                    delegate.accept(classVisitor, flags);
×
UNCOV
320
                }
×
321
            }
322
        }
323

324
        /**
325
         * A class writer factory that suppresses any class reader implementation that might be provided
326
         * upon constructing a class writer.
327
         */
328
        @HashCodeAndEqualsPlugin.Enhance
329
        class Suppressing implements Factory {
330

331
            /**
332
             * The factory to delegate to.
333
             */
334
            private final Factory delegate;
335

336
            /**
337
             * Creates a suppressing class writer factory.
338
             *
339
             * @param delegate The factory to delegate to.
340
             */
UNCOV
341
            public Suppressing(Factory delegate) {
×
UNCOV
342
                this.delegate = delegate;
×
UNCOV
343
            }
×
344

345
            /**
346
             * {@inheritDoc}
347
             */
348
            public AsmClassWriter make(int flags) {
UNCOV
349
                return delegate.make(flags);
×
350
            }
351

352
            /**
353
             * {@inheritDoc}
354
             */
355
            public AsmClassWriter make(int flags, AsmClassReader classReader) {
UNCOV
356
                return delegate.make(flags);
×
357
            }
358

359
            /**
360
             * {@inheritDoc}
361
             */
362
            public AsmClassWriter make(int flags, TypePool typePool) {
UNCOV
363
                return delegate.make(flags, typePool);
×
364
            }
365

366
            /**
367
             * {@inheritDoc}
368
             */
369
            public AsmClassWriter make(int flags, AsmClassReader classReader, TypePool typePool) {
UNCOV
370
                return delegate.make(flags, typePool);
×
371
            }
372
        }
373
    }
374

375
    /**
376
     * Am implementation that uses ASM's internal {@link ClassWriter}.
377
     */
378
    class ForAsm implements AsmClassWriter {
379

380
        /**
381
         * The represented class writer.
382
         */
383
        private final ClassWriter classWriter;
384

385
        /**
386
         * Creates a new class writer based upon ASM's own implementation.
387
         *
388
         * @param classWriter The represented class writer.
389
         */
390
        public ForAsm(ClassWriter classWriter) {
1✔
391
            this.classWriter = classWriter;
1✔
392
        }
1✔
393

394
        /**
395
         * {@inheritDoc}
396
         */
397
        public ClassVisitor getVisitor() {
398
            return classWriter;
1✔
399
        }
400

401
        /**
402
         * {@inheritDoc}
403
         */
404
        public byte[] getBinaryRepresentation() {
405
            return classWriter.toByteArray();
1✔
406
        }
407
    }
408

409
    /**
410
     * A Class File API-based implementation for a class writer.
411
     */
412
    class ForClassFileApi implements AsmClassWriter {
413

414
        /**
415
         * The dispatcher for interacting with a {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
416
         */
UNCOV
417
        private static final JdkClassWriter DISPATCHER = doPrivileged(JavaDispatcher.of(
×
418
                JdkClassWriter.class,
UNCOV
419
                ForClassFileApi.class.getClassLoader()));
×
420

421
        /**
422
         * The represented class writer.
423
         */
424
        private final ClassVisitor classWriter;
425

426
        /**
427
         * Creates a new class file API-based class writer.
428
         *
429
         * @param classWriter The represented class writer.
430
         */
UNCOV
431
        public ForClassFileApi(ClassVisitor classWriter) {
×
UNCOV
432
            if (!DISPATCHER.isInstance(classWriter)) {
×
UNCOV
433
                throw new IllegalArgumentException("Not a JDK class writer: " + classWriter);
×
434
            }
UNCOV
435
            this.classWriter = classWriter;
×
UNCOV
436
        }
×
437

438
        /**
439
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
440
         *
441
         * @param action The action to execute from a privileged context.
442
         * @param <T>    The type of the action's resolved value.
443
         * @return The action's resolved value.
444
         */
445
        @AccessControllerPlugin.Enhance
446
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
UNCOV
447
            return action.run();
×
448
        }
449

450
        /**
451
         * {@inheritDoc}
452
         */
453
        public ClassVisitor getVisitor() {
UNCOV
454
            return classWriter;
×
455
        }
456

457
        /**
458
         * {@inheritDoc}
459
         */
460
        public byte[] getBinaryRepresentation() {
UNCOV
461
            return DISPATCHER.toByteArray(classWriter);
×
462
        }
463

464
        /**
465
         * An API to interact with {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
466
         */
467
        @JavaDispatcher.Proxied("codes.rafael.asmjdkbridge.JdkClassWriter")
468
        protected interface JdkClassWriter {
469

470
            /**
471
             * Checks if the supplied instance is a {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
472
             *
473
             * @param value The value to evaluate.
474
             * @return {@code true} if the supplied instance is a  {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
475
             */
476
            @JavaDispatcher.Instance
477
            boolean isInstance(ClassVisitor value);
478

479
            /**
480
             * Create a new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
481
             *
482
             * @param flags         The flags to consider.
483
             * @param getSuperClass A resolver for the super class.
484
             * @param target        The target to invoke the super class resolver upon.
485
             * @return A new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
486
             */
487
            @JavaDispatcher.IsConstructor
488
            ClassVisitor make(int flags, Method getSuperClass, Object target);
489

490
            /**
491
             * Create a new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
492
             *
493
             * @param classReader   The class reader of which to reuse the constant pool.
494
             * @param flags         The flags to consider.
495
             * @param getSuperClass A resolver for the super class.
496
             * @param target        The target to invoke the super class resolver upon.
497
             * @return A new {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
498
             */
499
            @JavaDispatcher.IsConstructor
500
            ClassVisitor make(@JavaDispatcher.Proxied("codes.rafael.asmjdkbridge.JdkClassReader") Object classReader,
501
                              int flags,
502
                              Method getSuperClass,
503
                              Object target);
504

505
            /**
506
             * Reads the created class file byte array from a given {@code codes.rafael.asmjdkbridge.JdkClassWriter}.
507
             *
508
             * @param value The {@code codes.rafael.asmjdkbridge.JdkClassWriter} to read from.
509
             * @return The generated class file.
510
             */
511
            byte[] toByteArray(ClassVisitor value);
512
        }
513
    }
514

515
    /**
516
     * A class writer that piggy-backs on Byte Buddy's {@link TypePool} to avoid class loading or look-up errors when redefining a class.
517
     * This is not available when creating a new class where automatic frame computation is however not normally a requirement.
518
     */
519
    class FrameComputingClassWriter extends ClassWriter {
520

521
        /**
522
         * The type pool to use for computing stack map frames, if required.
523
         */
524
        private final TypePool typePool;
525

526
        /**
527
         * Creates a new frame computing class writer.
528
         *
529
         * @param flags    The flags to be handed to the writer.
530
         * @param typePool The type pool to use for computing stack map frames, if required.
531
         */
532
        public FrameComputingClassWriter(int flags, TypePool typePool) {
533
            super(flags);
1✔
534
            this.typePool = typePool;
1✔
535
        }
1✔
536

537
        /**
538
         * Creates a new frame computing class writer.
539
         *
540
         * @param classReader The class reader from which the original class is read.
541
         * @param flags       The flags to be handed to the writer.
542
         * @param typePool    The type pool to use for computing stack map frames, if required.
543
         */
544
        public FrameComputingClassWriter(ClassReader classReader, int flags, TypePool typePool) {
545
            super(classReader, flags);
1✔
546
            this.typePool = typePool;
1✔
547
        }
1✔
548

549
        /**
550
         * {@inheritDoc}
551
         */
552
        protected String getCommonSuperClass(String leftTypeName, String rightTypeName) {
553
            TypeDescription leftType = typePool.describe(leftTypeName.replace('/', '.')).resolve();
1✔
554
            TypeDescription rightType = typePool.describe(rightTypeName.replace('/', '.')).resolve();
1✔
555
            if (leftType.isAssignableFrom(rightType)) {
1✔
556
                return leftType.getInternalName();
1✔
557
            } else if (leftType.isAssignableTo(rightType)) {
1✔
558
                return rightType.getInternalName();
1✔
559
            } else if (leftType.isInterface() || rightType.isInterface()) {
1✔
560
                return TypeDescription.ForLoadedType.of(Object.class).getInternalName();
1✔
561
            } else {
562
                do {
563
                    TypeDescription.Generic superClass = leftType.getSuperClass();
1✔
564
                    if (superClass == null) {
1✔
UNCOV
565
                        return TypeDescription.ForLoadedType.of(Object.class).getInternalName();
×
566
                    }
567
                    leftType = superClass.asErasure();
1✔
568
                } while (!leftType.isAssignableFrom(rightType));
1✔
569
                return leftType.getInternalName();
1✔
570
            }
571
        }
572
    }
573

574
    /**
575
     * A pseudo-JDK class writer that resolves super classes using a {@link TypePool}, to pass in the constructor.
576
     */
577
    class SuperClassResolvingJdkClassWriter {
578

579
        /**
580
         * The {@link SuperClassResolvingJdkClassWriter#getSuperClass(String)} method.
581
         */
582
        protected static final Method GET_SUPER_CLASS;
583

584
        /*
585
         * Resolve the method instance to use for reflection.
586
         */
587
        static {
588
            Method getSuperClass;
589
            try {
UNCOV
590
                getSuperClass = SuperClassResolvingJdkClassWriter.class.getMethod("getSuperClass", String.class);
×
UNCOV
591
            } catch (NoSuchMethodException e) {
×
UNCOV
592
                throw new IllegalStateException("Failed to resolve own method", e);
×
UNCOV
593
            }
×
UNCOV
594
            GET_SUPER_CLASS = getSuperClass;
×
UNCOV
595
        }
×
596

597
        /**
598
         * The {@link TypePool} to use.
599
         */
600
        private final TypePool typePool;
601

602
        /**
603
         * Creates a super class resolving JDK class writer.
604
         *
605
         * @param typePool The {@link TypePool} to use.
606
         */
UNCOV
607
        public SuperClassResolvingJdkClassWriter(TypePool typePool) {
×
UNCOV
608
            this.typePool = typePool;
×
UNCOV
609
        }
×
610

611
        /**
612
         * Resolves the super class for a given internal class name, or {@code null} if a given class
613
         * represents an interface. The provided class name will never be {@link Object}.
614
         *
615
         * @param internalName The internal name of the class or interface of which to return a super type.
616
         * @return The internal name of the super class or {@code null} if the provided type name represents
617
         * an interface.
618
         */
619
        @MaybeNull
620
        @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Object class can never be passed.")
621
        public String getSuperClass(String internalName) {
UNCOV
622
            TypeDescription typeDescription = typePool.describe(internalName.replace('/', '.')).resolve();
×
UNCOV
623
            return typeDescription.isInterface()
×
624
                    ? null
UNCOV
625
                    : typeDescription.getSuperClass().asErasure().getInternalName();
×
626
        }
627
    }
628
}
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