Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c2e3a18 authored by Eric Holk's avatar Eric Holk Committed by android-build-merger
Browse files

Merge changes I12b38fa5,Ia11195b1

am: 8aa2b1df

Change-Id: I2ac308ac94fd49298641c1230e731c1406913281
parents a4a7c1a0 8aa2b1df
Loading
Loading
Loading
Loading
+57 −3
Original line number Diff line number Diff line
@@ -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;
  }
}

@@ -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());
@@ -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);
  }
}

@@ -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) {
@@ -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],
@@ -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();
+43 −22
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ class Instruction {
    kBranchEqz,
    kBranchNEqz,
    kCheckCast,
    kGetStaticField,
    kInvokeDirect,
    kInvokeInterface,
    kInvokeStatic,
@@ -162,6 +163,7 @@ class Instruction {
    kNew,
    kReturn,
    kReturnObject,
    kSetStaticField
  };

  ////////////////////////
@@ -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`
@@ -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_;
@@ -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
@@ -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,
@@ -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>
+1 −0
Original line number Diff line number Diff line
@@ -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"],
+21 −2
Original line number Diff line number Diff line
@@ -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() {}
@@ -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);
  }
}
+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