d1b4383660
This was causing the viewcompiler to crash on deeper layout hierarchies. We fix this by reserving several scratch registers. When an invoke instruction uses registers that don't fit in a 4-bit field, we move all of these values into the scratch registers and use an invoke/range instruction instead. The scratch registers are all above the highest allocated register, so they are guaranteed not to clobber meaningful values. Supporting more registers for invoke also required supporting register-to-register moves, which some how we'd gotten by without so far. Finally, to make viewcompiler fail more loudly when things go wrong, many DCHECKs have been changed to CHECKs. Bug: 123517491 Test: atest Change-Id: I9eb7c9bcf1fc7d713e664b331804bdcddafc95a4
181 lines
5.6 KiB
C++
181 lines
5.6 KiB
C++
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "dex_builder.h"
|
|
|
|
#include "dex/art_dex_file_loader.h"
|
|
#include "dex/dex_file.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace startop::dex;
|
|
|
|
// Takes a DexBuilder, encodes it into an in-memory DEX file, verifies the resulting DEX file and
|
|
// returns whether the verification was successful.
|
|
bool EncodeAndVerify(DexBuilder* dex_file) {
|
|
slicer::MemView image{dex_file->CreateImage()};
|
|
|
|
art::ArtDexFileLoader loader;
|
|
std::string error_msg;
|
|
std::unique_ptr<const art::DexFile> loaded_dex_file{loader.Open(image.ptr<const uint8_t>(),
|
|
image.size(),
|
|
/*location=*/"",
|
|
/*location_checksum=*/0,
|
|
/*oat_dex_file=*/nullptr,
|
|
/*verify=*/true,
|
|
/*verify_checksum=*/false,
|
|
&error_msg)};
|
|
return loaded_dex_file != nullptr;
|
|
}
|
|
|
|
// Write out and verify a DEX file that corresponds to:
|
|
//
|
|
// package dextest;
|
|
// public class DexTest {
|
|
// public static void foo() {}
|
|
// }
|
|
TEST(DexBuilderTest, VerifyDexWithClassMethod) {
|
|
DexBuilder dex_file;
|
|
|
|
auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
|
|
|
|
auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void()})};
|
|
method.BuildReturn();
|
|
method.Encode();
|
|
|
|
EXPECT_TRUE(EncodeAndVerify(&dex_file));
|
|
}
|
|
|
|
// Makes sure a bad DEX class fails to verify.
|
|
TEST(DexBuilderTest, VerifyBadDexWithClassMethod) {
|
|
DexBuilder dex_file;
|
|
|
|
auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
|
|
|
|
// This method has the error, because methods cannot take Void() as a parameter.
|
|
auto method{
|
|
cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void(), TypeDescriptor::Void()})};
|
|
method.BuildReturn();
|
|
method.Encode();
|
|
|
|
EXPECT_FALSE(EncodeAndVerify(&dex_file));
|
|
}
|
|
|
|
// Write out and verify a DEX file that corresponds to:
|
|
//
|
|
// package dextest;
|
|
// public class DexTest {
|
|
// public static int foo() { return 5; }
|
|
// }
|
|
TEST(DexBuilderTest, VerifyDexReturn5) {
|
|
DexBuilder dex_file;
|
|
|
|
auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
|
|
|
|
auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
|
|
auto r = method.MakeRegister();
|
|
method.BuildConst4(r, 5);
|
|
method.BuildReturn(r);
|
|
method.Encode();
|
|
|
|
EXPECT_TRUE(EncodeAndVerify(&dex_file));
|
|
}
|
|
|
|
// Write out and verify a DEX file that corresponds to:
|
|
//
|
|
// package dextest;
|
|
// public class DexTest {
|
|
// public static int foo(int x) { return x; }
|
|
// }
|
|
TEST(DexBuilderTest, VerifyDexReturnIntParam) {
|
|
DexBuilder dex_file;
|
|
|
|
auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
|
|
|
|
auto method{
|
|
cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
|
method.BuildReturn(Value::Parameter(0));
|
|
method.Encode();
|
|
|
|
EXPECT_TRUE(EncodeAndVerify(&dex_file));
|
|
}
|
|
|
|
// Write out and verify a DEX file that corresponds to:
|
|
//
|
|
// package dextest;
|
|
// public class DexTest {
|
|
// public static int foo(String s) { return s.length(); }
|
|
// }
|
|
TEST(DexBuilderTest, VerifyDexCallStringLength) {
|
|
DexBuilder dex_file;
|
|
|
|
auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
|
|
|
|
MethodBuilder method{cbuilder.CreateMethod(
|
|
"foo", Prototype{TypeDescriptor::Int(), TypeDescriptor::FromClassname("java.lang.String")})};
|
|
|
|
Value result = method.MakeRegister();
|
|
|
|
MethodDeclData string_length =
|
|
dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
|
|
"length",
|
|
Prototype{TypeDescriptor::Int()});
|
|
|
|
method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
|
|
method.BuildReturn(result);
|
|
|
|
method.Encode();
|
|
|
|
EXPECT_TRUE(EncodeAndVerify(&dex_file));
|
|
}
|
|
|
|
// Write out and verify a DEX file that corresponds to:
|
|
//
|
|
// package dextest;
|
|
// public class DexTest {
|
|
// public static int foo(String s) { return s.length(); }
|
|
// }
|
|
TEST(DexBuilderTest, VerifyDexCallManyRegisters) {
|
|
DexBuilder dex_file;
|
|
|
|
auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
|
|
|
|
MethodBuilder method{cbuilder.CreateMethod(
|
|
"foo", Prototype{TypeDescriptor::Int()})};
|
|
|
|
Value result = method.MakeRegister();
|
|
|
|
// Make a bunch of registers
|
|
for (size_t i = 0; i < 25; ++i) {
|
|
method.MakeRegister();
|
|
}
|
|
|
|
// Now load a string literal into a register
|
|
Value string_val = method.MakeRegister();
|
|
method.BuildConstString(string_val, "foo");
|
|
|
|
MethodDeclData string_length =
|
|
dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
|
|
"length",
|
|
Prototype{TypeDescriptor::Int()});
|
|
|
|
method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, string_val));
|
|
method.BuildReturn(result);
|
|
|
|
method.Encode();
|
|
|
|
EXPECT_TRUE(EncodeAndVerify(&dex_file));
|
|
}
|