MixinInjectorTest
This commit is contained in:
@@ -29,9 +29,9 @@ dependencies {
|
||||
implementation("org.projectlombok:lombok:1.18.10")
|
||||
|
||||
testImplementation("junit:junit:4.12")
|
||||
testImplementation("com.openosrs:mixins:${project.version}")
|
||||
// testRuntimeOnly("com.openosrs.rs:rs-client:${project.version}")
|
||||
testCompileOnly("com.openosrs.rs:runescape-api:${project.version}")
|
||||
// testRuntimeOnly("com.openosrs:mixins:${project.version}")
|
||||
// testRuntimeOnly("net.runelite.rs:vanilla")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.openosrs.injector.injectors;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.openosrs.injector.InjectUtil;
|
||||
@@ -62,7 +63,12 @@ public class MixinInjector extends AbstractInjector
|
||||
public void inject() throws Injexception
|
||||
{
|
||||
final Map<Provider<ClassFile>, List<ClassFile>> mixinTargets = initTargets();
|
||||
inject(mixinTargets);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void inject(Map<Provider<ClassFile>, List<ClassFile>> mixinTargets) throws Injexception
|
||||
{
|
||||
for (Map.Entry<Provider<ClassFile>, List<ClassFile>> entry : mixinTargets.entrySet())
|
||||
{
|
||||
injectFields(entry.getKey(), entry.getValue());
|
||||
@@ -245,7 +251,7 @@ public class MixinInjector extends AbstractInjector
|
||||
}
|
||||
else
|
||||
{
|
||||
deobSourceMethod = InjectUtil.findMethodDeep(inject.toDeob(targetClass.getClassName()), copiedName, mixinMethod.getDescriptor().rsApiToRsClient());
|
||||
deobSourceMethod = InjectUtil.findMethodDeep(inject.toDeob(targetClass.getName()), copiedName, mixinMethod.getDescriptor().rsApiToRsClient());
|
||||
}
|
||||
|
||||
if (mixinMethod.isStatic() != deobSourceMethod.isStatic())
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.openosrs.injector.injectors;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.openosrs.injector.TestInjection;
|
||||
import com.openosrs.injector.rsapi.RSApi;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Provider;
|
||||
import net.runelite.api.mixins.Copy;
|
||||
import net.runelite.api.mixins.Replace;
|
||||
import net.runelite.api.mixins.Shadow;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Method;
|
||||
import static net.runelite.asm.Type.INT;
|
||||
import net.runelite.asm.attributes.code.instructions.GetStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.asm.visitors.ClassFileVisitor;
|
||||
import net.runelite.deob.util.JarUtil;
|
||||
import net.runelite.mapping.ObfuscatedName;
|
||||
import net.runelite.mapping.ObfuscatedSignature;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
|
||||
@ObfuscatedName("com/openosrs/injector/injectors/VanillaTarget")
|
||||
class DeobTarget
|
||||
{
|
||||
@ObfuscatedName("ob_foo4")
|
||||
private static int foo4;
|
||||
|
||||
@ObfuscatedName("ob_foo3")
|
||||
@ObfuscatedSignature(
|
||||
signature = "(I)V",
|
||||
garbageValue = "123"
|
||||
)
|
||||
private void foo3()
|
||||
{
|
||||
// De-obfuscated foo3
|
||||
System.out.println("foo3");
|
||||
}
|
||||
}
|
||||
|
||||
class VanillaTarget
|
||||
{
|
||||
private static int ob_foo4;
|
||||
|
||||
private void ob_foo3(int garbageValue)
|
||||
{
|
||||
// Obfuscated foo3
|
||||
if (garbageValue != 123)
|
||||
{
|
||||
return;
|
||||
}
|
||||
System.out.println("foo3");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Source
|
||||
{
|
||||
@net.runelite.api.mixins.Inject
|
||||
private static int foo;
|
||||
@Shadow("foo4")
|
||||
private static int foo4;
|
||||
|
||||
@net.runelite.api.mixins.Inject
|
||||
private void foo2()
|
||||
{
|
||||
}
|
||||
|
||||
@Copy("foo3")
|
||||
abstract void foo3();
|
||||
|
||||
@Replace("foo3")
|
||||
private void rl$foo3()
|
||||
{
|
||||
System.out.println("replaced");
|
||||
System.out.println(foo4);
|
||||
foo3();
|
||||
}
|
||||
}
|
||||
|
||||
// Test shadowing the "foo" field injected by Source
|
||||
abstract class Source2
|
||||
{
|
||||
@Shadow("foo")
|
||||
private static int foo;
|
||||
|
||||
@net.runelite.api.mixins.Inject
|
||||
private void foo5()
|
||||
{
|
||||
System.out.println(foo);
|
||||
}
|
||||
}
|
||||
|
||||
public class MixinInjectorTest
|
||||
{
|
||||
@Test
|
||||
public void testInject() throws Exception
|
||||
{
|
||||
InputStream deobIn = getClass().getResourceAsStream("DeobTarget.class");
|
||||
ClassFile deobTarget = JarUtil.loadClass(ByteStreams.toByteArray(deobIn));
|
||||
|
||||
ClassGroup deob = new ClassGroup();
|
||||
deob.addClass(deobTarget);
|
||||
|
||||
InputStream vanillaIn = getClass().getResourceAsStream("VanillaTarget.class");
|
||||
ClassFile vanillaTarget = JarUtil.loadClass(ByteStreams.toByteArray(vanillaIn));
|
||||
|
||||
ClassGroup vanilla = new ClassGroup();
|
||||
vanilla.addClass(vanillaTarget);
|
||||
|
||||
Map<Provider<ClassFile>, List<ClassFile>> mixinClasses = new HashMap<>();
|
||||
mixinClasses.put(() -> loadClass(Source.class), Collections.singletonList(vanillaTarget));
|
||||
mixinClasses.put(() -> loadClass(Source2.class), Collections.singletonList(vanillaTarget));
|
||||
|
||||
TestInjection inject = new TestInjection(vanilla, deob, new ClassGroup(), new RSApi());
|
||||
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("com/openosrs/injector/injectors/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("com/openosrs/injector/injectors/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("com/openosrs/injector/injectors/VanillaTarget"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!field.getName().equals("foo"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.count(), 1);
|
||||
}
|
||||
|
||||
private static ClassFile loadClass(Class<?> clazz)
|
||||
{
|
||||
try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class"))
|
||||
{
|
||||
ClassReader reader = new ClassReader(is);
|
||||
ClassFileVisitor cv = new ClassFileVisitor();
|
||||
|
||||
reader.accept(cv, 0);
|
||||
|
||||
return cv.getClassFile();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user