/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.internal.ext.org.objectweb.asm.ClassReader;
import gw.internal.ext.org.objectweb.asm.ClassVisitor;
import gw.internal.ext.org.objectweb.asm.ClassWriter;
import gw.internal.ext.org.objectweb.asm.Label;
import gw.internal.ext.org.objectweb.asm.MethodVisitor;
import gw.internal.ext.org.objectweb.asm.Opcodes;
import gw.internal.ext.org.objectweb.asm.Type;
import gw.internal.ext.org.objectweb.asm.util.CheckClassAdapter;
import gw.internal.ext.org.objectweb.asm.util.TraceClassVisitor;
import gw.lang.GosuShop;
import gw.lang.reflect.IInjectableClassLoader;
import gw.lang.reflect.INonLoadableType;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeRef;
import gw.lang.reflect.ITypeRefFactory;
import gw.lang.reflect.RefreshKind;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuObject;
import gw.lang.reflect.java.IJavaBackedType;
import gw.util.cache.FqnCacheNode;
import gw.util.Predicate;
import gw.util.cache.WeakFqnCache;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* There is one TypeRefFactory per ModuleTypeLoader.
*/
public class TypeRefFactory implements ITypeRefFactory
{
private static boolean TRACE = false;
private static boolean VERIFY = false;
private static boolean ASM_CHECKER = false;
private static final int JAVA_VER = Opcodes.V1_5;
private static final Map<Class<? extends IType>, Class<? extends AbstractTypeRef>> ITYPE_PROXY_CLASS_BY_ITYPE_CLASS =
new HashMap<Class<? extends IType>, Class<? extends AbstractTypeRef>>();
private final WeakFqnCache<AbstractTypeRef> _refByName;
private boolean _bClearing;
public TypeRefFactory()
{
_refByName = new WeakFqnCache<AbstractTypeRef>();
}
/**
* Wraps the actual class with a proxy.
*/
@Override
public ITypeRef create( IType type )
{
// already a proxy? return as is then
if( type instanceof ITypeRef )
{
return (ITypeRef)type;
}
if( type instanceof INonLoadableType )
{
throw new UnsupportedOperationException( "Type references are not supported for nonloadable types: " + type.getName() );
}
String strTypeName = TypeLord.getNameWithQualifiedTypeVariables( type, true );
if( strTypeName == null || strTypeName.length() == 0 )
{
throw new IllegalStateException( "Type has no name" );
}
ITypeRef ref;
if (TypeSystem.isSingleModuleMode()) {
ref = getRefTheFastWay(type, strTypeName);
} else {
ref = getRefTheSafeWay(type, strTypeName);
}
return ref;
}
private ITypeRef getRefTheFastWay(IType type, String strTypeName) {
AbstractTypeRef ref = getRef(_refByName, strTypeName, type);
if (ref == null) {
TypeSystem.lock();
try {
ref = getRef(_refByName, strTypeName, type);
if (ref == null) {
ref = createTypeRefProxy(type);
putRef(_refByName, strTypeName, ref);
return ref;
}
} finally {
TypeSystem.unlock();
}
}
if (!type.isDiscarded()) {
ref._setType(type);
}
return ref;
}
private ITypeRef getRefTheSafeWay(IType type, String strTypeName) {
TypeSystem.lock();
try {
AbstractTypeRef ref = getRef(_refByName, strTypeName, type);
if (ref == null) {
ref = createTypeRefProxy(type);
putRef(_refByName, strTypeName, ref);
return ref;
}
if (!type.isDiscarded()) {
ref._setType(type);
}
return ref;
} finally {
TypeSystem.unlock();
}
}
private AbstractTypeRef createTypeRefProxy( IType type )
{
Class<? extends IType> typeClass = type.getClass();
try
{
Class<? extends AbstractTypeRef> proxyClass = getOrCreateTypeProxy( typeClass );
AbstractTypeRef typeRef = proxyClass.newInstance();
typeRef._setType( type );
return typeRef;
}
catch( Exception e )
{
throw new RuntimeException( e );
}
}
private Class<? extends AbstractTypeRef> getOrCreateTypeProxy( Class<? extends IType> typeClass )
{
Class<? extends AbstractTypeRef> proxyClass = ITYPE_PROXY_CLASS_BY_ITYPE_CLASS.get( typeClass );
try
{
if( proxyClass == null )
{
proxyClass = generateProxyClass( typeClass );
ITYPE_PROXY_CLASS_BY_ITYPE_CLASS.put( typeClass, proxyClass );
}
return proxyClass;
}
catch( Exception e )
{
throw new RuntimeException( e );
}
}
private Class<? extends AbstractTypeRef> generateProxyClass(Class<? extends IType> typeClass) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = writer;
StringWriter trace = null;
if (TRACE) {
trace = new StringWriter();
cv = new TraceClassVisitor(cv, new PrintWriter(trace));
if (ASM_CHECKER) {
cv = new CheckClassAdapter(cv);
}
}
List<Class> interfaces = new ArrayList<Class>();
getInterfacesFrom(typeClass, interfaces );
String strProxyClassName = typeClass.getName() + SYSTEM_PROXY_SUFFIX;
compileHeader(cv, strProxyClassName, interfaces);
cv.visitSource( strProxyClassName, null );
addDefaultConstructor(cv);
compileInterfaceMembers(cv, typeClass);
addToString(cv);
cv.visitEnd();
if( TRACE ) {
System.out.println( "========================================================================" );
System.out.println( strProxyClassName );
System.out.println( "========================================================================" );
System.out.println( trace );
}
byte[] bytes = writer.toByteArray();
verify( bytes );
//noinspection unchecked
ClassLoader classLoader = typeClass.getClassLoader();
if(classLoader instanceof IInjectableClassLoader) {
return ((IInjectableClassLoader) classLoader).defineClass(strProxyClassName, bytes);
} else {
try {
Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
return (Class<? extends AbstractTypeRef>) method.invoke(classLoader, strProxyClassName, bytes, 0, bytes.length);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
private void compileHeader(ClassVisitor cv, String name, List<Class> interfaces) {
cv.visit( JAVA_VER,
Opcodes.ACC_PUBLIC,
name.replace( '.', '/' ),
null,
getSlashName( AbstractTypeRef.class ),
getInterfaceNames(interfaces) );
}
private String[] getInterfaceNames(List<Class> interfaces) {
String[] interfaceNames = new String[interfaces.size()];
for (int i = 0; i < interfaces.size(); i++) {
interfaceNames[i] = getSlashName(interfaces.get(i));
}
return interfaceNames;
}
private void addDefaultConstructor(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod( Opcodes.ACC_PUBLIC,
"<init>",
"()V",
null, null );
mv.visitCode();
mv.visitVarInsn( Opcodes.ALOAD, 0 ); // Load the "this" pointer
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, getSlashName(AbstractTypeRef.class), "<init>", "()V");
mv.visitInsn( Opcodes.RETURN );
mv.visitMaxs(0, 0);
}
private void addToString(ClassVisitor cv) {
MethodVisitor mv = cv.visitMethod( Opcodes.ACC_PUBLIC,
"toString",
"()Ljava/lang/String;",
null, null );
mv.visitCode();
mv.visitVarInsn( Opcodes.ALOAD, 0 ); // Load the "this" pointer
// callMethod(mv, getMethod(AbstractTypeRef.class, "_getType"));
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
getSlashName( AbstractTypeRef.class ),
"_getType",
"()Lgw/lang/reflect/IType;" );
// callMethod(mv, getMethod(Object.class, "toString"));
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
getSlashName( Object.class ),
"toString",
"()Ljava/lang/String;" );
mv.visitInsn( Opcodes.ARETURN );
mv.visitMaxs(0, 0);
}
private void compileInterfaceMembers(ClassVisitor cv, Class typeClass) {
for( Method rawMethod : typeClass.getMethods() ) {
// Note we keep the method from the impl class because we need to keep its modifiers intact
Method ifaceMethod = getInterfaceMethod(rawMethod);
if( ifaceMethod == rawMethod )
{
continue;
}
int nParameters = rawMethod.getParameterTypes().length;
if( (rawMethod.getName().equals( "unloadTypeInfo" ) && nParameters == 0) ||
(rawMethod.getName().equals( "isStale" ) && nParameters == 0) ||
(rawMethod.getName().equals( "readResolve" ) && nParameters == 0) )
{
continue;
}
genMethod_DevMode( cv, ifaceMethod, rawMethod );
}
}
private void genMethod_DevMode(ClassVisitor cv, Method ifaceMethod, Method proxyMethod) {
int modifiers = proxyMethod.getModifiers();
if (proxyMethod.isSynthetic() || proxyMethod.isBridge() ) {
modifiers = modifiers | Modifier.VOLATILE;
}
MethodVisitor mv = cv.visitMethod( modifiers,
proxyMethod.getName(),
getMethodDescriptor(proxyMethod),
null,
getMethodExceptions(proxyMethod));
mv.visitCode();
// If the method is getName(), insert the short-circuit code
if(ifaceMethod.getName().equals("getName")) {
insertGetNameStart(mv);
}
// The code we're building up looks essentially like the following:
// _reload();
// gw.internal.gosu.parser.IGosuClassInternal type;
// try
// {
// type = (gw.internal.gosu.parser.IGosuClassInternal)_getType();
// } catch (ClassCastException ex) {
// throw new RuntimeException("Type interface changed. Expected gw.internal.gosu.parser.IGosuClassInternal for " + _getType().getName(), ex);
// }
// return (java.util.List)type.getStaticFunctions( $$ );
int iLastParamIndex = ifaceMethod.getParameterTypes().length + 1; // Use the first index after the this pointer and the arguments
int typeIndex = iLastParamIndex + 1;
int classCastExceptionIndex = iLastParamIndex + 2;
Label tryLabel = new Label();
Label catchLabel = new Label();
Label tryEndLabel = new Label();
Label endLabel = new Label();
mv.visitTryCatchBlock( tryLabel, tryEndLabel, catchLabel, getSlashName( ClassCastException.class ) );
//## hack: Before call_reload
visitDebugLineNumber( 1, mv );
call_reload(mv);
// outer try
mv.visitLabel( tryLabel );
assignType(ifaceMethod, mv, typeIndex);
mv.visitLabel( tryEndLabel );
mv.visitJumpInsn( Opcodes.GOTO, endLabel );
// inner catch
mv.visitLabel( catchLabel );
constructRuntimeException(mv, classCastExceptionIndex);
mv.visitInsn( Opcodes.ATHROW );
// call the method
mv.visitLabel( endLabel );
//## hack: Before delegateMethodCall
visitDebugLineNumber( 2, mv );
delegateMethodCall(ifaceMethod, proxyMethod, mv, typeIndex);
//## hack: Before return
visitDebugLineNumber( 3, mv );
if (ifaceMethod.getReturnType().getName().equals(void.class.getSimpleName())) {
mv.visitInsn( Opcodes.RETURN );
} else {
mv.visitInsn( getIns(Opcodes.IRETURN, ifaceMethod.getReturnType().getName()) );
}
mv.visitMaxs(0, 0);
}
private void visitDebugLineNumber( int iLine, MethodVisitor mv )
{
Label dbgLabel = new Label();
mv.visitLabel( dbgLabel );
mv.visitLineNumber(iLine, dbgLabel);
}
private void insertGetNameStart(MethodVisitor mv) {
// For the getName() method, we want to insert this code at the start:
// if(_getTypeNameInternal() != null) {
// return _getTypeNameInternal();
// }
mv.visitVarInsn( Opcodes.ALOAD, 0 );
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
getSlashName( AbstractTypeRef.class ),
"_getTypeNameInternal",
"()Ljava/lang/String;" );
Label afterIf = new Label();
mv.visitJumpInsn( Opcodes.IFNULL, afterIf );
mv.visitVarInsn( Opcodes.ALOAD, 0 );
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
getSlashName( AbstractTypeRef.class ),
"_getTypeNameInternal",
"()Ljava/lang/String;" );
mv.visitInsn( getIns( Opcodes.IRETURN, String.class) );
mv.visitLabel(afterIf);
}
private void call_reload(MethodVisitor mv) {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
getSlashName(AbstractTypeRef.class),
"_reload",
"()V");
}
private void assignType(Method ifaceMethod, MethodVisitor mv, int typeIndex) {
mv.visitVarInsn( Opcodes.ALOAD, 0 );
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
getSlashName(AbstractTypeRef.class),
"_getType",
"()Lgw/lang/reflect/IType;");
mv.visitTypeInsn(Opcodes.CHECKCAST, getSlashName(ifaceMethod.getDeclaringClass()));
mv.visitVarInsn(Opcodes.ASTORE, typeIndex);
}
private void constructRuntimeException(MethodVisitor mv, int exceptionIndex) {
/*RuntimeException("Type interface changed. Expected gw.internal.gosu.parser.IGosuClassInternal for " + _getType().getName(), ex);*/
mv.visitVarInsn( Opcodes.ASTORE, exceptionIndex );
mv.visitTypeInsn(Opcodes.NEW, getSlashName(TypeRefException.class));
mv.visitInsn( Opcodes.DUP );
mv.visitTypeInsn( Opcodes.NEW, getSlashName( StringBuilder.class ) );
mv.visitInsn( Opcodes.DUP );
mv.visitLdcInsn("Type interface changed. Expected gw.internal.gosu.parser.IGosuClassInternal for ");
mv.visitMethodInsn( Opcodes.INVOKESPECIAL,
getSlashName( StringBuilder.class ),
"<init>",
"(Ljava/lang/String;)V" );
mv.visitVarInsn( Opcodes.ALOAD, 0 );
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
getSlashName( AbstractTypeRef.class ),
"_getTypeNameInternal",
"()Ljava/lang/String;" );
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
getSlashName( StringBuilder.class),
"append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;");
mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
getSlashName( StringBuilder.class),
"toString",
"()Ljava/lang/String;");
mv.visitVarInsn( Opcodes.ALOAD, exceptionIndex );
mv.visitMethodInsn( Opcodes.INVOKESPECIAL,
getSlashName( TypeRefException.class ),
"<init>",
"(Ljava/lang/String;Ljava/lang/Throwable;)V" );
}
private void delegateMethodCall(Method ifaceMethod, Method proxyMethod, MethodVisitor mv, int typeIndex) {
mv.visitVarInsn( Opcodes.ALOAD, typeIndex );
for(int i = 0; i < ifaceMethod.getParameterTypes().length; i++) {
mv.visitVarInsn(getIns(Opcodes.ILOAD, ifaceMethod.getParameterTypes()[i].getName()), i + 1);
}
callMethod(mv, ifaceMethod);
if (!ifaceMethod.getReturnType().isPrimitive()) {
// The cast is necessary when we're invoking a bridge method on the parent; seems like that should never happen,
// though, and that this should really be unnecessary
mv.visitTypeInsn( Opcodes.CHECKCAST, getSlashName(proxyMethod.getReturnType().getName()) );
}
}
private boolean isTypeGosuClassInstance(Class<?> declaringClass) {
return IGosuObject.class.isAssignableFrom(declaringClass) &&
TypeSystem.getByFullNameIfValid(declaringClass.getName().replace('$', '.')) instanceof IGosuClass;
}
private Method getInterfaceMethod( Method method ) {
Class<?> declaringClass = method.getDeclaringClass();
do
{
if( isTypeGosuClassInstance(declaringClass) )
{
String gosuClassName = IGosuClass.ProxyUtil.getNameSansProxy(declaringClass.getName());
IGosuClassInternal gosuClass = (IGosuClassInternal) TypeSystem.getByFullNameIfValid(gosuClassName);
if(gosuClass == null) {
return method;
}
for( IType iface : gosuClass.getInterfaces() )
{
Class ifaceClass = getJavaClass( iface );
if( ifaceClass != null )
{
try
{
method = ifaceClass.getMethod( method.getName(), method.getParameterTypes() );
return getInterfaceMethod( method );
}
catch( NoSuchMethodException e )
{
// continue
}
}
}
IType supertype = gosuClass.getSupertype();
declaringClass = getJavaClass( supertype );
}
else
{
for( Class<?> iface : declaringClass.getInterfaces() )
{
try
{
method = iface.getMethod(method.getName(), method.getParameterTypes());
return getInterfaceMethod( method );
}
catch( NoSuchMethodException e )
{
// continue
}
}
declaringClass = declaringClass.getSuperclass();
}
} while( declaringClass != null );
return method;
}
@Override
public ITypeRef get( IType type )
{
if( type instanceof ITypeRef )
{
return (ITypeRef)type;
}
if( type instanceof INonLoadableType )
{
throw new UnsupportedOperationException( "Type references are not supported for nonloadable types: " + type.getName() );
}
String strTypeName = TypeLord.getNameWithQualifiedTypeVariables( type, true );
if( strTypeName == null || strTypeName.length() == 0 )
{
throw new IllegalStateException( "Type has no name" );
}
return getRef( _refByName, strTypeName, type );
}
@Override
public ITypeRef get( String strTypeName )
{
return getRef( _refByName, strTypeName, null );
}
@Override
public void clearCaches()
{
setClearing( true );
TypeSystem.lock();
try {
// Invalidate types, inner types first
_refByName.visitDepthFirst(
new Predicate<AbstractTypeRef>() {
public boolean evaluate( AbstractTypeRef typeRef ) {
if( typeRef != null ) {
typeRef._setStale( RefreshKind.MODIFICATION );
}
return true;
}
} );
// Remove dead types
_refByName.visitNodeDepthFirst(
new Predicate<FqnCacheNode>() {
public boolean evaluate( FqnCacheNode node ) {
@SuppressWarnings("unchecked")
WeakReference<AbstractTypeRef> ref = (WeakReference<AbstractTypeRef>)node.getUserData();
if( ref != null ) {
AbstractTypeRef typeRef = ref.get();
if( typeRef == null ) {
node.delete();
}
}
return true;
}
} );
} finally {
TypeSystem.unlock();
setClearing( false );
}
}
private int computeSortIndex(Map.Entry<String,WeakReference<AbstractTypeRef>> entry)
{
AbstractTypeRef ref = entry.getValue().get();
return ref != null ? ref._getIndexForSortingFast(entry.getKey()) : 10000;
}
private void setClearing( boolean bClearing )
{
_bClearing = bClearing;
}
@Override
public boolean isClearing()
{
return _bClearing;
}
private void getInterfacesFrom( Class<? extends IType> classOfType, List<Class> interfaces )
{
if( isTypeGosuClassInstance(classOfType) )
{
String gosuClassName = IGosuClass.ProxyUtil.getNameSansProxy(classOfType.getName());
IGosuClassInternal gosuClass = (IGosuClassInternal) TypeSystem.getByFullName(gosuClassName);
for(IType iface : gosuClass.getInterfaces()) {
Class ifaceClass = getJavaClass(iface);
if(ifaceClass != null && !interfaces.contains(ifaceClass)) {
interfaces.add(ifaceClass);
}
}
IType supertype = gosuClass.getSupertype();
if( supertype != null )
{
Class ifaceClass = getJavaClass( supertype );
if( ifaceClass != null )
{
getInterfacesFrom( ifaceClass, interfaces );
}
}
}
else
{
for( Class iface : classOfType.getInterfaces() )
{
if( !interfaces.contains( iface ) )
{
interfaces.add( iface );
}
}
Class superClass = classOfType.getSuperclass();
if( superClass != null )
{
getInterfacesFrom( superClass, interfaces );
}
}
}
private Class getJavaClass( IType iface )
{
Class ifaceClass = null;
if( iface instanceof IGosuClass )
{
ifaceClass = ((IGosuClass)iface).getBackingClass();
}
else if( iface instanceof IJavaBackedType )
{
ifaceClass = ((IJavaBackedType)iface).getBackingClass();
}
return ifaceClass;
}
private static void putRef( WeakFqnCache<AbstractTypeRef> map, String key, AbstractTypeRef value )
{
map.add( key, value );
}
private AbstractTypeRef getRef( WeakFqnCache<AbstractTypeRef> map, String key, IType type )
{
AbstractTypeRef typeRef = map.get( key );
if( typeRef == null )
{
return null;
}
else
{
//make sure we're returning a compatible type ref.
if( type != null && !typeRef.getClass().getName().startsWith( type.getClass().getName() ) ) {
return null;
}
return typeRef;
}
}
// ------------------ ASM Helpers
private int getIns( int opcode, Class type )
{
if( opcode == Opcodes.DUP )
{
return isWide( type ) ? Opcodes.DUP2 : opcode;
}
if( opcode == Opcodes.POP )
{
return isWide( type ) ? Opcodes.POP2 : opcode;
}
switch( opcode )
{
case Opcodes.ILOAD:
case Opcodes.ISTORE:
case Opcodes.IALOAD:
case Opcodes.IASTORE:
case Opcodes.IADD:
case Opcodes.ISUB:
case Opcodes.IMUL:
case Opcodes.IDIV:
case Opcodes.IREM:
case Opcodes.INEG:
case Opcodes.ISHL:
case Opcodes.ISHR:
case Opcodes.IUSHR:
case Opcodes.IAND:
case Opcodes.IOR:
case Opcodes.IXOR:
case Opcodes.IRETURN:
break;
default:
throw new IllegalArgumentException( "Opcode: " + Integer.toHexString( opcode ) + " is not handled" );
}
if( type == byte.class )
{
return Type.BYTE_TYPE.getOpcode( opcode );
}
else if( type == char.class )
{
return Type.CHAR_TYPE.getOpcode( opcode );
}
else if( type == short.class )
{
return Type.SHORT_TYPE.getOpcode( opcode );
}
else if( type == boolean.class)
{
return Type.BOOLEAN_TYPE.getOpcode( opcode );
}
else if( type == int.class)
{
return Type.INT_TYPE.getOpcode( opcode );
}
else if( type == long.class )
{
return Type.LONG_TYPE.getOpcode( opcode );
}
else if( type == float.class )
{
return Type.FLOAT_TYPE.getOpcode( opcode );
}
else if( type == double.class )
{
return Type.DOUBLE_TYPE.getOpcode( opcode );
}
else // handles array/ref types
{
return Type.getType(Object.class).getOpcode( opcode );
}
}
private int getIns( int opcode, String typeName )
{
if( opcode == Opcodes.DUP )
{
return isWide( typeName ) ? Opcodes.DUP2 : opcode;
}
if( opcode == Opcodes.POP )
{
return isWide( typeName ) ? Opcodes.POP2 : opcode;
}
switch( opcode )
{
case Opcodes.ILOAD:
case Opcodes.ISTORE:
case Opcodes.IALOAD:
case Opcodes.IASTORE:
case Opcodes.IADD:
case Opcodes.ISUB:
case Opcodes.IMUL:
case Opcodes.IDIV:
case Opcodes.IREM:
case Opcodes.INEG:
case Opcodes.ISHL:
case Opcodes.ISHR:
case Opcodes.IUSHR:
case Opcodes.IAND:
case Opcodes.IOR:
case Opcodes.IXOR:
case Opcodes.IRETURN:
break;
default:
throw new IllegalArgumentException( "Opcode: " + Integer.toHexString( opcode ) + " is not handled" );
}
if( typeName.equals("byte") )
{
return Type.BYTE_TYPE.getOpcode( opcode );
}
else if( typeName.equals("char") )
{
return Type.CHAR_TYPE.getOpcode( opcode );
}
else if( typeName.equals("short") )
{
return Type.SHORT_TYPE.getOpcode( opcode );
}
else if( typeName.equals("boolean") )
{
return Type.BOOLEAN_TYPE.getOpcode( opcode );
}
else if( typeName.equals("int") )
{
return Type.INT_TYPE.getOpcode( opcode );
}
else if( typeName.equals("long") )
{
return Type.LONG_TYPE.getOpcode( opcode );
}
else if( typeName.equals("float") )
{
return Type.FLOAT_TYPE.getOpcode( opcode );
}
else if( typeName.equals("double") )
{
return Type.DOUBLE_TYPE.getOpcode( opcode );
}
else // handles array/ref types
{
return Type.getType(Object.class).getOpcode( opcode );
}
}
private boolean isWide( Class type )
{
return type == long.class || type == double.class;
}
private boolean isWide( String typeName )
{
return typeName.equals("long") || typeName.equals("double");
}
private String getSlashName(Class type) {
return getSlashName(type.getName());
//return Type.getType(type).getInternalName();
}
private String getSlashName(String typeName) {
return typeName.replace('.', '/');
}
private String[] getParameterTypeDescriptors(Method _method) {
Class<?>[] paramTypes = _method.getParameterTypes();
String[] paramDescriptors = new String[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
paramDescriptors[i] = getDescriptor(paramTypes[i]);
// paramDescriptors[i] = AbstractElementTransformer.getDescriptor(paramTypes[i]).getDescriptor();
}
return paramDescriptors;
}
private String getReturnTypeDescriptor(Method _method) {
Class<?> returnType = _method.getReturnType();
return getDescriptor(returnType);
// return AbstractElementTransformer.getDescriptor(_method.getReturnType()).getDescriptor();
}
private String getDescriptor(Class<?> returnType) {
String name = returnType.getName();
if (!name.startsWith("[")) {
name = GosuShop.toSignature(name);
}
name = name.replace('.', '/');
return name;
}
private String getMethodDescriptor(Method m) {
return getMethodDescriptor(getParameterTypeDescriptors(m), getReturnTypeDescriptor(m));
}
private String getMethodDescriptor( String[] paramTypeDescriptors, String returnTypeDescriptor )
{
StringBuilder sb = new StringBuilder();
sb.append('(');
for (String paramTypeDescriptor : paramTypeDescriptors) {
sb.append(paramTypeDescriptor);
}
sb.append( ')' )
.append( returnTypeDescriptor );
return sb.toString();
}
private String[] getMethodExceptions( Method method )
{
Class<?>[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes == null || exceptionTypes.length == 0) {
return null;
}
String[] exceptionTypeNames = new String[exceptionTypes.length];
for (int i = 0; i < exceptionTypes.length; i++) {
exceptionTypeNames[i] = getSlashName(exceptionTypes[i]);
}
return exceptionTypeNames;
}
private void callMethod( MethodVisitor mv, Method method )
{
int opCode = Modifier.isStatic( method.getModifiers() )
? Opcodes.INVOKESTATIC
: method.getDeclaringClass().isInterface()
? Opcodes.INVOKEINTERFACE
: Opcodes.INVOKEVIRTUAL;
mv.visitMethodInsn( opCode,
getSlashName( method.getDeclaringClass() ),
method.getName(),
getMethodDescriptor( method ) );
}
private static void verify( byte[] bytes )
{
if( VERIFY )
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter( sw );
CheckClassAdapter.verify( new ClassReader( bytes ), false, pw );
String out = sw.toString();
if( out.length() > 0 )
{
System.out.println( out );
}
}
}
@Override
public List<ITypeRef> getSubordinateRefs(String topLevelTypeName) {
FqnCacheNode<WeakReference<AbstractTypeRef>> node = _refByName.getNode( topLevelTypeName );
final List<ITypeRef> types = new ArrayList<ITypeRef>();
if( node != null ) {
node.visitNodeDepthFirst(
new Predicate<FqnCacheNode>() {
public boolean evaluate( FqnCacheNode node ) {
@SuppressWarnings("unchecked")
WeakReference<AbstractTypeRef> ref = (WeakReference<AbstractTypeRef>)node.getUserData();
if( ref != null ) {
AbstractTypeRef typeRef = ref.get();
if( typeRef != null ) {
types.add( typeRef );
}
}
return true;
}
} );
}
return types;
}
public List<String> getTypesWithPrefix(String namespace, final String prefix) {
FqnCacheNode<WeakReference<AbstractTypeRef>> node = _refByName.getNode( namespace );
final List<String> types = new ArrayList<String>();
if( node != null ) {
node.visitNodeDepthFirst(
new Predicate<FqnCacheNode>() {
public boolean evaluate( FqnCacheNode node ) {
if (node.getName().startsWith(prefix)) {
@SuppressWarnings("unchecked")
WeakReference<AbstractTypeRef> ref = (WeakReference<AbstractTypeRef>)node.getUserData();
if( ref != null ) {
AbstractTypeRef typeRef = ref.get();
if( typeRef != null ) {
types.add(node.getFqn());
}
}
}
return true;
}
} );
}
return types;
}
}