Merge changes I12b38fa5,Ia11195b1
am: 8aa2b1dffd
Change-Id: I2ac308ac94fd49298641c1230e731c1406913281
This commit is contained in:
@@ -102,6 +102,12 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
|
|||||||
case Instruction::Op::kCheckCast:
|
case Instruction::Op::kCheckCast:
|
||||||
out << "kCheckCast";
|
out << "kCheckCast";
|
||||||
return out;
|
return out;
|
||||||
|
case Instruction::Op::kGetStaticField:
|
||||||
|
out << "kGetStaticField";
|
||||||
|
return out;
|
||||||
|
case Instruction::Op::kSetStaticField:
|
||||||
|
out << "kSetStaticField";
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +235,22 @@ ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) {
|
|||||||
return type;
|
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 {
|
ir::Proto* Prototype::Encode(DexBuilder* dex) const {
|
||||||
auto* proto = dex->Alloc<ir::Proto>();
|
auto* proto = dex->Alloc<ir::Proto>();
|
||||||
proto->shorty = dex->GetOrAddString(Shorty());
|
proto->shorty = dex->GetOrAddString(Shorty());
|
||||||
@@ -360,6 +382,9 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
|
|||||||
return EncodeNew(instruction);
|
return EncodeNew(instruction);
|
||||||
case Instruction::Op::kCheckCast:
|
case Instruction::Op::kCheckCast:
|
||||||
return EncodeCast(instruction);
|
return EncodeCast(instruction);
|
||||||
|
case Instruction::Op::kGetStaticField:
|
||||||
|
case Instruction::Op::kSetStaticField:
|
||||||
|
return EncodeStaticFieldOp(instruction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,7 +453,7 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct
|
|||||||
// first move all the arguments into contiguous temporary registers.
|
// first move all the arguments into contiguous temporary registers.
|
||||||
std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
|
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());
|
CHECK(prototype.has_value());
|
||||||
|
|
||||||
for (size_t i = 0; i < instruction.args().size(); ++i) {
|
for (size_t i = 0; i < instruction.args().size(); ++i) {
|
||||||
@@ -452,12 +477,12 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct
|
|||||||
|
|
||||||
Encode3rc(InvokeToInvokeRange(opcode),
|
Encode3rc(InvokeToInvokeRange(opcode),
|
||||||
instruction.args().size(),
|
instruction.args().size(),
|
||||||
instruction.method_id(),
|
instruction.index_argument(),
|
||||||
RegisterValue(scratch[0]));
|
RegisterValue(scratch[0]));
|
||||||
} else {
|
} else {
|
||||||
Encode35c(opcode,
|
Encode35c(opcode,
|
||||||
instruction.args().size(),
|
instruction.args().size(),
|
||||||
instruction.method_id(),
|
instruction.index_argument(),
|
||||||
arguments[0],
|
arguments[0],
|
||||||
arguments[1],
|
arguments[1],
|
||||||
arguments[2],
|
arguments[2],
|
||||||
@@ -514,6 +539,35 @@ void MethodBuilder::EncodeCast(const Instruction& instruction) {
|
|||||||
Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
|
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 {
|
size_t MethodBuilder::RegisterValue(const Value& value) const {
|
||||||
if (value.is_register()) {
|
if (value.is_register()) {
|
||||||
return value.value();
|
return value.value();
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class Instruction {
|
|||||||
kBranchEqz,
|
kBranchEqz,
|
||||||
kBranchNEqz,
|
kBranchNEqz,
|
||||||
kCheckCast,
|
kCheckCast,
|
||||||
|
kGetStaticField,
|
||||||
kInvokeDirect,
|
kInvokeDirect,
|
||||||
kInvokeInterface,
|
kInvokeInterface,
|
||||||
kInvokeStatic,
|
kInvokeStatic,
|
||||||
@@ -162,6 +163,7 @@ class Instruction {
|
|||||||
kNew,
|
kNew,
|
||||||
kReturn,
|
kReturn,
|
||||||
kReturnObject,
|
kReturnObject,
|
||||||
|
kSetStaticField
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
@@ -170,12 +172,12 @@ class Instruction {
|
|||||||
|
|
||||||
// For instructions with no return value and no arguments.
|
// For instructions with no return value and no arguments.
|
||||||
static inline Instruction OpNoArgs(Op opcode) {
|
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.
|
// For most instructions, which take some number of arguments and have an optional return value.
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
|
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`
|
// A cast instruction. Basically, `(type)val`
|
||||||
@@ -186,77 +188,87 @@ class Instruction {
|
|||||||
|
|
||||||
// For method calls.
|
// For method calls.
|
||||||
template <typename... T>
|
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) {
|
Value this_arg, T... args) {
|
||||||
return Instruction{
|
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
|
// Returns an object
|
||||||
template <typename... T>
|
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) {
|
Value this_arg, T... args) {
|
||||||
return Instruction{
|
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).
|
// For direct calls (basically, constructors).
|
||||||
template <typename... T>
|
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) {
|
Value this_arg, T... args) {
|
||||||
return Instruction{
|
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
|
// Returns an object
|
||||||
template <typename... T>
|
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) {
|
Value this_arg, T... args) {
|
||||||
return Instruction{
|
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.
|
// For static calls.
|
||||||
template <typename... T>
|
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) {
|
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
|
// Returns an object
|
||||||
template <typename... T>
|
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) {
|
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.
|
// For static calls.
|
||||||
template <typename... T>
|
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) {
|
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 //
|
// Accessors //
|
||||||
///////////////
|
///////////////
|
||||||
|
|
||||||
Op opcode() const { return opcode_; }
|
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_; }
|
bool result_is_object() const { return result_is_object_; }
|
||||||
const std::optional<const Value>& dest() const { return dest_; }
|
const std::optional<const Value>& dest() const { return dest_; }
|
||||||
const std::vector<const Value>& args() const { return args_; }
|
const std::vector<const Value>& args() const { return args_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
|
inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
|
||||||
: opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {}
|
: opcode_{opcode}, index_argument_{index_argument}, result_is_object_{false}, dest_{dest}, args_{} {}
|
||||||
|
|
||||||
template <typename... T>
|
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)
|
std::optional<const Value> dest, T... args)
|
||||||
: opcode_{opcode},
|
: opcode_{opcode},
|
||||||
method_id_{method_id},
|
index_argument_{index_argument},
|
||||||
result_is_object_{result_is_object},
|
result_is_object_{result_is_object},
|
||||||
dest_{dest},
|
dest_{dest},
|
||||||
args_{args...} {}
|
args_{args...} {}
|
||||||
|
|
||||||
const Op opcode_;
|
const Op opcode_;
|
||||||
// The index of the method to invoke, for kInvokeVirtual and similar opcodes.
|
// 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 bool result_is_object_;
|
||||||
const std::optional<const Value> dest_;
|
const std::optional<const Value> dest_;
|
||||||
const std::vector<const Value> args_;
|
const std::vector<const Value> args_;
|
||||||
@@ -319,6 +331,7 @@ class MethodBuilder {
|
|||||||
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
|
void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
|
||||||
void EncodeNew(const Instruction& instruction);
|
void EncodeNew(const Instruction& instruction);
|
||||||
void EncodeCast(const Instruction& instruction);
|
void EncodeCast(const Instruction& instruction);
|
||||||
|
void EncodeStaticFieldOp(const Instruction& instruction);
|
||||||
|
|
||||||
// Low-level instruction format encoding. See
|
// Low-level instruction format encoding. See
|
||||||
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
|
// https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
|
||||||
@@ -481,6 +494,11 @@ class DexBuilder {
|
|||||||
// See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
|
// See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
|
||||||
// imported classes.
|
// imported classes.
|
||||||
ir::Type* GetOrAddType(const std::string& descriptor);
|
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.
|
// 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,
|
const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
|
||||||
@@ -526,6 +544,9 @@ class DexBuilder {
|
|||||||
|
|
||||||
// Keep track of already-encoded protos.
|
// Keep track of already-encoded protos.
|
||||||
std::map<Prototype, ir::Proto*> proto_map_;
|
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>
|
template <typename... T>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ android_test {
|
|||||||
srcs: [
|
srcs: [
|
||||||
"src/android/startop/test/DexBuilderTest.java",
|
"src/android/startop/test/DexBuilderTest.java",
|
||||||
"src/android/startop/test/LayoutCompilerTest.java",
|
"src/android/startop/test/LayoutCompilerTest.java",
|
||||||
|
"src/android/startop/test/TestClass.java",
|
||||||
],
|
],
|
||||||
sdk_version: "current",
|
sdk_version: "current",
|
||||||
data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
|
data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ import java.lang.reflect.Method;
|
|||||||
|
|
||||||
// Adding tests here requires changes in several other places. See README.md in
|
// Adding tests here requires changes in several other places. See README.md in
|
||||||
// the view_compiler directory for more information.
|
// the view_compiler directory for more information.
|
||||||
public class DexBuilderTest {
|
public final class DexBuilderTest {
|
||||||
static ClassLoader loadDexFile(String filename) throws Exception {
|
static ClassLoader loadDexFile(String filename) throws Exception {
|
||||||
return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
|
return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
|
||||||
ClassLoader.getSystemClassLoader());
|
DexBuilderTest.class.getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hello() {}
|
public void hello() {}
|
||||||
@@ -167,4 +167,23 @@ public class DexBuilderTest {
|
|||||||
}
|
}
|
||||||
Assert.assertTrue(castFailed);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -282,6 +282,37 @@ void GenerateSimpleTestCases(const string& outdir) {
|
|||||||
method.Encode();
|
method.Encode();
|
||||||
}(castObjectToString);
|
}(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);
|
||||||
|
|
||||||
|
// Set a static field
|
||||||
|
// void setStaticField() { TestClass.staticInteger = 7; }
|
||||||
|
MethodBuilder setStaticField{
|
||||||
|
cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
|
||||||
|
[&](MethodBuilder& method) {
|
||||||
|
const ir::FieldDecl* field =
|
||||||
|
dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
|
||||||
|
"staticInteger",
|
||||||
|
TypeDescriptor::Int());
|
||||||
|
Value number{method.MakeRegister()};
|
||||||
|
method.BuildConst4(number, 7);
|
||||||
|
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
|
||||||
|
method.BuildReturn();
|
||||||
|
method.Encode();
|
||||||
|
}(setStaticField);
|
||||||
|
|
||||||
slicer::MemView image{dex_file.CreateImage()};
|
slicer::MemView image{dex_file.CreateImage()};
|
||||||
std::ofstream out_file(outdir + "/simple.dex");
|
std::ofstream out_file(outdir + "/simple.dex");
|
||||||
out_file.write(image.ptr<const char>(), image.size());
|
out_file.write(image.ptr<const char>(), image.size());
|
||||||
|
|||||||
Reference in New Issue
Block a user