ReflectionVisitor.java revision 3170:dc017a37aac5
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 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            if (p.isSynthetic() && !p.isImplicit() && !allowSynthetic) {
168                //patch treatment for local captures
169                if (isAnon || ((isLocal || isAnon) & !isStatic)) {
170                    expect = "val\\$.*";
171                    allowSynthetic = true;
172                    if (isFinal) {
173                        expect = "final val\\$.*";
174                    }
175                }
176            }
177
178            // Check expected flags
179            if (p.isSynthetic() && p.isImplicit()) {
180                error(prefix + "param[" + i + "]='" + pname +
181                      "' both isImplicit() and isSynthetic()");
182                break;
183            }
184            if (allowImplicit && allowSynthetic &&
185                !(p.isSynthetic() || p.isImplicit())) {
186                error(prefix + "param[" + i + "]='" + pname +
187                      "' isImplicit() or isSynthetic() expected");
188                break;
189            }
190
191            if (allowImplicit && !allowSynthetic && !p.isImplicit()) {
192                error(prefix + "param[" + i + "]='" + pname +
193                      "' isImplicit() expected");
194                break;
195            }
196            if (!allowImplicit && allowSynthetic && !p.isSynthetic()) {
197                error(prefix + "param[" + i + "]='" + pname +
198                      "' isSynthetic() expected");
199                break;
200            }
201
202            if (!allowImplicit && p.isImplicit()) {
203                error(prefix + "param[" + i + "]='" + pname +
204                      "' isImplicit() unexpected");
205                break;
206            }
207
208            if (!allowSynthetic && p.isSynthetic()) {
209                error(prefix + "param[" + i + "]='" + pname +
210                      "' isSynthetic() unexpected");
211                break;
212            }
213
214            // Check expected names
215            if (expect != null) {
216                if (pname.matches(expect))  continue;
217                error(prefix + "param[" + i + "]='" + pname +
218                      "' expected '" + expect + "'");
219                break;
220            }
221
222            // Test naming convention for explicit parameters.
223            boolean fidelity = !isAnon;
224            if (param != null && fidelity) {
225                char ch = param.charAt(0);
226                expect =  (++ch) + param;
227            }
228            if (isFinal && expect != null) {
229                expect = "final " + expect;
230            }
231            if (pname != null && fidelity) {
232                if (isFinal) {
233                    param = pname.substring(6);
234                } else {
235                param = pname;
236            }
237            }
238            if (expect != null && !expect.equals(pname)) {
239                error(prefix + "param[" + i + "]='" + pname +
240                      "' expected '" + expect + "'");
241                break;
242            }
243        }
244        if  (c.isSynthetic()) {
245            sb.append(")/*synthetic*/\n");
246        } else {
247            sb.append(")\n");
248        }
249    }
250
251    void testMethod(Method m) {
252
253        String prefix = clazz.getName() + "." + m.getName() + "() - ";
254
255        // Parameters must match parameter types
256        int paramTypes =  m.getParameterTypes().length;
257        int params = m.getParameters().length;
258        if (paramTypes != params) {
259            error(prefix + "number of parameter types (" + paramTypes
260                  + ") != number of parameters (" + params + ")");
261            return;
262        }
263
264        sb.append(clazz.getName()).append(".").append(m.getName()).append("(");
265        String sep = "";
266        String param = null;
267        int i = -1;
268        // For methods we expect all parameters to follow
269        // the test-case design pattern, except synthetic methods.
270        for (Parameter p : m.getParameters()) {
271            i++;
272            isFinal = false;
273            int pmodifier = p.getModifiers();
274            if (param == null) {
275                param = p.getName();
276                if (Modifier.isFinal(pmodifier)) {
277                    isFinal = true;
278                    param = "final " + param;
279                }
280                sb.append(sep).append(param);
281            } else  {
282                char c = param.charAt(0);
283                String expect =  m.isSynthetic() ? ("arg" + i) : ((++c) + param);
284                param = p.getName();
285                if (Modifier.isFinal(pmodifier)) {
286                    isFinal = true;
287                    expect = "final " + expect;
288                    param = "final " + param;
289                }
290                sb.append(sep).append(param);
291                if (!m.isBridge() && !expect.equals(param)) {
292                    error(prefix + "param[" + i + "]='"
293                          + param + "' expected '" + expect + "'");
294                    break;
295                }
296            }
297            if(isFinal)
298                param = param.substring(6);
299            if (p.isImplicit()) {
300                sb.append("/*implicit*/");
301            }
302            if (p.isSynthetic()) {
303                sb.append("/*synthetic*/");
304            }
305            sep = ", ";
306        }
307        if  (m.isSynthetic()) {
308            sb.append(")/*synthetic*/\n");
309        } else {
310            sb.append(")\n");
311        }
312    }
313}
314