undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2026, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test locating and invoking default/static method that defined
|
||||
* in interfaces and/or in inheritance
|
||||
* @bug 7184826
|
||||
* @build helper.Mod helper.Declared DefaultStaticTestData
|
||||
* @run junit DefaultStaticInvokeTest
|
||||
* @author Yong Lu
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import static helper.Mod.*;
|
||||
import static helper.Declared.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import helper.Mod;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class DefaultStaticInvokeTest {
|
||||
|
||||
// getMethods(): Make sure getMethods returns the expected methods.
|
||||
@ParameterizedTest
|
||||
@MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"})
|
||||
public void testGetMethods(String testTarget, Object param)
|
||||
throws Exception {
|
||||
testMethods(ALL_METHODS, testTarget, param);
|
||||
}
|
||||
|
||||
|
||||
// getDeclaredMethods(): Make sure getDeclaredMethods returns the expected methods.
|
||||
@ParameterizedTest
|
||||
@MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"})
|
||||
public void testGetDeclaredMethods(String testTarget, Object param)
|
||||
throws Exception {
|
||||
testMethods(DECLARED_ONLY, testTarget, param);
|
||||
}
|
||||
|
||||
|
||||
// getMethod(): Make sure that getMethod finds all methods it should find.
|
||||
@ParameterizedTest
|
||||
@MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"})
|
||||
public void testGetMethod(String testTarget, Object param)
|
||||
throws Exception {
|
||||
|
||||
Class<?> typeUnderTest = Class.forName(testTarget);
|
||||
|
||||
MethodDesc[] descs = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||||
|
||||
for (MethodDesc desc : descs) {
|
||||
assertTrue(isFoundByGetMethod(typeUnderTest,
|
||||
desc.name(),
|
||||
argTypes(param)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// getMethod(): Make sure that getMethod does *not* find certain methods.
|
||||
@ParameterizedTest
|
||||
@MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"})
|
||||
public void testGetMethodSuperInterfaces(String testTarget, Object param)
|
||||
throws Exception {
|
||||
|
||||
// Make sure static methods in superinterfaces are not found (unless the type under
|
||||
// test declares a static method with the same signature).
|
||||
|
||||
Class<?> typeUnderTest = Class.forName(testTarget);
|
||||
|
||||
for (Class<?> interfaze : typeUnderTest.getInterfaces()) {
|
||||
|
||||
for (MethodDesc desc : interfaze.getAnnotationsByType(MethodDesc.class)) {
|
||||
|
||||
boolean isStatic = desc.mod() == STATIC;
|
||||
|
||||
boolean declaredInThisType = isMethodDeclared(typeUnderTest,
|
||||
desc.name());
|
||||
|
||||
boolean expectedToBeFound = !isStatic || declaredInThisType;
|
||||
|
||||
if (expectedToBeFound)
|
||||
continue; // already tested in testGetMethod()
|
||||
|
||||
assertFalse(isFoundByGetMethod(typeUnderTest,
|
||||
desc.name(),
|
||||
argTypes(param)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Method.invoke(): Make sure Method.invoke returns the expected value.
|
||||
@ParameterizedTest
|
||||
@MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"})
|
||||
public void testMethodInvoke(String testTarget, Object param)
|
||||
throws Exception {
|
||||
Class<?> typeUnderTest = Class.forName(testTarget);
|
||||
MethodDesc[] expectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||||
|
||||
// test the method retrieved by Class.getMethod(String, Object[])
|
||||
for (MethodDesc toTest : expectedMethods) {
|
||||
String name = toTest.name();
|
||||
Method m = typeUnderTest.getMethod(name, argTypes(param));
|
||||
testThisMethod(toTest, m, typeUnderTest, param);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MethodHandle.invoke(): Make sure MethodHandle.invoke returns the expected value.
|
||||
@ParameterizedTest
|
||||
@MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"})
|
||||
public void testMethodHandleInvoke(String testTarget, Object param)
|
||||
throws Throwable {
|
||||
Class<?> typeUnderTest = Class.forName(testTarget);
|
||||
MethodDesc[] expectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||||
|
||||
for (MethodDesc toTest : expectedMethods) {
|
||||
String mName = toTest.name();
|
||||
Mod mod = toTest.mod();
|
||||
if (mod != STATIC && typeUnderTest.isInterface()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String result = null;
|
||||
String expectedReturn = toTest.retval();
|
||||
|
||||
MethodHandle methodHandle = getTestMH(typeUnderTest, mName, param);
|
||||
if (mName.equals("staticMethod")) {
|
||||
result = (param == null)
|
||||
? (String) methodHandle.invoke()
|
||||
: (String) methodHandle.invoke(param);
|
||||
} else {
|
||||
result = (param == null)
|
||||
? (String) methodHandle.invoke(typeUnderTest.newInstance())
|
||||
: (String) methodHandle.invoke(typeUnderTest.newInstance(), param);
|
||||
}
|
||||
|
||||
assertEquals(expectedReturn, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Lookup.findStatic / .findVirtual: Make sure IllegalAccessException is thrown as expected.
|
||||
@ParameterizedTest
|
||||
@MethodSource("DefaultStaticTestData#testClasses")
|
||||
public void testIAE(String testTarget, Object param)
|
||||
throws ClassNotFoundException {
|
||||
|
||||
Class<?> typeUnderTest = Class.forName(testTarget);
|
||||
MethodDesc[] expectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||||
|
||||
for (MethodDesc toTest : expectedMethods) {
|
||||
String mName = toTest.name();
|
||||
Mod mod = toTest.mod();
|
||||
if (mod != STATIC && typeUnderTest.isInterface()) {
|
||||
continue;
|
||||
}
|
||||
assertThrowsExactly(IllegalAccessException.class, () ->
|
||||
getTestMH(typeUnderTest, mName, param, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final String[] OBJECT_METHOD_NAMES = {
|
||||
"equals",
|
||||
"hashCode",
|
||||
"getClass",
|
||||
"notify",
|
||||
"notifyAll",
|
||||
"toString",
|
||||
"wait",
|
||||
"wait",
|
||||
"wait",};
|
||||
private static final String LAMBDA_METHOD_NAMES = "lambda$";
|
||||
private static final HashSet<String> OBJECT_NAMES = new HashSet<>(Arrays.asList(OBJECT_METHOD_NAMES));
|
||||
private static final boolean DECLARED_ONLY = true;
|
||||
private static final boolean ALL_METHODS = false;
|
||||
|
||||
private void testMethods(boolean declaredOnly, String testTarget, Object param)
|
||||
throws Exception {
|
||||
Class<?> typeUnderTest = Class.forName(testTarget);
|
||||
Method[] methods = declaredOnly
|
||||
? typeUnderTest.getDeclaredMethods()
|
||||
: typeUnderTest.getMethods();
|
||||
|
||||
MethodDesc[] baseExpectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||||
MethodDesc[] expectedMethods;
|
||||
|
||||
// If only declared filter out non-declared from expected result
|
||||
if (declaredOnly) {
|
||||
int nonDeclared = 0;
|
||||
for (MethodDesc desc : baseExpectedMethods) {
|
||||
if (desc.declared() == NO) {
|
||||
nonDeclared++;
|
||||
}
|
||||
}
|
||||
expectedMethods = new MethodDesc[baseExpectedMethods.length - nonDeclared];
|
||||
int i = 0;
|
||||
for (MethodDesc desc : baseExpectedMethods) {
|
||||
if (desc.declared() == YES) {
|
||||
expectedMethods[i++] = desc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
expectedMethods = baseExpectedMethods;
|
||||
}
|
||||
|
||||
HashMap<String, Method> myMethods = new HashMap<>(methods.length);
|
||||
for (Method m : methods) {
|
||||
String mName = m.getName();
|
||||
// don't add Object methods and method created from lambda expression
|
||||
if ((!OBJECT_NAMES.contains(mName)) && (!mName.contains(LAMBDA_METHOD_NAMES))) {
|
||||
myMethods.put(mName, m);
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(expectedMethods.length, myMethods.size());
|
||||
|
||||
for (MethodDesc toTest : expectedMethods) {
|
||||
|
||||
String name = toTest.name();
|
||||
Method candidate = myMethods.remove(name);
|
||||
|
||||
assertNotNull(candidate);
|
||||
|
||||
testThisMethod(toTest, candidate, typeUnderTest, param);
|
||||
|
||||
}
|
||||
|
||||
// Should be no methods left since we remove all we expect to see
|
||||
assertTrue(myMethods.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
private void testThisMethod(MethodDesc toTest, Method method,
|
||||
Class<?> typeUnderTest, Object param) throws Exception {
|
||||
// Test modifiers, and invoke
|
||||
Mod mod = toTest.mod();
|
||||
String expectedReturn = toTest.retval();
|
||||
switch (mod) {
|
||||
case STATIC:
|
||||
//assert candidate is static
|
||||
assertTrue(Modifier.isStatic(method.getModifiers()));
|
||||
assertFalse(method.isDefault());
|
||||
|
||||
// Test invoke it
|
||||
assertEquals(expectedReturn, tryInvoke(method, null, param));
|
||||
break;
|
||||
case DEFAULT:
|
||||
// if typeUnderTest is a class then instantiate and invoke
|
||||
if (!typeUnderTest.isInterface()) {
|
||||
assertEquals(expectedReturn, tryInvoke(
|
||||
method,
|
||||
typeUnderTest,
|
||||
param));
|
||||
}
|
||||
|
||||
//assert candidate is default
|
||||
assertFalse(Modifier.isStatic(method.getModifiers()));
|
||||
assertTrue(method.isDefault());
|
||||
break;
|
||||
case REGULAR:
|
||||
// if typeUnderTest must be a class
|
||||
assertEquals(expectedReturn, tryInvoke(
|
||||
method,
|
||||
typeUnderTest,
|
||||
param));
|
||||
|
||||
//assert candidate is neither default nor static
|
||||
assertFalse(Modifier.isStatic(method.getModifiers()));
|
||||
assertFalse(method.isDefault());
|
||||
break;
|
||||
case ABSTRACT:
|
||||
//assert candidate is neither default nor static
|
||||
assertFalse(Modifier.isStatic(method.getModifiers()));
|
||||
assertFalse(method.isDefault());
|
||||
break;
|
||||
default:
|
||||
fail(); //this should never happen
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private boolean isMethodDeclared(Class<?> type, String name) {
|
||||
MethodDesc[] methDescs = type.getAnnotationsByType(MethodDesc.class);
|
||||
for (MethodDesc desc : methDescs) {
|
||||
if (desc.declared() == YES && desc.name().equals(name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean isFoundByGetMethod(Class<?> c, String method, Class<?>... argTypes) {
|
||||
try {
|
||||
c.getMethod(method, argTypes);
|
||||
return true;
|
||||
} catch (NoSuchMethodException notFound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Class<?>[] argTypes(Object param) {
|
||||
return param == null ? new Class[0] : new Class[] { Object.class };
|
||||
}
|
||||
|
||||
|
||||
private Object tryInvoke(Method m, Class<?> receiverType, Object param)
|
||||
throws Exception {
|
||||
Object receiver = receiverType == null ? null : receiverType.newInstance();
|
||||
Object[] args = param == null ? new Object[0] : new Object[] { param };
|
||||
return m.invoke(receiver, args);
|
||||
}
|
||||
|
||||
|
||||
private MethodHandle getTestMH(Class clazz, String methodName, Object param)
|
||||
throws Exception {
|
||||
return getTestMH(clazz, methodName, param, false);
|
||||
}
|
||||
|
||||
|
||||
private MethodHandle getTestMH(Class clazz, String methodName,
|
||||
Object param, boolean isNegativeTest)
|
||||
throws Exception {
|
||||
MethodType mType = (param != null)
|
||||
? MethodType.genericMethodType(1)
|
||||
: MethodType.methodType(String.class);
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
if (!isNegativeTest) {
|
||||
return methodName.equals("staticMethod")
|
||||
? lookup.findStatic(clazz, methodName, mType)
|
||||
: lookup.findVirtual(clazz, methodName, mType);
|
||||
} else {
|
||||
return methodName.equals("staticMethod")
|
||||
? lookup.findVirtual(clazz, methodName, mType)
|
||||
: lookup.findStatic(clazz, methodName, mType);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue