jdk/make/modules/tools/src/com/sun/classanalyzer/ClassFileParser.java
author ohair
Tue, 07 Sep 2010 15:17:43 -0700
changeset 6326 047748ce0a45
parent 5506 202f599c92aa
permissions -rw-r--r--
6982946: Change make/jprt.properties to defer to JPRT itself for jdk platform list Reviewed-by: kamg

/*
 * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */
package com.sun.classanalyzer;

import com.sun.tools.classfile.*;
import com.sun.tools.classfile.Type.*;
import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
import static com.sun.tools.classfile.AccessFlags.*;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

/**
 *
 * @author Mandy Chung
 */
public class ClassFileParser {

    final Klass this_klass;
    final ClassFile classfile;
    final ConstantPoolParser constantPoolParser;
    final AnnotationParser annotationParser;
    final CodeAttributeParser codeAttributeParser;
    private final boolean buildDeps;

    protected ClassFileParser(InputStream in, long size, boolean buildDeps) throws IOException {
        try {
            this.classfile = ClassFile.read(in);
            this.this_klass = getKlass(this.classfile);
            this.buildDeps = buildDeps;
            this.constantPoolParser = new ConstantPoolParser(this);
            this.annotationParser = new AnnotationParser(this);
            this.codeAttributeParser = new CodeAttributeParser(this);
        } catch (ConstantPoolException ex) {
            throw new RuntimeException(ex);
        }
    }

    private Klass getKlass(ClassFile cf) throws ConstantPoolException {
        Klass k = Klass.getKlass(cf.getName());
        k.setAccessFlags(cf.access_flags.flags);
        k.setFileSize(cf.byteLength());
        return k;
    }

    public static ClassFileParser newParser(InputStream in, long size, boolean buildDeps) throws IOException {
        return new ClassFileParser(in, size, buildDeps);
    }

    public static ClassFileParser newParser(String classPathname, boolean buildDeps) throws IOException {
        return newParser(new File(classPathname), buildDeps);
    }

    public static ClassFileParser newParser(File f, boolean buildDeps) throws IOException {
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
        try {
            return newParser(in, f.length(), buildDeps);
        } finally {
            in.close();
        }
    }

    public void parseDependency(boolean publicAPIs) throws IOException {
        if (publicAPIs && !classfile.access_flags.is(ACC_PUBLIC)) {
            // process public APIs only
            return;
        }

        parseClassInfo();
        if (!publicAPIs) {
            // parse all references in the classfile
            constantPoolParser.parseDependency();
        }
        parseMethods(publicAPIs);
        parseFields(publicAPIs);
    }

    void parseClassInfo() throws IOException {
        ConstantPool cpool = classfile.constant_pool;
        try {
            Signature_attribute sigAttr = (Signature_attribute) classfile.attributes.get(Attribute.Signature);
            if (sigAttr == null) {
                // use info from class file header
                if (classfile.isClass() && classfile.super_class != 0) {
                    String sn = classfile.getSuperclassName();
                    addExtends(sn);
                }
                for (int i = 0; i < classfile.interfaces.length; i++) {
                    String interf = classfile.getInterfaceName(i);
                    if (classfile.isClass()) {
                        addImplements(interf);
                    } else {
                        addExtends(interf);
                    }
                }
            } else {
                Type t = sigAttr.getParsedSignature().getType(cpool);
                // The signature parser cannot disambiguate between a
                // FieldType and a ClassSignatureType that only contains a superclass type.
                if (t instanceof Type.ClassSigType) {
                    Type.ClassSigType cst = Type.ClassSigType.class.cast(t);
                    if (cst.superclassType != null) {
                        for (Klass k : getKlass(cst.superclassType)) {
                            addExtends(k);
                        }
                    }
                    if (cst.superinterfaceTypes != null) {
                        for (Type t1 : cst.superinterfaceTypes) {
                            for (Klass k : getKlass(t1)) {
                                addImplements(k);
                            }
                        }
                    }
                } else {
                    for (Klass k : getKlass(t)) {
                        addExtends(k);
                    }
                }
            }
            // parse attributes
            annotationParser.parseAttributes(classfile.attributes);
        } catch (ConstantPoolException ex) {
            throw new RuntimeException(ex);
        }
    }

    private void parseFields(boolean publicAPIs) throws IOException {
        ConstantPool cpool = classfile.constant_pool;
        for (Field f : classfile.fields) {
            try {
                AccessFlags flags = f.access_flags;
                if (publicAPIs && !flags.is(ACC_PUBLIC) && !flags.is(ACC_PROTECTED)) {
                    continue;
                }
                String fieldname = f.getName(cpool);
                Signature_attribute sigAttr = (Signature_attribute) f.attributes.get(Attribute.Signature);

                if (sigAttr == null) {
                    Set<Klass> types = parseDescriptor(f.descriptor);
                    String info = getFlag(flags) + " " + f.descriptor.getFieldType(cpool) + " " + fieldname;
                    addFieldTypes(types, info, flags);
                } else {
                    Type t = sigAttr.getParsedSignature().getType(cpool);
                    String info = getFlag(flags) + " " + t + " " + fieldname;
                    addFieldTypes(getKlass(t), info, flags);
                }
                // parse attributes
                annotationParser.parseAttributes(f.attributes);
            } catch (ConstantPoolException ex) {
                throw new RuntimeException(ex);
            } catch (InvalidDescriptor ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    private void parseMethods(boolean publicAPIs) {
        for (Method m : classfile.methods) {
            if (publicAPIs && !m.access_flags.is(ACC_PUBLIC) && !m.access_flags.is(ACC_PROTECTED)) {
                // only interest in the API level
                return;
            }

            parseMethod(m);
        }
    }

    String checkClassName(String classname) {
        int i = 0;
        while (i < classname.length()) {
            switch (classname.charAt(i)) {
                case 'Z':
                case 'B':
                case 'C':
                case 'S':
                case 'I':
                case 'J':
                case 'F':
                case 'D':
                    return "";
                case 'L':
                    if (!classname.endsWith(";")) {
                        throw new RuntimeException("Invalid classname " + classname);
                    }
                    return classname.substring(i + 1, classname.length() - 1);
                case '[':
                    i++;
                    break;
                default:
                    if (classname.endsWith(";")) {
                        throw new RuntimeException("Invalid classname " + classname);
                    }
                    return classname;

            }
        }
        throw new RuntimeException("Invalid classname " + classname);
    }

    private void addExtends(String classname) throws IOException {
        if (!buildDeps) {
            return;
        }

        addExtends(Klass.getKlass(classname));
    }

    private void addExtends(Klass k) {
        if (!buildDeps) {
            return;
        }

        ResolutionInfo resInfo = ResolutionInfo.resolvedExtends(this_klass, k);
        resInfo.setPublicAccess(classfile.access_flags.is(ACC_PUBLIC));
        this_klass.addDep(k, resInfo);
        k.addReferrer(this_klass, resInfo);
    }

    private void addImplements(String classname) throws IOException {
        if (!buildDeps) {
            return;
        }

        addImplements(Klass.getKlass(classname));
    }

    private void addImplements(Klass k) {
        if (!buildDeps) {
            return;
        }

        ResolutionInfo resInfo = ResolutionInfo.resolvedImplements(this_klass, k);
        resInfo.setPublicAccess(classfile.access_flags.is(ACC_PUBLIC));

        this_klass.addDep(k, resInfo);

        k.addReferrer(this_klass, resInfo);
    }

    private Set<Klass> getKlass(Type type) throws IOException {
        Set<Klass> refTypes = new TreeSet<Klass>();
        if (!buildDeps) {
            return refTypes;
        }

        type.accept(typevisitor, refTypes);
        return refTypes;
    }
    private Type.Visitor<Void, Set<Klass>> typevisitor = new Type.Visitor<Void, Set<Klass>>() {

        public Void visitSimpleType(SimpleType type, Set<Klass> klasses) {
            // nop
            return null;
        }

        public Void visitArrayType(ArrayType type, Set<Klass> klasses) {
            try {
                klasses.addAll(getKlass(type.elemType));
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            return null;

        }

        public Void visitMethodType(MethodType type, Set<Klass> klasses) {
            throw new InternalError("Unexpected type " + type);
        }

        public Void visitClassSigType(ClassSigType type, Set<Klass> klasses) {
            try {
                if (type.superclassType != null) {
                    klasses.addAll(getKlass(type.superclassType));
                }
                if (type.superinterfaceTypes != null) {
                    for (Type t : type.superinterfaceTypes) {
                        klasses.addAll(getKlass(t));
                    }
                }
                if (type.typeParamTypes != null) {
                    for (Type t : type.typeParamTypes) {
                        klasses.addAll(getKlass(t));
                    }
                }
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            return null;
        }

        public Void visitClassType(ClassType type, Set<Klass> klasses) {
            klasses.add(Klass.getKlass(type.getBinaryName()));
            if (type.typeArgs != null) {
                for (Type t : type.typeArgs) {
                    try {
                        klasses.addAll(getKlass(t));
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
            return null;

        }

        public Void visitTypeParamType(TypeParamType type, Set<Klass> klasses) {
            try {
                if (type.classBound != null) {
                    klasses.addAll(getKlass(type.classBound));
                }
                if (type.interfaceBounds != null) {
                    for (Type t : type.interfaceBounds) {
                        klasses.addAll(getKlass(t));
                    }
                }

            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }

            return null;

        }

        public Void visitWildcardType(WildcardType type, Set<Klass> klasses) {
            if (type.boundType != null) {
                try {
                    klasses.addAll(getKlass(type.boundType));
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
            return null;

        }
    };

    private void printMethod(Method m) {
        try {
            System.out.println("parsing " + m.getName(classfile.constant_pool) + "(" +
                    m.descriptor.getParameterTypes(classfile.constant_pool) + ") return type " +
                    m.descriptor.getReturnType(classfile.constant_pool));

        } catch (ConstantPoolException ex) {
        } catch (InvalidDescriptor ex) {
        }
    }

    private static StringBuilder appendWord(StringBuilder sb, String word) {
        if (sb.length() > 0) {
            sb.append(" ");
        }
        sb.append(word);
        return sb;
    }

    private static String getFlag(AccessFlags flags) {
        StringBuilder modifier = new StringBuilder();
        if (flags.is(ACC_PUBLIC)) {
            modifier.append("public");
        }
        if (flags.is(ACC_PRIVATE)) {
            modifier.append("private");
        }
        if (flags.is(ACC_PROTECTED)) {
            modifier.append("protected");
        }
        if (flags.is(ACC_STATIC)) {
            appendWord(modifier, "static");
        }
        if (flags.is(ACC_FINAL)) {
            appendWord(modifier, "final");
        }
        if (flags.is(ACC_SYNCHRONIZED)) {
            // return "synchronized";
        }
        if (flags.is(0x80)) {
            // return (t == Type.Field ? "transient" : null);
            // return "transient";
        }
        if (flags.is(ACC_VOLATILE)) {
            // return "volatile";
        }
        if (flags.is(ACC_NATIVE)) {
            // return "native";
        }
        if (flags.is(ACC_ABSTRACT)) {
            appendWord(modifier, "abstract");
        }
        if (flags.is(ACC_STRICT)) {
            // return "strictfp";
        }
        if (flags.is(ACC_MODULE)) {
            appendWord(modifier, "module");
        }
        return modifier.toString();
    }

    private Klass.Method toKlassMethod(Method m, Descriptor d) {
        try {
            ConstantPool cpool = classfile.constant_pool;
            String methodname = m.getName(cpool);
            StringBuilder sb = new StringBuilder();
            sb.append(getFlag(m.access_flags));
            if (methodname.equals("<init>")) {
                String s = this_klass.getBasename() + d.getParameterTypes(cpool);
                appendWord(sb, s);
            } else if (methodname.equals("<clinit>")) {
                // <clinit>
                appendWord(sb, methodname);
            } else {
                String s = d.getReturnType(cpool) + " " + methodname + d.getParameterTypes(cpool);
                appendWord(sb, s);
            }
            String signature = sb.toString().replace('/', '.');
            return this_klass.getMethod(methodname, signature);
        } catch (ConstantPoolException ex) {
            throw new RuntimeException(ex);
        } catch (InvalidDescriptor ex) {
            throw new RuntimeException(ex);
        }
    }

    Klass.Method parseMethod(Method m) {
        AccessFlags flags = m.access_flags;
        Descriptor d;
        List<? extends Type> methodExceptions = null;
        try {
            ConstantPool cpool = classfile.constant_pool;
            Klass.Method kmethod;
            Signature_attribute sigAttr = (Signature_attribute) m.attributes.get(Attribute.Signature);
            if (sigAttr == null) {
                d = m.descriptor;
                Set<Klass> types = parseDescriptor(d);

                kmethod = toKlassMethod(m, d);
                addMethodTypes(types, kmethod, flags);
            } else {
                Type.MethodType methodType;
                Signature methodSig = sigAttr.getParsedSignature();
                d = methodSig;
                try {
                    kmethod = toKlassMethod(m, d);
                    methodType = (Type.MethodType) methodSig.getType(cpool);
                    addMethodTypes(getKlass(methodType.returnType), kmethod, flags);
                    if (methodType.paramTypes != null) {
                        for (Type t : methodType.paramTypes) {
                            addMethodTypes(getKlass(t), kmethod, flags);
                        }
                    }
                    if (methodType.typeParamTypes != null) {
                        for (Type t : methodType.typeParamTypes) {
                            addMethodTypes(getKlass(t), kmethod, flags);
                        }
                    }

                    methodExceptions = methodType.throwsTypes;
                    if (methodExceptions != null) {
                        if (methodExceptions.size() == 0) {
                            methodExceptions = null;
                        } else {
                            for (Type t : methodExceptions) {
                                addCheckedExceptionTypes(getKlass(t), kmethod, flags);
                            }
                        }
                    }
                } catch (ConstantPoolException e) {
                    throw new RuntimeException(e);
                }
            }

            Attribute e_attr = m.attributes.get(Attribute.Exceptions);
            if (e_attr != null && methodExceptions == null) {
                // if there are generic exceptions, there must be erased exceptions
                if (e_attr instanceof Exceptions_attribute) {
                    Exceptions_attribute exceptions = (Exceptions_attribute) e_attr;
                    for (int i = 0; i < exceptions.number_of_exceptions; i++) {
                        String classname = checkClassName(exceptions.getException(i, classfile.constant_pool));
                        if (classname.length() > 0 && buildDeps) {
                            Klass to = Klass.getKlass(classname);
                            ResolutionInfo resInfo = ResolutionInfo.resolvedCheckedException(this_klass, to, kmethod);
                            resInfo.setPublicAccess(flags.is(ACC_PUBLIC));

                            this_klass.addDep(to, resInfo);
                            to.addReferrer(this_klass, resInfo);
                        }
                    }
                } else {
                    throw new RuntimeException("Invalid attribute: " + e_attr);
                }
            }

            Code_attribute c_attr = (Code_attribute) m.attributes.get(Attribute.Code);
            if (c_attr != null) {
                codeAttributeParser.parse(c_attr, kmethod);
            }
            kmethod.isAbstract = classfile.access_flags.is(ACC_ABSTRACT);
            kmethod.setCodeLength(m.byteLength());

            // parse annotation attributes
            annotationParser.parseAttributes(m.attributes, kmethod);
            return kmethod;
        } catch (ConstantPoolException ex) {
            throw new RuntimeException(ex);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    private void addFieldTypes(Set<Klass> types, String info, AccessFlags flags) {
        if (types.isEmpty() || !buildDeps) {
            return;
        }

        for (Klass to : types) {
            ResolutionInfo resInfo = ResolutionInfo.resolvedField(this_klass, to, info);
            resInfo.setPublicAccess(flags.is(ACC_PUBLIC));

            this_klass.addDep(to, resInfo);
            to.addReferrer(this_klass, resInfo);
        }
    }

    private void addReferencedTypes(Method m, Descriptor d, AccessFlags flags) {
        Set<Klass> types = parseDescriptor(d);

        Klass.Method method = toKlassMethod(m, d);
        addMethodTypes(types, method, flags);
    }

    private void addMethodTypes(Set<Klass> types, Klass.Method method, AccessFlags flags) {
        if (types.isEmpty() || !buildDeps) {
            return;
        }
        for (Klass to : types) {
            ResolutionInfo resInfo = ResolutionInfo.resolvedMethodSignature(this_klass, to, method);
            resInfo.setPublicAccess(flags.is(ACC_PUBLIC));

            this_klass.addDep(to, resInfo);
            to.addReferrer(this_klass, resInfo);
        }
    }

    private void addCheckedExceptionTypes(Set<Klass> types, Klass.Method method, AccessFlags flags) {
        if (types.isEmpty() || !buildDeps) {
            return;
        }
        for (Klass to : types) {
            ResolutionInfo resInfo = ResolutionInfo.resolvedCheckedException(this_klass, to, method);
            resInfo.setPublicAccess(flags.is(ACC_PUBLIC));

            this_klass.addDep(to, resInfo);
            to.addReferrer(this_klass, resInfo);
        }
    }

    private Set<Klass> parseDescriptor(Descriptor d) {
        Set<Klass> types = new TreeSet<Klass>();
        try {
            String desc = d.getValue(classfile.constant_pool);
            int p = 0;
            while (p < desc.length()) {
                String type;
                char ch;
                switch (ch = desc.charAt(p++)) {
                    case '(':
                    case ')':
                    case '[':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'F':
                    case 'I':
                    case 'J':
                    case 'S':
                    case 'Z':
                    case 'V':
                        continue;
                    case 'L':
                        int sep = desc.indexOf(';', p);
                        if (sep == -1) {
                            throw new RuntimeException("Invalid descriptor: " + (p - 1) + " " + desc);
                        }
                        type = checkClassName(desc.substring(p, sep));
                        p = sep + 1;
                        break;
                    default:
                        throw new RuntimeException("Invalid descriptor: " + (p - 1) + " " + desc);
                }

                if (!type.isEmpty() && buildDeps) {
                    Klass to = Klass.getKlass(type);
                    types.add(to);

                }
            }
        } catch (ConstantPoolException ex) {
            throw new RuntimeException(ex);
        }
        return types;
    }
}