summaryrefslogtreecommitdiff
path: root/startop/view_compiler
diff options
context:
space:
mode:
authorEric Holk <eholk@google.com>2019-07-25 15:14:01 -0700
committerEric Holk <eholk@google.com>2019-07-26 09:42:41 -0700
commit3092f99ae63f12f5c18d40f21616c98f2b6c62af (patch)
tree7b0114acfb0a8d85c1688ddca0c3913353de7d8b /startop/view_compiler
parenta0b2086373140496b131233ea44979098b9dedff (diff)
[viewcompiler] Add static field get instructions to DexBuilder
This allows us to generate code that can read static fields in a class. Once we include several other field operations, we will be able to generate more specialized inflation code in the view compiler. Bug: 111895153 Change-Id: Ia11195b1cea6d5a3ddbc60d972922586a062c853
Diffstat (limited to 'startop/view_compiler')
-rw-r--r--startop/view_compiler/dex_builder.cc37
-rw-r--r--startop/view_compiler/dex_builder.h59
-rw-r--r--startop/view_compiler/dex_builder_test/Android.bp1
-rw-r--r--startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java13
-rw-r--r--startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java23
-rw-r--r--startop/view_compiler/dex_testcase_generator.cc15
6 files changed, 121 insertions, 27 deletions
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 6047e8c74e38..66bc698e2136 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -102,6 +102,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
case Instruction::Op::kCheckCast:
out << "kCheckCast";
return out;
+ case Instruction::Op::kGetStaticField:
+ out << "kGetStaticField";
+ return out;
}
}
@@ -229,6 +232,22 @@ ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) {
return type;
}
+ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name,
+ TypeDescriptor type) {
+ const auto key = std::make_tuple(parent, name);
+ if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) {
+ return field_decls_by_key_[key];
+ }
+
+ ir::FieldDecl* field = Alloc<ir::FieldDecl>();
+ field->parent = GetOrAddType(parent);
+ field->name = GetOrAddString(name);
+ field->type = GetOrAddType(type);
+ dex_file_->fields_map[field->orig_index] = field;
+ field_decls_by_key_[key] = field;
+ return field;
+}
+
ir::Proto* Prototype::Encode(DexBuilder* dex) const {
auto* proto = dex->Alloc<ir::Proto>();
proto->shorty = dex->GetOrAddString(Shorty());
@@ -360,6 +379,8 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
return EncodeNew(instruction);
case Instruction::Op::kCheckCast:
return EncodeCast(instruction);
+ case Instruction::Op::kGetStaticField:
+ return EncodeStaticFieldOp(instruction);
}
}
@@ -428,7 +449,7 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct
// first move all the arguments into contiguous temporary registers.
std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
- const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
+ const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument());
CHECK(prototype.has_value());
for (size_t i = 0; i < instruction.args().size(); ++i) {
@@ -452,12 +473,12 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct
Encode3rc(InvokeToInvokeRange(opcode),
instruction.args().size(),
- instruction.method_id(),
+ instruction.index_argument(),
RegisterValue(scratch[0]));
} else {
Encode35c(opcode,
instruction.args().size(),
- instruction.method_id(),
+ instruction.index_argument(),
arguments[0],
arguments[1],
arguments[2],
@@ -514,6 +535,16 @@ void MethodBuilder::EncodeCast(const Instruction& instruction) {
Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
}
+void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) {
+ CHECK_EQ(Instruction::Op::kGetStaticField, instruction.opcode());
+ CHECK(instruction.dest().has_value());
+ CHECK(instruction.dest()->is_variable());
+ CHECK_EQ(0, instruction.args().size());
+
+ Encode21c(
+ ::art::Instruction::SGET, RegisterValue(*instruction.dest()), instruction.index_argument());
+}
+
size_t MethodBuilder::RegisterValue(const Value& value) const {
if (value.is_register()) {
return value.value();
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 541d80077bd3..a7ccb4a9452b 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -153,6 +153,7 @@ class Instruction {
kBranchEqz,
kBranchNEqz,
kCheckCast,
+ kGetStaticField,
kInvokeDirect,
kInvokeInterface,
kInvokeStatic,
@@ -170,12 +171,12 @@ class Instruction {
// For instructions with no return value and no arguments.
static inline Instruction OpNoArgs(Op opcode) {
- return Instruction{opcode, /*method_id*/ 0, /*dest*/ {}};
+ return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}};
}
// For most instructions, which take some number of arguments and have an optional return value.
template <typename... T>
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
- return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
+ return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
}
// A cast instruction. Basically, `(type)val`
@@ -186,49 +187,54 @@ class Instruction {
// For method calls.
template <typename... T>
- static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest,
Value this_arg, T... args) {
return Instruction{
- Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+ Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
}
// Returns an object
template <typename... T>
- static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeVirtualObject(size_t index_argument, std::optional<const Value> dest,
Value this_arg, T... args) {
return Instruction{
- Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+ Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
}
// For direct calls (basically, constructors).
template <typename... T>
- static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
Value this_arg, T... args) {
return Instruction{
- Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+ Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
}
// Returns an object
template <typename... T>
- static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeDirectObject(size_t index_argument, std::optional<const Value> dest,
Value this_arg, T... args) {
return Instruction{
- Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+ Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
}
// For static calls.
template <typename... T>
- static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
T... args) {
- return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...};
+ return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
}
// Returns an object
template <typename... T>
- static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeStaticObject(size_t index_argument, std::optional<const Value> dest,
T... args) {
- return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...};
+ return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
}
// For static calls.
template <typename... T>
- static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest,
+ static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
T... args) {
- return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...};
+ return Instruction{Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
+
+ }
+
+ static inline Instruction GetStaticField(size_t field_id, Value dest) {
+ return Instruction{Op::kGetStaticField, field_id, dest};
}
///////////////
@@ -236,27 +242,27 @@ class Instruction {
///////////////
Op opcode() const { return opcode_; }
- size_t method_id() const { return method_id_; }
+ size_t index_argument() const { return index_argument_; }
bool result_is_object() const { return result_is_object_; }
const std::optional<const Value>& dest() const { return dest_; }
const std::vector<const Value>& args() const { return args_; }
private:
- inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
- : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {}
+ inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
+ : opcode_{opcode}, index_argument_{index_argument}, result_is_object_{false}, dest_{dest}, args_{} {}
template <typename... T>
- inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object,
+ inline constexpr Instruction(Op opcode, size_t index_argument, bool result_is_object,
std::optional<const Value> dest, T... args)
: opcode_{opcode},
- method_id_{method_id},
+ index_argument_{index_argument},
result_is_object_{result_is_object},
dest_{dest},
args_{args...} {}
const Op opcode_;
// The index of the method to invoke, for kInvokeVirtual and similar opcodes.
- const size_t method_id_{0};
+ const size_t index_argument_{0};
const bool result_is_object_;
const std::optional<const Value> dest_;
const std::vector<const Value> args_;
@@ -319,6 +325,7 @@ class MethodBuilder {
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
void EncodeNew(const Instruction& instruction);
void EncodeCast(const Instruction& instruction);
+ void EncodeStaticFieldOp(const Instruction& instruction);
// Low-level instruction format encoding. See
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
@@ -481,6 +488,11 @@ class DexBuilder {
// See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
// imported classes.
ir::Type* GetOrAddType(const std::string& descriptor);
+ inline ir::Type* GetOrAddType(TypeDescriptor descriptor) {
+ return GetOrAddType(descriptor.descriptor());
+ }
+
+ ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type);
// Returns the method id for the method, creating it if it has not been created yet.
const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
@@ -526,6 +538,9 @@ class DexBuilder {
// Keep track of already-encoded protos.
std::map<Prototype, ir::Proto*> proto_map_;
+
+ // Keep track of fields that have been declared
+ std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_;
};
template <typename... T>
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index ac60e966fe43..9ad1ca1ef48e 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -39,6 +39,7 @@ android_test {
srcs: [
"src/android/startop/test/DexBuilderTest.java",
"src/android/startop/test/LayoutCompilerTest.java",
+ "src/android/startop/test/TestClass.java",
],
sdk_version: "current",
data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 42d4161ee81e..df11bfafd357 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -28,10 +28,10 @@ import org.junit.Test;
// Adding tests here requires changes in several other places. See README.md in
// the view_compiler directory for more information.
-public class DexBuilderTest {
+public final class DexBuilderTest {
static ClassLoader loadDexFile(String filename) throws Exception {
return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
- ClassLoader.getSystemClassLoader());
+ DexBuilderTest.class.getClassLoader());
}
public void hello() {}
@@ -171,4 +171,13 @@ public class DexBuilderTest {
}
Assert.assertTrue(castFailed);
}
+
+ @Test
+ public void readStaticField() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("readStaticField");
+ TestClass.staticInteger = 5;
+ Assert.assertEquals(5, method.invoke(null));
+ }
}
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
new file mode 100644
index 000000000000..dd7792306030
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+package android.startop.test;
+
+ /**
+ * A simple class to help test DexBuilder.
+ */
+public final class TestClass {
+ public static int staticInteger;
+
+ public int instanceField;
+}
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index f62ec5dde85e..5227f8054765 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -282,6 +282,21 @@ void GenerateSimpleTestCases(const string& outdir) {
method.Encode();
}(castObjectToString);
+ // Read a static field
+ // integer readStaticField() { return TestClass.staticInteger; }
+ MethodBuilder readStaticField{
+ cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ const ir::FieldDecl* field =
+ dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
+ "staticInteger",
+ TypeDescriptor::Int());
+ Value result{method.MakeRegister()};
+ method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
+ method.BuildReturn(result, /*is_object=*/false);
+ method.Encode();
+ }(readStaticField);
+
slicer::MemView image{dex_file.CreateImage()};
std::ofstream out_file(outdir + "/simple.dex");
out_file.write(image.ptr<const char>(), image.size());