ReflectionVisitor.java revision 2787:441711fd360d
1/*
2 * Copyright (c) 2013, 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 java.lang.reflect.*;
26
27/**
28 * Test MethodParameter attributs by reflection API
29 */
30public class ReflectionVisitor extends Tester.Visitor {
31
32    public ReflectionVisitor(Tester tester) {
33        super(tester);
34    }
35
36    public void error(String msg) {
37        super.error("reflection: " + msg);
38    }
39
40    public void warn(String msg) {
41        super.warn("reflection: " + msg);
42    }
43
44    boolean isEnum;
45    boolean isInterface;
46    boolean isAnon;
47    boolean isLocal;
48    boolean isMember;
49    boolean isStatic;
50    boolean isPublic;
51    boolean isFinal;
52    Class clazz;
53    StringBuilder sb;
54
55    /**
56     * Read class using {@code ClassFile}, and generate a list of methods
57     * with parameter names as available in the MethodParameters attribute.
58     */
59    void visitClass(final String cl, final File cfile, final StringBuilder sb)
60        throws Exception {
61
62        this.sb = sb;
63        clazz = Class.forName(cl);
64        isEnum = clazz.isEnum();
65        isInterface = clazz.isInterface();
66        isAnon = clazz.isAnonymousClass();
67        isLocal = clazz.isLocalClass();
68        isMember = clazz.isMemberClass();
69        isStatic = ((clazz.getModifiers() & Modifier.STATIC) != 0);
70        isPublic = ((clazz.getModifiers() & Modifier.PUBLIC) != 0);
71
72        sb.append(isStatic ? "static " : "")
73            .append(isPublic ? "public " : "")
74            .append(isEnum ? "enum " : isInterface ? "interface " : "class ")
75            .append(cl).append(" -- ")
76            .append(isMember? "inner" : "" )
77            .append(isLocal? "inner" : "" )
78            .append(isAnon ?  "anon" : "")
79            .append("\n");
80
81        for (Constructor c : clazz.getDeclaredConstructors()) {
82            testConstructor(c);
83        }
84
85        for (Method m :clazz.getDeclaredMethods()) {
86            testMethod(m);
87        }
88    }
89
90    void testConstructor(Constructor c) {
91
92        String prefix = clazz.getName() + "." + c.getName() + "() - ";
93
94        // Parameters must match parameter types
95        Parameter params[] = c.getParameters();
96        int paramTypes =  c.getParameterTypes().length;
97        if (paramTypes != params.length) {
98            error(prefix + "number of parameter types (" + paramTypes
99                  + ") != number of parameters (" + params.length + ")");
100            return;
101        }
102
103        sb.append(clazz.getName()).append(".").append("<init>").append("(");
104        String sep = "";
105
106        // Some paramters are expected
107        if (params.length < 2 && isEnum) {
108            error(prefix + "enum constuctor, two arguments expected");
109        } else if (params.length < 1 && (isAnon || isLocal ||
110                                         (isMember && !isStatic ))) {
111            error(prefix + "class constuctor,expected implicit argument");
112        }
113
114        int i = -1;
115        String param = null;
116        for (Parameter p : c.getParameters()) {
117            i++;
118            String pname = p.getName();
119            int pmodifier = p.getModifiers();
120            isFinal = false;
121            if (Modifier.isFinal(pmodifier)) {
122                isFinal = true;
123                pname = "final " + pname;
124            }
125            sb.append(sep).append(pname);
126            if (p.isImplicit()) sb.append("/*implicit*/");
127            if (p.isSynthetic()) sb.append("/*synthetic*/");
128            sep = ", ";
129
130            // Set expectations
131            String expect = null;
132            boolean allowImplicit = false;
133            boolean allowSynthetic = false;
134            if (isEnum) {
135                if (i == 0) {
136                    expect = "\\$enum\\$name";
137                    allowSynthetic = true;
138                } else if(i == 1) {
139                    expect = "\\$enum\\$ordinal";
140                    allowSynthetic = true;
141                }
142            } else if (i == 0) {
143                if (isAnon) {
144                    expect = "this\\$[0-9]+";
145                    allowImplicit = true;
146                    if (isFinal)
147                        expect = "final this\\$[0-9]+";
148                } else if (isLocal) {
149                    expect = "this\\$[0-9]+";
150                    allowImplicit = true;
151                    if (isFinal)
152                        expect = "final this\\$[0-9]+";
153                } else if ((isMember && !isStatic)) {
154                    expect = "this\\$[0-9]+";
155                    allowImplicit = true;
156                    if (!isPublic) {
157                        // some but not all non-public inner classes
158                        // have synthetic argument. For now we give
159                        // the test a bit of slack and allow either.
160                        allowSynthetic = true;
161                    }
162                    if (isFinal)
163                        expect = "final this\\$[0-9]+";
164                }
165            }
166
167            // Check expected flags
168            if (p.isSynthetic() && p.isImplicit()) {
169                error(prefix + "param[" + i + "]='" + pname +
170                      "' both isImplicit() and isSynthetic()");
171                break;
172            }
173            if (allowImplicit && allowSynthetic &&
174                !(p.isSynthetic() || p.isImplicit())) {
175                error(prefix + "param[" + i + "]='" + pname +
176                      "' isImplicit() or isSynthetic() expected");
177                break;
178            }
179
180            if (allowImplicit && !allowSynthetic && !p.isImplicit()) {
181                error(prefix + "param[" + i + "]='" + pname +
182                      "' isImplicit() expected");
183                break;
184            }
185            if (!allowImplicit && allowSynthetic && !p.isSynthetic()) {
186                error(prefix + "param[" + i + "]='" + pname +
187                      "' isSynthetic() expected");
188                break;
189            }
190
191            if (!allowImplicit && p.isImplicit()) {
192                error(prefix + "param[" + i + "]='" + pname +
193                      "' isImplicit() unexpected");
194                break;
195            }
196
197            if (!allowSynthetic && p.isSynthetic()) {
198                error(prefix + "param[" + i + "]='" + pname +
199                      "' isSynthetic() unexpected");
200                break;
201            }
202
203            // Check expected names
204            if (expect != null) {
205                if (pname.matches(expect))  continue;
206                error(prefix + "param[" + i + "]='" + pname +
207                      "' expected '" + expect + "'");
208                break;
209            }
210
211            // Test naming convention for explicit parameters.
212            boolean fidelity = !isAnon;
213            if (param != null && fidelity) {
214                char ch = param.charAt(0);
215                expect =  (++ch) + param;
216            }
217            if (isFinal && expect != null) {
218                expect = "final " + expect;
219            }
220            if (pname != null && fidelity) {
221                if (isFinal) {
222                    param = pname.substring(6);
223                } else {
224                param = pname;
225            }
226            }
227            if (expect != null && !expect.equals(pname)) {
228                error(prefix + "param[" + i + "]='" + pname +
229                      "' expected '" + expect + "'");
230                break;
231            }
232        }
233        if  (c.isSynthetic()) {
234            sb.append(")/*synthetic*/\n");
235        } else {
236            sb.append(")\n");
237        }
238    }
239
240    void testMethod(Method m) {
241
242        String prefix = clazz.getName() + "." + m.getName() + "() - ";
243
244        // Parameters must match parameter types
245        int paramTypes =  m.getParameterTypes().length;
246        int params = m.getParameters().length;
247        if (paramTypes != params) {
248            error(prefix + "number of parameter types (" + paramTypes
249                  + ") != number of parameters (" + params + ")");
250            return;
251        }
252
253        sb.append(clazz.getName()).append(".").append(m.getName()).append("(");
254        String sep = "";
255        String param = null;
256        int i = -1;
257        // For methods we expect all parameters to follow
258        // the test-case design pattern, except synthetic methods.
259        for (Parameter p : m.getParameters()) {
260            i++;
261            isFinal = false;
262            int pmodifier = p.getModifiers();
263            if (param == null) {
264                param = p.getName();
265                if (Modifier.isFinal(pmodifier)) {
266                    isFinal = true;
267                    param = "final " + param;
268                }
269                sb.append(sep).append(param);
270            } else  {
271                char c = param.charAt(0);
272                String expect =  m.isSynthetic() ? ("arg" + i) : ((++c) + param);
273                param = p.getName();
274                if (Modifier.isFinal(pmodifier)) {
275                    isFinal = true;
276                    expect = "final " + expect;
277                    param = "final " + param;
278                }
279                sb.append(sep).append(param);
280                if (!m.isBridge() && !m.getName().startsWith("lambda$") && !expect.equals(param)) {
281                    error(prefix + "param[" + i + "]='"
282                          + param + "' expected '" + expect + "'");
283                    break;
284                }
285            }
286            if(isFinal)
287                param = param.substring(6);
288            if (p.isImplicit()) {
289                sb.append("/*implicit*/");
290            }
291            if (p.isSynthetic()) {
292                sb.append("/*synthetic*/");
293            }
294            sep = ", ";
295        }
296        if  (m.isSynthetic()) {
297            sb.append(")/*synthetic*/\n");
298        } else {
299            sb.append(")\n");
300        }
301    }
302}
303