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
1379
test/jdk/java/lang/reflect/PublicMethods/Case1.results
Normal file
1379
test/jdk/java/lang/reflect/PublicMethods/Case1.results
Normal file
File diff suppressed because it is too large
Load diff
550
test/jdk/java/lang/reflect/PublicMethods/PublicMethodsTest.java
Normal file
550
test/jdk/java/lang/reflect/PublicMethods/PublicMethodsTest.java
Normal file
|
|
@ -0,0 +1,550 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2022, 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 javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8062389
|
||||
* @modules jdk.compiler
|
||||
* jdk.zipfs
|
||||
* @summary Nearly exhaustive test of Class.getMethod() and Class.getMethods()
|
||||
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies PublicMethodsTest
|
||||
*/
|
||||
public class PublicMethodsTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Case c = new Case1();
|
||||
|
||||
int[] diffs = new int[1];
|
||||
try (Stream<Map.Entry<int[], Map<String, String>>>
|
||||
expected = expectedResults(c)) {
|
||||
diffResults(c, expected)
|
||||
.forEach(diff -> {
|
||||
System.out.println(diff);
|
||||
diffs[0]++;
|
||||
});
|
||||
}
|
||||
|
||||
if (diffs[0] > 0) {
|
||||
throw new RuntimeException(
|
||||
"There were " + diffs[0] + " differences.");
|
||||
}
|
||||
}
|
||||
|
||||
// use this to generate .results file for particular case
|
||||
public static class Generate {
|
||||
public static void main(String[] args) {
|
||||
Case c = new Case1();
|
||||
dumpResults(generateResults(c))
|
||||
.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
interface Case {
|
||||
Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{(.+?)}");
|
||||
|
||||
// possible variants of interface method
|
||||
List<String> INTERFACE_METHODS = List.of(
|
||||
"", "void m();", "default void m() {}", "static void m() {}"
|
||||
);
|
||||
|
||||
// possible variants of class method
|
||||
List<String> CLASS_METHODS = List.of(
|
||||
"", "public abstract void m();",
|
||||
"public void m() {}", "public static void m() {}"
|
||||
);
|
||||
|
||||
// template with placeholders parsed with PLACEHOLDER_PATTERN
|
||||
String template();
|
||||
|
||||
// map of replacementKey (== PLACEHOLDER_PATTERN captured group #1) ->
|
||||
// list of possible replacements
|
||||
Map<String, List<String>> replacements();
|
||||
|
||||
// ordered list of replacement keys
|
||||
List<String> replacementKeys();
|
||||
|
||||
// names of types occurring in the template
|
||||
List<String> classNames();
|
||||
}
|
||||
|
||||
static class Case1 implements Case {
|
||||
|
||||
private static final String TEMPLATE = Stream.of(
|
||||
"interface I { ${I} }",
|
||||
"interface J { ${J} }",
|
||||
"interface K extends I, J { ${K} }",
|
||||
"abstract class C { ${C} }",
|
||||
"abstract class D extends C implements I { ${D} }",
|
||||
"abstract class E extends D implements J, K { ${E} }"
|
||||
).collect(joining("\n"));
|
||||
|
||||
private static final Map<String, List<String>> REPLACEMENTS = Map.of(
|
||||
"I", INTERFACE_METHODS,
|
||||
"J", INTERFACE_METHODS,
|
||||
"K", INTERFACE_METHODS,
|
||||
"C", CLASS_METHODS,
|
||||
"D", CLASS_METHODS,
|
||||
"E", CLASS_METHODS
|
||||
);
|
||||
|
||||
private static final List<String> REPLACEMENT_KEYS = REPLACEMENTS
|
||||
.keySet().stream().sorted().collect(Collectors.toList());
|
||||
|
||||
@Override
|
||||
public String template() {
|
||||
return TEMPLATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> replacements() {
|
||||
return REPLACEMENTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> replacementKeys() {
|
||||
return REPLACEMENT_KEYS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> classNames() {
|
||||
// just by accident, names of classes are equal to replacement keys
|
||||
// (this need not be the case in general)
|
||||
return REPLACEMENT_KEYS;
|
||||
}
|
||||
}
|
||||
|
||||
// generate all combinations as a tuple of indexes into lists of
|
||||
// replacements. The index of the element in int[] tuple represents the index
|
||||
// of the key in replacementKeys() list. The value of the element in int[] tuple
|
||||
// represents the index of the replacement string in list of strings in the
|
||||
// value of the entry of replacements() map with the corresponding key.
|
||||
static Stream<int[]> combinations(Case c) {
|
||||
int[] sizes = c.replacementKeys().stream()
|
||||
.mapToInt(key -> c.replacements().get(key).size())
|
||||
.toArray();
|
||||
|
||||
return Stream.iterate(
|
||||
new int[sizes.length],
|
||||
state -> state != null,
|
||||
state -> {
|
||||
int[] newState = state.clone();
|
||||
for (int i = 0; i < state.length; i++) {
|
||||
if (++newState[i] < sizes[i]) {
|
||||
return newState;
|
||||
}
|
||||
newState[i] = 0;
|
||||
}
|
||||
// wrapped-around
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// given the combination of indexes, return the expanded template
|
||||
static String expandTemplate(Case c, int[] combination) {
|
||||
|
||||
// 1st create a map: key -> replacement string
|
||||
Map<String, String> map = new HashMap<>(combination.length * 4 / 3 + 1);
|
||||
for (int i = 0; i < combination.length; i++) {
|
||||
String key = c.replacementKeys().get(i);
|
||||
String repl = c.replacements().get(key).get(combination[i]);
|
||||
map.put(key, repl);
|
||||
}
|
||||
|
||||
return Case.PLACEHOLDER_PATTERN
|
||||
.matcher(c.template())
|
||||
.replaceAll(match -> map.get(match.group(1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* compile expanded template into a ClassLoader that sees compiled classes
|
||||
*/
|
||||
static TestClassLoader compile(String source) throws CompileException {
|
||||
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
|
||||
if (javac == null) {
|
||||
throw new AssertionError("No Java compiler tool found.");
|
||||
}
|
||||
|
||||
ErrorsCollector errorsCollector = new ErrorsCollector();
|
||||
StandardJavaFileManager standardJavaFileManager =
|
||||
javac.getStandardFileManager(errorsCollector, Locale.ROOT,
|
||||
Charset.forName("UTF-8"));
|
||||
try {
|
||||
standardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, List.of());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
TestFileManager testFileManager = new TestFileManager(
|
||||
standardJavaFileManager, source);
|
||||
|
||||
JavaCompiler.CompilationTask javacTask;
|
||||
try {
|
||||
javacTask = javac.getTask(
|
||||
null, // use System.err
|
||||
testFileManager,
|
||||
errorsCollector,
|
||||
null,
|
||||
null,
|
||||
List.of(testFileManager.getJavaFileForInput(
|
||||
StandardLocation.SOURCE_PATH,
|
||||
TestFileManager.TEST_CLASS_NAME,
|
||||
JavaFileObject.Kind.SOURCE))
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
|
||||
javacTask.call();
|
||||
|
||||
if (errorsCollector.hasError()) {
|
||||
throw new CompileException(errorsCollector.getErrors());
|
||||
}
|
||||
|
||||
return new TestClassLoader(ClassLoader.getSystemClassLoader(),
|
||||
testFileManager);
|
||||
}
|
||||
|
||||
static class CompileException extends Exception {
|
||||
CompileException(List<Diagnostic<?>> diagnostics) {
|
||||
super(diagnostics.stream()
|
||||
.map(diag -> diag.toString())
|
||||
.collect(Collectors.joining("\n")));
|
||||
}
|
||||
}
|
||||
|
||||
static class TestFileManager
|
||||
extends ForwardingJavaFileManager<StandardJavaFileManager> {
|
||||
static final String TEST_CLASS_NAME = "Test";
|
||||
|
||||
private final String testSource;
|
||||
private final Map<String, ClassFileObject> classes = new HashMap<>();
|
||||
|
||||
TestFileManager(StandardJavaFileManager fileManager, String source) {
|
||||
super(fileManager);
|
||||
testSource = "public class " + TEST_CLASS_NAME + " {}\n" +
|
||||
source; // the rest of classes are package-private
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForInput(Location location,
|
||||
String className,
|
||||
JavaFileObject.Kind kind)
|
||||
throws IOException {
|
||||
if (location == StandardLocation.SOURCE_PATH &&
|
||||
kind == JavaFileObject.Kind.SOURCE &&
|
||||
TEST_CLASS_NAME.equals(className)) {
|
||||
return new SourceFileObject(className, testSource);
|
||||
}
|
||||
return super.getJavaFileForInput(location, className, kind);
|
||||
}
|
||||
|
||||
private static class SourceFileObject extends SimpleJavaFileObject {
|
||||
private final String source;
|
||||
|
||||
SourceFileObject(String className, String source) {
|
||||
super(
|
||||
URI.create("memory:/src/" +
|
||||
className.replace('.', '/') + ".java"),
|
||||
Kind.SOURCE
|
||||
);
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(Location location,
|
||||
String className,
|
||||
JavaFileObject.Kind kind,
|
||||
FileObject sibling)
|
||||
throws IOException {
|
||||
if (kind == JavaFileObject.Kind.CLASS) {
|
||||
ClassFileObject cfo = new ClassFileObject(className);
|
||||
classes.put(className, cfo);
|
||||
return cfo;
|
||||
}
|
||||
return super.getJavaFileForOutput(location, className, kind, sibling);
|
||||
}
|
||||
|
||||
private static class ClassFileObject extends SimpleJavaFileObject {
|
||||
final String className;
|
||||
ByteArrayOutputStream byteArrayOutputStream;
|
||||
|
||||
ClassFileObject(String className) {
|
||||
super(
|
||||
URI.create("memory:/out/" +
|
||||
className.replace('.', '/') + ".class"),
|
||||
Kind.CLASS
|
||||
);
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
return byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
byte[] getBytes() {
|
||||
if (byteArrayOutputStream == null) {
|
||||
throw new IllegalStateException(
|
||||
"No class file written for class: " + className);
|
||||
}
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
byte[] getClassBytes(String className) {
|
||||
ClassFileObject cfo = classes.get(className);
|
||||
return (cfo == null) ? null : cfo.getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
static class ErrorsCollector implements DiagnosticListener<JavaFileObject> {
|
||||
private final List<Diagnostic<?>> errors = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
|
||||
errors.add(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasError() {
|
||||
return !errors.isEmpty();
|
||||
}
|
||||
|
||||
List<Diagnostic<?>> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
static class TestClassLoader extends ClassLoader implements Closeable {
|
||||
private final TestFileManager fileManager;
|
||||
|
||||
public TestClassLoader(ClassLoader parent, TestFileManager fileManager) {
|
||||
super(parent);
|
||||
this.fileManager = fileManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] classBytes = fileManager.getClassBytes(name);
|
||||
if (classBytes == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return defineClass(name, classBytes, 0, classBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
fileManager.close();
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, String> generateResult(Case c, ClassLoader cl) {
|
||||
return
|
||||
c.classNames()
|
||||
.stream()
|
||||
.map(cn -> {
|
||||
try {
|
||||
return Class.forName(cn, false, cl);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Class not found: " + cn, e);
|
||||
}
|
||||
})
|
||||
.flatMap(clazz -> Stream.of(
|
||||
Map.entry(clazz.getName() + ".gM", generateGetMethodResult(clazz)),
|
||||
Map.entry(clazz.getName() + ".gMs", generateGetMethodsResult(clazz))
|
||||
))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
static String generateGetMethodResult(Class<?> clazz) {
|
||||
try {
|
||||
Method m = clazz.getMethod("m");
|
||||
return m.getDeclaringClass().getName() + "." + m.getName();
|
||||
} catch (NoSuchMethodException e) {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
|
||||
static String generateGetMethodsResult(Class<?> clazz) {
|
||||
return Stream.of(clazz.getMethods())
|
||||
.filter(m -> m.getDeclaringClass() != Object.class)
|
||||
.map(m -> m.getDeclaringClass().getName()
|
||||
+ "." + m.getName())
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
||||
static Stream<Map.Entry<int[], Map<String, String>>> generateResults(Case c) {
|
||||
return combinations(c)
|
||||
.flatMap(comb -> {
|
||||
String src = expandTemplate(c, comb);
|
||||
try {
|
||||
try (TestClassLoader cl = compile(src)) {
|
||||
// compilation was successful -> generate result
|
||||
return Stream.of(Map.entry(
|
||||
comb,
|
||||
generateResult(c, cl)
|
||||
));
|
||||
} catch (CompileException e) {
|
||||
// ignore uncompilable combinations
|
||||
return Stream.empty();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// from TestClassLoader.close()
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static Stream<Map.Entry<int[], Map<String, String>>> expectedResults(Case c) {
|
||||
try {
|
||||
BufferedReader r = new BufferedReader(new InputStreamReader(
|
||||
c.getClass().getResourceAsStream(
|
||||
c.getClass().getSimpleName() + ".results"),
|
||||
"UTF-8"
|
||||
));
|
||||
|
||||
return parseResults(r.lines())
|
||||
.onClose(() -> {
|
||||
try {
|
||||
r.close();
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Map.Entry<int[], Map<String, String>>> parseResults(
|
||||
Stream<String> lines
|
||||
) {
|
||||
return lines
|
||||
.map(l -> l.split(Pattern.quote("#")))
|
||||
.map(lkv -> Map.entry(
|
||||
Stream.of(lkv[0].split(Pattern.quote(",")))
|
||||
.mapToInt(Integer::parseInt)
|
||||
.toArray(),
|
||||
Stream.of(lkv[1].split(Pattern.quote("|")))
|
||||
.map(e -> e.split(Pattern.quote("=")))
|
||||
.collect(toMap(ekv -> ekv[0], ekv -> ekv[1]))
|
||||
));
|
||||
}
|
||||
|
||||
static Stream<String> dumpResults(
|
||||
Stream<Map.Entry<int[], Map<String, String>>> results
|
||||
) {
|
||||
return results
|
||||
.map(le ->
|
||||
IntStream.of(le.getKey())
|
||||
.mapToObj(String::valueOf)
|
||||
.collect(joining(","))
|
||||
+ "#" +
|
||||
le.getValue().entrySet().stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue())
|
||||
.collect(joining("|"))
|
||||
);
|
||||
}
|
||||
|
||||
static Stream<String> diffResults(
|
||||
Case c,
|
||||
Stream<Map.Entry<int[], Map<String, String>>> expectedResults
|
||||
) {
|
||||
return expectedResults
|
||||
.flatMap(exp -> {
|
||||
int[] comb = exp.getKey();
|
||||
Map<String, String> expected = exp.getValue();
|
||||
|
||||
String src = expandTemplate(c, comb);
|
||||
Map<String, String> actual;
|
||||
try {
|
||||
try (TestClassLoader cl = compile(src)) {
|
||||
actual = generateResult(c, cl);
|
||||
} catch (CompileException ce) {
|
||||
return Stream.of(src + "\n" +
|
||||
"got compilation error: " + ce);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// from TestClassLoader.close()
|
||||
return Stream.of(src + "\n" +
|
||||
"got IOException: " + ioe);
|
||||
}
|
||||
|
||||
if (actual.equals(expected)) {
|
||||
return Stream.empty();
|
||||
} else {
|
||||
Map<String, String> diff = new HashMap<>(expected);
|
||||
diff.entrySet().removeAll(actual.entrySet());
|
||||
return Stream.of(
|
||||
diff.entrySet()
|
||||
.stream()
|
||||
.map(e -> "expected: " + e.getKey() + ": " +
|
||||
e.getValue() + "\n" +
|
||||
" actual: " + e.getKey() + ": " +
|
||||
actual.get(e.getKey()) + "\n")
|
||||
.collect(joining("\n", src + "\n\n", "\n"))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue