mirror of https://github.com/apache/openjpa.git
OPENJPA-2909 generate CalendarProxy via ASM
This commit is contained in:
parent
94a2bf97be
commit
87458c708b
|
@ -98,6 +98,7 @@ public class ProxyManagerImpl
|
|||
|
||||
private static final Localizer _loc = Localizer.forPackage
|
||||
(ProxyManagerImpl.class);
|
||||
public static final Type TYPE_OBJECT = Type.getType(Object.class);
|
||||
|
||||
private static long _proxyId = 0L;
|
||||
private static final Map _stdCollections = new HashMap();
|
||||
|
@ -469,8 +470,7 @@ public class ProxyManagerImpl
|
|||
ProxyCalendar.class);
|
||||
Class pcls = loadBuildTimeProxy(type, l);
|
||||
if (pcls == null)
|
||||
pcls = GeneratedClasses.loadBCClass(
|
||||
generateProxyCalendarBytecode(type, true), l);
|
||||
pcls = generateAndLoadProxyCalendar(type, true, l);
|
||||
proxy = (ProxyCalendar) instantiateProxy(pcls, null, null);
|
||||
_proxies.put(type, proxy);
|
||||
}
|
||||
|
@ -676,15 +676,20 @@ public class ProxyManagerImpl
|
|||
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
|
||||
}
|
||||
|
||||
private Class generateAndLoadProxyCalendar(Class type, boolean runtime, ClassLoader l) {
|
||||
final String proxyClassName = getProxyClassName(type, runtime);
|
||||
final byte[] classBytes = generateProxyCalendarBytecode(type, runtime, proxyClassName);
|
||||
|
||||
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the bytecode for a date proxy for the given type.
|
||||
*/
|
||||
protected byte[] generateProxyDateBytecode(Class type, boolean runtime, String proxyClassName) {
|
||||
assertNotFinal(type);
|
||||
String proxyClassDef = proxyClassName.replace('.', '/');
|
||||
|
||||
String superClassFileNname = type.getName().replace('.', '/');
|
||||
|
||||
String superClassFileNname = Type.getInternalName(type);
|
||||
String[] interfaceNames = new String[]{Type.getInternalName(ProxyDate.class)};
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
|
@ -704,6 +709,34 @@ public class ProxyManagerImpl
|
|||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the bytecode for a calendar proxy for the given type.
|
||||
*/
|
||||
protected byte[] generateProxyCalendarBytecode(Class type, boolean runtime, String proxyClassName) {
|
||||
assertNotFinal(type);
|
||||
String proxyClassDef = proxyClassName.replace('.', '/');
|
||||
String superClassFileNname = Type.getInternalName(type);
|
||||
String[] interfaceNames = new String[]{Type.getInternalName(ProxyCalendar.class)};
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
|
||||
null, superClassFileNname, interfaceNames);
|
||||
|
||||
String classFileName = runtime ? type.getName() : proxyClassDef;
|
||||
cw.visitSource(classFileName + ".java", null);
|
||||
|
||||
delegateConstructors(cw, type, superClassFileNname);
|
||||
addInstanceVariables(cw);
|
||||
addProxyMethods(cw, true, proxyClassDef, type);
|
||||
addProxyCalendarMethods(cw, proxyClassDef, type);
|
||||
proxySetters(cw, proxyClassDef, type);
|
||||
addWriteReplaceMethod(cw, proxyClassDef, runtime);
|
||||
|
||||
return cw.toByteArray();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* add the instance variables to the class to be generated
|
||||
*/
|
||||
|
@ -808,13 +841,13 @@ public class ProxyManagerImpl
|
|||
* provide a detached proxy object (null out the StateManager).
|
||||
*/
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "clone",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class))
|
||||
Type.getMethodDescriptor(TYPE_OBJECT)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(parentClass), "clone",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class)), false);
|
||||
Type.getMethodDescriptor(TYPE_OBJECT), false);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Proxy.class));
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 1);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
|
@ -886,8 +919,9 @@ public class ProxyManagerImpl
|
|||
}
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class))
|
||||
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
|
||||
|
@ -939,6 +973,7 @@ public class ProxyManagerImpl
|
|||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance",
|
||||
Type.getMethodDescriptor(Type.getType(ProxyDate.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "<init>",
|
||||
|
@ -950,6 +985,130 @@ public class ProxyManagerImpl
|
|||
}
|
||||
}
|
||||
|
||||
private void addProxyCalendarMethods(ClassWriter cw, String proxyClassDef, Class type) {
|
||||
// calendar copy
|
||||
{
|
||||
Constructor cons = findCopyConstructor(type);
|
||||
Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes();
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy",
|
||||
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
|
||||
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, getParamTypes(params)), false);
|
||||
|
||||
// timeInMillis
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getTimeInMillis",
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE), false);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTimeInMillis",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE), false);
|
||||
|
||||
// lenient
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "isLenient",
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE), false);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setLenient",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.BOOLEAN_TYPE), false);
|
||||
|
||||
// firstDayOfWeek
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getFirstDayOfWeek",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE), false);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setFirstDayOfWeek",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false);
|
||||
|
||||
// minimalDaysInFirstWeek
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getMinimalDaysInFirstWeek",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE), false);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setMinimalDaysInFirstWeek",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false);
|
||||
|
||||
// timeZone
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Calendar.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Calendar.class), "getTimeZone",
|
||||
Type.getMethodDescriptor(Type.getType(TimeZone.class)), false);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "setTimeZone",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(TimeZone.class)), false);
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
// newInstance factory
|
||||
{
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance",
|
||||
Type.getMethodDescriptor(Type.getType(ProxyCalendar.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE), false);
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
// proxy the protected computeFields method b/c it is called on
|
||||
// mutate, and some setters are final and therefore not proxyable
|
||||
{
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PROTECTED, "computeFields",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitInsn(Opcodes.ICONST_1);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Proxies.class), "dirty",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false);
|
||||
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "computeFields",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE), false);
|
||||
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// proxy the protected computeFields method b/c it is called on
|
||||
// mutate, and some setters are final and therefore not proxyable
|
||||
m = bc.declareMethod("computeFields", void.class, null);
|
||||
m.makeProtected();
|
||||
code = m.getCode(true);
|
||||
code.aload().setThis();
|
||||
code.constant().setValue(true);
|
||||
code.invokestatic().setMethod(Proxies.class, "dirty", void.class,
|
||||
new Class[] { Proxy.class, boolean.class });
|
||||
code.aload().setThis();
|
||||
code.invokespecial().setMethod(type, "computeFields", void.class, null);
|
||||
code.vreturn();
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy setter methods of the given type.
|
||||
*
|
||||
|
@ -1006,13 +1165,13 @@ public class ProxyManagerImpl
|
|||
*/
|
||||
private void addWriteReplaceMethod(ClassWriter cw, String proxyClassDef, boolean runtime) {
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PROTECTED, "writeReplace",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class))
|
||||
Type.getMethodDescriptor(TYPE_OBJECT)
|
||||
, null, new String[]{Type.getInternalName(ObjectStreamException.class)});
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitInsn(runtime ? Opcodes.ICONST_0 : Opcodes.ICONST_1); // !runtime
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Proxies.class), "writeReplace",
|
||||
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false);
|
||||
Type.getMethodDescriptor(TYPE_OBJECT, Type.getType(Proxy.class), Type.BOOLEAN_TYPE), false);
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
|
@ -1120,25 +1279,6 @@ public class ProxyManagerImpl
|
|||
|
||||
/* ASM end */
|
||||
|
||||
/**
|
||||
* Generate the bytecode for a calendar proxy for the given type.
|
||||
*/
|
||||
protected BCClass generateProxyCalendarBytecode(Class type,
|
||||
boolean runtime) {
|
||||
assertNotFinal(type);
|
||||
Project project = new Project();
|
||||
BCClass bc = AccessController.doPrivileged(J2DoPrivHelper
|
||||
.loadProjectClassAction(project, getProxyClassName(type, runtime)));
|
||||
bc.setSuperclass(type);
|
||||
bc.declareInterface(ProxyCalendar.class);
|
||||
|
||||
delegateConstructors(bc, type);
|
||||
addProxyMethods(bc, true);
|
||||
addProxyCalendarMethods(bc, type);
|
||||
proxySetters(bc, type);
|
||||
addWriteReplaceMethod(bc, runtime);
|
||||
return bc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the bytecode for a bean proxy for the given type.
|
||||
|
@ -1523,100 +1663,7 @@ public class ProxyManagerImpl
|
|||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the methods in the {@link ProxyCalendar} interface.
|
||||
*/
|
||||
private void addProxyCalendarMethods(BCClass bc, Class type) {
|
||||
// calendar copy
|
||||
Constructor cons = findCopyConstructor(type);
|
||||
Class[] params = (cons == null) ? new Class[0]
|
||||
: cons.getParameterTypes();
|
||||
|
||||
BCMethod m = bc.declareMethod("copy", Object.class,
|
||||
new Class[] {Object.class});
|
||||
m.makePublic();
|
||||
Code code = m.getCode(true);
|
||||
|
||||
code.anew().setType(type);
|
||||
code.dup();
|
||||
if (params.length == 1) {
|
||||
code.aload().setParam(0);
|
||||
code.checkcast().setType(params[0]);
|
||||
}
|
||||
code.invokespecial().setMethod(type, "<init>", void.class, params);
|
||||
if (params.length == 0) {
|
||||
code.dup();
|
||||
code.aload().setParam(0);
|
||||
code.checkcast().setType(Calendar.class);
|
||||
code.invokevirtual().setMethod(Calendar.class, "getTimeInMillis",
|
||||
long.class, null);
|
||||
code.invokevirtual().setMethod(type, "setTimeInMillis", void.class,
|
||||
new Class[] { long.class });
|
||||
|
||||
code.dup();
|
||||
code.aload().setParam(0);
|
||||
code.checkcast().setType(Calendar.class);
|
||||
code.invokevirtual().setMethod(Calendar.class, "isLenient",
|
||||
boolean.class, null);
|
||||
code.invokevirtual().setMethod(type, "setLenient", void.class,
|
||||
new Class[] { boolean.class });
|
||||
|
||||
code.dup();
|
||||
code.aload().setParam(0);
|
||||
code.checkcast().setType(Calendar.class);
|
||||
code.invokevirtual().setMethod(Calendar.class, "getFirstDayOfWeek",
|
||||
int.class, null);
|
||||
code.invokevirtual().setMethod(type, "setFirstDayOfWeek",
|
||||
void.class, new Class[] { int.class });
|
||||
|
||||
code.dup();
|
||||
code.aload().setParam(0);
|
||||
code.checkcast().setType(Calendar.class);
|
||||
code.invokevirtual().setMethod(Calendar.class,
|
||||
"getMinimalDaysInFirstWeek", int.class, null);
|
||||
code.invokevirtual().setMethod(type, "setMinimalDaysInFirstWeek",
|
||||
void.class, new Class[] { int.class });
|
||||
|
||||
code.dup();
|
||||
code.aload().setParam(0);
|
||||
code.checkcast().setType(Calendar.class);
|
||||
code.invokevirtual().setMethod(Calendar.class, "getTimeZone",
|
||||
TimeZone.class, null);
|
||||
code.invokevirtual().setMethod(type, "setTimeZone", void.class,
|
||||
new Class[] { TimeZone.class });
|
||||
}
|
||||
code.areturn();
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
|
||||
// new instance factory
|
||||
m = bc.declareMethod("newInstance", ProxyCalendar.class, null);
|
||||
m.makePublic();
|
||||
code = m.getCode(true);
|
||||
code.anew().setType(bc);
|
||||
code.dup();
|
||||
code.invokespecial().setMethod("<init>", void.class, null);
|
||||
code.areturn();
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
|
||||
// proxy the protected computeFields method b/c it is called on
|
||||
// mutate, and some setters are final and therefore not proxyable
|
||||
m = bc.declareMethod("computeFields", void.class, null);
|
||||
m.makeProtected();
|
||||
code = m.getCode(true);
|
||||
code.aload().setThis();
|
||||
code.constant().setValue(true);
|
||||
code.invokestatic().setMethod(Proxies.class, "dirty", void.class,
|
||||
new Class[] { Proxy.class, boolean.class });
|
||||
code.aload().setThis();
|
||||
code.invokespecial().setMethod(type, "computeFields", void.class, null);
|
||||
code.vreturn();
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implement the methods in the {@link ProxyBean} interface.
|
||||
*/
|
||||
|
@ -2094,13 +2141,17 @@ public class ProxyManagerImpl
|
|||
}
|
||||
|
||||
// ASM generated proxies
|
||||
if (Date.class.isAssignableFrom(cls)) {
|
||||
if (Date.class.isAssignableFrom(cls) ||
|
||||
Calendar.class.isAssignableFrom(cls)) {
|
||||
final String proxyClassName = getProxyClassName(cls, false);
|
||||
|
||||
byte[] bytes = null;
|
||||
if (Date.class.isAssignableFrom(cls)) {
|
||||
bytes = mgr.generateProxyDateBytecode(cls, false, proxyClassName);
|
||||
}
|
||||
else if (Calendar.class.isAssignableFrom(cls)) {
|
||||
bytes = mgr.generateProxyCalendarBytecode(cls, false, proxyClassName);
|
||||
}
|
||||
if (bytes != null) {
|
||||
final String fileName = cls.getName().replace('.', '$') + PROXY_SUFFIX + ".class";
|
||||
java.nio.file.Files.write(new File(dir, fileName).toPath(), bytes);
|
||||
|
@ -2112,8 +2163,6 @@ public class ProxyManagerImpl
|
|||
bc = mgr.generateProxyCollectionBytecode(cls, false);
|
||||
else if (Map.class.isAssignableFrom(cls))
|
||||
bc = mgr.generateProxyMapBytecode(cls, false);
|
||||
else if (Calendar.class.isAssignableFrom(cls))
|
||||
bc = mgr.generateProxyCalendarBytecode(cls, false);
|
||||
else {
|
||||
final Class fCls = cls;
|
||||
// TODO Move this to J2DOPrivHelper
|
||||
|
|
Loading…
Reference in New Issue