diff --git a/base/src/main/java/proguard/analysis/datastructure/callgraph/CallGraph.java b/base/src/main/java/proguard/analysis/datastructure/callgraph/CallGraph.java index 98e860a0..58c1fa24 100644 --- a/base/src/main/java/proguard/analysis/datastructure/callgraph/CallGraph.java +++ b/base/src/main/java/proguard/analysis/datastructure/callgraph/CallGraph.java @@ -18,10 +18,14 @@ package proguard.analysis.datastructure.callgraph; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; -import java.util.stream.Collectors; +import org.jetbrains.annotations.Nullable; import proguard.classfile.ClassPool; import proguard.classfile.Clazz; import proguard.classfile.MethodSignature; @@ -31,8 +35,6 @@ /** * Collection of all {@link Call}s in a program, optimized for retrieval of incoming and outgoing * edges for any method in constant time. - * - * @author Samuel Hopstock */ public class CallGraph { @@ -40,9 +42,6 @@ public class CallGraph { public final Map> incoming; public final Map> outgoing; - /** If true, incoming edges are not explored further for known entry points. */ - private static final boolean STOP_AT_ENTRYPOINT = true; - private final boolean concurrent; /** Create an empty call graph. */ @@ -98,21 +97,25 @@ public void clear() { } /** - * See {@link #reconstructCallGraph(ClassPool, MethodSignature, int, int)} + * See {@link #reconstructCallGraph(ClassPool, MethodSignature, int, int, Set)} * * @param programClassPool The current {@link ClassPool} of the program that can be used for * mapping. class names to the actual {@link Clazz}. * @param start The {@link MethodSignature} of the method whose incoming call graph should be * calculated. + * @param stopMethods Set of {@link MethodSignature} to stop exploration at, if desired. * @return A {@link Node} that represents the single call graph root, i.e. the start method. */ - public Node reconstructCallGraph(ClassPool programClassPool, MethodSignature start) { + public Node reconstructCallGraph( + ClassPool programClassPool, MethodSignature start, Set stopMethods) { return CallGraphWalker.predecessorPathsAccept( - this, start, n -> handleUntilEntryPoint(programClassPool, n, null)); + this, start, n -> handleUntil(programClassPool, n, stopMethods, null)); } /** - * Calculate the incoming call graph for a method of interest, showing how it can be reached. + * Calculate the incoming call graph for a method of interest, showing how it can be reached from + * a given Set of stop methods, which typically are Android lifecycle methods such as an + * Activity's onCreate() method: * *

We have an inverted tree structure like the following example: * @@ -127,11 +130,6 @@ public Node reconstructCallGraph(ClassPool programClassPool, MethodSignature sta * shows that it can be reached from {@code onCreate()} via {@code proxy()}, and also directly * from {@code onResume()} or {@code unusedMethod()}. * - *

Generally, we still can't be sure whether the top most methods (leaves in the tree) can be - * reached themselves, if we don't find any incoming edges. But if these methods are {@link - * EntryPoint}s of an Android app, they will most likely be called at some point in the app - * lifecycle. - * * @param programClassPool The current {@link ClassPool} of the program that can be used for * mapping. class names to the actual {@link Clazz}. * @param start The {@link MethodSignature} of the method whose incoming call graph should be @@ -140,100 +138,75 @@ public Node reconstructCallGraph(ClassPool programClassPool, MethodSignature sta * CallGraphWalker#MAX_DEPTH_DEFAULT}. * @param maxWidth maximal width of reconstructed {@link CallGraph} similar to {@link * CallGraphWalker#MAX_WIDTH_DEFAULT}. + * @param stopMethods Set of method signatures to stop exploration, for example for entry points * @return A {@link Node} that represents the single call graph root, i.e. the start method. */ public Node reconstructCallGraph( - ClassPool programClassPool, MethodSignature start, int maxDepth, int maxWidth) { + ClassPool programClassPool, + MethodSignature start, + int maxDepth, + int maxWidth, + Set stopMethods) { return CallGraphWalker.predecessorPathsAccept( - this, start, n -> handleUntilEntryPoint(programClassPool, n, null), maxDepth, maxWidth); + this, start, n -> handleUntil(programClassPool, n, stopMethods, null), maxDepth, maxWidth); } /** - * Extension of {@link #reconstructCallGraph(ClassPool, MethodSignature)} that also collects all - * {@link EntryPoint}s found along the way. + * Extension of {@link #reconstructCallGraph(ClassPool, MethodSignature, Set)} that also collects + * all reached stop methods. * * @param programClassPool The current {@link ClassPool} of the program that can be used for * mapping. * @param start The {@link MethodSignature} of the method whose incoming call graph should be * calculated. - * @param entryPoints A set that will be filled with all {@link EntryPoint}s that are part of the - * incoming call graph. + * @param stopMethods A set of {@link MethodSignature} to stop exploration, e.g. app entry points + * @param reachedMethods A set that will be filled with all reached stop methods * @return A {@link Node} that represents the single call graph root, i.e. the start method. */ public Node reconstructCallGraph( - ClassPool programClassPool, MethodSignature start, Set entryPoints) { + ClassPool programClassPool, + MethodSignature start, + Set stopMethods, + Set reachedMethods) { return CallGraphWalker.predecessorPathsAccept( - this, start, n -> handleUntilEntryPoint(programClassPool, n, entryPoints)); + this, start, n -> handleUntil(programClassPool, n, stopMethods, reachedMethods)); } /** * Handler implementation for {@link CallGraphWalker#predecessorPathsAccept(CallGraph, - * MethodSignature, Predicate)} that checks discovered paths if they have arrived at a known entry - * point. + * MethodSignature, Predicate)} that checks if one of a given set of stop methods has been reached + * along the call graph paths. * * @param programClassPool The current {@link ClassPool} of the program that can be used for * mapping class names to the actual {@link Clazz}. - * @param curr The {@link Node} that represents the currently discovered call graph node and its - * successors. - * @param entryPoints a set containing the entrypoints seen on this path, will be filled during - * the reconstruction of the callgraph. - * @return true if we have arrived at an entry point, so that the {@link CallGraphWalker} stops - * exploring this particular path. + * @param current The {@link Node} that represents the currently discovered call graph node and + * its successors. + * @param reachedMethods A set for collecting reached stop methods, can be null. + * @return true if call graph exploration should continue, false otherwise. */ - private boolean handleUntilEntryPoint( - ClassPool programClassPool, Node curr, Set entryPoints) { - // Get all classes that contain known entryPoints and are superclasses of the current one - Clazz currClass = programClassPool.getClass(curr.signature.getClassName()); - if (currClass == null) { - log.warn("Could not find class {} in class pool", curr.signature.getClassName()); - curr.isTruncated = true; + private boolean handleUntil( + ClassPool programClassPool, + Node current, + Set stopMethods, + @Nullable Set reachedMethods) { + + MethodSignature currentSignature = current.signature; + String currentClassName = currentSignature.getClassName(); + Clazz currentClass = programClassPool.getClass(currentClassName); + + if (currentClass == null) { + log.warn("Could not find class {} in class pool", currentClassName); + current.isTruncated = true; return false; } - Set entrypointSuperclassNames = - EntryPoint.WELL_KNOWN_ENTRYPOINT_CLASSES.stream() - .filter(e -> classExtendsOrEquals(currClass, e.replace('.', '/'))) - .collect(Collectors.toSet()); - // If we are in a method overriding any known entrypoint, that's a call graph leaf - Optional matchingEntrypoint = - EntryPoint.WELL_KNOWN_ENTRYPOINTS.stream() - .filter( - e -> - entrypointSuperclassNames.contains(e.className) - && e.methodName.equals(curr.signature.method)) - .findFirst(); - if (matchingEntrypoint.isPresent()) { - curr.matchingEntrypoint = matchingEntrypoint.get(); - if (entryPoints != null) { - entryPoints.add( - new EntryPoint( - matchingEntrypoint.get().type, - curr.signature.getClassName(), - curr.signature.method)); - } - return !STOP_AT_ENTRYPOINT; - } - return true; - } - /** - * Check if a {@link Clazz} either matches the provided class name or extends this provided class. - * Both direct and transitive inheritance is allowed. - * - * @param currClass The {@link Clazz} that might be equal to or a subclass of the provided class - * name. - * @param className The potential super class name. - * @return True if currClass is equal to className or is one of its subclasses. - */ - private boolean classExtendsOrEquals(Clazz currClass, String className) { - if (Objects.equals(currClass.getSuperName(), className)) { - return true; - } - if (currClass.getSuperClass() != null) { - if (Objects.equals(currClass.getSuperClass().getName(), className)) { - return true; + if (stopMethods.contains(currentSignature)) { + if (reachedMethods != null) { + reachedMethods.add(currentSignature); } - return classExtendsOrEquals(currClass.getSuperClass(), className); + return false; } - return false; + + return true; } } diff --git a/base/src/main/java/proguard/analysis/datastructure/callgraph/EntryPoint.java b/base/src/main/java/proguard/analysis/datastructure/callgraph/EntryPoint.java deleted file mode 100644 index d9c22872..00000000 --- a/base/src/main/java/proguard/analysis/datastructure/callgraph/EntryPoint.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * ProGuardCORE -- library to process Java bytecode. - * - * Copyright (c) 2002-2021 Guardsquare NV - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package proguard.analysis.datastructure.callgraph; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import proguard.classfile.MethodDescriptor; -import proguard.classfile.MethodSignature; - -/** - * This class serves as a static collection of entry points for an app. An entry point is a method - * that is provided by the Android framework and can be hooked by the app to be called at a specific - * point in the app lifecycle. - * - *

The list of well known entry points has been curated using the official - * documentation and a third-party - * collection. - * - * @author Samuel Hopstock - */ -public class EntryPoint { - - public enum Type { - ACTIVITY, - SERVICE, - BROADCAST_RECEIVER, - CONTENT_PROVIDER - } - - public static final List ENTRY_POINTS_ACTIVITY = - Arrays.asList( - new EntryPoint(Type.ACTIVITY, "android.app.Activity", ""), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", ""), - // General lifecycle - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onCreate"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onStart"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onRestart"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onResume"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onPause"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onStop"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onDestroy"), - // Additional known entrypoints - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onAttachFragment"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onContentChanged"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onActivityResult"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onRestoreInstanceState"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onPostCreate"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onPostResume"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onAttachedToWindow"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onCreateOptionsMenu"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onPrepareOptionsMenu"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onUserInteraction"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onUserLeaveHint"), - new EntryPoint(Type.ACTIVITY, "android.app.Activity", "onSaveInstanceState")); - public static final List ENTRY_POINTS_FRAGMENT = - Arrays.asList( - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", ""), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", ""), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onInflate"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onAttach"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onCreate"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onCreateView"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onViewCreated"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onActivityCreated"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onViewStateRestored"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onStart"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onResume"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onCreateOptionsMenu"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onPrepareOptionsMenu"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onPause"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onSaveInstanceState"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onStop"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onDestroyView"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onDestroy"), - new EntryPoint(Type.ACTIVITY, "android.app.Fragment", "onDetach")); - public static final List ENTRY_POINTS_SERVICE = - Arrays.asList( - new EntryPoint(Type.SERVICE, "android.app.Service", ""), - new EntryPoint(Type.SERVICE, "android.app.Service", ""), - new EntryPoint(Type.SERVICE, "android.app.Service", "onCreate"), - new EntryPoint(Type.SERVICE, "android.app.Service", "onStartCommand"), - new EntryPoint(Type.SERVICE, "android.app.Service", "onBind"), - new EntryPoint(Type.SERVICE, "android.app.Service", "onUnbind"), - new EntryPoint(Type.SERVICE, "android.app.Service", "onRebind"), - new EntryPoint(Type.SERVICE, "android.app.Service", "onDestroy")); - public static final List ENTRY_POINTS_BROADCAST_RECEIVER = - Arrays.asList( - new EntryPoint(Type.BROADCAST_RECEIVER, "android.content.BroadcastReceiver", ""), - new EntryPoint(Type.BROADCAST_RECEIVER, "android.content.BroadcastReceiver", ""), - new EntryPoint( - Type.BROADCAST_RECEIVER, "android.content.BroadcastReceiver", "onReceive")); - public static final List ENTRY_POINTS_CONTENT_PROVIDER = - Arrays.asList( - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", ""), - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", ""), - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", "onCreate"), - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", "query"), - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", "insert"), - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", "update"), - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", "delete"), - new EntryPoint(Type.CONTENT_PROVIDER, "android.content.ContentProvider", "getType")); - public static final List WELL_KNOWN_ENTRYPOINTS = - Stream.of( - ENTRY_POINTS_ACTIVITY, - ENTRY_POINTS_FRAGMENT, - ENTRY_POINTS_SERVICE, - ENTRY_POINTS_BROADCAST_RECEIVER, - ENTRY_POINTS_CONTENT_PROVIDER) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - public static final Set WELL_KNOWN_ENTRYPOINT_CLASSES = - WELL_KNOWN_ENTRYPOINTS.stream().map(e -> e.className).collect(Collectors.toSet()); - - public final String className; - public final String methodName; - public final Type type; - - public EntryPoint(Type type, String className, String methodName) { - this.type = type; - this.className = className; - this.methodName = methodName; - } - - public static List getEntryPointsForType(Type type) { - switch (type) { - case ACTIVITY: - return ENTRY_POINTS_ACTIVITY; - case SERVICE: - return ENTRY_POINTS_SERVICE; - case BROADCAST_RECEIVER: - return ENTRY_POINTS_BROADCAST_RECEIVER; - case CONTENT_PROVIDER: - return ENTRY_POINTS_CONTENT_PROVIDER; - default: - throw new IllegalStateException("Unsupported entry point type"); - } - } - - public MethodSignature toSignature() { - return new MethodSignature(className.replace('.', '/'), methodName, (MethodDescriptor) null); - } - - @Override - public String toString() { - return toSignature().toString() + " (" + type + ")"; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - EntryPoint that = (EntryPoint) o; - return Objects.equals(className, that.className) && Objects.equals(methodName, that.methodName); - } - - @Override - public int hashCode() { - return Objects.hash(className, methodName); - } -} diff --git a/base/src/main/java/proguard/analysis/datastructure/callgraph/Node.java b/base/src/main/java/proguard/analysis/datastructure/callgraph/Node.java index 473c5a75..e7b6db67 100644 --- a/base/src/main/java/proguard/analysis/datastructure/callgraph/Node.java +++ b/base/src/main/java/proguard/analysis/datastructure/callgraph/Node.java @@ -48,7 +48,6 @@ public class Node { public final Set outgoingCallLocations = new HashSet<>(); public final Set successors = new HashSet<>(); - public EntryPoint matchingEntrypoint = null; public boolean isTruncated = false; public Node(MethodSignature signature) { @@ -78,7 +77,7 @@ public boolean predecessorsContain(MethodSignature signature) { if (this.signature.equals(signature)) { return true; } - return successors.stream().anyMatch(s -> s.predecessorsContain(signature)); + return predecessors.stream().anyMatch(s -> s.predecessorsContain(signature)); } /** diff --git a/base/src/main/java/proguard/classfile/JavaVersionConstants.java b/base/src/main/java/proguard/classfile/JavaVersionConstants.java index 07e2d7c7..38978d56 100644 --- a/base/src/main/java/proguard/classfile/JavaVersionConstants.java +++ b/base/src/main/java/proguard/classfile/JavaVersionConstants.java @@ -52,4 +52,5 @@ public class JavaVersionConstants { public static final String CLASS_VERSION_20 = "20"; public static final String CLASS_VERSION_21 = "21"; public static final String CLASS_VERSION_22 = "22"; + public static final String CLASS_VERSION_23 = "23"; } diff --git a/base/src/main/java/proguard/classfile/VersionConstants.java b/base/src/main/java/proguard/classfile/VersionConstants.java index a62c76c2..bbbecb13 100644 --- a/base/src/main/java/proguard/classfile/VersionConstants.java +++ b/base/src/main/java/proguard/classfile/VersionConstants.java @@ -69,6 +69,8 @@ public class VersionConstants { public static final int CLASS_VERSION_21_MINOR = 0; public static final int CLASS_VERSION_22_MAJOR = 66; public static final int CLASS_VERSION_22_MINOR = 0; + public static final int CLASS_VERSION_23_MAJOR = 67; + public static final int CLASS_VERSION_23_MINOR = 0; public static final int PREVIEW_VERSION_MINOR = 65535; public static final int CLASS_VERSION_1_0 = @@ -115,7 +117,9 @@ public class VersionConstants { (CLASS_VERSION_21_MAJOR << 16) | CLASS_VERSION_21_MINOR; public static final int CLASS_VERSION_22 = (CLASS_VERSION_22_MAJOR << 16) | CLASS_VERSION_22_MINOR; + public static final int CLASS_VERSION_23 = + (CLASS_VERSION_23_MAJOR << 16) | CLASS_VERSION_23_MINOR; public static final int MAX_SUPPORTED_VERSION = - (CLASS_VERSION_22_MAJOR << 16) | PREVIEW_VERSION_MINOR; + (CLASS_VERSION_23_MAJOR << 16) | PREVIEW_VERSION_MINOR; } diff --git a/base/src/main/java/proguard/classfile/util/ClassUtil.java b/base/src/main/java/proguard/classfile/util/ClassUtil.java index a2a77c89..06aefb70 100644 --- a/base/src/main/java/proguard/classfile/util/ClassUtil.java +++ b/base/src/main/java/proguard/classfile/util/ClassUtil.java @@ -94,98 +94,61 @@ public static int internalMinorClassVersion(int internalClassVersion) { * @return the internal class version number. */ public static int internalClassVersion(String externalClassVersion) { - return externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_0) - || externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_1) - ? VersionConstants.CLASS_VERSION_1_0 - : externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_2) - ? VersionConstants.CLASS_VERSION_1_2 - : externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_3) - ? VersionConstants.CLASS_VERSION_1_3 - : externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_4) - ? VersionConstants.CLASS_VERSION_1_4 - : externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_5_ALIAS) - || externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_5) - ? VersionConstants.CLASS_VERSION_1_5 - : externalClassVersion.equals(JavaVersionConstants.CLASS_VERSION_1_6_ALIAS) - || externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_1_6) - ? VersionConstants.CLASS_VERSION_1_6 - : externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_1_7_ALIAS) - || externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_1_7) - ? VersionConstants.CLASS_VERSION_1_7 - : externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_1_8_ALIAS) - || externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_1_8) - ? VersionConstants.CLASS_VERSION_1_8 - : externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_1_9_ALIAS) - || externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_1_9) - ? VersionConstants.CLASS_VERSION_1_9 - : externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_10) - ? VersionConstants.CLASS_VERSION_10 - : externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_11) - ? VersionConstants.CLASS_VERSION_11 - : externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_12) - ? VersionConstants.CLASS_VERSION_12 - : externalClassVersion.equals( - JavaVersionConstants.CLASS_VERSION_13) - ? VersionConstants.CLASS_VERSION_13 - : externalClassVersion.equals( - JavaVersionConstants - .CLASS_VERSION_14) - ? VersionConstants.CLASS_VERSION_14 - : externalClassVersion.equals( - JavaVersionConstants - .CLASS_VERSION_15) - ? VersionConstants.CLASS_VERSION_15 - : externalClassVersion.equals( - JavaVersionConstants - .CLASS_VERSION_16) - ? VersionConstants - .CLASS_VERSION_16 - : externalClassVersion.equals( - JavaVersionConstants - .CLASS_VERSION_17) - ? VersionConstants - .CLASS_VERSION_17 - : externalClassVersion - .equals( - JavaVersionConstants - .CLASS_VERSION_18) - ? VersionConstants - .CLASS_VERSION_18 - : externalClassVersion - .equals( - JavaVersionConstants - .CLASS_VERSION_19) - ? VersionConstants - .CLASS_VERSION_19 - : externalClassVersion - .equals( - JavaVersionConstants - .CLASS_VERSION_20) - ? VersionConstants - .CLASS_VERSION_20 - : externalClassVersion - .equals( - JavaVersionConstants - .CLASS_VERSION_21) - ? VersionConstants - .CLASS_VERSION_21 - : externalClassVersion - .equals( - JavaVersionConstants - .CLASS_VERSION_22) - ? VersionConstants - .CLASS_VERSION_22 - : 0; + switch (externalClassVersion) { + case JavaVersionConstants.CLASS_VERSION_1_0: + case JavaVersionConstants.CLASS_VERSION_1_1: + return VersionConstants.CLASS_VERSION_1_0; + case JavaVersionConstants.CLASS_VERSION_1_2: + return VersionConstants.CLASS_VERSION_1_2; + case JavaVersionConstants.CLASS_VERSION_1_3: + return VersionConstants.CLASS_VERSION_1_3; + case JavaVersionConstants.CLASS_VERSION_1_4: + return VersionConstants.CLASS_VERSION_1_4; + case JavaVersionConstants.CLASS_VERSION_1_5_ALIAS: + case JavaVersionConstants.CLASS_VERSION_1_5: + return VersionConstants.CLASS_VERSION_1_5; + case JavaVersionConstants.CLASS_VERSION_1_6_ALIAS: + case JavaVersionConstants.CLASS_VERSION_1_6: + return VersionConstants.CLASS_VERSION_1_6; + case JavaVersionConstants.CLASS_VERSION_1_7_ALIAS: + case JavaVersionConstants.CLASS_VERSION_1_7: + return VersionConstants.CLASS_VERSION_1_7; + case JavaVersionConstants.CLASS_VERSION_1_8_ALIAS: + case JavaVersionConstants.CLASS_VERSION_1_8: + return VersionConstants.CLASS_VERSION_1_8; + case JavaVersionConstants.CLASS_VERSION_1_9_ALIAS: + case JavaVersionConstants.CLASS_VERSION_1_9: + return VersionConstants.CLASS_VERSION_1_9; + case JavaVersionConstants.CLASS_VERSION_10: + return VersionConstants.CLASS_VERSION_10; + case JavaVersionConstants.CLASS_VERSION_11: + return VersionConstants.CLASS_VERSION_11; + case JavaVersionConstants.CLASS_VERSION_12: + return VersionConstants.CLASS_VERSION_12; + case JavaVersionConstants.CLASS_VERSION_13: + return VersionConstants.CLASS_VERSION_13; + case JavaVersionConstants.CLASS_VERSION_14: + return VersionConstants.CLASS_VERSION_14; + case JavaVersionConstants.CLASS_VERSION_15: + return VersionConstants.CLASS_VERSION_15; + case JavaVersionConstants.CLASS_VERSION_16: + return VersionConstants.CLASS_VERSION_16; + case JavaVersionConstants.CLASS_VERSION_17: + return VersionConstants.CLASS_VERSION_17; + case JavaVersionConstants.CLASS_VERSION_18: + return VersionConstants.CLASS_VERSION_18; + case JavaVersionConstants.CLASS_VERSION_19: + return VersionConstants.CLASS_VERSION_19; + case JavaVersionConstants.CLASS_VERSION_20: + return VersionConstants.CLASS_VERSION_20; + case JavaVersionConstants.CLASS_VERSION_21: + return VersionConstants.CLASS_VERSION_21; + case JavaVersionConstants.CLASS_VERSION_22: + return VersionConstants.CLASS_VERSION_22; + case JavaVersionConstants.CLASS_VERSION_23: + return VersionConstants.CLASS_VERSION_23; + } + return 0; } /** @@ -240,6 +203,8 @@ public static String externalClassVersion(int internalClassVersion) { return JavaVersionConstants.CLASS_VERSION_21; case VersionConstants.CLASS_VERSION_22: return JavaVersionConstants.CLASS_VERSION_22; + case VersionConstants.CLASS_VERSION_23: + return JavaVersionConstants.CLASS_VERSION_23; default: return null; } diff --git a/base/src/test/kotlin/proguard/classfile/util/ClassUtilTest.kt b/base/src/test/kotlin/proguard/classfile/util/ClassUtilTest.kt index 717c295b..bf68125b 100644 --- a/base/src/test/kotlin/proguard/classfile/util/ClassUtilTest.kt +++ b/base/src/test/kotlin/proguard/classfile/util/ClassUtilTest.kt @@ -29,6 +29,25 @@ class ClassUtilTest : BehaviorSpec({ } } + Given("Class file version 23") { + val version = 67 shl 16 + Then("The external version should be 23") { + externalClassVersion(version) shouldBe "23" + } + + And("Given a class file with version 23") { + val programClass = ClassBuilder( + version, + PUBLIC, + "Test", + "java/lang/Object", + ).programClass + Then("The internal version should be 23") { + programClass.u4version shouldBe internalClassVersion("23") + } + } + } + Given("A valid magic number") { Then("No exception should be thrown") { shouldNotThrowAny { diff --git a/docs/md/releasenotes.md b/docs/md/releasenotes.md index 565ad17c..d8cc7627 100644 --- a/docs/md/releasenotes.md +++ b/docs/md/releasenotes.md @@ -1,3 +1,18 @@ +## Version 9.1.6 + +### Java support + +- Update maximum supported Java class version to 67.65535 (Java 23). + +### API changes + +- Make `CallGraph` reconstruction/traversal methods generic in that they now take a `Set` to determine when to stop exploration +- Delete `EntryPoint` class + +### Bugfixes + +- Fix incorrect traversal direction in `predecessorsContain`. + ## Version 9.1.5 ### Improved diff --git a/gradle.properties b/gradle.properties index feac7be5..1a056d13 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Gradle build properties for ProGuard. -proguardCoreVersion = 9.1.6-SNAPSHOT +proguardCoreVersion = 9.1.7-SNAPSHOT kotlinVersion = 2.0.0