diff options
author | Eric Holk <eholk@google.com> | 2018-12-17 13:35:34 -0800 |
---|---|---|
committer | Eric Holk <eholk@google.com> | 2018-12-18 16:47:05 +0000 |
commit | 44d8cdfb06233b3a6322c00b9b4650fd31a56a0f (patch) | |
tree | c305de276a0a9fa7ef089e368bb1647d9231ccad /startop/view_compiler | |
parent | 610673690c4a91afbc8f6d81e8aa80667d683316 (diff) |
[view-compiler] DexBuilder: Add check-cast instruction
Bug: 111895153
Test: atest
Change-Id: I767e56713fab6beaa6970e58c4fc4d3560cc1304
Diffstat (limited to 'startop/view_compiler')
-rw-r--r-- | startop/view_compiler/dex_builder.cc | 17 | ||||
-rw-r--r-- | startop/view_compiler/dex_builder.h | 25 | ||||
-rw-r--r-- | startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java | 20 | ||||
-rw-r--r-- | startop/view_compiler/dex_testcase_generator.cc | 13 |
4 files changed, 67 insertions, 8 deletions
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc index 94879d0bbcc4..a78f7d53d135 100644 --- a/startop/view_compiler/dex_builder.cc +++ b/startop/view_compiler/dex_builder.cc @@ -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; } } @@ -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); } } @@ -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(); diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h index 45596acfdc44..06059c8c6e56 100644 --- a/startop/view_compiler/dex_builder.h +++ b/startop/view_compiler/dex_builder.h @@ -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, }; //////////////////////// @@ -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, @@ -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 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 1508ad9ee56b..42d4161ee81e 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 @@ -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; @@ -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); + } } diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc index 2781aa55d1df..f62ec5dde85e 100644 --- a/startop/view_compiler/dex_testcase_generator.cc +++ b/startop/view_compiler/dex_testcase_generator.cc @@ -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()); |