[Clirr-devel] CVS: clirr/core/src/java/net/sf/clirr/core/internal/asm ClassInfoCollector.java,NONE,1
Status: Alpha
Brought to you by:
lkuehne
From: <lk...@us...> - 2006-03-16 22:30:32
|
Update of /cvsroot/clirr/clirr/core/src/java/net/sf/clirr/core/internal/asm In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6735/src/java/net/sf/clirr/core/internal/asm Added Files: ClassInfoCollector.java package.html AsmMethod.java AsmTypeArrayBuilder.java AsmField.java AbstractAsmScoped.java AsmJavaType.java Repository.java Log Message: Replaced BCEL with ASM. This lays the groundwork for the Java5 RFEs and also fixes bug 1373831, which was caused by a bug in BCEL. As an added bonus, the uberjar file size drops by several hundred KB. --- NEW FILE --- package net.sf.clirr.core.internal.asm; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.commons.EmptyVisitor; /** * An ASM class visitor that collects the information clirr needs in a JavaType. * @author lk * */ class ClassInfoCollector extends ClassAdapter { private AsmJavaType javaType; private final Repository repository; ClassInfoCollector(Repository repository) { super(new EmptyVisitor()); this.repository = repository; } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { final String className = prettyprintClassName(name); final String superClassName = prettyprintClassName(superName); final String[] interfaceNames = prettyprintClassNames(interfaces); javaType = new AsmJavaType(repository, access, className, superClassName, interfaceNames); } public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { Type type = Type.getType(desc); final AsmField asmField = new AsmField(repository, access, name, value, type); javaType.addField(asmField); // currently no need for visiting annotations return null; } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { final Type[] argumentTypes = Type.getArgumentTypes(desc); final Type returnType = Type.getReturnType(desc); final AsmMethod asmMethod = new AsmMethod(repository, access, returnType, name, argumentTypes, exceptions); javaType.addMethod(asmMethod); // currently no need for visiting annotations return null; } public void visitInnerClass(String name, String outerName, String innerName, int access) { super.visitInnerClass(name, outerName, innerName, access); } private static String prettyprintClassName(final String internal) { if (internal == null) { return null; } return internal.replaceAll("/", "."); } private static String[] prettyprintClassNames(String[] internal) { String[] ret = new String[internal.length]; for (int i = 0; i < internal.length; i++) { ret[i] = prettyprintClassName(internal[i]); } return ret; } public AsmJavaType getJavaType() { return javaType; } } --- NEW FILE --- <html> <body> ObjectWeb's <a target="_top" href="http://asm.objectweb.org">ASM</a> is currently used to read the compiled Java classes. </body> </html> --- NEW FILE --- package net.sf.clirr.core.internal.asm; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Method; import net.sf.clirr.core.spi.Scope; public class AsmMethod extends AbstractAsmScoped implements Method { private final Repository repository; private final Type returnType; private final String name; private final Type[] argumentTypes; private final String[] exceptions; AsmMethod(Repository repository, int access, Type returnType, String name, Type[] argumentTypes, String[] exceptions) { super(access); this.repository = repository; this.returnType = returnType; this.name = name; this.argumentTypes = argumentTypes; this.exceptions = exceptions; } public JavaType getReturnType() { if (Type.VOID_TYPE.equals(returnType)) { return null; } return repository.findTypeByName(returnType.getClassName()); } public JavaType[] getArgumentTypes() { // TODO support primitive types JavaType[] ret = new JavaType[argumentTypes.length]; for (int i = 0; i < ret.length; i++) { final String className = argumentTypes[i].getClassName(); ret[i] = repository.findTypeByName(className); } return ret; } public JavaType[] getDeclaredExceptions() { JavaType[] ret = new JavaType[exceptions.length]; for (int i = 0; i < ret.length; i++) { ret[i] = repository.findTypeByName(exceptions[i]); } return ret; } public boolean isFinal() { return checkFlag(Opcodes.ACC_FINAL); } public boolean isStatic() { return checkFlag(Opcodes.ACC_STATIC); } public boolean isAbstract() { return checkFlag(Opcodes.ACC_ABSTRACT); } public boolean isDeprecated() { return checkFlag(Opcodes.ACC_DEPRECATED); } public String getName() { return name; } public Scope getEffectiveScope() { // TODO Auto-generated method stub return getDeclaredScope(); } } --- NEW FILE --- package net.sf.clirr.core.internal.asm; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import net.sf.clirr.core.CheckerException; import net.sf.clirr.core.ClassFilter; import net.sf.clirr.core.ClassSelector; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.TypeArrayBuilderSupport; public class AsmTypeArrayBuilder extends TypeArrayBuilderSupport { public AsmTypeArrayBuilder() { } public JavaType[] createClassSet(File[] jarFiles, ClassLoader thirdPartyClasses, ClassFilter classSelector) throws CheckerException { if (classSelector == null) { // create a class selector that selects all classes classSelector = new ClassSelector(ClassSelector.MODE_UNLESS); } ClassLoader classLoader = createClassLoader(jarFiles, thirdPartyClasses); Repository repository = new Repository(classLoader); List selected = new ArrayList(); for (int i = 0; i < jarFiles.length; i++) { File jarFile = jarFiles[i]; ZipFile zip = null; try { zip = new ZipFile(jarFile, ZipFile.OPEN_READ); } catch (IOException ex) { throw new CheckerException( "Cannot open " + jarFile + " for reading", ex); } Enumeration enumEntries = zip.entries(); while (enumEntries.hasMoreElements()) { ZipEntry zipEntry = (ZipEntry) enumEntries.nextElement(); if (!zipEntry.isDirectory() && zipEntry.getName().endsWith(".class")) { final AsmJavaType javaType = extractClass(repository, zipEntry, zip); if (classSelector.isSelected(javaType)) { selected.add(javaType); } } } } JavaType[] ret = new JavaType[selected.size()]; selected.toArray(ret); return ret; } private AsmJavaType extractClass( Repository repository, ZipEntry zipEntry, ZipFile zip) throws CheckerException { String name = zipEntry.getName(); InputStream is = null; try { is = zip.getInputStream(zipEntry); return repository.readJavaTypeFromStream(is); } catch (IOException ex) { throw new CheckerException( "Cannot read " + zipEntry.getName() + " from " + zip.getName(), ex); } finally { if (is != null) { try { is.close(); } catch (IOException ex) { throw new CheckerException("Cannot close " + zip.getName(), ex); } } } } } --- NEW FILE --- package net.sf.clirr.core.internal.asm; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import net.sf.clirr.core.spi.Field; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Scope; class AsmField extends AbstractAsmScoped implements Field { private final String name; private final Object value; private final Type type; private final Repository repository; AsmField(Repository repository, int access, String name, Object value, Type type) { super(access); this.repository = repository; this.name = name; this.value = value; this.type = type; } public JavaType getType() { // todo: handle primitive values and arrays return repository.findTypeByName(type.getClassName()); } public boolean isFinal() { return checkFlag(Opcodes.ACC_FINAL); } public boolean isStatic() { return checkFlag(Opcodes.ACC_STATIC); } public boolean isDeprecated() { return checkFlag(Opcodes.ACC_DEPRECATED); } public Object getConstantValue() { return value; } public String getName() { return name; } public Scope getEffectiveScope() { return getDeclaredScope(); // TODO: FIXME } } --- NEW FILE --- package net.sf.clirr.core.internal.asm; import org.objectweb.asm.Opcodes; import net.sf.clirr.core.spi.Scope; import net.sf.clirr.core.spi.Scoped; abstract class AbstractAsmScoped implements Scoped { private final int access; AbstractAsmScoped(int access) { this.access = access; } public Scope getDeclaredScope() { if (checkFlag(Opcodes.ACC_PRIVATE)) { return Scope.PRIVATE; } else if (checkFlag(Opcodes.ACC_PROTECTED)) { return Scope.PROTECTED; } else if (checkFlag(Opcodes.ACC_PUBLIC)) { return Scope.PUBLIC; } return Scope.PACKAGE; } /** * @return whether access field has mask set */ protected boolean checkFlag(int mask) { return (access & mask) != 0; } } --- NEW FILE --- package net.sf.clirr.core.internal.asm; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.Opcodes; import net.sf.clirr.core.spi.Field; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Method; import net.sf.clirr.core.spi.Scope; public class AsmJavaType extends AbstractAsmScoped implements JavaType { private final Repository repository; private final String name; private String superClassName; private final List fields = new ArrayList(); private final List methods = new ArrayList(); private final String[] interfaceNames; public AsmJavaType(Repository repository, int access, String name, String superClassName, String[] interfaceNames) { super(access); this.repository = repository; this.name = name; this.superClassName = superClassName; this.interfaceNames = interfaceNames; } public String getBasicName() { // TODO handle array types correctly return name; } public String getName() { return name; } public JavaType getContainingClass() { // TODO Auto-generated method stub return null; } public JavaType[] getSuperClasses() { if (superClassName == null) { return new JavaType[0]; } JavaType superType = repository.findTypeByName(superClassName); final JavaType[] superSuper = superType.getSuperClasses(); JavaType[] ret = new JavaType[superSuper.length + 1]; System.arraycopy(superSuper, 0, ret, 0, superSuper.length); ret[superSuper.length] = superType; return ret; } public JavaType[] getAllInterfaces() { JavaType[] ret = new JavaType[interfaceNames.length]; for (int i = 0; i < ret.length; i++) { ret[i] = repository.findTypeByName(interfaceNames[i]); } return ret; } public JavaType[] getInnerClasses() { // TODO Auto-generated method stub return null; } void addMethod(Method method) { methods.add(method); } public Method[] getMethods() { Method[] ret = new Method[methods.size()]; methods.toArray(ret); return ret; } void addField(Field field) { fields.add(field); } public Field[] getFields() { Field[] ret = new Field[fields.size()]; fields.toArray(ret); return ret; } public boolean isPrimitive() { // TODO Auto-generated method stub return false; } public int getArrayDimension() { // TODO: handle correctly for method argument and return types return 0; } public boolean isFinal() { return checkFlag(Opcodes.ACC_FINAL); } public boolean isAbstract() { return checkFlag(Opcodes.ACC_ABSTRACT); } public boolean isInterface() { return checkFlag(Opcodes.ACC_INTERFACE); } public Scope getEffectiveScope() { // TODO: replace with real impl return getDeclaredScope(); } public String toString() { return "AsmJavaType[" + name + "]"; } } --- NEW FILE --- package net.sf.clirr.core.internal.asm; import java.io.IOException; import java.io.InputStream; import java.net.URLClassLoader; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sf.clirr.core.spi.Field; import net.sf.clirr.core.spi.JavaType; import net.sf.clirr.core.spi.Method; import net.sf.clirr.core.spi.Scope; import org.objectweb.asm.ClassReader; /** * Stores all known JavaTypes, used to implement crossreferences between types. * * @author lkuehne */ class Repository { private static final Pattern PRIMITIVE_PATTERN = Pattern.compile("(int|float|long|double|boolean|char|short)(\\[\\])*"); private static final class PrimitiveType implements JavaType { private final String basicName; private final int dimension; private PrimitiveType(String name, int dimension) { this.basicName = name; this.dimension = dimension; } public String getBasicName() { return basicName; } public String getName() { String name = basicName; for (int i = 0; i < getArrayDimension(); i++) { name += "[]"; } return basicName; } public JavaType getContainingClass() { return null; } public JavaType[] getSuperClasses() { return new JavaType[0]; } public JavaType[] getAllInterfaces() { return new JavaType[0]; } public JavaType[] getInnerClasses() { return new JavaType[0]; } public Method[] getMethods() { return new Method[0]; } public Field[] getFields() { return new Field[0]; } public int getArrayDimension() { return dimension; } public boolean isPrimitive() { return true; } public boolean isFinal() { return true; } public boolean isAbstract() { return false; } public boolean isInterface() { return false; } public Scope getDeclaredScope() { return Scope.PUBLIC; } public Scope getEffectiveScope() { // TODO Auto-generated method stub return null; } public String toString() { return getName(); } } private final ClassLoader classLoader; private Map nameTypeMap = new HashMap(); public Repository(ClassLoader classLoader) { this.classLoader = classLoader; } /** * @param is * @return * @throws IOException */ AsmJavaType readJavaTypeFromStream(InputStream is) throws IOException { ClassReader parser = new ClassReader(is); ClassInfoCollector infoCollector = new ClassInfoCollector(this); parser.accept(infoCollector, true); final AsmJavaType javaType = infoCollector.getJavaType(); nameTypeMap.put(javaType.getName(), javaType); return javaType; } public JavaType findTypeByName(String typeName) { JavaType type = (JavaType) nameTypeMap.get(typeName); if (type != null) { return type; } final Matcher matcher = PRIMITIVE_PATTERN.matcher(typeName); if (matcher.matches()) { final String basicType = matcher.group(1); final String arrayBrackets = matcher.group(2); final int dimension = arrayBrackets == null ? 0 : arrayBrackets.length() / 2; JavaType primitive = new PrimitiveType(basicType, dimension); nameTypeMap.put(typeName, primitive); return primitive; } String resourceName = typeName.replace('.', '/') + ".class"; InputStream is = classLoader.getResourceAsStream(resourceName); if (is == null) { String clDetails; if (classLoader instanceof URLClassLoader) { URLClassLoader ucl = (URLClassLoader) classLoader; clDetails = String.valueOf(Arrays.asList(ucl.getURLs())); } else { clDetails = String.valueOf(classLoader); } throw new IllegalArgumentException("Type " + typeName + " is unknown in classLoader " + clDetails); } try { return readJavaTypeFromStream(is); } catch (IOException ex) { throw new IllegalStateException(); } finally { try { is.close(); } catch (IOException e) { } } } } |