use of net.runelite.asm.Method in project runelite by runelite.
the class DrawAfterWidgets method findStaticMethod.
private Method findStaticMethod(String name) {
for (ClassFile c : inject.getDeobfuscated().getClasses()) {
for (Method m : c.getMethods()) {
if (!m.getName().equals(name)) {
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(m.getAnnotations());
Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(m);
ClassFile c2 = inject.toObClass(c);
return c2.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : m.getDescriptor());
}
}
return null;
}
use of net.runelite.asm.Method in project runelite by runelite.
the class InjectInvoker method process.
/**
* Inject an invoker for a method
*
* @param m Method in the deobfuscated client to inject an invoker for
* @param other Class in the vanilla client of the same class m is a
* member of
* @param implementingClass Java class for the API interface the class
* will implement
*/
public void process(Method m, ClassFile other, java.lang.Class<?> implementingClass) {
Annotations an = m.getAnnotations();
if (an == null || an.find(EXPORT) == null) {
// not an exported method
return;
}
String exportedName = DeobAnnotations.getExportedName(an);
String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
if (obfuscatedName == null) {
obfuscatedName = m.getName();
}
String garbage = DeobAnnotations.getObfuscatedValue(m);
Method otherm = other.findMethod(obfuscatedName, inject.getMethodSignature(m));
assert otherm != null;
assert m.isStatic() == otherm.isStatic();
ClassGroup vanilla = inject.getVanilla();
ClassFile targetClass = m.isStatic() ? vanilla.findClass("client") : other;
// Place into implementing class, unless the method is static
java.lang.Class<?> targetClassJava = m.isStatic() ? Inject.CLIENT_CLASS : implementingClass;
if (targetClassJava == null) {
assert !m.isStatic();
// non static exported method on non exported interface, weird.
logger.debug("Non static exported method {} on non exported interface", exportedName);
return;
}
// api method to invoke 'otherm'
java.lang.reflect.Method apiMethod = inject.findImportMethodOnApi(targetClassJava, exportedName, null);
if (apiMethod == null) {
logger.debug("Unable to find api method on {} with imported name {}, not injecting invoker", targetClassJava, exportedName);
return;
}
injectInvoker(targetClass, apiMethod, m, otherm, garbage);
++injectedInvokers;
}
use of net.runelite.asm.Method in project runelite by runelite.
the class InjectSetter method injectSetter.
/**
* inject a setter into the vanilla classgroup
*
* @param targetClass Class where to inject the setter (field's class,
* or client)
* @param targetApiClass API targetClass implements, which may have the
* setter declared
* @param field Field of vanilla that will be set
* @param exportedName exported name of field
* @param setter
*/
public void injectSetter(ClassFile targetClass, Class<?> targetApiClass, Field field, String exportedName, Number setter) {
java.lang.reflect.Method method = inject.findImportMethodOnApi(targetApiClass, exportedName, true);
if (method == null) {
logger.warn("Setter injection for field {} but an API method was not found on {}", exportedName, targetApiClass);
return;
}
if (method.getParameterCount() != 1) {
logger.warn("Setter {} with not parameter count != 1?", exportedName);
return;
}
logger.info("Injecting setter for {} on {}", exportedName, targetApiClass);
assert targetClass.findMethod(method.getName()) == null;
assert field.isStatic() || field.getClassFile() == targetClass;
Signature sig = new Signature.Builder().setReturnType(Type.VOID).addArgument(Inject.classToType(method.getParameterTypes()[0])).build();
Method setterMethod = new Method(targetClass, method.getName(), sig);
setterMethod.setAccessFlags(ACC_PUBLIC);
targetClass.addMethod(setterMethod);
++injectedSetters;
Code code = new Code(setterMethod);
setterMethod.setCode(code);
Instructions instructions = code.getInstructions();
List<Instruction> ins = instructions.getInstructions();
// load this
if (!field.isStatic()) {
ins.add(new ALoad(instructions, 0));
}
// load argument
Type argumentType = sig.getTypeOfArg(0);
ins.add(inject.createLoadForTypeIndex(instructions, argumentType, 1));
// cast argument to field type
Type fieldType = field.getType();
if (!argumentType.equals(fieldType)) {
CheckCast checkCast = new CheckCast(instructions);
checkCast.setType(fieldType);
ins.add(checkCast);
}
if (setter != null) {
assert setter instanceof Integer || setter instanceof Long;
if (setter instanceof Integer) {
ins.add(new LDC(instructions, (int) setter));
ins.add(new IMul(instructions));
} else {
ins.add(new LDC(instructions, (long) setter));
ins.add(new LMul(instructions));
}
}
if (field.isStatic()) {
ins.add(new PutStatic(instructions, field));
} else {
ins.add(new PutField(instructions, field));
}
ins.add(new VReturn(instructions));
}
use of net.runelite.asm.Method in project runelite by runelite.
the class MixinInjector method setOwnersToTargetClass.
private void setOwnersToTargetClass(ClassFile mixinCf, ClassFile cf, Method method, Map<net.runelite.asm.pool.Field, Field> shadowFields, Map<net.runelite.asm.pool.Method, CopiedMethod> copiedMethods) throws InjectionException {
ListIterator<Instruction> iterator = method.getCode().getInstructions().getInstructions().listIterator();
while (iterator.hasNext()) {
Instruction i = iterator.next();
if (i instanceof InvokeInstruction) {
InvokeInstruction ii = (InvokeInstruction) i;
CopiedMethod copiedMethod = copiedMethods.get(ii.getMethod());
if (copiedMethod != null) {
ii.setMethod(copiedMethod.obMethod.getPoolMethod());
// Pass through garbage value if the method has one
if (copiedMethod.hasGarbageValue) {
int garbageIndex = copiedMethod.obMethod.isStatic() ? copiedMethod.obMethod.getDescriptor().size() - 1 : copiedMethod.obMethod.getDescriptor().size();
iterator.previous();
iterator.add(new ILoad(method.getCode().getInstructions(), garbageIndex));
iterator.next();
}
} else if (ii.getMethod().getClazz().getName().equals(mixinCf.getName())) {
ii.setMethod(new net.runelite.asm.pool.Method(new net.runelite.asm.pool.Class(cf.getName()), ii.getMethod().getName(), ii.getMethod().getType()));
}
} else if (i instanceof FieldInstruction) {
FieldInstruction fi = (FieldInstruction) i;
Field shadowed = shadowFields.get(fi.getField());
if (shadowed != null) {
fi.setField(shadowed.getPoolField());
} else if (fi.getField().getClazz().getName().equals(mixinCf.getName())) {
fi.setField(new net.runelite.asm.pool.Field(new net.runelite.asm.pool.Class(cf.getName()), fi.getField().getName(), fi.getField().getType()));
}
} else if (i instanceof PushConstantInstruction) {
PushConstantInstruction pi = (PushConstantInstruction) i;
if (mixinCf.getPoolClass().equals(pi.getConstant())) {
pi.setConstant(cf.getPoolClass());
}
}
verify(mixinCf, i);
}
}
use of net.runelite.asm.Method in project runelite by runelite.
the class ScriptVM method injectScriptVMHooks.
private void injectScriptVMHooks() throws InjectionException {
/*
This hooks local variable assignments in the copied version of runScript:
- The currently executing script > client.currentScript
- The currently executing script's program counter > client.currentScriptPC
- The currently executing opcode > client.vmExecuteOpcode(I)Z
The currently executing script variable is located as the outermost Script local
The PC is located by its use in PutField ScriptState::invokedFromPC
The currently executing opcode is found by searching for iaload with the script's instruction array
The script's instruction array is identified by looking for the getfield from script.instructions
bn.g @ rev 163 :
// Jump back to here if vmExecuteOpcode returns true
aload6 // Script.instructions
iinc 5 1 // ++PC
iload5 // PC
iaload
istore8
// <- Inject here
iload8
bipush 100
if_icmpge L52
*/
String scriptObName = DeobAnnotations.getObfuscatedName(inject.getDeobfuscated().findClass("Script").getAnnotations());
Method runScript = findObMethod("copy$runScript");
Method vmExecuteOpcode = findObMethod("vmExecuteOpcode");
Field scriptInstructions = findDeobField("instructions");
Field scriptStatePC = findDeobField("invokedFromPc");
Field currentScriptField = findObField("currentScript");
Field currentScriptPCField = findObField("currentScriptPC");
Execution e = new Execution(inject.getVanilla());
e.addMethod(runScript);
e.noInvoke = true;
AtomicReference<MethodContext> pcontext = new AtomicReference<>(null);
e.addMethodContextVisitor(pcontext::set);
e.run();
Instructions instrs = runScript.getCode().getInstructions();
Set<AStore> scriptStores = new HashSet<>();
Integer pcLocalVar = null;
Integer instructionArrayLocalVar = null;
IStore currentOpcodeStore = null;
ALoad localInstructionLoad = null;
MethodContext methodContext = pcontext.get();
for (InstructionContext instrCtx : methodContext.getInstructionContexts()) {
Instruction instr = instrCtx.getInstruction();
if (instr instanceof AStore) {
AStore store = (AStore) instr;
StackContext storedVarCtx = instrCtx.getPops().get(0);
// Find AStores that store a Script
if (storedVarCtx.getType().getInternalName().equals(scriptObName)) {
scriptStores.add(store);
}
// Find AStores that store the instructions
InstructionContext pusherCtx = storedVarCtx.getPushed();
if (pusherCtx.getInstruction() instanceof GetField) {
GetField getField = (GetField) pusherCtx.getInstruction();
if (getField.getMyField().equals(scriptInstructions)) {
instructionArrayLocalVar = store.getVariableIndex();
}
}
}
// Find the local that invokedFromPc is set from
if (instr instanceof PutField) {
PutField put = (PutField) instr;
if (put.getMyField() == scriptStatePC) {
StackContext pc = instrCtx.getPops().get(0);
assert Type.INT.equals(pc.getType()) : pc.getType();
InstructionContext mulctx = pc.pushed;
assert mulctx.getInstruction() instanceof IMul;
pcLocalVar = mulctx.getPops().stream().map(StackContext::getPushed).filter(i -> i.getInstruction() instanceof ILoad).map(i -> ((ILoad) i.getInstruction()).getVariableIndex()).findFirst().orElse(null);
}
}
}
// This has to run after the first loop because it relies on instructionArrayLocalVar being set
if (instructionArrayLocalVar == null) {
throw new InjectionException("Unable to find local instruction array");
}
for (InstructionContext instrCtx : methodContext.getInstructionContexts()) {
Instruction instr = instrCtx.getInstruction();
if (instr instanceof IALoad) {
StackContext array = instrCtx.getPops().get(1);
// Check where the array came from (looking for a getField scriptInstructions
InstructionContext pushedCtx = array.getPushed();
Instruction pushed = pushedCtx.getInstruction();
if (pushed instanceof ALoad) {
ALoad arrayLoad = (ALoad) pushed;
if (arrayLoad.getVariableIndex() == instructionArrayLocalVar) {
// Find the istore
IStore istore = (IStore) instrCtx.getPushes().get(0).getPopped().stream().map(InstructionContext::getInstruction).filter(i -> i instanceof IStore).findFirst().orElse(null);
if (istore != null) {
currentOpcodeStore = istore;
localInstructionLoad = arrayLoad;
}
}
}
}
}
// Add PutStatics to all Script AStores
{
int outerSciptIdx = scriptStores.stream().mapToInt(AStore::getVariableIndex).reduce(Math::min).orElseThrow(() -> new InjectionException("Unable to find any Script AStores in runScript"));
log.debug("Found script index {}", outerSciptIdx);
ListIterator<Instruction> instrIter = instrs.getInstructions().listIterator();
while (instrIter.hasNext()) {
Instruction instr = instrIter.next();
if (instr instanceof AStore) {
AStore il = (AStore) instr;
if (il.getVariableIndex() == outerSciptIdx) {
instrIter.previous();
instrIter.add(new Dup(instrs));
instrIter.add(new PutStatic(instrs, currentScriptField));
instrIter.next();
}
}
}
}
// Add PutStatics to all PC IStores and IIncs
{
if (pcLocalVar == null) {
throw new InjectionException("Unable to find ILoad for invokedFromPc IStore");
}
log.debug("Found pc index {}", pcLocalVar);
ListIterator<Instruction> instrIter = instrs.getInstructions().listIterator();
while (instrIter.hasNext()) {
Instruction instr = instrIter.next();
if (instr instanceof IStore) {
IStore il = (IStore) instr;
if (il.getVariableIndex() == pcLocalVar) {
instrIter.previous();
instrIter.add(new Dup(instrs));
instrIter.add(new PutStatic(instrs, currentScriptPCField));
instrIter.next();
}
}
if (instr instanceof IInc) {
IInc iinc = (IInc) instr;
if (iinc.getVariableIndex() == pcLocalVar) {
instrIter.add(new ILoad(instrs, pcLocalVar));
instrIter.add(new PutStatic(instrs, currentScriptPCField));
}
}
}
}
// Inject call to vmExecuteOpcode
log.debug("Found instruction array index {}", instructionArrayLocalVar);
if (currentOpcodeStore == null) {
throw new InjectionException("Unable to find IStore for current opcode");
}
int istorepc = instrs.getInstructions().indexOf(currentOpcodeStore);
assert istorepc >= 0;
Label nextIteration = instrs.createLabelFor(localInstructionLoad);
instrs.addInstruction(istorepc + 1, new ILoad(instrs, currentOpcodeStore.getVariableIndex()));
instrs.addInstruction(istorepc + 2, new InvokeStatic(instrs, vmExecuteOpcode.getPoolMethod()));
instrs.addInstruction(istorepc + 3, new IfNe(instrs, nextIteration));
}
Aggregations