pax_global_header00006660000000000000000000000064150236135120014510gustar00rootroot0000000000000052 comment=3375bb44884d15d807fcc19162f174cde6ddf5cb asm-jdk-bridge-0.0.10/000077500000000000000000000000001502361351200143465ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/.gitignore000066400000000000000000000005371502361351200163430ustar00rootroot00000000000000# Eclipse .classpath .project .settings/ # Intellij .idea/ *.iml *.iws # Mac .DS_Store # Maven log/ target/ site/ # Gradle .gradle/ byte-buddy-gradle-plugin/build/ byte-buddy-gradle-plugin/buildSrc/build/ byte-buddy-gradle-plugin/gradle/build/ byte-buddy-gradle-plugin/gradle/wrapper/ # Shade plugin dependency-reduced-pom.xml # Android */gen/* asm-jdk-bridge-0.0.10/LICENSE000066400000000000000000000261351502361351200153620ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. asm-jdk-bridge-0.0.10/README.md000066400000000000000000000014041502361351200156240ustar00rootroot00000000000000# asm-jdk-bridge A first approach to trial the JDK API for generation and reading of class files by adapting the ASM API. This should serve as a first prof of concept by plugging the reader/writer into existing ASM-based code without much change of code. This also serves as an adapter concept for Byte Buddy where ASM is used vastly. In order to use the adapter, simply replace an instance of ASM's `ClassReader` or `ClassWriter` with `JdkClassReader` or `JdkClassWriter`. The latter use the Class File API internally, but expose equal APIs to ASM. If the availability of the Class File API is unclear, `ProbingClassReader` and `ProbingClassWriter` can be used, which will discover the underlying JVM and delegate to ASM or the Class File API, depending on capability. asm-jdk-bridge-0.0.10/SECURITY.md000066400000000000000000000006411502361351200161400ustar00rootroot00000000000000# Security Policy ## Supported Versions Only the latest version is guaranteed to be secure. For this purpose, Byte Buddy maintains backwards compatibility. ## Reporting a Vulnerability Please disclose responsibly by [contacting the maintainer](mailto:rafael.wth@gmail.com). After a fix is released, any vulnerability will be publicly disclosed and registered. As of today, no vulnerabilities are known to exists. asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/000077500000000000000000000000001502361351200201035ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/pom.xml000066400000000000000000000051401502361351200214200ustar00rootroot00000000000000 4.0.0 codes.rafael.asmjdkbridge asm-jdk-bridge-parent 0.0.10 asm-jdk-bridge-test ASM to OpenJDK Class API bridge (test) Contains tests where the API is loaded as a module to assure that the modularization works. 24 org.ow2.asm asm 9.7.1 codes.rafael.asmjdkbridge asm-jdk-bridge ${project.version} test junit junit 4.13.2 test org.ow2.asm asm-util 9.7 test org.apache.maven.plugins maven-jar-plugin 3.4.2 true true org.apache.maven.plugins maven-install-plugin 3.1.3 true org.apache.maven.plugins maven-deploy-plugin 3.1.3 true asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/000077500000000000000000000000001502361351200206725ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/000077500000000000000000000000001502361351200216165ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/000077500000000000000000000000001502361351200225375ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/000077500000000000000000000000001502361351200236345ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/000077500000000000000000000000001502361351200250665ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/000077500000000000000000000000001502361351200275145ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/test/000077500000000000000000000000001502361351200304735ustar00rootroot00000000000000Annotations.java000066400000000000000000000014411502361351200335540ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Annotations.A(0) @Annotations.B(0) public class Annotations { @A(1) @B(1) Object o; @C( aa = "a", ba = 0, c = @D, ca = @D, e = E.VALUE, ea = E.VALUE ) Object v; @A(2) @B(2) Object a(@A(3) @B(3) Object o) { return o; } @Retention(RetentionPolicy.RUNTIME) @interface A { int value(); } @interface B { int value(); } @interface C { String[] aa(); int[] ba(); D c(); D[] ca(); E e(); E[] ea(); } @interface D { } enum E { VALUE } } ArrayInstructions.java000066400000000000000000000005661502361351200347710ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; public class ArrayInstructions { Object[] aa(Object[] x) { x[0] = x[1]; return x; } Object[] aa() { Object[] x = new Object[100]; x[0] = x[1]; return x; } Object[] aaa() { Object[][] x = new Object[100][100]; x[0][0] = x[1][1]; return x; } } AsmTestAttribute.java000066400000000000000000000045531502361351200345320ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import org.objectweb.asm.Attribute; import org.objectweb.asm.ByteVector; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; public class AsmTestAttribute extends Attribute { private final byte[] bytes; public AsmTestAttribute() { super("CustomAttribute"); bytes = null; } public AsmTestAttribute(byte[] bytes) { super("CustomAttribute"); this.bytes = bytes; } @Override @SuppressWarnings("deprecation") protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) { AsmTestAttribute attribute = new AsmTestAttribute(new byte[length]); System.arraycopy(classReader.b, offset, attribute.bytes, 0, length); return attribute; } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { ByteVector vector = new ByteVector(bytes.length); vector.putByteArray(bytes, 0, bytes.length); return vector; } @Override public boolean isUnknown() { return false; } public static class AsmCodeTestAttribute extends Attribute { private final byte[] bytes; public AsmCodeTestAttribute() { super("CustomCodeAttribute"); bytes = null; } public AsmCodeTestAttribute(byte[] bytes) { super("CustomCodeAttribute"); this.bytes = bytes; } @Override public boolean isCodeAttribute() { return true; } @Override @SuppressWarnings("deprecation") protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) { AsmCodeTestAttribute attribute = new AsmCodeTestAttribute(new byte[length]); System.arraycopy(classReader.b, offset, attribute.bytes, 0, length); return attribute; } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { ByteVector vector = new ByteVector(bytes.length); vector.putByteArray(bytes, 0, bytes.length); return vector; } } }BranchesAndStackMapFrames.java000066400000000000000000000017631502361351200362200ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; public class BranchesAndStackMapFrames { int s(int v) { if (v == 0) { return 1; } else { return 3; } } int a(int v) { int x = 0; if (v == 0) { return 1; } else { return 3; } } int f(int v) { int x1 = 0, x2 = 1, x3 = 3, x4 = 4; if (v == 0) { return 1; } else { return 3; } } int c(int v1, int v2) { if (v1 == 0) { int x1 = 0, x2 = 1, x3 = 3; if (v2 == 1) { return 1; } else { return 2; } } return 3; } int aac(int v1, int v2) { int x1 = 0; if (v1 == 0) { int x2 = 0; if (v2 == 0) { return 1; } else { return 2; } } else { return 3; } } } CustomAttributeExtractable.java000066400000000000000000000046501502361351200366010ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.io.ByteArrayInputStream; import java.io.InputStream; public class CustomAttributeExtractable { public static Class make() { String generated = CustomAttributeExtractable.class.getPackageName() + ".CustomAttributeGen"; ClassWriter classWriter = new ClassWriter(0); classWriter.visit(Opcodes.V19, Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, generated.replace('.', '/'), null, Type.getInternalName(Object.class), null); classWriter.visitAttribute(new AsmTestAttribute(new byte[]{1})); FieldVisitor fieldVisitor = classWriter.visitField(Opcodes.ACC_PUBLIC, "f", "Ljava/lang/Object;", null, null); fieldVisitor.visitAttribute(new AsmTestAttribute(new byte[]{2})); fieldVisitor.visitEnd(); MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "f", "()V", null, null); methodVisitor.visitAttribute(new AsmTestAttribute(new byte[]{3})); methodVisitor.visitAttribute(new AsmTestAttribute.AsmCodeTestAttribute(new byte[]{4})); methodVisitor.visitCode(); methodVisitor.visitInsn(Opcodes.RETURN); methodVisitor.visitMaxs(0, 1); methodVisitor.visitEnd(); classWriter.visitEnd(); byte[] classFile = classWriter.toByteArray(); try { return new ClassLoader(null) { @Override protected Class findClass(String name) throws ClassNotFoundException { if (name.equals(generated)) { return defineClass(name, classFile, 0, classFile.length); } return super.findClass(name); } @Override public InputStream getResourceAsStream(String name) { if (name.equals(generated.replace('.', '/') + ".class")) { return new ByteArrayInputStream(classFile); } return super.getResourceAsStream(name); } }.loadClass(generated); } catch (ClassNotFoundException e) { throw new IllegalStateException(e); } } } DeprecatedClass.java000066400000000000000000000002271502361351200343060ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; @Deprecated public class DeprecatedClass { @Deprecated Object o; @Deprecated void m() { } } FieldConstructorAndMethod.java000066400000000000000000000004241502361351200363340ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; public class FieldConstructorAndMethod { String v; String f() { return v; } FieldConstructorAndMethod c() { return new FieldConstructorAndMethod(); } String m() { return f(); } } Invokedynamic.java000066400000000000000000000002661502361351200340630ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import java.util.function.Function; public class Invokedynamic { Function i() { return value -> value; } } LoadStoreAndReturn.java000066400000000000000000000006431502361351200350010ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; public class LoadStoreAndReturn { Object a(Object x) { Object o = x; return o; } int i(int x) { int o = x; return o; } long i(long x) { long o = x; return o; } float f(float x) { float o = x; return o; } double f(double x) { double o = x; return o; } } NoRecordComponents.java000066400000000000000000000001221502361351200350330ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.sample; public record NoRecordComponents() { } Operations.java000066400000000000000000000005501502361351200334020ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; public class Operations { void o(int a) { int i = 1 + a; int ii = 100 + a; int i2 = 1000 + a; int i3 = 100000 + a; long l = 1L + a; float f = 1f + a; double d = 1d + a; } String c(Object o) { String s = (String) o; return s; } } RecordComponents.java000066400000000000000000000005151502361351200345440ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.sample; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public record RecordComponents( @A @B Object v1, Object v2 ) { @Retention(RetentionPolicy.RUNTIME) @interface A { } @Retention(RetentionPolicy.RUNTIME) @interface B { } } asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/test/Switches.java000066400000000000000000000010701502361351200331250ustar00rootroot00000000000000package codes.rafael.asmjdkbridge.test; public class Switches { int t(int i) { return switch (i) { case 0 -> 1; case 1 -> 2; case 2 -> 3; default -> 4; }; } int t2(int i) { return switch (i) { case 0 -> 1; case 1 -> 2; case 3 -> 4; default -> 5; }; } int s(int i) { return switch (i) { case 0 -> 1; case 10 -> 2; case 20 -> 2; default -> 3; }; } } SyntheticConstructor.java000066400000000000000000000001531502361351200354760ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; public class SyntheticConstructor { public class Inner { } } SyntheticParameters.java000066400000000000000000000006541502361351200352620ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public enum SyntheticParameters { INSTANCE(null); SyntheticParameters(@SampleAnnotation Void ignored) { } @Retention(RetentionPolicy.RUNTIME) private @interface SampleAnnotation { } public class InnerClass { public InnerClass(@SampleAnnotation Void unused) { } } } asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/test/Trivial.java000066400000000000000000000001021502361351200327410ustar00rootroot00000000000000package codes.rafael.asmjdkbridge.test; public class Trivial { } TryThrowCatch.java000066400000000000000000000011761502361351200340310ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; public class TryThrowCatch { int t() { try { throw new RuntimeException(); } catch (Exception e) { return 2; } finally { x(); } } int t2() { Object o = new Object(); try { throw new RuntimeException(); } catch (Exception e) { return 2; } finally { x(); } } int t3() { Object o = new Object(); try { throw new RuntimeException(); } finally { x(); } } void x() { } } TypeAnnotationsInCode.java000066400000000000000000000014671502361351200355100ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; public class TypeAnnotationsInCode { void i() { Object o = new @A(0) @B(0) Object(); } void t() { Object o = new ArrayList<@A(0) @B(0) Object>(); } void c() { try { throw new RuntimeException(); } catch (@A(1) @B(1) RuntimeException e) { } } void v() { @A(2) @B(2) Object v = new Object(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) @interface A { int value(); } @Target(ElementType.TYPE_USE) @interface B { int value(); } } TypeAnnotationsWithPath.java000066400000000000000000000023231502361351200360670ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.AbstractList; import java.util.List; public abstract class TypeAnnotationsWithPath< @TypeAnnotationsWithoutPath.A(-2) @TypeAnnotationsWithoutPath.B(-2) T extends List<@TypeAnnotationsWithPath.A(-1) @TypeAnnotationsWithPath.B(-1) Object>> extends AbstractList<@TypeAnnotationsWithPath.A(0) @TypeAnnotationsWithPath.B(0) Object> implements List<@TypeAnnotationsWithPath.A(1) @TypeAnnotationsWithPath.B(1) Object> { List<@A(2) @B(2) Object> f1; @A(3) @B(3) Object[] f2; List<@A(4) @B(4) Object> m(List<@A(5) @B(5) Object> p) { return p; } @A(5) @B(5) Object[] a(@A(6) @B(6) Object[] p) { return p; } TypeAnnotationsWithPath<@A(7) @B(7) T>.Inner a(TypeAnnotationsWithPath<@A(8) @B(8) T>.Inner p) { return p; } class Inner { } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) @interface A { int value(); } @Target(ElementType.TYPE_USE) @interface B { int value(); } } TypeAnnotationsWithoutPath.java000066400000000000000000000011761502361351200366240ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/main/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Annotations.A(0) @Annotations.B(0) public class TypeAnnotationsWithoutPath { @A(1) @B(1) Object f; @A(2) @B(2) Object m(@A(3) @B(3) Object p) throws @A(4) @B(4) RuntimeException { return p; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) @interface A { int value(); } @Target(ElementType.TYPE_USE) @interface B { int value(); } } asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/000077500000000000000000000000001502361351200216515ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/000077500000000000000000000000001502361351200225725ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/000077500000000000000000000000001502361351200236675ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/000077500000000000000000000000001502361351200251215ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/asmjdkbridge/000077500000000000000000000000001502361351200275475ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/asmjdkbridge/test/000077500000000000000000000000001502361351200305265ustar00rootroot00000000000000AsmAttributeTest.java000066400000000000000000000122671502361351200345660ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import codes.rafael.asmjdkbridge.JdkClassReader; import codes.rafael.asmjdkbridge.JdkClassWriter; import org.junit.Test; import org.objectweb.asm.Attribute; import org.objectweb.asm.ByteVector; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import java.io.InputStream; import java.util.function.Consumer; import static org.junit.Assert.assertEquals; public class AsmAttributeTest { @Test public void can_read_attribute_properties() throws Exception { Class target = CustomAttributeExtractable.make(); byte[] classFile; try (InputStream inputStream = target.getResourceAsStream(target.getName().substring(target.getPackageName().length() + 1) + ".class")) { classFile = inputStream.readAllBytes(); } StringBuilder asm = new StringBuilder(), jdk = new StringBuilder(); new ClassReader(classFile).accept(new ClassVisitor(Opcodes.ASM9) { }, new Attribute[] {new StringCollectingAttribute(asm)}, 0); new JdkClassReader(classFile, new StringCollectingAttribute(jdk)).accept(new ClassVisitor(Opcodes.ASM9) { }, 0); assertEquals(asm.toString(), jdk.toString()); } @Test public void can_write_attribute() { StringBuilder asm = new StringBuilder(), jdk = new StringBuilder(); apply(new ClassWriter(0), asm, ClassWriter::toByteArray); apply(new JdkClassWriter(0), jdk, ignored -> { }); assertEquals(asm.toString(), jdk.toString()); } static void apply(T classVisitor, StringBuilder sb, Consumer completion) { classVisitor.visit(Opcodes.V17, Opcodes.ACC_PUBLIC, "sample/Attributes", null, "java/lang/Object", null); classVisitor.visitAttribute(new StringCollectingAttribute(sb)); classVisitor.visitEnd(); completion.accept(classVisitor); } static class StringCollectingAttribute extends Attribute { private final StringBuilder sb; private boolean written; StringCollectingAttribute(StringBuilder sb) { super("CustomAttribute"); this.sb = sb; } @Override protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) { sb.append("----").append("\n"); sb.append(offset).append("\n"); sb.append(length).append("\n"); sb.append(codeAttributeOffset).append("\n"); sb.append(labels == null).append("\n"); sb.append(classReader.getClassName()).append("\n"); sb.append(classReader.getSuperName()).append("\n"); sb.append(classReader.readByte(10)).append("\n"); sb.append(classReader.readShort(10)).append("\n"); sb.append(classReader.readUnsignedShort(10)).append("\n"); sb.append(classReader.readInt(10)).append("\n"); sb.append(classReader.readLong(10)).append("\n"); sb.append(classReader.getItemCount()).append("\n"); sb.append(classReader.readConst(2, charBuffer)).append("\n"); sb.append(classReader.readClass(137, charBuffer)).append("\n"); sb.append(classReader.readUTF8(175, charBuffer)).append("\n"); return super.read(classReader, offset, length, charBuffer, codeAttributeOffset, labels); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { if (!written) { sb.append("----").append("\n"); sb.append(classWriter.newConst("const")).append("\n"); sb.append(classWriter.newUTF8("utf8")).append("\n"); sb.append(classWriter.newField("owner", "name", "Ljava/lang/Object;")).append("\n"); sb.append(classWriter.newMethod("owner", "name", "()V", false)).append("\n"); sb.append(classWriter.newMethodType("()V")).append("\n"); sb.append(classWriter.newNameType("name", "Ljava/lang/Object;")).append("\n"); sb.append(classWriter.newModule("module")).append("\n"); sb.append(classWriter.newClass("class")).append("\n"); sb.append(classWriter.newPackage("package")).append("\n"); sb.append(classWriter.newHandle(Opcodes.H_INVOKEVIRTUAL, "owner", "name", "()V", false)).append("\n"); sb.append(classWriter.newInvokeDynamic("name", "()V", new Handle(Opcodes.H_INVOKEVIRTUAL, "owner", "name", "()V", false), "bootstrap")).append("\n"); sb.append(classWriter.newConstantDynamic("name", "Ljava/lang/Object;", new Handle(Opcodes.H_INVOKEVIRTUAL, "owner", "name", "()V", false), "bootstrap")).append("\n"); written = true; } ByteVector vector = new ByteVector(1); vector.putByte(1); return vector; } } } JdkClassReaderTest.java000066400000000000000000000106621502361351200350000ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import codes.rafael.asmjdkbridge.JdkClassReader; import codes.rafael.asmjdkbridge.sample.NoRecordComponents; import codes.rafael.asmjdkbridge.sample.RecordComponents; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.util.TraceClassVisitor; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.Collection; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @RunWith(Parameterized.class) public class JdkClassReaderTest { @SuppressWarnings("deprecation") @Parameterized.Parameters(name = "{0} (reader={1})") public static Collection data() { return Arrays.asList(new Object[][]{ {Object.class, 0}, {Runnable.class, 0}, {Trivial.class, 0}, {LoadStoreAndReturn.class, 0}, {FieldConstructorAndMethod.class, 0}, {Operations.class, 0}, {DeprecatedClass.class, 0}, {SyntheticConstructor.Inner.class, 0}, {ArrayInstructions.class, 0}, {Invokedynamic.class, 0}, {BranchesAndStackMapFrames.class, 0}, {BranchesAndStackMapFrames.class, ClassReader.EXPAND_FRAMES}, {BranchesAndStackMapFrames.class, ClassReader.SKIP_FRAMES}, {BranchesAndStackMapFrames.class, ClassReader.SKIP_CODE}, {Switches.class, 0}, {TryThrowCatch.class, 0}, {RecordComponents.class, 0}, {NoRecordComponents.class, 0}, {Annotations.class, 0}, {TypeAnnotationsWithoutPath.class, 0}, {TypeAnnotationsWithPath.class, 0}, {TypeAnnotationsInCode.class, 0}, {CustomAttributeExtractable.make(), 0}, {SyntheticParameters.class, 0}, {SyntheticParameters.InnerClass.class, 0}, {String.class, 0}, {Integer.class, 0}, {Math.class, 0} }); } private final Class target; private final int flags; public JdkClassReaderTest(Class target, int flags) { this.target = target; this.flags = flags; } @Test public void parsed_class_files_are_equal() throws IOException { byte[] classFile; try (InputStream inputStream = target.getResourceAsStream(target.getName().substring(target.getPackageName().length() + 1) + ".class")) { classFile = inputStream.readAllBytes(); } StringWriter asm = new StringWriter(), jdk = new StringWriter(); toClassReader(classFile).accept(toVisitor(asm), new Attribute[]{ new AsmTestAttribute(), new AsmTestAttribute.AsmCodeTestAttribute() }, flags); new JdkClassReader(classFile, new AsmTestAttribute(), new AsmTestAttribute.AsmCodeTestAttribute()).accept(toVisitor(jdk), flags); assertEquals(asm.toString(), jdk.toString()); } @Test public void properties_are_equal() throws IOException { byte[] classFile; try (InputStream inputStream = target.getResourceAsStream(target.getName().substring(target.getPackageName().length() + 1) + ".class")) { classFile = inputStream.readAllBytes(); } ClassReader asm = toClassReader(classFile); JdkClassReader jdk = new JdkClassReader(classFile); assertEquals(asm.getAccess(), jdk.getAccess()); assertEquals(asm.getClassName(), jdk.getClassName()); assertEquals(asm.getSuperName(), jdk.getSuperName()); assertArrayEquals(asm.getInterfaces(), jdk.getInterfaces()); } private static ClassVisitor toVisitor(StringWriter writer) { return new TraceClassVisitor(new PrintWriter(writer)); } private static ClassReader toClassReader(byte[] bytes) { try { Constructor constructor = ClassReader.class.getDeclaredConstructor(byte[].class, int.class, boolean.class); constructor.setAccessible(true); return constructor.newInstance(bytes, 0, false); } catch (Exception e) { throw new AssertionError(e); } } } JdkClassReplicationTest.java000066400000000000000000000071441502361351200360500ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import codes.rafael.asmjdkbridge.JdkClassReader; import codes.rafael.asmjdkbridge.JdkClassWriter; import codes.rafael.asmjdkbridge.sample.NoRecordComponents; import codes.rafael.asmjdkbridge.sample.RecordComponents; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.util.TraceClassVisitor; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.Collection; import static org.junit.Assert.assertEquals; @RunWith(Parameterized.class) public class JdkClassReplicationTest { @SuppressWarnings("deprecation") @Parameterized.Parameters(name = "{0} (reader={1})") public static Collection data() { return Arrays.asList(new Object[][]{ {Trivial.class, 0}, {LoadStoreAndReturn.class, 0}, {FieldConstructorAndMethod.class, 0}, {Operations.class, 0}, {DeprecatedClass.class, 0}, {SyntheticConstructor.Inner.class, 0}, {ArrayInstructions.class, 0}, {Invokedynamic.class, 0}, {BranchesAndStackMapFrames.class, 0}, {BranchesAndStackMapFrames.class, ClassReader.EXPAND_FRAMES}, {Switches.class, 0}, {TryThrowCatch.class, 0}, {RecordComponents.class, 0}, {NoRecordComponents.class, 0}, {Annotations.class, 0}, {TypeAnnotationsWithoutPath.class, 0}, {TypeAnnotationsWithPath.class, 0}, {TypeAnnotationsInCode.class, 0}, {CustomAttributeExtractable.make(), 0}, {SyntheticParameters.class, 0}, {SyntheticParameters.InnerClass.class, 0}, {String.class, 0}, {Integer.class, 0}, {Math.class, 0} }); } private final Class target; private final int flags; public JdkClassReplicationTest(Class target, int flags) { this.target = target; this.flags = flags; } @Test public void parsed_class_files_are_equal() throws IOException { byte[] classFile; try (InputStream inputStream = target.getResourceAsStream(target.getName().substring(target.getPackageName().length() + 1) + ".class")) { classFile = inputStream.readAllBytes(); } StringWriter original = new StringWriter(), replicated = new StringWriter(); JdkClassReader classReader = new JdkClassReader(classFile); JdkClassWriter classWriter = new JdkClassWriter(classReader, 0); classReader.accept(classWriter, 0); toClassReader(classFile).accept(toVisitor(original), flags); toClassReader(classWriter.toByteArray()).accept(toVisitor(replicated), flags); assertEquals(original.toString(), replicated.toString()); } private static ClassVisitor toVisitor(StringWriter writer) { return new TraceClassVisitor(new PrintWriter(writer)); } private static ClassReader toClassReader(byte[] bytes) { try { Constructor constructor = ClassReader.class.getDeclaredConstructor(byte[].class, int.class, boolean.class); constructor.setAccessible(true); return constructor.newInstance(bytes, 0, false); } catch (Exception e) { throw new AssertionError(e); } } } JdkClassWriterTest.java000066400000000000000000000102151502361351200350440ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import codes.rafael.asmjdkbridge.JdkClassWriter; import codes.rafael.asmjdkbridge.sample.NoRecordComponents; import codes.rafael.asmjdkbridge.sample.RecordComponents; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.util.TraceClassVisitor; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.Collection; import static org.junit.Assert.assertEquals; @RunWith(Parameterized.class) public class JdkClassWriterTest { @SuppressWarnings("deprecation") @Parameterized.Parameters(name = "{0} (reader={1}, writer={2})") public static Collection data() { return Arrays.asList(new Object[][]{ {Object.class, 0, 0}, {Runnable.class, 0, 0}, {Trivial.class, 0, 0}, {LoadStoreAndReturn.class, 0, 0}, {FieldConstructorAndMethod.class, 0, 0}, {Operations.class, 0, 0}, {DeprecatedClass.class, 0, 0}, {SyntheticConstructor.Inner.class, 0, 0}, {ArrayInstructions.class, 0, 0}, {Invokedynamic.class, 0, 0}, {BranchesAndStackMapFrames.class, 0, ClassWriter.COMPUTE_FRAMES}, {BranchesAndStackMapFrames.class, ClassReader.EXPAND_FRAMES, ClassWriter.COMPUTE_FRAMES}, {Switches.class, 0, ClassWriter.COMPUTE_FRAMES}, {TryThrowCatch.class, 0, ClassWriter.COMPUTE_FRAMES}, {RecordComponents.class, 0, 0}, {NoRecordComponents.class, 0, 0}, {Annotations.class, 0, 0}, {TypeAnnotationsWithoutPath.class, 0, 0}, {TypeAnnotationsWithPath.class, 0, 0}, {TypeAnnotationsInCode.class, 0, ClassWriter.COMPUTE_FRAMES}, {CustomAttributeExtractable.make(), 0, 0}, {SyntheticParameters.class, 0, 0}, {SyntheticParameters.InnerClass.class, 0, 0}, {String.class, ClassReader.SKIP_FRAMES, 0}, {Integer.class, ClassReader.SKIP_FRAMES, 0}, {Math.class, 0, ClassWriter.COMPUTE_FRAMES} }); } private final Class target; private final int readerFlags, writerFlags; public JdkClassWriterTest(Class target, int readerFlags, int writerFlags) { this.target = target; this.readerFlags = readerFlags; this.writerFlags = writerFlags; } @Test public void parsed_class_files_are_equal() throws IOException { byte[] classFile; try (InputStream inputStream = target.getResourceAsStream(target.getName().substring(target.getPackageName().length() + 1) + ".class")) { classFile = inputStream.readAllBytes(); } StringWriter asm = new StringWriter(), jdk = new StringWriter(); toClassReader(classFile).accept(toVisitor(asm), readerFlags); JdkClassWriter writer = new JdkClassWriter(writerFlags); toClassReader(classFile).accept(writer, new Attribute[]{ new AsmTestAttribute(), new AsmTestAttribute.AsmCodeTestAttribute() }, readerFlags); toClassReader(writer.toByteArray()).accept(toVisitor(jdk), new Attribute[]{ new AsmTestAttribute(), new AsmTestAttribute.AsmCodeTestAttribute() }, readerFlags); assertEquals(asm.toString(), jdk.toString()); } private static ClassVisitor toVisitor(StringWriter writer) { return new TraceClassVisitor(new PrintWriter(writer)); } private static ClassReader toClassReader(byte[] bytes) { try { Constructor constructor = ClassReader.class.getDeclaredConstructor(byte[].class, int.class, boolean.class); constructor.setAccessible(true); return constructor.newInstance(bytes, 0, false); } catch (Exception e) { throw new AssertionError(e); } } } ProbingTest.java000066400000000000000000000014131502361351200335510ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge-test/src/test/java/codes/rafael/asmjdkbridge/testpackage codes.rafael.asmjdkbridge.test; import codes.rafael.asmjdkbridge.ProbingClassReader; import org.junit.Test; import java.io.InputStream; public class ProbingTest { @Test public void can_probe_supported_version() throws Exception { byte[] classFile; try (InputStream inputStream = Sample.class.getResourceAsStream(Sample.class.getName().substring(Sample.class.getPackageName().length() + 1) + ".class")) { classFile = inputStream.readAllBytes(); } ProbingClassReader classReader = new ProbingClassReader(classFile); ProbingClassReader.ClassWriterContainer classWriter = classReader.toClassWriter(0); classReader.accept(classWriter.getClassVisitor(), 0); } public static class Sample { } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/000077500000000000000000000000001502361351200171265ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/pom.xml000066400000000000000000000106621502361351200204500ustar00rootroot00000000000000 4.0.0 codes.rafael.asmjdkbridge asm-jdk-bridge-parent 0.0.10 asm-jdk-bridge ASM to OpenJDK Class API bridge Implements adapters to translate between ASM visitors and the OpenJDK class file API. org.ow2.asm asm 9.7.1 org.apache.maven.plugins maven-compiler-plugin 3.13.0 java9-compile compile compile 9 ${project.basedir}/src/main/java-9 ${project.build.outputDirectory}/META-INF/versions/9 java24-compile compile compile 24 ${project.basedir}/src/main/java-24 ${project.build.outputDirectory}/META-INF/versions/24 org.apache.maven.plugins maven-jar-plugin 3.4.2 META-INF/versions/9/codes META-INF/versions/9/codes/** true org.apache.maven.plugins maven-source-plugin 3.3.1 attach-sources verify jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 3.10.1 attach-javadocs jar -package asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/000077500000000000000000000000001502361351200177155ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/000077500000000000000000000000001502361351200206415ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/000077500000000000000000000000001502361351200220055ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/codes/000077500000000000000000000000001502361351200231025ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/codes/rafael/000077500000000000000000000000001502361351200243345ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/codes/rafael/asmjdkbridge/000077500000000000000000000000001502361351200267625ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/codes/rafael/asmjdkbridge/AsmAttribute.java000066400000000000000000000367761502361351200322540ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ConstantDynamic; import org.objectweb.asm.Handle; import org.objectweb.asm.Type; import java.lang.classfile.AttributeMapper; import java.lang.classfile.AttributedElement; import java.lang.classfile.BufWriter; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassReader; import java.lang.classfile.CodeModel; import java.lang.classfile.CustomAttribute; import java.lang.classfile.FieldModel; import java.lang.classfile.MethodModel; import java.lang.classfile.attribute.RecordComponentInfo; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantDynamicEntry; import java.lang.classfile.constantpool.DoubleEntry; import java.lang.classfile.constantpool.FloatEntry; import java.lang.classfile.constantpool.IntegerEntry; import java.lang.classfile.constantpool.LongEntry; import java.lang.classfile.constantpool.MethodHandleEntry; import java.lang.classfile.constantpool.ModuleEntry; import java.lang.classfile.constantpool.PackageEntry; import java.lang.classfile.constantpool.StringEntry; import java.lang.classfile.constantpool.Utf8Entry; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicCallSiteDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodTypeDesc; import java.lang.constant.ModuleDesc; import java.lang.constant.PackageDesc; import java.util.function.IntFunction; import java.util.function.Supplier; class AsmAttribute extends CustomAttribute { final Attribute attribute; static AsmAttribute of(Attribute attribute) { return new AsmAttribute(new AttributeMapper<>() { @Override public String name() { return attribute.type; } @Override public AsmAttribute readAttribute(AttributedElement attributedElement, ClassReader classReader, int payloadStart) { return new AsmAttribute(this, Attribute.read(attribute, new DelegatingClassReader(classReader.readBytes(0, classReader.classfileLength()), classReader, () -> switch (attributedElement) { case ClassModel model -> model; case CodeModel model -> model.parent().orElseThrow(IllegalStateException::new).parent().orElseThrow(IllegalStateException::new); case FieldModel model -> model.parent().orElseThrow(IllegalStateException::new); case MethodModel model -> model.parent().orElseThrow(IllegalStateException::new); case RecordComponentInfo _ -> throw new IllegalStateException(); }), payloadStart, classReader.readInt(payloadStart - 4), null, -1, null)); } @Override public void writeAttribute(BufWriter bufWriter, AsmAttribute asmAttribute) { bufWriter.writeIndex(bufWriter.constantPool().utf8Entry(asmAttribute.attribute.type)); byte[] bytes = Attribute.write(asmAttribute.attribute, new DelegatingClassWriter(bufWriter), null, 0, -1, -1); bufWriter.writeInt(bytes.length); bufWriter.writeBytes(bytes); } @Override public AttributeStability stability() { return attribute.isUnknown() ? AttributeStability.UNKNOWN : AttributeStability.UNSTABLE; } }, attribute); } private AsmAttribute(AttributeMapper mapper, Attribute attribute) { super(mapper); this.attribute = attribute; } private static class DelegatingClassReader extends org.objectweb.asm.ClassReader { private final java.lang.classfile.ClassReader delegate; private final Supplier model; private DelegatingClassReader(byte[] bytes, java.lang.classfile.ClassReader delegate, Supplier model) { super(bytes); this.delegate = delegate; this.model = model; } @Override public int getAccess() { throw new UnsupportedOperationException(); } @Override public String getClassName() { return delegate.thisClassEntry().asInternalName(); } @Override public String getSuperName() { return delegate.superclassEntry().map(ClassEntry::asInternalName).orElse(null); } @Override public String[] getInterfaces() { return model.get().interfaces().stream().map(ClassEntry::asInternalName).toArray(String[]::new); } @Override public void accept(ClassVisitor classVisitor, int parsingOptions) { throw new UnsupportedOperationException(); } @Override public void accept(ClassVisitor classVisitor, Attribute[] attributePrototypes, int parsingOptions) { throw new UnsupportedOperationException(); } @Override public int getItemCount() { return delegate.size(); } @Override public int getItem(int constantPoolEntryIndex) { throw new UnsupportedOperationException(); } @Override public int getMaxStringLength() { return 0; // no need for buffers, keep size minimal. } @Override public int readByte(int offset) { return delegate.readU1(offset); } @Override public int readUnsignedShort(int offset) { if (delegate == null) { return 0; } return delegate.readU2(offset); } @Override public short readShort(int offset) { if (delegate == null) { return 0; } return (short) delegate.readS2(offset); } @Override public int readInt(int offset) { return delegate.readInt(offset); } @Override public long readLong(int offset) { return delegate.readLong(offset); } @Override public String readUTF8(int offset, char[] charBuffer) { return delegate.readEntry(offset, Utf8Entry.class).stringValue(); } @Override public String readClass(int offset, char[] charBuffer) { return delegate.readEntry(offset, ClassEntry.class).name().stringValue(); } @Override public String readModule(int offset, char[] charBuffer) { return delegate.readEntry(offset, ModuleEntry.class).name().stringValue(); } @Override public String readPackage(int offset, char[] charBuffer) { return delegate.readEntry(offset, PackageEntry.class).name().stringValue(); } @Override public Object readConst(int constantPoolEntryIndex, char[] charBuffer) { return switch (delegate.entryByIndex(constantPoolEntryIndex)) { case IntegerEntry entry -> entry.intValue(); case LongEntry entry -> entry.longValue(); case FloatEntry entry -> entry.floatValue(); case DoubleEntry entry -> entry.doubleValue(); case StringEntry entry -> entry.stringValue(); case ClassEntry entry -> Type.getType("L" + entry.asInternalName() + ";"); case MethodHandleEntry entry -> JdkClassReader.toAsmConstant(entry.asSymbol()); case ConstantDynamicEntry entry -> JdkClassReader.toAsmConstant(entry.asSymbol()); default -> throw new IllegalArgumentException(); }; } } private static class DelegatingClassWriter extends ClassWriter { private final BufWriter delegate; private DelegatingClassWriter(BufWriter delegate) { super(0); this.delegate = delegate; } @Override public boolean hasFlags(int flags) { throw new UnsupportedOperationException(); } @Override public byte[] toByteArray() { throw new UnsupportedOperationException(); } @Override public int newConst(Object value) { return (switch (value) { case Boolean constant -> delegate.constantPool().intEntry(constant ? 1 : 0).index(); case Byte constant -> delegate.constantPool().intEntry(constant).index(); case Short constant -> delegate.constantPool().intEntry(constant).index(); case Character constant -> delegate.constantPool().intEntry(constant).index(); case Integer constant -> delegate.constantPool().intEntry(constant).index(); case Long constant -> delegate.constantPool().longEntry(constant).index(); case Float constant -> delegate.constantPool().floatEntry(constant).index(); case Double constant -> delegate.constantPool().doubleEntry(constant).index(); case String constant -> delegate.constantPool().stringEntry(constant).index(); case Type constant -> (switch (constant.getSort()) { case Type.OBJECT -> delegate.constantPool().classEntry(ClassDesc.ofInternalName(constant.getInternalName())); case Type.METHOD -> delegate.constantPool().methodTypeEntry(MethodTypeDesc.ofDescriptor(constant.getDescriptor())); default -> delegate.constantPool().classEntry(ClassDesc.ofDescriptor(constant.getDescriptor())); }).index(); case Handle constant -> newHandle(constant.getTag(), constant.getOwner(), constant.getName(), constant.getDesc(), constant.isInterface()); case ConstantDynamic constant -> newConstantDynamic(constant.getName(), constant.getDescriptor(), constant.getBootstrapMethod(), constant.getBootstrapMethodArgumentCount(), constant::getBootstrapMethodArgument); default -> throw new IllegalArgumentException(); }); } @Override public int newUTF8(String value) { return delegate.constantPool().utf8Entry(value).index(); } @Override public int newClass(String value) { return delegate.constantPool().classEntry(ClassDesc.ofInternalName(value)).index(); } @Override public int newMethodType(String methodDescriptor) { return delegate.constantPool().methodTypeEntry(MethodTypeDesc.ofDescriptor(methodDescriptor)).index(); } @Override public int newModule(String moduleName) { return delegate.constantPool().moduleEntry(ModuleDesc.of(moduleName)).index(); } @Override public int newPackage(String packageName) { return delegate.constantPool().packageEntry(PackageDesc.ofInternalName(packageName)).index(); } @Override public int newHandle(int tag, String owner, String name, String descriptor, boolean isInterface) { return delegate.constantPool().methodHandleEntry(MethodHandleDesc.of( DirectMethodHandleDesc.Kind.valueOf(tag, isInterface), ClassDesc.ofInternalName(owner), name, descriptor)).index(); } @Override public int newConstantDynamic(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { return newConstantDynamic(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments.length, index -> bootstrapMethodArguments[index]); } private int newConstantDynamic(String name, String descriptor, Handle bootstrapMethodHandle, int length, IntFunction resolver) { ConstantDesc[] constants = new ConstantDesc[length]; for (int index = 0; index < length; index++) { constants[index] = JdkClassWriter.toConstantDesc(resolver.apply(index)); } return delegate.constantPool().constantDynamicEntry(DynamicConstantDesc.ofNamed( MethodHandleDesc.of( DirectMethodHandleDesc.Kind.valueOf(bootstrapMethodHandle.getTag(), bootstrapMethodHandle.isInterface()), ClassDesc.ofInternalName(bootstrapMethodHandle.getOwner()), bootstrapMethodHandle.getName(), bootstrapMethodHandle.getDesc()), name, ClassDesc.ofDescriptor(descriptor), constants)).index(); } @Override public int newInvokeDynamic(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { ConstantDesc[] constants = new ConstantDesc[bootstrapMethodArguments.length]; for (int index = 0; index < bootstrapMethodArguments.length; index++) { constants[index] = JdkClassWriter.toConstantDesc(bootstrapMethodArguments[index]); } return delegate.constantPool().invokeDynamicEntry(DynamicCallSiteDesc.of( MethodHandleDesc.of( DirectMethodHandleDesc.Kind.valueOf(bootstrapMethodHandle.getTag(), bootstrapMethodHandle.isInterface()), ClassDesc.ofInternalName(bootstrapMethodHandle.getOwner()), bootstrapMethodHandle.getName(), bootstrapMethodHandle.getDesc()), name, MethodTypeDesc.ofDescriptor(descriptor), constants)).index(); } @Override public int newField(String owner, String name, String descriptor) { return delegate.constantPool().fieldRefEntry( ClassDesc.ofInternalName(owner), name, ClassDesc.ofDescriptor(descriptor)).index(); } @Override public int newMethod(String owner, String name, String descriptor, boolean isInterface) { if (isInterface) { return delegate.constantPool().interfaceMethodRefEntry( ClassDesc.ofInternalName(owner), name, MethodTypeDesc.ofDescriptor(descriptor)).index(); } else { return delegate.constantPool().methodRefEntry( ClassDesc.ofInternalName(owner), name, MethodTypeDesc.ofDescriptor(descriptor)).index(); } } @Override public int newNameType(String name, String descriptor) { return delegate.constantPool().nameAndTypeEntry( name, ClassDesc.ofDescriptor(descriptor)).index(); } } } AsmWrappedAttribute.java000066400000000000000000000155371502361351200335100ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/codes/rafael/asmjdkbridgepackage codes.rafael.asmjdkbridge; import org.objectweb.asm.Attribute; import org.objectweb.asm.ByteVector; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import java.lang.classfile.attribute.CharacterRangeInfo; import java.lang.classfile.attribute.CharacterRangeTableAttribute; import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.attribute.CompilationIDAttribute; import java.lang.classfile.attribute.ModuleHashInfo; import java.lang.classfile.attribute.ModuleHashesAttribute; import java.lang.classfile.attribute.ModuleResolutionAttribute; import java.lang.classfile.attribute.SourceIDAttribute; import java.lang.classfile.attribute.UnknownAttribute; import java.lang.classfile.instruction.CharacterRange; import java.util.List; abstract class AsmWrappedAttribute> extends Attribute { final A attribute; protected AsmWrappedAttribute(A attribute) { super(attribute.attributeName().stringValue()); this.attribute = attribute; } static T unwrap(Attribute attribute, Class type) { return type.cast(attribute instanceof AsmWrappedAttribute wrappedAttribute ? type.cast(wrappedAttribute.attribute) : type.cast(AsmAttribute.of(attribute))); } @Override protected final Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) { throw new UnsupportedOperationException(); } @Override protected abstract ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals); @Override public final boolean isUnknown() { return false; } static class AsmCompilationIdAttribute extends AsmWrappedAttribute { AsmCompilationIdAttribute(CompilationIDAttribute attribute) { super(attribute); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { int index = classWriter.newUTF8(attribute.compilationId().stringValue()); ByteVector byteVector = new ByteVector(2); byteVector.putShort(index); return byteVector; } } static class AsmModuleHashesAttribute extends AsmWrappedAttribute { AsmModuleHashesAttribute(ModuleHashesAttribute attribute) { super(attribute); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { ByteVector bytes = new ByteVector(); bytes.putShort(classWriter.newUTF8(attribute.algorithm().stringValue())); bytes.putShort(attribute.hashes().size()); for (ModuleHashInfo info : attribute.hashes()) { bytes.putShort(classWriter.newModule(info.moduleName().name().stringValue())); bytes.putShort(info.hash().length); bytes.putByteArray(info.hash(), 0, info.hash().length); } return bytes; } } static class AsmModuleResolutionAttribute extends AsmWrappedAttribute { AsmModuleResolutionAttribute(ModuleResolutionAttribute attribute) { super(attribute); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { ByteVector byteVector = new ByteVector(2); byteVector.putShort(attribute.resolutionFlags()); return byteVector; } } static class AsmSourceIdAttribute extends AsmWrappedAttribute { AsmSourceIdAttribute(SourceIDAttribute attribute) { super(attribute); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { int index = classWriter.newUTF8(attribute.sourceId().stringValue()); ByteVector byteVector = new ByteVector(2); byteVector.putShort(index); return byteVector; } } static class AsmCharacterRangeTableAttribute extends AsmWrappedAttribute { // Can never be retained as instructions might change BCIs. AsmCharacterRangeTableAttribute(CharacterRangeTableAttribute attribute) { super(attribute); } // Must be recalculated as label BCIs might change when code instructions are altered. Should be delayed to option to resolve by righter static AsmWrappedAttribute of(List characterRanges, CodeAttribute codeAttribute) { return new AsmCharacterRangeTableAttribute(CharacterRangeTableAttribute.of(characterRanges.stream().map(characterRange -> CharacterRangeInfo.of( codeAttribute.labelToBci(characterRange.startScope()), codeAttribute.labelToBci(characterRange.endScope()), characterRange.characterRangeStart(), characterRange.characterRangeEnd(), characterRange.flags())).toList())); } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { ByteVector byteVector = new ByteVector(2 + 14 * attribute.characterRangeTable().size()); byteVector.putShort(attribute.characterRangeTable().size()); for (CharacterRangeInfo characterRangeInfo : attribute.characterRangeTable()) { byteVector.putShort(characterRangeInfo.startPc()); byteVector.putShort(characterRangeInfo.endPc()); byteVector.putInt(characterRangeInfo.characterRangeStart()); byteVector.putInt(characterRangeInfo.characterRangeEnd()); byteVector.putShort(characterRangeInfo.flags()); } return byteVector; } @Override public boolean isCodeAttribute() { return true; } } static class AsmUnknownAttribute extends AsmWrappedAttribute { private final boolean code; AsmUnknownAttribute(UnknownAttribute attribute, boolean code) { super(attribute); this.code = code; } @Override protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) { ByteVector vector = new ByteVector(attribute.contents().length); vector.putByteArray(attribute.contents(), 0, attribute.contents().length); return vector; } @Override public boolean isCodeAttribute() { return code; } } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/codes/rafael/asmjdkbridge/JdkClassReader.java000066400000000000000000001600421502361351200324510ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ConstantDynamic; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Handle; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.ModuleVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.RecordComponentVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; import org.objectweb.asm.TypeReference; import java.io.IOException; import java.io.InputStream; import java.lang.classfile.Annotation; import java.lang.classfile.AnnotationElement; import java.lang.classfile.AnnotationValue; import java.lang.classfile.AttributeMapper; import java.lang.classfile.AttributedElement; import java.lang.classfile.Attributes; import java.lang.classfile.ClassFile; import java.lang.classfile.ClassModel; import java.lang.classfile.CodeElement; import java.lang.classfile.FieldModel; import java.lang.classfile.Instruction; import java.lang.classfile.Label; import java.lang.classfile.MethodModel; import java.lang.classfile.Opcode; import java.lang.classfile.TypeAnnotation; import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeVisibleParameterAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute; import java.lang.classfile.attribute.StackMapFrameInfo; import java.lang.classfile.attribute.UnknownAttribute; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.Utf8Entry; import java.lang.classfile.instruction.ArrayLoadInstruction; import java.lang.classfile.instruction.ArrayStoreInstruction; import java.lang.classfile.instruction.BranchInstruction; import java.lang.classfile.instruction.CharacterRange; import java.lang.classfile.instruction.ConstantInstruction; import java.lang.classfile.instruction.ConvertInstruction; import java.lang.classfile.instruction.DiscontinuedInstruction; import java.lang.classfile.instruction.ExceptionCatch; import java.lang.classfile.instruction.FieldInstruction; import java.lang.classfile.instruction.IncrementInstruction; import java.lang.classfile.instruction.InvokeDynamicInstruction; import java.lang.classfile.instruction.InvokeInstruction; import java.lang.classfile.instruction.LabelTarget; import java.lang.classfile.instruction.LineNumber; import java.lang.classfile.instruction.LoadInstruction; import java.lang.classfile.instruction.LocalVariable; import java.lang.classfile.instruction.LocalVariableType; import java.lang.classfile.instruction.LookupSwitchInstruction; import java.lang.classfile.instruction.MonitorInstruction; import java.lang.classfile.instruction.NewMultiArrayInstruction; import java.lang.classfile.instruction.NewObjectInstruction; import java.lang.classfile.instruction.NewPrimitiveArrayInstruction; import java.lang.classfile.instruction.NewReferenceArrayInstruction; import java.lang.classfile.instruction.NopInstruction; import java.lang.classfile.instruction.OperatorInstruction; import java.lang.classfile.instruction.ReturnInstruction; import java.lang.classfile.instruction.StackInstruction; import java.lang.classfile.instruction.StoreInstruction; import java.lang.classfile.instruction.SwitchCase; import java.lang.classfile.instruction.TableSwitchInstruction; import java.lang.classfile.instruction.ThrowInstruction; import java.lang.classfile.instruction.TypeCheckInstruction; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodTypeDesc; import java.lang.reflect.AccessFlag; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.SequencedMap; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; /** * A reader for class files that uses the JDK class file API. The created class reader is immutable. */ public class JdkClassReader { private static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251; private final ClassModel classModel; private final AttributeFunction attributes; /** * Creates a new class reader. * * @param classFile The class file to represent. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. */ public JdkClassReader(byte[] classFile, Attribute... attributePrototypes) { attributes = new AttributeFunction(attributePrototypes); classModel = ClassFile.of(ClassFile.AttributeMapperOption.of(attributes)).parse(classFile); } /** * Creates a new class reader. * * @param inputStream An input stream of the class file to represent. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. * @throws IOException If the stream cannot be read. */ public JdkClassReader(InputStream inputStream, Attribute... attributePrototypes) throws IOException { this(inputStream.readAllBytes(), attributePrototypes); } /** * Creates a new class reader. * * @param className The name of the class to represent. The class must be resolvable from the system loader. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. * @throws IOException If the class file cannot be read. */ public JdkClassReader(String className, Attribute... attributePrototypes) throws IOException { attributes = new AttributeFunction(attributePrototypes); ClassFile classFile = ClassFile.of(ClassFile.AttributeMapperOption.of(attributes)); try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class")) { classModel = classFile.parse(inputStream.readAllBytes()); } } ClassModel getClassModel() { return classModel; } public int getAccess() { return classModel.flags().flagsMask(); } public String getClassName() { return classModel.thisClass().asInternalName(); } /** * Returns the super class name of this class or {@code null} for {@link Object}. * * @return The super class name of this class or {@code null} for {@link Object}. */ public String getSuperName() { return classModel.superclass().map(ClassEntry::asInternalName).orElse(null); } /** * Returns the interface names of this class. * * @return The interface names of this class. */ public String[] getInterfaces() { return classModel.interfaces().stream().map(ClassEntry::asInternalName).toArray(String[]::new); } /** * Accepts a class visitor for the represented class file. * * @param classVisitor The class visitor to delegate calls to. * @param flags The ASM flags to consider when visiting the class file. */ public void accept(ClassVisitor classVisitor, int flags) { Map labels = new HashMap<>(); classVisitor.visit(classModel.minorVersion() << 16 | classModel.majorVersion(), getAccess() | (classModel.findAttribute(Attributes.deprecated()).isPresent() ? Opcodes.ACC_DEPRECATED : 0) | (classModel.findAttribute(Attributes.synthetic()).isPresent() ? Opcodes.ACC_SYNTHETIC : 0) | (classModel.findAttribute(Attributes.record()).isPresent() ? Opcodes.ACC_RECORD : 0), getClassName(), classModel.findAttribute(Attributes.signature()).map(signature -> signature.signature().stringValue()).orElse(null), getSuperName(), getInterfaces()); if ((flags & ClassReader.SKIP_DEBUG) == 0) { String sourceFile = classModel.findAttribute(Attributes.sourceFile()) .map(attribute -> attribute.sourceFile().stringValue()) .orElse(null); String debug = classModel.findAttribute(Attributes.sourceDebugExtension()) .map(attribute -> new String(attribute.contents(), StandardCharsets.UTF_8)) .orElse(null); if (sourceFile != null || debug != null) { classVisitor.visitSource(sourceFile, debug); } } classModel.findAttribute(Attributes.module()).ifPresent(module -> { String moduleName = module.moduleName().name().stringValue(); int moduleFlags = module.moduleFlagsMask(); String moduleVersion = module.moduleVersion().map(Utf8Entry::stringValue).orElse(null); ModuleVisitor moduleVisitor = classVisitor.visitModule(moduleName, moduleFlags, moduleVersion); if (moduleVisitor instanceof JdkClassWriter.WritingModuleVisitor writingModuleVisitor && writingModuleVisitor.has(classModel, moduleName, moduleFlags, moduleVersion)) { classModel.findAttribute(Attributes.moduleMainClass()).ifPresent(writingModuleVisitor::add); classModel.findAttribute(Attributes.modulePackages()).ifPresent(writingModuleVisitor::add); writingModuleVisitor.add(module); } else if (moduleVisitor != null) { classModel.findAttribute(Attributes.moduleMainClass()) .map(moduleMainClass -> moduleMainClass.mainClass().asInternalName()) .ifPresent(moduleVisitor::visitMainClass); classModel.findAttribute(Attributes.modulePackages()).stream() .flatMap(modulePackagesAttribute -> modulePackagesAttribute.packages().stream()) .map(packageName -> packageName.name().stringValue()) .forEach(moduleVisitor::visitPackage); module.requires().forEach(moduleRequire -> moduleVisitor.visitRequire(moduleRequire.requires().name().stringValue(), moduleRequire.requiresFlagsMask(), moduleRequire.requiresVersion().map(Utf8Entry::stringValue).orElse(null))); module.exports().forEach(moduleExport -> moduleVisitor.visitExport(moduleExport.exportedPackage().name().stringValue(), moduleExport.exportsFlagsMask(), moduleExport.exportsTo().isEmpty() ? null : moduleExport.exportsTo().stream().map(to -> to.name().stringValue()).toArray(String[]::new))); module.opens().forEach(moduleOpen -> moduleVisitor.visitOpen(moduleOpen.openedPackage().name().stringValue(), moduleOpen.opensFlagsMask(), moduleOpen.opensTo().isEmpty() ? null : moduleOpen.opensTo().stream().map(to -> to.name().stringValue()).toArray(String[]::new))); module.uses().forEach(moduleUses -> moduleVisitor.visitUse(moduleUses.asInternalName())); module.provides().forEach(provideInfo -> moduleVisitor.visitProvide( provideInfo.provides().asInternalName(), provideInfo.providesWith().stream().map(ClassEntry::asInternalName).toArray(String[]::new))); moduleVisitor.visitEnd(); } }); classModel.findAttribute(Attributes.nestHost()).ifPresent(nestHost -> classVisitor.visitNestHost(nestHost.nestHost().asInternalName())); classModel.findAttribute(Attributes.enclosingMethod()).ifPresent(enclosingMethod -> classVisitor.visitOuterClass(enclosingMethod.enclosingClass().asInternalName(), enclosingMethod.enclosingMethod().map(value -> value.name().stringValue()).orElse(null), enclosingMethod.enclosingMethod().map(value -> value.type().stringValue()).orElse(null))); acceptAnnotations(classModel, classVisitor::visitAnnotation, classVisitor::visitTypeAnnotation); classModel.findAttribute(Attributes.sourceId()).ifPresent(sourceIDAttribute -> classVisitor.visitAttribute(new AsmWrappedAttribute.AsmSourceIdAttribute(sourceIDAttribute))); classModel.findAttribute(Attributes.compilationId()).ifPresent(sourceIDAttribute -> classVisitor.visitAttribute(new AsmWrappedAttribute.AsmCompilationIdAttribute(sourceIDAttribute))); classModel.findAttribute(Attributes.moduleResolution()).ifPresent(moduleResolutionAttribute -> classVisitor.visitAttribute(new AsmWrappedAttribute.AsmModuleResolutionAttribute(moduleResolutionAttribute))); classModel.findAttribute(Attributes.moduleHashes()).ifPresent(moduleResolutionAttribute -> classVisitor.visitAttribute(new AsmWrappedAttribute.AsmModuleHashesAttribute(moduleResolutionAttribute))); acceptAttributes(classModel, false, classVisitor::visitAttribute); classModel.findAttribute(Attributes.nestMembers()).stream() .flatMap(nestMembers -> nestMembers.nestMembers().stream()) .forEach(nestMember -> classVisitor.visitNestMember(nestMember.asInternalName())); classModel.findAttribute(Attributes.permittedSubclasses()).stream() .flatMap(permittedSubclasses -> permittedSubclasses.permittedSubclasses().stream()) .forEach(nestMember -> classVisitor.visitPermittedSubclass(nestMember.asInternalName())); classModel.findAttribute(Attributes.innerClasses()).stream() .flatMap(innerClasses -> innerClasses.classes().stream()) .forEach(innerClass -> classVisitor.visitInnerClass(innerClass.innerClass().asInternalName(), innerClass.outerClass().map(ClassEntry::asInternalName).orElse(null), innerClass.innerName().map(Utf8Entry::stringValue).orElse(null), innerClass.flagsMask())); classModel.findAttribute(Attributes.record()).stream() .flatMap(record -> record.components().stream()) .forEach(recordComponent -> { RecordComponentVisitor recordComponentVisitor = classVisitor.visitRecordComponent(recordComponent.name().stringValue(), recordComponent.descriptor().stringValue(), recordComponent.findAttribute(Attributes.signature()).map(signature -> signature.signature().stringValue()).orElse(null)); if (recordComponentVisitor != null) { acceptAnnotations(recordComponent, recordComponentVisitor::visitAnnotation, recordComponentVisitor::visitTypeAnnotation); acceptAttributes(recordComponent, false, recordComponentVisitor::visitAttribute); recordComponentVisitor.visitEnd(); } }); for (FieldModel fieldModel : classModel.fields()) { int fieldFlags = fieldModel.flags().flagsMask() | (fieldModel.findAttribute(Attributes.deprecated()).isPresent() ? Opcodes.ACC_DEPRECATED : 0) | (fieldModel.findAttribute(Attributes.synthetic()).isPresent() ? Opcodes.ACC_SYNTHETIC : 0); String fieldName = fieldModel.fieldName().stringValue(); String fieldType = fieldModel.fieldType().stringValue(); String fieldSignature = fieldModel.findAttribute(Attributes.signature()).map(signature -> signature.signature().stringValue()).orElse(null); Object fieldConstant = fieldModel.findAttribute(Attributes.constantValue()).map(constantValue -> toAsmConstant(constantValue.constant().constantValue())).orElse(null); FieldVisitor fieldVisitor = classVisitor.visitField(fieldFlags, fieldName, fieldType, fieldSignature, fieldConstant); if (fieldVisitor instanceof JdkClassWriter.WritingFieldVisitor writingFieldVisitor && writingFieldVisitor.has(classModel, fieldFlags, fieldName, fieldType, fieldSignature, fieldConstant)) { writingFieldVisitor.add(fieldModel); } else if (fieldVisitor != null) { acceptAnnotations(fieldModel, fieldVisitor::visitAnnotation, fieldVisitor::visitTypeAnnotation); acceptAttributes(fieldModel, false, fieldVisitor::visitAttribute); fieldVisitor.visitEnd(); } } for (MethodModel methodModel : classModel.methods()) { int methodFlags = methodModel.flags().flagsMask() | (methodModel.findAttribute(Attributes.deprecated()).isPresent() ? Opcodes.ACC_DEPRECATED : 0) | (methodModel.findAttribute(Attributes.synthetic()).isPresent() ? Opcodes.ACC_SYNTHETIC : 0); String methodName = methodModel.methodName().stringValue(); String methodType = methodModel.methodType().stringValue(); String methodSignature = methodModel.findAttribute(Attributes.signature()).map(signature -> signature.signature().stringValue()).orElse(null); String[] methodExceptions = methodModel.findAttribute(Attributes.exceptions()).map(exceptions -> exceptions.exceptions().stream().map(ClassEntry::asInternalName).toArray(String[]::new)).orElse(null); MethodVisitor methodVisitor = classVisitor.visitMethod(methodFlags, methodName, methodType, methodSignature, methodExceptions); if (methodVisitor instanceof JdkClassWriter.WritingMethodVisitor writingMethodVisitor && writingMethodVisitor.has(classModel, methodFlags, methodName, methodType, methodSignature, methodExceptions)) { writingMethodVisitor.add(methodModel); } else if (methodVisitor != null) { if ((flags & ClassReader.SKIP_DEBUG) == 0) { methodModel.findAttribute(Attributes.methodParameters()).stream() .flatMap(methodParameters -> methodParameters.parameters().stream()) .forEach(methodParameter -> methodVisitor.visitParameter(methodParameter.name().map(Utf8Entry::stringValue).orElse(null), methodParameter.flagsMask())); } methodModel.findAttribute(Attributes.annotationDefault()).ifPresent(annotationDefault -> { AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault(); if (annotationVisitor != null) { appendAnnotationValue(annotationVisitor, null, annotationDefault.defaultValue()); annotationVisitor.visitEnd(); } }); acceptAnnotations(methodModel, methodVisitor::visitAnnotation, methodVisitor::visitTypeAnnotation); acceptParameterAnnotations(methodModel, methodVisitor, true); acceptParameterAnnotations(methodModel, methodVisitor, false); acceptAttributes(methodModel, false, methodVisitor::visitAttribute); methodModel.findAttribute(Attributes.code()).filter(_ -> (flags & ClassReader.SKIP_CODE) == 0).ifPresent(code -> { int localVariablesSize = Type.getMethodType(methodModel.methodType().stringValue()).getArgumentTypes().length + (methodModel.flags().has(AccessFlag.STATIC) ? 0 : 1); Map frames = (flags & ClassReader.SKIP_FRAMES) == 0 ? code.findAttribute(Attributes.stackMapTable()) .map(stackMapTable -> stackMapTable.entries().stream().collect(Collectors.toMap(StackMapFrameInfo::target, Function.identity()))) .orElse(Collections.emptyMap()) : Map.of(); SequencedMap localVariables = new LinkedHashMap<>(); Map>> offsetTypeAnnotations = new IdentityHashMap<>(); List> localVariableAnnotations = new ArrayList<>(); List characterRanges = new ArrayList<>(); methodVisitor.visitCode(); org.objectweb.asm.Label currentPositionLabel = null; PushbackIterator it = new PushbackIterator<>(code.iterator()); while (it.hasNext()) { CodeElement element = it.next(); switch (element) { case MonitorInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case TypeCheckInstruction value -> methodVisitor.visitTypeInsn(value.opcode().bytecode(), value.type().asInternalName()); case LoadInstruction value -> methodVisitor.visitVarInsn(switch (value.typeKind()) { case BOOLEAN, BYTE, CHAR, SHORT, INT -> Opcodes.ILOAD; case LONG -> Opcodes.LLOAD; case FLOAT -> Opcodes.FLOAD; case DOUBLE -> Opcodes.DLOAD; case REFERENCE -> Opcodes.ALOAD; default -> throw new IllegalStateException("Unexpected type: " + value.typeKind()); }, value.slot()); case OperatorInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case ReturnInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case InvokeInstruction value -> methodVisitor.visitMethodInsn(value.opcode().bytecode(), value.owner().asInternalName(), value.name().stringValue(), value.type().stringValue(), value.isInterface()); case IncrementInstruction value -> methodVisitor.visitIincInsn(value.slot(), value.constant()); case FieldInstruction value -> methodVisitor.visitFieldInsn(value.opcode().bytecode(), value.owner().asInternalName(), value.name().stringValue(), value.type().stringValue()); case InvokeDynamicInstruction value -> methodVisitor.visitInvokeDynamicInsn(value.name().stringValue(), value.type().stringValue(), (Handle) toAsmConstant(value.bootstrapMethod()), value.bootstrapArgs().stream().map(JdkClassReader::toAsmConstant).toArray()); case BranchInstruction value -> methodVisitor.visitJumpInsn( value.opcode() == Opcode.GOTO_W ? Opcodes.GOTO : value.opcode().bytecode(), labels.computeIfAbsent(value.target(), _ -> new org.objectweb.asm.Label())); case StoreInstruction value -> methodVisitor.visitVarInsn(switch (value.typeKind()) { case BOOLEAN, BYTE, CHAR, SHORT, INT -> Opcodes.ISTORE; case LONG -> Opcodes.LSTORE; case FLOAT -> Opcodes.FSTORE; case DOUBLE -> Opcodes.DSTORE; case REFERENCE -> Opcodes.ASTORE; default -> throw new IllegalStateException("Unexpected type: " + value.typeKind()); }, value.slot()); case NewReferenceArrayInstruction value -> methodVisitor.visitTypeInsn(value.opcode().bytecode(), value.componentType().asInternalName()); case LookupSwitchInstruction value -> { methodVisitor.visitLookupSwitchInsn(labels.computeIfAbsent(value.defaultTarget(), _ -> new org.objectweb.asm.Label()), value.cases().stream().mapToInt(SwitchCase::caseValue).toArray(), value.cases().stream().map(aCase -> labels.computeIfAbsent(aCase.target(), _ -> new org.objectweb.asm.Label())).toArray(org.objectweb.asm.Label[]::new)); } case TableSwitchInstruction value -> { Map cases = value.cases().stream().collect(Collectors.toMap(SwitchCase::caseValue, Function.identity())); org.objectweb.asm.Label dflt = labels.computeIfAbsent(value.defaultTarget(), _ -> new org.objectweb.asm.Label()); methodVisitor.visitTableSwitchInsn(value.lowValue(), value.highValue(), dflt, IntStream.rangeClosed(value.lowValue(), value.highValue()).mapToObj(index -> { SwitchCase switchCase = cases.get(index); return switchCase == null ? dflt : labels.computeIfAbsent(switchCase.target(), _ -> new org.objectweb.asm.Label()); }).toArray(org.objectweb.asm.Label[]::new)); } case ArrayStoreInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case ArrayLoadInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case ConstantInstruction value -> { switch (value.opcode()) { case LDC, LDC_W, LDC2_W -> methodVisitor.visitLdcInsn(toAsmConstant(value.constantValue())); case BIPUSH, SIPUSH -> methodVisitor.visitIntInsn(value.opcode().bytecode(), (Integer) value.constantValue()); default -> methodVisitor.visitInsn(value.opcode().bytecode()); } } case StackInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case NopInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case ThrowInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case NewObjectInstruction value -> methodVisitor.visitTypeInsn(value.opcode().bytecode(), value.className().asInternalName()); case ConvertInstruction value -> methodVisitor.visitInsn(value.opcode().bytecode()); case NewMultiArrayInstruction value -> methodVisitor.visitMultiANewArrayInsn(value.arrayType().asInternalName(), value.dimensions()); case NewPrimitiveArrayInstruction value -> methodVisitor.visitIntInsn(value.opcode().bytecode(), value.typeKind().newarrayCode()); case LocalVariableType value -> localVariables.compute(new MergedLocalVariableKey( labels.computeIfAbsent(value.startScope(), _ -> new org.objectweb.asm.Label()), labels.computeIfAbsent(value.endScope(), _ -> new org.objectweb.asm.Label()), value.name().stringValue(), value.slot() ), (_, values) -> new MergedLocalVariableValue(values == null ? null : values.descriptor, value.signature().stringValue())); case ExceptionCatch value -> methodVisitor.visitTryCatchBlock(labels.computeIfAbsent(value.tryStart(), _ -> new org.objectweb.asm.Label()), labels.computeIfAbsent(value.tryEnd(), _ -> new org.objectweb.asm.Label()), labels.computeIfAbsent(value.handler(), _ -> new org.objectweb.asm.Label()), value.catchType().map(ClassEntry::asInternalName).orElse(null)); case LocalVariable value -> localVariables.compute(new MergedLocalVariableKey( labels.computeIfAbsent(value.startScope(), _ -> new org.objectweb.asm.Label()), labels.computeIfAbsent(value.endScope(), _ -> new org.objectweb.asm.Label()), value.name().stringValue(), value.slot() ), (_, values) -> new MergedLocalVariableValue(value.typeSymbol().descriptorString(), values == null ? null : values.signature)); case LineNumber value -> { if ((flags & ClassReader.SKIP_DEBUG) == 0) { if (currentPositionLabel == null) { currentPositionLabel = new org.objectweb.asm.Label(); methodVisitor.visitLabel(currentPositionLabel); } methodVisitor.visitLineNumber(value.line(), currentPositionLabel); } } case LabelTarget value -> { currentPositionLabel = labels.computeIfAbsent(value.label(), _ -> new org.objectweb.asm.Label()); methodVisitor.visitLabel(currentPositionLabel); StackMapFrameInfo frame = frames.get(value.label()); if (frame != null) { if ((flags & ClassReader.SKIP_DEBUG) == 0 && it.hasNext()) { // Assure same ordering of ASM and JDK class reader with respect to line numbers and frames. CodeElement next = it.next(); if (next instanceof LineNumber line) { methodVisitor.visitLineNumber(line.line(), currentPositionLabel); } else { it.push(next); } } if ((flags & ClassReader.EXPAND_FRAMES) != 0) { methodVisitor.visitFrame(Opcodes.F_NEW, frame.locals().size(), frame.locals().isEmpty() ? null : frame.locals().stream() .map(verificationTypeInfo -> toAsmFrameValue(verificationTypeInfo, labels)) .toArray(), frame.stack().size(), frame.stack().isEmpty() ? null : frame.stack().stream() .map(verificationTypeInfo -> toAsmFrameValue(verificationTypeInfo, labels)) .toArray()); } else if (frame.frameType() < 64) { methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } else if (frame.frameType() < 128) { methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{toAsmFrameValue(frame.stack().getFirst(), labels)}); } else if (frame.frameType() < SAME_LOCALS_1_STACK_ITEM_EXTENDED) { throw new IllegalArgumentException("Invalid stackmap frame type: " + frame.frameType()); } else if (frame.frameType() == SAME_LOCALS_1_STACK_ITEM_EXTENDED) { methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{toAsmFrameValue(frame.stack().getFirst(), labels)}); } else if (frame.frameType() < SAME_EXTENDED) { methodVisitor.visitFrame(Opcodes.F_CHOP, localVariablesSize - frame.locals().size(), null, 0, null); localVariablesSize = frame.locals().size(); } else if (frame.frameType() == SAME_EXTENDED) { methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } else if (frame.frameType() < SAME_EXTENDED + 4) { int appended = frame.locals().size() - localVariablesSize; methodVisitor.visitFrame(Opcodes.F_APPEND, appended, frame.locals().stream().skip(localVariablesSize).map(verificationTypeInfo -> toAsmFrameValue(verificationTypeInfo, labels)).toArray(), 0, null); localVariablesSize = frame.locals().size(); } else { methodVisitor.visitFrame(Opcodes.F_FULL, frame.locals().size(), frame.locals().stream().map(verificationTypeInfo -> toAsmFrameValue(verificationTypeInfo, labels)).toArray(), frame.stack().size(), frame.stack().stream().map(verificationTypeInfo -> toAsmFrameValue(verificationTypeInfo, labels)).toArray()); localVariablesSize = frame.locals().size(); } } } case CharacterRange characterRange -> characterRanges.add(characterRange); case RuntimeVisibleTypeAnnotationsAttribute value -> appendCodeAnnotations(value.annotations(), true, methodVisitor, labels, localVariableAnnotations, offsetTypeAnnotations); case RuntimeInvisibleTypeAnnotationsAttribute value -> appendCodeAnnotations(value.annotations(), false, methodVisitor, labels, localVariableAnnotations, offsetTypeAnnotations); case DiscontinuedInstruction.JsrInstruction value -> methodVisitor.visitJumpInsn( (value.opcode() == Opcode.JSR_W ? Opcode.JSR : value.opcode()).bytecode(), labels.computeIfAbsent(value.target(), _ -> new org.objectweb.asm.Label())); case DiscontinuedInstruction.RetInstruction value -> methodVisitor.visitVarInsn( (value.opcode() == Opcode.RET_W ? Opcode.RET : value.opcode()).bytecode(), value.slot()); default -> throw new UnsupportedOperationException("Unknown value: " + element); } if (element instanceof Instruction) { offsetTypeAnnotations.getOrDefault(currentPositionLabel, Collections.emptyList()).forEach(entry -> appendAnnotationValues(methodVisitor.visitInsnAnnotation( TypeReference.newTypeReference(entry.getKey().targetInfo().targetType().targetTypeValue()).getValue(), toTypePath(entry.getKey().targetPath()), entry.getKey().annotation().className().stringValue(), entry.getValue()), entry.getKey().annotation().elements())); currentPositionLabel = null; } } if ((flags & ClassReader.SKIP_DEBUG) == 0) { localVariables.forEach((key, value) -> methodVisitor.visitLocalVariable(key.name(), value.descriptor(), value.signature(), key.start(), key.end(), key.slot())); } localVariableAnnotations.forEach(entry -> { TypeAnnotation.LocalVarTarget target = (TypeAnnotation.LocalVarTarget) entry.getKey().targetInfo(); appendAnnotationValues(methodVisitor.visitLocalVariableAnnotation( TypeReference.newTypeReference(entry.getKey().targetInfo().targetType().targetTypeValue()).getValue(), toTypePath(entry.getKey().targetPath()), target.table().stream().map(localVarTargetInfo -> labels.get(localVarTargetInfo.startLabel())).toArray(org.objectweb.asm.Label[]::new), target.table().stream().map(localVarTargetInfo -> labels.get(localVarTargetInfo.endLabel())).toArray(org.objectweb.asm.Label[]::new), target.table().stream().mapToInt(TypeAnnotation.LocalVarTargetInfo::index).toArray(), entry.getKey().annotation().className().stringValue(), entry.getValue()), entry.getKey().annotation().elements()); }); code.findAttribute(Attributes.characterRangeTable()).ifPresent(_ -> methodVisitor.visitAttribute(AsmWrappedAttribute.AsmCharacterRangeTableAttribute.of(characterRanges, code))); acceptAttributes(code, true, methodVisitor::visitAttribute); methodVisitor.visitMaxs(code.maxStack(), code.maxLocals()); }); methodVisitor.visitEnd(); } } classVisitor.visitEnd(); } private void acceptAnnotations(AttributedElement element, AnnotationVisitorSource annotationVisitorSource, TypeAnnotationVisitorSource typeAnnotationVisitorSource) { element.findAttribute(Attributes.runtimeVisibleAnnotations()).stream() .flatMap(annotations -> annotations.annotations().stream()) .forEach(annotation -> appendAnnotationValues(annotationVisitorSource.visitAnnotation(annotation.className().stringValue(), true), annotation.elements())); element.findAttribute(Attributes.runtimeInvisibleAnnotations()).stream() .flatMap(annotations -> annotations.annotations().stream()) .forEach(annotation -> appendAnnotationValues(annotationVisitorSource.visitAnnotation(annotation.className().stringValue(), false), annotation.elements())); element.findAttribute(Attributes.runtimeVisibleTypeAnnotations()).stream() .flatMap(annotations -> annotations.annotations().stream()) .forEach(annotation -> appendAnnotationValues(typeAnnotationVisitorSource.visitTypeAnnotation(toTypeReference(annotation.targetInfo()).getValue(), toTypePath(annotation.targetPath()), annotation.annotation().className().stringValue(), true), annotation.annotation().elements())); element.findAttribute(Attributes.runtimeInvisibleTypeAnnotations()).stream() .flatMap(annotations -> annotations.annotations().stream()) .forEach(annotation -> appendAnnotationValues(typeAnnotationVisitorSource.visitTypeAnnotation(toTypeReference(annotation.targetInfo()).getValue(), toTypePath(annotation.targetPath()), annotation.annotation().className().stringValue(), false), annotation.annotation().elements())); } private void acceptParameterAnnotations(MethodModel methodModel, MethodVisitor methodVisitor, boolean visible) { int count = methodModel.findAttribute(Attributes.methodParameters()) .map(parameters -> (int) parameters.parameters().stream().filter(parameter -> !parameter.has(AccessFlag.SYNTHETIC) && !parameter.has(AccessFlag.MANDATED)).count()) .orElseGet(() -> methodModel.methodTypeSymbol().parameterCount()); Optional>> target = visible ? methodModel.findAttribute(Attributes.runtimeVisibleParameterAnnotations()).map(RuntimeVisibleParameterAnnotationsAttribute::parameterAnnotations) : methodModel.findAttribute(Attributes.runtimeInvisibleParameterAnnotations()).map(RuntimeInvisibleParameterAnnotationsAttribute::parameterAnnotations); target.ifPresent(annotations -> { methodVisitor.visitAnnotableParameterCount(count, visible); for (int index = 0; index < annotations.size(); index++) { for (Annotation annotation : annotations.get(index)) { appendAnnotationValues(methodVisitor.visitParameterAnnotation(index, annotation.className().stringValue(), visible), annotation.elements()); } } }); } private void acceptAttributes(AttributedElement element, boolean code, Consumer consumer) { attributes.mappers.forEach((_, value) -> element .findAttributes(value.attributeMapper()) .forEach(attribute -> consumer.accept(attribute.attribute))); element.attributes().stream() .filter(attribute -> attribute instanceof java.lang.classfile.attribute.UnknownAttribute) .forEach(attribute -> consumer.accept(new AsmWrappedAttribute.AsmUnknownAttribute((UnknownAttribute) attribute, code))); } private void appendAnnotationValues(AnnotationVisitor annotationVisitor, List elements) { if (annotationVisitor != null) { elements.forEach(element -> appendAnnotationValue(annotationVisitor, element.name().stringValue(), element.value())); annotationVisitor.visitEnd(); } } private void appendAnnotationValue(AnnotationVisitor annotationVisitor, String name, AnnotationValue annotationValue) { if (annotationVisitor instanceof JdkClassWriter.WritingAnnotationVisitor writingAnnotationVisitor && writingAnnotationVisitor.has(classModel)) { writingAnnotationVisitor.add(name, annotationValue); return; } switch (annotationValue) { case AnnotationValue.OfConstant.OfBoolean value -> annotationVisitor.visit(name, value.booleanValue()); case AnnotationValue.OfConstant.OfByte value -> annotationVisitor.visit(name, value.byteValue()); case AnnotationValue.OfConstant.OfShort value -> annotationVisitor.visit(name, value.shortValue()); case AnnotationValue.OfConstant.OfChar value -> annotationVisitor.visit(name, value.charValue()); case AnnotationValue.OfConstant.OfInt value -> annotationVisitor.visit(name, value.intValue()); case AnnotationValue.OfConstant.OfLong value -> annotationVisitor.visit(name, value.longValue()); case AnnotationValue.OfConstant.OfFloat value -> annotationVisitor.visit(name, value.floatValue()); case AnnotationValue.OfConstant.OfDouble value -> annotationVisitor.visit(name, value.doubleValue()); case AnnotationValue.OfConstant.OfString value -> annotationVisitor.visit(name, toAsmConstant(value.stringValue())); case AnnotationValue.OfClass value -> annotationVisitor.visit(name, Type.getType(value.className().stringValue())); case AnnotationValue.OfAnnotation value -> appendAnnotationValues(annotationVisitor.visitAnnotation(name, value.annotation().className().stringValue()), value.annotation().elements()); case AnnotationValue.OfEnum value -> annotationVisitor.visitEnum(name, value.className().stringValue(), value.constantName().stringValue()); case AnnotationValue.OfArray value -> { Set tags = value.values().stream().map(AnnotationValue::tag).collect(Collectors.toSet()); if (tags.size() == 1) { // Handle arrays of primitive types as direct values. switch (tags.iterator().next()) { case AnnotationValue.TAG_BOOLEAN: { boolean[] array = new boolean[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfBoolean) value.values().get(index)).booleanValue(); } annotationVisitor.visit(name, array); return; } case AnnotationValue.TAG_BYTE: { byte[] array = new byte[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfByte) value.values().get(index)).byteValue(); } annotationVisitor.visit(name, array); return; } case AnnotationValue.TAG_SHORT: { short[] array = new short[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfShort) value.values().get(index)).shortValue(); } annotationVisitor.visit(name, array); return; } case AnnotationValue.TAG_CHAR: { char[] array = new char[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfChar) value.values().get(index)).charValue(); } annotationVisitor.visit(name, array); return; } case AnnotationValue.TAG_INT: { int[] array = new int[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfInt) value.values().get(index)).intValue(); } annotationVisitor.visit(name, array); return; } case AnnotationValue.TAG_LONG: { long[] array = new long[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfLong) value.values().get(index)).longValue(); } annotationVisitor.visit(name, array); return; } case AnnotationValue.TAG_FLOAT: { float[] array = new float[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfFloat) value.values().get(index)).floatValue(); } annotationVisitor.visit(name, array); return; } case AnnotationValue.TAG_DOUBLE: { double[] array = new double[value.values().size()]; for (int index = 0; index < value.values().size(); index++) { array[index] = ((AnnotationValue.OfConstant.OfDouble) value.values().get(index)).doubleValue(); } annotationVisitor.visit(name, array); return; } } } AnnotationVisitor nested = annotationVisitor.visitArray(name); if (nested != null) { value.values().forEach(entry -> appendAnnotationValue(nested, null, entry)); nested.visitEnd(); } } default -> throw new UnsupportedOperationException("Unknown annotation value: " + annotationValue); } } private void appendCodeAnnotations(List typeAnnotations, boolean visible, MethodVisitor methodVisitor, Map labels, List> localVariableAnnotations, Map>> offsetTypeAnnotations) { typeAnnotations.forEach(typeAnnotation -> { switch (typeAnnotation.targetInfo()) { case TypeAnnotation.LocalVarTarget ignored -> localVariableAnnotations.add(Map.entry(typeAnnotation, visible)); case TypeAnnotation.OffsetTarget value -> offsetTypeAnnotations.merge(labels.computeIfAbsent(value.target(), _ -> new org.objectweb.asm.Label()), Collections.singletonList(Map.entry(typeAnnotation, visible)), (left, right) -> Stream.of(left.stream(), right.stream()).flatMap(Function.identity()).collect(Collectors.toList())); case TypeAnnotation.CatchTarget value -> appendAnnotationValues(methodVisitor.visitTryCatchAnnotation( TypeReference.newTypeReference(value.targetType().targetTypeValue()).getValue(), toTypePath(typeAnnotation.targetPath()), typeAnnotation.annotation().className().stringValue(), visible), typeAnnotation.annotation().elements()); default -> throw new UnsupportedOperationException("Unexpected target: " + typeAnnotation.targetInfo()); } }); } static Object toAsmConstant(ConstantDesc constant) { return switch (constant) { case String value -> value; case Integer value -> value; case Long value -> value; case Float value -> value; case Double value -> value; case ClassDesc value -> Type.getType(value.descriptorString()); case MethodTypeDesc value -> Type.getMethodType(value.descriptorString()); case DirectMethodHandleDesc value -> new Handle(value.refKind(), toInternalName(value.owner()), value.methodName(), value.lookupDescriptor(), value.isOwnerInterface()); case MethodHandleDesc value -> throw new UnsupportedOperationException("Cannot map non-direct method handle to ASM constant: " + value); case DynamicConstantDesc value -> new ConstantDynamic(value.constantName(), value.constantType().descriptorString(), (Handle) toAsmConstant(value.bootstrapMethod()), value.bootstrapArgsList().stream().map(JdkClassReader::toAsmConstant).toArray()); }; } private static String toInternalName(ClassDesc constant) { if (!constant.isClassOrInterface()) { throw new IllegalArgumentException("Not a class or interface: " + constant); } String descriptor = constant.descriptorString(); return descriptor.substring(1, descriptor.length() - 1); } private static TypeReference toTypeReference(TypeAnnotation.TargetInfo targetInfo) { return switch (targetInfo) { case TypeAnnotation.SupertypeTarget value -> TypeReference.newSuperTypeReference(value.supertypeIndex()); case TypeAnnotation.TypeParameterTarget value -> TypeReference.newTypeParameterReference(value.targetType().targetTypeValue(), value.typeParameterIndex()); case TypeAnnotation.TypeParameterBoundTarget value -> TypeReference.newTypeParameterBoundReference(value.targetType().targetTypeValue(), value.typeParameterIndex(), value.boundIndex()); case TypeAnnotation.ThrowsTarget value -> TypeReference.newExceptionReference(value.throwsTargetIndex()); case TypeAnnotation.FormalParameterTarget value -> TypeReference.newFormalParameterReference(value.formalParameterIndex()); case TypeAnnotation.EmptyTarget value -> TypeReference.newTypeReference(value.targetType().targetTypeValue()); default -> throw new UnsupportedOperationException("Unexpected target: " + targetInfo); }; } private static TypePath toTypePath(List components) { if (components.isEmpty()) { return null; } return TypePath.fromString(components.stream().map(component -> switch (component.typePathKind()) { case ARRAY -> "["; case INNER_TYPE -> "."; case WILDCARD -> "*"; case TYPE_ARGUMENT -> component.typeArgumentIndex() + ";"; }).collect(Collectors.joining())); } private static Object toAsmFrameValue(StackMapFrameInfo.VerificationTypeInfo verificationTypeInfo, Map labels) { return switch (verificationTypeInfo) { case StackMapFrameInfo.SimpleVerificationTypeInfo value -> value.tag(); case StackMapFrameInfo.ObjectVerificationTypeInfo value -> value.className().asInternalName(); case StackMapFrameInfo.UninitializedVerificationTypeInfo value -> labels.computeIfAbsent(value.newTarget(), ignored -> new org.objectweb.asm.Label()); }; } @FunctionalInterface private interface AnnotationVisitorSource { AnnotationVisitor visitAnnotation(String descriptor, boolean visible); } @FunctionalInterface private interface TypeAnnotationVisitorSource { AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible); } private record MergedLocalVariableKey( org.objectweb.asm.Label start, org.objectweb.asm.Label end, String name, int slot ) { } private record MergedLocalVariableValue( String descriptor, String signature ) { } private static class PushbackIterator implements Iterator { private final Iterator it; private T value; private PushbackIterator(Iterator it) { this.it = it; } private void push(T value) { if (this.value != null) { throw new IllegalStateException(); } this.value = value; } @Override public boolean hasNext() { return value != null || it.hasNext(); } @Override public T next() { if (value == null) { return it.next(); } else { T next = value; value = null; return next; } } } private static class AttributeFunction implements Function> { private final Map mappers; private AttributeFunction(Attribute[] attributePrototypes) { mappers = Stream.of(attributePrototypes).collect(Collectors.toMap( prototype -> prototype.type, AsmAttribute::of )); } @Override public AttributeMapper apply(Utf8Entry entry) { AsmAttribute attribute = mappers.get(entry.stringValue()); return attribute == null ? null : attribute.attributeMapper(); } } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-24/codes/rafael/asmjdkbridge/JdkClassWriter.java000066400000000000000000002320271502361351200325260ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ConstantDynamic; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.ModuleVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.RecordComponentVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; import org.objectweb.asm.TypeReference; import java.lang.classfile.Annotation; import java.lang.classfile.AnnotationElement; import java.lang.classfile.AnnotationValue; import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassElement; import java.lang.classfile.ClassFile; import java.lang.classfile.ClassHierarchyResolver; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassSignature; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CodeElement; import java.lang.classfile.FieldElement; import java.lang.classfile.FieldModel; import java.lang.classfile.MethodElement; import java.lang.classfile.MethodModel; import java.lang.classfile.Opcode; import java.lang.classfile.Signature; import java.lang.classfile.TypeAnnotation; import java.lang.classfile.TypeKind; import java.lang.classfile.attribute.AnnotationDefaultAttribute; import java.lang.classfile.attribute.ConstantValueAttribute; import java.lang.classfile.attribute.DeprecatedAttribute; import java.lang.classfile.attribute.EnclosingMethodAttribute; import java.lang.classfile.attribute.ExceptionsAttribute; import java.lang.classfile.attribute.InnerClassInfo; import java.lang.classfile.attribute.InnerClassesAttribute; import java.lang.classfile.attribute.MethodParameterInfo; import java.lang.classfile.attribute.MethodParametersAttribute; import java.lang.classfile.attribute.ModuleAttribute; import java.lang.classfile.attribute.ModuleMainClassAttribute; import java.lang.classfile.attribute.ModulePackagesAttribute; import java.lang.classfile.attribute.NestHostAttribute; import java.lang.classfile.attribute.NestMembersAttribute; import java.lang.classfile.attribute.PermittedSubclassesAttribute; import java.lang.classfile.attribute.RecordAttribute; import java.lang.classfile.attribute.RecordComponentInfo; import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeVisibleParameterAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute; import java.lang.classfile.attribute.SignatureAttribute; import java.lang.classfile.attribute.SourceDebugExtensionAttribute; import java.lang.classfile.attribute.SourceFileAttribute; import java.lang.classfile.attribute.StackMapFrameInfo; import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.classfile.instruction.DiscontinuedInstruction; import java.lang.classfile.instruction.SwitchCase; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicCallSiteDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; import java.lang.constant.MethodTypeDesc; import java.lang.constant.ModuleDesc; import java.lang.constant.PackageDesc; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; /** * A class visitor that creates a class file. */ public class JdkClassWriter extends ClassVisitor { private final int flags; private final Function getSuperClass; private final ClassModel classModel; private final List nestMembers = new ArrayList<>(); private final List innerClasses = new ArrayList<>(); private final List permittedSubclasses = new ArrayList<>(); private final List recordComponents = new ArrayList<>(); private final List attributes = new ArrayList<>(); private final List visibleAnnotations = new ArrayList<>(), invisibleAnnotations = new ArrayList<>(); private final List visibleTypeAnnotations = new ArrayList<>(), invisibleTypeAnnotations = new ArrayList<>(); private ClassDesc thisClass; private boolean isRecord; private final List> classConsumers = new ArrayList<>(List.of(classBuilder -> { for (ClassElement attribute : attributes) { classBuilder.with(attribute); } if (!visibleAnnotations.isEmpty()) { classBuilder.with(RuntimeVisibleAnnotationsAttribute.of(visibleAnnotations)); } if (!invisibleAnnotations.isEmpty()) { classBuilder.with(RuntimeInvisibleAnnotationsAttribute.of(invisibleAnnotations)); } if (!visibleTypeAnnotations.isEmpty()) { classBuilder.with(RuntimeVisibleTypeAnnotationsAttribute.of(visibleTypeAnnotations)); } if (!invisibleTypeAnnotations.isEmpty()) { classBuilder.with(RuntimeInvisibleTypeAnnotationsAttribute.of(invisibleTypeAnnotations)); } if (!nestMembers.isEmpty()) { classBuilder.with(NestMembersAttribute.ofSymbols(nestMembers)); } if (!innerClasses.isEmpty()) { classBuilder.with(InnerClassesAttribute.of(innerClasses)); } if (!permittedSubclasses.isEmpty()) { classBuilder.with(PermittedSubclassesAttribute.ofSymbols(permittedSubclasses)); } if (isRecord || !recordComponents.isEmpty()) { classBuilder.with(RecordAttribute.of(recordComponents)); } })); private byte[] bytes; /** * Creates a class writer. * * @param flags The ASM flags to consider. */ public JdkClassWriter(int flags) { super(Opcodes.ASM9); this.flags = flags; classModel = null; getSuperClass = null; } /** * Creates a class writer. * * @param classReader A class reader of which to retain the constant pool, if possible. * @param flags The ASM flags to consider. */ public JdkClassWriter(JdkClassReader classReader, int flags) { super(Opcodes.ASM9); this.flags = flags; classModel = classReader == null ? null : classReader.getClassModel(); getSuperClass = null; } /** * Creates a class writer. * * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. As a method to allow * pre-Java 8 code to call this constructor via reflection. * @param target The target to invoke the reflective method on. */ public JdkClassWriter(int flags, Method getSuperClass, Object target) { super(Opcodes.ASM9); this.flags = flags; classModel = null; this.getSuperClass = getSuperClass == null ? null : name -> { try { return (String) getSuperClass.invoke(target, name); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } }; } /** * Creates a class writer. * * @param classReader A class reader of which to retain the constant pool, if possible. * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. As a method to allow * pre-Java 8 code to call this constructor via reflection. * @param target The target to invoke the reflective method on. */ public JdkClassWriter(JdkClassReader classReader, int flags, Method getSuperClass, Object target) { super(Opcodes.ASM9); this.flags = flags; classModel = classReader == null ? null : classReader.getClassModel(); this.getSuperClass = getSuperClass == null ? null : name -> { try { return (String) getSuperClass.invoke(target, name); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } }; } /** * Creates a class writer. * * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. */ public JdkClassWriter(int flags, Function getSuperClass) { super(Opcodes.ASM9); this.flags = flags; classModel = null; this.getSuperClass = getSuperClass; } /** * Creates a class writer. * * @param classReader A class reader of which to retain the constant pool, if possible. * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. */ public JdkClassWriter(JdkClassReader classReader, int flags, Function getSuperClass) { super(Opcodes.ASM9); this.flags = flags; classModel = classReader == null ? null : classReader.getClassModel(); this.getSuperClass = getSuperClass; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { thisClass = ClassDesc.ofInternalName(name); isRecord = (access & Opcodes.ACC_RECORD) != 0; classConsumers.add(classBuilder -> { classBuilder.withVersion(version & 0xFFFF, version >>> 16); classBuilder.withFlags(access & ~(Opcodes.ACC_DEPRECATED | Opcodes.ACC_RECORD)); if ((access & Opcodes.ACC_DEPRECATED) != 0) { classBuilder.with(DeprecatedAttribute.of()); } if (signature != null) { classBuilder.with(SignatureAttribute.of(classBuilder.constantPool().utf8Entry(signature))); } if (superName != null) { classBuilder.withSuperclass(ClassDesc.ofInternalName(superName)); } if (interfaces != null) { ClassDesc[] entries = new ClassDesc[interfaces.length]; for (int index = 0; index < interfaces.length; index++) { entries[index] = ClassDesc.ofInternalName(interfaces[index]); } classBuilder.withInterfaceSymbols(entries); } }); } @Override public void visitSource(String source, String debug) { classConsumers.add(classBuilder -> { if (source != null) { classBuilder.with(SourceFileAttribute.of(source)); } if (debug != null) { classBuilder.with(SourceDebugExtensionAttribute.of(debug.getBytes(StandardCharsets.UTF_8))); } }); } @Override public ModuleVisitor visitModule(String name, int access, String version) { return new WritingModuleVisitor(name, access, version); } class WritingModuleVisitor extends ModuleVisitor { private final String name; private final int access; private final String version; private String mainClass; private final List packages = new ArrayList<>(); private final List> moduleAttributeConsumers = new ArrayList<>(); private WritingModuleVisitor(String name, int access, String version) { super(Opcodes.ASM9); this.name = name; this.access = access; this.version = version; } boolean has(ClassModel classModel, String name, int access, String version) { return Objects.equals(JdkClassWriter.this.classModel, classModel) && Objects.equals(this.name, name) && this.access == access && Objects.equals(this.version, version); } void add(ClassElement element) { classConsumers.add(classBuilder -> classBuilder.with(element)); } @Override public void visitMainClass(String mainClass) { this.mainClass = mainClass; } @Override public void visitPackage(String packaze) { packages.add(PackageDesc.ofInternalName(packaze)); } @Override public void visitRequire(String module, int access, String version) { moduleAttributeConsumers.add(moduleAttributeBuilder -> moduleAttributeBuilder.requires(ModuleDesc.of(module), access, version)); } @Override public void visitExport(String packaze, int access, String... modules) { moduleAttributeConsumers.add(moduleAttributeBuilder -> { ModuleDesc[] descriptions = new ModuleDesc[modules.length]; for (int index = 0; index < modules.length; index++) { descriptions[index] = ModuleDesc.of(modules[index]); } moduleAttributeBuilder.exports(PackageDesc.ofInternalName(packaze), access, descriptions); }); } @Override public void visitOpen(String packaze, int access, String... modules) { moduleAttributeConsumers.add(moduleAttributeBuilder -> { ModuleDesc[] descriptions = new ModuleDesc[modules.length]; for (int index = 0; index < modules.length; index++) { descriptions[index] = ModuleDesc.of(modules[index]); } moduleAttributeBuilder.opens(PackageDesc.ofInternalName(packaze), access, descriptions); }); } @Override public void visitUse(String service) { moduleAttributeConsumers.add(moduleAttributeBuilder -> moduleAttributeBuilder.uses(ClassDesc.ofInternalName(service))); } @Override public void visitProvide(String service, String... providers) { moduleAttributeConsumers.add(moduleAttributeBuilder -> { ClassDesc[] descriptions = new ClassDesc[providers.length]; for (int index = 0; index < providers.length; index++) { descriptions[index] = ClassDesc.of(providers[index]); } moduleAttributeBuilder.provides(ClassDesc.ofInternalName(service), descriptions); }); } @Override public void visitEnd() { classConsumers.add(classBuilder -> { classBuilder.with(ModuleAttribute.of(ModuleDesc.of(name), moduleAttributeBuilder -> { moduleAttributeBuilder.moduleFlags(access & ~Opcodes.ACC_DEPRECATED); if (version != null) { moduleAttributeBuilder.moduleVersion(version); } moduleAttributeConsumers.forEach(moduleAttributeConsumer -> moduleAttributeConsumer.accept(moduleAttributeBuilder)); })); if (mainClass != null) { classBuilder.with(ModuleMainClassAttribute.of(ClassDesc.ofInternalName(mainClass))); } if (!packages.isEmpty()) { classBuilder.with(ModulePackagesAttribute.ofNames(packages)); } }); } } @Override public void visitNestHost(String nestHost) { classConsumers.add(classBuilder -> classBuilder.with(NestHostAttribute.of(ClassDesc.ofInternalName(nestHost)))); } @Override public void visitOuterClass(String owner, String name, String descriptor) { classConsumers.add(classBuilder -> classBuilder.with(EnclosingMethodAttribute.of(ClassDesc.ofInternalName(owner), Optional.ofNullable(name), Optional.ofNullable(descriptor).map(MethodTypeDesc::ofDescriptor)))); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { return WritingAnnotationVisitor.of(this, descriptor, (visible ? visibleAnnotations : invisibleAnnotations)::add); } @Override public void visitAttribute(Attribute attribute) { attributes.add(AsmWrappedAttribute.unwrap(attribute, ClassElement.class)); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { return WritingAnnotationVisitor.ofTypeAnnotation(this, descriptor, typeRef, typePath, (visible ? visibleTypeAnnotations : invisibleTypeAnnotations)::add); } @Override public void visitNestMember(String nestMember) { nestMembers.add(ClassDesc.ofInternalName(nestMember)); } @Override public void visitPermittedSubclass(String permittedSubclass) { permittedSubclasses.add(ClassDesc.ofInternalName(permittedSubclass)); } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { innerClasses.add(InnerClassInfo.of(ClassDesc.ofInternalName(name), Optional.ofNullable(outerName).map(ClassDesc::ofInternalName), Optional.ofNullable(innerName), access)); } @Override public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { return new RecordComponentVisitor(Opcodes.ASM9) { private final List> attributes = new ArrayList<>(); private final List visibleAnnotations = new ArrayList<>(), invisibleAnnotations = new ArrayList<>(); private final List visibleTypeAnnotations = new ArrayList<>(), invisibleTypeAnnotations = new ArrayList<>(); @Override public void visitAttribute(Attribute attribute) { attributes.add(AsmWrappedAttribute.unwrap(attribute, java.lang.classfile.Attribute.class)); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { return WritingAnnotationVisitor.of(JdkClassWriter.this, descriptor, (visible ? visibleAnnotations : invisibleAnnotations)::add); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { return WritingAnnotationVisitor.ofTypeAnnotation(JdkClassWriter.this, descriptor, typeRef, typePath, (visible ? visibleTypeAnnotations : invisibleTypeAnnotations)::add); } @Override public void visitEnd() { List> attributes = new ArrayList<>(this.attributes); if (!visibleAnnotations.isEmpty()) { attributes.add(RuntimeVisibleAnnotationsAttribute.of(visibleAnnotations)); } if (!invisibleAnnotations.isEmpty()) { attributes.add(RuntimeInvisibleAnnotationsAttribute.of(invisibleAnnotations)); } if (!visibleTypeAnnotations.isEmpty()) { attributes.add(RuntimeVisibleTypeAnnotationsAttribute.of(visibleTypeAnnotations)); } if (!invisibleTypeAnnotations.isEmpty()) { attributes.add(RuntimeInvisibleTypeAnnotationsAttribute.of(invisibleTypeAnnotations)); } if (signature != null) { attributes.add(SignatureAttribute.of(ClassSignature.parseFrom(signature))); } recordComponents.add(RecordComponentInfo.of(name, ClassDesc.ofDescriptor(descriptor), attributes)); } }; } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { return new WritingFieldVisitor(access, name, descriptor, signature, value); } class WritingFieldVisitor extends FieldVisitor { private final int access; private final String name; private final String descriptor; private final String signature; private final Object value; private final List attributes = new ArrayList<>(); private final List visibleAnnotations = new ArrayList<>(), invisibleAnnotations = new ArrayList<>(); private final List visibleTypeAnnotations = new ArrayList<>(), invisibleTypeAnnotations = new ArrayList<>(); private WritingFieldVisitor(int access, String name, String descriptor, String signature, Object value) { super(Opcodes.ASM9); this.access = access; this.name = name; this.descriptor = descriptor; this.signature = signature; this.value = value; } boolean has(ClassModel classModel, int access, String name, String descriptor, String signature, Object value) { return Objects.equals(JdkClassWriter.this.classModel, classModel) && this.access == access && Objects.equals(this.name, name) && Objects.equals(this.descriptor, descriptor) && Objects.equals(this.signature, signature) && Objects.equals(this.value, value); } void add(FieldModel field) { classConsumers.add(classBuilder -> classBuilder.with(field)); } @Override public void visitAttribute(Attribute attribute) { attributes.add(AsmWrappedAttribute.unwrap(attribute, FieldElement.class)); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { return WritingAnnotationVisitor.of(JdkClassWriter.this, descriptor, (visible ? visibleAnnotations : invisibleAnnotations)::add); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { return WritingAnnotationVisitor.ofTypeAnnotation(JdkClassWriter.this, descriptor, typeRef, typePath, (visible ? visibleTypeAnnotations : invisibleTypeAnnotations)::add); } @Override public void visitEnd() { classConsumers.add(classBuilder -> classBuilder.withField(name, ClassDesc.ofDescriptor(descriptor), fieldBuilder -> { fieldBuilder.withFlags(access & ~Opcodes.ACC_DEPRECATED); if ((access & Opcodes.ACC_DEPRECATED) != 0) { fieldBuilder.with(DeprecatedAttribute.of()); } if (signature != null) { fieldBuilder.with(SignatureAttribute.of(classBuilder.constantPool().utf8Entry(signature))); } for (FieldElement attribute : attributes) { fieldBuilder.with(attribute); } if (!visibleAnnotations.isEmpty()) { fieldBuilder.with(RuntimeVisibleAnnotationsAttribute.of(visibleAnnotations)); } if (!invisibleAnnotations.isEmpty()) { fieldBuilder.with(RuntimeInvisibleAnnotationsAttribute.of(invisibleAnnotations)); } if (!visibleTypeAnnotations.isEmpty()) { fieldBuilder.with(RuntimeVisibleTypeAnnotationsAttribute.of(visibleTypeAnnotations)); } if (!invisibleTypeAnnotations.isEmpty()) { fieldBuilder.with(RuntimeInvisibleTypeAnnotationsAttribute.of(invisibleTypeAnnotations)); } if (value != null) { fieldBuilder.with(ConstantValueAttribute.of(toConstantDesc(value))); } })); } } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { return new WritingMethodVisitor(access, name, descriptor, signature, exceptions); } class WritingMethodVisitor extends MethodVisitor { private final int access; private final String name; private final String descriptor; private final String signature; private final String[] exceptions; private List> codeConsumers; private final List attributes = new ArrayList<>(); private final List codeAttributes = new ArrayList<>(); private AnnotationValue defaultValue; private int catchCount = -1; private final List locals = new ArrayList<>(); private final List methodParameters = new ArrayList<>(); private final List visibleAnnotations = new ArrayList<>(), invisibleAnnotations = new ArrayList<>(); private final List visibleTypeAnnotations = new ArrayList<>(), invisibleTypeAnnotations = new ArrayList<>(); private final Map> visibleParameterAnnotations = new HashMap<>(), invisibleParameterAnnotations = new HashMap<>(); private int visibleParameterAnnotationsCount = -1, invisibleParameterAnnotationsCount = -1; private Map lineNumbers; private List stackMapFrames; private Map labels; private Consumer dalayedInstruction; private void addInstruction(Consumer consumer) { undelayInstruction(); dalayedInstruction = consumer; } private void undelayInstruction() { if (dalayedInstruction != null) { codeConsumers.add(dalayedInstruction); dalayedInstruction = null; } } private WritingMethodVisitor(int access, String name, String descriptor, String signature, String[] exceptions) { super(Opcodes.ASM9); this.access = access; this.name = name; this.descriptor = descriptor; this.signature = signature; this.exceptions = exceptions; } boolean has(ClassModel classModel, int access, String name, String descriptor, String signature, String[] exceptions) { return Objects.equals(JdkClassWriter.this.classModel, classModel) && this.access == access && Objects.equals(this.name, name) && Objects.equals(this.descriptor, descriptor) && Objects.equals(this.signature, signature) && Arrays.equals(this.exceptions, exceptions); } void add(MethodModel model) { classConsumers.add(classBuilder -> classBuilder.with(model)); } @Override public void visitCode() { codeConsumers = new ArrayList<>(); lineNumbers = new IdentityHashMap<>(); codeConsumers.add(_ -> { stackMapFrames = new ArrayList<>(); labels = new IdentityHashMap<>(); dalayedInstruction = null; }); if ((flags & ClassWriter.COMPUTE_FRAMES) == 0) { if ((access & Opcodes.ACC_STATIC) == 0) { locals.add(name.equals("") ? StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS : StackMapFrameInfo.ObjectVerificationTypeInfo.of(thisClass)); } Type type = Type.getMethodType(descriptor); for (Type argumentType : type.getArgumentTypes()) { locals.add(switch (argumentType.getSort()) { case Type.BOOLEAN, Type.BYTE, Type.SHORT, Type.CHAR, Type.INT -> StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER; case Type.LONG -> StackMapFrameInfo.SimpleVerificationTypeInfo.LONG; case Type.FLOAT -> StackMapFrameInfo.SimpleVerificationTypeInfo.FLOAT; case Type.DOUBLE -> StackMapFrameInfo.SimpleVerificationTypeInfo.DOUBLE; default -> StackMapFrameInfo.ObjectVerificationTypeInfo.of(ClassDesc.ofDescriptor(argumentType.getDescriptor())); }); } } } @Override public void visitAttribute(Attribute attribute) { if (attribute.isCodeAttribute()) { codeAttributes.add(AsmWrappedAttribute.unwrap(attribute, CodeElement.class)); } else { attributes.add(AsmWrappedAttribute.unwrap(attribute, MethodElement.class)); } } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { return WritingAnnotationVisitor.of(JdkClassWriter.this, descriptor, (visible ? visibleAnnotations : invisibleAnnotations)::add); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { return WritingAnnotationVisitor.ofTypeAnnotation(JdkClassWriter.this, descriptor, typeRef, typePath, (visible ? visibleTypeAnnotations : invisibleTypeAnnotations)::add); } @Override public void visitParameter(String name, int access) { methodParameters.add(MethodParameterInfo.ofParameter(Optional.ofNullable(name), access)); } @Override public void visitAnnotableParameterCount(int parameterCount, boolean visible) { if (visible) { visibleParameterAnnotationsCount = parameterCount; } else { invisibleParameterAnnotationsCount = parameterCount; } } @Override public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { return WritingAnnotationVisitor.of(JdkClassWriter.this, descriptor, (visible ? visibleParameterAnnotations : invisibleParameterAnnotations).computeIfAbsent(parameter, _ -> new ArrayList<>())::add); } @Override public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { int catchCount = this.catchCount; return WritingAnnotationVisitor.ofExceptionTypeAnnotation(JdkClassWriter.this, descriptor, typeRef, typePath, function -> codeConsumers.add(codeBuilder -> { TypeAnnotation annotation = function.apply(catchCount); codeBuilder.with(visible ? RuntimeVisibleTypeAnnotationsAttribute.of(annotation) : RuntimeInvisibleTypeAnnotationsAttribute.of(annotation)); })); } @Override public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { return WritingAnnotationVisitor.ofLabeledTypeAnnotation(JdkClassWriter.this, descriptor, typeRef, typePath, function -> codeConsumers.add(codeBuilder -> { TypeAnnotation annotation = function.apply(codeBuilder.newBoundLabel()); codeBuilder.with(visible ? RuntimeVisibleTypeAnnotationsAttribute.of(annotation) : RuntimeInvisibleTypeAnnotationsAttribute.of(annotation)); })); } @Override public AnnotationVisitor visitAnnotationDefault() { return WritingAnnotationVisitor.ofValue(JdkClassWriter.this, value -> defaultValue = value); } @Override public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] indices, String descriptor, boolean visible) { undelayInstruction(); return WritingAnnotationVisitor.ofTargetedTypeAnnotation(JdkClassWriter.this, descriptor, typeRef, typePath, function -> codeConsumers.add(codeBuilder -> { List targets = new ArrayList<>(); for (int index = 0; index < start.length; index++) { targets.add(TypeAnnotation.LocalVarTargetInfo.of(labels.computeIfAbsent(start[index], _ -> codeBuilder.newLabel()), labels.computeIfAbsent(end[index], _ -> codeBuilder.newLabel()), indices[index])); } TypeAnnotation annotation = function.apply(targets); codeBuilder.with(visible ? RuntimeVisibleTypeAnnotationsAttribute.of(annotation) : RuntimeInvisibleTypeAnnotationsAttribute.of(annotation)); })); } @Override public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { undelayInstruction(); if ((flags & ClassWriter.COMPUTE_FRAMES) != 0) { return; } codeConsumers.add(codeBuilder -> { List stacks = new ArrayList<>(numStack); for (int index = 0; index < numStack; index++) { stacks.add(toVerificationTypeInfo(stack[index], label -> labels.computeIfAbsent(label, _ -> codeBuilder.newLabel()))); } switch (type) { case Opcodes.F_SAME: break; case Opcodes.F_SAME1: break; case Opcodes.F_APPEND: for (int index = 0; index < numLocal; index++) { locals.add(toVerificationTypeInfo(local[index], label -> labels.computeIfAbsent(label, _ -> codeBuilder.newLabel()))); } break; case Opcodes.F_CHOP: locals.subList(locals.size() - numLocal, locals.size()).clear(); break; case Opcodes.F_FULL: case Opcodes.F_NEW: locals.clear(); for (int index = 0; index < numLocal; index++) { locals.add(toVerificationTypeInfo(local[index], label -> labels.computeIfAbsent(label, _ -> codeBuilder.newLabel()))); } break; default: throw new IllegalArgumentException("Unsupported type: " + type); } stackMapFrames.add(StackMapFrameInfo.of(codeBuilder.newBoundLabel(), new ArrayList<>(locals), stacks)); }); } private static StackMapFrameInfo.VerificationTypeInfo toVerificationTypeInfo(Object value, Function labels) { if (value == Opcodes.TOP) { return StackMapFrameInfo.SimpleVerificationTypeInfo.TOP; } else if (value == Opcodes.INTEGER) { return StackMapFrameInfo.SimpleVerificationTypeInfo.INTEGER; } else if (value == Opcodes.LONG) { return StackMapFrameInfo.SimpleVerificationTypeInfo.LONG; } else if (value == Opcodes.FLOAT) { return StackMapFrameInfo.SimpleVerificationTypeInfo.FLOAT; } else if (value == Opcodes.DOUBLE) { return StackMapFrameInfo.SimpleVerificationTypeInfo.DOUBLE; } else if (value == Opcodes.NULL) { return StackMapFrameInfo.SimpleVerificationTypeInfo.NULL; } else if (value == Opcodes.UNINITIALIZED_THIS) { return StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS; } else if (value instanceof Label label) { return StackMapFrameInfo.UninitializedVerificationTypeInfo.of(labels.apply(label)); } else if (value instanceof String name) { return StackMapFrameInfo.ObjectVerificationTypeInfo.of(name.startsWith("[") ? ClassDesc.ofDescriptor(name) : ClassDesc.ofInternalName(name)); } else { throw new IllegalArgumentException("Unsupported type: " + value); } } @Override public void visitInsn(int opcode) { Consumer codeConsumer = switch (opcode) { case Opcodes.NOP -> CodeBuilder::nop; case Opcodes.ACONST_NULL -> CodeBuilder::aconst_null; case Opcodes.ICONST_M1 -> CodeBuilder::iconst_m1; case Opcodes.ICONST_0 -> CodeBuilder::iconst_0; case Opcodes.ICONST_1 -> CodeBuilder::iconst_1; case Opcodes.ICONST_2 -> CodeBuilder::iconst_2; case Opcodes.ICONST_3 -> CodeBuilder::iconst_3; case Opcodes.ICONST_4 -> CodeBuilder::iconst_4; case Opcodes.ICONST_5 -> CodeBuilder::iconst_5; case Opcodes.LCONST_0 -> CodeBuilder::lconst_0; case Opcodes.LCONST_1 -> CodeBuilder::lconst_1; case Opcodes.FCONST_0 -> CodeBuilder::fconst_0; case Opcodes.FCONST_1 -> CodeBuilder::fconst_1; case Opcodes.FCONST_2 -> CodeBuilder::fconst_2; case Opcodes.DCONST_0 -> CodeBuilder::dconst_0; case Opcodes.DCONST_1 -> CodeBuilder::dconst_1; case Opcodes.IALOAD -> CodeBuilder::iaload; case Opcodes.LALOAD -> CodeBuilder::laload; case Opcodes.FALOAD -> CodeBuilder::faload; case Opcodes.DALOAD -> CodeBuilder::daload; case Opcodes.AALOAD -> CodeBuilder::aaload; case Opcodes.BALOAD -> CodeBuilder::baload; case Opcodes.CALOAD -> CodeBuilder::caload; case Opcodes.SALOAD -> CodeBuilder::saload; case Opcodes.IASTORE -> CodeBuilder::iastore; case Opcodes.LASTORE -> CodeBuilder::lastore; case Opcodes.FASTORE -> CodeBuilder::fastore; case Opcodes.DASTORE -> CodeBuilder::dastore; case Opcodes.AASTORE -> CodeBuilder::aastore; case Opcodes.BASTORE -> CodeBuilder::bastore; case Opcodes.CASTORE -> CodeBuilder::castore; case Opcodes.SASTORE -> CodeBuilder::sastore; case Opcodes.POP -> CodeBuilder::pop; case Opcodes.POP2 -> CodeBuilder::pop2; case Opcodes.DUP -> CodeBuilder::dup; case Opcodes.DUP_X1 -> CodeBuilder::dup_x1; case Opcodes.DUP_X2 -> CodeBuilder::dup_x2; case Opcodes.DUP2 -> CodeBuilder::dup2; case Opcodes.DUP2_X1 -> CodeBuilder::dup2_x1; case Opcodes.DUP2_X2 -> CodeBuilder::dup2_x2; case Opcodes.SWAP -> CodeBuilder::swap; case Opcodes.IADD -> CodeBuilder::iadd; case Opcodes.LADD -> CodeBuilder::ladd; case Opcodes.FADD -> CodeBuilder::fadd; case Opcodes.DADD -> CodeBuilder::dadd; case Opcodes.ISUB -> CodeBuilder::isub; case Opcodes.LSUB -> CodeBuilder::lsub; case Opcodes.FSUB -> CodeBuilder::fsub; case Opcodes.DSUB -> CodeBuilder::dsub; case Opcodes.IMUL -> CodeBuilder::imul; case Opcodes.LMUL -> CodeBuilder::lmul; case Opcodes.FMUL -> CodeBuilder::fmul; case Opcodes.DMUL -> CodeBuilder::dmul; case Opcodes.IDIV -> CodeBuilder::idiv; case Opcodes.LDIV -> CodeBuilder::ldiv; case Opcodes.FDIV -> CodeBuilder::fdiv; case Opcodes.DDIV -> CodeBuilder::ddiv; case Opcodes.IREM -> CodeBuilder::irem; case Opcodes.LREM -> CodeBuilder::lrem; case Opcodes.FREM -> CodeBuilder::frem; case Opcodes.DREM -> CodeBuilder::drem; case Opcodes.INEG -> CodeBuilder::ineg; case Opcodes.LNEG -> CodeBuilder::lneg; case Opcodes.FNEG -> CodeBuilder::fneg; case Opcodes.DNEG -> CodeBuilder::dneg; case Opcodes.ISHL -> CodeBuilder::ishl; case Opcodes.LSHL -> CodeBuilder::lshl; case Opcodes.ISHR -> CodeBuilder::ishr; case Opcodes.LSHR -> CodeBuilder::lshr; case Opcodes.IUSHR -> CodeBuilder::iushr; case Opcodes.LUSHR -> CodeBuilder::lushr; case Opcodes.IAND -> CodeBuilder::iand; case Opcodes.LAND -> CodeBuilder::land; case Opcodes.IOR -> CodeBuilder::ior; case Opcodes.LOR -> CodeBuilder::lor; case Opcodes.IXOR -> CodeBuilder::ixor; case Opcodes.LXOR -> CodeBuilder::lxor; case Opcodes.I2L -> CodeBuilder::i2l; case Opcodes.I2F -> CodeBuilder::i2f; case Opcodes.I2D -> CodeBuilder::i2d; case Opcodes.L2I -> CodeBuilder::l2i; case Opcodes.L2F -> CodeBuilder::l2f; case Opcodes.L2D -> CodeBuilder::l2d; case Opcodes.F2I -> CodeBuilder::f2i; case Opcodes.F2L -> CodeBuilder::f2l; case Opcodes.F2D -> CodeBuilder::f2d; case Opcodes.D2I -> CodeBuilder::d2i; case Opcodes.D2L -> CodeBuilder::d2l; case Opcodes.D2F -> CodeBuilder::d2f; case Opcodes.I2B -> CodeBuilder::i2b; case Opcodes.I2C -> CodeBuilder::i2c; case Opcodes.I2S -> CodeBuilder::i2s; case Opcodes.LCMP -> CodeBuilder::lcmp; case Opcodes.FCMPL -> CodeBuilder::fcmpl; case Opcodes.FCMPG -> CodeBuilder::fcmpg; case Opcodes.DCMPL -> CodeBuilder::dcmpl; case Opcodes.DCMPG -> CodeBuilder::dcmpg; case Opcodes.IRETURN -> CodeBuilder::ireturn; case Opcodes.LRETURN -> CodeBuilder::lreturn; case Opcodes.FRETURN -> CodeBuilder::freturn; case Opcodes.DRETURN -> CodeBuilder::dreturn; case Opcodes.ARETURN -> CodeBuilder::areturn; case Opcodes.RETURN -> CodeBuilder::return_; case Opcodes.ARRAYLENGTH -> CodeBuilder::arraylength; case Opcodes.ATHROW -> CodeBuilder::athrow; case Opcodes.MONITORENTER -> CodeBuilder::monitorenter; case Opcodes.MONITOREXIT -> CodeBuilder::monitorexit; default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode); }; addInstruction(codeConsumer); } @Override public void visitIntInsn(int opcode, int operand) { Consumer codeConsumer = switch (opcode) { case Opcodes.BIPUSH -> codeBuilder -> codeBuilder.bipush(operand); case Opcodes.SIPUSH -> codeBuilder -> codeBuilder.sipush(operand); case Opcodes.NEWARRAY -> codeBuilder -> codeBuilder.newarray(TypeKind.fromNewarrayCode(operand)); default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode); }; addInstruction(codeConsumer); } @Override public void visitVarInsn(int opcode, int varIndex) { Consumer codeConsumer = switch (opcode) { case Opcodes.ILOAD -> codeBuilder -> codeBuilder.iload(varIndex); case Opcodes.LLOAD -> codeBuilder -> codeBuilder.lload(varIndex); case Opcodes.FLOAD -> codeBuilder -> codeBuilder.fload(varIndex); case Opcodes.DLOAD -> codeBuilder -> codeBuilder.dload(varIndex); case Opcodes.ALOAD -> codeBuilder -> codeBuilder.aload(varIndex); case Opcodes.ISTORE -> codeBuilder -> codeBuilder.istore(varIndex); case Opcodes.LSTORE -> codeBuilder -> codeBuilder.lstore(varIndex); case Opcodes.FSTORE -> codeBuilder -> codeBuilder.fstore(varIndex); case Opcodes.DSTORE -> codeBuilder -> codeBuilder.dstore(varIndex); case Opcodes.ASTORE -> codeBuilder -> codeBuilder.astore(varIndex); case Opcodes.RET -> codeBuilder -> codeBuilder.with(DiscontinuedInstruction.RetInstruction.of(varIndex)); default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode); }; addInstruction(codeConsumer); } @Override public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { Consumer codeConsumer = codeBuilder -> codeBuilder.fieldAccess(switch (opcode) { case Opcodes.GETFIELD -> Opcode.GETFIELD; case Opcodes.PUTFIELD -> Opcode.PUTFIELD; case Opcodes.GETSTATIC -> Opcode.GETSTATIC; case Opcodes.PUTSTATIC -> Opcode.PUTSTATIC; default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode); }, ClassDesc.ofInternalName(owner), name, ClassDesc.ofDescriptor(descriptor)); addInstruction(codeConsumer); } @Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { Consumer codeConsumer = codeBuilder -> codeBuilder.invoke(switch (opcode) { case Opcodes.INVOKEVIRTUAL -> Opcode.INVOKEVIRTUAL; case Opcodes.INVOKEINTERFACE -> Opcode.INVOKEINTERFACE; case Opcodes.INVOKESPECIAL -> Opcode.INVOKESPECIAL; case Opcodes.INVOKESTATIC -> Opcode.INVOKESTATIC; default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode); }, owner.startsWith("[") ? ClassDesc.ofDescriptor(owner) : ClassDesc.ofInternalName(owner), name, MethodTypeDesc.ofDescriptor(descriptor), isInterface); addInstruction(codeConsumer); } @Override public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { ConstantDesc[] constants = new ConstantDesc[bootstrapMethodArguments.length]; for (int index = 0; index < bootstrapMethodArguments.length; index++) { constants[index] = toConstantDesc(bootstrapMethodArguments[index]); } Consumer codeConsumer = codeBuilder -> codeBuilder.invokedynamic(DynamicCallSiteDesc.of(MethodHandleDesc.of(DirectMethodHandleDesc.Kind.valueOf(bootstrapMethodHandle.getTag(), bootstrapMethodHandle.isInterface()), ClassDesc.ofInternalName(bootstrapMethodHandle.getOwner()), bootstrapMethodHandle.getName(), bootstrapMethodHandle.getDesc()), name, MethodTypeDesc.ofDescriptor(descriptor), constants)); addInstruction(codeConsumer); } @Override public void visitJumpInsn(int opcode, Label label) { Consumer codeConsumer = switch (opcode) { case Opcodes.IFEQ -> codeBuilder -> codeBuilder.ifeq(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IFNE -> codeBuilder -> codeBuilder.ifne(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IFLT -> codeBuilder -> codeBuilder.iflt(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IFGE -> codeBuilder -> codeBuilder.ifge(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IFGT -> codeBuilder -> codeBuilder.ifgt(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IFLE -> codeBuilder -> codeBuilder.ifle(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ICMPEQ -> codeBuilder -> codeBuilder.if_icmpeq(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ICMPNE -> codeBuilder -> codeBuilder.if_icmpne(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ICMPLT -> codeBuilder -> codeBuilder.if_icmplt(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ICMPGE -> codeBuilder -> codeBuilder.if_icmpge(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ICMPGT -> codeBuilder -> codeBuilder.if_icmpgt(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ICMPLE -> codeBuilder -> codeBuilder.if_icmple(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ACMPEQ -> codeBuilder -> codeBuilder.if_acmpeq(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IF_ACMPNE -> codeBuilder -> codeBuilder.if_acmpne(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.GOTO -> codeBuilder -> codeBuilder.goto_(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IFNULL -> codeBuilder -> codeBuilder.ifnull(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.IFNONNULL -> codeBuilder -> codeBuilder.ifnonnull(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); case Opcodes.JSR -> codeBuilder -> codeBuilder.with(DiscontinuedInstruction.JsrInstruction.of(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel()))); default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode); }; addInstruction(codeConsumer); } @Override public void visitLdcInsn(Object value) { ConstantDesc constant = toConstantDesc(value); Consumer codeConsumer = codeBuilder -> codeBuilder.ldc(constant); addInstruction(codeConsumer); } @Override public void visitIincInsn(int varIndex, int increment) { Consumer codeConsumer = codeBuilder -> codeBuilder.iinc(varIndex, increment); addInstruction(codeConsumer); } @Override public void visitLabel(Label label) { Consumer codeConsumer = codeBuilder -> { codeBuilder.labelBinding(labels.computeIfAbsent(label, _ -> codeBuilder.newLabel())); Integer lineNumber = lineNumbers.remove(label); if (lineNumber != null) { codeBuilder.lineNumber(lineNumber); } }; addInstruction(codeConsumer); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { Consumer codeConsumer = codeBuilder -> { List switchCases = new ArrayList<>(); for (int index = 0; index < labels.length; index++) { if (!Objects.equals(labels[index], dflt)) { switchCases.add(SwitchCase.of(min + index, this.labels.computeIfAbsent(labels[index], _ -> codeBuilder.newLabel()))); } } codeBuilder.tableswitch(min, max, this.labels.computeIfAbsent(dflt, _ -> codeBuilder.newLabel()), switchCases); }; addInstruction(codeConsumer); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { Consumer codeConsumer = codeBuilder -> { SwitchCase[] switchCases = new SwitchCase[labels.length]; for (int index = 0; index < labels.length; index++) { switchCases[index] = SwitchCase.of(keys[index], this.labels.computeIfAbsent(labels[index], _ -> codeBuilder.newLabel())); } codeBuilder.lookupswitch(this.labels.computeIfAbsent(dflt, _ -> codeBuilder.newLabel()), List.of(switchCases)); }; addInstruction(codeConsumer); } @Override public void visitTypeInsn(int opcode, String type) { ClassDesc description = type.startsWith("[") ? ClassDesc.ofDescriptor(type) : ClassDesc.ofInternalName(type); Consumer codeConsumer = switch (opcode) { case Opcodes.NEW -> codeBuilder -> codeBuilder.new_(description); case Opcodes.ANEWARRAY -> codeBuilder -> codeBuilder.anewarray(description); case Opcodes.CHECKCAST -> codeBuilder -> codeBuilder.checkcast(description); case Opcodes.INSTANCEOF -> codeBuilder -> codeBuilder.instanceOf(description); default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode); }; addInstruction(codeConsumer); } @Override public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { Consumer codeConsumer = codeBuilder -> codeBuilder.multianewarray(ClassDesc.ofDescriptor(descriptor), numDimensions); addInstruction(codeConsumer); } @Override public void visitLineNumber(int line, Label start) { lineNumbers.put(start, line); } @Override public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { undelayInstruction(); codeConsumers.add(codeBuilder -> { if (descriptor != null) { codeBuilder.localVariable(index, name, ClassDesc.ofDescriptor(descriptor), labels.computeIfAbsent(start, _ -> codeBuilder.newLabel()), labels.computeIfAbsent(end, _ -> codeBuilder.newLabel())); } if (signature != null) { codeBuilder.localVariableType(index, name, Signature.parseFrom(signature), labels.computeIfAbsent(start, _ -> codeBuilder.newLabel()), labels.computeIfAbsent(end, _ -> codeBuilder.newLabel())); } }); } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { undelayInstruction(); catchCount += 1; codeConsumers.add(codeBuilder -> { if (type == null) { codeBuilder.exceptionCatchAll(labels.computeIfAbsent(start, _ -> codeBuilder.newLabel()), labels.computeIfAbsent(end, _ -> codeBuilder.newLabel()), labels.computeIfAbsent(handler, _ -> codeBuilder.newLabel())); } else { codeBuilder.exceptionCatch(labels.computeIfAbsent(start, _ -> codeBuilder.newLabel()), labels.computeIfAbsent(end, _ -> codeBuilder.newLabel()), labels.computeIfAbsent(handler, _ -> codeBuilder.newLabel()), ClassDesc.ofInternalName(type)); } }); } @Override public void visitMaxs(int maxStack, int maxLocals) { } @Override public void visitEnd() { MethodTypeDesc methodTypeDesc = MethodTypeDesc.ofDescriptor(descriptor); classConsumers.add(classBuilder -> classBuilder.withMethod(name, methodTypeDesc, access & ~Opcodes.ACC_DEPRECATED, methodBuilder -> { if ((access & Opcodes.ACC_DEPRECATED) != 0) { methodBuilder.with(DeprecatedAttribute.of()); } if (signature != null) { methodBuilder.with(SignatureAttribute.of(classBuilder.constantPool().utf8Entry(signature))); } if (exceptions != null) { ClassDesc[] entries = new ClassDesc[exceptions.length]; for (int index = 0; index < exceptions.length; index++) { entries[index] = ClassDesc.ofInternalName(exceptions[index]); } methodBuilder.with(ExceptionsAttribute.ofSymbols(entries)); } for (MethodElement attribute : attributes) { methodBuilder.with(attribute); } if (defaultValue != null) { methodBuilder.with(AnnotationDefaultAttribute.of(defaultValue)); } if (!visibleAnnotations.isEmpty()) { methodBuilder.with(RuntimeVisibleAnnotationsAttribute.of(visibleAnnotations)); } if (!invisibleAnnotations.isEmpty()) { methodBuilder.with(RuntimeInvisibleAnnotationsAttribute.of(invisibleAnnotations)); } if (!visibleTypeAnnotations.isEmpty()) { methodBuilder.with(RuntimeVisibleTypeAnnotationsAttribute.of(visibleTypeAnnotations)); } if (!invisibleTypeAnnotations.isEmpty()) { methodBuilder.with(RuntimeInvisibleTypeAnnotationsAttribute.of(invisibleTypeAnnotations)); } if (!methodParameters.isEmpty()) { methodBuilder.with(MethodParametersAttribute.of(methodParameters)); } if (!visibleParameterAnnotations.isEmpty()) { List> annotations = new ArrayList<>(); for (int index = 0; index < (visibleParameterAnnotationsCount < 0 ? methodTypeDesc.parameterCount() : visibleParameterAnnotationsCount); index++) { annotations.add(visibleParameterAnnotations.getOrDefault(index, List.of())); } methodBuilder.with(RuntimeVisibleParameterAnnotationsAttribute.of(annotations)); } if (!invisibleParameterAnnotations.isEmpty()) { List> annotations = new ArrayList<>(); for (int index = 0; index < (invisibleParameterAnnotationsCount < 0 ? methodTypeDesc.parameterCount() : invisibleParameterAnnotationsCount); index++) { annotations.add(invisibleParameterAnnotations.getOrDefault(index, List.of())); } methodBuilder.with(RuntimeInvisibleParameterAnnotationsAttribute.of(annotations)); } if (codeConsumers != null) { undelayInstruction(); methodBuilder.withCode(codeBuilder -> { codeConsumers.forEach(codeConsumer -> codeConsumer.accept(codeBuilder)); if (!stackMapFrames.isEmpty()) { codeBuilder.with(StackMapTableAttribute.of(stackMapFrames)); } for (CodeElement attribute : codeAttributes) { codeBuilder.with(attribute); } }); } })); } } @Override public void visitEnd() { ClassFile classFile; if ((flags & ClassWriter.COMPUTE_FRAMES) == 0) { classFile = ClassFile.of(ClassFile.DeadCodeOption.KEEP_DEAD_CODE, ClassFile.StackMapsOption.DROP_STACK_MAPS); } else { classFile = ClassFile.of(ClassFile.DeadCodeOption.PATCH_DEAD_CODE, ClassFile.StackMapsOption.STACK_MAPS_WHEN_REQUIRED, ClassFile.ClassHierarchyResolverOption.of(classDesc -> { if (!classDesc.isClassOrInterface()) { return null; } else if (classDesc.equals(ConstantDescs.CD_Object)) { return ClassHierarchyResolver.ClassHierarchyInfo.ofClass(null); } String superClass = getSuperClass(classDesc.displayName().replace('.', '/')); return superClass == null ? ClassHierarchyResolver.ClassHierarchyInfo.ofInterface() : ClassHierarchyResolver.ClassHierarchyInfo.ofClass(ClassDesc.ofInternalName(superClass)); })); } if (classModel == null) { bytes = classFile.build(thisClass, classBuilder -> classConsumers.forEach(classConsumer -> classConsumer.accept(classBuilder))); } else { ConstantPoolBuilder constantPoolBuilder = ConstantPoolBuilder.of(classModel); bytes = classFile.build(constantPoolBuilder.classEntry(thisClass), constantPoolBuilder, classBuilder -> classConsumers.forEach(classConsumer -> classConsumer.accept(classBuilder))); } } static ConstantDesc toConstantDesc(Object asm) { return switch (asm) { case Integer value -> value; case Long value -> value; case Float value -> value; case Double value -> value; case String value -> value; case Type value -> switch (value.getSort()) { case Type.OBJECT, Type.ARRAY -> ClassDesc.ofDescriptor(value.getDescriptor()); case Type.METHOD -> MethodTypeDesc.ofDescriptor(value.getDescriptor()); default -> throw new IllegalArgumentException("Unexpected type sort: " + value.getSort()); }; case Handle value -> MethodHandleDesc.of(DirectMethodHandleDesc.Kind.valueOf(value.getTag(), value.isInterface()), ClassDesc.ofInternalName(value.getOwner()), value.getName(), value.getDesc()); case ConstantDynamic value -> { ConstantDesc[] constants = new ConstantDesc[value.getBootstrapMethodArgumentCount()]; for (int index = 0; index < value.getBootstrapMethodArgumentCount(); index++) { constants[index] = toConstantDesc(value.getBootstrapMethodArgument(index)); } yield DynamicConstantDesc.ofNamed(MethodHandleDesc.of(DirectMethodHandleDesc.Kind.valueOf(value.getBootstrapMethod().getTag(), value.getBootstrapMethod().isInterface()), ClassDesc.ofInternalName(value.getBootstrapMethod().getOwner()), value.getBootstrapMethod().getName(), value.getBootstrapMethod().getDesc()), value.getName(), ClassDesc.ofDescriptor(value.getDescriptor()), constants); } case null, default -> throw new IllegalArgumentException("Unexpected constant: " + asm); }; } /** * Returns the generated class file. * * @return The class file as a byte array. */ public byte[] toByteArray() { if (bytes == null) { throw new IllegalStateException("Did not visitEnd, and no byte array was created"); } return bytes; } /** * Returns the super class of the class that is provided by name. The default implementation * resolves the super class from this instance's class' {@link ClassLoader}, unless * {@link #getClassLoader()} is overridden. *

* This is used for generating stack map frames. * * @param name The name of the class for which to resolve the super class. * @return The name of the resolved super class. */ protected String getSuperClass(String name) { if (getSuperClass != null) { return getSuperClass.apply(name); } ClassLoader classLoader = getClassLoader(); Class type; try { type = Class.forName(name.replace('/', '.'), false, classLoader); } catch (ClassNotFoundException e) { throw new TypeNotPresentException(name.replace('/', '.'), e); } if (type.isInterface()) { return null; } else { return Type.getInternalName(type.getSuperclass()); } } /** * Returns the class loader to use for resolving the super class of a discovered class. * * @return The class loader to use for resolving a super class's name. */ protected ClassLoader getClassLoader() { return getClass().getClassLoader(); } class WritingAnnotationVisitor extends AnnotationVisitor { private final BiConsumer consumer; private final Runnable onEnd; private static AnnotationVisitor of(JdkClassWriter classWriter, String descriptor, Consumer consumer) { List elements = new ArrayList<>(); return classWriter.new WritingAnnotationVisitor((name, value) -> elements.add(AnnotationElement.of(name, value)), () -> consumer.accept(Annotation.of(ClassDesc.ofDescriptor(descriptor), elements))); } private static AnnotationVisitor ofValue(JdkClassWriter classWriter, Consumer consumer) { return classWriter.new WritingAnnotationVisitor((_, value) -> consumer.accept(value), () -> { }); } private static AnnotationVisitor ofTypeAnnotation(JdkClassWriter classWriter, String descriptor, int typeRef, TypePath typePath, Consumer consumer) { return ofUnresolvedTypeAnnotation(classWriter, descriptor, typeRef, typePath, function -> consumer.accept(function.apply(reference -> switch (reference.getSort()) { case TypeReference.CLASS_TYPE_PARAMETER -> TypeAnnotation.TargetInfo.ofClassTypeParameter(reference.getTypeParameterIndex()); case TypeReference.METHOD_TYPE_PARAMETER -> TypeAnnotation.TargetInfo.ofMethodTypeParameter(reference.getTypeParameterIndex()); case TypeReference.CLASS_EXTENDS -> TypeAnnotation.TargetInfo.ofClassExtends(reference.getSuperTypeIndex()); case TypeReference.CLASS_TYPE_PARAMETER_BOUND -> TypeAnnotation.TargetInfo.ofClassTypeParameterBound(reference.getTypeParameterIndex(), reference.getTypeParameterBoundIndex()); case TypeReference.METHOD_TYPE_PARAMETER_BOUND -> TypeAnnotation.TargetInfo.ofMethodTypeParameterBound(reference.getTypeParameterIndex(), reference.getTypeParameterBoundIndex()); case TypeReference.FIELD -> TypeAnnotation.TargetInfo.ofField(); case TypeReference.METHOD_RETURN -> TypeAnnotation.TargetInfo.ofMethodReturn(); case TypeReference.METHOD_RECEIVER -> TypeAnnotation.TargetInfo.ofMethodReceiver(); case TypeReference.METHOD_FORMAL_PARAMETER -> TypeAnnotation.TargetInfo.ofMethodFormalParameter(reference.getFormalParameterIndex()); case TypeReference.THROWS -> TypeAnnotation.TargetInfo.ofThrows(reference.getExceptionIndex()); default -> throw new IllegalArgumentException("Unexpected reference sort: " + reference.getSort()); }))); } private static AnnotationVisitor ofExceptionTypeAnnotation(JdkClassWriter classWriter, String descriptor, int typeRef, TypePath typePath, Consumer> consumer) { return ofUnresolvedTypeAnnotation(classWriter, descriptor, typeRef, typePath, function -> consumer.accept(index -> function.apply(reference -> switch (reference.getSort()) { case TypeReference.EXCEPTION_PARAMETER -> TypeAnnotation.TargetInfo.ofExceptionParameter(index); default -> throw new IllegalArgumentException("Unexpected reference sort: " + reference.getSort()); }))); } private static AnnotationVisitor ofLabeledTypeAnnotation(JdkClassWriter classWriter, String descriptor, int typeRef, TypePath typePath, Consumer> consumer) { return ofUnresolvedTypeAnnotation(classWriter, descriptor, typeRef, typePath, function -> consumer.accept(label -> function.apply(reference -> switch (reference.getSort()) { case TypeReference.INSTANCEOF -> TypeAnnotation.TargetInfo.ofInstanceofExpr(label); case TypeReference.NEW -> TypeAnnotation.TargetInfo.ofNewExpr(label); case TypeReference.CONSTRUCTOR_REFERENCE -> TypeAnnotation.TargetInfo.ofConstructorReference(label); case TypeReference.METHOD_REFERENCE -> TypeAnnotation.TargetInfo.ofMethodReference(label); case TypeReference.CAST -> TypeAnnotation.TargetInfo.ofCastExpr(label, reference.getTypeArgumentIndex()); case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT -> TypeAnnotation.TargetInfo.ofConstructorInvocationTypeArgument(label, reference.getTypeArgumentIndex()); case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT -> TypeAnnotation.TargetInfo.ofMethodInvocationTypeArgument(label, reference.getTypeArgumentIndex()); case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT -> TypeAnnotation.TargetInfo.ofConstructorReferenceTypeArgument(label, reference.getTypeArgumentIndex()); case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT -> TypeAnnotation.TargetInfo.ofMethodReferenceTypeArgument(label, reference.getTypeArgumentIndex()); default -> throw new IllegalArgumentException("Unexpected reference sort: " + reference.getSort()); }))); } private static AnnotationVisitor ofTargetedTypeAnnotation(JdkClassWriter classWriter, String descriptor, int typeRef, TypePath typePath, Consumer, TypeAnnotation>> consumer) { return ofUnresolvedTypeAnnotation(classWriter, descriptor, typeRef, typePath, function -> consumer.accept(targets -> function.apply(reference -> switch (reference.getSort()) { case TypeReference.LOCAL_VARIABLE -> TypeAnnotation.TargetInfo.ofLocalVariable(targets); case TypeReference.RESOURCE_VARIABLE -> TypeAnnotation.TargetInfo.ofResourceVariable(targets); default -> throw new IllegalArgumentException("Unexpected reference sort: " + reference.getSort()); }))); } private static AnnotationVisitor ofUnresolvedTypeAnnotation(JdkClassWriter classWriter, String descriptor, int typeRef, TypePath typePath, Consumer, TypeAnnotation>> consumer) { List elements = new ArrayList<>(); List components; if (typePath == null) { components = List.of(); } else { components = new ArrayList<>(typePath.getLength()); for (int index = 0; index < typePath.getLength(); index++) { components.add(switch (typePath.getStep(index)) { case TypePath.ARRAY_ELEMENT -> TypeAnnotation.TypePathComponent.ARRAY; case TypePath.INNER_TYPE -> TypeAnnotation.TypePathComponent.INNER_TYPE; case TypePath.WILDCARD_BOUND -> TypeAnnotation.TypePathComponent.WILDCARD; case TypePath.TYPE_ARGUMENT -> TypeAnnotation.TypePathComponent.of(TypeAnnotation.TypePathComponent.Kind.TYPE_ARGUMENT, typePath.getStepArgument(index)); default -> throw new IllegalArgumentException("Unkniwn type path type: " + typePath.getStep(index)); }); } } TypeReference reference = new TypeReference(typeRef); return classWriter.new WritingAnnotationVisitor((name, value) -> elements.add(AnnotationElement.of(name, value)), () -> consumer.accept(targeting -> TypeAnnotation.of(targeting.apply(reference), components, Annotation.of(ClassDesc.ofDescriptor(descriptor), elements)))); } private WritingAnnotationVisitor(BiConsumer consumer, Runnable onEnd) { super(Opcodes.ASM9); this.consumer = consumer; this.onEnd = onEnd; } boolean has(ClassModel classModel) { return Objects.equals(JdkClassWriter.this.classModel, classModel); } void add(String name, AnnotationValue annotationValue) { consumer.accept(name, annotationValue); } @Override public void visit(String name, Object asm) { consumer.accept(name, switch (asm) { case Boolean value -> AnnotationValue.ofBoolean(value); case Byte value -> AnnotationValue.ofByte(value); case Short value -> AnnotationValue.ofShort(value); case Character value -> AnnotationValue.ofChar(value); case Integer value -> AnnotationValue.ofInt(value); case Long value -> AnnotationValue.ofLong(value); case Float value -> AnnotationValue.ofFloat(value); case Double value -> AnnotationValue.ofDouble(value); case String value -> AnnotationValue.ofString(value); case boolean[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofBoolean(array[index]); } yield AnnotationValue.ofArray(values); } case byte[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofByte(array[index]); } yield AnnotationValue.ofArray(values); } case short[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofShort(array[index]); } yield AnnotationValue.ofArray(values); } case char[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofChar(array[index]); } yield AnnotationValue.ofArray(values); } case int[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofInt(array[index]); } yield AnnotationValue.ofArray(values); } case long[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofLong(array[index]); } yield AnnotationValue.ofArray(values); } case float[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofFloat(array[index]); } yield AnnotationValue.ofArray(values); } case double[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofDouble(array[index]); } yield AnnotationValue.ofArray(values); } case String[] array -> { AnnotationValue[] values = new AnnotationValue[array.length]; for (int index = 0; index < array.length; index++) { values[index] = AnnotationValue.ofString(array[index]); } yield AnnotationValue.ofArray(values); } case Type type -> AnnotationValue.ofClass(ClassDesc.ofDescriptor(type.getDescriptor())); case null, default -> throw new IllegalArgumentException("Unknown annotation value: " + asm); }); } @Override public void visitEnum(String name, String descriptor, String value) { consumer.accept(name, AnnotationValue.ofEnum(ClassDesc.ofDescriptor(descriptor), value)); } @Override public AnnotationVisitor visitAnnotation(String name, String descriptor) { return WritingAnnotationVisitor.of(JdkClassWriter.this, descriptor, annotation -> consumer.accept(name, AnnotationValue.ofAnnotation(annotation))); } @Override public AnnotationVisitor visitArray(String name) { List values = new ArrayList<>(); return new WritingAnnotationVisitor((_, value) -> values.add(value), () -> consumer.accept(name, AnnotationValue.ofArray(values))); } @Override public void visitEnd() { onEnd.run(); } } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-9/000077500000000000000000000000001502361351200217305ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-9/codes/000077500000000000000000000000001502361351200230255ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-9/codes/rafael/000077500000000000000000000000001502361351200242575ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-9/codes/rafael/asmjdkbridge/000077500000000000000000000000001502361351200267055ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-9/codes/rafael/asmjdkbridge/Dummy.java000066400000000000000000000000641502361351200306430ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; class Dummy { } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java-9/module-info.java000066400000000000000000000001541502361351200250110ustar00rootroot00000000000000module codes.rafael.asmjdkbridge { requires org.objectweb.asm; exports codes.rafael.asmjdkbridge; } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/000077500000000000000000000000001502361351200215625ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/000077500000000000000000000000001502361351200226575ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/rafael/000077500000000000000000000000001502361351200241115ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/rafael/asmjdkbridge/000077500000000000000000000000001502361351200265375ustar00rootroot00000000000000asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/rafael/asmjdkbridge/JdkClassReader.java000066400000000000000000000056771502361351200322420ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Type; import java.io.IOException; import java.io.InputStream; /** * A reader for class files that uses the JDK class file API. The created class reader is immutable. */ public class JdkClassReader { /** * Creates a new class reader. * * @param classFile The class file to represent. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. */ public JdkClassReader(byte[] classFile, Attribute... attributePrototypes) { throw new UnsupportedOperationException(); } /** * Creates a new class reader. * * @param inputStream An input stream of the class file to represent. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. * @throws IOException If the stream cannot be read. */ public JdkClassReader(InputStream inputStream, Attribute... attributePrototypes) throws IOException { throw new UnsupportedOperationException(); } /** * Creates a new class reader. * * @param className The name of the class to represent. The class must be resolvable from the system loader. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. * @throws IOException If the class file cannot be read. */ public JdkClassReader(String className, Attribute... attributePrototypes) throws IOException { throw new UnsupportedOperationException(); } /** * Returns the access flags of this class as stored in the class file. * * @return The access flags of this class as stored in the class file. */ public int getAccess() { throw new UnsupportedOperationException(); } /** * Returns the internal name of this class. * * @return The internal name of this class. */ public String getClassName() { throw new UnsupportedOperationException(); } /** * Returns the internal super class name of this class or {@code null} for {@link Object}. * * @return The internal super class name of this class or {@code null} for {@link Object}. */ public String getSuperName() { throw new UnsupportedOperationException(); } /** * Returns the internal interface names of this class. Maybe {@code null}. * * @return The internal interface names of this class. */ public String[] getInterfaces() { throw new UnsupportedOperationException(); } /** * Accepts a class visitor for the represented class file. * * @param classVisitor The class visitor to delegate calls to. * @param flags The ASM flags to consider when visiting the class file. */ public void accept(ClassVisitor classVisitor, int flags) { throw new UnsupportedOperationException(); } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/rafael/asmjdkbridge/JdkClassWriter.java000066400000000000000000000155711502361351200323060ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.ModuleVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.RecordComponentVisitor; import org.objectweb.asm.TypePath; import java.lang.reflect.Method; import java.util.function.Function; /** * A class visitor that creates a class file which is based upon the JDK Class File API. */ public class JdkClassWriter extends ClassVisitor { /** * Creates a class writer. * * @param flags The ASM flags to consider. */ public JdkClassWriter(int flags) { super(Opcodes.ASM9); throw new UnsupportedOperationException(); } /** * Creates a class writer. * * @param classReader A class reader of which to retain the constant pool, if possible. * @param flags The ASM flags to consider. */ public JdkClassWriter(JdkClassReader classReader, int flags) { super(Opcodes.ASM9); throw new UnsupportedOperationException(); } /** * Creates a class writer. * * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. As a method to allow * pre-Java 8 code to call this constructor via reflection. * @param target The target to invoke the reflective method on. */ public JdkClassWriter(int flags, Method getSuperClass, Object target) { super(Opcodes.ASM9); throw new UnsupportedOperationException(); } /** * Creates a class writer. * * @param classReader A class reader of which to retain the constant pool, if possible. * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. As a method to allow * pre-Java 8 code to call this constructor via reflection. * @param target The target to invoke the reflective method on. */ public JdkClassWriter(JdkClassReader classReader, int flags, Method getSuperClass, Object target) { super(Opcodes.ASM9); throw new UnsupportedOperationException(); } /** * Creates a class writer. * * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. */ public JdkClassWriter(int flags, Function getSuperClass) { super(Opcodes.ASM9); throw new UnsupportedOperationException(); } /** * Creates a class writer. * * @param classReader A class reader of which to retain the constant pool, if possible. * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. */ public JdkClassWriter(JdkClassReader classReader, int flags, Function getSuperClass) { super(Opcodes.ASM9); throw new UnsupportedOperationException(); } /** * Returns the generated class file. * * @return The class file as a byte array. */ public byte[] toByteArray() { throw new UnsupportedOperationException(); } /** * Returns the super class of the class that is provided by name. The default implementation * resolves the super class from this instance's class' {@link ClassLoader}, unless * {@link #getClassLoader()} is overridden. *

* This is used for generating stack map frames. * * @param name The name of the class for which to resolve the super class. * @return The name of the resolved super class. */ protected String getSuperClass(String name) { throw new UnsupportedOperationException(); } /** * Returns the class loader to use for resolving the super class of a discovered class. * * @return The class loader to use for resolving a super class's name. */ protected ClassLoader getClassLoader() { throw new UnsupportedOperationException(); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { throw new UnsupportedOperationException(); } @Override public void visitSource(String source, String debug) { throw new UnsupportedOperationException(); } @Override public ModuleVisitor visitModule(String name, int access, String version) { throw new UnsupportedOperationException(); } @Override public void visitNestHost(String nestHost) { throw new UnsupportedOperationException(); } @Override public void visitOuterClass(String owner, String name, String descriptor) { throw new UnsupportedOperationException(); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { throw new UnsupportedOperationException(); } @Override public void visitAttribute(Attribute attribute) { throw new UnsupportedOperationException(); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { throw new UnsupportedOperationException(); } @Override public void visitNestMember(String nestMember) { throw new UnsupportedOperationException(); } @Override public void visitPermittedSubclass(String permittedSubclass) { throw new UnsupportedOperationException(); } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { throw new UnsupportedOperationException(); } @Override public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { throw new UnsupportedOperationException(); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { throw new UnsupportedOperationException(); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { throw new UnsupportedOperationException(); } @Override public void visitEnd() { throw new UnsupportedOperationException(); } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/rafael/asmjdkbridge/ProbingClassReader.java000066400000000000000000000133021502361351200331120ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; /** * A class reader that automatically resolves a suitable reader, either based on ASM (if ASM officially * supports a class file version) or a JDK Class File API based implementation. */ public class ProbingClassReader { private final ProbingResolver resolver; /** * Creates a new class reader. * * @param classFile The class file to represent. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. */ public ProbingClassReader(byte[] classFile, Attribute... attributePrototypes) { resolver = ProbingResolver.ofClassFile(classFile, attributePrototypes); } /** * Creates a new class reader. * * @param inputStream An input stream of the class file to represent. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. * @throws IOException If the stream cannot be read. */ public ProbingClassReader(InputStream inputStream, Attribute... attributePrototypes) throws IOException { this(readAllBytes(inputStream), attributePrototypes); } /** * Creates a new class reader. * * @param className The name of the class to represent. The class must be resolvable from the system loader. * @param attributePrototypes Prototypes of ASM attributes to map if discovered. * @throws IOException If the class file cannot be read. */ public ProbingClassReader(String className, Attribute... attributePrototypes) throws IOException { byte[] classFile; try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class")) { classFile = readAllBytes(inputStream); } resolver = ProbingResolver.ofClassFile(classFile, attributePrototypes); } private static byte[] readAllBytes(InputStream inputStream) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 8]; int length; while ((length = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, length); } return outputStream.toByteArray(); } /** * Returns the access flags of this class as stored in the class file. * * @return The access flags of this class as stored in the class file. */ public int getAccess() { return resolver.getAccess(); } /** * Returns the internal name of this class. * * @return The internal name of this class. */ public String getClassName() { return resolver.getClassName(); } /** * Returns the internal super class name of this class or {@code null} for {@link Object}. * * @return The internal super class name of this class or {@code null} for {@link Object}. */ public String getSuperName() { return resolver.getSuperName(); } /** * Returns the internal interface names of this class. Maybe {@code null}. * * @return The internal interface names of this class. */ public String[] getInterfaces() { return resolver.getInterfaces(); } /** * Accepts a class visitor for the represented class file. * * @param classVisitor The class visitor to delegate calls to. * @param flags The ASM flags to consider when visiting the class file. */ public void accept(ClassVisitor classVisitor, int flags) { resolver.accept(classVisitor, flags); } /** * Resolves the underlying class reader to an implementation-equivalent class writer. Using this approach, * an attempt is made to retain the constant pool if a class is supposed to be transformed. * * @param flags The ASM flags to consider when creating the class writer. * @return A suitable container for a class writer. */ public ClassWriterContainer toClassWriter(int flags) { return resolver.toClassWriter(flags); } /** * A container for a class writer. * * @param The type of the class visitor that represents the class writer. */ public abstract static class ClassWriterContainer { final T delegate; ClassWriterContainer(T delegate) { this.delegate = delegate; } /** * Returns the underlying ASM class visitor. * * @return The underlying ASM class visitor. */ public ClassVisitor getClassVisitor() { return delegate; } /** * Returns the byte array that is created by this class writer. * * @return A byte array that represents the generated class file. */ public abstract byte[] toByteArray(); static class OfAsm extends ClassWriterContainer { OfAsm(ClassReader classReader, int flags) { super(new ClassWriter(classReader, flags)); } @Override public byte[] toByteArray() { return delegate.toByteArray(); } } static class OfJdk extends ClassWriterContainer { OfJdk(JdkClassReader classReader, int flags) { super(new JdkClassWriter(classReader, flags)); } @Override public byte[] toByteArray() { return delegate.toByteArray(); } } } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/rafael/asmjdkbridge/ProbingClassWriter.java000066400000000000000000000062671502361351200332000ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.function.Function; /** * A class writer that automatically resolves a suitable writer, either based on ASM (if ASM officially * supports a class file version) or a JDK Class File API based implementation. */ public class ProbingClassWriter extends ClassVisitor { private final int flags; private final Function getSuperClass; /** * Creates a class writer. * * @param flags The ASM flags to consider. */ public ProbingClassWriter(int flags) { super(Opcodes.ASM9); this.flags = flags; getSuperClass = null; } /** * Creates a class writer. * * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. As a method to allow * pre-Java 8 code to call this constructor via reflection. * @param target The target to invoke the reflective method on. */ public ProbingClassWriter(int flags, Method getSuperClass, Object target) { super(Opcodes.ASM9); this.flags = flags; this.getSuperClass = getSuperClass == null ? null : name -> { try { return (String) getSuperClass.invoke(target, name); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); } }; } /** * Creates a class writer. * * @param flags The ASM flags to consider. * @param getSuperClass A resolver for the supplied internal class name's internal super class name. If * a class is an interface, {@code null} should be returned. */ public ProbingClassWriter(int flags, Function getSuperClass) { super(Opcodes.ASM9); this.flags = flags; this.getSuperClass = getSuperClass; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { cv = ProbingResolver.ofVersion(flags, version, getSuperClass); super.visit(version, access, name, signature, superName, interfaces); } /** * Returns the generated class file. * * @return The class file as a byte array. */ public byte[] toByteArray() { if (cv instanceof JdkClassWriter) { return ((JdkClassWriter) cv).toByteArray(); } else if (cv instanceof ClassWriter) { return ((ClassWriter) cv).toByteArray(); } else if (cv instanceof ProbingClassWriter) { return ((ProbingClassWriter) cv).toByteArray(); } else if (cv == null) { throw new IllegalStateException("No version discovered"); } else { throw new IllegalStateException("Unexpected type: " + cv.getClass().getTypeName()); } } } asm-jdk-bridge-0.0.10/asm-jdk-bridge/src/main/java/codes/rafael/asmjdkbridge/ProbingResolver.java000066400000000000000000000117041502361351200325270ustar00rootroot00000000000000package codes.rafael.asmjdkbridge; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import java.util.function.Function; abstract class ProbingResolver { private static final String OBJECT = "java/lang/Object"; private static final int SUPPORTED; static { int version = Opcodes.V24; try { int candidate = 25; while (!Thread.interrupted()) { version = (Integer) Opcodes.class.getField("V" + candidate++).get(null); } } catch (Throwable ignored) { } SUPPORTED = version & 0xFFFF; } static ClassVisitor ofVersion(int flags, int version, Function getSuperClass) { if (version > SUPPORTED) { return new JdkClassWriter(flags, getSuperClass); } else { return new ClassWriter(flags) { @Override protected String getCommonSuperClass(String type1, String type2) { if (getSuperClass == null) { return super.getCommonSuperClass(type1, type2); } if (type1.equals(type2)) { return type1; } String class1 = getSuperClass.apply(type1), class2 = getSuperClass.apply(type2); if (class1 == null || class2 == null) { return OBJECT; } else { while (!class1.equals(OBJECT)) { if (class1.equals(type2)) { return type2; } class1 = getSuperClass.apply(class1); } while (!class2.equals(OBJECT)) { if (class2.equals(type1)) { return type1; } class2 = getSuperClass.apply(class2); } return OBJECT; } } }; } } static ProbingResolver ofClassFile(byte[] classFile, Attribute[] attributePrototypes) { int majorVersion = classFile[6] << 8 | classFile[7]; if (majorVersion > SUPPORTED) { return new OfJdk(classFile, attributePrototypes); } else { return new OfAsm(classFile, attributePrototypes); } } abstract int getAccess(); abstract String getClassName(); abstract String getSuperName(); abstract String[] getInterfaces(); abstract void accept(ClassVisitor classVisitor, int flags); abstract ProbingClassReader.ClassWriterContainer toClassWriter(int flags); static class OfAsm extends ProbingResolver { private final ClassReader classReader; private final Attribute[] attributePrototypes; OfAsm(byte[] classFile, Attribute[] attributePrototypes) { classReader = new ClassReader(classFile); this.attributePrototypes = attributePrototypes; } @Override int getAccess() { return classReader.getAccess(); } @Override String getClassName() { return classReader.getClassName(); } @Override String getSuperName() { return classReader.getSuperName(); } @Override String[] getInterfaces() { return classReader.getInterfaces(); } @Override void accept(ClassVisitor classVisitor, int flags) { classReader.accept(classVisitor, attributePrototypes, flags); } @Override ProbingClassReader.ClassWriterContainer toClassWriter(int flags) { return new ProbingClassReader.ClassWriterContainer.OfAsm(classReader, flags); } } static class OfJdk extends ProbingResolver { private final JdkClassReader classReader; OfJdk(byte[] classFile, Attribute[] attributePrototypes) { classReader = new JdkClassReader(classFile, attributePrototypes); } @Override int getAccess() { return classReader.getAccess(); } @Override String getClassName() { return classReader.getClassName(); } @Override String getSuperName() { return classReader.getSuperName(); } @Override String[] getInterfaces() { return classReader.getInterfaces(); } @Override void accept(ClassVisitor classVisitor, int flags) { classReader.accept(classVisitor, flags); } @Override ProbingClassReader.ClassWriterContainer toClassWriter(int flags) { return new ProbingClassReader.ClassWriterContainer.OfJdk(classReader, flags); } } } asm-jdk-bridge-0.0.10/pom.xml000066400000000000000000000066301502361351200156700ustar00rootroot00000000000000 4.0.0 codes.rafael.asmjdkbridge asm-jdk-bridge-parent 0.0.10 pom ASM to OpenJDK Class API bridge (parent) A common parent for the ASM to OpenJDK Class File API. 2025 asm-jdk-bridge asm-jdk-bridge-test https://github.com/raphw/asm-jdk-bridge 8 UTF-8 The Apache Software License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0.txt repo A business-friendly OSS license raphw Rafael Winterhalter rafael.wth@gmail.com https://rafael.codes developer +1 org.sonatype.oss oss-parent 9 github.com https://github.com/raphw/asm-jdk-bridge/issues scm:git:git@github.com:raphw/asm-jdk-bridge.git scm:git:git@github.com:raphw/asm-jdk-bridge.git git@github.com:raphw/asm-jdk-bridge.git HEAD org.apache.maven.plugins maven-javadoc-plugin 3.10.1 org.apache.maven.plugins maven-gpg-plugin 3.2.4 sign-artifacts install sign org.sonatype.central central-publishing-maven-plugin 0.7.0 true true central