diff --git a/springloaded/lib/asm-5.0_BETA-sources.zip b/springloaded/lib/asm-5.0_BETA-sources.zip
new file mode 100644
index 0000000..20bbae9
Binary files /dev/null and b/springloaded/lib/asm-5.0_BETA-sources.zip differ
diff --git a/springloaded/src/main/java/org/springsource/loaded/Constants.java b/springloaded/src/main/java/org/springsource/loaded/Constants.java
index 22e6c25..7dad0a9 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Constants.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Constants.java
@@ -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$";
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
index beba62e..673355a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
@@ -1480,4 +1480,8 @@ public class ReloadableType {
return superRtype;
}
}
+
+ public boolean hasStaticInitializer() {
+ return this.typedescriptor.hasClinit();
+ }
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java b/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java
index 44e3852..b5ff6aa 100644
--- a/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java
@@ -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;
*
+ * Due to ObjectStream (added in SL 1.2.0)
+ *
+ * - Class.getDeclaredConstructors
+ *
- Field.get
+ *
- Field.getLong
+ *
- Method.invoke
+ *
+ * The method hasStaticInitializer(Class) in ObjectStream needs special handling.
*
*
- * 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:
+ bits |= JLC_GETDECLAREDCONSTRUCTORS;
+ mv.visitMethodInsn(INVOKESTATIC, classname, jlcGetDeclaredConstructorsMember,jlcGetDeclaredConstructorsDescriptor);
+ return true;
} else if (name.equals("getConstructor")) {
// stack on arrival:
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:
+ 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:
+ mv.visitMethodInsn(INVOKESTATIC, classname, jlrfGetMember, jlrfGetDescriptor);
+ return true;
+ } else if (name.equals("getLong")) {
+ bits |= JLRF_GETLONG;
+ // stack on arrival:
+ 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);
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/JVMPlugin.java b/springloaded/src/main/java/org/springsource/loaded/agent/JVMPlugin.java
index 832945a..ef08895 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/JVMPlugin.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/JVMPlugin.java
@@ -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 */
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> localDescs =
+// new ConcurrentHashMap<>();
+//
+// /** cache mapping field group/local desc pairs -> field reflectors */
+// static final ConcurrentMap> reflectors =
+// new ConcurrentHashMap<>();
+//
+// /** queue for WeakReferences to local classes */
+// private static final ReferenceQueue> localDescsQueue =
+// new ReferenceQueue<>();
+// /** queue for WeakReferences to field reflectors keys */
+// private static final ReferenceQueue> 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
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java b/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java
index eb461a9..1ba852f 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java
@@ -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);
diff --git a/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java b/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java
index b61e68a..378ae4a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java
@@ -84,6 +84,16 @@ public class ReflectiveInterceptor {
classToRType = new WeakHashMap, WeakReference>();
}
}
+
+ @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).
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java b/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java
index 082323b..e5e225e 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java
@@ -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 {
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java b/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java
index b65d091..79d9432 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java
@@ -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();
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java
index 90fc2f1..319f6a2 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java
@@ -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());
+ }
+ }
}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java
index c45281f..82f0950 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java
@@ -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());
- }
- }
}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/SystemClassReflectionRewriterTests.java b/springloaded/src/test/java/org/springsource/loaded/test/SystemClassReflectionRewriterTests.java
index 2c91881..4874671 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/SystemClassReflectionRewriterTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/SystemClassReflectionRewriterTests.java
@@ -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) ()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) ()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) ()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) ()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 {
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/TypeRewriterTests.java b/springloaded/src/test/java/org/springsource/loaded/test/TypeRewriterTests.java
index 3934a4e..f465fa6 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/TypeRewriterTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/TypeRewriterTests.java
@@ -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);
}
diff --git a/testdata-groovy/src/remote/Code.groovy b/testdata-groovy/src/remote/Code.groovy
new file mode 100644
index 0000000..e99c333
--- /dev/null
+++ b/testdata-groovy/src/remote/Code.groovy
@@ -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()
+ }
+}
\ No newline at end of file
diff --git a/testdata-groovy/src/remote/FakeClosure.java b/testdata-groovy/src/remote/FakeClosure.java
new file mode 100644
index 0000000..2bfda2b
--- /dev/null
+++ b/testdata-groovy/src/remote/FakeClosure.java
@@ -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);
+ }
+
+}
diff --git a/testdata-groovy/src/remote/SerializeG.java b/testdata-groovy/src/remote/SerializeG.java
new file mode 100644
index 0000000..9057bba
--- /dev/null
+++ b/testdata-groovy/src/remote/SerializeG.java
@@ -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");
+ return true;
+ }
+ catch (Exception e) {
+ return false;
+ }
+ }
+ static {
+ System.out.println();
+ }
+}
diff --git a/testdata/src/main/java/remote/Person.java b/testdata/src/main/java/remote/Person.java
new file mode 100644
index 0000000..dc77dfd
--- /dev/null
+++ b/testdata/src/main/java/remote/Person.java
@@ -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;
+ }
+
+}
diff --git a/testdata/src/main/java/remote/Person2.java b/testdata/src/main/java/remote/Person2.java
new file mode 100644
index 0000000..1e4cdd7
--- /dev/null
+++ b/testdata/src/main/java/remote/Person2.java
@@ -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);
+// }
+}
diff --git a/testdata/src/main/java/remote/PersonB.java b/testdata/src/main/java/remote/PersonB.java
new file mode 100644
index 0000000..c1e6db0
--- /dev/null
+++ b/testdata/src/main/java/remote/PersonB.java
@@ -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;
+ }
+
+}
diff --git a/testdata/src/main/java/remote/PersonB2.java b/testdata/src/main/java/remote/PersonB2.java
new file mode 100644
index 0000000..6d52aec
--- /dev/null
+++ b/testdata/src/main/java/remote/PersonB2.java
@@ -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);
+ }
+}
diff --git a/testdata/src/main/java/remote/Serialize.java b/testdata/src/main/java/remote/Serialize.java
new file mode 100644
index 0000000..3ff2e50
--- /dev/null
+++ b/testdata/src/main/java/remote/Serialize.java
@@ -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