-
Notifications
You must be signed in to change notification settings - Fork 5
invoke‐custom
Starting from DEX version 038, the invoke-custom
instruction was introduced to enable flexible, dynamic invocation of methods. It plays a crucial role in dynamic method handling, particularly useful for lambda expressions and functional programming in Java and allows Dalvik VM to dynamically call methods at runtime by referencing a "call site", which points to specific runtime metadata defining the method to be called. In simple terms, invoke-custom
makes it possible to execute methods that are not directly known or accessible at compile time but are defined dynamically when the application runs. This dynamic invocation mechanism aligns with Java's java.lang.invoke
API and provides key support for features like lambda expressions.
Here’s a basic outline of the syntax:
invoke-custom {vC, vD, vE, vF, vG}, call_site@BBBB
-
{vC, vD, vE, vF, vG}
: Registers containing arguments passed to the invoked method. -
call_site@BBBB
: Reference to a "call site" in the DEX file (identified by the indexBBBB
), which points to a method and the metadata required for invocation.
Each invoke-custom
instruction has the following fields:
- Argument word count (4 bits): Specifies the number of arguments for the method.
-
Call site reference index (16 bits): An index pointing to the
call_site_item
in the DEX file, which contains all the metadata for the invocation. - Argument registers: Up to five registers that hold the method's arguments.
call_site_item
is a special DEX structure used to hold metadata about a call site. It's located in the DEX file's data section and contains details about the method to be invoked, including the following:
-
Bootstrap Method Handle: A reference to a "bootstrap" method that sets up the call site. This method typically belongs to classes like
LambdaMetafactory
in Java and must return ajava.lang.invoke.CallSite
. - Method Name: A string representing the name of the method to be resolved.
- Method Type: A descriptor defining the method’s argument and return types.
This setup gives invoke-custom
the information needed to invoke a method dynamically.
Here’s what a typical call_site_item
would look like conceptually:
{
bootstrap_method: VALUE_METHOD_HANDLE (e.g., LambdaMetafactory.metafactory),
method_name: VALUE_STRING (e.g., "isSplitMetaElement"),
method_type: VALUE_METHOD_TYPE (e.g., (Lcom/reandroid/arsc/chunk/xml/ResXmlElement;)Z)
}
Additional arguments can also be included as constant values, passed to the bootstrap method in order.
The invoke-custom
instruction executes in two main phases:
-
Call Site Resolution:
- The DEX interpreter checks if there’s an existing
CallSite
object for the specified call site. - If a
CallSite
doesn’t exist, the bootstrap method specified incall_site_item
is invoked with the provided arguments. This bootstrap method returns aCallSite
object that represents the method to be called. - The
CallSite
object is cached for future calls to improve performance.
- The DEX interpreter checks if there’s an existing
-
Call Site Invocation:
- After the
CallSite
is resolved, theMethodHandle
within theCallSite
is invoked. - This invocation works similarly to the
invoke-polymorphic
instruction, where the arguments passed in theinvoke-custom
instruction are used directly.
- After the
Consider this Java code in the ApkSplitInfoCleaner class:
FilterIterator.of(manifest.recursiveElements(), ApkSplitInfoCleaner::isSplitMetaElement)
In bytecode, this invocation of isSplitMetaElement
as a lambda will be compiled into an invoke-custom
call, where the DEX file contains a call_site_item
that references LambdaMetafactory.metafactory
to create a lambda expression.
Here’s what this might look like in Dalvik bytecode:
invoke-custom {}, call_site_2("test", ()Ljava/util/function/Predicate;, (Ljava/lang/Object;)Z,
invoke-static@Lcom/reandroid/apk/ApkSplitInfoCleaner;->isSplitMetaElement(Lcom/reandroid/arsc/chunk/xml/ResXmlElement;)Z,
(Lcom/reandroid/arsc/chunk/xml/ResXmlElement;)Z)@Ljava/lang/invoke/LambdaMetafactory;->metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
This example showcases the following:
-
call_site_2
defines the arguments for theLambdaMetafactory.metafactory
bootstrap method. - The bootstrap method generates a
CallSite
instance, enabling theisSplitMetaElement
method to be invoked as a lambda expression matching a functional interface.
invoke-polymorphic
is another bytecode instruction (Also introduced starting from DEX version 038 onwards) similar to invoke-custom
, but it's specifically used for invoking MethodHandle
methods like invoke
or invokeExact
. While invoke-custom
is for more general dynamic invocation, invoke-polymorphic
is restricted to methods directly supporting polymorphic invocation in MethodHandle
.