/* * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.io.*; import java.lang.reflect.*; /** * Test MethodParameter attributs by reflection API */ public class ReflectionVisitor extends MethodParametersTester.Visitor { public ReflectionVisitor(MethodParametersTester tester) { super(tester); } public void error(String msg) { super.error("reflection: " + msg); } public void warn(String msg) { super.warn("reflection: " + msg); } boolean isEnum; boolean isInterface; boolean isAnon; boolean isLocal; boolean isMember; boolean isStatic; boolean isPublic; boolean isFinal; Class clazz; StringBuilder sb; /** * Read class using {@code ClassFile}, and generate a list of methods * with parameter names as available in the MethodParameters attribute. */ void visitClass(final String cl, final File cfile, final StringBuilder sb) throws Exception { this.sb = sb; clazz = Class.forName(cl); isEnum = clazz.isEnum(); isInterface = clazz.isInterface(); isAnon = clazz.isAnonymousClass(); isLocal = clazz.isLocalClass(); isMember = clazz.isMemberClass(); isStatic = ((clazz.getModifiers() & Modifier.STATIC) != 0); isPublic = ((clazz.getModifiers() & Modifier.PUBLIC) != 0); sb.append(isStatic ? "static " : "") .append(isPublic ? "public " : "") .append(isEnum ? "enum " : isInterface ? "interface " : "class ") .append(cl).append(" -- ") .append(isMember? "inner" : "" ) .append(isLocal? "inner" : "" ) .append(isAnon ? "anon" : "") .append("\n"); for (Constructor c : clazz.getDeclaredConstructors()) { testConstructor(c); } for (Method m :clazz.getDeclaredMethods()) { testMethod(m); } } void testConstructor(Constructor c) { String prefix = clazz.getName() + "." + c.getName() + "() - "; // Parameters must match parameter types Parameter params[] = c.getParameters(); int paramTypes = c.getParameterTypes().length; if (paramTypes != params.length) { error(prefix + "number of parameter types (" + paramTypes + ") != number of parameters (" + params.length + ")"); return; } sb.append(clazz.getName()).append(".").append("<init>").append("("); String sep = ""; // Some paramters are expected if (params.length < 2 && isEnum) { error(prefix + "enum constuctor, two arguments expected"); } else if (params.length < 1 && (isAnon || isLocal || (isMember && !isStatic ))) { error(prefix + "class constuctor,expected implicit argument"); } int i = -1; String param = null; for (Parameter p : c.getParameters()) { i++; String pname = p.getName(); int pmodifier = p.getModifiers(); isFinal = false; if (Modifier.isFinal(pmodifier)) { isFinal = true; pname = "final " + pname; } sb.append(sep).append(pname); if (p.isImplicit()) sb.append("/*implicit*/"); if (p.isSynthetic()) sb.append("/*synthetic*/"); sep = ", "; // Set expectations String expect = null; boolean allowImplicit = false; boolean allowSynthetic = false; if (isEnum) { if (i == 0) { expect = "\\$enum\\$name"; allowSynthetic = true; } else if(i == 1) { expect = "\\$enum\\$ordinal"; allowSynthetic = true; } } else if (i == 0) { if (isAnon) { expect = "this\\$[0-9]+"; allowImplicit = true; if (isFinal) expect = "final this\\$[0-9]+"; } else if (isLocal) { expect = "this\\$[0-9]+"; allowImplicit = true; if (isFinal) expect = "final this\\$[0-9]+"; } else if ((isMember && !isStatic)) { expect = "this\\$[0-9]+"; allowImplicit = true; if (!isPublic) { // some but not all non-public inner classes // have synthetic argument. For now we give // the test a bit of slack and allow either. allowSynthetic = true; } if (isFinal) expect = "final this\\$[0-9]+"; } } if (p.isSynthetic() && !p.isImplicit() && !allowSynthetic) { //patch treatment for local captures if (isAnon || ((isLocal || isAnon) & !isStatic)) { expect = "val\\$.*"; allowSynthetic = true; if (isFinal) { expect = "final val\\$.*"; } } } // Check expected flags if (p.isSynthetic() && p.isImplicit()) { error(prefix + "param[" + i + "]='" + pname + "' both isImplicit() and isSynthetic()"); break; } if (allowImplicit && allowSynthetic && !(p.isSynthetic() || p.isImplicit())) { error(prefix + "param[" + i + "]='" + pname + "' isImplicit() or isSynthetic() expected"); break; } if (allowImplicit && !allowSynthetic && !p.isImplicit()) { error(prefix + "param[" + i + "]='" + pname + "' isImplicit() expected"); break; } if (!allowImplicit && allowSynthetic && !p.isSynthetic()) { error(prefix + "param[" + i + "]='" + pname + "' isSynthetic() expected"); break; } if (!allowImplicit && p.isImplicit()) { error(prefix + "param[" + i + "]='" + pname + "' isImplicit() unexpected"); break; } if (!allowSynthetic && p.isSynthetic()) { error(prefix + "param[" + i + "]='" + pname + "' isSynthetic() unexpected"); break; } // Check expected names if (expect != null) { if (pname.matches(expect)) continue; error(prefix + "param[" + i + "]='" + pname + "' expected '" + expect + "'"); break; } // Test naming convention for explicit parameters. boolean fidelity = !isAnon; if (param != null && fidelity) { char ch = param.charAt(0); expect = (++ch) + param; } if (isFinal && expect != null) { expect = "final " + expect; } if (pname != null && fidelity) { if (isFinal) { param = pname.substring(6); } else { param = pname; } } if (expect != null && !expect.equals(pname)) { error(prefix + "param[" + i + "]='" + pname + "' expected '" + expect + "'"); break; } } if (c.isSynthetic()) { sb.append(")/*synthetic*/\n"); } else { sb.append(")\n"); } } void testMethod(Method m) { String prefix = clazz.getName() + "." + m.getName() + "() - "; // Parameters must match parameter types int paramTypes = m.getParameterTypes().length; int params = m.getParameters().length; if (paramTypes != params) { error(prefix + "number of parameter types (" + paramTypes + ") != number of parameters (" + params + ")"); return; } sb.append(clazz.getName()).append(".").append(m.getName()).append("("); String sep = ""; String param = null; int i = -1; // For methods we expect all parameters to follow // the test-case design pattern, except synthetic methods. for (Parameter p : m.getParameters()) { i++; isFinal = false; int pmodifier = p.getModifiers(); if (param == null) { param = p.getName(); if (Modifier.isFinal(pmodifier)) { isFinal = true; param = "final " + param; } sb.append(sep).append(param); } else { char c = param.charAt(0); String expect = m.isSynthetic() ? ("arg" + i) : ((++c) + param); param = p.getName(); if (Modifier.isFinal(pmodifier)) { isFinal = true; expect = "final " + expect; param = "final " + param; } sb.append(sep).append(param); if (!m.isBridge() && !expect.equals(param)) { error(prefix + "param[" + i + "]='" + param + "' expected '" + expect + "'"); break; } } if(isFinal) param = param.substring(6); if (p.isImplicit()) { sb.append("/*implicit*/"); } if (p.isSynthetic()) { sb.append("/*synthetic*/"); } sep = ", "; } if (m.isSynthetic()) { sb.append(")/*synthetic*/\n"); } else { sb.append(")\n"); } } }