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

Binary file not shown.

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).

View File

@@ -18,8 +18,8 @@ package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Modifier;
@@ -31,7 +31,6 @@ import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.Utils.ReturnType;
import org.springsource.loaded.test.infra.Result;
/**
* Tests for the TypeRegistry that exercise it in the same way it will actively be used when managing ReloadableType instances.
*
@@ -268,6 +267,103 @@ public class ReloadableTypeTests extends SpringLoadedTests {
assertEquals("Hello",(String)r.returnValue);
}
// Basic write/read then reload then write/read again
@Test
public void serialization1() throws Exception {
TypeRegistry tr = getTypeRegistry("remote..*");
ReloadableType person = tr.addType("remote.Person", loadBytesForClass("remote.Person"));
// When the Serialize class is run directly, we see: byteinfo:len=98:crc=c1047cf6
// When run via this test, we see: byteinfo:len=98:crc=7e07276a
// Tried running the Serialize code directly but with a clinit in the Person class: 2b4c0df4
ReloadableType runner = tr.addType("remote.Serialize", loadBytesForClass("remote.Serialize"));
Result r = null;
r = runUnguarded(runner.getClazz(), "run");
assertContains("check ok", r.stdout);
person.loadNewVersion("2",retrieveRename("remote.Person", "remote.Person2"));
r = runUnguarded(runner.getClazz(), "run");
assertContains("check ok", r.stdout);
}
// Unlike the first test, this one will reload the class in between serialize and deserialize
@Test
public void serialization2() throws Exception {
TypeRegistry tr = getTypeRegistry("remote..*");
ReloadableType person = tr.addType("remote.Person", loadBytesForClass("remote.Person"));
// byteinfo:len=98:crc=7e07276a
ReloadableType runner = tr.addType("remote.Serialize", loadBytesForClass("remote.Serialize"));
Class<?> clazz = runner.getClazz();
Object instance = clazz.newInstance();
Result r = null;
// Basic: write and read the same Person
r = runOnInstance(clazz,instance,"writePerson");
assertStdoutContains("Person stored ok", r);
r = runOnInstance(clazz,instance,"readPerson");
assertContains("Person read ok", r.stdout);
// Advanced: write it, reload, then read back from the written form
r = runOnInstance(clazz,instance,"writePerson");
assertStdoutContains("Person stored ok", r);
person.loadNewVersion("2",retrieveRename("remote.Person", "remote.Person2"));
r = runOnInstance(clazz,instance,"readPerson");
assertContains("Person read ok", r.stdout);
}
// Variant of the second test but using serialVersionUID and adding methods to the class on reload
@Test
public void serialization3() throws Exception {
TypeRegistry tr = getTypeRegistry("remote..*");
ReloadableType person = tr.addType("remote.PersonB", loadBytesForClass("remote.PersonB"));
ReloadableType runner = tr.addType("remote.SerializeB", loadBytesForClass("remote.SerializeB"));
Class<?> clazz = runner.getClazz();
Object instance = clazz.newInstance();
Result r = null;
// Basic: write and read the same Person
r = runOnInstance(runner.getClazz(),instance,"writePerson");
assertStdoutContains("Person stored ok", r);
r = runOnInstance(runner.getClazz(),instance,"readPerson");
assertContains("Person read ok", r.stdout);
// Advanced: write it, reload, then read back from the written form
r = runOnInstance(runner.getClazz(),instance,"writePerson");
assertStdoutContains("Person stored ok", r);
person.loadNewVersion("2",retrieveRename("remote.PersonB", "remote.PersonB2"));
r = runOnInstance(runner.getClazz(),instance,"readPerson");
assertContains("Person read ok", r.stdout);
r = runOnInstance(clazz,instance,"printInitials");
assertContains("Person read ok\nWS", r.stdout);
}
// Deserialize something we serialized earlier
// This test cannot work without the agent. The agent must intercept java.lang.ObjectStream and its use of reflection
// There is a test that will work in the SpringLoadedTestsInSeparateJVM
public void serialization4() throws Exception {
TypeRegistry tr = getTypeRegistry("remote..*");
// ReloadableType person =
tr.addType("remote.Person", loadBytesForClass("remote.Person"));
// When the Serialize class is run directly, we see: byteinfo:len=98:crc=c1047cf6
// When run via this test, we see: byteinfo:len=98:crc=7e07276a
ReloadableType runner = tr.addType("remote.Serialize", loadBytesForClass("remote.Serialize"));
Class<?> clazz = runner.getClazz();
Object instance = clazz.newInstance();
Result r = runOnInstance(clazz,instance,"checkPredeserializedData");
assertStdoutContains("Person stored ok", r);
}
// extra class in the middle: A in jar, subtype AB reloadable, subtype BBBBB reloadable
@Test
public void invokeStaticReloading_gh4_6() throws Exception {

View File

@@ -78,6 +78,7 @@ public class ReloadingJVM {
System.out.println("Found agent at "+agentJarLocation);
System.out.println("(client) Test data directory is "+testdataDirectory);
}
javaclasspath = javaclasspath + File.pathSeparator + new File("../testdata-groovy/groovy-all-1.8.6.jar").toString();
javaclasspath = javaclasspath + File.pathSeparator + testdataDirectory.toString();
if (DEBUG_CLIENT_SIDE) {
System.out.println("(client) Classpath for JVM that is being launched: " + javaclasspath);
@@ -87,15 +88,18 @@ public class ReloadingJVM {
if (agentOptions!=null && agentOptions.length()>0) {
AGENT_OPTION_STRING = "-Dspringloaded="+agentOptions;
}
if (DEBUG_CLIENT_SIDE) {
System.out.println("java.home="+System.getProperty("java.home"));
}
process = Runtime.getRuntime().exec(
"java -noverify -javaagent:" + agentJarLocation + " -cp " + javaclasspath + " " + AGENT_OPTION_STRING +
System.getProperty("java.home")+"/bin/java -noverify -javaagent:" + agentJarLocation + " -cp " + javaclasspath + " " + AGENT_OPTION_STRING +
" "+OPTS+" "
+ ReloadingJVMCommandProcess.class.getName(), new String[] { OPTS });
writer = new DataOutputStream(process.getOutputStream());
reader = new DataInputStream(process.getInputStream());
readerErrors = new DataInputStream(process.getErrorStream());
if (debug) {
System.out.println("Debugging launched VM, port 5000");
System.out.println("Debugging launched VM, port 5100");
}
JVMOutput text = waitFor("ReloadingJVM:started");
if (DEBUG_CLIENT_SIDE) {
@@ -119,7 +123,7 @@ public class ReloadingJVM {
return captureOutput(message);
}
private final static boolean DEBUG_CLIENT_SIDE = false;
private final static boolean DEBUG_CLIENT_SIDE = true;
private JVMOutput sendAndReceive(String message) {
try {
@@ -227,6 +231,9 @@ public class ReloadingJVM {
}
String classfile = classname.replaceAll("\\.",File.separator)+".class";
File f = new File("../testdata/bin",classfile);
if (!f.exists()) {
f = new File("../testdata-groovy/bin",classfile);
}
byte[] data = Utils.load(f);
// Ensure directories exist
int dotPos = classname.lastIndexOf(".");
@@ -235,6 +242,22 @@ public class ReloadingJVM {
}
Utils.write(new File(testdataDirectory,classfile),data);
}
public void copyResourceToTestDataDirectory(String resourcename) {
if (DEBUG_CLIENT_SIDE) {
System.out.println("(client) copying resource to test data directory: "+resourcename);
}
File f = new File("../testdata-groovy/",resourcename);
byte[] data = Utils.load(f);
// // Ensure directories exist
// int dotPos = classname.lastIndexOf(".");
// if (dotPos!=-1) {
// new File(testdataDirectory,classname.substring(0,dotPos).replaceAll("\\.",File.separator)).mkdirs();
// }
Utils.write(new File(testdataDirectory,resourcename),data);
}
public void clearTestdataDirectory() {
File[] fs = testdataDirectory.listFiles();

View File

@@ -65,6 +65,7 @@ import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.Utils;
import org.springsource.loaded.agent.SpringLoadedPreProcessor;
import org.springsource.loaded.test.ReloadingJVM.JVMOutput;
import org.springsource.loaded.test.infra.ClassPrinter;
import org.springsource.loaded.test.infra.MethodPrinter;
import org.springsource.loaded.test.infra.Result;
@@ -1278,5 +1279,29 @@ public abstract class SpringLoadedTests implements Constants {
} catch (Exception e) {}
}
protected void assertStdout(String expectedStdout, JVMOutput actualOutput) {
if (!expectedStdout.equals(actualOutput.stdout)) {
// assertEquals(expectedStdout, actualOutput.stdout);
fail("Expected stdout '" + expectedStdout + "' not found in \n" + actualOutput.toString());
}
}
protected void assertStdoutContains(String expectedStdout, JVMOutput actualOutput) {
if (!actualOutput.stdout.contains(expectedStdout)) {
fail("Expected stdout:\n" + expectedStdout + "\nbut was:\n" + actualOutput.stdout.toString()+"\nComplete output: \n"+actualOutput.toString());
}
}
protected void assertStdoutContains(String expectedStdout, Result r) {
// TODO Auto-generated method stub
}
protected void assertStderrContains(String expectedStderrContains, JVMOutput actualOutput) {
if (actualOutput.stderr.indexOf(expectedStderrContains) == -1) {
fail("Expected stderr to contain '" + expectedStderrContains + "'\n" + actualOutput.toString());
}
}
}

View File

@@ -15,8 +15,6 @@
*/
package org.springsource.loaded.test;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -73,7 +71,66 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
JVMOutput output = jvm.run("issue34.Implementation3");
assertStdout("Hello World!\n", output);
}
@Test
public void serialization() throws Exception {
jvm.copyToTestdataDirectory("remote.Serialize");
jvm.copyToTestdataDirectory("remote.Person");
JVMOutput output = null;
// When the Serialize class is run directly, we see: byteinfo:len=98:crc=c1047cf6
// When run via the non separate JVM test, we see: byteinfo:len=98:crc=7e07276a
// When run here, we see: byteinfo:len=98:crc=c1047cf6
output = jvm.run("remote.Serialize");
assertStdoutContains("check ok\n", output);
// Load new Person
jvm.updateClass("remote.Person", retrieveRename("remote.Person","remote.Person2"));
pause(2);
output = jvm.run("remote.Serialize");
assertStdoutContains("check ok\n", output);
// Load original Person
jvm.updateClass("remote.Person", loadBytesForClass("remote.Person"));
pause(2);
output = jvm.run("remote.Serialize");
assertStdoutContains("check ok\n", output);
}
// Deserializing something serialized earlier
@Test
public void serialization2() throws Exception {
jvm.copyToTestdataDirectory("remote.Serialize");
jvm.copyToTestdataDirectory("remote.Person");
JVMOutput output = null;
output = jvm.run("remote.Serialize");
assertStdoutContains("check ok\n", output);
jvm.newInstance("a", "remote.Serialize");
JVMOutput jo = jvm.call("a","checkPredeserializedData");
assertStdoutContains("Pre-serialized form checked ok\n", jo);
}
// Deserialize a groovy closure
@Test
public void serializationGroovy() throws Exception {
// debug();
jvm.copyToTestdataDirectory("remote.SerializeG");
jvm.copyToTestdataDirectory("remote.FakeClosure");
// Notes on serialization
// When SerializeG run standalone, reports byteinfo:len=283:crc=245529d9
// When run in agented JVM, reports byteinfo:len=283:crc=245529d9
jvm.newInstance("a", "remote.SerializeG");
JVMOutput jo = jvm.call("a","checkPredeserializedData");
assertStdoutContains("Pre-serialized groovy form checked ok\n", jo);
}
@Test
public void githubIssue34_2() throws Exception {
jvm.copyToTestdataDirectory("issue34.InnerEnum$sorters");
@@ -175,23 +232,6 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
try { Thread.sleep(2000); } catch (Exception e) {}
}
private void assertStdout(String expectedStdout, JVMOutput actualOutput) {
if (!expectedStdout.equals(actualOutput.stdout)) {
// assertEquals(expectedStdout, actualOutput.stdout);
fail("Expected stdout '" + expectedStdout + "' not found in \n" + actualOutput.toString());
}
}
private void assertStdoutContains(String expectedStdout, JVMOutput actualOutput) {
if (!actualOutput.stdout.contains(expectedStdout)) {
fail("Expected stdout:\n" + expectedStdout + "\nbut was:\n" + actualOutput.stdout.toString()+"\nComplete output: \n"+actualOutput.toString());
}
}
private void assertStderrContains(String expectedStderrContains, JVMOutput actualOutput) {
if (actualOutput.stderr.indexOf(expectedStderrContains) == -1) {
fail("Expected stderr to contain '" + expectedStderrContains + "'\n" + actualOutput.toString());
}
}
}

View File

@@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -83,7 +84,7 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertTrue((rr.bits & ~JLC_GETDECLAREDFIELDS) == 0);
assertEquals(1, callcount);
assertEquals("getDeclaredFields()", rr.summarize());
assertEquals("Class.getDeclaredFields()", rr.summarize());
}
@Test
@@ -129,7 +130,7 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertEquals(2, events.size());
assertEquals("helper2(system.Two,s)", events.get(0));
assertEquals("helper2(system.Two,foo)", events.get(1));
assertEquals("getDeclaredField()", rr.summarize());
assertEquals("Class.getDeclaredField()", rr.summarize());
}
@Test
@@ -175,7 +176,7 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertEquals(2, events.size());
assertEquals("helper2(system.Three,s)", events.get(0));
assertEquals("helper2(system.Three,foo)", events.get(1));
assertEquals("getField()", rr.summarize());
assertEquals("Class.getField()", rr.summarize());
}
@Test
@@ -217,7 +218,98 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertTrue((rr.bits & ~JLC_GETDECLAREDMETHODS) == 0);
assertEquals(1, callcount);
assertEquals("getDeclaredMethods()", rr.summarize());
assertEquals("Class.getDeclaredMethods()", rr.summarize());
}
@Test
public void jlClass_getDeclaredConstructors() throws Exception {
byte[] classbytes = loadBytesForClass("system.Ten");
RewriteResult rr = SystemClassReflectionRewriter.rewrite("system.Ten", classbytes);
byte[] newbytes = rr.bytes;
Class<?> clazz = loadit("system.Ten", newbytes);
// Check the new field and method are in the type:
//@formatter:off
assertEquals(
"CLASS: system/Ten v50 0x0021(public synchronized) super java/lang/Object\n"+
"SOURCE: Ten.java null\n"+
"FIELD 0x0009(public static) __sljlcgdcs Ljava/lang/reflect/Method;\n"+
"METHOD: 0x0001(public) <init>()V\n"+
"METHOD: 0x0001(public) runIt()Ljava/lang/String;\n"+
"METHOD: 0x0001(public) cs()[Ljava/lang/reflect/Constructor;\n"+
"METHOD: 0x000a(private static) __sljlcgdcs(Ljava/lang/Class;)[Ljava/lang/reflect/Constructor;\n"+
"\n",
toStringClass(newbytes));
//@formatter:on
Object value = run(clazz, "runIt");
// Check that without the field initialized, things behave as expected
assertEquals("complete:constructors:null?false constructors:size=1", value);
assertEquals(0, callcount);
// Set the field
Method m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helper4", Class.class);
assertNotNull(m);
clazz.getDeclaredField(jlcGetDeclaredConstructorsMember).set(null, m);
// Now re-run, should be intercepted to call our helper
value = run(clazz, "runIt");
assertEquals("complete:constructors:null?true", value);
// Check the correct amount of rewriting went on
assertTrue((rr.bits & JLC_GETDECLAREDCONSTRUCTORS) != 0);
assertTrue((rr.bits & ~JLC_GETDECLAREDCONSTRUCTORS) == 0);
assertEquals(1, callcount);
assertEquals("Class.getDeclaredConstructors()", rr.summarize());
}
@Test
public void jlrMethod_Invoke() throws Exception {
byte[] classbytes = loadBytesForClass("system.Eleven");
RewriteResult rr = SystemClassReflectionRewriter.rewrite("system.Eleven", classbytes);
byte[] newbytes = rr.bytes;
Class<?> clazz = loadit("system.Eleven", newbytes);
// Check the new field and method are in the type:
//@formatter:off
assertEquals(
"CLASS: system/Eleven v50 0x0021(public synchronized) super java/lang/Object\n"+
"SOURCE: Eleven.java null\n"+
"FIELD 0x0009(public static) __sljlcgdm Ljava/lang/reflect/Method;\n"+
"FIELD 0x0009(public static) __sljlrmi Ljava/lang/reflect/Method;\n"+
"METHOD: 0x0001(public) <init>()V\n"+
"METHOD: 0x0001(public) runIt()Ljava/lang/String; java/lang/Exception\n"+
"METHOD: 0x0089(public static) invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; java/lang/Exception\n"+
"METHOD: 0x0001(public) foo(ILjava/lang/String;)Ljava/lang/String;\n"+
"METHOD: 0x008a(private static) __sljlcgdm(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; java/lang/NoSuchMethodException\n"+
"METHOD: 0x000a(private static) __sljlrmi(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; java/lang/IllegalAccessException java/lang/reflect/InvocationTargetException\n"+
"\n",
toStringClass(newbytes));
//@formatter:on
Object value = run(clazz, "runIt");
// Check that without the field initialized, things behave as expected
assertEquals("complete:obj=i=12:s=abc", value);
assertEquals(0, callcount);
// Set the field
Method m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperJLRMI_0", Class.class, String.class, Class[].class);
assertNotNull(m);
clazz.getDeclaredField(jlcgdm).set(null, m);
m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperJLRMI", Method.class, Object.class, Object[].class);
assertNotNull(m);
clazz.getDeclaredField(jlrmInvokeMember).set(null, m);
// Now re-run, should be intercepted to call our helper
value = run(clazz, "runIt");
assertEquals("complete:obj=null", value);
// Check the correct amount of rewriting went on
assertTrue((rr.bits & (JLC_GETDECLAREDMETHOD|JLRM_INVOKE)) != 0);
assertTrue((rr.bits & ~(JLC_GETDECLAREDMETHOD|JLRM_INVOKE)) == 0);
assertEquals(1, callcount);
assertEquals("Class.getDeclaredMethod() Method.invoke()", rr.summarize());
}
@Test
@@ -263,7 +355,7 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertEquals(2, events.size());
assertEquals("helper4(system.Five,runIt)", events.get(0));
assertEquals("helper4(system.Five,foobar)", events.get(1));
assertEquals("getDeclaredMethod()", rr.summarize());
assertEquals("Class.getDeclaredMethod()", rr.summarize());
}
@Test
@@ -309,7 +401,7 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertEquals(2, events.size());
assertEquals("helper4(system.Six,runIt)", events.get(0));
assertEquals("helper4(system.Six,foo)", events.get(1));
assertEquals("getMethod()", rr.summarize());
assertEquals("Class.getMethod()", rr.summarize());
}
@Test
@@ -356,9 +448,108 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertEquals("helper5(system.Seven)", events.get(0));
assertEquals("helper5(system.Seven)", events.get(1));
assertEquals("helper5(system.Seven)", events.get(2));
assertEquals("getDeclaredConstructor()", rr.summarize());
assertEquals("Class.getDeclaredConstructor()", rr.summarize());
}
@Test
public void jlrField_getLong() throws Exception {
byte[] classbytes = loadBytesForClass("system.Thirteen");
RewriteResult rr = SystemClassReflectionRewriter.rewrite("system.Thirteen", classbytes);
byte[] newbytes = rr.bytes;
Class<?> clazz = loadit("system.Thirteen", newbytes);
// Check the new field and method are in the type:
//@formatter:off
assertEquals(
"CLASS: system/Thirteen v50 0x0021(public synchronized) super java/lang/Object\n"+
"SOURCE: Thirteen.java null\n"+
"FIELD 0x0001(public) foo J\n"+
"FIELD 0x0009(public static) __sljlcgf Ljava/lang/reflect/Method;\n"+
"FIELD 0x0009(public static) __sljlrfgl Ljava/lang/reflect/Method;\n"+
"METHOD: 0x0001(public) <init>()V\n"+
"METHOD: 0x0001(public) runIt()Ljava/lang/String; java/lang/Exception\n"+
"METHOD: 0x0001(public) gf()Ljava/lang/Long; java/lang/Exception\n"+
"METHOD: 0x000a(private static) __sljlcgf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field; java/lang/NoSuchFieldException\n"+
"METHOD: 0x000a(private static) __sljlrfgl(Ljava/lang/reflect/Field;Ljava/lang/Object;)J java/lang/IllegalAccessException java/lang/IllegalArgumentException\n"+
"\n",
toStringClass(newbytes));
//@formatter:on
Object value = run(clazz, "runIt");
// Check that without the field initialized, things behave as expected
assertEquals("complete:value=42", value);
assertEquals(0, callcount);
// Set the field
Method m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperJLRFGL_0", Class.class, String.class);
assertNotNull(m);
clazz.getDeclaredField(jlcgf).set(null, m);
m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperJLRFGL", Field.class, Object.class);
assertNotNull(m);
clazz.getDeclaredField(jlrfGetLongMember).set(null, m);
// Now re-run, should be intercepted to call our helper
value = run(clazz, "runIt");
assertEquals("complete:value=0", value);
// Check the correct amount of rewriting went on
assertTrue((rr.bits & (JLRF_GETLONG | JLC_GETFIELD)) != 0);
assertTrue((rr.bits & ~(JLRF_GETLONG | JLC_GETFIELD)) == 0);
assertEquals(1, callcount);
assertEquals("Class.getField() Field.getLong()", rr.summarize());
}
@Test
public void jlrField_get() throws Exception {
byte[] classbytes = loadBytesForClass("system.Twelve");
RewriteResult rr = SystemClassReflectionRewriter.rewrite("system.Twelve", classbytes);
byte[] newbytes = rr.bytes;
Class<?> clazz = loadit("system.Twelve", newbytes);
// Check the new field and method are in the type:
//@formatter:off
assertEquals(
"CLASS: system/Twelve v50 0x0021(public synchronized) super java/lang/Object\n"+
"SOURCE: Twelve.java null\n"+
"FIELD 0x0001(public) foo Ljava/lang/String;\n"+
"FIELD 0x0009(public static) __sljlcgf Ljava/lang/reflect/Method;\n"+
"FIELD 0x0009(public static) __sljlrfg Ljava/lang/reflect/Method;\n"+
"METHOD: 0x0001(public) <init>()V\n"+
"METHOD: 0x0001(public) runIt()Ljava/lang/String; java/lang/Exception\n"+
"METHOD: 0x0001(public) gf()Ljava/lang/Object; java/lang/Exception\n"+
"METHOD: 0x000a(private static) __sljlcgf(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field; java/lang/NoSuchFieldException\n"+
"METHOD: 0x000a(private static) __sljlrfg(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object; java/lang/IllegalAccessException java/lang/IllegalArgumentException\n"+
"\n",
toStringClass(newbytes));
//@formatter:on
Object value = run(clazz, "runIt");
// Check that without the field initialized, things behave as expected
assertEquals("complete:value=abc", value);
assertEquals(0, callcount);
// Set the field
Method m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperJLRFG_0", Class.class, String.class);
assertNotNull(m);
clazz.getDeclaredField(jlcgf).set(null, m);
m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperJLRFG", Field.class, Object.class);
assertNotNull(m);
clazz.getDeclaredField(jlrfGetMember).set(null, m);
// Now re-run, should be intercepted to call our helper
value = run(clazz, "runIt");
assertEquals("complete:value=null", value);
// Check the correct amount of rewriting went on
assertTrue((rr.bits & (JLRF_GET | JLC_GETFIELD)) != 0);
assertTrue((rr.bits & ~(JLRF_GET | JLC_GETFIELD)) == 0);
assertEquals(1, callcount);
assertEquals("Class.getField() Field.get()", rr.summarize());
}
@Test
public void jlClass_getModifiers() throws Exception {
byte[] classbytes = loadBytesForClass("system.Eight");
@@ -405,7 +596,7 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertEquals("helper6(system.Eight)", events.get(0));
assertEquals("helper6(system.DefaultVis)", events.get(1));
assertEquals("helper6(system.Eight$Inner)", events.get(2));
assertEquals("getModifiers()", rr.summarize());
assertEquals("Class.getModifiers()", rr.summarize());
}
@Test
@@ -438,6 +629,11 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertNotNull(m);
clazz.getDeclaredField(jlcgms).set(null, m);
m = SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperGMs", Class.class);
assertNotNull(m);
clazz.getDeclaredField(jlcgms).set(null, m);
// Now re-run, should be intercepted to call our helper
value = run(clazz, "runIt");
assertEquals("complete:methods:null?true", value);
@@ -447,7 +643,7 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
assertTrue((rr.bits & ~JLC_GETMETHODS) == 0);
assertEquals(1, callcount);
assertEquals("getMethods()", rr.summarize());
assertEquals("Class.getMethods()", rr.summarize());
}
// ---
@@ -466,6 +662,13 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
callcount++;
return null;
}
// helper method - standin for Class.getDeclaredConstructors()
@SuppressWarnings("rawtypes")
public static Constructor[] helper4(Class<?> clazz) {
callcount++;
return null;
}
// helper method - standin for Class.getMethods()
public static Method[] helperGMs(Class<?> clazz) {
@@ -473,6 +676,39 @@ public class SystemClassReflectionRewriterTests extends SpringLoadedTests {
return null;
}
// The testcase for JLRMI uses GDM to find the Method object, so that is why this helper exists
public static Method helperJLRMI_0(Class<?> clazz, String name, Class<?>[] argTypes) throws Exception {
return SystemClassReflectionRewriterTests.class.getDeclaredMethod("helperJLRMI",Method.class,Object.class,Object[].class);
}
public static Object helperJLRMI(Method m, Object instance, Object[] arguments) {
callcount++;
return null;
}
public String string = "wibble";
public static Field helperJLRFG_0(Class<?> clazz, String name) throws Exception {
return SystemClassReflectionRewriterTests.class.getDeclaredField("string");
}
public static Object helperJLRFG(Field f, Object instance) {
callcount++;
return null;
}
public long lll = 99L;
public static Field helperJLRFGL_0(Class<?> clazz, String name) throws Exception {
return SystemClassReflectionRewriterTests.class.getDeclaredField("lll");
}
public static Long helperJLRFGL(Field f, Object instance) {
callcount++;
return 0L;
}
// TODO what about SecurityException on these get methods?
// helper method - standin for Class.getDeclaredField(String s) and Class.getField(String s)
public static Field[] helper2(Class<?> clazz, String s) throws NoSuchFieldException {

View File

@@ -262,7 +262,7 @@ public class TypeRewriterTests extends SpringLoadedTests {
ReloadableType itype = r.addType(t, loadBytesForClass(t));
ReloadableType subitype = r.addType(t2, loadBytesForClass(t2));
ClassPrinter.print(subitype.bytesLoaded);
// ClassPrinter.print(subitype.bytesLoaded);
// An interface will get the reloadable type field
assertEquals("0x19(public static final) r$type Lorg/springsource/loaded/ReloadableType;",
@@ -399,7 +399,7 @@ public class TypeRewriterTests extends SpringLoadedTests {
// Load a real new version
rtype.loadNewVersion("002", retrieveRename(t, t + "2"));
ClassPrinter.print(rtype.getLatestExecutorBytes());
// ClassPrinter.print(rtype.getLatestExecutorBytes());
result = runConstructor(rtype.getClazz(), "java.lang.String", "Wabble");
assertEquals("WabbleWabble", result.stdout);
assertEquals(rtype.getClazz().getName(), result.returnValue.getClass().getName());
@@ -1321,7 +1321,7 @@ public class TypeRewriterTests extends SpringLoadedTests {
// a new version of the concrete type without method() in it
rAbsimpl.loadNewVersion("2", retrieveRename(absimpl, absimpl + "2"));
rImpl.loadNewVersion("2", retrieveRename(impl, impl + "2", absimpl + "2:" + absimpl));
ClassPrinter.print(rAbsimpl.bytesLoaded);
// ClassPrinter.print(rAbsimpl.bytesLoaded);
result = runUnguarded(rImpl.getClazz(), "run");
assertEquals("2", result.stdout);
}
@@ -1347,7 +1347,7 @@ public class TypeRewriterTests extends SpringLoadedTests {
result = runUnguarded(type.getClazz(), "run");
assertEquals("hello", result.returnValue);
type.loadNewVersion("2", retrieveRename(t, t + "2"));
ClassPrinter.print(type.getLatestExecutorBytes());
// ClassPrinter.print(type.getLatestExecutorBytes());
result = runUnguarded(type.getClazz(), "run");
assertEquals("world", result.returnValue);
}

View File

@@ -0,0 +1,11 @@
package remote
class Code {
// Create a serialized closure
static void main(String[] args) {
def a = { 1 + 1 }
def out = new ObjectOutputStream(new FileOutputStream('ser.obj'))
out.writeObject(a)
out.close()
}
}

View File

@@ -0,0 +1,16 @@
package remote;
import org.codehaus.groovy.runtime.GeneratedClosure;
import groovy.lang.Closure;
@SuppressWarnings({ "rawtypes", "serial" })
public class FakeClosure extends Closure implements GeneratedClosure {
public String field;
public FakeClosure(Object owner) {
super(owner);
}
}

View File

@@ -0,0 +1,149 @@
package remote;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.zip.CRC32;
public class SerializeG {
// This is a serialized FakeClosure( field=abc)
private static final String serString =
"aced00057372001272656d6f74652e46616b65436c6f73757265cebbffd721e98fea0200014c00056669656c647400124c6a6176612f6c616e672f537472696e673b7872001367726f6f76792e6c616e672e436c6f737572653ca0c76616126c5a0200074900096469726563746976654900196d6178696d756d4e756d6265724f66506172616d657465727349000f7265736f6c766553747261746567794c000864656c65676174657400124c6a6176612f6c616e672f4f626a6563743b4c00056f776e657271007e00035b000e706172616d6574657254797065737400125b4c6a6176612f6c616e672f436c6173733b4c000a746869734f626a65637471007e0003787000000000000000000000000070707070740003616263";
private static final byte[] serBytes;
static {
serBytes = new byte[serString.length()/2];
int p = 0;
while (p < serString.length()) {
String oneByte = serString.substring(p,p+2);
int b = Integer.decode("0x"+oneByte);
serBytes[p/2]=(byte)b;
p+=2;
}
System.out.println();
}
public static void main(String[] args) {
if (args!=null && args.length!=0 && args[0].equals("ds")) {
checkPredeserializedData();
}
else {
run();
checkPredeserializedData();
}
}
public static void checkPredeserializedData() {
FakeClosure p = (FakeClosure)read(serBytes);
if (p==null) {
throw new IllegalStateException("Unable to deserialize FakeClosure!");
}
if (!p.field.equals("abc")) {
throw new IllegalStateException("Unable to deserialized pre-serialized data: "+p.field);
}
System.out.println("Pre-serialized groovy form checked ok");
}
public static byte[] loadBytesFromStream(InputStream stream) {
try {
BufferedInputStream bis = new BufferedInputStream(stream);
byte[] theData = new byte[10000000];
int dataReadSoFar = 0;
byte[] buffer = new byte[1024];
int read = 0;
while ((read = bis.read(buffer)) != -1) {
System.arraycopy(buffer, 0, theData, dataReadSoFar, read);
dataReadSoFar += read;
}
bis.close();
// Resize to actual data read
byte[] returnData = new byte[dataReadSoFar];
System.arraycopy(theData, 0, returnData, 0, dataReadSoFar);
return returnData;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void run() {
new SerializeG().run2();
}
public void run2() {
FakeClosure p = new FakeClosure(null);
p.field = "abc";
byte[] bs = write(p);
dumpinfo(bs);
FakeClosure p2 = (FakeClosure)read(bs);
check(p,p2);
}
public static void run3() {
String p = new String("abc");
byte[] bs = write(p);
dumpinfo(bs);
String p2 = (String)read(bs);
if (!p2.equals("abc")) {
throw new IllegalStateException();
}
}
private static void dumpinfo(byte[] bytes) {
CRC32 crc = new CRC32();
crc.update(bytes,0,bytes.length);
StringBuilder data = new StringBuilder();
for (int i=0;i<bytes.length;i++) {
int val = bytes[i];
String s = "00"+Integer.toHexString(val);
data.append(s.substring(s.length()-2));
}
System.out.println("Bytedata:"+data.toString());
System.out.println("byteinfo:len="+bytes.length+":crc="+Long.toHexString(crc.getValue()));
// when run directly, this will print: byteinfo:len=98:crc=c1047cf6
}
public static void check(FakeClosure before, FakeClosure after) {
if (before==null && after!=null) {
throw new IllegalStateException("Missing deserialized object for comparison");
}
if (!before.field.equals(after.field)) {
IllegalStateException ise = new IllegalStateException("Not the same "+before.field+" and "+after.field);
throw ise;
}
System.out.println("check ok");
}
public static byte[] write(Object o) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
return null;
}
}
public static Object read(byte[] bs) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bs);
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
return o;
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,26 @@
package remote;
import java.lang.reflect.Method;
public class OneOne {
public static void main(String[] args) throws Exception {
System.out.println(hasStaticInitializer(OneOne.class));
}
private static boolean hasStaticInitializer(Class cl) {
try {
Method[] ms = cl.getDeclaredMethods();
for (Method m: ms) {
System.out.println(m);
}
cl.getDeclaredMethod("<clinit>");
return true;
}
catch (Exception e) {
return false;
}
}
static {
System.out.println();
}
}

View File

@@ -0,0 +1,28 @@
package remote;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Person implements Serializable {
private String firstname;
private String lastname;
public Person(String f, String l) {
this.firstname = f;
this.lastname = l;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public String toString() {
return firstname+" "+lastname;
}
}

View File

@@ -0,0 +1,31 @@
package remote;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Person2 implements Serializable {
private String firstname;
private String lastname;
public Person2(String f, String l) {
this.firstname = f;
this.lastname = l;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public String toString() {
return firstname+" "+lastname;
}
// private String getInitials() {
// return firstname.charAt(0)+""+lastname.charAt(0);
// }
}

View File

@@ -0,0 +1,29 @@
package remote;
import java.io.Serializable;
public class PersonB implements Serializable {
private static final long serialVersionUID = 1L;
private String firstname;
private String lastname;
public PersonB(String f, String l) {
this.firstname = f;
this.lastname = l;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public String toString() {
return firstname+" "+lastname;
}
}

View File

@@ -0,0 +1,33 @@
package remote;
import java.io.Serializable;
public class PersonB2 implements Serializable {
private static final long serialVersionUID = 1L;
private String firstname;
private String lastname;
public PersonB2(String f, String l) {
this.firstname = f;
this.lastname = l;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public String toString() {
return firstname+" "+lastname;
}
public String getInitials() {
return firstname.charAt(0)+""+lastname.charAt(0);
}
}

View File

@@ -0,0 +1,173 @@
package remote;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.zip.CRC32;
public class Serialize {
private static byte[] storedBytes;
// This is a serialized Person(Wilf,Smith)
private static final String serString =
"aced00057372000d72656d6f74652e506572736f6e176b418850fd97d50200024c000966697273746e616d657400124c6a6176612f6c616e672f537472696e673b4c00086c6173746e616d6571007e0001787074000457696c66740005536d697468";
private static final byte[] serBytes;
static {
serBytes = new byte[serString.length()/2];
int p = 0;
while (p < serString.length()) {
String oneByte = serString.substring(p,p+2);
int b = Integer.decode("0x"+oneByte);
serBytes[p/2]=(byte)b;
p+=2;
}
System.out.println();
}
public static void main(String[] args) {
if (args!=null && args.length!=0 && args[0].equals("ds")) {
new Serialize().checkPredeserializedData();
}
else {
run();
new Serialize().checkPredeserializedData();
}
}
public void checkPredeserializedData() {
Person p = (Person)read(serBytes);
if (p==null) {
throw new IllegalStateException("Unable to deserialize Person!");
}
if (!p.toString().equals("Wilf Smith")) {
throw new IllegalStateException("Unable to deserialized pre-serialized data: "+p.toString());
}
System.out.println("Pre-serialized form checked ok");
}
public static byte[] loadBytesFromStream(InputStream stream) {
try {
BufferedInputStream bis = new BufferedInputStream(stream);
byte[] theData = new byte[10000000];
int dataReadSoFar = 0;
byte[] buffer = new byte[1024];
int read = 0;
while ((read = bis.read(buffer)) != -1) {
System.arraycopy(buffer, 0, theData, dataReadSoFar, read);
dataReadSoFar += read;
}
bis.close();
// Resize to actual data read
byte[] returnData = new byte[dataReadSoFar];
System.arraycopy(theData, 0, returnData, 0, dataReadSoFar);
return returnData;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void run() {
// new Serialize().run1();
new Serialize().run2();
}
public void writePerson() {
Person p = new Person("Wilf","Smith");
storedBytes = write(p);
System.out.println("Person stored ok");
}
public Person readPerson() {
Person person = (Person)read(storedBytes);
if (!person.toString().equals("Wilf Smith")) {
throw new IllegalStateException("Expected 'Wilf Smith' but was '"+person.toString()+"'");
}
System.out.println("Person read ok");
return person;
}
public void printSecret() throws Exception {
Person p = readPerson();
Field f = p.getClass().getDeclaredField("newSecretField");
f.setAccessible(true);
Object value = f.get(p);
System.out.println(value);
}
public void run1() {
String s = "abc";
byte[] bs = write(s);
String s2 = (String)read(bs);
check(s,s2);
}
public void run2() {
Person p = new Person("Wilf","Smith");
byte[] bs = write(p);
dumpinfo(bs);
Person p2 = (Person)read(bs);
check(p,p2);
}
private void dumpinfo(byte[] bytes) {
CRC32 crc = new CRC32();
crc.update(bytes,0,bytes.length);
StringBuilder data = new StringBuilder();
for (int i=0;i<bytes.length;i++) {
int val = bytes[i];
String s = "00"+Integer.toHexString(val);
data.append(s.substring(s.length()-2));
}
System.out.println("Bytedata:"+data.toString());
System.out.println("byteinfo:len="+bytes.length+":crc="+Long.toHexString(crc.getValue()));
// when run directly, this will print: byteinfo:len=98:crc=c1047cf6
}
public static void check(Object before, Object after) {
if (before==null && after!=null) {
throw new IllegalStateException("Missing deserialized object for comparison");
}
if (!before.toString().equals(after.toString())) {
IllegalStateException ise = new IllegalStateException("Not the same "+before+" and "+after);
throw ise;
}
System.out.println("check ok");
}
public static byte[] write(Object o) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
return null;
}
}
public static Object read(byte[] bs) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bs);
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
return o;
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,112 @@
package remote;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.zip.CRC32;
public class SerializeB {
private static byte[] storedBytes;
public static void main(String[] args) {
run();
}
public static void run() {
// new Serialize().run1();
new SerializeB().run2();
}
public void writePerson() {
PersonB p = new PersonB("Wilf","Smith");
storedBytes = write(p);
System.out.println("Person stored ok");
}
public PersonB readPerson() {
PersonB person = (PersonB)read(storedBytes);
if (!person.toString().equals("Wilf Smith")) {
throw new IllegalStateException("Expected 'Wilf Smith' but was '"+person.toString()+"'");
}
System.out.println("Person read ok");
return person;
}
public void printSecret() throws Exception {
PersonB p = readPerson();
Field f = p.getClass().getDeclaredField("newSecretField");
f.setAccessible(true);
Object value = f.get(p);
System.out.println(value);
}
public void printInitials() throws Exception {
PersonB person = readPerson();
Method m = person.getClass().getDeclaredMethod("getInitials");
m.setAccessible(true);
String value = (String)m.invoke(person);
System.out.println(value);
}
public void run1() {
String s = "abc";
byte[] bs = write(s);
String s2 = (String)read(bs);
check(s,s2);
}
public void run2() {
PersonB p = new PersonB("Wilf","Smith");
byte[] bs = write(p);
dumpinfo(bs);
PersonB p2 = (PersonB)read(bs);
check(p,p2);
}
private void dumpinfo(byte[] bytes) {
CRC32 crc = new CRC32();
crc.update(bytes,0,bytes.length);
System.out.println("byteinfo:len="+bytes.length+":crc="+Long.toHexString(crc.getValue()));
}
public static void check(Object before, Object after) {
if (before==null && after!=null) {
throw new IllegalStateException("Missing deserialized object for comparison");
}
if (!before.toString().equals(after.toString())) {
IllegalStateException ise = new IllegalStateException("Not the same "+before+" and "+after);
ise.printStackTrace();
throw ise;
}
System.out.println("check ok");
}
public static byte[] write(Object o) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
return null;
}
}
public static Object read(byte[] bs) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bs);
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
return o;
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,29 @@
package system;
import java.lang.reflect.Method;
/*
* Method.invoke(instance, arguments)
*
* This test class represents a class in the system set for the VM. These classes cannot have their reflective calls directly
* intercepted because we cannot introduce dependencies on types in a lower classloader, so we have to call the reflective
* interceptor reflectively!
*/
public class Eleven {
public String runIt() throws Exception {
StringBuilder data = new StringBuilder();
Object obj = invoke(new Eleven(),12,"abc");
data.append("obj="+obj);
return "complete:" + data.toString().trim();
}
public static Object invoke(Object instance, Object... args) throws Exception {
Method m = Eleven.class.getDeclaredMethod("foo", Integer.TYPE,String.class);
return m.invoke(instance,args);
}
public String foo(int i, String s) {
return "i="+i+":s="+s;
}
}

28
testdata/src/main/java/system/Ten.java vendored Normal file
View File

@@ -0,0 +1,28 @@
package system;
import java.lang.reflect.Constructor;
/*
* Class.getDeclaredConstructors()
*
* This test class represents a class in the system set for the VM. These classes cannot have their reflective calls directly
* intercepted because we cannot introduce dependencies on types in a lower classloader, so we have to call the reflective
* interceptor reflectively!
*/
@SuppressWarnings("rawtypes")
public class Ten {
public String runIt() {
StringBuilder data = new StringBuilder();
Constructor[] constructors = cs();
data.append("constructors:null?" + (constructors == null) + " ");
if (constructors != null) {
data.append("constructors:size=" + constructors.length + " ");
}
return "complete:" + data.toString().trim();
}
public Constructor[] cs() {
return this.getClass().getDeclaredConstructors();
}
}

View File

@@ -0,0 +1,27 @@
package system;
import java.lang.reflect.Field;
/*
* Field.getLong()
*
* This test class represents a class in the system set for the VM. These classes cannot have their reflective calls directly
* intercepted because we cannot introduce dependencies on types in a lower classloader, so we have to call the reflective
* interceptor reflectively!
*/
public class Thirteen {
public long foo = 42L;
public String runIt() throws Exception {
StringBuilder data = new StringBuilder();
Object value = gf();
data.append("value="+value);
return "complete:" + data.toString().trim();
}
public Long gf() throws Exception {
Field f = Thirteen.class.getField("foo");
return f.getLong(this);
}
}

View File

@@ -0,0 +1,27 @@
package system;
import java.lang.reflect.Field;
/*
* Field.get()
*
* This test class represents a class in the system set for the VM. These classes cannot have their reflective calls directly
* intercepted because we cannot introduce dependencies on types in a lower classloader, so we have to call the reflective
* interceptor reflectively!
*/
public class Twelve {
public String foo = "abc";
public String runIt() throws Exception {
StringBuilder data = new StringBuilder();
Object value = gf();
data.append("value="+value);
return "complete:" + data.toString().trim();
}
public Object gf() throws Exception {
Field f = Twelve.class.getField("foo");
return f.get(this);
}
}