diff options
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()); |