Loading startop/view_compiler/dex_builder.cc +57 −3 Original line number Diff line number Diff line Loading @@ -102,6 +102,12 @@ 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; case Instruction::Op::kSetStaticField: out << "kSetStaticField"; return out; } } Loading Loading @@ -229,6 +235,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()); Loading Loading @@ -360,6 +382,9 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { return EncodeNew(instruction); case Instruction::Op::kCheckCast: return EncodeCast(instruction); case Instruction::Op::kGetStaticField: case Instruction::Op::kSetStaticField: return EncodeStaticFieldOp(instruction); } } Loading Loading @@ -428,7 +453,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) { Loading @@ -452,12 +477,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], Loading Loading @@ -514,6 +539,35 @@ void MethodBuilder::EncodeCast(const Instruction& instruction) { Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); } void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) { switch (instruction.opcode()) { case Instruction::Op::kGetStaticField: { 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()); break; } case Instruction::Op::kSetStaticField: { CHECK(!instruction.dest().has_value()); const auto& args = instruction.args(); CHECK_EQ(1, args.size()); CHECK(args[0].is_variable()); Encode21c(::art::Instruction::SPUT, RegisterValue(args[0]), instruction.index_argument()); break; } default: { LOG(FATAL) << "Unsupported static field operation"; } } } size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); Loading startop/view_compiler/dex_builder.h +43 −22 Original line number Diff line number Diff line Loading @@ -153,6 +153,7 @@ class Instruction { kBranchEqz, kBranchNEqz, kCheckCast, kGetStaticField, kInvokeDirect, kInvokeInterface, kInvokeStatic, Loading @@ -162,6 +163,7 @@ class Instruction { kNew, kReturn, kReturnObject, kSetStaticField }; //////////////////////// Loading @@ -170,12 +172,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` Loading @@ -186,77 +188,87 @@ 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}; } static inline Instruction SetStaticField(size_t field_id, Value value) { return Instruction{Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value}; } /////////////// // Accessors // /////////////// 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_; Loading Loading @@ -319,6 +331,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 Loading Loading @@ -481,6 +494,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, Loading Loading @@ -526,6 +544,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> Loading startop/view_compiler/dex_builder_test/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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"], Loading startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +21 −2 Original line number Diff line number Diff line Loading @@ -24,10 +24,10 @@ import java.lang.reflect.Method; // 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() {} Loading Loading @@ -167,4 +167,23 @@ 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)); } @Test public void setStaticField() throws Exception { ClassLoader loader = loadDexFile("simple.dex"); Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); Method method = clazz.getMethod("setStaticField"); TestClass.staticInteger = 5; method.invoke(null); Assert.assertEquals(7, TestClass.staticInteger); } } startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java 0 → 100644 +23 −0 Original line number Diff line number Diff line /* * 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; } Loading
startop/view_compiler/dex_builder.cc +57 −3 Original line number Diff line number Diff line Loading @@ -102,6 +102,12 @@ 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; case Instruction::Op::kSetStaticField: out << "kSetStaticField"; return out; } } Loading Loading @@ -229,6 +235,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()); Loading Loading @@ -360,6 +382,9 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { return EncodeNew(instruction); case Instruction::Op::kCheckCast: return EncodeCast(instruction); case Instruction::Op::kGetStaticField: case Instruction::Op::kSetStaticField: return EncodeStaticFieldOp(instruction); } } Loading Loading @@ -428,7 +453,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) { Loading @@ -452,12 +477,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], Loading Loading @@ -514,6 +539,35 @@ void MethodBuilder::EncodeCast(const Instruction& instruction) { Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); } void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) { switch (instruction.opcode()) { case Instruction::Op::kGetStaticField: { 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()); break; } case Instruction::Op::kSetStaticField: { CHECK(!instruction.dest().has_value()); const auto& args = instruction.args(); CHECK_EQ(1, args.size()); CHECK(args[0].is_variable()); Encode21c(::art::Instruction::SPUT, RegisterValue(args[0]), instruction.index_argument()); break; } default: { LOG(FATAL) << "Unsupported static field operation"; } } } size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); Loading
startop/view_compiler/dex_builder.h +43 −22 Original line number Diff line number Diff line Loading @@ -153,6 +153,7 @@ class Instruction { kBranchEqz, kBranchNEqz, kCheckCast, kGetStaticField, kInvokeDirect, kInvokeInterface, kInvokeStatic, Loading @@ -162,6 +163,7 @@ class Instruction { kNew, kReturn, kReturnObject, kSetStaticField }; //////////////////////// Loading @@ -170,12 +172,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` Loading @@ -186,77 +188,87 @@ 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}; } static inline Instruction SetStaticField(size_t field_id, Value value) { return Instruction{Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value}; } /////////////// // Accessors // /////////////// 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_; Loading Loading @@ -319,6 +331,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 Loading Loading @@ -481,6 +494,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, Loading Loading @@ -526,6 +544,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> Loading
startop/view_compiler/dex_builder_test/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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"], Loading
startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +21 −2 Original line number Diff line number Diff line Loading @@ -24,10 +24,10 @@ import java.lang.reflect.Method; // 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() {} Loading Loading @@ -167,4 +167,23 @@ 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)); } @Test public void setStaticField() throws Exception { ClassLoader loader = loadDexFile("simple.dex"); Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); Method method = clazz.getMethod("setStaticField"); TestClass.staticInteger = 5; method.invoke(null); Assert.assertEquals(7, TestClass.staticInteger); } }
startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java 0 → 100644 +23 −0 Original line number Diff line number Diff line /* * 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; }