blob: f62ec5dde85eeca4c3306a8a89b9b77bc9b0fa4a [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 {
50 Value r{return5.MakeRegister()};
51 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) {
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 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 {
83 Value result = returnStringLength.MakeRegister();
84 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 {
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 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 {
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 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) {
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 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) {
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 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) {
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 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 Holkd683f9f2018-10-26 16:08:09 -0700285 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
290int main(int argc, char** argv) {
291 CHECK_EQ(argc, 2);
292
293 string outdir = argv[1];
294
295 GenerateTrivialDexFile(outdir);
296 GenerateSimpleTestCases(outdir);
297}