Support for loading classes from DEX files stored in a ByteBuffer.

Bug: http://b/26878838
Test: test-art-target-gtest-dex_file_test

Change-Id: I68845c35784386bde6270102f0079d4f07bc4d03
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a8fb27a..2547359 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -135,6 +135,7 @@
         "native_bridge_art_interface.cc",
         "native_stack_dump.cc",
         "native/dalvik_system_DexFile.cc",
+        "native/dalvik_system_InMemoryDexClassLoader_DexData.cc",
         "native/dalvik_system_VMDebug.cc",
         "native/dalvik_system_VMRuntime.cc",
         "native/dalvik_system_VMStack.cc",
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 90c678c..53838fd 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -222,6 +222,10 @@
                                                        nullptr,
                                                        oat_dex_file,
                                                        error_msg);
+  if (dex_file == nullptr) {
+    return nullptr;
+  }
+
   if (verify && !DexFileVerifier::Verify(dex_file.get(),
                                          dex_file->Begin(),
                                          dex_file->Size(),
@@ -230,7 +234,32 @@
                                          error_msg)) {
     return nullptr;
   }
+  return dex_file;
+}
 
+std::unique_ptr<const DexFile> DexFile::Open(const std::string& location,
+                                             uint32_t location_checksum,
+                                             std::unique_ptr<MemMap> mem_map,
+                                             bool verify,
+                                             bool verify_checksum,
+                                             std::string* error_msg) {
+  ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
+  std::unique_ptr<const DexFile> dex_file = OpenMemory(location,
+                                                       location_checksum,
+                                                       std::move(mem_map),
+                                                       error_msg);
+  if (dex_file == nullptr) {
+    return nullptr;
+  }
+
+  if (verify && !DexFileVerifier::Verify(dex_file.get(),
+                                         dex_file->Begin(),
+                                         dex_file->Size(),
+                                         location.c_str(),
+                                         verify_checksum,
+                                         error_msg)) {
+    return nullptr;
+  }
   return dex_file;
 }
 
@@ -263,7 +292,7 @@
                               /*low_4gb*/false,
                               location,
                               error_msg));
-    if (map.get() == nullptr) {
+    if (map == nullptr) {
       DCHECK(!error_msg->empty());
       return nullptr;
     }
@@ -277,7 +306,9 @@
 
   const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
 
-  std::unique_ptr<const DexFile> dex_file(OpenMemory(location, dex_header->checksum_, map.release(),
+  std::unique_ptr<const DexFile> dex_file(OpenMemory(location,
+                                                     dex_header->checksum_,
+                                                     std::move(map),
                                                      error_msg));
   if (dex_file.get() == nullptr) {
     *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location,
@@ -314,13 +345,13 @@
 
 std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location,
                                                    uint32_t location_checksum,
-                                                   MemMap* mem_map,
+                                                   std::unique_ptr<MemMap> mem_map,
                                                    std::string* error_msg) {
   return OpenMemory(mem_map->Begin(),
                     mem_map->Size(),
                     location,
                     location_checksum,
-                    mem_map,
+                    std::move(mem_map),
                     nullptr,
                     error_msg);
 }
@@ -350,9 +381,11 @@
     *error_code = ZipOpenErrorCode::kExtractToMemoryError;
     return nullptr;
   }
-  std::unique_ptr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(),
-                                               error_msg));
-  if (dex_file.get() == nullptr) {
+  std::unique_ptr<const DexFile> dex_file(OpenMemory(location,
+                                                     zip_entry->GetCrc32(),
+                                                     std::move(map),
+                                                     error_msg));
+  if (dex_file == nullptr) {
     *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
                               error_msg->c_str());
     *error_code = ZipOpenErrorCode::kDexFileError;
@@ -437,14 +470,14 @@
                                                    size_t size,
                                                    const std::string& location,
                                                    uint32_t location_checksum,
-                                                   MemMap* mem_map,
+                                                   std::unique_ptr<MemMap> mem_map,
                                                    const OatDexFile* oat_dex_file,
                                                    std::string* error_msg) {
   DCHECK(base != nullptr);
   DCHECK_NE(size, 0U);
   CHECK_ALIGNED(base, 4);  // various dex file structures must be word aligned
   std::unique_ptr<DexFile> dex_file(
-      new DexFile(base, size, location, location_checksum, mem_map, oat_dex_file));
+      new DexFile(base, size, location, location_checksum, std::move(mem_map), oat_dex_file));
   if (!dex_file->Init(error_msg)) {
     dex_file.reset();
   }
@@ -454,13 +487,13 @@
 DexFile::DexFile(const uint8_t* base, size_t size,
                  const std::string& location,
                  uint32_t location_checksum,
-                 MemMap* mem_map,
+                 std::unique_ptr<MemMap> mem_map,
                  const OatDexFile* oat_dex_file)
     : begin_(base),
       size_(size),
       location_(location),
       location_checksum_(location_checksum),
-      mem_map_(mem_map),
+      mem_map_(std::move(mem_map)),
       header_(reinterpret_cast<const Header*>(base)),
       string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
       type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 767f921..1200416 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -422,10 +422,6 @@
                    std::string* error_msg,
                    std::vector<std::unique_ptr<const DexFile>>* dex_files);
 
-  // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry.
-  // If this function returns false, Open will not succeed. The inverse is not true, however.
-  static bool MaybeDex(const char* filename);
-
   // Opens .dex file, backed by existing memory
   static std::unique_ptr<const DexFile> Open(const uint8_t* base, size_t size,
                                              const std::string& location,
@@ -435,6 +431,18 @@
                                              bool verify_checksum,
                                              std::string* error_msg);
 
+  // Opens .dex file that has been memory-mapped by the caller.
+  static std::unique_ptr<const DexFile> Open(const std::string& location,
+                                             uint32_t location_checkum,
+                                             std::unique_ptr<MemMap> mem_map,
+                                             bool verify,
+                                             bool verify_checksum,
+                                             std::string* error_msg);
+
+  // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry.
+  // If this function returns false, Open will not succeed. The inverse is not true, however.
+  static bool MaybeDex(const char* filename);
+
   // Open all classesXXX.dex files from a zip archive.
   static bool OpenFromZip(const ZipArchive& zip_archive,
                           const std::string& location,
@@ -1175,7 +1183,7 @@
   // Opens a .dex file at the given address backed by a MemMap
   static std::unique_ptr<const DexFile> OpenMemory(const std::string& location,
                                                    uint32_t location_checksum,
-                                                   MemMap* mem_map,
+                                                   std::unique_ptr<MemMap> mem_map,
                                                    std::string* error_msg);
 
   // Opens a .dex file at the given address, optionally backed by a MemMap
@@ -1183,14 +1191,14 @@
                                                    size_t size,
                                                    const std::string& location,
                                                    uint32_t location_checksum,
-                                                   MemMap* mem_map,
+                                                   std::unique_ptr<MemMap> mem_map,
                                                    const OatDexFile* oat_dex_file,
                                                    std::string* error_msg);
 
   DexFile(const uint8_t* base, size_t size,
           const std::string& location,
           uint32_t location_checksum,
-          MemMap* mem_map,
+          std::unique_ptr<MemMap> mem_map,
           const OatDexFile* oat_dex_file);
 
   // Top-level initializer that calls other Init methods.
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 2704d8a..2328e3d 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -22,6 +22,7 @@
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
 #include "dex_file-inl.h"
+#include "mem_map.h"
 #include "os.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
@@ -61,7 +62,7 @@
   255, 255, 255, 255
 };
 
-static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+static inline std::vector<uint8_t> DecodeBase64(const char* src) {
   std::vector<uint8_t> tmp;
   uint32_t t = 0, y = 0;
   int g = 3;
@@ -73,13 +74,11 @@
       c = 0;
       // prevent g < 0 which would potentially allow an overflow later
       if (--g < 0) {
-        *dst_size = 0;
-        return nullptr;
+        return std::vector<uint8_t>();
       }
     } else if (g != 3) {
       // we only allow = to be at the end
-      *dst_size = 0;
-      return nullptr;
+        return std::vector<uint8_t>();
     }
     t = (t << 6) | c;
     if (++y == 4) {
@@ -94,17 +93,9 @@
     }
   }
   if (y != 0) {
-    *dst_size = 0;
-    return nullptr;
+    return std::vector<uint8_t>();
   }
-  std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
-  if (dst_size != nullptr) {
-    *dst_size = tmp.size();
-  } else {
-    *dst_size = 0;
-  }
-  std::copy(tmp.begin(), tmp.end(), dst.get());
-  return dst.release();
+  return tmp;
 }
 
 // Although this is the same content logically as the Nested test dex,
@@ -175,14 +166,13 @@
 static void DecodeAndWriteDexFile(const char* base64, const char* location) {
   // decode base64
   CHECK(base64 != nullptr);
-  size_t length;
-  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length));
-  CHECK(dex_bytes.get() != nullptr);
+  std::vector<uint8_t> dex_bytes = DecodeBase64(base64);
+  CHECK_NE(dex_bytes.size(), 0u);
 
   // write to provided file
   std::unique_ptr<File> file(OS::CreateEmptyFile(location));
   CHECK(file.get() != nullptr);
-  if (!file->WriteFully(dex_bytes.get(), length)) {
+  if (!file->WriteFully(dex_bytes.data(), dex_bytes.size())) {
     PLOG(FATAL) << "Failed to write base64 as dex file";
   }
   if (file->FlushCloseOrErase() != 0) {
@@ -208,9 +198,67 @@
   return dex_file;
 }
 
+static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64,
+                                                                const char* location,
+                                                                uint32_t location_checksum) {
+  CHECK(base64 != nullptr);
+  std::vector<uint8_t> dex_bytes = DecodeBase64(base64);
+  CHECK_NE(dex_bytes.size(), 0u);
+
+  std::string error_message;
+  std::unique_ptr<MemMap> region(MemMap::MapAnonymous("test-region",
+                                                      nullptr,
+                                                      dex_bytes.size(),
+                                                      PROT_READ | PROT_WRITE,
+                                                      /* low_4gb */ false,
+                                                      /* reuse */ false,
+                                                      &error_message));
+  memcpy(region->Begin(), dex_bytes.data(), dex_bytes.size());
+  std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
+                                                        location_checksum,
+                                                        std::move(region),
+                                                        /* verify */ true,
+                                                        /* verify_checksum */ true,
+                                                        &error_message));
+  CHECK(dex_file != nullptr) << error_message;
+  return dex_file;
+}
+
 TEST_F(DexFileTest, Header) {
   ScratchFile tmp;
   std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str()));
+  ASSERT_TRUE(raw != nullptr);
+
+  const DexFile::Header& header = raw->GetHeader();
+  // TODO: header.magic_
+  EXPECT_EQ(0x00d87910U, header.checksum_);
+  // TODO: header.signature_
+  EXPECT_EQ(904U, header.file_size_);
+  EXPECT_EQ(112U, header.header_size_);
+  EXPECT_EQ(0U, header.link_size_);
+  EXPECT_EQ(0U, header.link_off_);
+  EXPECT_EQ(15U, header.string_ids_size_);
+  EXPECT_EQ(112U, header.string_ids_off_);
+  EXPECT_EQ(7U, header.type_ids_size_);
+  EXPECT_EQ(172U, header.type_ids_off_);
+  EXPECT_EQ(2U, header.proto_ids_size_);
+  EXPECT_EQ(200U, header.proto_ids_off_);
+  EXPECT_EQ(1U, header.field_ids_size_);
+  EXPECT_EQ(224U, header.field_ids_off_);
+  EXPECT_EQ(3U, header.method_ids_size_);
+  EXPECT_EQ(232U, header.method_ids_off_);
+  EXPECT_EQ(2U, header.class_defs_size_);
+  EXPECT_EQ(256U, header.class_defs_off_);
+  EXPECT_EQ(584U, header.data_size_);
+  EXPECT_EQ(320U, header.data_off_);
+
+  EXPECT_EQ(header.checksum_, raw->GetLocationChecksum());
+}
+
+TEST_F(DexFileTest, HeaderInMemory) {
+  ScratchFile tmp;
+  std::unique_ptr<const DexFile> raw =
+      OpenDexFileInMemoryBase64(kRawDex, tmp.GetFilename().c_str(), 0x00d87910U);
   ASSERT_TRUE(raw.get() != nullptr);
 
   const DexFile::Header& header = raw->GetHeader();
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
new file mode 100644
index 0000000..08bf978
--- /dev/null
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "dalvik_system_InMemoryDexClassLoader_DexData.h"
+
+#include <sstream>
+
+#include "class_linker.h"
+#include "common_throws.h"
+#include "dex_file.h"
+#include "jni_internal.h"
+#include "mem_map.h"
+#include "mirror/class_loader.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
+
+namespace art {
+
+static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
+  if (end <= start) {
+    ScopedObjectAccess soa(env);
+    ThrowWrappedIOException("Bad range");
+    return nullptr;
+  }
+
+  std::string error_message;
+  size_t length = static_cast<size_t>(end - start);
+  std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
+                                                           nullptr,
+                                                           length,
+                                                           PROT_READ | PROT_WRITE,
+                                                           /* low_4gb */ false,
+                                                           /* reuse */ false,
+                                                           &error_message));
+  if (dex_mem_map == nullptr) {
+    ScopedObjectAccess soa(env);
+    ThrowWrappedIOException("%s", error_message.c_str());
+  }
+  return dex_mem_map;
+}
+
+static jlong DexFileToCookie(const DexFile* dex_file) {
+  return reinterpret_cast<jlong>(dex_file);
+}
+
+static const DexFile* CookieToDexFile(jlong cookie) {
+  return reinterpret_cast<const DexFile*>(cookie);
+}
+
+static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
+  std::string location = StringPrintf("InMemoryDexClassLoader_DexData@%p-%p",
+                                      dex_mem_map->Begin(),
+                                      dex_mem_map->End());
+  std::string error_message;
+  std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
+                                                        0,
+                                                        std::move(dex_mem_map),
+                                                        /* verify */ true,
+                                                        /* verify_location */ true,
+                                                        &error_message));
+  if (dex_file == nullptr) {
+    ScopedObjectAccess soa(env);
+    ThrowWrappedIOException("%s", error_message.c_str());
+    return nullptr;
+  }
+
+  if (!dex_file->DisableWrite()) {
+    ScopedObjectAccess soa(env);
+    ThrowWrappedIOException("Failed to make dex file read-only");
+    return nullptr;
+  }
+
+  return dex_file.release();
+}
+
+static jlong InMemoryDexClassLoader_DexData_initializeWithDirectBuffer(
+    JNIEnv* env, jclass, jobject buffer, jint start, jint end) {
+  uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
+  if (base_address == nullptr) {
+    ScopedObjectAccess soa(env);
+    ThrowWrappedIOException("dexFileBuffer not direct");
+    return 0;
+  }
+
+  std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+  if (dex_mem_map == nullptr) {
+    DCHECK(Thread::Current()->IsExceptionPending());
+    return 0;
+  }
+
+  size_t length = static_cast<size_t>(end - start);
+  memcpy(dex_mem_map->Begin(), base_address, length);
+  return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
+}
+
+static jlong InMemoryDexClassLoader_DexData_initializeWithArray(
+    JNIEnv* env, jclass, jbyteArray buffer, jint start, jint end) {
+  std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+  if (dex_mem_map == nullptr) {
+    DCHECK(Thread::Current()->IsExceptionPending());
+    return 0;
+  }
+
+  auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
+  env->GetByteArrayRegion(buffer, start, end - start, destination);
+  return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
+}
+
+static void InMemoryDexClassLoader_DexData_uninitialize(JNIEnv* env, jclass, jlong cookie) {
+  const DexFile* dex_file = CookieToDexFile(cookie);
+  if (kIsDebugBuild) {
+    ScopedObjectAccess soa(env);
+    ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+    CHECK(class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr);
+  }
+  delete dex_file;
+}
+
+static jclass InMemoryDexClassLoader_DexData_findClass(
+    JNIEnv* env, jobject dexData, jstring name, jobject loader, jlong cookie) {
+  ScopedUtfChars scoped_class_name(env, name);
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+
+  const char* class_name = scoped_class_name.c_str();
+  const std::string descriptor(DotToDescriptor(class_name));
+  const char* class_descriptor = descriptor.c_str();
+  const size_t hash = ComputeModifiedUtf8Hash(class_descriptor);
+  const DexFile* dex_file = CookieToDexFile(cookie);
+  const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(class_descriptor, hash);
+  if (dex_class_def != nullptr) {
+    ScopedObjectAccess soa(env);
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    StackHandleScope<1> handle_scope(soa.Self());
+    Handle<mirror::ClassLoader> class_loader(
+        handle_scope.NewHandle(soa.Decode<mirror::ClassLoader*>(loader)));
+    class_linker->RegisterDexFile(*dex_file, class_loader.Get());
+    mirror::Class* result = class_linker->DefineClass(
+        soa.Self(), class_descriptor, hash, class_loader, *dex_file, *dex_class_def);
+    if (result != nullptr) {
+      // Ensure the class table has a strong reference to the
+      // InMemoryClassLoader/DexData instance now that a class has
+      // been loaded.
+      class_linker->InsertDexFileInToClassLoader(
+          soa.Decode<mirror::Object*>(dexData), class_loader.Get());
+      return soa.AddLocalReference<jclass>(result);
+    }
+  }
+
+  VLOG(class_linker) << "Failed to find dex_class_def " << class_name;
+  return nullptr;
+}
+
+static JNINativeMethod gMethods[] = {
+  NATIVE_METHOD(InMemoryDexClassLoader_DexData,
+                initializeWithDirectBuffer,
+                "(Ljava/nio/ByteBuffer;II)J"),
+  NATIVE_METHOD(InMemoryDexClassLoader_DexData, initializeWithArray, "([BII)J"),
+  NATIVE_METHOD(InMemoryDexClassLoader_DexData, uninitialize, "(J)V"),
+  NATIVE_METHOD(InMemoryDexClassLoader_DexData,
+                findClass,
+                "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"),
+};
+
+void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env) {
+  REGISTER_NATIVE_METHODS("dalvik/system/InMemoryDexClassLoader$DexData");
+}
+
+}  // namespace art
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h
new file mode 100644
index 0000000..f73d07a
--- /dev/null
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8ea31a1..a365a73 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -90,6 +90,7 @@
 #include "mirror/throwable.h"
 #include "monitor.h"
 #include "native/dalvik_system_DexFile.h"
+#include "native/dalvik_system_InMemoryDexClassLoader_DexData.h"
 #include "native/dalvik_system_VMDebug.h"
 #include "native/dalvik_system_VMRuntime.h"
 #include "native/dalvik_system_VMStack.h"
@@ -1389,6 +1390,7 @@
 
 void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
   register_dalvik_system_DexFile(env);
+  register_dalvik_system_InMemoryDexClassLoader_DexData(env);
   register_dalvik_system_VMDebug(env);
   register_dalvik_system_VMRuntime(env);
   register_dalvik_system_VMStack(env);