Serialization handling - see github issue #24

This commit is contained in:
Andy Clement
2014-03-11 12:39:34 -07:00
parent 4bd2f93d71
commit 513257a84d
27 changed files with 1704 additions and 58 deletions

View File

@@ -130,8 +130,14 @@ public interface Constants extends Opcodes {
public static int JLC_GETMODIFIERS = 0x0080;
public static int JLC_GETMETHODS = 0x0100;
public static int JLC_GETCONSTRUCTOR = 0x0200;
public static int JLC_GETDECLAREDCONSTRUCTORS = 0x0400;
public static int JLRM_INVOKE = 0x0800;
public static int JLRF_GET = 0x1000;
public static int JLRF_GETLONG = 0x2000;
public static int JLOS_HASSTATICINITIALIZER = 0x4000;
// For rewritten reflection in system classes, these are used:
// The member names are used for fields *and* methods
static final String jlcgdfs = "__sljlcgdfs";
static final String jlcgdfsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Field;";
static final String jlcgdf = "__sljlcgdf";
@@ -152,6 +158,17 @@ public interface Constants extends Opcodes {
static final String jlcgmodsDescriptor = "(Ljava/lang/Class;)I";
static final String jlcgms = "__sljlcgms";
static final String jlcgmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
// TODO migrate those above to this slightly more comprehensible format
static final String jlcGetDeclaredConstructorsMember = "__sljlcgdcs";
static final String jlcGetDeclaredConstructorsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Constructor;";
static final String jlrmInvokeMember = "__sljlrmi";
static final String jlrmInvokeDescriptor = "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
static final String jlrfGetMember = "__sljlrfg";
static final String jlrfGetDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;";
static final String jlrfGetLongMember = "__sljlrfgl";
static final String jlrfGetLongDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J";
static final String jloObjectStream_hasInitializerMethod = "__sljlos_him";
static final String methodSuffixSuperDispatcher = "_$superdispatcher$";
}

View File

@@ -1480,4 +1480,8 @@ public class ReloadableType {
return superRtype;
}
}
public boolean hasStaticInitializer() {
return this.typedescriptor.hasClinit();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2012 VMware and contributors
* Copyright 2010-2014 Pivotal Software, Inc. and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.springsource.loaded;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -53,9 +54,17 @@ import org.objectweb.asm.Opcodes;
* <ul>
* <li>getMethods
* </ul>
* Due to ObjectStream (added in SL 1.2.0)
* <ul>
* <li>Class.getDeclaredConstructors
* <li>Field.get
* <li>Field.getLong
* <li>Method.invoke
* </ul>
* The method hasStaticInitializer(Class) in ObjectStream needs special handling.
*
* <p>
* This class modifiers the calls to the reflective APIs, adds the fields and helper methods. The wiring of the SpringLoaded
* This class modifies the calls to the reflective APIs, adds the fields and helper methods. The wiring of the SpringLoaded
* reflectiveinterceptor into types affected by this rewriter is currently done in SpringLoadedPreProcessor.
*
* @author Andy Clement
@@ -70,7 +79,8 @@ public class SystemClassReflectionRewriter {
log.info("SystemClassReflectionRewriter running for " + slashedClassName);
}
ClassReader fileReader = new ClassReader(bytes);
RewriteClassAdaptor classAdaptor = new RewriteClassAdaptor();
boolean is_jlObjectStream = slashedClassName.equals("java/io/ObjectStreamClass");
RewriteClassAdaptor classAdaptor = new RewriteClassAdaptor(is_jlObjectStream);
// TODO always skip frames? or just for javassist things?
fileReader.accept(classAdaptor, ClassReader.SKIP_FRAMES);
return new RewriteResult(classAdaptor.getBytes(), classAdaptor.getBits());
@@ -91,16 +101,21 @@ public class SystemClassReflectionRewriter {
public String summarize() {
StringBuilder s = new StringBuilder();
s.append((bits & JLC_GETDECLAREDCONSTRUCTOR) != 0 ? "getDeclaredConstructor()" : "");
s.append((bits & JLC_GETCONSTRUCTOR) != 0 ? "getConstructor()" : "");
s.append((bits & JLC_GETMODIFIERS) != 0 ? "getModifiers()" : "");
s.append((bits & JLC_GETDECLAREDFIELDS) != 0 ? "getDeclaredFields() " : "");
s.append((bits & JLC_GETDECLAREDFIELD) != 0 ? "getDeclaredField() " : "");
s.append((bits & JLC_GETFIELD) != 0 ? "getField() " : "");
s.append((bits & JLC_GETDECLAREDMETHODS) != 0 ? "getDeclaredMethods() " : "");
s.append((bits & JLC_GETDECLAREDMETHOD) != 0 ? "getDeclaredMethod() " : "");
s.append((bits & JLC_GETMETHOD) != 0 ? "getMethod() " : "");
s.append((bits & JLC_GETMETHODS) != 0 ? "getMethods() " : "");
s.append((bits & JLC_GETDECLAREDCONSTRUCTORS) != 0 ? "Class.getDeclaredConstructors()":"");
s.append((bits & JLC_GETDECLAREDCONSTRUCTOR) != 0 ? "Class.getDeclaredConstructor()" : "");
s.append((bits & JLC_GETCONSTRUCTOR) != 0 ? "Class.getConstructor()" : "");
s.append((bits & JLC_GETMODIFIERS) != 0 ? "Class.getModifiers()" : "");
s.append((bits & JLC_GETDECLAREDFIELDS) != 0 ? "Class.getDeclaredFields() " : "");
s.append((bits & JLC_GETDECLAREDFIELD) != 0 ? "Class.getDeclaredField() " : "");
s.append((bits & JLC_GETFIELD) != 0 ? "Class.getField() " : "");
s.append((bits & JLC_GETDECLAREDMETHODS) != 0 ? "Class.getDeclaredMethods() " : "");
s.append((bits & JLC_GETDECLAREDMETHOD) != 0 ? "Class.getDeclaredMethod() " : "");
s.append((bits & JLC_GETMETHOD) != 0 ? "Class.getMethod() " : "");
s.append((bits & JLC_GETMETHODS) != 0 ? "Class.getMethods() " : "");
s.append((bits & JLRM_INVOKE) != 0 ? "Method.invoke() " : "");
s.append((bits & JLRF_GET) != 0 ? "Field.get() " : "");
s.append((bits & JLRF_GETLONG) != 0 ? "Field.getLong() " : "");
s.append((bits & JLOS_HASSTATICINITIALIZER) != 0 ? "jlObjectStream.hasStaticInitializer() " : "");
return s.toString().trim();
}
}
@@ -110,6 +125,7 @@ public class SystemClassReflectionRewriter {
private ClassWriter cw;
int bits = 0x0000;
private String classname;
private boolean is_jlObjectStream;
// enum SpecialRewrite { NotSpecial, java_io_ObjectStreamClass_2 };
// private SpecialRewrite special = SpecialRewrite.NotSpecial;
@@ -119,11 +135,14 @@ public class SystemClassReflectionRewriter {
String s = new StringBuilder(owner).append(".").append(methodName).toString();
return MethodInvokerRewriter.RewriteClassAdaptor.intercepted.contains(s);
}
public RewriteClassAdaptor() {
// TODO should it also compute frames?
public RewriteClassAdaptor(boolean is_jlObjectStream) {
super(ASM5,new ClassWriter(ClassWriter.COMPUTE_MAXS));
cw = (ClassWriter) cv;
this.is_jlObjectStream = is_jlObjectStream;
if (this.is_jlObjectStream) {
bits |= JLOS_HASSTATICINITIALIZER;
}
}
public byte[] getBytes() {
@@ -143,9 +162,29 @@ public class SystemClassReflectionRewriter {
// special = SpecialRewrite.java_io_ObjectStreamClass_2;
// }
}
static Method m = null;
private static boolean hasStaticInitializer(Class cl) {
try {
return (Boolean)m.invoke(null,cl);
}catch (Exception e) {
return false;
}
}
@Override
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
// if (is_jlObjectStream) {
// // TODO [serialization] clear those caches in the JVMPlugin
// // TODO [serialization] deal with FieldReflectors and changing formats? Maybe leave that for now and assume all the real fields are 'first'?
// // TODO [serialization] because not all classes to be serialized are reloadable ones, we'll need to change what we do here, generate the existing native method but an additional one that can delegate to it or call our SL layer
// if (name.equals("hasStaticInitializer")) {
// bits |= JLOS_HASSTATICINITIALIZER;
// SystemClassReflectionGenerator.generateJLObjectStream_hasStaticInitializer(cw, classname);
// return null;
// }
// }
MethodVisitor mv = super.visitMethod(flags, name, descriptor, signature, exceptions);
return new RewritingMethodAdapter(mv);
}
@@ -181,12 +220,27 @@ public class SystemClassReflectionRewriter {
if ((bits & JLC_GETDECLAREDCONSTRUCTOR) != 0) {
SystemClassReflectionGenerator.generateJLCGDC(cw, classname);
}
if ((bits & JLC_GETDECLAREDCONSTRUCTORS) != 0) {
SystemClassReflectionGenerator.generateJLC_GetDeclaredConstructors(cw, classname);
}
if ((bits & JLC_GETMETHODS) != 0) {
SystemClassReflectionGenerator.generateJLCGetXXXMethods(cw, classname, "getMethods");
}
if ((bits & JLC_GETCONSTRUCTOR) != 0) {
SystemClassReflectionGenerator.generateJLCGC(cw, classname);
}
if ((bits & JLRM_INVOKE) != 0) {
SystemClassReflectionGenerator.generateJLRM_Invoke(cw, classname);
}
if ((bits & JLRF_GET) != 0) {
SystemClassReflectionGenerator.generateJLRF_Get(cw, classname);
}
if ((bits & JLRF_GETLONG) != 0) {
SystemClassReflectionGenerator.generateJLRF_GetLong(cw, classname);
}
if (this.is_jlObjectStream) {
SystemClassReflectionGenerator.generateJLObjectStream_hasStaticInitializer(cw, classname);
}
}
class RewritingMethodAdapter extends MethodVisitor implements Opcodes, Constants {
@@ -279,6 +333,11 @@ public class SystemClassReflectionRewriter {
return true;
}
}
else if (is_jlObjectStream && owner.equals(classname) && name.equals("hasStaticInitializer")) {
// Call our interception method generated into this type
mv.visitMethodInsn(INVOKESTATIC,classname,jloObjectStream_hasInitializerMethod,desc);
return true;
}
return false;
}
@@ -319,6 +378,11 @@ public class SystemClassReflectionRewriter {
bits |= JLC_GETDECLAREDCONSTRUCTOR;
mv.visitMethodInsn(INVOKESTATIC, classname, jlcgdc, jlcgdcDescriptor);
return true;
} else if (name.equals("getDeclaredConstructors")) {
// stack on arrival: <Class instance>
bits |= JLC_GETDECLAREDCONSTRUCTORS;
mv.visitMethodInsn(INVOKESTATIC, classname, jlcGetDeclaredConstructorsMember,jlcGetDeclaredConstructorsDescriptor);
return true;
} else if (name.equals("getConstructor")) {
// stack on arrival: <Class instance> <Class[] paramTypes>
bits |= JLC_GETCONSTRUCTOR;
@@ -344,10 +408,28 @@ public class SystemClassReflectionRewriter {
// seen: in Proxy Constructor.newInstance() is used on the newly created proxy class - we don't need to intercept that
return false;
}
} else if (owner.equals("java/lang/reflect/Method")) {
if (name.equals("invoke")) {
bits |= JLRM_INVOKE;
// stack on arrival: <Method> <target instance> <parameters array>
mv.visitMethodInsn(INVOKESTATIC, classname, jlrmInvokeMember, jlrmInvokeDescriptor);
return true;
}
} else if (owner.equals("java/lang/reflect/Field")) {
if (name.equals("get")) {
bits |= JLRF_GET;
// stack on arrival: <Field> <target instance>
mv.visitMethodInsn(INVOKESTATIC, classname, jlrfGetMember, jlrfGetDescriptor);
return true;
} else if (name.equals("getLong")) {
bits |= JLRF_GETLONG;
// stack on arrival: <Field> <target instance>
mv.visitMethodInsn(INVOKESTATIC, classname, jlrfGetLongMember, jlrfGetLongDescriptor);
return true;
}
}
System.err.println("!!! SystemClassReflectionRewriter: nyi for " + owner + "." + name);
return false;
//throw new IllegalStateException("nyi for " + owner + "." + name);
}
}
}
@@ -463,6 +545,312 @@ class SystemClassReflectionGenerator implements Constants {
// return 0;
// }
// }
// public static Method __sljlcgdcs;
// private static Constructor[] __sljlcgdcs(Class<?> clazz) {
// if (__sljlcgdcs == null) {
// return clazz.getDeclaredConstructors();
// }
// try {
// return (Constructor[])__sljlcgdcs.invoke(null,clazz);
// } catch (Exception e) {
// return null;
// }
// }
// public static Method __sljlrmi;
// private static Object __sljlrmi(Method method, Object instance, Object[] args) throws InvocationTargetException, IllegalAccessException {
// if (__sljlrmi == null) {
// return method.invoke(instance,args);
// }
// try {
// return __sljlrmi.invoke(null, method, instance, args);
// } catch (Exception e) {
// return null;
// }
// }
// public static Method __sljlrfg;
// private static Object __sljlrfg(Field field, Object instance) throws IllegalArgumentException, IllegalAccessException {
// if (__sljlrfg == null) {
// return field.get(instance);
// }
// try {
// return __sljlrfg.invoke(null, field,instance);
// } catch (Exception e) {
// return null;
// }
// }
// public static Method __sljlrfgl;
// private static long __sljlrfgl(Field field, Object instance) throws IllegalArgumentException, IllegalAccessException {
// if (__sljlrfgl == null) {
// return field.getLong(instance);
// }
// try {
// return (Long)__sljlrfgl.invoke(null, field, instance);
// } catch (Exception e) {
// return 0;
// }
// }
public static void generateJLRF_GetLong(ClassWriter cw, String classname) {
FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, jlrfGetLongMember, "Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC, jlrfGetLongMember, jlrfGetLongDescriptor,
null, new String[]{"java/lang/IllegalAccessException","java/lang/IllegalArgumentException"});
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitFieldInsn(GETSTATIC, classname, jlrfGetLongMember, "Ljava/lang/reflect/Method;");
mv.visitJumpInsn(IFNONNULL, l0);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Field", "getLong", "(Ljava/lang/Object;)J");
mv.visitInsn(LRETURN);
mv.visitLabel(l0);
mv.visitFieldInsn(GETSTATIC, classname, jlrfGetLongMember, "Ljava/lang/reflect/Method;");
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ICONST_2);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ALOAD, 0); // target field
mv.visitInsn(AASTORE);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ALOAD, 1); // instance on which to get the field
mv.visitInsn(AASTORE);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitLabel(l1);
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL,"java/lang/Long","longValue","()J");
mv.visitInsn(LRETURN);
mv.visitLabel(l2);
mv.visitVarInsn(ASTORE, 2);
Label l5 = new Label();
mv.visitLabel(l5);
Label l6 = new Label();
mv.visitLabel(l6);
mv.visitLdcInsn(0L);
mv.visitInsn(LRETURN);
Label l7 = new Label();
mv.visitLabel(l7);
mv.visitMaxs(8, 4);
mv.visitEnd();
}
/**
* Create a method that can be used to intercept the calls to hasStaticInitializer made in the ObjectStreamClass.
* The method will ask SpringLoaded whether the type has a static initializer. SpringLoaded will be able to answer
* if it is a reloadable type. If it is not a reloadable type then springloaded will throw an exception which will
* be caught here and the 'regular' call to hasStaticInitializer will be made.
*
* @param cw the classwriter to create the method in
* @param classname the name of the class being visited
*/
public static void generateJLObjectStream_hasStaticInitializer(
ClassWriter cw, String classname) {
FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, jloObjectStream_hasInitializerMethod, "Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC, jloObjectStream_hasInitializerMethod, "(Ljava/lang/Class;)Z", null, null);
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
mv.visitLabel(l0);
mv.visitFieldInsn(GETSTATIC, classname, jloObjectStream_hasInitializerMethod, "Ljava/lang/reflect/Method;");
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ICONST_1);
mv.visitTypeInsn(ANEWARRAY,"java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(AASTORE);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
mv.visitLabel(l1);
mv.visitInsn(IRETURN);
mv.visitLabel(l2);
// If not a reloadable type, we'll end up here (the method we called threw IllegalStateException), just make that native method call
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD,0);
mv.visitMethodInsn(INVOKESTATIC, classname, "hasStaticInitializer","(Ljava/lang/Class;)Z");
mv.visitInsn(IRETURN);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
public static void generateJLRF_Get(ClassWriter cw, String classname) {
FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, jlrfGetMember, "Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC, jlrfGetMember, jlrfGetDescriptor,
null, new String[]{"java/lang/IllegalAccessException","java/lang/IllegalArgumentException"});
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitFieldInsn(GETSTATIC, classname, jlrfGetMember, "Ljava/lang/reflect/Method;");
mv.visitJumpInsn(IFNONNULL, l0);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Field", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
mv.visitLabel(l0);
mv.visitFieldInsn(GETSTATIC, classname, jlrfGetMember, "Ljava/lang/reflect/Method;");
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ICONST_2);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ALOAD, 0); // target field
mv.visitInsn(AASTORE);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ALOAD, 1); // instance on which to get the field
mv.visitInsn(AASTORE);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitLabel(l1);
mv.visitInsn(ARETURN);
mv.visitLabel(l2);
mv.visitVarInsn(ASTORE, 2);
Label l5 = new Label();
mv.visitLabel(l5);
Label l6 = new Label();
mv.visitLabel(l6);
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
Label l7 = new Label();
mv.visitLabel(l7);
mv.visitMaxs(8, 4);
mv.visitEnd();
}
public static void generateJLRM_Invoke(ClassWriter cw, String classname) {
FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, jlrmInvokeMember, "Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC, jlrmInvokeMember, jlrmInvokeDescriptor,
null, new String[]{"java/lang/IllegalAccessException","java/lang/reflect/InvocationTargetException"});
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitFieldInsn(GETSTATIC, classname, jlrmInvokeMember, "Ljava/lang/reflect/Method;");
mv.visitJumpInsn(IFNONNULL, l0);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
mv.visitLabel(l0);
mv.visitFieldInsn(GETSTATIC, classname, jlrmInvokeMember, "Ljava/lang/reflect/Method;");
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ICONST_3);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ALOAD, 0); // target method
mv.visitInsn(AASTORE);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ALOAD, 1); // instance on which to call the method
mv.visitInsn(AASTORE);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_2);
mv.visitVarInsn(ALOAD, 2); // arguments to method call
mv.visitInsn(AASTORE);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitLabel(l1);
mv.visitInsn(ARETURN);
mv.visitLabel(l2);
mv.visitVarInsn(ASTORE, 3);
Label l5 = new Label();
mv.visitLabel(l5);
Label l6 = new Label();
mv.visitLabel(l6);
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
Label l7 = new Label();
mv.visitLabel(l7);
mv.visitMaxs(8, 4);
mv.visitEnd();
}
public static void generateJLC_GetDeclaredConstructors(ClassWriter cw, String classname) {
FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, jlcGetDeclaredConstructorsMember, "Ljava/lang/reflect/Method;", null, null);
fv.visitEnd();
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC, jlcGetDeclaredConstructorsMember, jlcGetDeclaredConstructorsDescriptor,
null, null);
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitFieldInsn(GETSTATIC, classname, jlcGetDeclaredConstructorsMember, "Ljava/lang/reflect/Method;");
mv.visitJumpInsn(IFNONNULL, l0);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;");
mv.visitInsn(ARETURN);
mv.visitLabel(l0);
mv.visitFieldInsn(GETSTATIC, classname, jlcGetDeclaredConstructorsMember, "Ljava/lang/reflect/Method;");
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ICONST_1);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(AASTORE);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/reflect/Constructor;");
mv.visitLabel(l1);
mv.visitInsn(ARETURN);
mv.visitLabel(l2);
mv.visitVarInsn(ASTORE, 1);
Label l5 = new Label();
mv.visitLabel(l5);
Label l6 = new Label();
mv.visitLabel(l6);
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
Label l7 = new Label();
mv.visitLabel(l7);
// mv.visitLocalVariable("clazz", "Ljava/lang/Class;", "Ljava/lang/Class<*>;", l3, l7, 0);
// mv.visitLocalVariable("e", "Ljava/lang/Exception;", null, l5, l7, 1);
mv.visitMaxs(6, 2);
mv.visitEnd();
}
public static void generateJLCGMODS(ClassWriter cw, String classname) {
FieldVisitor fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, "__sljlcgmods", "Ljava/lang/reflect/Method;", null, null);

View File

@@ -18,11 +18,14 @@ package org.springsource.loaded.agent;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.LoadtimeInstrumentationPlugin;
@@ -50,11 +53,58 @@ public class JVMPlugin implements ReloadEventProcessorPlugin, LoadtimeInstrument
private Field threadGroupContext_contextsField; /* Map<ThreadGroup,ThreadGroupContext> */
private Method threadGroupContext_removeBeanInfoMethod; /* removeBeanInfo(Class<?> type) { */
private void tidySerialization(Class<?> reloadedClass) {
// if (true) return;
try {
Class<?> clazz = Class.forName("java.io.ObjectStreamClass$Caches");
Field localDescsField = clazz.getDeclaredField("localDescs");
localDescsField.setAccessible(true);
ConcurrentMap cm = (ConcurrentMap)localDescsField.get(null);
// TODO [serialization] a bit extreme to wipe out everything
cm.clear();
// Field reflectorsField = clazz.getDeclaredField("reflectors");
// reflectorsField.setAccessible(true);
// cm = (ConcurrentMap)reflectorsField.get(null);
// cm.clear();
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e);
} catch (SecurityException e) {
throw new IllegalStateException(e);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
// private static class Caches {
// /** cache mapping local classes -> descriptors */
// static final ConcurrentMap<WeakClassKey,Reference<?>> localDescs =
// new ConcurrentHashMap<>();
//
// /** cache mapping field group/local desc pairs -> field reflectors */
// static final ConcurrentMap<FieldReflectorKey,Reference<?>> reflectors =
// new ConcurrentHashMap<>();
//
// /** queue for WeakReferences to local classes */
// private static final ReferenceQueue<Class<?>> localDescsQueue =
// new ReferenceQueue<>();
// /** queue for WeakReferences to field reflectors keys */
// private static final ReferenceQueue<Class<?>> reflectorsQueue =
// new ReferenceQueue<>();
// }
}
@SuppressWarnings({ "restriction", "unchecked" })
public void reloadEvent(String typename, Class<?> clazz, String encodedTimestamp) {
if (pluginBroken) {
return;
}
tidySerialization(clazz);
if (introspectorLoaded) {
// Clear out the Introspector BeanInfo cache entry that might exist for this class

View File

@@ -88,6 +88,12 @@ public class SpringLoadedPreProcessor implements Constants {
systemClassesContainingReflection.add("java/lang/reflect/Proxy");
// So that javabeans introspection is intercepter
systemClassesContainingReflection.add("java/beans/Introspector");
// Related to serialization
// TODO [serialization] Caches in ObjectStreamClass for descriptors, need clearing on reload
systemClassesContainingReflection.add("java/io/ObjectStreamClass");
systemClassesContainingReflection.add("java/io/ObjectStreamClass$EntryFuture");
// Don't need this right now, instead we are not removing 'final' from the serialVersionUID
// // Need to catch at least the call to access the serialVersionUID made in getDeclaredSUID()
// systemClassesContainingReflection.add("java/io/ObjectStreamClass$2");
@@ -133,8 +139,8 @@ public class SpringLoadedPreProcessor implements Constants {
try {
// TODO [perf] why are we not using the cache here, is it because the list is so short?
RewriteResult rr = SystemClassReflectionRewriter.rewrite(slashedClassName, bytes);
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.FINER)) {
log.finer("System class rewritten: name="+slashedClassName+" rewrite summary="+rr.summarize());
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("System class rewritten: name="+slashedClassName+" rewrite summary="+rr.summarize());
}
systemClassesRequiringInitialization.put(slashedClassName, rr.bits);
return rr.bytes;
@@ -421,13 +427,39 @@ public class SpringLoadedPreProcessor implements Constants {
f.setAccessible(true);
f.set(null, method_jlcgc);
}
if ((bits & Constants.JLC_GETDECLAREDCONSTRUCTORS) != 0) {
Field f = clazz.getDeclaredField(jlcGetDeclaredConstructorsMember);
f.setAccessible(true);
f.set(null,method_jlcgdcs);
}
if ((bits & Constants.JLRF_GET) != 0) {
Field f = clazz.getDeclaredField(jlrfGetMember);
f.setAccessible(true);
f.set(null,method_jlrfg);
}
if ((bits & Constants.JLRF_GETLONG) != 0) {
Field f = clazz.getDeclaredField(jlrfGetLongMember);
f.setAccessible(true);
f.set(null,method_jlrfgl);
}
if ((bits & Constants.JLRM_INVOKE) != 0) {
Field f = clazz.getDeclaredField(jlrmInvokeMember);
f.setAccessible(true);
f.set(null,method_jlrmi);
}
if ((bits & Constants.JLOS_HASSTATICINITIALIZER) != 0) {
Field f = clazz.getDeclaredField(jloObjectStream_hasInitializerMethod);
f.setAccessible(true);
f.set(null,method_jloObjectStream_hasInitializerMethod);
}
}
private static final Class<?> EMPTY_CLASS_ARRAY_CLAZZ = Class[].class;
// TODO threads
private static boolean prepared = false;
private static Method method_jlcgdfs, method_jlcgdf, method_jlcgf, method_jlcgdms, method_jlcgdm, method_jlcgm, method_jlcgdc,
method_jlcgc, method_jlcgmods, method_jlcgms;
method_jlcgc, method_jlcgmods, method_jlcgms, method_jlcgdcs, method_jlrfg, method_jlrfgl, method_jlrmi,
method_jloObjectStream_hasInitializerMethod;
/**
* Cache the Method objects that will be injected.
@@ -447,6 +479,12 @@ public class SpringLoadedPreProcessor implements Constants {
method_jlcgc = clazz.getDeclaredMethod("jlClassGetConstructor", Class.class, EMPTY_CLASS_ARRAY_CLAZZ);
method_jlcgmods = clazz.getDeclaredMethod("jlClassGetModifiers", Class.class);
method_jlcgms = clazz.getDeclaredMethod("jlClassGetMethods", Class.class);
method_jlcgdcs = clazz.getDeclaredMethod("jlClassGetDeclaredConstructors",Class.class);
method_jlrfg = clazz.getDeclaredMethod("jlrFieldGet",Field.class,Object.class);
method_jlrfgl = clazz.getDeclaredMethod("jlrFieldGetLong",Field.class,Object.class);
method_jlrmi = clazz.getDeclaredMethod("jlrMethodInvoke",Method.class,Object.class,Object[].class);
method_jloObjectStream_hasInitializerMethod = clazz.getDeclaredMethod("jlosHasStaticInitializer",Class.class);
} catch (NoSuchMethodException nsme) {
// cant happen, a-hahaha
throw new Impossible(nsme);

View File

@@ -84,6 +84,16 @@ public class ReflectiveInterceptor {
classToRType = new WeakHashMap<Class<?>, WeakReference<ReloadableType>>();
}
}
@UsedByGeneratedCode
public static boolean jlosHasStaticInitializer(Class<?> clazz) {
ReloadableType rtype = getRType(clazz);
if (rtype == null) {
// Exception tells the caller to use the 'old way' to determine if there is a static initializer
throw new IllegalStateException();
}
return rtype.hasStaticInitializer();
}
/*
* Implementation of java.lang.class.getDeclaredMethod(String name, Class... params).