Search in sources :

Example 86 with ClassFile

use of net.runelite.asm.ClassFile in project runelite by runelite.

the class Inject method run.

public void run() throws InjectionException {
    Map<ClassFile, java.lang.Class> implemented = new HashMap<>();
    // check below works
    for (ClassFile cf : deobfuscated.getClasses()) {
        Annotations an = cf.getAnnotations();
        if (an == null || an.size() == 0) {
            continue;
        }
        String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
        if (obfuscatedName == null) {
            obfuscatedName = cf.getName();
        }
        ClassFile other = vanilla.findClass(obfuscatedName);
        assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
        java.lang.Class implementingClass = injectInterface(cf, other);
        // it can not implement an interface but still have exported static fields, which are
        // moved to client
        implemented.put(cf, implementingClass);
    }
    // requires interfaces to be injected
    mixinInjector.inject();
    construct.inject(implemented);
    for (ClassFile cf : deobfuscated.getClasses()) {
        java.lang.Class implementingClass = implemented.get(cf);
        Annotations an = cf.getAnnotations();
        if (an == null || an.size() == 0) {
            continue;
        }
        String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
        if (obfuscatedName == null) {
            obfuscatedName = cf.getName();
        }
        ClassFile other = vanilla.findClass(obfuscatedName);
        assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
        for (Field f : cf.getFields()) {
            an = f.getAnnotations();
            if (an == null || an.find(DeobAnnotations.EXPORT) == null) {
                // not an exported field
                continue;
            }
            Annotation exportAnnotation = an.find(DeobAnnotations.EXPORT);
            String exportedName = exportAnnotation.getElement().getString();
            obfuscatedName = DeobAnnotations.getObfuscatedName(an);
            Annotation getterAnnotation = an.find(DeobAnnotations.OBFUSCATED_GETTER);
            Number getter = null;
            if (getterAnnotation != null) {
                getter = (Number) getterAnnotation.getElement().getValue();
            }
            // the ob jar is the same as the vanilla so this field must exist in this class.
            Type obType = getFieldType(f);
            Field otherf = other.findField(obfuscatedName, obType);
            assert otherf != null;
            assert f.isStatic() == otherf.isStatic();
            // target class for getter
            ClassFile targetClass = f.isStatic() ? vanilla.findClass("client") : other;
            // target api class for getter
            java.lang.Class targetApiClass = f.isStatic() ? CLIENT_CLASS : implementingClass;
            if (targetApiClass == null) {
                assert !f.isStatic();
                // non static field exported on non exported interface
                logger.debug("Non static exported field {} on non exported interface", exportedName);
                continue;
            }
            java.lang.reflect.Method apiMethod = findImportMethodOnApi(targetApiClass, exportedName, true);
            if (apiMethod != null) {
                Number setter = null;
                if (getter != null) {
                    // inverse getter to get the setter
                    setter = DMath.modInverse(getter);
                }
                setters.injectSetter(targetClass, targetApiClass, otherf, exportedName, setter);
            }
            apiMethod = findImportMethodOnApi(targetApiClass, exportedName, false);
            if (apiMethod == null) {
                logger.debug("Unable to find import method on api class {} with imported name {}, not injecting getter", targetApiClass, exportedName);
                continue;
            }
            // check that otherf is converable to apiMethod's
            // return type
            Type fieldType = otherf.getType();
            Type returnType = classToType(apiMethod.getReturnType());
            if (!validateTypeIsConvertibleTo(fieldType, returnType)) {
                throw new InjectionException("Type " + fieldType + " is not convertable to " + returnType + " for getter " + apiMethod);
            }
            getters.injectGetter(targetClass, apiMethod, otherf, getter);
        }
        for (Method m : cf.getMethods()) {
            hookMethod.process(m);
            invokes.process(m, other, implementingClass);
        }
    }
    logger.info("Injected {} getters, {} settters, {} invokers", getters.getInjectedGetters(), setters.getInjectedSetters(), invokes.getInjectedInvokers());
    drawAfterWidgets.inject();
    scriptVM.inject();
}
Also used : ClassFile(net.runelite.asm.ClassFile) HashMap(java.util.HashMap) Method(net.runelite.asm.Method) Annotation(net.runelite.asm.attributes.annotation.Annotation) Field(net.runelite.asm.Field) Type(net.runelite.asm.Type) Annotations(net.runelite.asm.attributes.Annotations) DeobAnnotations(net.runelite.deob.DeobAnnotations) Class(net.runelite.asm.pool.Class)

Example 87 with ClassFile

use of net.runelite.asm.ClassFile in project runelite by runelite.

the class InjectSetterTest method testInjectSetterInt.

@Test
public void testInjectSetterInt() throws NoSuchMethodException {
    Inject inject = mock(Inject.class);
    when(inject.findImportMethodOnApi(any(Class.class), anyString(), any(Boolean.class))).thenReturn(APIClass.class.getDeclaredMethod("setTest", int.class));
    when(inject.createLoadForTypeIndex(any(Instructions.class), any(Type.class), anyInt())).thenReturn(mock(Instruction.class));
    InjectSetter instance = new InjectSetter(inject);
    ClassFile targetClass = new ClassFile();
    targetClass.setName("test");
    Field field = new Field(targetClass, "test", Type.INT);
    targetClass.addField(field);
    instance.injectSetter(targetClass, APIClass.class, field, "test", null);
    Method injectedMethod = targetClass.findMethod("setTest");
    assertNotNull(injectedMethod);
    Code code = injectedMethod.getCode();
    Instructions instructions = code.getInstructions();
    assertFalse(instructions.getInstructions().stream().filter(i -> i.getType() == CHECKCAST).findAny().isPresent());
}
Also used : Assert.assertNotNull(org.junit.Assert.assertNotNull) Field(net.runelite.asm.Field) Assert.assertTrue(org.junit.Assert.assertTrue) Code(net.runelite.asm.attributes.Code) Test(org.junit.Test) Mockito.when(org.mockito.Mockito.when) Type(net.runelite.asm.Type) Matchers.anyString(org.mockito.Matchers.anyString) Matchers.any(org.mockito.Matchers.any) ClassFile(net.runelite.asm.ClassFile) CHECKCAST(net.runelite.asm.attributes.code.InstructionType.CHECKCAST) Method(net.runelite.asm.Method) Assert.assertFalse(org.junit.Assert.assertFalse) Instructions(net.runelite.asm.attributes.code.Instructions) Matchers.anyInt(org.mockito.Matchers.anyInt) Instruction(net.runelite.asm.attributes.code.Instruction) Mockito.mock(org.mockito.Mockito.mock) ClassFile(net.runelite.asm.ClassFile) Instructions(net.runelite.asm.attributes.code.Instructions) Method(net.runelite.asm.Method) Instruction(net.runelite.asm.attributes.code.Instruction) Code(net.runelite.asm.attributes.Code) Field(net.runelite.asm.Field) Type(net.runelite.asm.Type) Test(org.junit.Test)

Example 88 with ClassFile

use of net.runelite.asm.ClassFile in project runelite by runelite.

the class InjectSetterTest method testInjectSetterObject.

@Test
public void testInjectSetterObject() throws NoSuchMethodException {
    Inject inject = mock(Inject.class);
    when(inject.findImportMethodOnApi(any(Class.class), anyString(), any(Boolean.class))).thenReturn(APIClass.class.getDeclaredMethod("setTestObject", Object.class));
    when(inject.createLoadForTypeIndex(any(Instructions.class), any(Type.class), anyInt())).thenReturn(mock(Instruction.class));
    InjectSetter instance = new InjectSetter(inject);
    ClassFile targetClass = new ClassFile();
    targetClass.setName("test");
    Field field = new Field(targetClass, "testObject", Type.STRING);
    targetClass.addField(field);
    instance.injectSetter(targetClass, APIClass.class, field, "testObject", null);
    Method injectedMethod = targetClass.findMethod("setTestObject");
    assertNotNull(injectedMethod);
    Code code = injectedMethod.getCode();
    Instructions instructions = code.getInstructions();
    assertTrue(instructions.getInstructions().stream().filter(i -> i.getType() == CHECKCAST).findAny().isPresent());
}
Also used : Assert.assertNotNull(org.junit.Assert.assertNotNull) Field(net.runelite.asm.Field) Assert.assertTrue(org.junit.Assert.assertTrue) Code(net.runelite.asm.attributes.Code) Test(org.junit.Test) Mockito.when(org.mockito.Mockito.when) Type(net.runelite.asm.Type) Matchers.anyString(org.mockito.Matchers.anyString) Matchers.any(org.mockito.Matchers.any) ClassFile(net.runelite.asm.ClassFile) CHECKCAST(net.runelite.asm.attributes.code.InstructionType.CHECKCAST) Method(net.runelite.asm.Method) Assert.assertFalse(org.junit.Assert.assertFalse) Instructions(net.runelite.asm.attributes.code.Instructions) Matchers.anyInt(org.mockito.Matchers.anyInt) Instruction(net.runelite.asm.attributes.code.Instruction) Mockito.mock(org.mockito.Mockito.mock) ClassFile(net.runelite.asm.ClassFile) Instructions(net.runelite.asm.attributes.code.Instructions) Method(net.runelite.asm.Method) Instruction(net.runelite.asm.attributes.code.Instruction) Code(net.runelite.asm.attributes.Code) Field(net.runelite.asm.Field) Type(net.runelite.asm.Type) Test(org.junit.Test)

Example 89 with ClassFile

use of net.runelite.asm.ClassFile in project runelite by runelite.

the class MixinInjectorTest method testInject.

@Test
public void testInject() throws Exception {
    InputStream deobIn = getClass().getResourceAsStream("DeobTarget.class");
    ClassFile deobTarget = ClassUtil.loadClass(deobIn);
    ClassGroup deob = new ClassGroup();
    deob.addClass(deobTarget);
    InputStream vanillaIn = getClass().getResourceAsStream("VanillaTarget.class");
    ClassFile vanillaTarget = ClassUtil.loadClass(vanillaIn);
    ClassGroup vanilla = new ClassGroup();
    vanilla.addClass(vanillaTarget);
    Map<Class<?>, List<ClassFile>> mixinClasses = new HashMap<>();
    mixinClasses.put(Source.class, Collections.singletonList(vanillaTarget));
    mixinClasses.put(Source2.class, Collections.singletonList(vanillaTarget));
    Inject inject = new Inject(deob, vanilla);
    new MixinInjector(inject).inject(mixinClasses);
    // Check if "foo" has been injected
    Field foo = vanillaTarget.findField("foo");
    assertNotNull(foo);
    assertEquals(INT, foo.getType());
    assertEquals(ACC_PUBLIC | ACC_STATIC, foo.getAccessFlags());
    // Check if "foo2()V" has been injected
    Method foo2 = vanillaTarget.findMethod("foo2");
    assertNotNull(foo2);
    assertEquals(new Signature("()V"), foo2.getDescriptor());
    assertEquals(ACC_PUBLIC, foo2.getAccessFlags());
    // Check if "ob_foo3(I)V" was copied
    Method foo3 = vanillaTarget.findMethod("copy$foo3");
    assertNotNull(foo3);
    assertEquals(new Signature("(I)V"), foo3.getDescriptor());
    assertEquals(ACC_PUBLIC, foo3.getAccessFlags());
    // Check if "ob_foo3(I)V" was replaced
    Method ob_foo3 = vanillaTarget.findMethod("ob_foo3");
    assertNotNull(ob_foo3);
    assertEquals(new Signature("(I)V"), ob_foo3.getDescriptor());
    assertEquals(ob_foo3.getCode().getInstructions().getInstructions().stream().filter(i -> i instanceof LDC && ((LDC) i).getConstant().equals("replaced")).count(), 1);
    // Check that the "foo4" field access in the new code body was mapped correctly
    assertEquals(ob_foo3.getCode().getInstructions().getInstructions().stream().filter(i -> {
        if (!(i instanceof GetStatic)) {
            return false;
        }
        net.runelite.asm.pool.Field field = ((GetStatic) i).getField();
        if (!field.getClazz().getName().equals("net/runelite/injector/VanillaTarget")) {
            return false;
        }
        if (!field.getName().equals("ob_foo4")) {
            return false;
        }
        return true;
    }).count(), 1);
    // Check that the "foo3()" call in the new code body was mapped to the copy
    assertEquals(ob_foo3.getCode().getInstructions().getInstructions().stream().filter(i -> {
        if (!(i instanceof InvokeVirtual)) {
            return false;
        }
        net.runelite.asm.pool.Method method = ((InvokeVirtual) i).getMethod();
        if (!method.getClazz().getName().equals("net/runelite/injector/VanillaTarget")) {
            return false;
        }
        if (!method.getName().equals("copy$foo3")) {
            return false;
        }
        return true;
    }).count(), 1);
    // Check if "foo5()V" was injected
    Method foo5 = vanillaTarget.findMethod("foo5");
    assertNotNull(foo5);
    assertEquals(new Signature("()V"), foo5.getDescriptor());
    assertEquals(ACC_PUBLIC, foo5.getAccessFlags());
    // Check that the shadow "foo" field access was mapped correctly
    assertEquals(foo5.getCode().getInstructions().getInstructions().stream().filter(i -> {
        if (!(i instanceof GetStatic)) {
            return false;
        }
        net.runelite.asm.pool.Field field = ((GetStatic) i).getField();
        if (!field.getClazz().getName().equals("net/runelite/injector/VanillaTarget")) {
            return false;
        }
        if (!field.getName().equals("foo")) {
            return false;
        }
        return true;
    }).count(), 1);
}
Also used : ClassFile(net.runelite.asm.ClassFile) HashMap(java.util.HashMap) InputStream(java.io.InputStream) LDC(net.runelite.asm.attributes.code.instructions.LDC) Method(net.runelite.asm.Method) Field(net.runelite.asm.Field) GetStatic(net.runelite.asm.attributes.code.instructions.GetStatic) InvokeVirtual(net.runelite.asm.attributes.code.instructions.InvokeVirtual) ClassGroup(net.runelite.asm.ClassGroup) ObfuscatedSignature(net.runelite.mapping.ObfuscatedSignature) Signature(net.runelite.asm.signature.Signature) List(java.util.List) Test(org.junit.Test)

Example 90 with ClassFile

use of net.runelite.asm.ClassFile in project runelite by runelite.

the class InjectMojo method writeClasses.

private void writeClasses(ClassGroup group, File outputDirectory) throws IOException {
    for (ClassFile cf : group.getClasses()) {
        File classFile = getClassFile(outputDirectory, cf);
        byte[] classData = JarUtil.writeClass(group, cf);
        try (FileOutputStream fout = new FileOutputStream(classFile, false)) {
            fout.write(classData);
        }
    }
}
Also used : ClassFile(net.runelite.asm.ClassFile) FileOutputStream(java.io.FileOutputStream) File(java.io.File) ClassFile(net.runelite.asm.ClassFile)

Aggregations

ClassFile (net.runelite.asm.ClassFile)103 Method (net.runelite.asm.Method)62 Field (net.runelite.asm.Field)39 ClassGroup (net.runelite.asm.ClassGroup)32 Code (net.runelite.asm.attributes.Code)21 Instruction (net.runelite.asm.attributes.code.Instruction)18 Test (org.junit.Test)18 Signature (net.runelite.asm.signature.Signature)17 Type (net.runelite.asm.Type)16 Instructions (net.runelite.asm.attributes.code.Instructions)16 ArrayList (java.util.ArrayList)14 List (java.util.List)13 Logger (org.slf4j.Logger)10 LoggerFactory (org.slf4j.LoggerFactory)10 IOException (java.io.IOException)9 InputStream (java.io.InputStream)9 Annotation (net.runelite.asm.attributes.annotation.Annotation)9 PushConstantInstruction (net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction)9 HashMap (java.util.HashMap)8 GetFieldInstruction (net.runelite.asm.attributes.code.instruction.types.GetFieldInstruction)7