1 /*
2  * Copyright (c) 2013, 2016, 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 
24 import java.io.*;
25 import java.lang.reflect.*;
26 
27 /**
28  * Test MethodParameter attributs by reflection API
29  */
30 public class ReflectionVisitor extends MethodParametersTester.Visitor {
31 
ReflectionVisitor(MethodParametersTester tester)32     public ReflectionVisitor(MethodParametersTester tester) {
33         super(tester);
34     }
35 
error(String msg)36     public void error(String msg) {
37         super.error("reflection: " + msg);
38     }
39 
warn(String msg)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      */
visitClass(final String cl, final File cfile, final StringBuilder sb)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 
testConstructor(Constructor c)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 
testMethod(Method m)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