6889255: javac MethodSymbol throws NPE if ClassReader does not read parameter names correctly jdk7-b75

Mon, 19 Oct 2009 13:38:09 -0700

author
jjg
date
Mon, 19 Oct 2009 13:38:09 -0700
changeset 428
2485f5641ed0
parent 427
6ba399eff2cb
child 429
c8083dc525b6
child 432
a491ad1bb624

6889255: javac MethodSymbol throws NPE if ClassReader does not read parameter names correctly
Reviewed-by: darcy

src/share/classes/com/sun/tools/javac/code/Symbol.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/jvm/ClassReader.java file | annotate | diff | comparison | revisions
test/tools/javac/6889255/T6889255.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java	Fri Oct 16 12:56:50 2009 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java	Mon Oct 19 13:38:09 2009 -0700
     1.3 @@ -1212,25 +1212,58 @@
     1.4          public List<VarSymbol> params() {
     1.5              owner.complete();
     1.6              if (params == null) {
     1.7 -                List<Name> names = savedParameterNames;
     1.8 +                // If ClassReader.saveParameterNames has been set true, then
     1.9 +                // savedParameterNames will be set to a list of names that
    1.10 +                // matches the types in type.getParameterTypes().  If any names
    1.11 +                // were not found in the class file, those names in the list will
    1.12 +                // be set to the empty name.
    1.13 +                // If ClassReader.saveParameterNames has been set false, then
    1.14 +                // savedParameterNames will be null.
    1.15 +                List<Name> paramNames = savedParameterNames;
    1.16                  savedParameterNames = null;
    1.17 -                if (names == null) {
    1.18 -                    names = List.nil();
    1.19 -                    int i = 0;
    1.20 -                    for (Type t : type.getParameterTypes())
    1.21 -                        names = names.prepend(name.table.fromString("arg" + i++));
    1.22 -                    names = names.reverse();
    1.23 -                }
    1.24 +                // discard the provided names if the list of names is the wrong size.
    1.25 +                if (paramNames == null || paramNames.size() != type.getParameterTypes().size())
    1.26 +                    paramNames = List.nil();
    1.27                  ListBuffer<VarSymbol> buf = new ListBuffer<VarSymbol>();
    1.28 +                List<Name> remaining = paramNames;
    1.29 +                // assert: remaining and paramNames are both empty or both
    1.30 +                // have same cardinality as type.getParameterTypes()
    1.31 +                int i = 0;
    1.32                  for (Type t : type.getParameterTypes()) {
    1.33 -                    buf.append(new VarSymbol(PARAMETER, names.head, t, this));
    1.34 -                    names = names.tail;
    1.35 +                    Name paramName;
    1.36 +                    if (remaining.isEmpty()) {
    1.37 +                        // no names for any parameters available
    1.38 +                        paramName = createArgName(i, paramNames);
    1.39 +                    } else {
    1.40 +                        paramName = remaining.head;
    1.41 +                        remaining = remaining.tail;
    1.42 +                        if (paramName.isEmpty()) {
    1.43 +                            // no name for this specific parameter
    1.44 +                            paramName = createArgName(i, paramNames);
    1.45 +                        }
    1.46 +                    }
    1.47 +                    buf.append(new VarSymbol(PARAMETER, paramName, t, this));
    1.48 +                    i++;
    1.49                  }
    1.50                  params = buf.toList();
    1.51              }
    1.52              return params;
    1.53          }
    1.54  
    1.55 +        // Create a name for the argument at position 'index' that is not in
    1.56 +        // the exclude list. In normal use, either no names will have been
    1.57 +        // provided, in which case the exclude list is empty, or all the names
    1.58 +        // will have been provided, in which case this method will not be called.
    1.59 +        private Name createArgName(int index, List<Name> exclude) {
    1.60 +            String prefix = "arg";
    1.61 +            while (true) {
    1.62 +                Name argName = name.table.fromString(prefix + index);
    1.63 +                if (!exclude.contains(argName))
    1.64 +                    return argName;
    1.65 +                prefix += "$";
    1.66 +            }
    1.67 +        }
    1.68 +
    1.69          public Symbol asMemberOf(Type site, Types types) {
    1.70              return new MethodSymbol(flags_field, name, types.memberType(site, this), owner);
    1.71          }
     2.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Fri Oct 16 12:56:50 2009 -0700
     2.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Mon Oct 19 13:38:09 2009 -0700
     2.3 @@ -29,6 +29,7 @@
     2.4  import java.net.URI;
     2.5  import java.net.URISyntaxException;
     2.6  import java.nio.CharBuffer;
     2.7 +import java.util.Arrays;
     2.8  import java.util.EnumSet;
     2.9  import java.util.HashMap;
    2.10  import java.util.Map;
    2.11 @@ -191,6 +192,16 @@
    2.12       */
    2.13      boolean debugJSR308;
    2.14  
    2.15 +    /** A table to hold the constant pool indices for method parameter
    2.16 +     * names, as given in LocalVariableTable attributes.
    2.17 +     */
    2.18 +    int[] parameterNameIndices;
    2.19 +
    2.20 +    /**
    2.21 +     * Whether or not any parameter names have been found.
    2.22 +     */
    2.23 +    boolean haveParameterNameIndices;
    2.24 +
    2.25      /** Get the ClassReader instance for this invocation. */
    2.26      public static ClassReader instance(Context context) {
    2.27          ClassReader instance = context.get(classReaderKey);
    2.28 @@ -922,32 +933,33 @@
    2.29                  void read(Symbol sym, int attrLen) {
    2.30                      int newbp = bp + attrLen;
    2.31                      if (saveParameterNames) {
    2.32 -                        // pick up parameter names from the variable table
    2.33 -                        List<Name> parameterNames = List.nil();
    2.34 -                        int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0;
    2.35 -                        int endParam = firstParam + Code.width(sym.type.getParameterTypes());
    2.36 +                        // Pick up parameter names from the variable table.
    2.37 +                        // Parameter names are not explicitly identified as such,
    2.38 +                        // but all parameter name entries in the LocalVariableTable
    2.39 +                        // have a start_pc of 0.  Therefore, we record the name
    2.40 +                        // indicies of all slots with a start_pc of zero in the
    2.41 +                        // parameterNameIndicies array.
    2.42 +                        // Note that this implicitly honors the JVMS spec that
    2.43 +                        // there may be more than one LocalVariableTable, and that
    2.44 +                        // there is no specified ordering for the entries.
    2.45                          int numEntries = nextChar();
    2.46 -                        for (int i=0; i<numEntries; i++) {
    2.47 +                        for (int i = 0; i < numEntries; i++) {
    2.48                              int start_pc = nextChar();
    2.49                              int length = nextChar();
    2.50                              int nameIndex = nextChar();
    2.51                              int sigIndex = nextChar();
    2.52                              int register = nextChar();
    2.53 -                            if (start_pc == 0 &&
    2.54 -                                firstParam <= register &&
    2.55 -                                register < endParam) {
    2.56 -                                int index = firstParam;
    2.57 -                                for (Type t : sym.type.getParameterTypes()) {
    2.58 -                                    if (index == register) {
    2.59 -                                        parameterNames = parameterNames.prepend(readName(nameIndex));
    2.60 -                                        break;
    2.61 -                                    }
    2.62 -                                    index += Code.width(t);
    2.63 +                            if (start_pc == 0) {
    2.64 +                                // ensure array large enough
    2.65 +                                if (register >= parameterNameIndices.length) {
    2.66 +                                    int newSize = Math.max(register, parameterNameIndices.length + 8);
    2.67 +                                    parameterNameIndices =
    2.68 +                                            Arrays.copyOf(parameterNameIndices, newSize);
    2.69                                  }
    2.70 +                                parameterNameIndices[register] = nameIndex;
    2.71 +                                haveParameterNameIndices = true;
    2.72                              }
    2.73                          }
    2.74 -                        parameterNames = parameterNames.reverse();
    2.75 -                        ((MethodSymbol)sym).savedParameterNames = parameterNames;
    2.76                      }
    2.77                      bp = newbp;
    2.78                  }
    2.79 @@ -1839,6 +1851,8 @@
    2.80                                        syms.methodClass);
    2.81          }
    2.82          MethodSymbol m = new MethodSymbol(flags, name, type, currentOwner);
    2.83 +        if (saveParameterNames)
    2.84 +            initParameterNames(m);
    2.85          Symbol prevOwner = currentOwner;
    2.86          currentOwner = m;
    2.87          try {
    2.88 @@ -1846,9 +1860,90 @@
    2.89          } finally {
    2.90              currentOwner = prevOwner;
    2.91          }
    2.92 +        if (saveParameterNames)
    2.93 +            setParameterNames(m, type);
    2.94          return m;
    2.95      }
    2.96  
    2.97 +    /**
    2.98 +     * Init the parameter names array.
    2.99 +     * Parameter names are currently inferred from the names in the
   2.100 +     * LocalVariableTable attributes of a Code attribute.
   2.101 +     * (Note: this means parameter names are currently not available for
   2.102 +     * methods without a Code attribute.)
   2.103 +     * This method initializes an array in which to store the name indexes
   2.104 +     * of parameter names found in LocalVariableTable attributes. It is
   2.105 +     * slightly supersized to allow for additional slots with a start_pc of 0.
   2.106 +     */
   2.107 +    void initParameterNames(MethodSymbol sym) {
   2.108 +        // make allowance for synthetic parameters.
   2.109 +        final int excessSlots = 4;
   2.110 +        int expectedParameterSlots =
   2.111 +                Code.width(sym.type.getParameterTypes()) + excessSlots;
   2.112 +        if (parameterNameIndices == null
   2.113 +                || parameterNameIndices.length < expectedParameterSlots) {
   2.114 +            parameterNameIndices = new int[expectedParameterSlots];
   2.115 +        } else
   2.116 +            Arrays.fill(parameterNameIndices, 0);
   2.117 +        haveParameterNameIndices = false;
   2.118 +    }
   2.119 +
   2.120 +    /**
   2.121 +     * Set the parameter names for a symbol from the name index in the
   2.122 +     * parameterNameIndicies array. The type of the symbol may have changed
   2.123 +     * while reading the method attributes (see the Signature attribute).
   2.124 +     * This may be because of generic information or because anonymous
   2.125 +     * synthetic parameters were added.   The original type (as read from
   2.126 +     * the method descriptor) is used to help guess the existence of
   2.127 +     * anonymous synthetic parameters.
   2.128 +     * On completion, sym.savedParameter names will either be null (if
   2.129 +     * no parameter names were found in the class file) or will be set to a
   2.130 +     * list of names, one per entry in sym.type.getParameterTypes, with
   2.131 +     * any missing names represented by the empty name.
   2.132 +     */
   2.133 +    void setParameterNames(MethodSymbol sym, Type jvmType) {
   2.134 +        // if no names were found in the class file, there's nothing more to do
   2.135 +        if (!haveParameterNameIndices)
   2.136 +            return;
   2.137 +
   2.138 +        int firstParam = ((sym.flags() & STATIC) == 0) ? 1 : 0;
   2.139 +        // the code in readMethod may have skipped the first parameter when
   2.140 +        // setting up the MethodType. If so, we make a corresponding allowance
   2.141 +        // here for the position of the first parameter.  Note that this
   2.142 +        // assumes the skipped parameter has a width of 1 -- i.e. it is not
   2.143 +        // a double width type (long or double.)
   2.144 +        if (sym.name == names.init && currentOwner.hasOuterInstance()) {
   2.145 +            // Sometimes anonymous classes don't have an outer
   2.146 +            // instance, however, there is no reliable way to tell so
   2.147 +            // we never strip this$n
   2.148 +            if (!currentOwner.name.isEmpty())
   2.149 +                firstParam += 1;
   2.150 +        }
   2.151 +
   2.152 +        if (sym.type != jvmType) {
   2.153 +            // reading the method attributes has caused the symbol's type to
   2.154 +            // be changed. (i.e. the Signature attribute.)  This may happen if
   2.155 +            // there are hidden (synthetic) parameters in the descriptor, but
   2.156 +            // not in the Signature.  The position of these hidden parameters
   2.157 +            // is unspecified; for now, assume they are at the beginning, and
   2.158 +            // so skip over them. The primary case for this is two hidden
   2.159 +            // parameters passed into Enum constructors.
   2.160 +            int skip = Code.width(jvmType.getParameterTypes())
   2.161 +                    - Code.width(sym.type.getParameterTypes());
   2.162 +            firstParam += skip;
   2.163 +        }
   2.164 +        List<Name> paramNames = List.nil();
   2.165 +        int index = firstParam;
   2.166 +        for (Type t: sym.type.getParameterTypes()) {
   2.167 +            int nameIdx = (index < parameterNameIndices.length
   2.168 +                    ? parameterNameIndices[index] : 0);
   2.169 +            Name name = nameIdx == 0 ? names.empty : readName(nameIdx);
   2.170 +            paramNames = paramNames.prepend(name);
   2.171 +            index += Code.width(t);
   2.172 +        }
   2.173 +        sym.savedParameterNames = paramNames.reverse();
   2.174 +    }
   2.175 +
   2.176      /** Skip a field or method
   2.177       */
   2.178      void skipMember() {
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/test/tools/javac/6889255/T6889255.java	Mon Oct 19 13:38:09 2009 -0700
     3.3 @@ -0,0 +1,485 @@
     3.4 +/*
     3.5 + * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
     3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.7 + *
     3.8 + * This code is free software; you can redistribute it and/or modify it
     3.9 + * under the terms of the GNU General Public License version 2 only, as
    3.10 + * published by the Free Software Foundation.
    3.11 + *
    3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    3.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    3.15 + * version 2 for more details (a copy is included in the LICENSE file that
    3.16 + * accompanied this code).
    3.17 + *
    3.18 + * You should have received a copy of the GNU General Public License version
    3.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    3.21 + *
    3.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    3.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
    3.24 + * have any questions.
    3.25 + */
    3.26 +
    3.27 +/*
    3.28 + * @test
    3.29 + * @bug 6889255
    3.30 + * @summary ClassReader does not read parameter names correctly
    3.31 + */
    3.32 +
    3.33 +import java.io.*;
    3.34 +import java.util.*;
    3.35 +import javax.tools.StandardLocation;
    3.36 +import com.sun.tools.javac.code.Flags;
    3.37 +import com.sun.tools.javac.code.Kinds;
    3.38 +import com.sun.tools.javac.code.Scope;
    3.39 +import com.sun.tools.javac.code.Symbol.*;
    3.40 +import com.sun.tools.javac.code.Type;
    3.41 +import com.sun.tools.javac.code.Type.ClassType;
    3.42 +import com.sun.tools.javac.code.TypeTags;
    3.43 +import com.sun.tools.javac.file.JavacFileManager;
    3.44 +import com.sun.tools.javac.jvm.ClassReader;
    3.45 +import com.sun.tools.javac.util.Context;
    3.46 +import com.sun.tools.javac.util.Names;
    3.47 +
    3.48 +public class T6889255 {
    3.49 +    boolean testInterfaces = true;
    3.50 +    boolean testSyntheticMethods = true;
    3.51 +
    3.52 +    // The following enums control the generation of the test methods to be compiled.
    3.53 +    enum GenericKind {
    3.54 +        NOT_GENERIC,
    3.55 +        GENERIC
    3.56 +    };
    3.57 +
    3.58 +    enum ClassKind {
    3.59 +        CLASS("Clss"),
    3.60 +        INTERFACE("Intf"),
    3.61 +        ENUM("Enum");
    3.62 +        final String base;
    3.63 +        ClassKind(String base) { this.base = base; }
    3.64 +    };
    3.65 +
    3.66 +    enum NestedKind {
    3.67 +        /** Declare methods inside the outermost container. */
    3.68 +        NONE,
    3.69 +        /** Declare methods inside a container with a 'static' modifier. */
    3.70 +        NESTED,
    3.71 +        /** Declare methods inside a container without a 'static' modifier. */
    3.72 +        INNER,
    3.73 +        /** Declare methods inside a local class in an initializer. */
    3.74 +        INIT_LOCAL,
    3.75 +        /** Declare methods inside an anonymous class in an initializer. */
    3.76 +        INIT_ANON,
    3.77 +        /** Declare methods inside a local class in a method. */
    3.78 +        METHOD_LOCAL,
    3.79 +        /** Declare methods inside an anonymous class in a method. */
    3.80 +        METHOD_ANON
    3.81 +    };
    3.82 +
    3.83 +    enum MethodKind {
    3.84 +        ABSTRACT,
    3.85 +        CONSTRUCTOR,
    3.86 +        METHOD,
    3.87 +        STATIC_METHOD,
    3.88 +        BRIDGE_METHOD
    3.89 +    };
    3.90 +
    3.91 +    enum FinalKind {
    3.92 +        /** Method body does not reference external final variables. */
    3.93 +        NO_FINAL,
    3.94 +        /** Method body references external final variables. */
    3.95 +        USE_FINAL
    3.96 +    };
    3.97 +
    3.98 +    public static void main(String... args) throws Exception {
    3.99 +        new T6889255().run();
   3.100 +    }
   3.101 +
   3.102 +    void run() throws Exception {
   3.103 +        genTest();
   3.104 +
   3.105 +        test("no-args", false);
   3.106 +        test("g",       true,  "-g");
   3.107 +
   3.108 +        if (errors > 0)
   3.109 +            throw new Exception(errors + " errors found");
   3.110 +    }
   3.111 +
   3.112 +    /**
   3.113 +     * Create a file containing lots of method definitions to be tested.
   3.114 +     * There are 3 sets of nested loops that generate the methods.
   3.115 +     * 1. The outermost set declares [generic] (class | interface | enum)
   3.116 +     * 2. The middle set declares [(nested | inner | anon | local)] class
   3.117 +     * 3. The innermost set declares
   3.118 +     *      [generic] (constructor|method|static-method|bridge-method) [using final variables in outer scope]
   3.119 +     * Invalid combinations are filtered out.
   3.120 +     */
   3.121 +    void genTest() throws Exception {
   3.122 +        BufferedWriter out = new BufferedWriter(new FileWriter("Test.java"));
   3.123 +
   3.124 +        // This interface is used to force bridge methods to be generated, by
   3.125 +        // implementing its methods with subtypes of Object
   3.126 +        out.write("interface Base {\n");
   3.127 +        out.write("    Object base_m1(int i1);\n");
   3.128 +        out.write("    Object base_m2(int i1);\n");
   3.129 +        out.write("}\n");
   3.130 +
   3.131 +        int outerNum = 0;
   3.132 +        // Outermost set of loops, to generate a top level container
   3.133 +        for (GenericKind outerGenericKind: GenericKind.values()) {
   3.134 +            for (ClassKind outerClassKind: ClassKind.values()) {
   3.135 +                if (outerGenericKind == GenericKind.GENERIC && outerClassKind == ClassKind.ENUM)
   3.136 +                    continue;
   3.137 +                String outerClassName = outerClassKind.base + (outerNum++);
   3.138 +                String outerTypeArg = outerClassKind.toString().charAt(0) + "T";
   3.139 +                if (outerClassKind == ClassKind.CLASS)
   3.140 +                    out.write("abstract ");
   3.141 +                out.write(outerClassKind.toString().toLowerCase() + " " + outerClassName);
   3.142 +                if (outerGenericKind == GenericKind.GENERIC)
   3.143 +                    out.write("<" + outerTypeArg + ">");
   3.144 +                if (outerClassKind == ClassKind.INTERFACE)
   3.145 +                    out.write(" extends Base");
   3.146 +                else
   3.147 +                    out.write(" implements Base");
   3.148 +                out.write(" {\n");
   3.149 +                if (outerClassKind == ClassKind.ENUM) {
   3.150 +                    out.write("    E1(0,0,0), E2(0,0,0), E3(0,0,0);\n");
   3.151 +                    out.write("    " + outerClassName + "(int i1, int i2, int i3) { }\n");
   3.152 +                }
   3.153 +                // Middle set of loops, to generate an optional nested container
   3.154 +                int nestedNum = 0;
   3.155 +                int methodNum = 0;
   3.156 +                for (GenericKind nestedGenericKind: GenericKind.values()) {
   3.157 +                    nextNestedKind:
   3.158 +                    for (NestedKind nestedKind: NestedKind.values()) {
   3.159 +                        // if the nested kind is none, there is no point iterating over all
   3.160 +                        // nested generic kinds, so arbitarily limit it to just one kind
   3.161 +                        if (nestedKind == NestedKind.NONE && nestedGenericKind != GenericKind.NOT_GENERIC)
   3.162 +                            continue;
   3.163 +                        if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON)
   3.164 +                                && nestedGenericKind == GenericKind.GENERIC)
   3.165 +                            continue;
   3.166 +                        String indent = "    ";
   3.167 +                        boolean haveFinal = false;
   3.168 +                        switch (nestedKind) {
   3.169 +                            case METHOD_ANON: case METHOD_LOCAL:
   3.170 +                                if (outerClassKind == ClassKind.INTERFACE)
   3.171 +                                    continue nextNestedKind;
   3.172 +                                out.write(indent + "void m" +  + (nestedNum++) + "() {\n");
   3.173 +                                indent += "    ";
   3.174 +                                out.write(indent + "final int fi1 = 0;\n");
   3.175 +                                haveFinal = true;
   3.176 +                                break;
   3.177 +                            case INIT_ANON: case INIT_LOCAL:
   3.178 +                                if (outerClassKind == ClassKind.INTERFACE)
   3.179 +                                    continue nextNestedKind;
   3.180 +                                out.write(indent + "{\n");
   3.181 +                                indent += "    ";
   3.182 +                                break;
   3.183 +                        }
   3.184 +                        for (ClassKind nestedClassKind: ClassKind.values()) {
   3.185 +                            if ((nestedGenericKind == GenericKind.GENERIC)
   3.186 +                                    && (nestedClassKind == ClassKind.ENUM))
   3.187 +                                continue;
   3.188 +                            if ((nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.METHOD_LOCAL
   3.189 +                                    || nestedKind == NestedKind.INIT_ANON || nestedKind == NestedKind.INIT_LOCAL)
   3.190 +                                    && nestedClassKind != ClassKind.CLASS)
   3.191 +                                continue;
   3.192 +                            // if the nested kind is none, there is no point iterating over all
   3.193 +                            // nested class kinds, so arbitarily limit it to just one kind
   3.194 +                            if (nestedKind == NestedKind.NONE && nestedClassKind != ClassKind.CLASS)
   3.195 +                                continue;
   3.196 +
   3.197 +                            ClassKind methodClassKind;
   3.198 +                            String methodClassName;
   3.199 +                            boolean allowAbstractMethods;
   3.200 +                            boolean allowStaticMethods;
   3.201 +                            switch (nestedKind) {
   3.202 +                                case NONE:
   3.203 +                                    methodClassKind = outerClassKind;
   3.204 +                                    methodClassName = outerClassName;
   3.205 +                                    allowAbstractMethods = (outerClassKind == ClassKind.CLASS);
   3.206 +                                    allowStaticMethods = (outerClassKind != ClassKind.INTERFACE);
   3.207 +                                    break;
   3.208 +                                case METHOD_ANON:
   3.209 +                                case INIT_ANON:
   3.210 +                                    out.write(indent + "new Base() {\n");
   3.211 +                                    indent += "    ";
   3.212 +                                    methodClassKind = ClassKind.CLASS;
   3.213 +                                    methodClassName = null;
   3.214 +                                    allowAbstractMethods = false;
   3.215 +                                    allowStaticMethods = false;
   3.216 +                                    break;
   3.217 +                                default: { // INNER, NESTED, LOCAL
   3.218 +                                    String nestedClassName = "N" + nestedClassKind.base + (nestedNum++);
   3.219 +                                    String nestedTypeArg = nestedClassKind.toString().charAt(0) + "T";
   3.220 +                                    out.write(indent);
   3.221 +                                    if (nestedKind == NestedKind.NESTED)
   3.222 +                                        out.write("static ");
   3.223 +                                    if (nestedClassKind == ClassKind.CLASS)
   3.224 +                                        out.write("abstract ");
   3.225 +                                    out.write(nestedClassKind.toString().toLowerCase() + " " + nestedClassName);
   3.226 +                                    if (nestedGenericKind == GenericKind.GENERIC)
   3.227 +                                        out.write("<" + nestedTypeArg + ">");
   3.228 +                                    if (nestedClassKind == ClassKind.INTERFACE)
   3.229 +                                        out.write(" extends Base ");
   3.230 +                                    else
   3.231 +                                        out.write(" implements Base ");
   3.232 +                                    out.write(" {\n");
   3.233 +                                    indent += "    ";
   3.234 +                                    if (nestedClassKind == ClassKind.ENUM) {
   3.235 +                                        out.write(indent + "E1(0,0,0), E2(0,0,0), E3(0,0,0);\n");
   3.236 +                                        out.write(indent + nestedClassName + "(int i1, int i2, int i3) { }\n");
   3.237 +                                    }
   3.238 +                                    methodClassKind = nestedClassKind;
   3.239 +                                    methodClassName = nestedClassName;
   3.240 +                                    allowAbstractMethods = (nestedClassKind == ClassKind.CLASS);
   3.241 +                                    allowStaticMethods = (nestedKind == NestedKind.NESTED && nestedClassKind != ClassKind.INTERFACE);
   3.242 +                                    break;
   3.243 +                                }
   3.244 +                            }
   3.245 +
   3.246 +                            // Innermost loops, to generate methods
   3.247 +                            for (GenericKind methodGenericKind: GenericKind.values()) {
   3.248 +                                for (FinalKind finalKind: FinalKind.values()) {
   3.249 +                                    for (MethodKind methodKind: MethodKind.values()) {
   3.250 +//                                        out.write("// " + outerGenericKind
   3.251 +//                                                + " " + outerClassKind
   3.252 +//                                                + " " + nestedKind
   3.253 +//                                                + " " + nestedGenericKind
   3.254 +//                                                + " " + nestedClassKind
   3.255 +//                                                + " " + methodGenericKind
   3.256 +//                                                + " " + finalKind
   3.257 +//                                                + " " + methodKind
   3.258 +//                                                + "\n");
   3.259 +                                        switch (methodKind) {
   3.260 +                                            case CONSTRUCTOR:
   3.261 +                                                if (nestedKind == NestedKind.METHOD_ANON || nestedKind == NestedKind.INIT_ANON)
   3.262 +                                                    break;
   3.263 +                                                if (methodClassKind != ClassKind.CLASS)
   3.264 +                                                    break;
   3.265 +                                                if (finalKind == FinalKind.USE_FINAL && !haveFinal)
   3.266 +                                                    break;
   3.267 +                                                out.write(indent);
   3.268 +                                                if (methodGenericKind == GenericKind.GENERIC) {
   3.269 +                                                    out.write("<CT> " + methodClassName + "(CT c1, CT c2");
   3.270 +                                                } else {
   3.271 +                                                    out.write(methodClassName + "(boolean b1, char c2");
   3.272 +                                                }
   3.273 +                                                if (finalKind == FinalKind.USE_FINAL) {
   3.274 +                                                    // add a dummy parameter to avoid duplicate declaration
   3.275 +                                                    out.write(", int i3) { int i = fi1; }\n");
   3.276 +                                                } else
   3.277 +                                                    out.write(") { }\n");
   3.278 +                                                break;
   3.279 +                                            case ABSTRACT:
   3.280 +                                                if (!allowAbstractMethods)
   3.281 +                                                    continue;
   3.282 +                                                // fallthrough
   3.283 +                                            case METHOD:
   3.284 +                                                if (finalKind == FinalKind.USE_FINAL && !haveFinal)
   3.285 +                                                    break;
   3.286 +                                                out.write(indent);
   3.287 +                                                if (methodKind == MethodKind.ABSTRACT)
   3.288 +                                                    out.write("abstract ");
   3.289 +                                                if (methodGenericKind == GenericKind.GENERIC)
   3.290 +                                                    out.write("<MT> ");
   3.291 +                                                out.write("void m" + (methodNum++) + "(int i1, long l2, float f3)");
   3.292 +                                                if (methodKind == MethodKind.ABSTRACT || methodClassKind == ClassKind.INTERFACE)
   3.293 +                                                    out.write(";\n");
   3.294 +                                                else {
   3.295 +                                                    out.write(" {");
   3.296 +                                                    if (finalKind == FinalKind.USE_FINAL)
   3.297 +                                                        out.write(" int i = fi1;");
   3.298 +                                                    out.write(" }\n");
   3.299 +                                                }
   3.300 +                                                break;
   3.301 +                                            case BRIDGE_METHOD:
   3.302 +                                                if (methodGenericKind == GenericKind.GENERIC)
   3.303 +                                                    break;
   3.304 +                                                out.write(indent);
   3.305 +                                                // methods Base.base_m1 and Base.base_m2 are declared for the
   3.306 +                                                // benefit of bridge methods. They need to be implemented
   3.307 +                                                // whether or not a final variable is used.
   3.308 +                                                String methodName = (finalKind == FinalKind.NO_FINAL ? "base_m1" : "base_m2");
   3.309 +                                                out.write("public String " + methodName + "(int i1)");
   3.310 +                                                if (methodClassKind == ClassKind.INTERFACE)
   3.311 +                                                    out.write(";\n");
   3.312 +                                                else {
   3.313 +                                                    out.write(" {");
   3.314 +                                                    if (finalKind == FinalKind.USE_FINAL && haveFinal)
   3.315 +                                                        out.write(" int i = fi1;");
   3.316 +                                                    out.write(" return null; }\n");
   3.317 +                                                }
   3.318 +                                                break;
   3.319 +                                            case STATIC_METHOD:
   3.320 +                                                if (!allowStaticMethods)
   3.321 +                                                    break;
   3.322 +                                                if (finalKind == FinalKind.USE_FINAL && !haveFinal)
   3.323 +                                                    break;
   3.324 +                                                out.write(indent + "static ");
   3.325 +                                                if (methodGenericKind == GenericKind.GENERIC)
   3.326 +                                                    out.write("<MT> ");
   3.327 +                                                out.write("void m" + (methodNum++) + "(int i1, long l2, float f3) {");
   3.328 +                                                if (finalKind == FinalKind.USE_FINAL)
   3.329 +                                                    out.write(" int i = fi1;");
   3.330 +                                                out.write(" }\n");
   3.331 +                                                break;
   3.332 +                                        }
   3.333 +
   3.334 +                                    }
   3.335 +                                }
   3.336 +                            }
   3.337 +                            if (nestedKind != NestedKind.NONE) {
   3.338 +                                indent = indent.substring(0, indent.length() - 4);
   3.339 +                                out.write(indent + "};\n");
   3.340 +                            }
   3.341 +                        }
   3.342 +                        switch (nestedKind) {
   3.343 +                            case METHOD_ANON: case METHOD_LOCAL:
   3.344 +                            case INIT_ANON: case INIT_LOCAL:
   3.345 +                                indent = indent.substring(0, indent.length() - 4);
   3.346 +                                out.write(indent + "}\n\n");
   3.347 +                        }
   3.348 +                    }
   3.349 +                }
   3.350 +                out.write("}\n\n");
   3.351 +            }
   3.352 +        }
   3.353 +        out.close();
   3.354 +    }
   3.355 +
   3.356 +
   3.357 +    void test(String testName, boolean expectNames, String... opts) throws Exception {
   3.358 +        System.err.println("Test " + testName
   3.359 +                + ": expectNames:" + expectNames
   3.360 +                + " javacOpts:" + Arrays.asList(opts));
   3.361 +
   3.362 +        File outDir = new File(testName);
   3.363 +        outDir.mkdirs();
   3.364 +        compile(outDir, opts);
   3.365 +
   3.366 +        Context ctx = new Context();
   3.367 +        JavacFileManager fm = new JavacFileManager(ctx, true, null);
   3.368 +        fm.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(outDir));
   3.369 +        ClassReader cr = ClassReader.instance(ctx);
   3.370 +        cr.saveParameterNames = true;
   3.371 +        Names names = Names.instance(ctx);
   3.372 +
   3.373 +        Set<String> classes = getTopLevelClasses(outDir);
   3.374 +        Deque<String> work = new LinkedList<String>(classes);
   3.375 +        String classname;
   3.376 +        while ((classname = work.poll()) != null) {
   3.377 +            System.err.println("Checking class " + classname);
   3.378 +            ClassSymbol sym = cr.enterClass(names.table.fromString(classname));
   3.379 +            sym.complete();
   3.380 +
   3.381 +            if ((sym.flags() & Flags.INTERFACE) != 0 && !testInterfaces)
   3.382 +                continue;
   3.383 +
   3.384 +            for (Scope.Entry e = sym.members_field.elems; e != null; e = e.sibling) {
   3.385 +                System.err.println("Checking member " + e.sym);
   3.386 +                switch (e.sym.kind) {
   3.387 +                    case Kinds.TYP: {
   3.388 +                        String name = e.sym.flatName().toString();
   3.389 +                        if (!classes.contains(name)) {
   3.390 +                            classes.add(name);
   3.391 +                            work.add(name);
   3.392 +                        }
   3.393 +                        break;
   3.394 +                    }
   3.395 +                    case Kinds.MTH:
   3.396 +                        verify((MethodSymbol) e.sym, expectNames);
   3.397 +                        break;
   3.398 +                }
   3.399 +
   3.400 +            }
   3.401 +        }
   3.402 +    }
   3.403 +
   3.404 +    void verify(MethodSymbol m, boolean expectNames) {
   3.405 +        if ((m.flags() & Flags.SYNTHETIC) != 0 && !testSyntheticMethods)
   3.406 +            return;
   3.407 +
   3.408 +        //System.err.println("verify: " + m.params());
   3.409 +        int i = 1;
   3.410 +        for (VarSymbol v: m.params()) {
   3.411 +            String expectName;
   3.412 +            if (expectNames)
   3.413 +                expectName = getExpectedName(v, i);
   3.414 +            else
   3.415 +                expectName = "arg" + (i - 1);
   3.416 +            checkEqual(expectName, v.name.toString());
   3.417 +            i++;
   3.418 +        }
   3.419 +    }
   3.420 +
   3.421 +    String getExpectedName(VarSymbol v, int i) {
   3.422 +        // special cases:
   3.423 +        // synthetic method
   3.424 +        if (((v.owner.owner.flags() & Flags.ENUM) != 0)
   3.425 +                && v.owner.name.toString().equals("valueOf"))
   3.426 +            return "name";
   3.427 +        // interfaces don't have saved names
   3.428 +        // -- no Code attribute for the LocalVariableTable attribute
   3.429 +        if ((v.owner.owner.flags() & Flags.INTERFACE) != 0)
   3.430 +            return "arg" + (i - 1);
   3.431 +        // abstract methods don't have saved names
   3.432 +        // -- no Code attribute for the LocalVariableTable attribute
   3.433 +        if ((v.owner.flags() & Flags.ABSTRACT) != 0)
   3.434 +            return "arg" + (i - 1);
   3.435 +        // bridge methods use xN
   3.436 +        if ((v.owner.flags() & Flags.BRIDGE) != 0)
   3.437 +            return "x" + (i - 1);
   3.438 +
   3.439 +        // The rest of this method assumes the local conventions in the test program
   3.440 +        Type t = v.type;
   3.441 +        String s;
   3.442 +        if (t.tag == TypeTags.CLASS)
   3.443 +            s = ((ClassType) t).tsym.name.toString();
   3.444 +        else
   3.445 +            s = t.toString();
   3.446 +        return String.valueOf(Character.toLowerCase(s.charAt(0))) + i;
   3.447 +    }
   3.448 +
   3.449 +    void compile(File outDir, String... opts) throws Exception {
   3.450 +        //File testSrc = new File(System.getProperty("test.src"), ".");
   3.451 +        List<String> args = new ArrayList<String>();
   3.452 +        args.add("-d");
   3.453 +        args.add(outDir.getPath());
   3.454 +        args.addAll(Arrays.asList(opts));
   3.455 +        //args.add(new File(testSrc, "Test.java").getPath());
   3.456 +        args.add("Test.java");
   3.457 +        StringWriter sw = new StringWriter();
   3.458 +        PrintWriter pw = new PrintWriter(sw);
   3.459 +        int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
   3.460 +        pw.close();
   3.461 +        if (rc != 0) {
   3.462 +            System.err.println(sw.toString());
   3.463 +            throw new Exception("compilation failed unexpectedly");
   3.464 +        }
   3.465 +    }
   3.466 +
   3.467 +    Set<String> getTopLevelClasses(File outDir) {
   3.468 +        Set<String> classes = new HashSet<String>();
   3.469 +        for (String f: outDir.list()) {
   3.470 +            if (f.endsWith(".class") && !f.contains("$"))
   3.471 +                classes.add(f.replace(".class", ""));
   3.472 +        }
   3.473 +        return classes;
   3.474 +    }
   3.475 +
   3.476 +    void checkEqual(String expect, String found) {
   3.477 +        if (!expect.equals(found))
   3.478 +            error("mismatch: expected:" + expect + " found:" + found);
   3.479 +    }
   3.480 +
   3.481 +    void error(String msg) {
   3.482 +        System.err.println(msg);
   3.483 +        errors++;
   3.484 +        throw new Error();
   3.485 +    }
   3.486 +
   3.487 +    int errors;
   3.488 +}

mercurial