| /* |
| * 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)); |
| } |
| } |