Skip to content

CLocalVariable

Lenni0451 edited this page Mar 17, 2024 · 1 revision

With CLocalVariable you can capture local variables and use them in CTransformers.
When setting modifiable to true you can change the value of the variable and it will get copied back to the target method.

Specifying the target variable

There are three ways to specify the target variable:

  • Name in the local variable table
  • Ordinal of the variable in the local variable table
  • Load/Store variable index

The priority is in the order listed above. The first one that is present in the class file will be used.
The name and ordinal required the local variable table to be present in the class file. This may not be the case for obfuscated code.
The load/store variable index works without the local variable table but is harder to figure out and breaks faster when the code of the target method changes.

If none of the three are specified the name of the parameter will be used as the name in the local variable table.
If the name of the parameter cannot be found an exception will be thrown.

Name in the local variable table

public void test(@CLocalVariable(name = "varName") String varName)

This will use the variable with the name varName in the local variable table of the target method.

Ordinal of the variable in the local variable table

public void test(@CLocalVariable(name = "varName") String varName)

This will use the first String variable in the local variable table of the target method.

Load/Store variable index

public void test(@CLocalVariable(index = 1) String varName)

This will use the variable with the index 1 in the target method.
If ClassTransform can't figure out the type of the variable itself you need to specify the opcode in the loadOpcode field.
Example:

public void test(@CLocalVariable(index = 1, loadOpcode = Opcodes.ALOAD) String varName)

Modifying the variable

If the modifiable field is set to true the value of the variable will be copied back to the target method after the transformer is done.
The default for this is false as it may not be clear immediately that the variable will be modified.
Example:

public void test(@CLocalVariable(name = "varName", modifiable = true) String varName)

Example

Original method:

public String method(final String arg) {
    return "hello " + arg;
}

Transformer method:

//Capture the String argument of the method
@CInject(method = "method", target = @CTarget("HEAD"))
public void transform(@CLocalVariable(modifiable = true) String arg) {
    //Your IDE may complain about the variable not being used
    arg = "world";
}

Injected code:

public String method(final String arg) {
    Object[] capturedLocals = new Object[1];
    capturedLocals[0] = arg;
    transform(capturedLocals);
    arg = (String) capturedLocals[0]; //This part is skipped if the variable is not modifiable
    return "hello " + arg;
}

//ClassTransform automatically remapped the transformer method to use an Object[]
public void transform(Object[] capturedLocals) {
    capturedLocals[0] = "world";
}

ClassTransform automatically merges all the captured locals into an array to copy them back to the target method.
The merging of parameters is always done no matter if any of them are modifiable.