From 2fbc27414e67561f909feb4147dfd65cd508ebf0 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 27 Dec 2015 23:43:06 +0100 Subject: [PATCH 01/14] First stab on rewriting internalRuntimeForbidden heuristics to be reliable without static signature file: - On Java 9 (w/wo Jigsaw) check module system for runtime class (module name starts with "java." or "jdk.") - Invert the package pattern: Only list packages in official Java documentation (java.*, javax.* and few others extracted from Javadocs) - Start to rename the setting: internalRuntimeForbidden -> nonPortableRuntimeForbidden --- .../de/thetaphi/forbiddenapis/AsmUtils.java | 49 ++++++++++++++++--- .../de/thetaphi/forbiddenapis/Checker.java | 43 ++++++++++------ .../thetaphi/forbiddenapis/ClassScanner.java | 24 +++++---- .../signatures/jdk-deprecated-1.7.txt | 2 +- .../signatures/jdk-deprecated-1.8.txt | 5 +- src/test/antunit/TestInternalRuntimeCalls.xml | 2 +- .../thetaphi/forbiddenapis/AsmUtilsTest.java | 29 ++++++++--- .../thetaphi/forbiddenapis/DeprecatedGen.java | 2 +- 8 files changed, 109 insertions(+), 47 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java b/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java index 3ceeb826..b2487b20 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java +++ b/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java @@ -16,6 +16,7 @@ * limitations under the License. */ +import java.net.URL; import java.util.Arrays; import java.util.Locale; import java.util.regex.Pattern; @@ -26,24 +27,44 @@ public final class AsmUtils { private AsmUtils() {} private static final String REGEX_META_CHARS = ".^$+{}[]|()\\"; - private static final Pattern INTERNAL_PACKAGE_PATTERN; - static { + + /** Package prefixes of documented Java API (extracted from Javadocs of Java 8). */ + private static final Pattern PORTABLE_RUNTIME_PACKAGE_PATTERN = makePkgPrefixPattern("java", "javax", "org.ietf.jgss", "org.omg", "org.w3c.dom", "org.xml.sax"); + + /** Pattern that matches all module names, which are shipped by default in Java. + * (see: {@code http://openjdk.java.net/projects/jigsaw/spec/sotms/}): + * The remaining platform modules will share the 'java.' name prefix and are likely to include, + * e.g., java.sql for database connectivity, java.xml for XML processing, and java.logging for + * logging. Modules that are not defined in the Java SE 9 Platform Specification but instead + * specific to the JDK will, by convention, share the 'jdk.' name prefix. + */ + private static final Pattern RUNTIME_MODULES_PATTERN = makePkgPrefixPattern("java", "jdk"); + + private static Pattern makePkgPrefixPattern(String... prefixes) { final StringBuilder sb = new StringBuilder(); boolean first = true; - for (final String pkg : Arrays.asList("sun.", "oracle.", "com.sun.", "com.oracle.", "jdk.", "sunw.")) { + for (final String pkg : Arrays.asList(prefixes)) { sb.append(first ? '(' : '|').append(Pattern.quote(pkg)); first = false; } - INTERNAL_PACKAGE_PATTERN = Pattern.compile(sb.append(").*").toString()); + sb.append(")").append(Pattern.quote(".")).append(".*"); + return Pattern.compile(sb.toString()); } private static boolean isRegexMeta(char c) { return REGEX_META_CHARS.indexOf(c) != -1; } - /** Returns true, if the given binary class name (dotted) is likely a internal class (like sun.misc.Unsafe) */ - public static boolean isInternalClass(String className) { - return INTERNAL_PACKAGE_PATTERN.matcher(className).matches(); + /** Returns true, if the given binary class name (dotted) is part of the documented and portable Java APIs. */ + public static boolean isPortableRuntimeClass(String className) { + return PORTABLE_RUNTIME_PACKAGE_PATTERN.matcher(className).matches(); + } + + /** Returns true, if the given Java 9 module name is part of the runtime (no custom 3rd party module). + * @param module the module name or {@code null}, if in unnamed module + */ + public static boolean isRuntimeModule(String module) { + return module != null && RUNTIME_MODULES_PATTERN.matcher(module).matches(); } /** Converts a binary class name (dotted) to the JVM internal one (slashed). Only accepts valid class names, no arrays. */ @@ -103,5 +124,19 @@ public static Pattern glob2Pattern(String... globs) { } return Pattern.compile(regex.toString(), 0); } + + /** Returns the module name from a {@code jrt:/} URL. */ + public static String getModuleName(URL jrtUrl) { + String mod = jrtUrl.getPath(); + if (mod != null) { + mod = mod.substring(1); + int p = mod.indexOf('/'); + if (p >= 0) { + mod = mod.substring(0, p); + } + return mod.isEmpty() ? null : mod; + } + return null; + } } diff --git a/src/main/java/de/thetaphi/forbiddenapis/Checker.java b/src/main/java/de/thetaphi/forbiddenapis/Checker.java index bb76230a..040ba31a 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/Checker.java +++ b/src/main/java/de/thetaphi/forbiddenapis/Checker.java @@ -72,7 +72,7 @@ public static enum Option { final NavigableSet runtimePaths; final ClassLoader loader; - final java.lang.reflect.Method method_Class_getModule, method_Module_getResourceAsStream; + final java.lang.reflect.Method method_Class_getModule, method_Module_getResourceAsStream, method_Module_getName; final EnumSet