blob: 9b53f2c1882a4839cc3c91813c42293392b09f80 [file] [log] [blame]
/*
* 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");
}
}
}