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.lexer.Lexer; 35 import com.intellij.testFramework.LexerTestCase; 36 37 import java.util.Random; 38 39 /** 40 * This is mostly just a smoke test to make sure the lexer is working. The lexer itself has its 41 * own tests in the smali module 42 */ 43 public class SmaliLexerTest extends LexerTestCase { testHelloWorld()44 public void testHelloWorld() { 45 String text = 46 ".class public LHelloWorld;\n" + 47 ".super Ljava/lang/Object;\n" + 48 ".method public static main([Ljava/lang/String;)V\n" + 49 " .registers 2\n" + 50 " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" + 51 " const-string v1, \"Hello World!\"\n" + 52 " invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" + 53 " return-void\n" + 54 ".end method"; 55 56 doTest(text, 57 "CLASS_DIRECTIVE ('.class')\n" + 58 "WHITE_SPACE (' ')\n" + 59 "ACCESS_SPEC ('public')\n" + 60 "WHITE_SPACE (' ')\n" + 61 "CLASS_DESCRIPTOR ('LHelloWorld;')\n" + 62 "WHITE_SPACE ('\\n')\n" + 63 "SUPER_DIRECTIVE ('.super')\n" + 64 "WHITE_SPACE (' ')\n" + 65 "CLASS_DESCRIPTOR ('Ljava/lang/Object;')\n" + 66 "WHITE_SPACE ('\\n')\n" + 67 "METHOD_DIRECTIVE ('.method')\n" + 68 "WHITE_SPACE (' ')\n" + 69 "ACCESS_SPEC ('public')\n" + 70 "WHITE_SPACE (' ')\n" + 71 "ACCESS_SPEC ('static')\n" + 72 "WHITE_SPACE (' ')\n" + 73 "SIMPLE_NAME ('main')\n" + 74 "OPEN_PAREN ('(')\n" + 75 "ARRAY_TYPE_PREFIX ('[')\n" + 76 "CLASS_DESCRIPTOR ('Ljava/lang/String;')\n" + 77 "CLOSE_PAREN (')')\n" + 78 "VOID_TYPE ('V')\n" + 79 "WHITE_SPACE ('\\n ')\n" + 80 "REGISTERS_DIRECTIVE ('.registers')\n" + 81 "WHITE_SPACE (' ')\n" + 82 "POSITIVE_INTEGER_LITERAL ('2')\n" + 83 "WHITE_SPACE ('\\n ')\n" + 84 "INSTRUCTION_FORMAT21c_FIELD ('sget-object')\n" + 85 "WHITE_SPACE (' ')\n" + 86 "REGISTER ('v0')\n" + 87 "COMMA (',')\n" + 88 "WHITE_SPACE (' ')\n" + 89 "CLASS_DESCRIPTOR ('Ljava/lang/System;')\n" + 90 "ARROW ('->')\n" + 91 "SIMPLE_NAME ('out')\n" + 92 "COLON (':')\n" + 93 "CLASS_DESCRIPTOR ('Ljava/io/PrintStream;')\n" + 94 "WHITE_SPACE ('\\n ')\n" + 95 "INSTRUCTION_FORMAT21c_STRING ('const-string')\n" + 96 "WHITE_SPACE (' ')\n" + 97 "REGISTER ('v1')\n" + 98 "COMMA (',')\n" + 99 "WHITE_SPACE (' ')\n" + 100 "STRING_LITERAL ('\"Hello World!\"')\n" + 101 "WHITE_SPACE ('\\n ')\n" + 102 "INSTRUCTION_FORMAT35c_METHOD ('invoke-virtual')\n" + 103 "WHITE_SPACE (' ')\n" + 104 "OPEN_BRACE ('{')\n" + 105 "REGISTER ('v0')\n" + 106 "COMMA (',')\n" + 107 "WHITE_SPACE (' ')\n" + 108 "REGISTER ('v1')\n" + 109 "CLOSE_BRACE ('}')\n" + 110 "COMMA (',')\n" + 111 "WHITE_SPACE (' ')\n" + 112 "CLASS_DESCRIPTOR ('Ljava/io/PrintStream;')\n" + 113 "ARROW ('->')\n" + 114 "SIMPLE_NAME ('println')\n" + 115 "OPEN_PAREN ('(')\n" + 116 "CLASS_DESCRIPTOR ('Ljava/lang/String;')\n" + 117 "CLOSE_PAREN (')')\n" + 118 "VOID_TYPE ('V')\n" + 119 "WHITE_SPACE ('\\n ')\n" + 120 "INSTRUCTION_FORMAT10x ('return-void')\n" + 121 "WHITE_SPACE ('\\n')\n" + 122 "END_METHOD_DIRECTIVE ('.end method')" 123 ); 124 } 125 createLexer()126 @Override protected Lexer createLexer() { 127 return new SmaliLexer(); 128 } 129 getDirPath()130 @Override protected String getDirPath() { 131 return ""; 132 } 133 testErrorToken()134 public void testErrorToken() { 135 String text = ".class public .blah"; 136 doTest(text, 137 "CLASS_DIRECTIVE ('.class')\n" + 138 "WHITE_SPACE (' ')\n" + 139 "ACCESS_SPEC ('public')\n" + 140 "WHITE_SPACE (' ')\n" + 141 "BAD_CHARACTER ('.blah')\n"); 142 } 143 144 /** 145 * Type out an example smali file character by character, ensuring that no exceptions are thrown 146 */ testPartialText()147 public void testPartialText() { 148 String text = 149 ".class public LHelloWorld;\n" + 150 ".super Ljava/lang/Object;\n" + 151 ".method public static main([Ljava/lang/String;)V\n" + 152 " .registers 2\n" + 153 " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" + 154 " const-string v1, \"Hello World!\"\n" + 155 " invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" + 156 " return-void\n" + 157 ".end method"; 158 159 for (int i=1; i<text.length(); i++) { 160 printTokens(text.substring(i), 0); 161 } 162 } 163 164 /** 165 * Generate some random text and make sure the lexer doesn't throw any exceptions 166 */ testRandomText()167 public void testRandomText() { 168 for (int i=0; i<100; i++) { 169 String randomString = randomString(1000); 170 171 printTokens(randomString, 0); 172 } 173 } 174 175 private Random random = new Random(123456789); randomString(int length)176 private String randomString(int length) { 177 StringBuilder sb = new StringBuilder(); 178 for (int i=0; i<length; i++) { 179 int type = random.nextInt(10); 180 181 if (type == 9) { 182 int randomCodepoint; 183 do { 184 randomCodepoint = random.nextInt(); 185 } while(!Character.isValidCodePoint(randomCodepoint)); 186 sb.appendCodePoint(randomCodepoint); 187 } else if (type == 8) { 188 char randomChar; 189 do { 190 randomChar = (char)random.nextInt(2^16); 191 } while(!Character.isValidCodePoint(randomChar)); 192 sb.append(randomChar); 193 } else if (type > 4) { 194 sb.append((char)random.nextInt(256)); 195 } else if (type == 4) { 196 sb.append(' '); 197 } else { 198 sb.append((char)random.nextInt(128)); 199 } 200 } 201 202 return sb.toString(); 203 } 204 } 205