mirror of
https://github.com/apache/openjpa.git
synced 2025-02-07 02:29:22 +00:00
OPENJPA-2909 implement ASM proxy for Collections
This commit is contained in:
parent
87458c708b
commit
12f780eb76
@ -109,6 +109,7 @@
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<!--
|
||||
<execution>
|
||||
<id>generate-standard-sco-proxies</id>
|
||||
<phase>process-classes</phase>
|
||||
@ -123,6 +124,7 @@
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
-->
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.openjpa.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.xbean.asm9.ClassWriter;
|
||||
import org.apache.xbean.asm9.MethodVisitor;
|
||||
|
||||
/**
|
||||
* Helper to keep track of generated methods when using ASM ClassWriter.
|
||||
*
|
||||
* @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
|
||||
*/
|
||||
public class ClassWriterTracker {
|
||||
|
||||
private final ClassWriter cw;
|
||||
private List<String> createdMethods = new ArrayList<>();
|
||||
|
||||
public ClassWriterTracker(ClassWriter cw) {
|
||||
this.cw = cw;
|
||||
}
|
||||
|
||||
public ClassWriter getCw() {
|
||||
return cw;
|
||||
}
|
||||
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature,
|
||||
final String[] exceptionTypes) {
|
||||
MethodVisitor mv = cw.visitMethod(access, name, descriptor, signature, exceptionTypes);
|
||||
|
||||
createdMethods.add(name + descriptor);
|
||||
|
||||
return mv;
|
||||
}
|
||||
|
||||
public boolean hasMethod(final String name, final String descriptor) {
|
||||
return createdMethods.contains(name + descriptor);
|
||||
}
|
||||
}
|
@ -58,6 +58,7 @@ import org.apache.openjpa.lib.util.J2DoPrivHelper;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.lib.util.Options;
|
||||
import org.apache.openjpa.lib.util.StringUtil;
|
||||
import org.apache.openjpa.util.asm.AsmHelper;
|
||||
import org.apache.openjpa.util.proxy.DelayedArrayListProxy;
|
||||
import org.apache.openjpa.util.proxy.DelayedHashSetProxy;
|
||||
import org.apache.openjpa.util.proxy.DelayedLinkedHashSetProxy;
|
||||
@ -74,6 +75,7 @@ import org.apache.openjpa.util.proxy.ProxyDate;
|
||||
import org.apache.openjpa.util.proxy.ProxyMap;
|
||||
import org.apache.openjpa.util.proxy.ProxyMaps;
|
||||
import org.apache.xbean.asm9.ClassWriter;
|
||||
import org.apache.xbean.asm9.Label;
|
||||
import org.apache.xbean.asm9.MethodVisitor;
|
||||
import org.apache.xbean.asm9.Opcodes;
|
||||
import org.apache.xbean.asm9.Type;
|
||||
@ -403,25 +405,6 @@ public class ProxyManagerImpl
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cached factory proxy for the given collection type.
|
||||
*/
|
||||
private ProxyCollection getFactoryProxyCollection(Class type) {
|
||||
// we don't lock here; ok if two proxies get generated for same type
|
||||
ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
|
||||
if (proxy == null) {
|
||||
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
|
||||
ProxyCollection.class);
|
||||
Class pcls = loadBuildTimeProxy(type, l);
|
||||
if (pcls == null)
|
||||
pcls = GeneratedClasses.loadBCClass(
|
||||
generateProxyCollectionBytecode(type, true), l);
|
||||
proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
|
||||
_proxies.put(type, proxy);
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cached factory proxy for the given map type.
|
||||
*/
|
||||
@ -477,6 +460,25 @@ public class ProxyManagerImpl
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cached factory proxy for the given collection type.
|
||||
*/
|
||||
private ProxyCollection getFactoryProxyCollection(Class type) {
|
||||
// we don't lock here; ok if two proxies get generated for same type
|
||||
ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
|
||||
if (proxy == null) {
|
||||
ClassLoader l = GeneratedClasses.getMostDerivedLoader(type,
|
||||
ProxyCollection.class);
|
||||
Class pcls = loadBuildTimeProxy(type, l);
|
||||
if (pcls == null)
|
||||
pcls = generateAndLoadProxyCollection(type, true, l);
|
||||
proxy = (ProxyCollection) instantiateProxy(pcls, null, null);
|
||||
_proxies.put(type, proxy);
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the cached factory proxy for the given bean type.
|
||||
*/
|
||||
@ -683,6 +685,13 @@ public class ProxyManagerImpl
|
||||
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
|
||||
}
|
||||
|
||||
private Class generateAndLoadProxyCollection(Class type, boolean runtime, ClassLoader l) {
|
||||
final String proxyClassName = getProxyClassName(type, runtime);
|
||||
final byte[] classBytes = generateProxyCollectionBytecode(type, runtime, proxyClassName);
|
||||
|
||||
return GeneratedClasses.loadAsmClass(proxyClassName, classBytes, ProxyDate.class, l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the bytecode for a date proxy for the given type.
|
||||
*/
|
||||
@ -696,15 +705,16 @@ public class ProxyManagerImpl
|
||||
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
|
||||
null, superClassFileNname, interfaceNames);
|
||||
|
||||
ClassWriterTracker ct = new ClassWriterTracker(cw);
|
||||
String classFileName = runtime ? type.getName() : proxyClassDef;
|
||||
cw.visitSource(classFileName + ".java", null);
|
||||
|
||||
delegateConstructors(cw, type, superClassFileNname);
|
||||
addInstanceVariables(cw);
|
||||
addProxyMethods(cw, true, proxyClassDef, type);
|
||||
addProxyDateMethods(cw, proxyClassDef, type);
|
||||
proxySetters(cw, proxyClassDef, type);
|
||||
addWriteReplaceMethod(cw, proxyClassDef, runtime);
|
||||
delegateConstructors(ct, type, superClassFileNname);
|
||||
addInstanceVariables(ct);
|
||||
addProxyMethods(ct, true, proxyClassDef, type);
|
||||
addProxyDateMethods(ct, proxyClassDef, type);
|
||||
proxySetters(ct, proxyClassDef, type);
|
||||
addWriteReplaceMethod(ct, proxyClassDef, runtime);
|
||||
|
||||
return cw.toByteArray();
|
||||
}
|
||||
@ -722,31 +732,351 @@ public class ProxyManagerImpl
|
||||
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
|
||||
null, superClassFileNname, interfaceNames);
|
||||
|
||||
ClassWriterTracker ct = new ClassWriterTracker(cw);
|
||||
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);
|
||||
delegateConstructors(ct, type, superClassFileNname);
|
||||
addInstanceVariables(ct);
|
||||
addProxyMethods(ct, true, proxyClassDef, type);
|
||||
addProxyCalendarMethods(ct, proxyClassDef, type);
|
||||
proxySetters(ct, proxyClassDef, type);
|
||||
addWriteReplaceMethod(ct, proxyClassDef, runtime);
|
||||
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the bytecode for a collection proxy for the given type.
|
||||
*/
|
||||
protected byte[] generateProxyCollectionBytecode(Class type, boolean runtime, String proxyClassName) {
|
||||
assertNotFinal(type);
|
||||
String proxyClassDef = proxyClassName.replace('.', '/');
|
||||
String superClassFileNname = Type.getInternalName(type);
|
||||
String[] interfaceNames = new String[]{Type.getInternalName(ProxyCollection.class)};
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(Opcodes.V11, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, proxyClassDef,
|
||||
null, superClassFileNname, interfaceNames);
|
||||
|
||||
ClassWriterTracker ct = new ClassWriterTracker(cw);
|
||||
String classFileName = runtime ? type.getName() : proxyClassDef;
|
||||
cw.visitSource(classFileName + ".java", null);
|
||||
|
||||
delegateConstructors(ct, type, superClassFileNname);
|
||||
addInstanceVariables(ct);
|
||||
addProxyMethods(ct, false, proxyClassDef, type);
|
||||
addProxyCollectionMethods(ct, proxyClassDef, type);
|
||||
proxyRecognizedMethods(ct, proxyClassDef, type,ProxyCollections.class, ProxyCollection.class);
|
||||
proxySetters(ct, proxyClassDef, type);
|
||||
addWriteReplaceMethod(ct, proxyClassDef, runtime);
|
||||
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
private void addProxyCollectionMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
|
||||
// change tracker
|
||||
{
|
||||
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
|
||||
"changeTracker", Type.getDescriptor(CollectionChangeTracker.class), null, null).visitEnd();
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getChangeTracker",
|
||||
Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(CollectionChangeTracker.class));
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
// collection copy
|
||||
{
|
||||
Constructor cons = findCopyConstructor(type);
|
||||
if (cons == null && SortedSet.class.isAssignableFrom(type)) {
|
||||
cons = findComparatorConstructor(type);
|
||||
}
|
||||
Class[] params = (cons == null) ? new Class[0]
|
||||
: cons.getParameterTypes();
|
||||
|
||||
MethodVisitor mv = ct.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);
|
||||
|
||||
if (params.length == 1) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
if (params[0] == Comparator.class) {
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(SortedSet.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(SortedSet.class), "comparator",
|
||||
Type.getMethodDescriptor(Type.getType(Comparator.class)), true);
|
||||
}
|
||||
else {
|
||||
// otherwise just pass the parameter
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
|
||||
}
|
||||
}
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
|
||||
|
||||
if (params.length == 0 || params[0] == Comparator.class) {
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Collection.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(type), "addAll",
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, TYPE_OBJECT), true);
|
||||
mv.visitInsn(Opcodes.POP);
|
||||
}
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
// element type
|
||||
{
|
||||
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
|
||||
"elementType", Type.getDescriptor(Class.class), null, null).visitEnd();
|
||||
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getElementType",
|
||||
Type.getMethodDescriptor(Type.getType(Class.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassDef, "elementType", Type.getDescriptor(Class.class));
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
|
||||
}
|
||||
|
||||
// new instance factory
|
||||
{
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
|
||||
Type.getMethodDescriptor(Type.getType(ProxyCollection.class),
|
||||
Type.getType(Class.class), Type.getType(Comparator.class), Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitTypeInsn(Opcodes.NEW, proxyClassDef);
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
|
||||
Constructor cons = findComparatorConstructor(type);
|
||||
Class[] params = (cons == null) ? new Class[0] : cons.getParameterTypes();
|
||||
if (params.length == 1) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, proxyClassDef, "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
|
||||
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 5);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 5);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "elementType", Type.getDescriptor(Class.class));
|
||||
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 3);
|
||||
Label lNotTrack = new Label();
|
||||
mv.visitJumpInsn(Opcodes.IFEQ, lNotTrack);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 5);
|
||||
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(CollectionChangeTrackerImpl.class));
|
||||
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 5);
|
||||
|
||||
mv.visitInsn(allowsDuplicates(type) ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
|
||||
mv.visitInsn(isOrdered(type) ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
|
||||
mv.visitVarInsn(Opcodes.ILOAD, 4);
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(CollectionChangeTrackerImpl.class), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Collection.class),
|
||||
Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE),
|
||||
false);
|
||||
mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassDef, "changeTracker", Type.getDescriptor(CollectionChangeTracker.class));
|
||||
|
||||
mv.visitLabel(lNotTrack);
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 5);
|
||||
|
||||
mv.visitInsn(Opcodes.ARETURN);
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void proxyRecognizedMethods(ClassWriterTracker ct, String proxyClassDef, Class<?> type,
|
||||
Class<?> helper, Class<?> proxyType) {
|
||||
Method[] meths = type.getMethods();
|
||||
|
||||
for (Method meth : meths) {
|
||||
// Java 8 methods with a return type of KeySetView do not need to be proxied
|
||||
if (meth.getReturnType().getName().contains("KeySetView")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class[] helperParams = toHelperParameters(meth.getParameterTypes(), proxyType);
|
||||
|
||||
// first check for overriding method
|
||||
try {
|
||||
Method match;
|
||||
match = helper.getMethod(meth.getName(), helperParams);
|
||||
proxyOverrideMethod(ct, meth, match, helperParams);
|
||||
continue;
|
||||
}
|
||||
catch (NoSuchMethodException nsme) {
|
||||
// all fine
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new GeneralException(e);
|
||||
}
|
||||
|
||||
// check for before and after methods, either of which may not
|
||||
// exist
|
||||
Method before = null;
|
||||
try {
|
||||
before = helper.getMethod("before" + StringUtil.capitalize(meth.getName()), helperParams);
|
||||
}
|
||||
catch (NoSuchMethodException nsme) {
|
||||
// all fine
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new GeneralException(e);
|
||||
}
|
||||
Method after = null;
|
||||
Class[] afterParams = null;
|
||||
|
||||
try {
|
||||
afterParams = toHelperAfterParameters(helperParams,
|
||||
meth.getReturnType(), (before == null)
|
||||
? void.class : before.getReturnType());
|
||||
after = helper.getMethod("after"
|
||||
+ StringUtil.capitalize(meth.getName()), afterParams);
|
||||
}
|
||||
catch (NoSuchMethodException nsme) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new GeneralException(e);
|
||||
}
|
||||
if (before != null || after != null)
|
||||
proxyBeforeAfterMethod(ct, type, meth, helperParams, before, after, afterParams);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy the given method with one that overrides it by calling into the
|
||||
* given helper.
|
||||
*/
|
||||
private void proxyOverrideMethod(ClassWriterTracker ct, Method meth, Method helper, Class[] helperParams) {
|
||||
MethodVisitor mv = ct.visitMethod(meth.getModifiers() & ~Modifier.SYNCHRONIZED, meth.getName(),
|
||||
Type.getMethodDescriptor(meth), null, null);
|
||||
mv.visitCode();
|
||||
|
||||
// push all the method params to the stack
|
||||
// we only start at param[1] as param[0] of the helper method is the instance itself
|
||||
// and will get loaded with ALOAD_0 (this)
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
for (int i = 1; i < helperParams.length; i++)
|
||||
{
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(helper.getDeclaringClass()), helper.getName(),
|
||||
Type.getMethodDescriptor(helper), false);
|
||||
|
||||
mv.visitInsn(AsmHelper.getReturnInsn(meth.getReturnType()));
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy the given method with one that overrides it by calling into the
|
||||
* given helper.
|
||||
*/
|
||||
private void proxyBeforeAfterMethod(ClassWriterTracker ct, Class type, Method meth, Class[] helperParams,
|
||||
Method before, Method after, Class[] afterParams) {
|
||||
|
||||
MethodVisitor mv = ct.visitMethod(meth.getModifiers() & ~Modifier.SYNCHRONIZED, meth.getName(),
|
||||
Type.getMethodDescriptor(meth), null, null);
|
||||
mv.visitCode();
|
||||
|
||||
int beforeRetPos = -1;
|
||||
int variableNr = helperParams.length;;
|
||||
|
||||
// invoke before
|
||||
if (before != null) {
|
||||
// push all the method params to the stack
|
||||
// we only start at param[1] as param[0] of the helper method is the instance itself
|
||||
// and will get loaded with ALOAD_0 (this)
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
for (int i = 1; i < helperParams.length; i++)
|
||||
{
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(before.getDeclaringClass()), before.getName(),
|
||||
Type.getMethodDescriptor(before), false);
|
||||
|
||||
if (after != null && before.getReturnType() != void.class) {
|
||||
// this is always a boolean and 1 after the
|
||||
beforeRetPos = variableNr++;
|
||||
mv.visitVarInsn(AsmHelper.getStoreInsn(before.getReturnType()), beforeRetPos);
|
||||
}
|
||||
}
|
||||
|
||||
// invoke super
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
for (int i = 1; i < helperParams.length; i++)
|
||||
{
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
|
||||
}
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), meth.getName(),
|
||||
Type.getMethodDescriptor(meth), false);
|
||||
|
||||
// invoke after
|
||||
if (after != null) {
|
||||
int retPos = -1;
|
||||
if (meth.getReturnType() != void.class) {
|
||||
retPos = variableNr++;
|
||||
mv.visitVarInsn(AsmHelper.getStoreInsn(meth.getReturnType()), retPos);
|
||||
}
|
||||
|
||||
// push all the method params to the stack
|
||||
// we only start at param[1] as param[0] of the helper method is the instance itself
|
||||
// and will get loaded with ALOAD_0 (this)
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
for (int i = 1; i < helperParams.length; i++)
|
||||
{
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(helperParams[i]), i);
|
||||
}
|
||||
|
||||
if (retPos != -1) {
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(meth.getReturnType()),retPos);
|
||||
}
|
||||
if (beforeRetPos != -1) {
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(before.getReturnType()),beforeRetPos);
|
||||
}
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(after.getDeclaringClass()), after.getName(),
|
||||
Type.getMethodDescriptor(after), false);
|
||||
}
|
||||
|
||||
mv.visitInsn(AsmHelper.getReturnInsn(meth.getReturnType()));
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* add the instance variables to the class to be generated
|
||||
*/
|
||||
private void addInstanceVariables(ClassWriter cw) {
|
||||
private void addInstanceVariables(ClassWriterTracker ct) {
|
||||
// variable #1, the state manager
|
||||
cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
|
||||
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
|
||||
"sm", Type.getDescriptor(OpenJPAStateManager.class), null, null).visitEnd();
|
||||
|
||||
// variable #2, the state manager
|
||||
cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
|
||||
ct.getCw().visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_TRANSIENT,
|
||||
"field", Type.getDescriptor(int.class), null, null).visitEnd();
|
||||
}
|
||||
|
||||
@ -754,19 +1084,19 @@ public class ProxyManagerImpl
|
||||
/**
|
||||
* Create pass-through constructors to base type.
|
||||
*/
|
||||
private void delegateConstructors(ClassWriter cw, Class type, String superClassFileNname) {
|
||||
private void delegateConstructors(ClassWriterTracker ct, Class type, String superClassFileNname) {
|
||||
Constructor[] constructors = type.getConstructors();
|
||||
|
||||
for (Constructor constructor : constructors) {
|
||||
Class[] params = constructor.getParameterTypes();
|
||||
String[] exceptionTypes = getInternalNames(constructor.getExceptionTypes());
|
||||
String[] exceptionTypes = AsmHelper.getInternalNames(constructor.getExceptionTypes());
|
||||
String descriptor = Type.getConstructorDescriptor(constructor);
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", descriptor, null, exceptionTypes);
|
||||
MethodVisitor mv = ct.visitMethod(Opcodes.ACC_PUBLIC, "<init>", descriptor, null, exceptionTypes);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
for (int i = 1; i <= params.length; i++)
|
||||
{
|
||||
mv.visitVarInsn(getVarInsn(params[i-1]), i);
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(params[i-1]), i);
|
||||
}
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassFileNname, "<init>", descriptor, false);
|
||||
|
||||
@ -780,14 +1110,14 @@ public class ProxyManagerImpl
|
||||
* Implement the methods in the {@link Proxy} interface, with the exception
|
||||
* of {@link Proxy#copy}.
|
||||
*
|
||||
* @param changeTracker whether to implement a null change tracker; if false
|
||||
* @param defaultChangeTracker whether to implement a null change tracker; if false
|
||||
* the change tracker method is left unimplemented
|
||||
* @param proxyClassDef
|
||||
*/
|
||||
private void addProxyMethods(ClassWriter cw, boolean changeTracker, String proxyClassDef, Class<?> parentClass) {
|
||||
private void addProxyMethods(ClassWriterTracker ct, boolean defaultChangeTracker, String proxyClassDef, Class<?> parentClass) {
|
||||
|
||||
{
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "setOwner",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "setOwner",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OpenJPAStateManager.class), Type.INT_TYPE)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -805,7 +1135,7 @@ public class ProxyManagerImpl
|
||||
}
|
||||
|
||||
{
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getOwner",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getOwner",
|
||||
Type.getMethodDescriptor(Type.getType(OpenJPAStateManager.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -819,7 +1149,7 @@ public class ProxyManagerImpl
|
||||
}
|
||||
|
||||
{
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getOwnerField",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getOwnerField",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -840,7 +1170,7 @@ public class ProxyManagerImpl
|
||||
* was invoked. So, we are now overriding the clone() method so as to
|
||||
* provide a detached proxy object (null out the StateManager).
|
||||
*/
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "clone",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "clone",
|
||||
Type.getMethodDescriptor(TYPE_OBJECT)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -863,8 +1193,8 @@ public class ProxyManagerImpl
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
if (changeTracker) {
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "getChangeTracker",
|
||||
if (defaultChangeTracker) {
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "getChangeTracker",
|
||||
Type.getMethodDescriptor(Type.getType(ChangeTracker.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -878,7 +1208,7 @@ public class ProxyManagerImpl
|
||||
/**
|
||||
* Implement the methods in the {@link ProxyDate} interface.
|
||||
*/
|
||||
private void addProxyDateMethods(ClassWriter cw, String proxyClassDef, Class type) {
|
||||
private void addProxyDateMethods(ClassWriterTracker ct, String proxyClassDef, Class type) {
|
||||
|
||||
final boolean hasDefaultCons = hasConstructor(type);
|
||||
final boolean hasMillisCons = hasConstructor(type, long.class);
|
||||
@ -889,7 +1219,7 @@ public class ProxyManagerImpl
|
||||
|
||||
// add a default constructor that delegates to the millis constructor
|
||||
if (!hasDefaultCons) {
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
|
||||
MethodVisitor mv = ct.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE), null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
@ -918,7 +1248,7 @@ public class ProxyManagerImpl
|
||||
params = new Class[0];
|
||||
}
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "copy",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
|
||||
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -926,21 +1256,20 @@ public class ProxyManagerImpl
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
|
||||
if (params.length == 1) {
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
if (params[0] == long.class) {
|
||||
// call getTime on the given Date if the current type has a long constructor
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(java.util.Date.class));
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(java.util.Date.class), "getTime",
|
||||
Type.getMethodDescriptor(Type.LONG_TYPE), false);
|
||||
}
|
||||
else {
|
||||
// otherwise just pass the parameter
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(params[0]));
|
||||
}
|
||||
}
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), "<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, getParamTypes(params)), false);
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
|
||||
|
||||
if (params.length == 0) {
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
@ -970,7 +1299,7 @@ public class ProxyManagerImpl
|
||||
|
||||
{
|
||||
// new instance factory
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
|
||||
Type.getMethodDescriptor(Type.getType(ProxyDate.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -985,13 +1314,13 @@ public class ProxyManagerImpl
|
||||
}
|
||||
}
|
||||
|
||||
private void addProxyCalendarMethods(ClassWriter cw, String proxyClassDef, Class type) {
|
||||
private void addProxyCalendarMethods(ClassWriterTracker ct, 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",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "copy",
|
||||
Type.getMethodDescriptor(TYPE_OBJECT, TYPE_OBJECT)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -999,7 +1328,7 @@ public class ProxyManagerImpl
|
||||
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);
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.getParamTypes(params)), false);
|
||||
|
||||
// timeInMillis
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
@ -1053,7 +1382,7 @@ public class ProxyManagerImpl
|
||||
|
||||
// newInstance factory
|
||||
{
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "newInstance",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, "newInstance",
|
||||
Type.getMethodDescriptor(Type.getType(ProxyCalendar.class))
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -1070,7 +1399,7 @@ public class ProxyManagerImpl
|
||||
// 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",
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PROTECTED, "computeFields",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE)
|
||||
, null, null);
|
||||
mv.visitCode();
|
||||
@ -1088,25 +1417,6 @@ public class ProxyManagerImpl
|
||||
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();
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1114,28 +1424,30 @@ public class ProxyManagerImpl
|
||||
*
|
||||
* @return true if we generated any setters, false otherwise
|
||||
*/
|
||||
private boolean proxySetters(ClassWriter cw, String proxyClassDef, Class type) {
|
||||
private boolean proxySetters(ClassWriterTracker ct, String proxyClassDef, Class type) {
|
||||
Method[] meths = type.getMethods();
|
||||
|
||||
int setters = 0;
|
||||
for (Method meth : meths) {
|
||||
if (isSetter(meth) && !Modifier.isFinal(meth.getModifiers())) {
|
||||
|
||||
setters++;
|
||||
proxySetter(cw, proxyClassDef, type, meth);
|
||||
proxySetter(ct, proxyClassDef, type, meth);
|
||||
}
|
||||
}
|
||||
return setters > 0;
|
||||
}
|
||||
|
||||
private void proxySetter(ClassWriter cw, String proxyClassDef, Class type, Method meth) {
|
||||
private void proxySetter(ClassWriterTracker ct, String proxyClassDef, Class type, Method meth) {
|
||||
Class[] params = meth.getParameterTypes();
|
||||
Class ret = meth.getReturnType();
|
||||
|
||||
final String methodDescriptor = Type.getMethodDescriptor(Type.getType(ret), getParamTypes(params));
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, meth.getName(),
|
||||
methodDescriptor
|
||||
, null, null);
|
||||
final String methodDescriptor = Type.getMethodDescriptor(Type.getType(ret), AsmHelper.getParamTypes(params));
|
||||
if (ct.hasMethod(meth.getName(), methodDescriptor)) {
|
||||
// this method already got created
|
||||
return;
|
||||
}
|
||||
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PUBLIC, meth.getName(), methodDescriptor, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
mv.visitInsn(Opcodes.ICONST_1);
|
||||
@ -1147,13 +1459,13 @@ public class ProxyManagerImpl
|
||||
// push all the method params to the stack
|
||||
for (int i = 1; i <= params.length; i++)
|
||||
{
|
||||
mv.visitVarInsn(getVarInsn(params[i-1]), i);
|
||||
mv.visitVarInsn(AsmHelper.getLoadInsn(params[i-1]), i);
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(type), meth.getName(),
|
||||
methodDescriptor, false);
|
||||
|
||||
mv.visitInsn(getReturnInsn(ret));
|
||||
mv.visitInsn(AsmHelper.getReturnInsn(ret));
|
||||
mv.visitMaxs(-1, -1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
@ -1163,8 +1475,8 @@ public class ProxyManagerImpl
|
||||
* Add a writeReplace implementation that serializes to a non-proxy type
|
||||
* unless detached and this is a build-time generated class.
|
||||
*/
|
||||
private void addWriteReplaceMethod(ClassWriter cw, String proxyClassDef, boolean runtime) {
|
||||
MethodVisitor mv = cw.visitMethod(Modifier.PROTECTED, "writeReplace",
|
||||
private void addWriteReplaceMethod(ClassWriterTracker ct, String proxyClassDef, boolean runtime) {
|
||||
MethodVisitor mv = ct.visitMethod(Modifier.PROTECTED, "writeReplace",
|
||||
Type.getMethodDescriptor(TYPE_OBJECT)
|
||||
, null, new String[]{Type.getInternalName(ObjectStreamException.class)});
|
||||
mv.visitCode();
|
||||
@ -1180,23 +1492,7 @@ public class ProxyManagerImpl
|
||||
|
||||
/* a few utility methods to make life with ASM easier */
|
||||
|
||||
private String[] getInternalNames(Class[] classes) {
|
||||
String[] internalNames = new String[classes.length];
|
||||
|
||||
for (int i=0; i<classes.length; i++) {
|
||||
internalNames[i] = Type.getInternalName(classes[i]);
|
||||
}
|
||||
return internalNames;
|
||||
}
|
||||
|
||||
private Type[] getParamTypes(Class[] params) {
|
||||
Type[] types = new Type[params.length];
|
||||
for (int i=0; i<types.length; i++) {
|
||||
types[i] = Type.getType(params[i]);
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
private boolean hasConstructor(Class type, Class<?>... paramTypes) {
|
||||
try {
|
||||
@ -1207,76 +1503,6 @@ public class ProxyManagerImpl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate bytecode instruction to load a value from a variable to the stack
|
||||
*
|
||||
* @param type Type to load
|
||||
* @return Bytecode instruction to use
|
||||
*/
|
||||
private int getVarInsn(Class<?> type)
|
||||
{
|
||||
if (type.isPrimitive())
|
||||
{
|
||||
if (Integer.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Boolean.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Character.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Byte.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Short.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Float.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.FLOAD;
|
||||
}
|
||||
else if (Long.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.LLOAD;
|
||||
}
|
||||
else if (Double.TYPE.equals(type))
|
||||
{
|
||||
return Opcodes.DLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
return Opcodes.ALOAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* calclates the proper Return instruction opcode for the given class
|
||||
*/
|
||||
private int getReturnInsn(Class ret) {
|
||||
if (ret.equals(Void.TYPE)) {
|
||||
return Opcodes.RETURN;
|
||||
}
|
||||
if (ret.equals(Integer.TYPE)) {
|
||||
return Opcodes.IRETURN;
|
||||
}
|
||||
if (ret.equals(Long.TYPE)) {
|
||||
return Opcodes.LRETURN;
|
||||
}
|
||||
if (ret.equals(Float.TYPE)) {
|
||||
return Opcodes.FRETURN;
|
||||
}
|
||||
if (ret.equals(Double.TYPE)) {
|
||||
return Opcodes.DRETURN;
|
||||
}
|
||||
return Opcodes.ARETURN;
|
||||
}
|
||||
|
||||
|
||||
/* ASM end */
|
||||
|
||||
|
||||
@ -1663,7 +1889,7 @@ public class ProxyManagerImpl
|
||||
code.calculateMaxStack();
|
||||
code.calculateMaxLocals();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implement the methods in the {@link ProxyBean} interface.
|
||||
*/
|
||||
@ -2142,16 +2368,22 @@ public class ProxyManagerImpl
|
||||
|
||||
// ASM generated proxies
|
||||
if (Date.class.isAssignableFrom(cls) ||
|
||||
Calendar.class.isAssignableFrom(cls)) {
|
||||
Calendar.class.isAssignableFrom(cls) ||
|
||||
Collection.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);
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(cls)) {
|
||||
bytes = mgr.generateProxyCollectionBytecode(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);
|
||||
@ -2159,8 +2391,6 @@ public class ProxyManagerImpl
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Collection.class.isAssignableFrom(cls))
|
||||
bc = mgr.generateProxyCollectionBytecode(cls, false);
|
||||
else if (Map.class.isAssignableFrom(cls))
|
||||
bc = mgr.generateProxyMapBytecode(cls, false);
|
||||
else {
|
||||
|
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.openjpa.util.asm;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.xbean.asm9.Opcodes;
|
||||
import org.apache.xbean.asm9.Type;
|
||||
|
||||
/**
|
||||
* Utility methods to deal with ASM bytecode
|
||||
*
|
||||
* @author <a href="mailto:struberg@apache.org">Mark Struberg</a>
|
||||
*/
|
||||
public final class AsmHelper {
|
||||
private AsmHelper() {
|
||||
// utility class ct
|
||||
}
|
||||
|
||||
/**
|
||||
* Calclates the proper Return instruction opcode for the given class
|
||||
*
|
||||
* @param type the type to get returned
|
||||
* @return the proper Opcode RETURN, ARETURN, IRETURN, etc
|
||||
*/
|
||||
public static int getReturnInsn(Class type) {
|
||||
if (type.equals(Void.TYPE)) {
|
||||
return Opcodes.RETURN;
|
||||
}
|
||||
if (type.isPrimitive()) {
|
||||
if (Integer.TYPE.equals(type)) {
|
||||
return Opcodes.IRETURN;
|
||||
}
|
||||
else if (Boolean.TYPE.equals(type)) {
|
||||
return Opcodes.IRETURN;
|
||||
}
|
||||
else if (Character.TYPE.equals(type)) {
|
||||
return Opcodes.IRETURN;
|
||||
}
|
||||
else if (Byte.TYPE.equals(type)) {
|
||||
return Opcodes.IRETURN;
|
||||
}
|
||||
else if (Short.TYPE.equals(type)) {
|
||||
return Opcodes.IRETURN;
|
||||
}
|
||||
else if (Float.TYPE.equals(type)) {
|
||||
return Opcodes.FRETURN;
|
||||
}
|
||||
else if (Long.TYPE.equals(type)) {
|
||||
return Opcodes.LRETURN;
|
||||
}
|
||||
else if (Double.TYPE.equals(type)) {
|
||||
return Opcodes.DRETURN;
|
||||
}
|
||||
}
|
||||
return Opcodes.ARETURN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calclates the proper STORE instruction opcode for the given type
|
||||
*
|
||||
* @param type the type to get stored
|
||||
* @return the proper Opcode ISTORE, ASTORE, LSTORE, etc
|
||||
*/
|
||||
public static int getStoreInsn(Class<?> type) {
|
||||
if (type.equals(Void.TYPE)) {
|
||||
throw new IllegalArgumentException("Void type cannot be stored");
|
||||
}
|
||||
if (type.isPrimitive()) {
|
||||
if (Integer.TYPE.equals(type)) {
|
||||
return Opcodes.ISTORE;
|
||||
}
|
||||
else if (Boolean.TYPE.equals(type)) {
|
||||
return Opcodes.ISTORE;
|
||||
}
|
||||
else if (Character.TYPE.equals(type)) {
|
||||
return Opcodes.ISTORE;
|
||||
}
|
||||
else if (Byte.TYPE.equals(type)) {
|
||||
return Opcodes.ISTORE;
|
||||
}
|
||||
else if (Short.TYPE.equals(type)) {
|
||||
return Opcodes.ISTORE;
|
||||
}
|
||||
else if (Float.TYPE.equals(type)) {
|
||||
return Opcodes.FSTORE;
|
||||
}
|
||||
else if (Long.TYPE.equals(type)) {
|
||||
return Opcodes.LSTORE;
|
||||
}
|
||||
else if (Double.TYPE.equals(type)) {
|
||||
return Opcodes.DSTORE;
|
||||
}
|
||||
}
|
||||
|
||||
return Opcodes.ASTORE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calclates the proper LOAD instruction opcode for the given type.
|
||||
* This is the appropriate bytecode instruction to load a value from a variable to the stack.
|
||||
*
|
||||
* @param type the type to get loaded
|
||||
* @return the proper Opcode ILOAD, ALOAD, LLOAD, etc
|
||||
*/
|
||||
public static int getLoadInsn(Class<?> type) {
|
||||
if (type.equals(Void.TYPE)) {
|
||||
throw new IllegalArgumentException("Void type cannot be loaded");
|
||||
}
|
||||
if (type.isPrimitive()) {
|
||||
if (Integer.TYPE.equals(type)) {
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Boolean.TYPE.equals(type)) {
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Character.TYPE.equals(type)) {
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Byte.TYPE.equals(type)) {
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Short.TYPE.equals(type)) {
|
||||
return Opcodes.ILOAD;
|
||||
}
|
||||
else if (Float.TYPE.equals(type)) {
|
||||
return Opcodes.FLOAD;
|
||||
}
|
||||
else if (Long.TYPE.equals(type)) {
|
||||
return Opcodes.LLOAD;
|
||||
}
|
||||
else if (Double.TYPE.equals(type)) {
|
||||
return Opcodes.DLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
return Opcodes.ALOAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internal names for the given classes
|
||||
* @see Type#getInternalName(Class)
|
||||
*/
|
||||
public static String[] getInternalNames(Class[] classes) {
|
||||
String[] internalNames = new String[classes.length];
|
||||
|
||||
for (int i=0; i<classes.length; i++) {
|
||||
internalNames[i] = Type.getInternalName(classes[i]);
|
||||
}
|
||||
return internalNames;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the ASM Types for the given classes
|
||||
* @see Type#getType(Method)
|
||||
*/
|
||||
public static Type[] getParamTypes(Class[] params) {
|
||||
Type[] types = new Type[params.length];
|
||||
for (int i=0; i<types.length; i++) {
|
||||
types[i] = Type.getType(params[i]);
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user