8001108: an attempt to use "<init>" as a method name should elicit NoSuchMethodException
Summary: add an explicit check for leading "<", upgrade the unit tests
Reviewed-by: twisti, darcy
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Sat Oct 05 05:30:39 2013 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Sat Oct 05 05:30:39 2013 -0700
@@ -289,6 +289,9 @@
* In general, the conditions under which a method handle may be
* looked up for a method {@code M} are exactly equivalent to the conditions
* under which the lookup class could have compiled and resolved a call to {@code M}.
+ * Where the JVM would raise exceptions like {@code NoSuchMethodError},
+ * a method handle lookup will generally raise a corresponding
+ * checked exception, such as {@code NoSuchMethodException}.
* And the effect of invoking the method handle resulting from the lookup
* is exactly equivalent to executing the compiled and resolved call to {@code M}.
* The same point is true of fields and constructors.
@@ -305,6 +308,12 @@
* (which will necessarily be a superclass of the lookup class)
* to the lookup class itself.
* <p>
+ * The JVM represents constructors and static initializer blocks as internal methods
+ * with special names ({@code "<init>"} and {@code "<clinit>"}).
+ * The internal syntax of invocation instructions allows them to refer to such internal
+ * methods as if they were normal methods, but the JVM bytecode verifier rejects them.
+ * A lookup of such an internal method will produce a {@code NoSuchMethodException}.
+ * <p>
* In some cases, access between nested classes is obtained by the Java compiler by creating
* an wrapper method to access a private method of another class
* in the same top-level declaration.
@@ -604,6 +613,15 @@
* The returned method handle will have
* {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
* the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * <b>Example:</b>
+ * <p><blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle MH_asList = publicLookup().findStatic(Arrays.class,
+ "asList", methodType(List.class, Object[].class));
+assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
+ * }</pre></blockquote>
* @param refc the class from which the method is accessed
* @param name the name of the method
* @param type the type of the method
@@ -654,6 +672,34 @@
* {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}
* with the same {@code type} argument.
*
+ * <b>Example:</b>
+ * <p><blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle MH_concat = publicLookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class,
+ "hashCode", methodType(int.class));
+MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class,
+ "hashCode", methodType(int.class));
+assertEquals("xy", (String) MH_concat.invokeExact("x", "y"));
+assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy"));
+assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy"));
+// interface method:
+MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class,
+ "subSequence", methodType(CharSequence.class, int.class, int.class));
+assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString());
+// constructor "internal method" must be accessed differently:
+MethodType MT_newString = methodType(void.class); //()V for new String()
+try { assertEquals("impossible", lookup()
+ .findVirtual(String.class, "<init>", MT_newString));
+ } catch (NoSuchMethodException ex) { } // OK
+MethodHandle MH_newString = publicLookup()
+ .findConstructor(String.class, MT_newString);
+assertEquals("", (String) MH_newString.invokeExact());
+ * }</pre></blockquote>
+ *
* @param refc the class or interface from which the method is accessed
* @param name the name of the method
* @param type the type of the method, with the receiver argument omitted
@@ -696,12 +742,30 @@
* If the constructor's class has not yet been initialized, that is done
* immediately, before the method handle is returned.
* <p>
- * Note: The requested type must have a return type of {@code void}.
- * This is consistent with the JVM's treatment of constructor type descriptors.
+ * <em>(Note: The requested type must have a return type of {@code void}.
+ * This is consistent with the JVM's treatment of constructor type descriptors.)</em>
* <p>
* The returned method handle will have
* {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
* the constructor's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p><b>Example:</b>
+ * <p><blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+MethodHandle MH_newArrayList = publicLookup().findConstructor(
+ ArrayList.class, methodType(void.class, Collection.class));
+Collection orig = Arrays.asList("x", "y");
+Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig);
+assert(orig != copy);
+assertEquals(orig, copy);
+// a variable-arity constructor:
+MethodHandle MH_newProcessBuilder = publicLookup().findConstructor(
+ ProcessBuilder.class, methodType(void.class, String[].class));
+ProcessBuilder pb = (ProcessBuilder)
+ MH_newProcessBuilder.invoke("x", "y", "z");
+assertEquals("[x, y, z]", pb.command().toString());
+ * }</pre></blockquote>
* @param refc the class or interface from which the method is accessed
* @param type the type of the method, with the receiver argument omitted, and a void return type
* @return the desired method handle
@@ -741,6 +805,45 @@
* The returned method handle will have
* {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
* the method's variable arity modifier bit ({@code 0x0080}) is set.
+ * <p>
+ * <em>(Note: JVM internal methods named {@code "<init>"} are not visible to this API,
+ * even though the {@code invokespecial} instruction can refer to them
+ * in special circumstances. Use {@link #findConstructor findConstructor}
+ * to access instance initialization methods in a safe manner.)</em>
+ * <p><b>Example:</b>
+ * <p><blockquote><pre>{@code
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+...
+static class Listie extends ArrayList {
+ public String toString() { return "[wee Listie]"; }
+ static Lookup lookup() { return MethodHandles.lookup(); }
+}
+...
+// no access to constructor via invokeSpecial:
+MethodHandle MH_newListie = Listie.lookup()
+ .findConstructor(Listie.class, methodType(void.class));
+Listie l = (Listie) MH_newListie.invokeExact();
+try { assertEquals("impossible", Listie.lookup().findSpecial(
+ Listie.class, "<init>", methodType(void.class), Listie.class));
+ } catch (NoSuchMethodException ex) { } // OK
+// access to super and self methods via invokeSpecial:
+MethodHandle MH_super = Listie.lookup().findSpecial(
+ ArrayList.class, "toString" , methodType(String.class), Listie.class);
+MethodHandle MH_this = Listie.lookup().findSpecial(
+ Listie.class, "toString" , methodType(String.class), Listie.class);
+MethodHandle MH_duper = Listie.lookup().findSpecial(
+ Object.class, "toString" , methodType(String.class), Listie.class);
+assertEquals("[]", (String) MH_super.invokeExact(l));
+assertEquals(""+l, (String) MH_this.invokeExact(l));
+assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method
+try { assertEquals("inaccessible", Listie.lookup().findSpecial(
+ String.class, "toString", methodType(String.class), Listie.class));
+ } catch (IllegalAccessException ex) { } // OK
+Listie subl = new Listie() { public String toString() { return "[subclass]"; } };
+assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
+ * }</pre></blockquote>
+ *
* @param refc the class or interface from which the method is accessed
* @param name the name of the method (which must not be "<init>")
* @param type the type of the method, with the receiver argument omitted
@@ -1105,6 +1208,7 @@
checkSymbolicClass(refc); // do this before attempting to resolve
name.getClass(); // NPE
type.getClass(); // NPE
+ checkMethodName(refKind, name); // NPE check on name
return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(),
NoSuchMethodException.class);
}
@@ -1124,6 +1228,13 @@
throw new MemberName(refc).makeAccessException("symbolic reference class is not public", this);
}
+ /** Check name for an illegal leading "<" character. */
+ void checkMethodName(byte refKind, String name) throws NoSuchMethodException {
+ if (name.startsWith("<") && refKind != REF_newInvokeSpecial)
+ throw new NoSuchMethodException("illegal method name: "+name);
+ }
+
+
/**
* Find my trustable caller class if m is a caller sensitive method.
* If this lookup object has private access, then the caller class is the lookupClass.
--- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Sat Oct 05 05:30:39 2013 -0700
+++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Sat Oct 05 05:30:39 2013 -0700
@@ -53,7 +53,11 @@
}
public void run() throws Throwable {
+ testMisc();
+ testFindStatic();
+ testFindConstructor();
testFindVirtual();
+ testFindSpecial();
testPermuteArguments();
testDropArguments();
testFilterArguments();
@@ -99,7 +103,8 @@
{}
- @Test public void testFindVirtual() throws Throwable {
+ @Test public void testMisc() throws Throwable {
+// Extra tests, not from javadoc:
{}
MethodHandle CONCAT_3 = LOOKUP.findVirtual(String.class,
"concat", methodType(String.class, String.class));
@@ -114,6 +119,92 @@
{}
}
+ @Test public void testFindStatic() throws Throwable {
+{}
+MethodHandle MH_asList = publicLookup().findStatic(Arrays.class,
+ "asList", methodType(List.class, Object[].class));
+assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
+{}
+ }
+
+ @Test public void testFindVirtual() throws Throwable {
+{}
+MethodHandle MH_concat = publicLookup().findVirtual(String.class,
+ "concat", methodType(String.class, String.class));
+MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class,
+ "hashCode", methodType(int.class));
+MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class,
+ "hashCode", methodType(int.class));
+assertEquals("xy", (String) MH_concat.invokeExact("x", "y"));
+assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy"));
+assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy"));
+// interface method:
+MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class,
+ "subSequence", methodType(CharSequence.class, int.class, int.class));
+assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString());
+// constructor "internal method" must be accessed differently:
+MethodType MT_newString = methodType(void.class); //()V for new String()
+try { assertEquals("impossible", lookup()
+ .findVirtual(String.class, "<init>", MT_newString));
+ } catch (NoSuchMethodException ex) { } // OK
+MethodHandle MH_newString = publicLookup()
+ .findConstructor(String.class, MT_newString);
+assertEquals("", (String) MH_newString.invokeExact());
+{}
+ }
+
+ @Test public void testFindConstructor() throws Throwable {
+{}
+MethodHandle MH_newArrayList = publicLookup().findConstructor(
+ ArrayList.class, methodType(void.class, Collection.class));
+Collection orig = Arrays.asList("x", "y");
+Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig);
+assert(orig != copy);
+assertEquals(orig, copy);
+// a variable-arity constructor:
+MethodHandle MH_newProcessBuilder = publicLookup().findConstructor(
+ ProcessBuilder.class, methodType(void.class, String[].class));
+ProcessBuilder pb = (ProcessBuilder)
+ MH_newProcessBuilder.invoke("x", "y", "z");
+assertEquals("[x, y, z]", pb.command().toString());
+{}
+ }
+
+// for testFindSpecial
+{}
+static class Listie extends ArrayList {
+ public String toString() { return "[wee Listie]"; }
+ static Lookup lookup() { return MethodHandles.lookup(); }
+}
+{}
+
+ @Test public void testFindSpecial() throws Throwable {
+{}
+// no access to constructor via invokeSpecial:
+MethodHandle MH_newListie = Listie.lookup()
+ .findConstructor(Listie.class, methodType(void.class));
+Listie l = (Listie) MH_newListie.invokeExact();
+try { assertEquals("impossible", Listie.lookup().findSpecial(
+ Listie.class, "<init>", methodType(void.class), Listie.class));
+ } catch (NoSuchMethodException ex) { } // OK
+// access to super and self methods via invokeSpecial:
+MethodHandle MH_super = Listie.lookup().findSpecial(
+ ArrayList.class, "toString" , methodType(String.class), Listie.class);
+MethodHandle MH_this = Listie.lookup().findSpecial(
+ Listie.class, "toString" , methodType(String.class), Listie.class);
+MethodHandle MH_duper = Listie.lookup().findSpecial(
+ Object.class, "toString" , methodType(String.class), Listie.class);
+assertEquals("[]", (String) MH_super.invokeExact(l));
+assertEquals(""+l, (String) MH_this.invokeExact(l));
+assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method
+try { assertEquals("inaccessible", Listie.lookup().findSpecial(
+ String.class, "toString", methodType(String.class), Listie.class));
+ } catch (IllegalAccessException ex) { } // OK
+Listie subl = new Listie() { public String toString() { return "[subclass]"; } };
+assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
+{}
+ }
+
@Test public void testPermuteArguments() throws Throwable {
{{
{} /// JAVADOC
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java Sat Oct 05 05:30:39 2013 -0700
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java Sat Oct 05 05:30:39 2013 -0700
@@ -387,6 +387,7 @@
protected Example(String name) { this.name = name; }
@SuppressWarnings("LeakingThisInConstructor")
protected Example(int x) { this(); called("protected <init>", this, x); }
+ //Example(Void x) { does not exist; lookup elicts NoSuchMethodException }
@Override public String toString() { return name; }
public void v0() { called("v0", this); }
@@ -487,6 +488,9 @@
return lookup.in(defc);
}
+ /** Is findVirtual (etc.) of "<init>" supposed to elicit a NoSuchMethodException? */
+ final static boolean INIT_REF_CAUSES_NSME = true;
+
@Test
public void testFindStatic() throws Throwable {
if (CAN_SKIP_WORKING) return;
@@ -507,6 +511,8 @@
testFindStatic(Example.class, Object.class, "s7", float.class, double.class);
testFindStatic(false, PRIVATE, Example.class, void.class, "bogus");
+ testFindStatic(false, PRIVATE, Example.class, void.class, "<init>", int.class);
+ testFindStatic(false, PRIVATE, Example.class, void.class, "<init>", Void.class);
testFindStatic(false, PRIVATE, Example.class, void.class, "v0");
}
@@ -529,11 +535,12 @@
target = maybeMoveIn(lookup, defc).findStatic(defc, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ assertExceptionClass(
+ (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+ ? NoSuchMethodException.class
+ : IllegalAccessException.class,
+ noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
- if (name.contains("bogus"))
- assertTrue(noAccess instanceof NoSuchMethodException);
- else
- assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -551,6 +558,13 @@
System.out.print(':');
}
+ static void assertExceptionClass(Class<? extends Throwable> expected,
+ Throwable actual) {
+ if (expected.isInstance(actual)) return;
+ actual.printStackTrace();
+ assertEquals(expected, actual.getClass());
+ }
+
static final boolean DEBUG_METHOD_HANDLE_NAMES = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
// rough check of name string
@@ -580,6 +594,8 @@
testFindVirtual(PubExample.class, void.class, "Pub/pro_v0");
testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus");
+ testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "<init>", int.class);
+ testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "<init>", Void.class);
testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "s0");
// test dispatch
@@ -628,11 +644,12 @@
target = maybeMoveIn(lookup, defc).findVirtual(defc, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ assertExceptionClass(
+ (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+ ? NoSuchMethodException.class
+ : IllegalAccessException.class,
+ noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
- if (name.contains("bogus"))
- assertTrue(noAccess instanceof NoSuchMethodException);
- else
- assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -682,11 +699,11 @@
testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0");
// Do some negative testing:
- testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus");
- testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus");
for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) {
testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0");
+ testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "bogus");
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class);
+ testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", Void.class);
testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
}
}
@@ -712,19 +729,25 @@
countTest(positive);
String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
+ Lookup specialLookup = maybeMoveIn(lookup, specialCaller);
+ boolean specialAccessOK = (specialLookup.lookupClass() == specialCaller &&
+ (specialLookup.lookupModes() & Lookup.PRIVATE) != 0);
MethodHandle target = null;
Exception noAccess = null;
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
- if (verbosity >= 5) System.out.println(" lookup => "+maybeMoveIn(lookup, specialCaller));
- target = maybeMoveIn(lookup, specialCaller).findSpecial(defc, methodName, type, specialCaller);
+ if (verbosity >= 5) System.out.println(" lookup => "+specialLookup);
+ target = specialLookup.findSpecial(defc, methodName, type, specialCaller);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ assertExceptionClass(
+ (!specialAccessOK) // this check should happen first
+ ? IllegalAccessException.class
+ : (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+ ? NoSuchMethodException.class
+ : IllegalAccessException.class,
+ noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
- if (name.contains("bogus"))
- assertTrue(noAccess instanceof NoSuchMethodException);
- else
- assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target
@@ -769,7 +792,7 @@
target = lookup.findConstructor(defc, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
- assertTrue(noAccess instanceof IllegalAccessException);
+ assertTrue(noAccess.getClass().getName(), noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("findConstructor "+defc.getName()+".<init>/"+type+" => "+target
@@ -800,6 +823,8 @@
testBind(Example.class, Object.class, "v2", int.class, Object.class);
testBind(Example.class, Object.class, "v2", int.class, int.class);
testBind(false, PRIVATE, Example.class, void.class, "bogus");
+ testBind(false, PRIVATE, Example.class, void.class, "<init>", int.class);
+ testBind(false, PRIVATE, Example.class, void.class, "<init>", Void.class);
testBind(SubExample.class, void.class, "Sub/v0");
testBind(SubExample.class, void.class, "Sub/pkg_v0");
testBind(IntExample.Impl.class, void.class, "Int/v0");
@@ -823,11 +848,12 @@
target = maybeMoveIn(lookup, defc).bind(receiver, methodName, type);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ assertExceptionClass(
+ (name.contains("bogus") || INIT_REF_CAUSES_NSME && name.contains("<init>"))
+ ? NoSuchMethodException.class
+ : IllegalAccessException.class,
+ noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
- if (name.contains("bogus"))
- assertTrue(noAccess instanceof NoSuchMethodException);
- else
- assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target
@@ -890,6 +916,10 @@
countTest(positive);
String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo
MethodType type = MethodType.methodType(ret, params);
+ Lookup specialLookup = (specialCaller != null ? maybeMoveIn(lookup, specialCaller) : null);
+ boolean specialAccessOK = (specialCaller != null &&
+ specialLookup.lookupClass() == specialCaller &&
+ (specialLookup.lookupModes() & Lookup.PRIVATE) != 0);
Method rmethod = defc.getDeclaredMethod(methodName, params);
MethodHandle target = null;
Exception noAccess = null;
@@ -898,16 +928,15 @@
try {
if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type);
if (isSpecial)
- target = maybeMoveIn(lookup, specialCaller).unreflectSpecial(rmethod, specialCaller);
+ target = specialLookup.unreflectSpecial(rmethod, specialCaller);
else
target = maybeMoveIn(lookup, defc).unreflect(rmethod);
} catch (ReflectiveOperationException ex) {
noAccess = ex;
+ assertExceptionClass(
+ IllegalAccessException.class, // NSME is impossible, since it was already reflected
+ noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
- if (name.contains("bogus"))
- assertTrue(noAccess instanceof NoSuchMethodException);
- else
- assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type
@@ -1141,11 +1170,12 @@
} catch (ReflectiveOperationException ex) {
mh = null;
noAccess = ex;
+ assertExceptionClass(
+ (fname.contains("bogus"))
+ ? NoSuchFieldException.class
+ : IllegalAccessException.class,
+ noAccess);
if (verbosity >= 5) ex.printStackTrace(System.out);
- if (fname.contains("bogus"))
- assertTrue(noAccess instanceof NoSuchFieldException);
- else
- assertTrue(noAccess instanceof IllegalAccessException);
}
if (verbosity >= 3)
System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype