ClassFileVisitor.java revision 3169:ef2011e4555a
1/*
2 * Copyright (c) 2013, 2015 Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24import java.io.*;
25import com.sun.tools.classfile.*;
26
27/**
28 * The {@code ClassFileVisitor} reads a class file using the
29 * {@code com.sun.tools.classfile} library. It iterates over the methods
30 * in a class, and checks MethodParameters attributes against JLS
31 * requirements, as well as assumptions about the javac implementations.
32 * <p>
33 * It enforces the following rules:
34 * <ul>
35 * <li>All non-synthetic methods with arguments must have the
36 * MethodParameters attribute. </li>
37 * <li>At most one MethodParameters attribute per method.</li>
38 * <li>An empty MethodParameters attribute is not allowed (i.e. no
39 * attribute for methods taking no parameters).</li>
40 * <li>The number of recorded parameter names much equal the number
41 * of parameters, including any implicit or synthetic parameters generated
42 * by the compiler.</li>
43 * <li>Although the spec allow recording parameters with no name, the javac
44 * implementation is assumed to record a name for all parameters. That is,
45 * the Methodparameters attribute must record a non-zero, valid constant
46 * pool index for each parameter.</li>
47 * <li>Check presence, expected names (e.g. this$N, $enum$name, ...) and flags
48 * (e.g. ACC_SYNTHETIC, ACC_MANDATED) for compiler generated parameters.</li>
49 * <li>Names of explicit parameters must reflect the names in the Java source.
50 * This is checked by assuming a design pattern where any name is permitted
51 * for the first explicit parameter. For subsequent parameters the following
52 * rule is checked: <i>param[n] == ++param[n-1].charAt(0) + param[n-1]</i>
53 * </ul>
54 */
55class ClassFileVisitor extends Tester.Visitor {
56
57    Tester tester;
58
59    public String cname;
60    public boolean isEnum;
61    public boolean isInterface;
62    public boolean isInner;
63    public boolean isPublic;
64    public boolean isStatic;
65    public boolean isAnon;
66    public ClassFile classFile;
67
68
69    public ClassFileVisitor(Tester tester) {
70        super(tester);
71    }
72
73    public void error(String msg) {
74        super.error("classfile: " + msg);
75    }
76
77    public void warn(String msg) {
78        super.warn("classfile: " + msg);
79    }
80
81    /**
82     * Read the class and determine some key characteristics, like if it's
83     * an enum, or inner class, etc.
84     */
85    void visitClass(final String cname, final File cfile, final StringBuilder sb) throws Exception {
86        this.cname = cname;
87        classFile = ClassFile.read(cfile);
88        isEnum = classFile.access_flags.is(AccessFlags.ACC_ENUM);
89        isInterface = classFile.access_flags.is(AccessFlags.ACC_INTERFACE);
90        isPublic = classFile.access_flags.is(AccessFlags.ACC_PUBLIC);
91        isInner = false;
92        isStatic = false;
93        isAnon = false;
94
95        Attribute attr = classFile.getAttribute("InnerClasses");
96        if (attr != null) attr.accept(new InnerClassVisitor(), null);
97        isAnon = isInner & isAnon;
98
99        sb.append(isStatic ? "static " : "")
100            .append(isPublic ? "public " : "")
101            .append(isEnum ? "enum " : isInterface ? "interface " : "class ")
102            .append(cname).append(" -- ");
103        if (isInner) {
104            sb.append(isAnon ? "anon" : "inner");
105        }
106        sb.append("\n");
107
108        for (Method method : classFile.methods) {
109            new MethodVisitor().visitMethod(method, sb);
110        }
111    }
112
113    /**
114     * Used to visit InnerClasses_attribute of a class,
115     * to determne if this class is an local class, and anonymous
116     * inner class or a none-static member class. These types of
117     * classes all have an containing class instances field that
118     * requires an implicit or synthetic constructor argument.
119     */
120    class InnerClassVisitor extends AttributeVisitor<Void, Void> {
121        public Void visitInnerClasses(InnerClasses_attribute iattr, Void v) {
122            try{
123                for (InnerClasses_attribute.Info info : iattr.classes) {
124                    if (info.getInnerClassInfo(classFile.constant_pool) == null) continue;
125                    String in = info.getInnerClassInfo(classFile.constant_pool).getName();
126                    if (in == null || !cname.equals(in)) continue;
127                    isInner = true;
128                    isAnon = null == info.getInnerName(classFile.constant_pool);
129                    isStatic = info.inner_class_access_flags.is(AccessFlags.ACC_STATIC);
130                    break;
131                }
132            } catch(Exception e) {
133                throw new IllegalStateException(e);
134            }
135            return null;
136        }
137    }
138
139    /**
140     * Check the MethodParameters attribute of a method.
141     */
142    class MethodVisitor extends AttributeVisitor<Void, StringBuilder> {
143
144        public String mName;
145        public Descriptor mDesc;
146        public int mParams;
147        public int mAttrs;
148        public int mNumParams;
149        public boolean mSynthetic;
150        public boolean mIsConstructor;
151        public boolean mIsClinit;
152        public boolean mIsBridge;
153        public boolean isFinal;
154        public String prefix;
155
156        void visitMethod(Method method, StringBuilder sb) throws Exception {
157
158            mName = method.getName(classFile.constant_pool);
159            mDesc = method.descriptor;
160            mParams =  mDesc.getParameterCount(classFile.constant_pool);
161            mAttrs = method.attributes.attrs.length;
162            mNumParams = -1; // no MethodParameters attribute found
163            mSynthetic = method.access_flags.is(AccessFlags.ACC_SYNTHETIC);
164            mIsConstructor = mName.equals("<init>");
165            mIsClinit = mName.equals("<clinit>");
166            prefix = cname + "." + mName + "() - ";
167            mIsBridge = method.access_flags.is(AccessFlags.ACC_BRIDGE);
168
169            if (mIsClinit) {
170                sb = new StringBuilder(); // Discard output
171            }
172            sb.append(cname).append(".").append(mName).append("(");
173
174            for (Attribute a : method.attributes) {
175                a.accept(this, sb);
176            }
177            if (mNumParams == -1) {
178                if (mSynthetic) {
179                    // We don't generate MethodParameters attribute for synthetic
180                    // methods, so we are creating a parameter pattern to match
181                    // ReflectionVisitor API output.
182                    for (int i = 0; i < mParams; i++) {
183                        if (i == 0)
184                            sb.append("arg").append(i);
185                        else
186                            sb.append(", arg").append(i);
187                    }
188                    sb.append(")/*synthetic*/");
189                } else {
190                    sb.append(")");
191                }
192            }
193            sb.append("\n");
194
195            // IMPL: methods with arguments must have a MethodParameters
196            // attribute, except possibly some synthetic methods.
197            if (mNumParams == -1 && mParams > 0 && ! mSynthetic) {
198                error(prefix + "missing MethodParameters attribute");
199            }
200        }
201
202        public Void visitMethodParameters(MethodParameters_attribute mp,
203                                          StringBuilder sb) {
204
205            // SPEC: At most one MethodParameters attribute allowed
206            if (mNumParams != -1) {
207                error(prefix + "Multiple MethodParameters attributes");
208                return null;
209            }
210
211            mNumParams = mp.method_parameter_table_length;
212
213            // SPEC: An empty attribute is not allowed!
214            if (mNumParams == 0) {
215                error(prefix + "0 length MethodParameters attribute");
216                return null;
217            }
218
219            // SPEC: one name per parameter.
220            if (mNumParams != mParams) {
221                error(prefix + "found " + mNumParams +
222                      " parameters, expected " + mParams);
223                return null;
224            }
225
226            // IMPL: Whether MethodParameters attributes will be generated
227            // for some synthetics is unresolved. For now, assume no.
228            if (mSynthetic) {
229                warn(prefix + "synthetic has MethodParameter attribute");
230            }
231
232            String sep = "";
233            String userParam = null;
234            for (int x = 0; x <  mNumParams; x++) {
235                isFinal = (mp.method_parameter_table[x].flags & AccessFlags.ACC_FINAL) != 0;
236                // IMPL: Assume all parameters are named, something.
237                int cpi = mp.method_parameter_table[x].name_index;
238                if (cpi == 0) {
239                    error(prefix + "name expected, param[" + x + "]");
240                    return null;
241                }
242
243                // SPEC: a non 0 index, must be valid!
244                String param = null;
245                try {
246                    param = classFile.constant_pool.getUTF8Value(cpi);
247                    if (isFinal)
248                        param = "final " + param;
249                    sb.append(sep).append(param);
250                    sep = ", ";
251                } catch(ConstantPoolException e) {
252                    error(prefix + "invalid index " + cpi + " for param["
253                          + x + "]");
254                    return null;
255                }
256
257
258                // Check availability, flags and special names
259                int check = checkParam(mp, param, x, sb, isFinal);
260                if (check < 0) {
261                    return null;
262                }
263
264                // TEST: check test assumptions about parameter name.
265                // Expected names are calculated starting with the
266                // 2nd explicit (user given) parameter.
267                // param[n] == ++param[n-1].charAt(0) + param[n-1]
268                String expect = null;
269                if (userParam != null) {
270                    char c = userParam.charAt(0);
271                    expect =  (++c) + userParam;
272                }
273                if(isFinal && expect != null)
274                    expect = "final " + expect;
275                if (check > 0) {
276                    if(isFinal) {
277                        userParam = param.substring(6);
278                    } else {
279                    userParam = param;
280                }
281                }
282                if (check > 0 && expect != null && !param.equals(expect)) {
283                    error(prefix + "param[" + x + "]='"
284                          + param + "' expected '" + expect + "'");
285                    return null;
286                }
287            }
288            if (mSynthetic) {
289                sb.append(")/*synthetic*/");
290            } else {
291                sb.append(")");
292            }
293            return null;
294        }
295
296        /*
297         * Check a parameter for conformity to JLS and javac specific
298         * assumptions.
299         * Return -1, if an error is detected. Otherwise, return 0, if
300         * the parameter is compiler generated, or 1 for an (presumably)
301         * explicitly declared parameter.
302         */
303        int checkParam(MethodParameters_attribute mp, String param, int index,
304                       StringBuilder sb, boolean isFinal) {
305
306            boolean synthetic = (mp.method_parameter_table[index].flags
307                                 & AccessFlags.ACC_SYNTHETIC) != 0;
308            boolean mandated = (mp.method_parameter_table[index].flags
309                                & AccessFlags.ACC_MANDATED) != 0;
310
311            // Setup expectations for flags and special names
312            String expect = null;
313            boolean allowMandated = false;
314            boolean allowSynthetic = false;
315            if (mSynthetic || synthetic) {
316                // not an implementation gurantee, but okay for now
317                expect = "arg" + index; // default
318            }
319            if (mIsConstructor) {
320                if (isEnum) {
321                    if (index == 0) {
322                        expect = "\\$enum\\$name";
323                        allowSynthetic = true;
324                    } else if(index == 1) {
325                        expect = "\\$enum\\$ordinal";
326                        allowSynthetic = true;
327                    }
328                } else if (index == 0) {
329                    if (isAnon) {
330                        expect = "this\\$[0-9]+";
331                        allowMandated = true;
332                        if (isFinal) {
333                            expect = "final this\\$[0-9]+";
334                        }
335                    } else if (isInner && !isStatic) {
336                        expect = "this\\$[0-9]+";
337                        allowMandated = true;
338                        if (!isPublic) {
339                            // some but not all non-public inner classes
340                            // have synthetic argument. For now we give
341                            // the test a bit of slack and allow either.
342                            allowSynthetic = true;
343                        }
344                        if (isFinal) {
345                            expect = "final this\\$[0-9]+";
346                        }
347                    }
348                }
349
350                if (synthetic && !mandated && !allowSynthetic) {
351                    //patch treatment for local captures
352                    if (isAnon || (isInner & !isStatic)) {
353                        expect = "val\\$.*";
354                        allowSynthetic = true;
355                        if (isFinal) {
356                            expect = "final val\\$.*";
357                        }
358                    }
359                }
360            } else if (isEnum && mNumParams == 1 && index == 0 && mName.equals("valueOf")) {
361                expect = "name";
362                allowMandated = true;
363            } else if (mIsBridge) {
364                allowSynthetic = true;
365                /*  you can't expect an special name for bridges' parameters.
366                 *  The name of the original parameters are now copied.
367                 */
368                expect = null;
369            }
370            if (mandated) sb.append("/*implicit*/");
371            if (synthetic) sb.append("/*synthetic*/");
372
373            // IMPL: our rules a somewhat fuzzy, sometimes allowing both mandated
374            // and synthetic. However, a parameters cannot be both.
375            if (mandated && synthetic) {
376                error(prefix + "param[" + index + "] == \"" + param
377                      + "\" ACC_SYNTHETIC and ACC_MANDATED");
378                return -1;
379            }
380            // ... but must be either, if both "allowed".
381            if (!(mandated || synthetic) && allowMandated && allowSynthetic) {
382                error(prefix + "param[" + index + "] == \"" + param
383                      + "\" expected ACC_MANDATED or ACC_SYNTHETIC");
384                return -1;
385            }
386
387            // ... if only one is "allowed", we meant "required".
388            if (!mandated && allowMandated && !allowSynthetic) {
389                error(prefix + "param[" + index + "] == \"" + param
390                      + "\" expected ACC_MANDATED");
391                return -1;
392            }
393            if (!synthetic && !allowMandated && allowSynthetic) {
394                error(prefix + "param[" + index + "] == \"" + param
395                      + "\" expected ACC_SYNTHETIC");
396                return -1;
397            }
398
399            // ... and not "allowed", means prohibited.
400            if (mandated && !allowMandated) {
401                error(prefix + "param[" + index + "] == \"" + param
402                      + "\" unexpected, is ACC_MANDATED");
403                return -1;
404            }
405            if (synthetic && !allowSynthetic) {
406                error(prefix + "param[" + index + "] == \"" + param
407                      + "\" unexpected, is ACC_SYNTHETIC");
408                return -1;
409            }
410
411            // Test special name expectations
412            if (expect != null) {
413                if (param.matches(expect)) {
414                    return 0;
415                }
416                error(prefix + "param[" + index + "]='" + param +
417                      "' expected '" + expect + "'");
418                return -1;
419            }
420
421            // No further checking for synthetic methods.
422            if (mSynthetic) {
423                return 0;
424            }
425            // Otherwise, do check test parameter naming convention.
426            return 1;
427        }
428    }
429}
430