Add support for hooking all methods of a class#23
Conversation
When only 'class' is specified in a hook configuration (without 'method' or 'methods'), the hook system now automatically enumerates and hooks all methods of that class using Java reflection. This feature provides: - Comprehensive coverage of all methods in a class - Simpler hook configurations for exploratory analysis - Automatic discovery of available methods - No need to maintain method lists when classes change The implementation enhances buildHookOperations() to detect class-only hooks and use getDeclaredMethods() to enumerate all methods. Each method's overloads are then hooked automatically, with graceful handling of methods that cannot be hooked (e.g., synthetic methods, constructors). Updated documentation in docs/usage.md to explain the new feature with examples. Closes #22
There was a problem hiding this comment.
Pull request overview
This PR adds the ability to hook all methods of a class by specifying only the class field in the hook configuration, which eliminates the need to manually enumerate individual methods. This is useful for comprehensive monitoring and exploratory analysis of unfamiliar codebases.
Changes:
- Enhanced
buildHookOperations()function to detect when nomethodormethodsfield is present and automatically enumerate and hook all methods using Java reflection - Updated function documentation to describe the new scenario
- Added documentation section in
docs/usage.mdexplaining the new feature with examples
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| frooky/android/base_script.js | Added logic to enumerate and hook all methods of a class when only the class field is specified, with appropriate error handling |
| docs/usage.md | Added documentation section explaining how to hook all methods of a class |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| // If only class is specified (no method/methods), hook all methods | ||
| if (!hook.method && (!hook.methods || hook.methods.length === 0)) { |
There was a problem hiding this comment.
The condition will trigger the "hook all methods" behavior when methods is explicitly set to an empty array. This may be unexpected behavior - if a user explicitly specifies methods: [], they likely intend to hook zero methods, not all methods. Consider checking if the methods property exists in the hook object (using !('methods' in hook)) rather than checking if it's falsy or empty. This would distinguish between "methods not specified" (hook all) and "methods specified as empty array" (hook none).
| if (!hook.method && (!hook.methods || hook.methods.length === 0)) { | |
| if (!hook.method && !("methods" in hook)) { |
| // If only class is specified (no method/methods), hook all methods | ||
| if (!hook.method && (!hook.methods || hook.methods.length === 0)) { |
There was a problem hiding this comment.
When only class and overloads are specified (without method or methods), the new logic will hook all methods of the class and silently ignore the overloads field. This could be confusing for users who mistakenly specify overloads without a method name. Consider adding validation to detect this case and emit a warning or error, similar to how the invalid combination of methods + overloads is handled at line 381.
| // If only class is specified (no method/methods), hook all methods | |
| if (!hook.method && (!hook.methods || hook.methods.length === 0)) { | |
| // Invalid configuration: overloads without method/methods | |
| if (!hook.method && (!hook.methods || hook.methods.length === 0) && | |
| hook.overloads && hook.overloads.length > 0) { | |
| let errInvalidOverloads = "Invalid hook configuration for " + hook.class + ": 'overloads' requires a singular 'method' name, not only 'class'."; | |
| console.error(errInvalidOverloads); | |
| errors.push(errInvalidOverloads); | |
| return {operations: operations, count: 0, errors: errors, errorCount: errors.length}; | |
| } | |
| // If only class is specified (no method/methods/overloads), hook all methods | |
| if (!hook.method && (!hook.methods || hook.methods.length === 0) && | |
| (!hook.overloads || hook.overloads.length === 0)) { |
There was a problem hiding this comment.
fyi I have cleaned up and optimised this method in this PR. So we might have conflicts
https://github.com/cpholguera/frooky/pull/11/changes#diff-b86f398903cad8f58ed73e2143f1c7f5d043c6a83d25f1a51e529e87fdae9091R373-R488
There was a problem hiding this comment.
Thanks and no worries, if necessary we can close mine in favor of yours after we clarify all API changes we want to do and how (hook all methods from class, add overloads for multiple methods, etc.). For now, I'll stop working on this one until TypeScript is ready.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
We may need some fixes, because currently it's not fully intuitive:
This makes me realize that we need to update the hooks definitions. overloads are tied to a specific method. Now having the individual definition of [
{
"class": "android.app.SharedPreferencesImpl$EditorImpl",
"methods": [
{
"name": "putString",
"overloads": [
{
"args": [
"java.lang.String",
"java.lang.String"
]
}
]
},
{
"name": "putStringSet",
"overloads": [
{
"args": [
"java.lang.String",
"java.util.Set"
]
}
]
}
]
},
{
"class": "android.app.SharedPreferencesImpl$EditorImpl",
"methods": [
{
"name": "putString"
},
{
"name": "putStringSet"
}
]
}
]We could keep the method+overloads option for simplicity and backward compatibility and have this new approach for more advanced hooks. It's all about balance. |
Summary
Closes #22
This PR adds the ability to hook all methods of a class by specifying only the
classfield in the hook configuration, eliminating the need to enumerate individual methods.Problem
Previously, when users wanted to comprehensively monitor a class, they had to either:
methodsarrayThis was tedious, error-prone, and difficult to maintain when classes had many methods or when exploring unfamiliar codebases.
Solution
Enhanced the
buildHookOperations()function infrooky/android/base_script.jsto automatically hook all methods when onlyclassis specified:methodormethodsfield is presentgetDeclaredMethods()) to enumerate all methodsUsage Example
Before (had to enumerate methods):
{ "class": "android.app.SharedPreferencesImpl$EditorImpl", "methods": ["putString", "putInt", "putBoolean", "apply", "commit", ...] }After (just specify the class):
{ "class": "android.app.SharedPreferencesImpl$EditorImpl" }This automatically hooks all 13 methods:
putString,putInt,putBoolean,putLong,putFloat,putStringSet,remove,clear,commit,apply,commitToMemory,notifyListeners, andlambda$notifyListeners$0.Testing
Verified with
android.app.SharedPreferencesImpl$EditorImpl:Documentation
Updated
docs/usage.mdwith a new "Hook All Methods of a Class" section explaining the feature with examples.Benefits
✅ Comprehensive coverage - Capture all method calls on a class
✅ Less maintenance - No need to update hook files when methods are added
✅ Discovery - Helps identify which methods are actually being called
✅ Convenience - Simpler hook configurations for exploratory analysis