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

raphw / byte-buddy / #760

30 Mar 2025 06:24PM UTC coverage: 85.202%. Remained the same
#760

push

raphw
[release] Release new version.

29341 of 34437 relevant lines covered (85.2%)

0.85 hits per line

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

78.95
/byte-buddy-dep/src/main/java/net/bytebuddy/ClassFileVersion.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;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.build.AccessControllerPlugin;
20
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
21
import net.bytebuddy.description.type.TypeDescription;
22
import net.bytebuddy.dynamic.ClassFileLocator;
23
import net.bytebuddy.utility.nullability.MaybeNull;
24
import org.objectweb.asm.Opcodes;
25

26
import java.io.IOException;
27
import java.io.Serializable;
28
import java.lang.reflect.Method;
29
import java.security.PrivilegedAction;
30

31
/**
32
 * A wrapper object for representing a validated class file version in the format that is specified by the
33
 * <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html">JVMS</a>.
34
 */
35
public class ClassFileVersion implements Comparable<ClassFileVersion>, Serializable {
36

37
    /**
38
     * The class's serial version UID.
39
     */
40
    private static final long serialVersionUID = 1L;
41

42
    /**
43
     * Returns the minimal version number that is legal.
44
     */
45
    protected static final int BASE_VERSION = 44;
46

47
    /**
48
     * The class file version of Java 1.
49
     */
50
    public static final ClassFileVersion JAVA_V1 = new ClassFileVersion(Opcodes.V1_1);
1✔
51

52
    /**
53
     * The class file version of Java 2.
54
     */
55
    public static final ClassFileVersion JAVA_V2 = new ClassFileVersion(Opcodes.V1_2);
1✔
56

57
    /**
58
     * The class file version of Java 3.
59
     */
60
    public static final ClassFileVersion JAVA_V3 = new ClassFileVersion(Opcodes.V1_3);
1✔
61

62
    /**
63
     * The class file version of Java 4.
64
     */
65
    public static final ClassFileVersion JAVA_V4 = new ClassFileVersion(Opcodes.V1_4);
1✔
66

67
    /**
68
     * The class file version of Java 5.
69
     */
70
    public static final ClassFileVersion JAVA_V5 = new ClassFileVersion(Opcodes.V1_5);
1✔
71

72
    /**
73
     * The class file version of Java 6.
74
     */
75
    public static final ClassFileVersion JAVA_V6 = new ClassFileVersion(Opcodes.V1_6);
1✔
76

77
    /**
78
     * The class file version of Java 7.
79
     */
80
    public static final ClassFileVersion JAVA_V7 = new ClassFileVersion(Opcodes.V1_7);
1✔
81

82
    /**
83
     * The class file version of Java 8.
84
     */
85
    public static final ClassFileVersion JAVA_V8 = new ClassFileVersion(Opcodes.V1_8);
1✔
86

87
    /**
88
     * The class file version of Java 9.
89
     */
90
    public static final ClassFileVersion JAVA_V9 = new ClassFileVersion(Opcodes.V9);
1✔
91

92
    /**
93
     * The class file version of Java 10.
94
     */
95
    public static final ClassFileVersion JAVA_V10 = new ClassFileVersion(Opcodes.V10);
1✔
96

97
    /**
98
     * The class file version of Java 11.
99
     */
100
    public static final ClassFileVersion JAVA_V11 = new ClassFileVersion(Opcodes.V11);
1✔
101

102
    /**
103
     * The class file version of Java 12.
104
     */
105
    public static final ClassFileVersion JAVA_V12 = new ClassFileVersion(Opcodes.V12);
1✔
106

107
    /**
108
     * The class file version of Java 13.
109
     */
110
    public static final ClassFileVersion JAVA_V13 = new ClassFileVersion(Opcodes.V13);
1✔
111

112
    /**
113
     * The class file version of Java 14.
114
     */
115
    public static final ClassFileVersion JAVA_V14 = new ClassFileVersion(Opcodes.V14);
1✔
116

117
    /**
118
     * The class file version of Java 15.
119
     */
120
    public static final ClassFileVersion JAVA_V15 = new ClassFileVersion(Opcodes.V15);
1✔
121

122
    /**
123
     * The class file version of Java 16.
124
     */
125
    public static final ClassFileVersion JAVA_V16 = new ClassFileVersion(Opcodes.V16);
1✔
126

127
    /**
128
     * The class file version of Java 17.
129
     */
130
    public static final ClassFileVersion JAVA_V17 = new ClassFileVersion(Opcodes.V17);
1✔
131

132
    /**
133
     * The class file version of Java 18.
134
     */
135
    public static final ClassFileVersion JAVA_V18 = new ClassFileVersion(Opcodes.V18);
1✔
136

137
    /**
138
     * The class file version of Java 19.
139
     */
140
    public static final ClassFileVersion JAVA_V19 = new ClassFileVersion(Opcodes.V19);
1✔
141

142
    /**
143
     * The class file version of Java 20.
144
     */
145
    public static final ClassFileVersion JAVA_V20 = new ClassFileVersion(Opcodes.V20);
1✔
146

147
    /**
148
     * The class file version of Java 21.
149
     */
150
    public static final ClassFileVersion JAVA_V21 = new ClassFileVersion(Opcodes.V21);
1✔
151

152
    /**
153
     * The class file version of Java 22.
154
     */
155
    public static final ClassFileVersion JAVA_V22 = new ClassFileVersion(Opcodes.V22);
1✔
156

157
    /**
158
     * The class file version of Java 23.
159
     */
160
    public static final ClassFileVersion JAVA_V23 = new ClassFileVersion(Opcodes.V23);
1✔
161

162
    /**
163
     * The class file version of Java 24.
164
     */
165
    public static final ClassFileVersion JAVA_V24 = new ClassFileVersion(Opcodes.V24);
1✔
166

167
    /**
168
     * The class file version of Java 24.
169
     */
170
    public static final ClassFileVersion JAVA_V25 = new ClassFileVersion(Opcodes.V25);
1✔
171

172
    /**
173
     * An array of class file versions in their sorting order.
174
     */
175
    private static final ClassFileVersion[] CLASS_FILE_VERSIONS = new ClassFileVersion[] {ClassFileVersion.JAVA_V1,
1✔
176
            ClassFileVersion.JAVA_V2,
177
            ClassFileVersion.JAVA_V3,
178
            ClassFileVersion.JAVA_V4,
179
            ClassFileVersion.JAVA_V5,
180
            ClassFileVersion.JAVA_V6,
181
            ClassFileVersion.JAVA_V7,
182
            ClassFileVersion.JAVA_V8,
183
            ClassFileVersion.JAVA_V9,
184
            ClassFileVersion.JAVA_V10,
185
            ClassFileVersion.JAVA_V11,
186
            ClassFileVersion.JAVA_V12,
187
            ClassFileVersion.JAVA_V13,
188
            ClassFileVersion.JAVA_V14,
189
            ClassFileVersion.JAVA_V15,
190
            ClassFileVersion.JAVA_V16,
191
            ClassFileVersion.JAVA_V17,
192
            ClassFileVersion.JAVA_V18,
193
            ClassFileVersion.JAVA_V19,
194
            ClassFileVersion.JAVA_V20,
195
            ClassFileVersion.JAVA_V21,
196
            ClassFileVersion.JAVA_V22,
197
            ClassFileVersion.JAVA_V23,
198
            ClassFileVersion.JAVA_V24,
199
            ClassFileVersion.JAVA_V25};
200

201
    /**
202
     * A version locator for the executing JVM.
203
     */
204
    private static final VersionLocator VERSION_LOCATOR = doPrivileged(VersionLocator.Resolver.INSTANCE);
1✔
205

206
    /**
207
     * The version number that is represented by this class file version instance.
208
     */
209
    private final int versionNumber;
210

211
    /**
212
     * Creates a wrapper for a given minor-major release of the Java class file format.
213
     *
214
     * @param versionNumber The minor-major release number.
215
     */
216
    protected ClassFileVersion(int versionNumber) {
1✔
217
        this.versionNumber = versionNumber;
1✔
218
    }
1✔
219

220
    /**
221
     * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
222
     *
223
     * @param action The action to execute from a privileged context.
224
     * @param <T>    The type of the action's resolved value.
225
     * @return The action's resolved value.
226
     */
227
    @AccessControllerPlugin.Enhance
228
    private static <T> T doPrivileged(PrivilegedAction<T> action) {
229
        return action.run();
×
230
    }
231

232
    /**
233
     * Creates a wrapper for a given minor-major release of the Java class file format.
234
     *
235
     * @param versionNumber The minor-major release number.
236
     * @return A representation of the version number.
237
     */
238
    public static ClassFileVersion ofMinorMajor(int versionNumber) {
239
        ClassFileVersion classFileVersion = new ClassFileVersion(versionNumber);
1✔
240
        if (classFileVersion.getMajorVersion() > 0 && classFileVersion.getMajorVersion() <= BASE_VERSION) {
1✔
241
            throw new IllegalArgumentException("Class version " + versionNumber + " is not valid");
1✔
242
        }
243
        return classFileVersion;
1✔
244
    }
245

246
    /**
247
     * Returns the Java class file by its representation by a version string in accordance to the formats known to <i>javac</i>.
248
     *
249
     * @param javaVersionString The Java version string.
250
     * @return The appropriate class file version.
251
     */
252
    public static ClassFileVersion ofJavaVersionString(String javaVersionString) {
253
        int index = javaVersionString.indexOf('.');
1✔
254
        try {
255
            int javaVersion;
256
            if (index == -1) {
1✔
257
                javaVersion = Integer.parseInt(javaVersionString);
1✔
258
            } else {
259
                javaVersion = Integer.parseInt(javaVersionString.substring(index + 1));
1✔
260
                if (Integer.parseInt(javaVersionString.substring(0, index)) != 1 || javaVersion > 8) {
1✔
261
                    throw new IllegalArgumentException("Java versions with minor version must be of format 1.[1-7]: " + javaVersionString);
1✔
262
                }
263
            }
264
            return ofJavaVersion(javaVersion);
1✔
265
        } catch (NumberFormatException exception) {
×
266
            throw new IllegalStateException("Failed to read Java version from: " + javaVersionString, exception);
×
267
        }
268
    }
269

270
    /**
271
     * Creates a class file version for a given major release of Java. Currently, all versions reaching from
272
     * Java 1 to Java 9 are supported.
273
     *
274
     * @param javaVersion The Java version.
275
     * @return A wrapper for the given Java class file version.
276
     */
277
    public static ClassFileVersion ofJavaVersion(int javaVersion) {
278
        if (javaVersion < 1) {
1✔
279
            throw new IllegalArgumentException("Java version must be positive: " + javaVersion);
1✔
280
        } else if (javaVersion - 1 < CLASS_FILE_VERSIONS.length) {
1✔
281
            return CLASS_FILE_VERSIONS[javaVersion - 1];
1✔
282
        } else {
283
            return new ClassFileVersion(BASE_VERSION + javaVersion);
×
284
        }
285
    }
286

287
    /**
288
     * Returns the latest officially supported Java version when experimental support is not enabled.
289
     *
290
     * @return The latest officially supported Java version.
291
     */
292
    public static ClassFileVersion latest() {
293
        return ClassFileVersion.JAVA_V25;
1✔
294
    }
295

296
    /**
297
     * Finds the highest class file version that is compatible to the current JVM version. Prior to Java 9, this is achieved
298
     * by parsing the {@code java.version} property which is provided by {@link java.lang.System#getProperty(String)}. If the system
299
     * property is not available, an {@link IllegalStateException} is thrown.
300
     *
301
     * @return The currently running Java process's class file version.
302
     */
303
    public static ClassFileVersion ofThisVm() {
304
        return VERSION_LOCATOR.resolve();
1✔
305
    }
306

307
    /**
308
     * Finds the highest class file version that is compatible to the current JVM version. Prior to Java 9, this is achieved
309
     * by parsing the {@code java.version} property which is provided by {@link java.lang.System#getProperty(String)}. If the system
310
     * property is not available, the {@code fallback} version is returned.
311
     *
312
     * @param fallback The version to fall back to if locating a class file version is not possible.
313
     * @return The currently running Java process's class file version or the fallback if locating this version is impossible.
314
     */
315
    @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
316
    public static ClassFileVersion ofThisVm(ClassFileVersion fallback) {
317
        try {
318
            return ofThisVm();
1✔
319
        } catch (Exception ignored) {
×
320
            return fallback;
×
321
        }
322
    }
323

324
    /**
325
     * Extracts a class' class version. The class' byte code is located by querying the {@link ClassLoader} of the class.
326
     *
327
     * @param type The type for which to locate a class file version.
328
     * @return The type's class file version.
329
     * @throws IOException If an error occurs while reading the class file.
330
     */
331
    public static ClassFileVersion of(Class<?> type) throws IOException {
332
        return of(type, ClassFileLocator.ForClassLoader.of(type.getClassLoader()));
1✔
333
    }
334

335
    /**
336
     * Extracts a class' class version.
337
     *
338
     * @param type             The type for which to locate a class file version.
339
     * @param classFileLocator The class file locator to query for a class file.
340
     * @return The type's class file version.
341
     * @throws IOException If an error occurs while reading the class file.
342
     */
343
    public static ClassFileVersion of(Class<?> type, ClassFileLocator classFileLocator) throws IOException {
344
        return of(TypeDescription.ForLoadedType.of(type), classFileLocator);
1✔
345
    }
346

347
    /**
348
     * Extracts a class' class version.
349
     *
350
     * @param typeDescription  The type for which to locate a class file version.
351
     * @param classFileLocator The class file locator to query for a class file.
352
     * @return The type's class file version.
353
     * @throws IOException If an error occurs while reading the class file.
354
     */
355
    public static ClassFileVersion of(TypeDescription typeDescription, ClassFileLocator classFileLocator) throws IOException {
356
        return ofClassFile(classFileLocator.locate(typeDescription.getName()).resolve());
1✔
357
    }
358

359
    /**
360
     * Extracts a class' class version from a class file.
361
     *
362
     * @param binaryRepresentation The class file's binary representation.
363
     * @return The supplied class file's class file version.
364
     */
365
    public static ClassFileVersion ofClassFile(byte[] binaryRepresentation) {
366
        if (binaryRepresentation.length < 7) {
1✔
367
            throw new IllegalArgumentException("Supplied byte array is too short to be a class file with " + binaryRepresentation.length + " byte");
1✔
368
        }
369
        return ofMinorMajor(binaryRepresentation[4] << 24
1✔
370
                | binaryRepresentation[5] << 16
371
                | binaryRepresentation[6] << 8
372
                | binaryRepresentation[7]);
373
    }
374

375
    /**
376
     * Returns the minor-major release number of this class file version.
377
     *
378
     * @return The minor-major release number of this class file version.
379
     */
380
    public int getMinorMajorVersion() {
381
        return versionNumber;
1✔
382
    }
383

384
    /**
385
     * Returns the major version this instance represents.
386
     *
387
     * @return The major version this instance represents.
388
     */
389
    public short getMajorVersion() {
390
        return (short) (versionNumber & 0xFFFF);
1✔
391
    }
392

393
    /**
394
     * Returns the minor version this instance represents.
395
     *
396
     * @return The minor version this instance represents.
397
     */
398
    public short getMinorVersion() {
399
        return (short) (versionNumber >>> 16);
1✔
400
    }
401

402
    /**
403
     * Returns the Java runtime version number of this class file version.
404
     *
405
     * @return The Java runtime version.
406
     */
407
    public int getJavaVersion() {
408
        return getMajorVersion() - BASE_VERSION;
1✔
409
    }
410

411
    /**
412
     * Checks if this class file version is at least as new as the provided version.
413
     *
414
     * @param classFileVersion The version to check against.
415
     * @return {@code true} if this version is at least of the given version.
416
     */
417
    public boolean isAtLeast(ClassFileVersion classFileVersion) {
418
        return compareTo(classFileVersion) > -1;
1✔
419
    }
420

421
    /**
422
     * Checks if this class file version is newer than the provided version.
423
     *
424
     * @param classFileVersion The version to check against.
425
     * @return {@code true} if this version is newer than the provided version.
426
     */
427
    public boolean isGreaterThan(ClassFileVersion classFileVersion) {
428
        return compareTo(classFileVersion) > 0;
1✔
429
    }
430

431
    /**
432
     * Checks if this class file version is at most as new as the provided version.
433
     *
434
     * @param classFileVersion The version to check against.
435
     * @return {@code true} if this version is as most as new as the provided version.
436
     */
437
    public boolean isAtMost(ClassFileVersion classFileVersion) {
438
        return compareTo(classFileVersion) < 1;
1✔
439
    }
440

441
    /**
442
     * Checks if this class file version is older than the provided version.
443
     *
444
     * @param classFileVersion The version to check against.
445
     * @return {@code true} if this version is older than the provided version.
446
     */
447
    public boolean isLessThan(ClassFileVersion classFileVersion) {
448
        return compareTo(classFileVersion) < 0;
1✔
449
    }
450

451
    /**
452
     * Returns this class file version indicating a class using preview features.
453
     *
454
     * @return This class file version but indicating the use of preview features.
455
     */
456
    public ClassFileVersion asPreviewVersion() {
457
        return new ClassFileVersion(versionNumber | Opcodes.V_PREVIEW);
1✔
458
    }
459

460
    /**
461
     * Returns {@code true} if this class file version indicates the use of preview features.
462
     *
463
     * @return {@code true} if this class file version indicates the use of preview features.
464
     */
465
    public boolean isPreviewVersion() {
466
        return (versionNumber & Opcodes.V_PREVIEW) == Opcodes.V_PREVIEW;
1✔
467
    }
468

469
    /**
470
     * {@inheritDoc}
471
     */
472
    public int compareTo(ClassFileVersion other) {
473
        return Integer.signum(getMajorVersion() == other.getMajorVersion()
1✔
474
                ? getMinorVersion() - other.getMinorVersion()
1✔
475
                : getMajorVersion() - other.getMajorVersion());
1✔
476
    }
477

478
    @Override
479
    public int hashCode() {
480
        return versionNumber;
1✔
481
    }
482

483
    @Override
484
    public boolean equals(@MaybeNull Object other) {
485
        if (this == other) {
1✔
486
            return true;
1✔
487
        } else if (other == null || getClass() != other.getClass()) {
1✔
488
            return false;
×
489
        }
490
        return versionNumber == ((ClassFileVersion) other).versionNumber;
1✔
491
    }
492

493
    @Override
494
    public String toString() {
495
        return "Java " + getJavaVersion() + " (" + getMinorMajorVersion() + ")";
1✔
496
    }
497

498
    /**
499
     * A locator for the executing VM's Java version.
500
     */
501
    protected interface VersionLocator {
502

503
        /**
504
         * A suffix that might indicate an early access version of Java.
505
         */
506
        String EARLY_ACCESS = "-ea";
507

508
        /**
509
         * A suffix that might indicate an internal version of Java.
510
         */
511
        String INTERNAL = "-internal";
512

513
        /**
514
         * The property for reading the current VM's Java version.
515
         */
516
        String JAVA_VERSION = "java.version";
517

518
        /**
519
         * Locates the current VM's major version number.
520
         *
521
         * @return The current VM's major version number.
522
         */
523
        ClassFileVersion resolve();
524

525
        /**
526
         * A resolver for the current VM's class file version.
527
         */
528
        enum Resolver implements PrivilegedAction<VersionLocator> {
1✔
529

530
            /**
531
             * The singleton instance.
532
             */
533
            INSTANCE;
1✔
534

535
            /**
536
             * {@inheritDoc}
537
             */
538
            @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
539
            public VersionLocator run() {
540
                try {
541
                    Class<?> type = Class.forName(Runtime.class.getName() + "$Version");
×
542
                    Method method;
543
                    try {
544
                        method = type.getMethod("feature");
×
545
                    } catch (NoSuchMethodException ignored) {
×
546
                        method = type.getMethod("major");
×
547
                    }
×
548
                    return new Resolved(ClassFileVersion.ofJavaVersion((Integer) method.invoke(Runtime.class.getMethod("version").invoke(null))));
×
549
                } catch (Throwable ignored) {
1✔
550
                    try {
551
                        String versionString = System.getProperty(JAVA_VERSION);
1✔
552
                        if (versionString == null) {
1✔
553
                            throw new IllegalStateException("Java version property is not set");
×
554
                        } else if (versionString.equals("0")) { // Used by Android, assume Java 6 defensively.
1✔
555
                            return new Resolved(ClassFileVersion.JAVA_V6);
×
556
                        }
557
                        if (versionString.endsWith(EARLY_ACCESS)) {
1✔
558
                            versionString = versionString.substring(0, versionString.length() - EARLY_ACCESS.length());
×
559
                        } else if (versionString.endsWith(INTERNAL)) {
1✔
560
                            versionString = versionString.substring(0, versionString.length() - INTERNAL.length());
×
561
                        }
562
                        int[] versionIndex = {-1, 0, 0};
1✔
563
                        for (int index = 1; index < 3; index++) {
1✔
564
                            versionIndex[index] = versionString.indexOf('.', versionIndex[index - 1] + 1);
1✔
565
                            if (versionIndex[index] == -1) {
1✔
566
                                throw new IllegalStateException("This JVM's version string does not seem to be valid: " + versionString);
×
567
                            }
568
                        }
569
                        return new Resolved(ClassFileVersion.ofJavaVersion(Integer.parseInt(versionString.substring(versionIndex[1] + 1, versionIndex[2]))));
1✔
570
                    } catch (Throwable throwable) {
×
571
                        return new Unresolved(throwable.getMessage());
×
572
                    }
573
                }
574
            }
575
        }
576

577
        /**
578
         * A version locator for a resolved class file version.
579
         */
580
        @HashCodeAndEqualsPlugin.Enhance
581
        class Resolved implements VersionLocator {
582

583
            /**
584
             * The resolved class file version.
585
             */
586
            private final ClassFileVersion classFileVersion;
587

588
            /**
589
             * Creates a new resolved version locator.
590
             *
591
             * @param classFileVersion The resolved class file version.
592
             */
593
            protected Resolved(ClassFileVersion classFileVersion) {
1✔
594
                this.classFileVersion = classFileVersion;
1✔
595
            }
1✔
596

597
            /**
598
             * {@inheritDoc}
599
             */
600
            public ClassFileVersion resolve() {
601
                return classFileVersion;
1✔
602
            }
603
        }
604

605
        /**
606
         * An unresolved version locator.
607
         */
608
        @HashCodeAndEqualsPlugin.Enhance
609
        class Unresolved implements VersionLocator {
610

611
            /**
612
             * The message of the exception that explains the resolution error.
613
             */
614
            private final String message;
615

616
            /**
617
             * Creates an unresolved version locator.
618
             *
619
             * @param message The message of the exception that explains the resolution error.
620
             */
621
            protected Unresolved(String message) {
×
622
                this.message = message;
×
623
            }
×
624

625
            /**
626
             * {@inheritDoc}
627
             */
628
            public ClassFileVersion resolve() {
629
                throw new IllegalStateException("Failed to resolve the class file version of the current VM: " + message);
×
630
            }
631
        }
632
    }
633
}
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