blob: 6af96757746def5742c7bd40f04f634c6c783573 [file] [log] [blame]
/*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* 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 org.jf.smalidea;
import com.intellij.debugger.SourcePosition;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jf.dexlib2.Opcode;
import org.jf.smalidea.psi.impl.*;
import org.junit.Assert;
import java.util.List;
public class SmaliMethodTest extends LightCodeInsightFixtureTestCase {
public void testMethodRegisters() {
String text =
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".me<ref>thod blah()V\n" +
" .registers 123\n" +
" return-void\n" +
".end method";
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
text.replace("<ref>", ""));
PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
Assert.assertNotNull(leafElement);
SmaliMethod methodElement = (SmaliMethod)leafElement.getParent();
Assert.assertNotNull(methodElement);
Assert.assertEquals(123, methodElement.getRegisterCount());
Assert.assertEquals(1, methodElement.getParameterRegisterCount());
}
public void testMethodRegisters2() {
String text =
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".me<ref>thod blah(IJLjava/lang/String;)V\n" +
" .locals 123\n" +
" return-void\n" +
".end method";
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
text.replace("<ref>", ""));
PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
Assert.assertNotNull(leafElement);
SmaliMethod methodElement = (SmaliMethod)leafElement.getParent();
Assert.assertNotNull(methodElement);
Assert.assertEquals(128, methodElement.getRegisterCount());
Assert.assertEquals(5, methodElement.getParameterRegisterCount());
}
public void testStaticRegisterCount() {
String text =
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".method static blah(IJLjava/lang/String;)V\n" +
" .locals 123\n" +
" return-void\n" +
".end method";
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
SmaliClass smaliClass = file.getPsiClass();
SmaliMethod smaliMethod = smaliClass.getMethods()[0];
Assert.assertEquals(127, smaliMethod.getRegisterCount());
Assert.assertEquals(4, smaliMethod.getParameterRegisterCount());
Assert.assertEquals(0, smaliMethod.getParameterList().getParameters()[0].getParameterRegisterNumber());
Assert.assertEquals(123, smaliMethod.getParameterList().getParameters()[0].getRegisterNumber());
}
public void testMethodParams() {
myFixture.addFileToProject("my/TestAnnotation.smali",
".class public interface abstract annotation Lmy/TestAnnotation;\n" +
".super Ljava/lang/Object;\n" +
".implements Ljava/lang/annotation/Annotation;\n" +
"\n" +
".method public abstract testBooleanValue()Z\n" +
".end method\n" +
"\n" +
".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
".end method\n" +
"\n" +
".method public abstract testStringValue()Ljava/lang/String;\n" +
".end method");
String text =
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".method blah(IJLjava/lang/String;)V\n" +
" .locals 123\n" +
" .param p1, \"anInt\"\n" +
" .param p2\n" +
" .annotation runtime Lmy/TestAnnotation;\n" +
" testStringValue = \"myValue\"\n" +
" .end annotation\n" +
" .end param\n" +
" return-void\n" +
".end method";
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
SmaliClass smaliClass = file.getPsiClass();
SmaliMethod smaliMethod = smaliClass.getMethods()[0];
SmaliMethodParamList paramList = smaliMethod.getParameterList();
SmaliMethodParameter[] parameters = paramList.getParameters();
Assert.assertEquals(3, parameters.length);
Assert.assertEquals("int", parameters[0].getType().getCanonicalText());
Assert.assertEquals("\"anInt\"", parameters[0].getName());
Assert.assertEquals(1, parameters[0].getRegisterCount());
Assert.assertEquals(124, parameters[0].getRegisterNumber());
Assert.assertEquals(1, parameters[0].getParameterRegisterNumber());
Assert.assertEquals(0, parameters[0].getAnnotations().length);
Assert.assertEquals("long", parameters[1].getType().getCanonicalText());
Assert.assertNull(parameters[1].getName());
Assert.assertEquals(2, parameters[1].getRegisterCount());
Assert.assertEquals(125, parameters[1].getRegisterNumber());
Assert.assertEquals(2, parameters[1].getParameterRegisterNumber());
Assert.assertEquals(1, parameters[1].getAnnotations().length);
Assert.assertEquals("my.TestAnnotation", parameters[1].getAnnotations()[0].getQualifiedName());
Assert.assertEquals("java.lang.String", parameters[2].getType().getCanonicalText());
Assert.assertNull(parameters[2].getName());
Assert.assertEquals(1, parameters[2].getRegisterCount());
Assert.assertEquals(127, parameters[2].getRegisterNumber());
Assert.assertEquals(4, parameters[2].getParameterRegisterNumber());
Assert.assertEquals(0, parameters[2].getAnnotations().length);
}
public void testVarArgsMethod() {
String text =
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".method varargs static blah(IJ[Ljava/lang/String;)V\n" +
" .locals 123\n" +
" return-void\n" +
".end method\n" +
".method varargs static blah2(IJLjava/lang/String;)V\n" +
" .locals 123\n" +
" return-void\n" +
".end method";
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
SmaliClass smaliClass = file.getPsiClass();
SmaliMethod smaliMethod = smaliClass.getMethods()[0];
Assert.assertTrue(smaliMethod.isVarArgs());
Assert.assertFalse(smaliMethod.getParameterList().getParameters()[0].isVarArgs());
Assert.assertFalse(smaliMethod.getParameterList().getParameters()[1].isVarArgs());
Assert.assertTrue(smaliMethod.getParameterList().getParameters()[2].isVarArgs());
smaliMethod = smaliClass.getMethods()[1];
Assert.assertTrue(smaliMethod.isVarArgs());
Assert.assertFalse(smaliMethod.getParameterList().getParameters()[0].isVarArgs());
Assert.assertFalse(smaliMethod.getParameterList().getParameters()[1].isVarArgs());
Assert.assertFalse(smaliMethod.getParameterList().getParameters()[2].isVarArgs());
}
private static final String instructionsTestClass =
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".method public getRandomParentType(I)I\n" +
" .registers 4\n" +
" .param p1, \"edge\" # I\n" +
"\n" +
" .prologue\n" +
" const/4 v1, 0x2\n" +
"\n" +
" .line 179\n" +
" if-nez p1, :cond_5\n" +
"\n" +
" move v0, v1\n" +
"\n" +
" .line 185\n" +
" :goto_4\n" +
" return v0\n" +
"\n" +
" .line 182\n" +
" :cond_5\n" +
" if-ne p1, v1, :cond_f\n" +
"\n" +
" .line 183\n" +
" sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
"\n" +
" const/4 v1, 0x3\n" +
"\n" +
" invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
"\n" +
" move-result v0\n" +
"\n" +
" goto :goto_4\n" +
"\n" +
" .line 185\n" +
" :cond_f\n" +
" sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
"\n" +
" invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
"\n" +
" move-result v0\n" +
"\n" +
" goto :goto_4\n" +
".end method";
public void testGetInstructions() {
String text = instructionsTestClass;
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
SmaliClass smaliClass = file.getPsiClass();
SmaliMethod smaliMethod = smaliClass.getMethods()[0];
List<SmaliInstruction> instructions = smaliMethod.getInstructions();
Assert.assertEquals(14, instructions.size());
}
private void checkSourcePosition(SmaliMethod smaliMethod, int codeOffset, Opcode opcode) {
SourcePosition sourcePosition = smaliMethod.getSourcePositionForCodeOffset(codeOffset);
Assert.assertNotNull(sourcePosition);
SmaliInstruction instruction = (SmaliInstruction)sourcePosition.getElementAt();
Assert.assertEquals(opcode, instruction.getOpcode());
Assert.assertEquals(codeOffset, instruction.getOffset());
}
public void testGetSourcePositionForCodeOffset() {
String text = instructionsTestClass;
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
SmaliClass smaliClass = file.getPsiClass();
SmaliMethod smaliMethod = smaliClass.getMethods()[0];
checkSourcePosition(smaliMethod, 0, Opcode.CONST_4);
checkSourcePosition(smaliMethod, 2, Opcode.IF_NEZ);
checkSourcePosition(smaliMethod, 6, Opcode.MOVE);
checkSourcePosition(smaliMethod, 8, Opcode.RETURN);
checkSourcePosition(smaliMethod, 10, Opcode.IF_NE);
checkSourcePosition(smaliMethod, 14, Opcode.SGET_OBJECT);
checkSourcePosition(smaliMethod, 18, Opcode.CONST_4);
checkSourcePosition(smaliMethod, 20, Opcode.INVOKE_VIRTUAL);
checkSourcePosition(smaliMethod, 26, Opcode.MOVE_RESULT);
checkSourcePosition(smaliMethod, 28, Opcode.GOTO);
checkSourcePosition(smaliMethod, 30, Opcode.SGET_OBJECT);
checkSourcePosition(smaliMethod, 34, Opcode.INVOKE_VIRTUAL);
checkSourcePosition(smaliMethod, 40, Opcode.MOVE_RESULT);
checkSourcePosition(smaliMethod, 42, Opcode.GOTO);
}
public void testThrowsList() {
String text = instructionsTestClass;
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
SmaliClass smaliClass = file.getPsiClass();
SmaliMethod smaliMethod = smaliClass.getMethods()[0];
SmaliThrowsList throwsList = smaliMethod.getThrowsList();
Assert.assertNotNull(throwsList);
Assert.assertEquals(0, throwsList.getReferencedTypes().length);
Assert.assertEquals(0, throwsList.getReferenceElements().length);
}
public void testPrimitiveReturnType() {
String text = "" +
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".method blah()I\n" +
" .registers 123\n" +
" return-void\n" +
".end method";
SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
SmaliClass smaliClass = file.getPsiClass();
Assert.assertNotNull(smaliClass);
SmaliMethod smaliMethod = smaliClass.getMethods()[0];
Assert.assertNotNull(smaliMethod.getReturnType());
Assert.assertTrue(smaliMethod.getReturnType().isConvertibleFrom(PsiPrimitiveType.INT));
Assert.assertTrue(smaliMethod.getReturnType().isAssignableFrom(PsiPrimitiveType.INT));
}
}