Skip to content

Upgrade Gradle and AGP, add docs and impl save cache#9

Closed
Howard20181 wants to merge 80 commits intolibxposed:masterfrom
Howard20181:docs
Closed

Upgrade Gradle and AGP, add docs and impl save cache#9
Howard20181 wants to merge 80 commits intolibxposed:masterfrom
Howard20181:docs

Conversation

@Howard20181
Copy link
Copy Markdown

No description provided.

@Howard20181 Howard20181 marked this pull request as ready for review February 9, 2026 13:52
Copilot AI review requested due to automatic review settings February 9, 2026 13:52
@Howard20181 Howard20181 marked this pull request as draft February 9, 2026 13:53
@Howard20181
Copy link
Copy Markdown
Author

尝试实现按条件查找方法,发现 LSPosedDexParser 返回的 invoked_methods 全是空的

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the build/tooling (Gradle wrapper + Android Gradle Plugin), expands public API documentation for HookBuilder, and adds infrastructure intended to persist and reuse DEX/matcher caches across runs (including new DEX-invocation relationship collection).

Changes:

  • Upgrade Gradle wrapper and AGP; add Foojay toolchain resolver support.
  • Add extensive Javadoc/KDoc for HookBuilder and Kotlin extensions.
  • Add (de)serialization for MatchCache and introduce method/constructor invocation relationship tracking during DEX analysis.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
settings.gradle.kts Upgrades AGP and adds Foojay toolchain resolver plugin.
helper/build.gradle.kts Bumps compileSdk and androidx annotation dependency versions.
helper/proguard-rules.pro Reworks R8/Proguard rules for reflection-heavy helper library.
helper/src/main/java/io/github/libxposed/helper/Misc.java Adds cache encoding helpers for reflection objects.
helper/src/main/java/io/github/libxposed/helper/HookBuilderImpl.java Implements cache save, adjusts DEX reading, adds invocation graph collection, and wires cache observers.
helper/src/main/java/io/github/libxposed/helper/HookBuilder.java Adds extensive API documentation and usage examples.
helper-ktx/src/main/kotlin/io/github/libxposed/helper/ktx/HookBuilderKt.kt Adds documentation for parameter matching and signature helpers.
helper-ktx/build.gradle.kts Updates compileSdk and fixes signing to run only when keys exist.
gradlew / gradlew.bat Wrapper script updates from Gradle upgrade.
gradle/wrapper/gradle-wrapper.properties Points wrapper at Gradle 8.14.4 and adds wrapper safety knobs.
gradle/wrapper/gradle-wrapper.jar Updates wrapper JAR to match the new Gradle version.
gradle/gradle-daemon-jvm.properties Adds generated Foojay toolchain URLs / JVM toolchain settings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +253 to +290
static String encodeField(@Nullable java.lang.reflect.Field field) {
if (field == null) return "";
return field.getDeclaringClass().getName() + "->" + field.getName() + ":" + field.getType().getName();
}

/**
* Encode a Method to its string representation for caching.
* Format: declaringClass->methodName(param1,param2,...)returnType
* Returns empty string if method is null.
*/
@NonNull
static String encodeMethod(@Nullable java.lang.reflect.Method method) {
if (method == null) return "";
var params = new StringBuilder();
var parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) params.append(",");
params.append(parameterTypes[i].getName());
}
return method.getDeclaringClass().getName() + "->" + method.getName() + "(" + params + ")" + method.getReturnType().getName();
}

/**
* Encode a Constructor to its string representation for caching.
* Format: declaringClass-><init>(param1,param2,...)V
* Returns empty string if constructor is null.
*/
@NonNull
static String encodeConstructor(@Nullable java.lang.reflect.Constructor<?> constructor) {
if (constructor == null) return "";
var params = new StringBuilder();
var parameterTypes = constructor.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) params.append(",");
params.append(parameterTypes[i].getName());
}
return constructor.getDeclaringClass().getName() + "-><init>(" + params + ")V";
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

encodeField/encodeMethod/encodeConstructor generate signatures using Java type names + commas, but the cache reload path feeds these strings into Reflector.loadField/loadMethod/loadConstructor, which expect either Smali descriptors (when using ->) or the specific Java-like formats those parsers support. As-is, cached entries for methods/constructors/fields will fail to deserialize and will always miss; please align the encoded format with what Reflector can parse (e.g., emit Smali descriptors, or emit the non--> Java-like formats that Reflector supports).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback align the encoded format with Smali descriptors

Comment thread helper/src/main/java/io/github/libxposed/helper/HookBuilderImpl.java Outdated
Comment on lines 558 to 560
} catch (Throwable e) {
if (exceptionHandler != null) exceptionHandler.test(e);
return;
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outer catch (Throwable e) in analysisDex() now silently returns without invoking exceptionHandler. This makes DEX parsing failures very hard to diagnose and changes behavior from other error paths in this class; please report the exception via exceptionHandler (or rethrow) before returning.

Copilot uses AI. Check for mistakes.
Comment on lines +748 to +758
private Class<?>[] getParameterTypesFromProto(DexParser.ProtoId protoId) {
try {
var paramTypeIds = protoId.getParameters();
if (paramTypeIds == null || paramTypeIds.length == 0) {
return new Class<?>[0];
}
var paramTypes = new Class<?>[paramTypeIds.length];
for (int i = 0; i < paramTypeIds.length; i++) {
var descriptor = paramTypeIds[i].getDescriptor().getString();
paramTypes[i] = reflector.loadClass(descriptor);
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getParameterTypesFromProto() passes DEX descriptors directly to reflector.loadClass(). For primitive descriptors (e.g., "I", "Z"), Reflector.loadClass() will throw ClassNotFoundException, so any method with primitive parameters will fail to resolve and be skipped. Please map primitive descriptors to their primitive Class (and handle arrays) before falling back to reflector.loadClass() for reference types.

Copilot uses AI. Check for mistakes.
Comment on lines +1737 to +1763
// Check invoked methods constraint
if (invokedMethods != null && reflect instanceof Method) {
var currentMethod = (Method) reflect;
var invokedMethodsSet = methodInvocationsMap.get(currentMethod);
if (invokedMethodsSet == null || invokedMethodsSet.isEmpty()) {
return false;
}

// Test if the invoked methods set matches the constraint
var hashSet = new HashSet<>(invokedMethodsSet);
if (!invokedMethods.test(hashSet)) {
return false;
}
}

// Check invoked constructors constraint
if (invokedConstructors != null && reflect instanceof Method) {
var currentMethod = (Method) reflect;
var invokedConstructorsSet = constructorInvocationsMap.get(currentMethod);
if (invokedConstructorsSet == null || invokedConstructorsSet.isEmpty()) {
return false;
}

// Test if the invoked constructors set matches the constraint
var hashSet = new HashSet<>(invokedConstructorsSet);
return invokedConstructors.test(hashSet);
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setInvokedMethods/setInvokedConstructors are part of ExecutableMatcher (methods + constructors), but the constraint checks here only run when reflect instanceof Method. As a result, these filters will never apply to constructors, and analysisDex() also resolves the current executable via getDeclaredMethod, so <init> bodies are skipped entirely. If constructors are intended to be supported, collect invocation relationships for constructors too and apply these constraints for Constructor<?> candidates.

Copilot uses AI. Check for mistakes.
Comment thread helper/src/main/java/io/github/libxposed/helper/HookBuilderImpl.java Outdated
Comment on lines 3291 to +3341
public final FieldLazySequence getAssignedFields() {
dexAnalysis = true;
return null;
final var m = new FieldLazySequenceImpl(rootMatcher);
addObserver((ItemObserver<Reflect>) result -> {
// DEX analysis will populate assigned fields
// The actual implementation requires DEX parsing to extract field assignment information
m.match(Collections.emptyList());
});
return m;
}

@DexAnalysis
@NonNull
@Override
public final FieldLazySequence getAccessedFields() {
dexAnalysis = true;
return null;
final var m = new FieldLazySequenceImpl(rootMatcher);
addObserver((ItemObserver<Reflect>) result -> {
// DEX analysis will populate accessed fields
// The actual implementation requires DEX parsing to extract field access information
m.match(Collections.emptyList());
});
return m;
}

@DexAnalysis
@NonNull
@Override
public final MethodLazySequence getInvokedMethods() {
dexAnalysis = true;
return null;
final var m = new MethodLazySequenceImpl(rootMatcher);
addObserver((ItemObserver<Reflect>) result -> {
// DEX analysis will populate invoked methods
// The actual implementation requires DEX parsing to extract method invocation information
m.match(Collections.emptyList());
});
return m;
}

@DexAnalysis
@NonNull
@Override
public final ConstructorLazySequence getInvokedConstructors() {
dexAnalysis = true;
return null;
final var m = new ConstructorLazySequenceImpl(rootMatcher);
addObserver((ItemObserver<Reflect>) result -> {
// DEX analysis will populate invoked constructors
// The actual implementation requires DEX parsing to extract constructor invocation information
m.match(Collections.emptyList());
});
return m;
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAssignedFields/getAccessedFields/getInvokedMethods/getInvokedConstructors now return non-null sequences but they are always matched with Collections.emptyList(). Given the interface contract/docstrings, callers will expect these sequences to reflect actual DEX analysis results. Please either wire these accessors to the data produced by analysisDex() (e.g., the invocation maps), or keep the previous behavior but avoid violating @NonNull/contract expectations (e.g., throw an explicit unsupported exception).

Copilot uses AI. Check for mistakes.
Comment thread helper/src/main/java/io/github/libxposed/helper/HookBuilder.java Outdated
Comment thread settings.gradle.kts Outdated
Howard20181 and others added 4 commits February 10, 2026 02:11
…l.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…l.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…l.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Howard20181 and others added 11 commits February 10, 2026 18:41
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…l.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…l.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…l.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…l.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
The observer was guarded by `if (key != null && matchCache != null)` but
`key` is assigned only after the sequence is constructed (in onBuild()),
meaning the observer would never be added and constructor list results
wouldn't be persisted into matchCache.

Changed to match the pattern used by all other LazySequenceImpl classes:
register the observer whenever matchCache != null and check key inside
the callback.

Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Copilot AI and others added 28 commits February 10, 2026 12:16
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
…n DEX analysis

Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
…s null

Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
…vior

Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
… ignoring

Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
…ionMap usage

Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Replace Executable keys with Member for method/constructor/field maps to unify reflection handling and remove the asExecutable helper. Update constraint evaluation to check assignedFields and accessedFields against the current Member, returning early when constraints fail. Small cleanups: convert parameter type loops to enhanced for-loops in MatchCache and add null-safety checks when resolving primitive classes in Reflector.
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Fix non-idempotent Future.get() causing repeated cache write failures
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
Co-authored-by: Howard20181 <40033067+Howard20181@users.noreply.github.com>
@Howard20181 Howard20181 deleted the docs branch February 10, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants