Home | History | Annotate | Download | only in view_compiler
      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