Home | History | Annotate | Download | only in smalidea
      1 /*
      2  * Copyright 2014, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.smalidea;
     33 
     34 import com.intellij.debugger.SourcePosition;
     35 import com.intellij.psi.PsiElement;
     36 import com.intellij.psi.PsiPrimitiveType;
     37 import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
     38 import org.jf.dexlib2.Opcode;
     39 import org.jf.smalidea.psi.impl.*;
     40 import org.junit.Assert;
     41 
     42 import java.util.List;
     43 
     44 public class SmaliMethodTest extends LightCodeInsightFixtureTestCase {
     45     public void testMethodRegisters() {
     46         String text =
     47                 ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
     48                 ".me<ref>thod blah()V\n" +
     49                 "    .registers 123\n" +
     50                 "    return-void\n" +
     51                 ".end method";
     52 
     53         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
     54                 text.replace("<ref>", ""));
     55 
     56         PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
     57         Assert.assertNotNull(leafElement);
     58         SmaliMethod methodElement = (SmaliMethod)leafElement.getParent();
     59         Assert.assertNotNull(methodElement);
     60 
     61         Assert.assertEquals(123, methodElement.getRegisterCount());
     62         Assert.assertEquals(1, methodElement.getParameterRegisterCount());
     63     }
     64 
     65     public void testMethodRegisters2() {
     66         String text =
     67                 ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
     68                 ".me<ref>thod blah(IJLjava/lang/String;)V\n" +
     69                 "    .locals 123\n" +
     70                 "    return-void\n" +
     71                 ".end method";
     72 
     73         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
     74                 text.replace("<ref>", ""));
     75 
     76         PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
     77         Assert.assertNotNull(leafElement);
     78         SmaliMethod methodElement = (SmaliMethod)leafElement.getParent();
     79         Assert.assertNotNull(methodElement);
     80 
     81         Assert.assertEquals(128, methodElement.getRegisterCount());
     82         Assert.assertEquals(5, methodElement.getParameterRegisterCount());
     83     }
     84 
     85     public void testStaticRegisterCount() {
     86         String text =
     87                 ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
     88                 ".method static blah(IJLjava/lang/String;)V\n" +
     89                 "    .locals 123\n" +
     90                 "    return-void\n" +
     91                 ".end method";
     92 
     93         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
     94         SmaliClass smaliClass = file.getPsiClass();
     95         SmaliMethod smaliMethod = smaliClass.getMethods()[0];
     96 
     97         Assert.assertEquals(127, smaliMethod.getRegisterCount());
     98         Assert.assertEquals(4, smaliMethod.getParameterRegisterCount());
     99 
    100         Assert.assertEquals(0, smaliMethod.getParameterList().getParameters()[0].getParameterRegisterNumber());
    101         Assert.assertEquals(123, smaliMethod.getParameterList().getParameters()[0].getRegisterNumber());
    102     }
    103 
    104     public void testMethodParams() {
    105         myFixture.addFileToProject("my/TestAnnotation.smali",
    106                 ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
    107                 ".super Ljava/lang/Object;\n" +
    108                 ".implements Ljava/lang/annotation/Annotation;\n" +
    109                 "\n" +
    110                 ".method public abstract testBooleanValue()Z\n" +
    111                 ".end method\n" +
    112                 "\n" +
    113                 ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
    114                 ".end method\n" +
    115                 "\n" +
    116                 ".method public abstract testStringValue()Ljava/lang/String;\n" +
    117                 ".end method");
    118 
    119         String text =
    120                 ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
    121                 ".method blah(IJLjava/lang/String;)V\n" +
    122                 "    .locals 123\n" +
    123                 "    .param p1, \"anInt\"\n" +
    124                 "    .param p2\n" +
    125                 "        .annotation runtime Lmy/TestAnnotation;\n" +
    126                 "            testStringValue = \"myValue\"\n" +
    127                 "        .end annotation\n" +
    128                 "    .end param\n" +
    129                 "    return-void\n" +
    130                 ".end method";
    131 
    132         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
    133 
    134         SmaliClass smaliClass = file.getPsiClass();
    135         SmaliMethod smaliMethod = smaliClass.getMethods()[0];
    136 
    137         SmaliMethodParamList paramList = smaliMethod.getParameterList();
    138         SmaliMethodParameter[] parameters = paramList.getParameters();
    139         Assert.assertEquals(3, parameters.length);
    140 
    141         Assert.assertEquals("int", parameters[0].getType().getCanonicalText());
    142         Assert.assertEquals("\"anInt\"", parameters[0].getName());
    143         Assert.assertEquals(1, parameters[0].getRegisterCount());
    144         Assert.assertEquals(124, parameters[0].getRegisterNumber());
    145         Assert.assertEquals(1, parameters[0].getParameterRegisterNumber());
    146         Assert.assertEquals(0, parameters[0].getAnnotations().length);
    147 
    148         Assert.assertEquals("long", parameters[1].getType().getCanonicalText());
    149         Assert.assertNull(parameters[1].getName());
    150         Assert.assertEquals(2, parameters[1].getRegisterCount());
    151         Assert.assertEquals(125, parameters[1].getRegisterNumber());
    152         Assert.assertEquals(2, parameters[1].getParameterRegisterNumber());
    153         Assert.assertEquals(1, parameters[1].getAnnotations().length);
    154         Assert.assertEquals("my.TestAnnotation", parameters[1].getAnnotations()[0].getQualifiedName());
    155 
    156         Assert.assertEquals("java.lang.String", parameters[2].getType().getCanonicalText());
    157         Assert.assertNull(parameters[2].getName());
    158         Assert.assertEquals(1, parameters[2].getRegisterCount());
    159         Assert.assertEquals(127, parameters[2].getRegisterNumber());
    160         Assert.assertEquals(4, parameters[2].getParameterRegisterNumber());
    161         Assert.assertEquals(0, parameters[2].getAnnotations().length);
    162     }
    163 
    164     public void testVarArgsMethod() {
    165         String text =
    166                 ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
    167                 ".method varargs static blah(IJ[Ljava/lang/String;)V\n" +
    168                 "    .locals 123\n" +
    169                 "    return-void\n" +
    170                 ".end method\n" +
    171                 ".method varargs static blah2(IJLjava/lang/String;)V\n" +
    172                 "    .locals 123\n" +
    173                 "    return-void\n" +
    174                 ".end method";
    175 
    176         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
    177         SmaliClass smaliClass = file.getPsiClass();
    178         SmaliMethod smaliMethod = smaliClass.getMethods()[0];
    179 
    180         Assert.assertTrue(smaliMethod.isVarArgs());
    181         Assert.assertFalse(smaliMethod.getParameterList().getParameters()[0].isVarArgs());
    182         Assert.assertFalse(smaliMethod.getParameterList().getParameters()[1].isVarArgs());
    183         Assert.assertTrue(smaliMethod.getParameterList().getParameters()[2].isVarArgs());
    184 
    185         smaliMethod = smaliClass.getMethods()[1];
    186         Assert.assertTrue(smaliMethod.isVarArgs());
    187         Assert.assertFalse(smaliMethod.getParameterList().getParameters()[0].isVarArgs());
    188         Assert.assertFalse(smaliMethod.getParameterList().getParameters()[1].isVarArgs());
    189         Assert.assertFalse(smaliMethod.getParameterList().getParameters()[2].isVarArgs());
    190     }
    191 
    192     private static final String instructionsTestClass =
    193             ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
    194                     ".method public getRandomParentType(I)I\n" +
    195                     "    .registers 4\n" +
    196                     "    .param p1, \"edge\"    # I\n" +
    197                     "\n" +
    198                     "    .prologue\n" +
    199                     "    const/4 v1, 0x2\n" +
    200                     "\n" +
    201                     "    .line 179\n" +
    202                     "    if-nez p1, :cond_5\n" +
    203                     "\n" +
    204                     "    move v0, v1\n" +
    205                     "\n" +
    206                     "    .line 185\n" +
    207                     "    :goto_4\n" +
    208                     "    return v0\n" +
    209                     "\n" +
    210                     "    .line 182\n" +
    211                     "    :cond_5\n" +
    212                     "    if-ne p1, v1, :cond_f\n" +
    213                     "\n" +
    214                     "    .line 183\n" +
    215                     "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
    216                     "\n" +
    217                     "    const/4 v1, 0x3\n" +
    218                     "\n" +
    219                     "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
    220                     "\n" +
    221                     "    move-result v0\n" +
    222                     "\n" +
    223                     "    goto :goto_4\n" +
    224                     "\n" +
    225                     "    .line 185\n" +
    226                     "    :cond_f\n" +
    227                     "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
    228                     "\n" +
    229                     "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
    230                     "\n" +
    231                     "    move-result v0\n" +
    232                     "\n" +
    233                     "    goto :goto_4\n" +
    234                     ".end method";
    235 
    236     public void testGetInstructions() {
    237         String text = instructionsTestClass;
    238 
    239         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
    240         SmaliClass smaliClass = file.getPsiClass();
    241         SmaliMethod smaliMethod = smaliClass.getMethods()[0];
    242 
    243         List<SmaliInstruction> instructions = smaliMethod.getInstructions();
    244         Assert.assertEquals(14, instructions.size());
    245     }
    246 
    247     private void checkSourcePosition(SmaliMethod smaliMethod, int codeOffset, Opcode opcode) {
    248         SourcePosition sourcePosition = smaliMethod.getSourcePositionForCodeOffset(codeOffset);
    249         Assert.assertNotNull(sourcePosition);
    250 
    251         SmaliInstruction instruction = (SmaliInstruction)sourcePosition.getElementAt();
    252         Assert.assertEquals(opcode, instruction.getOpcode());
    253         Assert.assertEquals(codeOffset, instruction.getOffset());
    254     }
    255 
    256     public void testGetSourcePositionForCodeOffset() {
    257         String text = instructionsTestClass;
    258 
    259         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
    260         SmaliClass smaliClass = file.getPsiClass();
    261         SmaliMethod smaliMethod = smaliClass.getMethods()[0];
    262 
    263         checkSourcePosition(smaliMethod, 0, Opcode.CONST_4);
    264         checkSourcePosition(smaliMethod, 2, Opcode.IF_NEZ);
    265         checkSourcePosition(smaliMethod, 6, Opcode.MOVE);
    266         checkSourcePosition(smaliMethod, 8, Opcode.RETURN);
    267         checkSourcePosition(smaliMethod, 10, Opcode.IF_NE);
    268         checkSourcePosition(smaliMethod, 14, Opcode.SGET_OBJECT);
    269         checkSourcePosition(smaliMethod, 18, Opcode.CONST_4);
    270         checkSourcePosition(smaliMethod, 20, Opcode.INVOKE_VIRTUAL);
    271         checkSourcePosition(smaliMethod, 26, Opcode.MOVE_RESULT);
    272         checkSourcePosition(smaliMethod, 28, Opcode.GOTO);
    273         checkSourcePosition(smaliMethod, 30, Opcode.SGET_OBJECT);
    274         checkSourcePosition(smaliMethod, 34, Opcode.INVOKE_VIRTUAL);
    275         checkSourcePosition(smaliMethod, 40, Opcode.MOVE_RESULT);
    276         checkSourcePosition(smaliMethod, 42, Opcode.GOTO);
    277     }
    278 
    279     public void testThrowsList() {
    280         String text = instructionsTestClass;
    281 
    282         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
    283         SmaliClass smaliClass = file.getPsiClass();
    284         SmaliMethod smaliMethod = smaliClass.getMethods()[0];
    285 
    286         SmaliThrowsList throwsList = smaliMethod.getThrowsList();
    287         Assert.assertNotNull(throwsList);
    288         Assert.assertEquals(0, throwsList.getReferencedTypes().length);
    289         Assert.assertEquals(0, throwsList.getReferenceElements().length);
    290     }
    291 
    292     public void testPrimitiveReturnType() {
    293         String text = "" +
    294                 ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
    295                 ".method blah()I\n" +
    296                 "    .registers 123\n" +
    297                 "    return-void\n" +
    298                 ".end method";
    299 
    300         SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
    301         SmaliClass smaliClass = file.getPsiClass();
    302         Assert.assertNotNull(smaliClass);
    303         SmaliMethod smaliMethod = smaliClass.getMethods()[0];
    304 
    305         Assert.assertNotNull(smaliMethod.getReturnType());
    306         Assert.assertTrue(smaliMethod.getReturnType().isConvertibleFrom(PsiPrimitiveType.INT));
    307         Assert.assertTrue(smaliMethod.getReturnType().isAssignableFrom(PsiPrimitiveType.INT));
    308     }
    309 }
    310