1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "dalvik_system_DexFile.h"
18
19#include <sstream>
20
21#include "base/logging.h"
22#include "base/stl_util.h"
23#include "base/stringprintf.h"
24#include "class_linker.h"
25#include "common_throws.h"
26#include "compiler_filter.h"
27#include "dex_file-inl.h"
28#include "jni_internal.h"
29#include "mirror/class_loader.h"
30#include "mirror/object-inl.h"
31#include "mirror/string.h"
32#include "oat_file.h"
33#include "oat_file_assistant.h"
34#include "oat_file_manager.h"
35#include "os.h"
36#include "profiler.h"
37#include "runtime.h"
38#include "scoped_thread_state_change.h"
39#include "ScopedLocalRef.h"
40#include "ScopedUtfChars.h"
41#include "utils.h"
42#include "well_known_classes.h"
43#include "zip_archive.h"
44
45namespace art {
46
47static bool ConvertJavaArrayToDexFiles(
48    JNIEnv* env,
49    jobject arrayObject,
50    /*out*/ std::vector<const DexFile*>& dex_files,
51    /*out*/ const OatFile*& oat_file) {
52  jarray array = reinterpret_cast<jarray>(arrayObject);
53
54  jsize array_size = env->GetArrayLength(array);
55  if (env->ExceptionCheck() == JNI_TRUE) {
56    return false;
57  }
58
59  // TODO: Optimize. On 32bit we can use an int array.
60  jboolean is_long_data_copied;
61  jlong* long_data = env->GetLongArrayElements(reinterpret_cast<jlongArray>(array),
62                                               &is_long_data_copied);
63  if (env->ExceptionCheck() == JNI_TRUE) {
64    return false;
65  }
66
67  oat_file = reinterpret_cast<const OatFile*>(static_cast<uintptr_t>(long_data[kOatFileIndex]));
68  dex_files.reserve(array_size - 1);
69  for (jsize i = kDexFileIndexStart; i < array_size; ++i) {
70    dex_files.push_back(reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(long_data[i])));
71  }
72
73  env->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), long_data, JNI_ABORT);
74  return env->ExceptionCheck() != JNI_TRUE;
75}
76
77static jlongArray ConvertDexFilesToJavaArray(JNIEnv* env,
78                                             const OatFile* oat_file,
79                                             std::vector<std::unique_ptr<const DexFile>>& vec) {
80  // Add one for the oat file.
81  jlongArray long_array = env->NewLongArray(static_cast<jsize>(kDexFileIndexStart + vec.size()));
82  if (env->ExceptionCheck() == JNI_TRUE) {
83    return nullptr;
84  }
85
86  jboolean is_long_data_copied;
87  jlong* long_data = env->GetLongArrayElements(long_array, &is_long_data_copied);
88  if (env->ExceptionCheck() == JNI_TRUE) {
89    return nullptr;
90  }
91
92  long_data[kOatFileIndex] = reinterpret_cast<uintptr_t>(oat_file);
93  for (size_t i = 0; i < vec.size(); ++i) {
94    long_data[kDexFileIndexStart + i] = reinterpret_cast<uintptr_t>(vec[i].get());
95  }
96
97  env->ReleaseLongArrayElements(long_array, long_data, 0);
98  if (env->ExceptionCheck() == JNI_TRUE) {
99    return nullptr;
100  }
101
102  // Now release all the unique_ptrs.
103  for (auto& dex_file : vec) {
104    dex_file.release();
105  }
106
107  return long_array;
108}
109
110// A smart pointer that provides read-only access to a Java string's UTF chars.
111// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if
112// passed a null jstring. The correct idiom is:
113//
114//   NullableScopedUtfChars name(env, javaName);
115//   if (env->ExceptionCheck()) {
116//       return null;
117//   }
118//   // ... use name.c_str()
119//
120// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option.
121class NullableScopedUtfChars {
122 public:
123  NullableScopedUtfChars(JNIEnv* env, jstring s) : mEnv(env), mString(s) {
124    mUtfChars = (s != nullptr) ? env->GetStringUTFChars(s, nullptr) : nullptr;
125  }
126
127  ~NullableScopedUtfChars() {
128    if (mUtfChars) {
129      mEnv->ReleaseStringUTFChars(mString, mUtfChars);
130    }
131  }
132
133  const char* c_str() const {
134    return mUtfChars;
135  }
136
137  size_t size() const {
138    return strlen(mUtfChars);
139  }
140
141  // Element access.
142  const char& operator[](size_t n) const {
143    return mUtfChars[n];
144  }
145
146 private:
147  JNIEnv* mEnv;
148  jstring mString;
149  const char* mUtfChars;
150
151  // Disallow copy and assignment.
152  NullableScopedUtfChars(const NullableScopedUtfChars&);
153  void operator=(const NullableScopedUtfChars&);
154};
155
156static jobject DexFile_openDexFileNative(JNIEnv* env,
157                                         jclass,
158                                         jstring javaSourceName,
159                                         jstring javaOutputName,
160                                         jint flags ATTRIBUTE_UNUSED,
161                                         jobject class_loader,
162                                         jobjectArray dex_elements) {
163  ScopedUtfChars sourceName(env, javaSourceName);
164  if (sourceName.c_str() == nullptr) {
165    return 0;
166  }
167  NullableScopedUtfChars outputName(env, javaOutputName);
168  if (env->ExceptionCheck()) {
169    return 0;
170  }
171  Runtime* const runtime = Runtime::Current();
172  ClassLinker* linker = runtime->GetClassLinker();
173  std::vector<std::unique_ptr<const DexFile>> dex_files;
174  std::vector<std::string> error_msgs;
175  const OatFile* oat_file = nullptr;
176
177  dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
178                                                               outputName.c_str(),
179                                                               class_loader,
180                                                               dex_elements,
181                                                               /*out*/ &oat_file,
182                                                               /*out*/ &error_msgs);
183
184  if (!dex_files.empty()) {
185    jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
186    if (array == nullptr) {
187      ScopedObjectAccess soa(env);
188      for (auto& dex_file : dex_files) {
189        if (linker->FindDexCache(soa.Self(), *dex_file, true) != nullptr) {
190          dex_file.release();
191        }
192      }
193    }
194    return array;
195  } else {
196    ScopedObjectAccess soa(env);
197    CHECK(!error_msgs.empty());
198    // The most important message is at the end. So set up nesting by going forward, which will
199    // wrap the existing exception as a cause for the following one.
200    auto it = error_msgs.begin();
201    auto itEnd = error_msgs.end();
202    for ( ; it != itEnd; ++it) {
203      ThrowWrappedIOException("%s", it->c_str());
204    }
205
206    return nullptr;
207  }
208}
209
210static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
211  std::vector<const DexFile*> dex_files;
212  const OatFile* oat_file;
213  if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) {
214    Thread::Current()->AssertPendingException();
215    return JNI_FALSE;
216  }
217  Runtime* const runtime = Runtime::Current();
218  bool all_deleted = true;
219  {
220    ScopedObjectAccess soa(env);
221    mirror::Object* dex_files_object = soa.Decode<mirror::Object*>(cookie);
222    mirror::LongArray* long_dex_files = dex_files_object->AsLongArray();
223    // Delete dex files associated with this dalvik.system.DexFile since there should not be running
224    // code using it. dex_files is a vector due to multidex.
225    ClassLinker* const class_linker = runtime->GetClassLinker();
226    int32_t i = kDexFileIndexStart;  // Oat file is at index 0.
227    for (const DexFile* dex_file : dex_files) {
228      if (dex_file != nullptr) {
229        // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
230        // are calls to DexFile.close while the ART DexFile is still in use.
231        if (class_linker->FindDexCache(soa.Self(), *dex_file, true) == nullptr) {
232          // Clear the element in the array so that we can call close again.
233          long_dex_files->Set(i, 0);
234          delete dex_file;
235        } else {
236          all_deleted = false;
237        }
238      }
239      ++i;
240    }
241  }
242
243  // oat_file can be null if we are running without dex2oat.
244  if (all_deleted && oat_file != nullptr) {
245    // If all of the dex files are no longer in use we can unmap the corresponding oat file.
246    VLOG(class_linker) << "Unregistering " << oat_file;
247    runtime->GetOatFileManager().UnRegisterAndDeleteOatFile(oat_file);
248  }
249  return all_deleted ? JNI_TRUE : JNI_FALSE;
250}
251
252static jclass DexFile_defineClassNative(JNIEnv* env,
253                                        jclass,
254                                        jstring javaName,
255                                        jobject javaLoader,
256                                        jobject cookie,
257                                        jobject dexFile) {
258  std::vector<const DexFile*> dex_files;
259  const OatFile* oat_file;
260  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {
261    VLOG(class_linker) << "Failed to find dex_file";
262    DCHECK(env->ExceptionCheck());
263    return nullptr;
264  }
265
266  ScopedUtfChars class_name(env, javaName);
267  if (class_name.c_str() == nullptr) {
268    VLOG(class_linker) << "Failed to find class_name";
269    return nullptr;
270  }
271  const std::string descriptor(DotToDescriptor(class_name.c_str()));
272  const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
273  for (auto& dex_file : dex_files) {
274    const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor.c_str(), hash);
275    if (dex_class_def != nullptr) {
276      ScopedObjectAccess soa(env);
277      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
278      StackHandleScope<1> hs(soa.Self());
279      Handle<mirror::ClassLoader> class_loader(
280          hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
281      class_linker->RegisterDexFile(*dex_file, class_loader.Get());
282      mirror::Class* result = class_linker->DefineClass(soa.Self(),
283                                                        descriptor.c_str(),
284                                                        hash,
285                                                        class_loader,
286                                                        *dex_file,
287                                                        *dex_class_def);
288      // Add the used dex file. This only required for the DexFile.loadClass API since normal
289      // class loaders already keep their dex files live.
290      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object*>(dexFile),
291                                                 class_loader.Get());
292      if (result != nullptr) {
293        VLOG(class_linker) << "DexFile_defineClassNative returning " << result
294                           << " for " << class_name.c_str();
295        return soa.AddLocalReference<jclass>(result);
296      }
297    }
298  }
299  VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();
300  return nullptr;
301}
302
303// Needed as a compare functor for sets of const char
304struct CharPointerComparator {
305  bool operator()(const char *str1, const char *str2) const {
306    return strcmp(str1, str2) < 0;
307  }
308};
309
310// Note: this can be an expensive call, as we sort out duplicates in MultiDex files.
311static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie) {
312  const OatFile* oat_file = nullptr;
313  std::vector<const DexFile*> dex_files;
314  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
315    DCHECK(env->ExceptionCheck());
316    return nullptr;
317  }
318
319  // Push all class descriptors into a set. Use set instead of unordered_set as we want to
320  // retrieve all in the end.
321  std::set<const char*, CharPointerComparator> descriptors;
322  for (auto& dex_file : dex_files) {
323    for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
324      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
325      const char* descriptor = dex_file->GetClassDescriptor(class_def);
326      descriptors.insert(descriptor);
327    }
328  }
329
330  // Now create output array and copy the set into it.
331  jobjectArray result = env->NewObjectArray(descriptors.size(),
332                                            WellKnownClasses::java_lang_String,
333                                            nullptr);
334  if (result != nullptr) {
335    auto it = descriptors.begin();
336    auto it_end = descriptors.end();
337    jsize i = 0;
338    for (; it != it_end; it++, ++i) {
339      std::string descriptor(DescriptorToDot(*it));
340      ScopedLocalRef<jstring> jdescriptor(env, env->NewStringUTF(descriptor.c_str()));
341      if (jdescriptor.get() == nullptr) {
342        return nullptr;
343      }
344      env->SetObjectArrayElement(result, i, jdescriptor.get());
345    }
346  }
347  return result;
348}
349
350static jint GetDexOptNeeded(JNIEnv* env,
351                            const char* filename,
352                            const char* instruction_set,
353                            const char* compiler_filter_name,
354                            bool profile_changed) {
355  if ((filename == nullptr) || !OS::FileExists(filename)) {
356    LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
357    ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
358    const char* message = (filename == nullptr) ? "<empty file name>" : filename;
359    env->ThrowNew(fnfe.get(), message);
360    return -1;
361  }
362
363  const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
364  if (target_instruction_set == kNone) {
365    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
366    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set));
367    env->ThrowNew(iae.get(), message.c_str());
368    return -1;
369  }
370
371  CompilerFilter::Filter filter;
372  if (!CompilerFilter::ParseCompilerFilter(compiler_filter_name, &filter)) {
373    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
374    std::string message(StringPrintf("Compiler filter %s is invalid.", compiler_filter_name));
375    env->ThrowNew(iae.get(), message.c_str());
376    return -1;
377  }
378
379  // TODO: Verify the dex location is well formed, and throw an IOException if
380  // not?
381
382  OatFileAssistant oat_file_assistant(filename, target_instruction_set, profile_changed, false);
383
384  // Always treat elements of the bootclasspath as up-to-date.
385  if (oat_file_assistant.IsInBootClassPath()) {
386    return OatFileAssistant::kNoDexOptNeeded;
387  }
388  return oat_file_assistant.GetDexOptNeeded(filter);
389}
390
391static jstring DexFile_getDexFileStatus(JNIEnv* env,
392                                        jclass,
393                                        jstring javaFilename,
394                                        jstring javaInstructionSet) {
395  ScopedUtfChars filename(env, javaFilename);
396  if (env->ExceptionCheck()) {
397    return nullptr;
398  }
399
400  ScopedUtfChars instruction_set(env, javaInstructionSet);
401  if (env->ExceptionCheck()) {
402    return nullptr;
403  }
404
405  const InstructionSet target_instruction_set = GetInstructionSetFromString(
406      instruction_set.c_str());
407  if (target_instruction_set == kNone) {
408    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
409    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
410    env->ThrowNew(iae.get(), message.c_str());
411    return nullptr;
412  }
413
414  OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set,
415                                      false /* profile_changed */,
416                                      false /* load_executable */);
417
418  std::ostringstream status;
419  bool oat_file_exists = false;
420  bool odex_file_exists = false;
421  if (oat_file_assistant.OatFileExists()) {
422    oat_file_exists = true;
423    status << *oat_file_assistant.OatFileName() << " [compilation_filter=";
424    status << CompilerFilter::NameOfFilter(oat_file_assistant.OatFileCompilerFilter());
425    status << ", status=" << oat_file_assistant.OatFileStatus();
426  }
427
428  if (oat_file_assistant.OdexFileExists()) {
429    odex_file_exists = true;
430    if (oat_file_exists) {
431      status << "] ";
432    }
433    status << *oat_file_assistant.OdexFileName() << " [compilation_filter=";
434    status << CompilerFilter::NameOfFilter(oat_file_assistant.OdexFileCompilerFilter());
435    status << ", status=" << oat_file_assistant.OdexFileStatus();
436  }
437
438  if (!oat_file_exists && !odex_file_exists) {
439    status << "invalid[";
440  }
441
442  status << "]";
443  return env->NewStringUTF(status.str().c_str());
444}
445
446static jint DexFile_getDexOptNeeded(JNIEnv* env,
447                                    jclass,
448                                    jstring javaFilename,
449                                    jstring javaInstructionSet,
450                                    jstring javaTargetCompilerFilter,
451                                    jboolean newProfile) {
452  ScopedUtfChars filename(env, javaFilename);
453  if (env->ExceptionCheck()) {
454    return -1;
455  }
456
457  ScopedUtfChars instruction_set(env, javaInstructionSet);
458  if (env->ExceptionCheck()) {
459    return -1;
460  }
461
462  ScopedUtfChars target_compiler_filter(env, javaTargetCompilerFilter);
463  if (env->ExceptionCheck()) {
464    return -1;
465  }
466
467  return GetDexOptNeeded(env,
468                         filename.c_str(),
469                         instruction_set.c_str(),
470                         target_compiler_filter.c_str(),
471                         newProfile == JNI_TRUE);
472}
473
474// public API
475static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
476  ScopedUtfChars filename_utf(env, javaFilename);
477  if (env->ExceptionCheck()) {
478    return JNI_FALSE;
479  }
480
481  const char* filename = filename_utf.c_str();
482  if ((filename == nullptr) || !OS::FileExists(filename)) {
483    LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist";
484    ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
485    const char* message = (filename == nullptr) ? "<empty file name>" : filename;
486    env->ThrowNew(fnfe.get(), message);
487    return JNI_FALSE;
488  }
489
490  OatFileAssistant oat_file_assistant(filename, kRuntimeISA, false, false);
491  return oat_file_assistant.IsUpToDate() ? JNI_FALSE : JNI_TRUE;
492}
493
494static jboolean DexFile_isValidCompilerFilter(JNIEnv* env,
495                                            jclass javeDexFileClass ATTRIBUTE_UNUSED,
496                                            jstring javaCompilerFilter) {
497  ScopedUtfChars compiler_filter(env, javaCompilerFilter);
498  if (env->ExceptionCheck()) {
499    return -1;
500  }
501
502  CompilerFilter::Filter filter;
503  return CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)
504      ? JNI_TRUE : JNI_FALSE;
505}
506
507static jboolean DexFile_isProfileGuidedCompilerFilter(JNIEnv* env,
508                                                      jclass javeDexFileClass ATTRIBUTE_UNUSED,
509                                                      jstring javaCompilerFilter) {
510  ScopedUtfChars compiler_filter(env, javaCompilerFilter);
511  if (env->ExceptionCheck()) {
512    return -1;
513  }
514
515  CompilerFilter::Filter filter;
516  if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
517    return JNI_FALSE;
518  }
519  return CompilerFilter::DependsOnProfile(filter) ? JNI_TRUE : JNI_FALSE;
520}
521
522static jstring DexFile_getNonProfileGuidedCompilerFilter(JNIEnv* env,
523                                                         jclass javeDexFileClass ATTRIBUTE_UNUSED,
524                                                         jstring javaCompilerFilter) {
525  ScopedUtfChars compiler_filter(env, javaCompilerFilter);
526  if (env->ExceptionCheck()) {
527    return nullptr;
528  }
529
530  CompilerFilter::Filter filter;
531  if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
532    return javaCompilerFilter;
533  }
534
535  CompilerFilter::Filter new_filter = CompilerFilter::GetNonProfileDependentFilterFrom(filter);
536
537  // Filter stayed the same, return input.
538  if (filter == new_filter) {
539    return javaCompilerFilter;
540  }
541
542  // Create a new string object and return.
543  std::string new_filter_str = CompilerFilter::NameOfFilter(new_filter);
544  return env->NewStringUTF(new_filter_str.c_str());
545}
546
547static jboolean DexFile_isBackedByOatFile(JNIEnv* env, jclass, jobject cookie) {
548  const OatFile* oat_file = nullptr;
549  std::vector<const DexFile*> dex_files;
550  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
551    DCHECK(env->ExceptionCheck());
552    return false;
553  }
554  return oat_file != nullptr;
555}
556
557static jstring DexFile_getDexFileOutputPath(JNIEnv* env,
558                                            jclass,
559                                            jstring javaFilename,
560                                            jstring javaInstructionSet) {
561  ScopedUtfChars filename(env, javaFilename);
562  if (env->ExceptionCheck()) {
563    return nullptr;
564  }
565
566  ScopedUtfChars instruction_set(env, javaInstructionSet);
567  if (env->ExceptionCheck()) {
568    return nullptr;
569  }
570
571  const InstructionSet target_instruction_set = GetInstructionSetFromString(
572      instruction_set.c_str());
573  if (target_instruction_set == kNone) {
574    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
575    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
576    env->ThrowNew(iae.get(), message.c_str());
577    return nullptr;
578  }
579
580  OatFileAssistant oat_file_assistant(filename.c_str(),
581                                      target_instruction_set,
582                                      false /* profile_changed */,
583                                      false /* load_executable */);
584
585  std::unique_ptr<OatFile> best_oat_file = oat_file_assistant.GetBestOatFile();
586  if (best_oat_file == nullptr) {
587    return nullptr;
588  }
589
590  return env->NewStringUTF(best_oat_file->GetLocation().c_str());
591}
592
593static JNINativeMethod gMethods[] = {
594  NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
595  NATIVE_METHOD(DexFile,
596                defineClassNative,
597                "(Ljava/lang/String;"
598                "Ljava/lang/ClassLoader;"
599                "Ljava/lang/Object;"
600                "Ldalvik/system/DexFile;"
601                ")Ljava/lang/Class;"),
602  NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
603  NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
604  NATIVE_METHOD(DexFile, getDexOptNeeded,
605                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
606  NATIVE_METHOD(DexFile, openDexFileNative,
607                "(Ljava/lang/String;"
608                "Ljava/lang/String;"
609                "I"
610                "Ljava/lang/ClassLoader;"
611                "[Ldalvik/system/DexPathList$Element;"
612                ")Ljava/lang/Object;"),
613  NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
614  NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
615  NATIVE_METHOD(DexFile,
616                getNonProfileGuidedCompilerFilter,
617                "(Ljava/lang/String;)Ljava/lang/String;"),
618  NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
619  NATIVE_METHOD(DexFile, getDexFileStatus,
620                "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
621  NATIVE_METHOD(DexFile, getDexFileOutputPath,
622                "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")
623};
624
625void register_dalvik_system_DexFile(JNIEnv* env) {
626  REGISTER_NATIVE_METHODS("dalvik/system/DexFile");
627}
628
629}  // namespace art
630