changeset 1025:c55928005af4

7031005: javap prints "extends java.lang.Object" Reviewed-by: mcimadamore
author jjg
date Wed, 30 Mar 2011 18:32:16 -0700
parents 02ba4ff98742
children b945b846c979 28570a737e83
files src/share/classes/com/sun/tools/classfile/Type.java src/share/classes/com/sun/tools/javap/ClassWriter.java test/tools/javap/6937244/T6937244A.java test/tools/javap/T4880663.java test/tools/javap/T4880672.java test/tools/javap/TestSuperclass.java
diffstat 6 files changed, 336 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/classfile/Type.java	Wed Mar 30 18:18:11 2011 -0700
+++ b/src/share/classes/com/sun/tools/classfile/Type.java	Wed Mar 30 18:32:16 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, 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
@@ -41,6 +41,11 @@
  */
 public abstract class Type {
     protected Type() { }
+
+    public boolean isObject() {
+        return false;
+    }
+
     public abstract <R,D> R accept(Visitor<R,D> visitor, D data);
 
     protected static void append(StringBuilder sb, String prefix, List<? extends Type> types, String suffix) {
@@ -262,6 +267,13 @@
             return sb.toString();
         }
 
+        @Override
+        public boolean isObject() {
+            return (outerType == null)
+                    && name.equals("java/lang/Object")
+                    && (typeArgs == null || typeArgs.isEmpty());
+        }
+
         public final ClassType outerType;
         public final String name;
         public final List<Type> typeArgs;
--- a/src/share/classes/com/sun/tools/javap/ClassWriter.java	Wed Mar 30 18:18:11 2011 -0700
+++ b/src/share/classes/com/sun/tools/javap/ClassWriter.java	Wed Mar 30 18:32:16 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2011, 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
@@ -48,6 +48,13 @@
 import com.sun.tools.classfile.Signature_attribute;
 import com.sun.tools.classfile.SourceFile_attribute;
 import com.sun.tools.classfile.Type;
+import com.sun.tools.classfile.Type.ArrayType;
+import com.sun.tools.classfile.Type.ClassSigType;
+import com.sun.tools.classfile.Type.ClassType;
+import com.sun.tools.classfile.Type.MethodType;
+import com.sun.tools.classfile.Type.SimpleType;
+import com.sun.tools.classfile.Type.TypeParamType;
+import com.sun.tools.classfile.Type.WildcardType;
 
 import static com.sun.tools.classfile.AccessFlags.*;
 
@@ -166,8 +173,10 @@
             // use info from class file header
             if (classFile.isClass() && classFile.super_class != 0 ) {
                 String sn = getJavaSuperclassName(cf);
-                print(" extends ");
-                print(sn);
+                if (!sn.equals("java.lang.Object")) {
+                    print(" extends ");
+                    print(sn);
+                }
             }
             for (int i = 0; i < classFile.interfaces.length; i++) {
                 print(i == 0 ? (classFile.isClass() ? " implements " : " extends ") : ",");
@@ -176,13 +185,14 @@
         } else {
             try {
                 Type t = sigAttr.getParsedSignature().getType(constant_pool);
+                JavaTypePrinter p = new JavaTypePrinter(classFile.isInterface());
                 // The signature parser cannot disambiguate between a
                 // FieldType and a ClassSignatureType that only contains a superclass type.
-                if (t instanceof Type.ClassSigType)
-                    print(getJavaName(t.toString()));
-                else {
+                if (t instanceof Type.ClassSigType) {
+                    print(p.print(t));
+                } else if (options.verbose || !t.isObject()) {
                     print(" extends ");
-                    print(getJavaName(t.toString()));
+                    print(p.print(t));
                 }
             } catch (ConstantPoolException e) {
                 print(report(e));
@@ -210,6 +220,124 @@
         indent(-1);
         println("}");
     }
+    // where
+        class JavaTypePrinter implements Type.Visitor<StringBuilder,StringBuilder> {
+            boolean isInterface;
+
+            JavaTypePrinter(boolean isInterface) {
+                this.isInterface = isInterface;
+            }
+
+            String print(Type t) {
+                return t.accept(this, new StringBuilder()).toString();
+            }
+
+            public StringBuilder visitSimpleType(SimpleType type, StringBuilder sb) {
+                sb.append(getJavaName(type.name));
+                return sb;
+            }
+
+            public StringBuilder visitArrayType(ArrayType type, StringBuilder sb) {
+                append(sb, type.elemType);
+                sb.append("[]");
+                return sb;
+            }
+
+            public StringBuilder visitMethodType(MethodType type, StringBuilder sb) {
+                appendIfNotEmpty(sb, "<", type.typeParamTypes, "> ");
+                append(sb, type.returnType);
+                append(sb, " (", type.paramTypes, ")");
+                appendIfNotEmpty(sb, " throws ", type.throwsTypes, "");
+                return sb;
+            }
+
+            public StringBuilder visitClassSigType(ClassSigType type, StringBuilder sb) {
+                appendIfNotEmpty(sb, "<", type.typeParamTypes, ">");
+                if (isInterface) {
+                    appendIfNotEmpty(sb, " extends ", type.superinterfaceTypes, "");
+                } else {
+                    if (type.superclassType != null
+                            && (options.verbose || !type.superclassType.isObject())) {
+                        sb.append(" extends ");
+                        append(sb, type.superclassType);
+                    }
+                    appendIfNotEmpty(sb, " implements ", type.superinterfaceTypes, "");
+                }
+                return sb;
+            }
+
+            public StringBuilder visitClassType(ClassType type, StringBuilder sb) {
+                if (type.outerType != null) {
+                    append(sb, type.outerType);
+                    sb.append(".");
+                }
+                sb.append(getJavaName(type.name));
+                appendIfNotEmpty(sb, "<", type.typeArgs, ">");
+                return sb;
+            }
+
+            public StringBuilder visitTypeParamType(TypeParamType type, StringBuilder sb) {
+                sb.append(type.name);
+                String sep = " extends ";
+                if (type.classBound != null
+                        && (options.verbose || !type.classBound.isObject())) {
+                    sb.append(sep);
+                    append(sb, type.classBound);
+                    sep = " & ";
+                }
+                if (type.interfaceBounds != null) {
+                    for (Type bound: type.interfaceBounds) {
+                        sb.append(sep);
+                        append(sb, bound);
+                        sep = " & ";
+                    }
+                }
+                return sb;
+            }
+
+            public StringBuilder visitWildcardType(WildcardType type, StringBuilder sb) {
+                switch (type.kind) {
+                    case UNBOUNDED:
+                        sb.append("?");
+                        break;
+                    case EXTENDS:
+                        sb.append("? extends ");
+                        append(sb, type.boundType);
+                        break;
+                    case SUPER:
+                        sb.append("? super ");
+                        append(sb, type.boundType);
+                        break;
+                    default:
+                        throw new AssertionError();
+                }
+                return sb;
+            }
+
+            private void append(StringBuilder sb, Type t) {
+                t.accept(this, sb);
+            }
+
+            private void append(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
+                sb.append(prefix);
+                String sep = "";
+                for (Type t: list) {
+                    sb.append(sep);
+                    append(sb, t);
+                    sep = ", ";
+                }
+                sb.append(suffix);
+            }
+
+            private void appendIfNotEmpty(StringBuilder sb, String prefix, List<? extends Type> list, String suffix) {
+                if (!isEmpty(list))
+                    append(sb, prefix, list, suffix);
+            }
+
+            private boolean isEmpty(List<? extends Type> list) {
+                return (list == null || list.isEmpty());
+            }
+        }
 
     protected void writeFields() {
         for (Field f: classFile.fields) {
@@ -298,7 +426,7 @@
             try {
                 methodType = (Type.MethodType) methodSig.getType(constant_pool);
                 methodExceptions = methodType.throwsTypes;
-                if (methodExceptions != null && methodExceptions.size() == 0)
+                if (methodExceptions != null && methodExceptions.isEmpty())
                     methodExceptions = null;
             } catch (ConstantPoolException e) {
                 // report error?
--- a/test/tools/javap/6937244/T6937244A.java	Wed Mar 30 18:18:11 2011 -0700
+++ b/test/tools/javap/6937244/T6937244A.java	Wed Mar 30 18:32:16 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, 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
@@ -49,8 +49,8 @@
         int count = 0;
 
         for (String line: out.split("[\r\n]+")) {
-            if (line.contains("extends")) {
-                verify(line, "extends java.lang.Object implements java.util.List<java.lang.String>");
+            if (line.contains("implements")) {
+                verify(line, "implements java.util.List<java.lang.String>");
                 count++;
             }
 
--- a/test/tools/javap/T4880663.java	Wed Mar 30 18:18:11 2011 -0700
+++ b/test/tools/javap/T4880663.java	Wed Mar 30 18:32:16 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 4880663 6715757
+ * @bug 4880663 6715757 7031005
  * @summary javap could output whitespace between class name and opening brace
  *          javap prints "extends java.lang.Object"
  */
@@ -39,7 +39,7 @@
     public void run() throws IOException {
         File javaFile = writeTestFile();
         File classFile = compileTestFile(javaFile);
-        verify(classFile, "class Test extends java.lang.Object {");
+        verify(classFile, "class Test {");
 
         if (errors > 0)
             throw new Error(errors + " found.");
--- a/test/tools/javap/T4880672.java	Wed Mar 30 18:18:11 2011 -0700
+++ b/test/tools/javap/T4880672.java	Wed Mar 30 18:32:16 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, 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
@@ -24,7 +24,7 @@
 
 /*
  * @test
- * @bug 4880672
+ * @bug 4880672 7031005
  * @summary javap does not output inner interfaces of an interface
  */
 
@@ -39,7 +39,7 @@
 
     void run() {
         verify("java.util.Map", "public interface java.util.Map$Entry");
-        verify("T4880672", "class T4880672$A$B extends java.lang.Object");
+        verify("T4880672", "class T4880672$A$B");
         verify("C", ""); // must not give error if no InnerClasses attribute
         if (errors > 0)
             throw new Error(errors + " found.");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javap/TestSuperclass.java	Wed Mar 30 18:32:16 2011 -0700
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+/*
+ * @test
+ * @bug 7031005
+ * @summary javap prints "extends java.lang.Object"
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Arrays;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaCompiler.CompilationTask;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+public class TestSuperclass {
+    enum ClassKind {
+        CLASS("class"),
+        INTERFACE("interface");
+        ClassKind(String keyword) {
+            this.keyword = keyword;
+        }
+        final String keyword;
+    }
+
+    enum GenericKind {
+        NO(""),
+        YES("<T>");
+        GenericKind(String typarams) {
+            this.typarams = typarams;
+        }
+        final String typarams;
+    }
+
+    enum SuperKind {
+        NONE(null),
+        SUPER("Super");
+        SuperKind(String name) {
+            this.name = name;
+        }
+        String extend() {
+            return (name == null) ? "" : "extends " + name;
+        }
+        String decl(ClassKind ck) {
+            return (name == null) ? "" : ck.keyword + " " + name + " { }";
+        }
+        final String name;
+    }
+
+    public static void main(String... args) throws Exception {
+        JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
+        int errors = 0;
+
+        for (ClassKind ck: ClassKind.values()) {
+            for (GenericKind gk: GenericKind.values()) {
+                for (SuperKind sk: SuperKind.values()) {
+                    errors += new TestSuperclass(ck, gk, sk).run(comp, fm);
+                }
+            }
+        }
+
+        if (errors > 0)
+            throw new Exception(errors + " errors found");
+    }
+
+    final ClassKind ck;
+    final GenericKind gk;
+    final SuperKind sk;
+
+    TestSuperclass(ClassKind ck, GenericKind gk, SuperKind sk) {
+        this.ck = ck;
+        this.gk = gk;
+        this.sk = sk;
+    }
+
+    int run(JavaCompiler comp, StandardJavaFileManager fm) throws IOException {
+        System.err.println("test: ck:" + ck + " gk:" + gk + " sk:" + sk);
+        File testDir = new File(ck + "-" + gk + "-" + sk);
+        testDir.mkdirs();
+        fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(testDir));
+
+        JavaSource js = new JavaSource();
+        System.err.println(js.getCharContent(false));
+        CompilationTask t = comp.getTask(null, fm, null, null, null, Arrays.asList(js));
+        if (!t.call())
+            throw new Error("compilation failed");
+
+        File testClass = new File(testDir, "Test.class");
+        String out = javap(testClass);
+
+        // Extract class sig from first line of Java source
+        String expect = js.source.replaceAll("(?s)^(.* Test[^{]+?) *\\{.*", "$1");
+
+        // Extract class sig from line from javap output
+        String found = out.replaceAll("(?s).*\n(.* Test[^{]+?) *\\{.*", "$1");
+
+        checkEqual("class signature", expect, found);
+
+        return errors;
+    }
+
+    String javap(File file) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        String[] args = { file.getPath() };
+        int rc = com.sun.tools.javap.Main.run(args, pw);
+        pw.close();
+        String out = sw.toString();
+        if (!out.isEmpty())
+            System.err.println(out);
+        if (rc != 0)
+            throw new Error("javap failed: rc=" + rc);
+        return out;
+    }
+
+    void checkEqual(String label, String expect, String found) {
+        if (!expect.equals(found))
+            error("Unexpected " + label + " found: '" + found + "', expected: '" + expect + "'");
+    }
+
+    void error(String msg) {
+        System.err.println("Error: " + msg);
+        errors++;
+    }
+
+    int errors;
+
+    class JavaSource extends SimpleJavaFileObject {
+        static final String template =
+                  "#CK Test#GK #EK { }\n"
+                + "#SK\n";
+        final String source;
+
+        public JavaSource() {
+            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+            source = template
+                    .replace("#CK", ck.keyword)
+                    .replace("#GK", gk.typarams)
+                    .replace("#EK", sk.extend())
+                    .replace("#SK", sk.decl(ck));
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+    }
+
+}