Merge "ART: More cutouts for unstarted runtime"
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index b4732c8..c7f81ea 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -71,6 +71,17 @@
 // Separate objects into multiple bins to optimize dirty memory use.
 static constexpr bool kBinObjects = true;
 
+static void CheckNoDexObjectsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  Class* klass = obj->GetClass();
+  CHECK_NE(PrettyClass(klass), "com.android.dex.Dex");
+}
+
+static void CheckNoDexObjects() {
+  ScopedObjectAccess soa(Thread::Current());
+  Runtime::Current()->GetHeap()->VisitObjects(CheckNoDexObjectsCallback, nullptr);
+}
+
 bool ImageWriter::PrepareImageAddressSpace() {
   target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet());
   {
@@ -83,6 +94,16 @@
   gc::Heap* heap = Runtime::Current()->GetHeap();
   heap->CollectGarbage(false);  // Remove garbage.
 
+  // Dex caches must not have their dex fields set in the image. These are memory buffers of mapped
+  // dex files.
+  //
+  // We may open them in the unstarted-runtime code for class metadata. Their fields should all be
+  // reset in PruneNonImageClasses and the objects reclaimed in the GC. Make sure that's actually
+  // true.
+  if (kIsDebugBuild) {
+    CheckNoDexObjects();
+  }
+
   if (!AllocMemory()) {
     return false;
   }
@@ -644,6 +665,9 @@
         dex_cache->SetResolvedField(i, NULL);
       }
     }
+    // Clean the dex field. It might have been populated during the initialization phase, but
+    // contains data only valid during a real run.
+    dex_cache->SetFieldObject<false>(mirror::DexCache::DexOffset(), nullptr);
   }
 }
 
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 95f3357..356a438 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -97,8 +97,8 @@
   }
 }
 
-static void UnstartedClassForName(Thread* self, ShadowFrame* shadow_frame, JValue* result,
-                                  size_t arg_offset)
+static void UnstartedClassForName(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString();
   StackHandleScope<1> hs(self);
@@ -108,8 +108,8 @@
   CheckExceptionGenerateClassNotFound(self);
 }
 
-static void UnstartedClassForNameLong(Thread* self, ShadowFrame* shadow_frame, JValue* result,
-                                      size_t arg_offset)
+static void UnstartedClassForNameLong(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString();
   bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0;
@@ -123,8 +123,8 @@
   CheckExceptionGenerateClassNotFound(self);
 }
 
-static void UnstartedClassClassForName(Thread* self, ShadowFrame* shadow_frame, JValue* result,
-                                       size_t arg_offset)
+static void UnstartedClassClassForName(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset)->AsString();
   bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0;
@@ -138,8 +138,8 @@
   CheckExceptionGenerateClassNotFound(self);
 }
 
-static void UnstartedClassNewInstance(Thread* self, ShadowFrame* shadow_frame, JValue* result,
-                                      size_t arg_offset)
+static void UnstartedClassNewInstance(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   StackHandleScope<3> hs(self);  // Class, constructor, object.
   mirror::Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
@@ -189,8 +189,8 @@
   }
 }
 
-static void UnstartedClassGetDeclaredField(Thread* self, ShadowFrame* shadow_frame, JValue* result,
-                                           size_t arg_offset)
+static void UnstartedClassGetDeclaredField(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
   // going the reflective Dex way.
@@ -234,8 +234,8 @@
   result->SetL(field.Get());
 }
 
-static void UnstartedVmClassLoaderFindLoadedClass(Thread* self, ShadowFrame* shadow_frame,
-                                                  JValue* result, size_t arg_offset)
+static void UnstartedVmClassLoaderFindLoadedClass(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
   mirror::ClassLoader* class_loader =
@@ -263,8 +263,8 @@
   result->SetL(Runtime::Current()->GetClassLinker()->FindPrimitiveClass('V'));
 }
 
-static void UnstartedSystemArraycopy(Thread* self, ShadowFrame* shadow_frame,
-                                     JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
+static void UnstartedSystemArraycopy(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // Special case array copying without initializing System.
   mirror::Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType();
@@ -297,8 +297,8 @@
   }
 }
 
-static void UnstartedThreadLocalGet(Thread* self, ShadowFrame* shadow_frame, JValue* result,
-                                    size_t arg_offset ATTRIBUTE_UNUSED)
+static void UnstartedThreadLocalGet(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::string caller(PrettyMethod(shadow_frame->GetLink()->GetMethod()));
   bool ok = false;
@@ -347,8 +347,8 @@
   }
 }
 
-static void UnstartedMathCeil(Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame,
-                              JValue* result, size_t arg_offset) {
+static void UnstartedMathCeil(
+    Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   double in = shadow_frame->GetVRegDouble(arg_offset);
   double out;
   // Special cases:
@@ -362,27 +362,190 @@
   result->SetD(out);
 }
 
-static void UnstartedArtMethodGetMethodName(Thread* self, ShadowFrame* shadow_frame,
-                                            JValue* result, size_t arg_offset)
+static void UnstartedArtMethodGetMethodName(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::ArtMethod* method = shadow_frame->GetVRegReference(arg_offset)->AsArtMethod();
   result->SetL(method->GetNameAsString(self));
 }
 
-static void UnstartedObjectHashCode(Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame,
-                                    JValue* result, size_t arg_offset)
+static void UnstartedObjectHashCode(
+    Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
   result->SetI(obj->IdentityHashCode());
 }
 
-static void UnstartedDoubleDoubleToRawLongBits(Thread* self ATTRIBUTE_UNUSED,
-                                               ShadowFrame* shadow_frame, JValue* result,
-                                               size_t arg_offset) {
+static void UnstartedDoubleDoubleToRawLongBits(
+    Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   double in = shadow_frame->GetVRegDouble(arg_offset);
   result->SetJ(bit_cast<int64_t>(in));
 }
 
+static mirror::Object* GetDexFromDexCache(Thread* self, mirror::DexCache* dex_cache)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const DexFile* dex_file = dex_cache->GetDexFile();
+  if (dex_file == nullptr) {
+    return nullptr;
+  }
+
+  // Create the direct byte buffer.
+  JNIEnv* env = self->GetJniEnv();
+  DCHECK(env != nullptr);
+  void* address = const_cast<void*>(reinterpret_cast<const void*>(dex_file->Begin()));
+  jobject byte_buffer = env->NewDirectByteBuffer(address, dex_file->Size());
+  if (byte_buffer == nullptr) {
+    DCHECK(self->IsExceptionPending());
+    return nullptr;
+  }
+
+  jvalue args[1];
+  args[0].l = byte_buffer;
+  return self->DecodeJObject(
+      env->CallStaticObjectMethodA(WellKnownClasses::com_android_dex_Dex,
+                                   WellKnownClasses::com_android_dex_Dex_create,
+                                   args));
+}
+
+static void UnstartedDexCacheGetDexNative(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // We will create the Dex object, but the image writer will release it before creating the
+  // art file.
+  mirror::Object* src = shadow_frame->GetVRegReference(arg_offset);
+  bool have_dex = false;
+  if (src != nullptr) {
+    mirror::Object* dex = GetDexFromDexCache(self, reinterpret_cast<mirror::DexCache*>(src));
+    if (dex != nullptr) {
+      have_dex = true;
+      result->SetL(dex);
+    }
+  }
+  if (!have_dex) {
+    self->ClearException();
+    Runtime::Current()->AbortTransactionAndThrowInternalError(self, "Could not create Dex object");
+  }
+}
+
+static void UnstartedMemoryPeek(
+    Primitive::Type type, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+  int64_t address = shadow_frame->GetVRegLong(arg_offset);
+  // TODO: Check that this is in the heap somewhere. Otherwise we will segfault instead of
+  //       aborting the transaction.
+
+  switch (type) {
+    case Primitive::kPrimByte: {
+      result->SetB(*reinterpret_cast<int8_t*>(static_cast<intptr_t>(address)));
+      return;
+    }
+
+    case Primitive::kPrimShort: {
+      result->SetS(*reinterpret_cast<int16_t*>(static_cast<intptr_t>(address)));
+      return;
+    }
+
+    case Primitive::kPrimInt: {
+      result->SetI(*reinterpret_cast<int32_t*>(static_cast<intptr_t>(address)));
+      return;
+    }
+
+    case Primitive::kPrimLong: {
+      result->SetJ(*reinterpret_cast<int64_t*>(static_cast<intptr_t>(address)));
+      return;
+    }
+
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimVoid:
+    case Primitive::kPrimNot:
+      LOG(FATAL) << "Not in the Memory API: " << type;
+      UNREACHABLE();
+  }
+  LOG(FATAL) << "Should not reach here";
+  UNREACHABLE();
+}
+
+static void UnstartedMemoryPeekEntry(
+    Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  std::string name(PrettyMethod(shadow_frame->GetMethod()));
+  if (name == "byte libcore.io.Memory.peekByte(long)") {
+    UnstartedMemoryPeek(Primitive::kPrimByte, shadow_frame, result, arg_offset);
+  } else if (name == "short libcore.io.Memory.peekShortNative(long)") {
+    UnstartedMemoryPeek(Primitive::kPrimShort, shadow_frame, result, arg_offset);
+  } else if (name == "int libcore.io.Memory.peekIntNative(long)") {
+    UnstartedMemoryPeek(Primitive::kPrimInt, shadow_frame, result, arg_offset);
+  } else if (name == "long libcore.io.Memory.peekLongNative(long)") {
+    UnstartedMemoryPeek(Primitive::kPrimLong, shadow_frame, result, arg_offset);
+  } else {
+    LOG(FATAL) << "Unsupported Memory.peek entry: " << name;
+    UNREACHABLE();
+  }
+}
+
+static void UnstartedMemoryPeekArray(
+    Primitive::Type type, Thread* self, ShadowFrame* shadow_frame, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  int64_t address_long = shadow_frame->GetVRegLong(arg_offset);
+  mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset + 2);
+  if (obj == nullptr) {
+    Runtime::Current()->AbortTransactionAndThrowInternalError(self, "Null pointer in peekArray");
+    return;
+  }
+  mirror::Array* array = obj->AsArray();
+
+  int offset = shadow_frame->GetVReg(arg_offset + 3);
+  int count = shadow_frame->GetVReg(arg_offset + 4);
+  if (offset < 0 || offset + count > array->GetLength()) {
+    std::string error_msg(StringPrintf("Array out of bounds in peekArray: %d/%d vs %d",
+                                       offset, count, array->GetLength()));
+    Runtime::Current()->AbortTransactionAndThrowInternalError(self, error_msg.c_str());
+    return;
+  }
+
+  switch (type) {
+    case Primitive::kPrimByte: {
+      int8_t* address = reinterpret_cast<int8_t*>(static_cast<intptr_t>(address_long));
+      mirror::ByteArray* byte_array = array->AsByteArray();
+      for (int32_t i = 0; i < count; ++i, ++address) {
+        byte_array->SetWithoutChecks<true>(i + offset, *address);
+      }
+      return;
+    }
+
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      LOG(FATAL) << "Type unimplemented for Memory Array API, should not reach here: " << type;
+      UNREACHABLE();
+
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+    case Primitive::kPrimVoid:
+    case Primitive::kPrimNot:
+      LOG(FATAL) << "Not in the Memory API: " << type;
+      UNREACHABLE();
+  }
+  LOG(FATAL) << "Should not reach here";
+  UNREACHABLE();
+}
+
+static void UnstartedMemoryPeekArrayEntry(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  std::string name(PrettyMethod(shadow_frame->GetMethod()));
+  if (name == "void libcore.io.Memory.peekByteArray(long, byte[], int, int)") {
+    UnstartedMemoryPeekArray(Primitive::kPrimByte, self, shadow_frame, arg_offset);
+  } else {
+    LOG(FATAL) << "Unsupported Memory.peekArray entry: " << name;
+    UNREACHABLE();
+  }
+}
+
 static void UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self,
                                                   mirror::ArtMethod* method ATTRIBUTE_UNUSED,
                                                   mirror::Object* receiver ATTRIBUTE_UNUSED,
@@ -621,10 +784,10 @@
   result->SetI(Primitive::ComponentSize(primitive_type));
 }
 
-typedef void(*InvokeHandler)(Thread* self, ShadowFrame* shadow_frame, JValue* result,
+typedef void (*InvokeHandler)(Thread* self, ShadowFrame* shadow_frame, JValue* result,
     size_t arg_size);
 
-typedef void(*JNIHandler)(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
+typedef void (*JNIHandler)(Thread* self, mirror::ArtMethod* method, mirror::Object* receiver,
     uint32_t* args, JValue* result);
 
 static bool tables_initialized_ = false;
@@ -668,6 +831,18 @@
           &UnstartedMathCeil },
       { "java.lang.Object java.lang.ThreadLocal.get()",
           &UnstartedThreadLocalGet },
+      { "com.android.dex.Dex java.lang.DexCache.getDexNative()",
+          &UnstartedDexCacheGetDexNative },
+      { "byte libcore.io.Memory.peekByte(long)",
+          &UnstartedMemoryPeekEntry },
+      { "short libcore.io.Memory.peekShortNative(long)",
+          &UnstartedMemoryPeekEntry },
+      { "int libcore.io.Memory.peekIntNative(long)",
+          &UnstartedMemoryPeekEntry },
+      { "long libcore.io.Memory.peekLongNative(long)",
+          &UnstartedMemoryPeekEntry },
+      { "void libcore.io.Memory.peekByteArray(long, byte[], int, int)",
+          &UnstartedMemoryPeekArrayEntry },
   };
 
   for (auto& def : defs) {
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 3c947ab..c548c03 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -59,6 +59,10 @@
     return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_));
   }
 
+  static MemberOffset DexOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(DexCache, dex_);
+  }
+
   static MemberOffset StringsOffset() {
     return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_);
   }