3131import java .io .IOException ;
3232import java .io .InputStream ;
3333import java .io .PrintStream ;
34+ import java .lang .annotation .Documented ;
3435import java .lang .instrument .ClassDefinition ;
3536
3637import net .tascalate .asmx .ClassReader ;
4344class RuntimeBytecodeInjector {
4445
4546 private static final String CLASS = "java.lang.invoke.InnerClassLambdaMetafactory" ;
46-
47- private static ClassDefinition loadClassDefinition (Class <?> clazz ) throws IOException {
48- ByteArrayOutputStream out = new ByteArrayOutputStream ();
49- byte [] buff = new byte [1024 ];
50- try (InputStream in = Object .class .getResourceAsStream ('/' + clazz .getName ().replace ('.' , '/' ) + ".class" )) {
51- int c = 0 ;
52- while ((c = in .read (buff )) > 0 ) {
53- out .write (buff , 0 , c );
54- }
55- }
56- out .close ();
57- byte [] bytes = out .toByteArray ();
58- return new ClassDefinition (clazz , bytes );
59- }
6047
61- private static ClassDefinition loadClassDefinition ( String className ) throws ClassNotFoundException , IOException {
62- return loadClassDefinition (Class . forName ( className )) ;
48+ static interface LambdaClassTransformer {
49+ byte [] transform (Class <?> lambdaOwningClass , byte [] lambdaClassBytes ) throws Throwable ;
6350 }
6451
65- static boolean isValidCaller (Object o ) {
66- return o != null && CLASS .equals (o .getClass ().getName ());
52+ static boolean isInjectionApplied () {
53+ Class <?> cls ;
54+ try {
55+ cls = ClassLoader .getSystemClassLoader ().loadClass (CLASS );
56+ } catch (ClassNotFoundException ex ) {
57+ throw new RuntimeException (ex );
58+ }
59+ Documented anno = cls .getAnnotation (Documented .class );
60+ return anno != null ;
6761 }
68-
62+
6963 static ClassDefinition modifyLambdaMetafactory () throws ClassNotFoundException , IOException {
7064 ClassDefinition original = loadClassDefinition (CLASS );
7165 ClassReader classReader = new ClassReader (new ByteArrayInputStream (original .getDefinitionClassFile ()));
@@ -93,14 +87,7 @@ public void visitMethodInsn(int opcode, String owner, String name, String descri
9387 super .visitMethodInsn (opcode , owner , name , descriptor , isInterface );
9488 visitVarInsn (bytesType .getOpcode (ISTORE ), bytesVar );
9589
96- /*
97- Type stringType = Type.getType(String.class);
98- visitFieldInsn(GETSTATIC, systemType.getInternalName(), "out", printStreamType.getDescriptor());
99- visitLdcInsn("HERE WE ARE: ");
100- visitMethodInsn(INVOKEVIRTUAL, printStreamType.getInternalName(), "print", Type.getMethodDescriptor(Type.VOID_TYPE, stringType), false);
101- */
102-
103- visitInsn (ICONST_4 );
90+ visitInsn (ICONST_3 );
10491 visitTypeInsn (ANEWARRAY , objectType .getInternalName ());
10592 visitVarInsn (objectsType .getOpcode (ISTORE ), params );
10693
@@ -117,30 +104,19 @@ public void visitMethodInsn(int opcode, String owner, String name, String descri
117104 visitFieldInsn (GETFIELD , "java/lang/invoke/AbstractValidatingLambdaMetafactory" , "targetClass" , classType .getDescriptor ());
118105 visitInsn (AASTORE );
119106
120- // params[2] = inBytes
107+ // params[2] = inBytes, after call replaced by outBytes
121108 visitVarInsn (objectsType .getOpcode (ILOAD ), params );
122109 visitInsn (ICONST_2 );
123110 visitVarInsn (bytesType .getOpcode (ILOAD ), bytesVar );
124111 visitInsn (AASTORE );
125112
126- // params[3] = inBytes, after call replaced by outBytes
127- visitVarInsn (objectsType .getOpcode (ILOAD ), params );
128- visitInsn (ICONST_3 );
129- visitVarInsn (bytesType .getOpcode (ILOAD ), bytesVar );
130- visitInsn (AASTORE );
131-
132113 visitFieldInsn (GETSTATIC , systemType .getInternalName (), "out" , printStreamType .getDescriptor ());
133114 visitVarInsn (objectsType .getOpcode (ILOAD ), params );
134- visitMethodInsn (INVOKEVIRTUAL , printStreamType .getInternalName (), "println" , Type .getMethodDescriptor (Type .VOID_TYPE , objectType ), false );
135-
136- /*
137- visitFieldInsn(GETSTATIC, systemType.getInternalName(), "out", printStreamType.getDescriptor());
138- visitMethodInsn(INVOKEVIRTUAL, printStreamType.getInternalName(), "println", Type.getMethodDescriptor(Type.VOID_TYPE), false);
139- */
115+ visitMethodInsn (INVOKEVIRTUAL , printStreamType .getInternalName (), "print" , Type .getMethodDescriptor (Type .VOID_TYPE , objectType ), false );
140116
141- // get outBytes, params[3 ]
117+ // get outBytes, params[2 ]
142118 visitVarInsn (objectsType .getOpcode (ILOAD ), params );
143- visitInsn (ICONST_3 );
119+ visitInsn (ICONST_2 );
144120 visitInsn (AALOAD );
145121 } else {
146122 super .visitMethodInsn (opcode , owner , name , descriptor , isInterface );
@@ -149,6 +125,18 @@ public void visitMethodInsn(int opcode, String owner, String name, String descri
149125 };
150126
151127 ClassVisitor cv = new ClassVisitor (ASM9 , classWriter ) {
128+ @ Override
129+ public void visit (int version ,
130+ int access ,
131+ String name ,
132+ String signature ,
133+ String superName ,String [] interfaces ) {
134+ super .visit (version , access , name , signature , superName , interfaces );
135+ Type annoType = Type .getType (Documented .class );
136+ visitAnnotation (annoType .getDescriptor (), true ).visitEnd ();
137+ }
138+
139+ @ Override
152140 public MethodVisitor visitMethod (int access , String name , String desc , String signature , String [] exceptions ) {
153141 MethodVisitor mv = super .visitMethod (access , name , desc , signature , exceptions );
154142 if ("spinInnerClass" .equals (name ) || "generateInnerClass" .equals (name )) {
@@ -162,4 +150,54 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
162150 classReader .accept (cv , ClassReader .EXPAND_FRAMES );
163151 return new ClassDefinition (original .getDefinitionClass (), classWriter .toByteArray ());
164152 }
153+
154+ static void installTransformer (final LambdaClassTransformer transformer ) {
155+ System .setOut (new PrintStream (System .out , true ) {
156+ @ Override
157+ public void print (Object o ) {
158+ if (o instanceof Object []) {
159+ Object [] params = (Object [])o ;
160+ if (params .length == 3 &&
161+ RuntimeBytecodeInjector .isValidCaller (params [0 ]) &&
162+ params [1 ] instanceof Class &&
163+ params [2 ] instanceof byte []) {
164+
165+ byte [] inBytes = (byte [])params [2 ];
166+ byte [] outBytes = inBytes ;
167+ try {
168+ outBytes = transformer .transform ((Class <?>)params [1 ], inBytes );
169+ } catch (Throwable ex ) {
170+
171+ }
172+ params [2 ] = outBytes == null ? inBytes : outBytes ;
173+ return ;
174+ }
175+ }
176+ super .println (o );
177+ }
178+ });
179+ }
180+
181+ private static boolean isValidCaller (Object o ) {
182+ return o != null && CLASS .equals (o .getClass ().getName ());
183+ }
184+
185+ private static ClassDefinition loadClassDefinition (Class <?> clazz ) throws IOException {
186+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
187+ byte [] buff = new byte [1024 ];
188+ try (InputStream in = Object .class .getResourceAsStream ('/' + clazz .getName ().replace ('.' , '/' ) + ".class" )) {
189+ int c = 0 ;
190+ while ((c = in .read (buff )) > 0 ) {
191+ out .write (buff , 0 , c );
192+ }
193+ }
194+ out .close ();
195+ byte [] bytes = out .toByteArray ();
196+ return new ClassDefinition (clazz , bytes );
197+ }
198+
199+ private static ClassDefinition loadClassDefinition (String className ) throws ClassNotFoundException , IOException {
200+ return loadClassDefinition (Class .forName (className ));
201+ }
202+
165203}
0 commit comments