Merge "[view-compiler] DexBuilder: Add check-cast instruction" am: bce4bd1745
am: 758823ae17
Change-Id: I12ce815eb4f83c6abc60ad7402c754e3c69cb0d9
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user