1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "android-base/logging.h" 18 #include "dex_builder.h" 19 20 #include <fstream> 21 #include <string> 22 23 // Adding tests here requires changes in several other places. See README.md in 24 // the view_compiler directory for more information. 25 26 using namespace startop::dex; 27 using namespace std; 28 29 void GenerateTrivialDexFile(const string& outdir) { 30 DexBuilder dex_file; 31 32 ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.Trivial")}; 33 cbuilder.set_source_file("dex_testcase_generator.cc#GenerateTrivialDexFile"); 34 35 slicer::MemView image{dex_file.CreateImage()}; 36 std::ofstream out_file(outdir + "/trivial.dex"); 37 out_file.write(image.ptr<const char>(), image.size()); 38 } 39 40 // Generates test cases that test around 1 instruction. 41 void GenerateSimpleTestCases(const string& outdir) { 42 DexBuilder dex_file; 43 44 ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.SimpleTests")}; 45 cbuilder.set_source_file("dex_testcase_generator.cc#GenerateSimpleTestCases"); 46 47 // int return5() { return 5; } 48 auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})}; 49 { 50 Value r{return5.MakeRegister()}; 51 return5.BuildConst4(r, 5); 52 return5.BuildReturn(r); 53 } 54 return5.Encode(); 55 56 // int return5() { return 5; } 57 auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")}; 58 auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})}; 59 [&](MethodBuilder& method) { 60 Value five{method.MakeRegister()}; 61 method.BuildConst4(five, 5); 62 Value object{method.MakeRegister()}; 63 method.BuildNew( 64 object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five); 65 method.BuildReturn(object, /*is_object=*/true); 66 }(returnInteger5); 67 returnInteger5.Encode(); 68 69 // // int returnParam(int x) { return x; } 70 auto returnParam{cbuilder.CreateMethod("returnParam", 71 Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; 72 returnParam.BuildReturn(Value::Parameter(0)); 73 returnParam.Encode(); 74 75 // int returnStringLength(String x) { return x.length(); } 76 auto string_type{TypeDescriptor::FromClassname("java.lang.String")}; 77 MethodDeclData string_length{ 78 dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()})}; 79 80 auto returnStringLength{ 81 cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})}; 82 { 83 Value result = returnStringLength.MakeRegister(); 84 returnStringLength.AddInstruction( 85 Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0))); 86 returnStringLength.BuildReturn(result); 87 } 88 returnStringLength.Encode(); 89 90 // int returnIfZero(int x) { if (x == 0) { return 5; } else { return 3; } } 91 MethodBuilder returnIfZero{cbuilder.CreateMethod( 92 "returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; 93 { 94 Value resultIfZero{returnIfZero.MakeRegister()}; 95 Value else_target{returnIfZero.MakeLabel()}; 96 returnIfZero.AddInstruction(Instruction::OpWithArgs( 97 Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target)); 98 // else branch 99 returnIfZero.BuildConst4(resultIfZero, 3); 100 returnIfZero.AddInstruction( 101 Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfZero)); 102 // then branch 103 returnIfZero.AddInstruction( 104 Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); 105 returnIfZero.BuildConst4(resultIfZero, 5); 106 returnIfZero.AddInstruction( 107 Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfZero)); 108 } 109 returnIfZero.Encode(); 110 111 // int returnIfNotZero(int x) { if (x != 0) { return 5; } else { return 3; } } 112 MethodBuilder returnIfNotZero{cbuilder.CreateMethod( 113 "returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; 114 { 115 Value resultIfNotZero{returnIfNotZero.MakeRegister()}; 116 Value else_target{returnIfNotZero.MakeLabel()}; 117 returnIfNotZero.AddInstruction(Instruction::OpWithArgs( 118 Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target)); 119 // else branch 120 returnIfNotZero.BuildConst4(resultIfNotZero, 3); 121 returnIfNotZero.AddInstruction( 122 Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero)); 123 // then branch 124 returnIfNotZero.AddInstruction( 125 Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); 126 returnIfNotZero.BuildConst4(resultIfNotZero, 5); 127 returnIfNotZero.AddInstruction( 128 Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero)); 129 } 130 returnIfNotZero.Encode(); 131 132 // Make sure backwards branches work too. 133 // 134 // Pseudo code for test: 135 // { 136 // zero = 0; 137 // result = 1; 138 // if (zero == 0) goto B; 139 // A: 140 // return result; 141 // B: 142 // result = 2; 143 // if (zero == 0) goto A; 144 // result = 3; 145 // return result; 146 // } 147 // If it runs correctly, this test should return 2. 148 MethodBuilder backwardsBranch{ 149 cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})}; 150 [](MethodBuilder& method) { 151 Value zero = method.MakeRegister(); 152 Value result = method.MakeRegister(); 153 Value labelA = method.MakeLabel(); 154 Value labelB = method.MakeLabel(); 155 method.BuildConst4(zero, 0); 156 method.BuildConst4(result, 1); 157 method.AddInstruction( 158 Instruction::OpWithArgs(Instruction::Op::kBranchEqz, /*dest=*/{}, zero, labelB)); 159 160 method.AddInstruction( 161 Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, labelA)); 162 method.BuildReturn(result); 163 164 method.AddInstruction( 165 Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, labelB)); 166 method.BuildConst4(result, 2); 167 method.AddInstruction( 168 Instruction::OpWithArgs(Instruction::Op::kBranchEqz, /*dest=*/{}, zero, labelA)); 169 170 method.BuildConst4(result, 3); 171 method.BuildReturn(result); 172 }(backwardsBranch); 173 backwardsBranch.Encode(); 174 175 // Test that we can make a null value. Basically: 176 // 177 // public static String returnNull() { return null; } 178 MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})}; 179 [](MethodBuilder& method) { 180 Value zero = method.MakeRegister(); 181 method.BuildConst4(zero, 0); 182 method.BuildReturn(zero, /*is_object=*/true); 183 }(returnNull); 184 returnNull.Encode(); 185 186 // Test that we can make String literals. Basically: 187 // 188 // public static String makeString() { return "Hello, World!"; } 189 MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})}; 190 [](MethodBuilder& method) { 191 Value string = method.MakeRegister(); 192 method.BuildConstString(string, "Hello, World!"); 193 method.BuildReturn(string, /*is_object=*/true); 194 }(makeString); 195 makeString.Encode(); 196 197 // Make sure strings are sorted correctly. 198 // 199 // int returnStringIfZeroAB(int x) { if (x == 0) { return "a"; } else { return "b"; } } 200 MethodBuilder returnStringIfZeroAB{ 201 cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})}; 202 [&](MethodBuilder& method) { 203 Value resultIfZero{method.MakeRegister()}; 204 Value else_target{method.MakeLabel()}; 205 method.AddInstruction(Instruction::OpWithArgs( 206 Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target)); 207 // else branch 208 method.BuildConstString(resultIfZero, "b"); 209 method.AddInstruction( 210 Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); 211 // then branch 212 method.AddInstruction( 213 Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); 214 method.BuildConstString(resultIfZero, "a"); 215 method.AddInstruction( 216 Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); 217 method.Encode(); 218 }(returnStringIfZeroAB); 219 // int returnStringIfZeroAB(int x) { if (x == 0) { return "b"; } else { return "a"; } } 220 MethodBuilder returnStringIfZeroBA{ 221 cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})}; 222 [&](MethodBuilder& method) { 223 Value resultIfZero{method.MakeRegister()}; 224 Value else_target{method.MakeLabel()}; 225 method.AddInstruction(Instruction::OpWithArgs( 226 Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target)); 227 // else branch 228 method.BuildConstString(resultIfZero, "a"); 229 method.AddInstruction( 230 Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); 231 // then branch 232 method.AddInstruction( 233 Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target)); 234 method.BuildConstString(resultIfZero, "b"); 235 method.AddInstruction( 236 Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero)); 237 method.Encode(); 238 }(returnStringIfZeroBA); 239 240 // Make sure we can invoke static methods that return an object 241 // String invokeStaticReturnObject(int n, int radix) { return java.lang.Integer.toString(n, 242 // radix); } 243 MethodBuilder invokeStaticReturnObject{ 244 cbuilder.CreateMethod("invokeStaticReturnObject", 245 Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})}; 246 [&](MethodBuilder& method) { 247 Value result{method.MakeRegister()}; 248 MethodDeclData to_string{dex_file.GetOrDeclareMethod( 249 TypeDescriptor::FromClassname("java.lang.Integer"), 250 "toString", 251 Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})}; 252 method.AddInstruction(Instruction::InvokeStaticObject( 253 to_string.id, result, Value::Parameter(0), Value::Parameter(1))); 254 method.BuildReturn(result, /*is_object=*/true); 255 method.Encode(); 256 }(invokeStaticReturnObject); 257 258 // Make sure we can invoke virtual methods that return an object 259 // String invokeVirtualReturnObject(String s, int n) { return s.substring(n); } 260 MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod( 261 "invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})}; 262 [&](MethodBuilder& method) { 263 Value result{method.MakeRegister()}; 264 MethodDeclData substring{dex_file.GetOrDeclareMethod( 265 string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})}; 266 method.AddInstruction(Instruction::InvokeVirtualObject( 267 substring.id, result, Value::Parameter(0), Value::Parameter(1))); 268 method.BuildReturn(result, /*is_object=*/true); 269 method.Encode(); 270 }(invokeVirtualReturnObject); 271 272 // Make sure we can cast objects 273 // String castObjectToString(Object o) { return (String)o; } 274 MethodBuilder castObjectToString{cbuilder.CreateMethod( 275 "castObjectToString", 276 Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})}; 277 [&](MethodBuilder& method) { 278 const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor()); 279 method.AddInstruction( 280 Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index))); 281 method.BuildReturn(Value::Parameter(0), /*is_object=*/true); 282 method.Encode(); 283 }(castObjectToString); 284 285 slicer::MemView image{dex_file.CreateImage()}; 286 std::ofstream out_file(outdir + "/simple.dex"); 287 out_file.write(image.ptr<const char>(), image.size()); 288 } 289 290 int main(int argc, char** argv) { 291 CHECK_EQ(argc, 2); 292 293 string outdir = argv[1]; 294 295 GenerateTrivialDexFile(outdir); 296 GenerateSimpleTestCases(outdir); 297 } 298