package com.offbynull.coroutines.instrumenter.asm; import static com.offbynull.coroutines.instrumenter.testhelpers.TestUtils.readZipFromResource; import com.offbynull.coroutines.instrumenter.asm.VariableTable.Variable; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; public final class VariableTableTest { private ClassNode classNode; private MethodNode methodNode; @BeforeEach public void setUp() throws IOException { byte[] classData = readZipFromResource("SimpleStub.zip").get("SimpleStub.class"); ClassReader classReader = new ClassReader(classData); classNode = new ClassNode(); classReader.accept(classNode, 0); methodNode = classNode.methods.get(1); // stub should be here } @Test public void mustBeAbleToAccessThisReference() { VariableTable fixture = new VariableTable(classNode, methodNode); Variable var = fixture.getArgument(0); assertEquals(var.getType(), Type.getObjectType(classNode.name)); assertEquals(var.getIndex(), 0); assertTrue(var.isUsed()); } @Test public void mustBeAbleToAccessParameter() { // Augment stub method before testing methodNode.desc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.LONG_TYPE); VariableTable fixture = new VariableTable(classNode, methodNode); Variable var = fixture.getArgument(1); assertEquals(var.getType(), Type.LONG_TYPE); assertEquals(var.getIndex(), 1); assertTrue(var.isUsed()); } @Test public void mustBeAbleToAcquireExtraVariable() { VariableTable fixture = new VariableTable(classNode, methodNode); Variable var1 = fixture.acquireExtra(Type.LONG_TYPE); assertEquals(var1.getType(), Type.LONG_TYPE); assertEquals(var1.getIndex(), 1); assertTrue(var1.isUsed()); Variable var2 = fixture.acquireExtra(Type.BOOLEAN_TYPE); assertEquals(var2.getType(), Type.BOOLEAN_TYPE); assertEquals(var2.getIndex(), 3); assertTrue(var2.isUsed()); } @Test public void mustBeAbleToReleaseExtraVariable() { VariableTable fixture = new VariableTable(classNode, methodNode); Variable var1 = fixture.acquireExtra(Type.LONG_TYPE); Variable var2 = fixture.acquireExtra(Type.LONG_TYPE); fixture.releaseExtra(var1); fixture.releaseExtra(var2); assertFalse(var1.isUsed()); assertFalse(var2.isUsed()); } @Test public void mustFailIfReleasingAlreadyReleasedExtraVariable() { VariableTable fixture = new VariableTable(classNode, methodNode); Variable var1 = fixture.acquireExtra(Type.LONG_TYPE); fixture.releaseExtra(var1); assertThrows(IllegalArgumentException.class, () -> { fixture.releaseExtra(var1); }); } @Test public void mustFailIfAccessingReleasedExtraVariableType() { VariableTable fixture = new VariableTable(classNode, methodNode); Variable var1 = fixture.acquireExtra(Type.LONG_TYPE); fixture.releaseExtra(var1); assertThrows(IllegalArgumentException.class, () -> { var1.getType(); }); } @Test public void mustFailIfAccessingReleasedExtraVariableIndex() { VariableTable fixture = new VariableTable(classNode, methodNode); Variable var1 = fixture.acquireExtra(Type.LONG_TYPE); fixture.releaseExtra(var1); assertThrows(IllegalArgumentException.class, () -> { var1.getIndex(); }); } @Test public void mustNotReturnTheSameObjectIfReacquiringExtraVariable() { VariableTable fixture = new VariableTable(classNode, methodNode); Variable var1 = fixture.acquireExtra(Type.LONG_TYPE); fixture.releaseExtra(var1); Variable var1Reacquired = fixture.acquireExtra(Type.LONG_TYPE); assertFalse(var1.isUsed()); assertEquals(var1Reacquired.getType(), Type.LONG_TYPE); assertEquals(var1Reacquired.getIndex(), 1); assertTrue(var1Reacquired.isUsed()); assertThrows(IllegalArgumentException.class, () -> { var1.getIndex(); }); } }