/* * Copyright 2013. Guidewire Software, Inc. */ package gw.lang.reflect.java.asm; import gw.internal.ext.org.objectweb.asm.AnnotationVisitor; import gw.internal.ext.org.objectweb.asm.Attribute; import gw.internal.ext.org.objectweb.asm.ClassReader; import gw.internal.ext.org.objectweb.asm.ClassVisitor; import gw.internal.ext.org.objectweb.asm.FieldVisitor; import gw.internal.ext.org.objectweb.asm.MethodVisitor; import gw.internal.ext.org.objectweb.asm.signature.SignatureReader; import gw.lang.reflect.Modifier; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.java.IAsmJavaClassInfo; import gw.lang.reflect.java.IJavaClassInfo; import gw.lang.reflect.module.IModule; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** */ public class AsmClass implements IAsmType, IGeneric { public static final AsmClass BYTE; public static final AsmClass SHORT; public static final AsmClass CHAR; public static final AsmClass INT; public static final AsmClass LONG; public static final AsmClass FLOAT; public static final AsmClass DOUBLE; public static final AsmClass BOOLEAN; public static final AsmClass VOID; private static final Map<String, AsmClass> PRIMITIVES = new HashMap<String, AsmClass>(); static { PRIMITIVES.put( "byte", BYTE = new AsmClass( AsmPrimitiveType.findPrimitive( "byte" ) ) ); PRIMITIVES.put( "short", SHORT = new AsmClass( AsmPrimitiveType.findPrimitive( "short" ) ) ); PRIMITIVES.put( "char", CHAR = new AsmClass( AsmPrimitiveType.findPrimitive( "char" ) ) ); PRIMITIVES.put( "int", INT = new AsmClass( AsmPrimitiveType.findPrimitive( "int" ) ) ); PRIMITIVES.put( "long", LONG = new AsmClass( AsmPrimitiveType.findPrimitive( "long" ) ) ); PRIMITIVES.put( "float", FLOAT = new AsmClass( AsmPrimitiveType.findPrimitive( "float" ) ) ); PRIMITIVES.put( "double", DOUBLE = new AsmClass( AsmPrimitiveType.findPrimitive( "double" ) ) ); PRIMITIVES.put( "boolean", BOOLEAN = new AsmClass( AsmPrimitiveType.findPrimitive( "boolean" ) ) ); PRIMITIVES.put( "void", VOID = new AsmClass( AsmPrimitiveType.findPrimitive( "void" ) ) ); } public static AsmClass findPrimitive( String className ) { return PRIMITIVES.get( className ); } private Object _module; private int _version; private int _modifiers; private AsmType _type; private AsmType _superClass; private List<AsmType> _interfaces; private AsmType _enclosingType; private boolean _bGeneric; private Map<String, AsmInnerClassType> _innerClasses; private List<AsmField> _fields; private List<AsmMethod> _methodsAndCtors; private List<AsmAnnotation> _annotations; AsmClass( Object module, byte[] classBytes ) { _module = module; ClassReader cr = new ClassReader( classBytes ); cr.accept( new AsmClassVisitor(), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES ); } private AsmClass( AsmPrimitiveType ptype ) { _type = ptype; _modifiers = Modifier.PUBLIC | Modifier.STATIC; _superClass = null; _innerClasses = Collections.emptyMap(); _interfaces = Collections.emptyList(); _fields = Collections.emptyList(); _methodsAndCtors = Collections.emptyList(); _annotations = Collections.emptyList(); } public AsmType getType() { return _type; } public AsmType getRawType() { return _type.getRawType(); } public AsmType getEnclosingType() { return _enclosingType; } public AsmType getComponentType() { return null; } public AsmType getSuperClass() { return _superClass; } public Map<String, AsmInnerClassType> getInnerClasses() { return _innerClasses; } public List<AsmType> getInterfaces() { return _interfaces; } public List<AsmField> getDeclaredFields() { return _fields; } private void addField( AsmField field ) { if( _fields.isEmpty() ) { _fields = new ArrayList<AsmField>(); } _fields.add( field ); } public List<AsmMethod> getDeclaredMethodsAndConstructors() { return _methodsAndCtors; } private void addMethod( AsmMethod method ) { if( _methodsAndCtors.isEmpty() ) { _methodsAndCtors = new ArrayList<AsmMethod>(); } _methodsAndCtors.add( method ); } public List<AsmAnnotation> getDeclaredAnnotations() { return _annotations; } private void addAnnotation( AsmAnnotation annotation ) { if( _annotations.isEmpty() ) { _annotations = new ArrayList<AsmAnnotation>(); } _annotations.add( annotation ); } public boolean isGeneric() { return _bGeneric; } public void setGeneric() { _bGeneric = true; } public int getModifiers() { return _modifiers; } public int getVersion() { return _version; } public String toString() { return _type.getName(); } public String getName() { return _type.getName(); } public String getNameWithArrayBrackets() { return _type.getNameWithArrayBrackets(); } @Override public String getSimpleName() { String name = _type.getSimpleName(); int iDollar = _enclosingType == null ? -1 : name.lastIndexOf( '$' ); if( iDollar > 0 ) { name = name.substring( iDollar+1 ); } return name; } @Override public List<AsmType> getTypeParameters() { // Only valid if type params are TYPE VARIABLES return _type.getTypeParameters(); } @Override public boolean isParameterized() { // AsmClass is never parameterized, but can be generic where type params are *type variables* return false; } @Override public boolean isArray() { return false; } @Override public boolean isTypeVariable() { return false; } @Override public boolean isPrimitive() { return _type.isPrimitive(); } @Override public String getFqn() { return _type.getFqn(); } public boolean isInterface() { return Modifier.isInterface( getModifiers() ); } public boolean isEnum() { return Modifier.isEnum( getModifiers() ); } public boolean isAnnotation() { return (getModifiers() & 0x00002000) != 0; } public boolean isAnnotationPresent( Class<? extends Annotation> annotationClass ) { return getAnnotation( annotationClass ) != null; } public AsmAnnotation getAnnotation( Class annotationClass ) { for( AsmAnnotation anno: getDeclaredAnnotations() ) { if( annotationClass.getName().equals( anno.getType().getName() ) ) { return anno; } } return null; } private class AsmClassVisitor implements ClassVisitor { @Override public void visit( int version, int access, String name, String signature, String superName, String[] interfaces ) { _type = AsmUtil.makeType( name ); AsmClass outerClass = ensureOuterIsLoadedFirst(); // barf _version = version; _modifiers = access; if( outerClass != null ) { barf( outerClass ); } _superClass = Modifier.isInterface( access ) ? null : (superName != null ? AsmUtil.makeType( superName ) : null); _innerClasses = Collections.emptyMap(); _fields = Collections.emptyList(); _methodsAndCtors = Collections.emptyList(); _annotations = Collections.emptyList(); assignInterfaces( interfaces ); assignGenericInfo( signature ); } private void barf( AsmClass outerClass ) { AsmInnerClassType innerClass = outerClass.getInnerClasses().get( _type.getName() ); if( innerClass != null ) { _modifiers = innerClass.getModifiers(); } } private AsmClass ensureOuterIsLoadedFirst() { String typeName = _type.getName(); int iDollar = typeName.lastIndexOf( '$' ); if( iDollar > 0 ) { String outerName = typeName.substring( 0, iDollar ); IJavaClassInfo classInfo = TypeSystem.getJavaClassInfo( outerName, (IModule)_module ); if( classInfo != null ) { _enclosingType = AsmUtil.makeType( outerName ); return (AsmClass)((IAsmJavaClassInfo)classInfo).getAsmType(); } } return null; } @Override public void visitSource( String s, String s2 ) { } @Override public void visitOuterClass( String owner, String name, String desc ) { _enclosingType = owner == null ? null : AsmUtil.makeType( owner ); } @Override public AnnotationVisitor visitAnnotation( String desc, boolean bVisibleAtRuntime ) { AsmAnnotation asmAnnotation = new AsmAnnotation( desc, bVisibleAtRuntime ); addAnnotation( asmAnnotation ); return new AsmAnnotationVisitor( asmAnnotation ); } @Override public void visitAttribute( Attribute attribute ) { } @Override public void visitInnerClass( String name, String outerName, String innerName, int access ) { if( outerName != null && !AsmUtil.makeDotName( outerName ).equals( getType().getName() ) ) { return; } if( innerName == null ) { // anonymous return; } int iDollar = name.lastIndexOf( '$' ); if( iDollar >= 0 && iDollar < name.length() - 1 && Character.isDigit( name.charAt( iDollar + 1 ) ) ) { // local inner class return; } if( _innerClasses.isEmpty() ) { _innerClasses = new HashMap<String, AsmInnerClassType>( 2 ); } String innerClass = AsmUtil.makeDotName( name ); _innerClasses.put( innerClass, new AsmInnerClassType( innerClass, access ) ); } @Override public FieldVisitor visitField( int access, String name, String desc, String signature, Object value ) { AsmField field = new AsmField( AsmClass.this, access, name, desc, value ); if( signature != null ) { SignatureReader sr = new SignatureReader( signature ); sr.accept( new FieldSignatureVisitor( field ) ); } addField( field ); return new FieldDeclarationVisitor( field ); } @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions ) { AsmMethod method = new AsmMethod( AsmClass.this, access, name, desc, exceptions ); if( signature != null ) { SignatureReader sr = new SignatureReader( signature ); sr.accept( new MethodDeclarationSignatureVisitor( method, method.getMethodType() ) ); } addMethod( method ); return new MethodDeclarationVisitor( method ); } @Override public void visitEnd() { } private void assignGenericInfo( String signature ) { if( signature != null ) { SignatureReader sr = new SignatureReader( signature ); sr.accept( new TypeDeclarationSignatureVisitor( AsmClass.this, _type ) ); } } private void assignInterfaces( String[] interfaces ) { if( interfaces != null ) { List<AsmType> ifaces = new ArrayList<AsmType>( interfaces.length ); for( int i = 0; i < interfaces.length; i++ ) { ifaces.add( AsmUtil.makeType( interfaces[i] ) ); } _interfaces = ifaces; } else { _interfaces = Collections.emptyList(); } } } }