blob: 5dda59e3473fe214a83defe16e8f1ebe21451920 [file] [log] [blame]
Eric Holkd683f9f2018-10-26 16:08:09 -07001/*
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
26using namespace startop::dex;
27using namespace std;
28
29void 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.
41void 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 Holkd62c5aa2018-11-01 15:50:24 -070049 {
Eric Holk5c6a1a52019-09-17 13:28:34 -070050 LiveRegister r{return5.AllocRegister()};
Eric Holkd62c5aa2018-11-01 15:50:24 -070051 return5.BuildConst4(r, 5);
52 return5.BuildReturn(r);
53 }
Eric Holkd683f9f2018-10-26 16:08:09 -070054 return5.Encode();
55
Eric Holkb3927582018-11-08 16:40:16 -080056 // 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -070060 LiveRegister five{method.AllocRegister()};
Eric Holkb3927582018-11-08 16:40:16 -080061 method.BuildConst4(five, 5);
Eric Holk5c6a1a52019-09-17 13:28:34 -070062 LiveRegister object{method.AllocRegister()};
Eric Holkb3927582018-11-08 16:40:16 -080063 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 Holkd683f9f2018-10-26 16:08:09 -070069 // // 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 Holkd62c5aa2018-11-01 15:50:24 -070082 {
Eric Holk5c6a1a52019-09-17 13:28:34 -070083 LiveRegister result = returnStringLength.AllocRegister();
Eric Holkd62c5aa2018-11-01 15:50:24 -070084 returnStringLength.AddInstruction(
85 Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
86 returnStringLength.BuildReturn(result);
87 }
Eric Holkd683f9f2018-10-26 16:08:09 -070088 returnStringLength.Encode();
89
Eric Holkd62c5aa2018-11-01 15:50:24 -070090 // 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 {
Eric Holk5c6a1a52019-09-17 13:28:34 -070094 LiveRegister resultIfZero{returnIfZero.AllocRegister()};
Eric Holkd62c5aa2018-11-01 15:50:24 -070095 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 Holkc69449d2018-12-13 11:35:58 -0800111 // 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 {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700115 LiveRegister resultIfNotZero{returnIfNotZero.AllocRegister()};
Eric Holkc69449d2018-12-13 11:35:58 -0800116 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 Holkd62c5aa2018-11-01 15:50:24 -0700132 // 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700151 LiveRegister zero = method.AllocRegister();
152 LiveRegister result = method.AllocRegister();
Eric Holkd62c5aa2018-11-01 15:50:24 -0700153 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 Holk3cc4afc2018-11-08 14:16:20 -0800175 // 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700180 LiveRegister zero = method.AllocRegister();
Eric Holk3cc4afc2018-11-08 14:16:20 -0800181 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700191 LiveRegister string = method.AllocRegister();
Eric Holk3cc4afc2018-11-08 14:16:20 -0800192 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700203 LiveRegister resultIfZero{method.AllocRegister()};
Eric Holk3cc4afc2018-11-08 14:16:20 -0800204 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700223 LiveRegister resultIfZero{method.AllocRegister()};
Eric Holk3cc4afc2018-11-08 14:16:20 -0800224 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 Holkc69449d2018-12-13 11:35:58 -0800240 // 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700247 LiveRegister result{method.AllocRegister()};
Eric Holkc69449d2018-12-13 11:35:58 -0800248 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) {
Eric Holk5c6a1a52019-09-17 13:28:34 -0700263 LiveRegister result{method.AllocRegister()};
Eric Holkc69449d2018-12-13 11:35:58 -0800264 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 Holk44d8cdf2018-12-17 13:35:34 -0800272 // 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 Holkf3b95892019-07-30 14:47:06 -0700285 TypeDescriptor test_class = TypeDescriptor::FromClassname("android.startop.test.TestClass");
286
Eric Holk3092f992019-07-25 15:14:01 -0700287 // Read a static field
Eric Holkf3b95892019-07-30 14:47:06 -0700288 // int readStaticField() { return TestClass.staticInteger; }
Eric Holk3092f992019-07-25 15:14:01 -0700289 MethodBuilder readStaticField{
290 cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
291 [&](MethodBuilder& method) {
292 const ir::FieldDecl* field =
Eric Holkf3b95892019-07-30 14:47:06 -0700293 dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
Eric Holk5c6a1a52019-09-17 13:28:34 -0700294 LiveRegister result{method.AllocRegister()};
Eric Holk3092f992019-07-25 15:14:01 -0700295 method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
296 method.BuildReturn(result, /*is_object=*/false);
297 method.Encode();
298 }(readStaticField);
299
Eric Holk70445d02019-07-26 09:37:46 -0700300 // Set a static field
301 // void setStaticField() { TestClass.staticInteger = 7; }
302 MethodBuilder setStaticField{
303 cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
304 [&](MethodBuilder& method) {
305 const ir::FieldDecl* field =
Eric Holkf3b95892019-07-30 14:47:06 -0700306 dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
Eric Holk5c6a1a52019-09-17 13:28:34 -0700307 LiveRegister number{method.AllocRegister()};
Eric Holk70445d02019-07-26 09:37:46 -0700308 method.BuildConst4(number, 7);
309 method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
310 method.BuildReturn();
311 method.Encode();
312 }(setStaticField);
313
Eric Holkf3b95892019-07-30 14:47:06 -0700314 // Read an instance field
315 // int readInstanceField(TestClass obj) { return obj.instanceField; }
316 MethodBuilder readInstanceField{
317 cbuilder.CreateMethod("readInstanceField", Prototype{TypeDescriptor::Int(), test_class})};
318 [&](MethodBuilder& method) {
319 const ir::FieldDecl* field =
320 dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
Eric Holk5c6a1a52019-09-17 13:28:34 -0700321 LiveRegister result{method.AllocRegister()};
Eric Holkf3b95892019-07-30 14:47:06 -0700322 method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
323 method.BuildReturn(result, /*is_object=*/false);
324 method.Encode();
325 }(readInstanceField);
326
327 // Set an instance field
328 // void setInstanceField(TestClass obj) { obj.instanceField = 7; }
329 MethodBuilder setInstanceField{
330 cbuilder.CreateMethod("setInstanceField", Prototype{TypeDescriptor::Void(), test_class})};
331 [&](MethodBuilder& method) {
332 const ir::FieldDecl* field =
333 dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
Eric Holk5c6a1a52019-09-17 13:28:34 -0700334 LiveRegister number{method.AllocRegister()};
Eric Holkf3b95892019-07-30 14:47:06 -0700335 method.BuildConst4(number, 7);
336 method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
337 method.BuildReturn();
338 method.Encode();
339 }(setInstanceField);
340
Eric Holkd683f9f2018-10-26 16:08:09 -0700341 slicer::MemView image{dex_file.CreateImage()};
342 std::ofstream out_file(outdir + "/simple.dex");
343 out_file.write(image.ptr<const char>(), image.size());
344}
345
346int main(int argc, char** argv) {
347 CHECK_EQ(argc, 2);
348
349 string outdir = argv[1];
350
351 GenerateTrivialDexFile(outdir);
352 GenerateSimpleTestCases(outdir);
353}