Eric Holk | d683f9f | 2018-10-26 16:08:09 -0700 | [diff] [blame] | 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()})}; |
Eric Holk | d62c5aa | 2018-11-01 15:50:24 -0700 | [diff] [blame] | 49 | { |
| 50 | Value r{return5.MakeRegister()}; |
| 51 | return5.BuildConst4(r, 5); |
| 52 | return5.BuildReturn(r); |
| 53 | } |
Eric Holk | d683f9f | 2018-10-26 16:08:09 -0700 | [diff] [blame] | 54 | return5.Encode(); |
| 55 | |
Eric Holk | b392758 | 2018-11-08 16:40:16 -0800 | [diff] [blame] | 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 | |
Eric Holk | d683f9f | 2018-10-26 16:08:09 -0700 | [diff] [blame] | 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})}; |
Eric Holk | d62c5aa | 2018-11-01 15:50:24 -0700 | [diff] [blame] | 82 | { |
| 83 | Value result = returnStringLength.MakeRegister(); |
| 84 | returnStringLength.AddInstruction( |
| 85 | Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0))); |
| 86 | returnStringLength.BuildReturn(result); |
| 87 | } |
Eric Holk | d683f9f | 2018-10-26 16:08:09 -0700 | [diff] [blame] | 88 | returnStringLength.Encode(); |
| 89 | |
Eric Holk | d62c5aa | 2018-11-01 15:50:24 -0700 | [diff] [blame] | 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 | |
Eric Holk | c69449d | 2018-12-13 11:35:58 -0800 | [diff] [blame] | 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 | |
Eric Holk | d62c5aa | 2018-11-01 15:50:24 -0700 | [diff] [blame] | 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 | |
Eric Holk | 3cc4afc | 2018-11-08 14:16:20 -0800 | [diff] [blame] | 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 | |
Eric Holk | c69449d | 2018-12-13 11:35:58 -0800 | [diff] [blame] | 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 | |
Eric Holk | 44d8cdf | 2018-12-17 13:35:34 -0800 | [diff] [blame] | 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 | |
Eric Holk | d683f9f | 2018-10-26 16:08:09 -0700 | [diff] [blame] | 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 | } |