Loading startop/view_compiler/dex_builder.cc +17 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) { case Instruction::Op::kNew: out << "kNew"; return out; case Instruction::Op::kCheckCast: out << "kCheckCast"; return out; } } Loading Loading @@ -329,6 +332,8 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { return EncodeBranch(art::Instruction::IF_NEZ, instruction); case Instruction::Op::kNew: return EncodeNew(instruction); case Instruction::Op::kCheckCast: return EncodeCast(instruction); } } Loading Loading @@ -422,6 +427,18 @@ void MethodBuilder::EncodeNew(const Instruction& instruction) { Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value()); } void MethodBuilder::EncodeCast(const Instruction& instruction) { DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode()); DCHECK(instruction.dest().has_value()); DCHECK(instruction.dest()->is_variable()); DCHECK_EQ(1, instruction.args().size()); const Value& type = instruction.args()[0]; DCHECK_LT(RegisterValue(*instruction.dest()), 256); DCHECK(type.is_type()); Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); } size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); Loading startop/view_compiler/dex_builder.h +17 −8 Original line number Diff line number Diff line Loading @@ -142,17 +142,18 @@ class Instruction { // The operation performed by this instruction. These are virtual instructions that do not // correspond exactly to DEX instructions. enum class Op { kReturn, kReturnObject, kMove, kInvokeVirtual, kInvokeDirect, kInvokeStatic, kInvokeInterface, kBindLabel, kBranchEqz, kBranchNEqz, kNew kCheckCast, kInvokeDirect, kInvokeInterface, kInvokeStatic, kInvokeVirtual, kMove, kNew, kReturn, kReturnObject, }; //////////////////////// Loading @@ -168,6 +169,13 @@ class Instruction { 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...}; } // A cast instruction. Basically, `(type)val` static inline Instruction Cast(Value val, Value type) { DCHECK(type.is_type()); return OpWithArgs(Op::kCheckCast, val, type); } // For method calls. template <typename... T> static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest, Loading Loading @@ -302,6 +310,7 @@ class MethodBuilder { void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode); void EncodeBranch(art::Instruction::Code op, const Instruction& instruction); void EncodeNew(const Instruction& instruction); void EncodeCast(const Instruction& instruction); // Low-level instruction format encoding. See // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of Loading startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import com.google.common.io.ByteStreams; import dalvik.system.InMemoryDexClassLoader; import dalvik.system.PathClassLoader; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import org.junit.Assert; Loading Loading @@ -151,4 +152,23 @@ public class DexBuilderTest { Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class); Assert.assertEquals("bc", method.invoke(null, "abc", 1)); } @Test public void castObjectToString() throws Exception { ClassLoader loader = loadDexFile("simple.dex"); Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); Method method = clazz.getMethod("castObjectToString", Object.class); Assert.assertEquals("abc", method.invoke(null, "abc")); boolean castFailed = false; try { method.invoke(null, 5); } catch (InvocationTargetException e) { if (e.getCause() instanceof ClassCastException) { castFailed = true; } else { throw e; } } Assert.assertTrue(castFailed); } } startop/view_compiler/dex_testcase_generator.cc +13 −0 Original line number Diff line number Diff line Loading @@ -269,6 +269,19 @@ void GenerateSimpleTestCases(const string& outdir) { method.Encode(); }(invokeVirtualReturnObject); // Make sure we can cast objects // String castObjectToString(Object o) { return (String)o; } MethodBuilder castObjectToString{cbuilder.CreateMethod( "castObjectToString", Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})}; [&](MethodBuilder& method) { const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor()); method.AddInstruction( Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index))); method.BuildReturn(Value::Parameter(0), /*is_object=*/true); method.Encode(); }(castObjectToString); slicer::MemView image{dex_file.CreateImage()}; std::ofstream out_file(outdir + "/simple.dex"); out_file.write(image.ptr<const char>(), image.size()); Loading Loading
startop/view_compiler/dex_builder.cc +17 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) { case Instruction::Op::kNew: out << "kNew"; return out; case Instruction::Op::kCheckCast: out << "kCheckCast"; return out; } } Loading Loading @@ -329,6 +332,8 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { return EncodeBranch(art::Instruction::IF_NEZ, instruction); case Instruction::Op::kNew: return EncodeNew(instruction); case Instruction::Op::kCheckCast: return EncodeCast(instruction); } } Loading Loading @@ -422,6 +427,18 @@ void MethodBuilder::EncodeNew(const Instruction& instruction) { Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value()); } void MethodBuilder::EncodeCast(const Instruction& instruction) { DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode()); DCHECK(instruction.dest().has_value()); DCHECK(instruction.dest()->is_variable()); DCHECK_EQ(1, instruction.args().size()); const Value& type = instruction.args()[0]; DCHECK_LT(RegisterValue(*instruction.dest()), 256); DCHECK(type.is_type()); Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); } size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); Loading
startop/view_compiler/dex_builder.h +17 −8 Original line number Diff line number Diff line Loading @@ -142,17 +142,18 @@ class Instruction { // The operation performed by this instruction. These are virtual instructions that do not // correspond exactly to DEX instructions. enum class Op { kReturn, kReturnObject, kMove, kInvokeVirtual, kInvokeDirect, kInvokeStatic, kInvokeInterface, kBindLabel, kBranchEqz, kBranchNEqz, kNew kCheckCast, kInvokeDirect, kInvokeInterface, kInvokeStatic, kInvokeVirtual, kMove, kNew, kReturn, kReturnObject, }; //////////////////////// Loading @@ -168,6 +169,13 @@ class Instruction { 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...}; } // A cast instruction. Basically, `(type)val` static inline Instruction Cast(Value val, Value type) { DCHECK(type.is_type()); return OpWithArgs(Op::kCheckCast, val, type); } // For method calls. template <typename... T> static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest, Loading Loading @@ -302,6 +310,7 @@ class MethodBuilder { void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode); void EncodeBranch(art::Instruction::Code op, const Instruction& instruction); void EncodeNew(const Instruction& instruction); void EncodeCast(const Instruction& instruction); // Low-level instruction format encoding. See // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of Loading
startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import com.google.common.io.ByteStreams; import dalvik.system.InMemoryDexClassLoader; import dalvik.system.PathClassLoader; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import org.junit.Assert; Loading Loading @@ -151,4 +152,23 @@ public class DexBuilderTest { Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class); Assert.assertEquals("bc", method.invoke(null, "abc", 1)); } @Test public void castObjectToString() throws Exception { ClassLoader loader = loadDexFile("simple.dex"); Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); Method method = clazz.getMethod("castObjectToString", Object.class); Assert.assertEquals("abc", method.invoke(null, "abc")); boolean castFailed = false; try { method.invoke(null, 5); } catch (InvocationTargetException e) { if (e.getCause() instanceof ClassCastException) { castFailed = true; } else { throw e; } } Assert.assertTrue(castFailed); } }
startop/view_compiler/dex_testcase_generator.cc +13 −0 Original line number Diff line number Diff line Loading @@ -269,6 +269,19 @@ void GenerateSimpleTestCases(const string& outdir) { method.Encode(); }(invokeVirtualReturnObject); // Make sure we can cast objects // String castObjectToString(Object o) { return (String)o; } MethodBuilder castObjectToString{cbuilder.CreateMethod( "castObjectToString", Prototype{string_type, TypeDescriptor::FromClassname("java.lang.Object")})}; [&](MethodBuilder& method) { const ir::Type* type_def = dex_file.GetOrAddType(string_type.descriptor()); method.AddInstruction( Instruction::Cast(Value::Parameter(0), Value::Type(type_def->orig_index))); method.BuildReturn(Value::Parameter(0), /*is_object=*/true); method.Encode(); }(castObjectToString); slicer::MemView image{dex_file.CreateImage()}; std::ofstream out_file(outdir + "/simple.dex"); out_file.write(image.ptr<const char>(), image.size()); Loading