dalvik_system_DexFile.cc revision 5f9a8017be9893dc175d1e7bcad59dd80656d49b
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 "android-base/stringprintf.h"
22
23#include "base/file_utils.h"
24#include "base/logging.h"
25#include "base/stl_util.h"
26#include "class_linker.h"
27#include <class_loader_context.h>
28#include "common_throws.h"
29#include "compiler_filter.h"
30#include "dex/art_dex_file_loader.h"
31#include "dex/dex_file-inl.h"
32#include "dex/dex_file_loader.h"
33#include "jit/debugger_interface.h"
34#include "jni_internal.h"
35#include "mirror/class_loader.h"
36#include "mirror/object-inl.h"
37#include "mirror/string.h"
38#include "native_util.h"
39#include "nativehelper/jni_macros.h"
40#include "nativehelper/scoped_local_ref.h"
41#include "nativehelper/scoped_utf_chars.h"
42#include "oat_file.h"
43#include "oat_file_assistant.h"
44#include "oat_file_manager.h"
45#include "os.h"
46#include "runtime.h"
47#include "scoped_thread_state_change-inl.h"
48#include "utils.h"
49#include "well_known_classes.h"
50#include "zip_archive.h"
51
52namespace art {
53
54using android::base::StringPrintf;
55
56static bool ConvertJavaArrayToDexFiles(
57    JNIEnv* env,
58    jobject arrayObject,
59    /*out*/ std::vector<const DexFile*>& dex_files,
60    /*out*/ const OatFile*& oat_file) {
61  jarray array = reinterpret_cast<jarray>(arrayObject);
62
63  jsize array_size = env->GetArrayLength(array);
64  if (env->ExceptionCheck() == JNI_TRUE) {
65    return false;
66  }
67
68  // TODO: Optimize. On 32bit we can use an int array.
69  jboolean is_long_data_copied;
70  jlong* long_data = env->GetLongArrayElements(reinterpret_cast<jlongArray>(array),
71                                               &is_long_data_copied);
72  if (env->ExceptionCheck() == JNI_TRUE) {
73    return false;
74  }
75
76  oat_file = reinterpret_cast<const OatFile*>(static_cast<uintptr_t>(long_data[kOatFileIndex]));
77  dex_files.reserve(array_size - 1);
78  for (jsize i = kDexFileIndexStart; i < array_size; ++i) {
79    dex_files.push_back(reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(long_data[i])));
80  }
81
82  env->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), long_data, JNI_ABORT);
83  return env->ExceptionCheck() != JNI_TRUE;
84}
85
86static jlongArray ConvertDexFilesToJavaArray(JNIEnv* env,
87                                             const OatFile* oat_file,
88                                             std::vector<std::unique_ptr<const DexFile>>& vec) {
89  // Add one for the oat file.
90  jlongArray long_array = env->NewLongArray(static_cast<jsize>(kDexFileIndexStart + vec.size()));
91  if (env->ExceptionCheck() == JNI_TRUE) {
92    return nullptr;
93  }
94
95  jboolean is_long_data_copied;
96  jlong* long_data = env->GetLongArrayElements(long_array, &is_long_data_copied);
97  if (env->ExceptionCheck() == JNI_TRUE) {
98    return nullptr;
99  }
100
101  long_data[kOatFileIndex] = reinterpret_cast<uintptr_t>(oat_file);
102  for (size_t i = 0; i < vec.size(); ++i) {
103    long_data[kDexFileIndexStart + i] = reinterpret_cast<uintptr_t>(vec[i].get());
104  }
105
106  env->ReleaseLongArrayElements(long_array, long_data, 0);
107  if (env->ExceptionCheck() == JNI_TRUE) {
108    return nullptr;
109  }
110
111  // Now release all the unique_ptrs.
112  for (auto& dex_file : vec) {
113    dex_file.release();
114  }
115
116  return long_array;
117}
118
119// A smart pointer that provides read-only access to a Java string's UTF chars.
120// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if
121// passed a null jstring. The correct idiom is:
122//
123//   NullableScopedUtfChars name(env, javaName);
124//   if (env->ExceptionCheck()) {
125//       return null;
126//   }
127//   // ... use name.c_str()
128//
129// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option.
130class NullableScopedUtfChars {
131 public:
132  NullableScopedUtfChars(JNIEnv* env, jstring s) : mEnv(env), mString(s) {
133    mUtfChars = (s != nullptr) ? env->GetStringUTFChars(s, nullptr) : nullptr;
134  }
135
136  ~NullableScopedUtfChars() {
137    if (mUtfChars) {
138      mEnv->ReleaseStringUTFChars(mString, mUtfChars);
139    }
140  }
141
142  const char* c_str() const {
143    return mUtfChars;
144  }
145
146  size_t size() const {
147    return strlen(mUtfChars);
148  }
149
150  // Element access.
151  const char& operator[](size_t n) const {
152    return mUtfChars[n];
153  }
154
155 private:
156  JNIEnv* mEnv;
157  jstring mString;
158  const char* mUtfChars;
159
160  // Disallow copy and assignment.
161  NullableScopedUtfChars(const NullableScopedUtfChars&);
162  void operator=(const NullableScopedUtfChars&);
163};
164
165static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
166  if (end <= start) {
167    ScopedObjectAccess soa(env);
168    ThrowWrappedIOException("Bad range");
169    return nullptr;
170  }
171
172  std::string error_message;
173  size_t length = static_cast<size_t>(end - start);
174  std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
175                                                           nullptr,
176                                                           length,
177                                                           PROT_READ | PROT_WRITE,
178                                                           /* low_4gb */ false,
179                                                           /* reuse */ false,
180                                                           &error_message));
181  if (dex_mem_map == nullptr) {
182    ScopedObjectAccess soa(env);
183    ThrowWrappedIOException("%s", error_message.c_str());
184  }
185  return dex_mem_map;
186}
187
188static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
189  std::string location = StringPrintf("Anonymous-DexFile@%p-%p",
190                                      dex_mem_map->Begin(),
191                                      dex_mem_map->End());
192  std::string error_message;
193  const ArtDexFileLoader dex_file_loader;
194  std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location,
195                                                               0,
196                                                               std::move(dex_mem_map),
197                                                               /* verify */ true,
198                                                               /* verify_location */ true,
199                                                               &error_message));
200  if (dex_file == nullptr) {
201    ScopedObjectAccess soa(env);
202    ThrowWrappedIOException("%s", error_message.c_str());
203    return nullptr;
204  }
205
206  if (!dex_file->DisableWrite()) {
207    ScopedObjectAccess soa(env);
208    ThrowWrappedIOException("Failed to make dex file read-only");
209    return nullptr;
210  }
211
212  return dex_file.release();
213}
214
215static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data) {
216  std::unique_ptr<const DexFile> dex_file(CreateDexFile(env, std::move(data)));
217  if (dex_file.get() == nullptr) {
218    DCHECK(env->ExceptionCheck());
219    return nullptr;
220  }
221  std::vector<std::unique_ptr<const DexFile>> dex_files;
222  dex_files.push_back(std::move(dex_file));
223  return ConvertDexFilesToJavaArray(env, nullptr, dex_files);
224}
225
226static jobject DexFile_createCookieWithDirectBuffer(JNIEnv* env,
227                                                    jclass,
228                                                    jobject buffer,
229                                                    jint start,
230                                                    jint end) {
231  uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
232  if (base_address == nullptr) {
233    ScopedObjectAccess soa(env);
234    ThrowWrappedIOException("dexFileBuffer not direct");
235    return 0;
236  }
237
238  std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
239  if (dex_mem_map == nullptr) {
240    DCHECK(Thread::Current()->IsExceptionPending());
241    return 0;
242  }
243
244  size_t length = static_cast<size_t>(end - start);
245  memcpy(dex_mem_map->Begin(), base_address, length);
246  return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
247}
248
249static jobject DexFile_createCookieWithArray(JNIEnv* env,
250                                             jclass,
251                                             jbyteArray buffer,
252                                             jint start,
253                                             jint end) {
254  std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
255  if (dex_mem_map == nullptr) {
256    DCHECK(Thread::Current()->IsExceptionPending());
257    return 0;
258  }
259
260  auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
261  env->GetByteArrayRegion(buffer, start, end - start, destination);
262  return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
263}
264
265// TODO(calin): clean up the unused parameters (here and in libcore).
266static jobject DexFile_openDexFileNative(JNIEnv* env,
267                                         jclass,
268                                         jstring javaSourceName,
269                                         jstring javaOutputName ATTRIBUTE_UNUSED,
270                                         jint flags ATTRIBUTE_UNUSED,
271                                         jobject class_loader,
272                                         jobjectArray dex_elements) {
273  ScopedUtfChars sourceName(env, javaSourceName);
274  if (sourceName.c_str() == nullptr) {
275    return 0;
276  }
277
278  Runtime* const runtime = Runtime::Current();
279  ClassLinker* linker = runtime->GetClassLinker();
280  std::vector<std::unique_ptr<const DexFile>> dex_files;
281  std::vector<std::string> error_msgs;
282  const OatFile* oat_file = nullptr;
283
284  dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
285                                                               class_loader,
286                                                               dex_elements,
287                                                               /*out*/ &oat_file,
288                                                               /*out*/ &error_msgs);
289
290  if (!dex_files.empty()) {
291    jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
292    if (array == nullptr) {
293      ScopedObjectAccess soa(env);
294      for (auto& dex_file : dex_files) {
295        if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
296          dex_file.release();
297        }
298      }
299    }
300    return array;
301  } else {
302    ScopedObjectAccess soa(env);
303    CHECK(!error_msgs.empty());
304    // The most important message is at the end. So set up nesting by going forward, which will
305    // wrap the existing exception as a cause for the following one.
306    auto it = error_msgs.begin();
307    auto itEnd = error_msgs.end();
308    for ( ; it != itEnd; ++it) {
309      ThrowWrappedIOException("%s", it->c_str());
310    }
311
312    return nullptr;
313  }
314}
315
316static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
317  std::vector<const DexFile*> dex_files;
318  const OatFile* oat_file;
319  if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) {
320    Thread::Current()->AssertPendingException();
321    return JNI_FALSE;
322  }
323  Runtime* const runtime = Runtime::Current();
324  bool all_deleted = true;
325  {
326    ScopedObjectAccess soa(env);
327    ObjPtr<mirror::Object> dex_files_object = soa.Decode<mirror::Object>(cookie);
328    ObjPtr<mirror::LongArray> long_dex_files = dex_files_object->AsLongArray();
329    // Delete dex files associated with this dalvik.system.DexFile since there should not be running
330    // code using it. dex_files is a vector due to multidex.
331    ClassLinker* const class_linker = runtime->GetClassLinker();
332    int32_t i = kDexFileIndexStart;  // Oat file is at index 0.
333    for (const DexFile* dex_file : dex_files) {
334      if (dex_file != nullptr) {
335        DeregisterDexFileForNative(soa.Self(), dex_file->Begin());
336        // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
337        // are calls to DexFile.close while the ART DexFile is still in use.
338        if (!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
339          // Clear the element in the array so that we can call close again.
340          long_dex_files->Set(i, 0);
341          delete dex_file;
342        } else {
343          all_deleted = false;
344        }
345      }
346      ++i;
347    }
348  }
349
350  // oat_file can be null if we are running without dex2oat.
351  if (all_deleted && oat_file != nullptr) {
352    // If all of the dex files are no longer in use we can unmap the corresponding oat file.
353    VLOG(class_linker) << "Unregistering " << oat_file;
354    runtime->GetOatFileManager().UnRegisterAndDeleteOatFile(oat_file);
355  }
356  return all_deleted ? JNI_TRUE : JNI_FALSE;
357}
358
359static jclass DexFile_defineClassNative(JNIEnv* env,
360                                        jclass,
361                                        jstring javaName,
362                                        jobject javaLoader,
363                                        jobject cookie,
364                                        jobject dexFile) {
365  std::vector<const DexFile*> dex_files;
366  const OatFile* oat_file;
367  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out*/ dex_files, /*out*/ oat_file)) {
368    VLOG(class_linker) << "Failed to find dex_file";
369    DCHECK(env->ExceptionCheck());
370    return nullptr;
371  }
372
373  ScopedUtfChars class_name(env, javaName);
374  if (class_name.c_str() == nullptr) {
375    VLOG(class_linker) << "Failed to find class_name";
376    return nullptr;
377  }
378  const std::string descriptor(DotToDescriptor(class_name.c_str()));
379  const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
380  for (auto& dex_file : dex_files) {
381    const DexFile::ClassDef* dex_class_def =
382        OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);
383    if (dex_class_def != nullptr) {
384      ScopedObjectAccess soa(env);
385      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
386      StackHandleScope<1> hs(soa.Self());
387      Handle<mirror::ClassLoader> class_loader(
388          hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
389      ObjPtr<mirror::DexCache> dex_cache =
390          class_linker->RegisterDexFile(*dex_file, class_loader.Get());
391      if (dex_cache == nullptr) {
392        // OOME or InternalError (dexFile already registered with a different class loader).
393        soa.Self()->AssertPendingException();
394        return nullptr;
395      }
396      ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
397                                                               descriptor.c_str(),
398                                                               hash,
399                                                               class_loader,
400                                                               *dex_file,
401                                                               *dex_class_def);
402      // Add the used dex file. This only required for the DexFile.loadClass API since normal
403      // class loaders already keep their dex files live.
404      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
405                                                 class_loader.Get());
406      if (result != nullptr) {
407        VLOG(class_linker) << "DexFile_defineClassNative returning " << result
408                           << " for " << class_name.c_str();
409        return soa.AddLocalReference<jclass>(result);
410      }
411    }
412  }
413  VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();
414  return nullptr;
415}
416
417// Needed as a compare functor for sets of const char
418struct CharPointerComparator {
419  bool operator()(const char *str1, const char *str2) const {
420    return strcmp(str1, str2) < 0;
421  }
422};
423
424// Note: this can be an expensive call, as we sort out duplicates in MultiDex files.
425static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie) {
426  const OatFile* oat_file = nullptr;
427  std::vector<const DexFile*> dex_files;
428  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
429    DCHECK(env->ExceptionCheck());
430    return nullptr;
431  }
432
433  // Push all class descriptors into a set. Use set instead of unordered_set as we want to
434  // retrieve all in the end.
435  std::set<const char*, CharPointerComparator> descriptors;
436  for (auto& dex_file : dex_files) {
437    for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
438      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
439      const char* descriptor = dex_file->GetClassDescriptor(class_def);
440      descriptors.insert(descriptor);
441    }
442  }
443
444  // Now create output array and copy the set into it.
445  jobjectArray result = env->NewObjectArray(descriptors.size(),
446                                            WellKnownClasses::java_lang_String,
447                                            nullptr);
448  if (result != nullptr) {
449    auto it = descriptors.begin();
450    auto it_end = descriptors.end();
451    jsize i = 0;
452    for (; it != it_end; it++, ++i) {
453      std::string descriptor(DescriptorToDot(*it));
454      ScopedLocalRef<jstring> jdescriptor(env, env->NewStringUTF(descriptor.c_str()));
455      if (jdescriptor.get() == nullptr) {
456        return nullptr;
457      }
458      env->SetObjectArrayElement(result, i, jdescriptor.get());
459    }
460  }
461  return result;
462}
463
464static jint GetDexOptNeeded(JNIEnv* env,
465                            const char* filename,
466                            const char* instruction_set,
467                            const char* compiler_filter_name,
468                            const char* class_loader_context,
469                            bool profile_changed,
470                            bool downgrade) {
471  if ((filename == nullptr) || !OS::FileExists(filename)) {
472    LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
473    ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
474    const char* message = (filename == nullptr) ? "<empty file name>" : filename;
475    env->ThrowNew(fnfe.get(), message);
476    return -1;
477  }
478
479  const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
480  if (target_instruction_set == InstructionSet::kNone) {
481    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
482    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set));
483    env->ThrowNew(iae.get(), message.c_str());
484    return -1;
485  }
486
487  CompilerFilter::Filter filter;
488  if (!CompilerFilter::ParseCompilerFilter(compiler_filter_name, &filter)) {
489    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
490    std::string message(StringPrintf("Compiler filter %s is invalid.", compiler_filter_name));
491    env->ThrowNew(iae.get(), message.c_str());
492    return -1;
493  }
494
495  std::unique_ptr<ClassLoaderContext> context = nullptr;
496  if (class_loader_context != nullptr) {
497    context = ClassLoaderContext::Create(class_loader_context);
498
499    if (context == nullptr) {
500      ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
501      std::string message(StringPrintf("Class loader context '%s' is invalid.",
502                                       class_loader_context));
503      env->ThrowNew(iae.get(), message.c_str());
504      return -1;
505    }
506  }
507
508  // TODO: Verify the dex location is well formed, and throw an IOException if
509  // not?
510
511  OatFileAssistant oat_file_assistant(filename, target_instruction_set, false);
512
513  // Always treat elements of the bootclasspath as up-to-date.
514  if (oat_file_assistant.IsInBootClassPath()) {
515    return OatFileAssistant::kNoDexOptNeeded;
516  }
517
518  return oat_file_assistant.GetDexOptNeeded(filter,
519                                            profile_changed,
520                                            downgrade,
521                                            context.get());
522}
523
524static jstring DexFile_getDexFileStatus(JNIEnv* env,
525                                        jclass,
526                                        jstring javaFilename,
527                                        jstring javaInstructionSet) {
528  ScopedUtfChars filename(env, javaFilename);
529  if (env->ExceptionCheck()) {
530    return nullptr;
531  }
532
533  ScopedUtfChars instruction_set(env, javaInstructionSet);
534  if (env->ExceptionCheck()) {
535    return nullptr;
536  }
537
538  const InstructionSet target_instruction_set = GetInstructionSetFromString(
539      instruction_set.c_str());
540  if (target_instruction_set == InstructionSet::kNone) {
541    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
542    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
543    env->ThrowNew(iae.get(), message.c_str());
544    return nullptr;
545  }
546
547  OatFileAssistant oat_file_assistant(filename.c_str(), target_instruction_set,
548                                      false /* load_executable */);
549  return env->NewStringUTF(oat_file_assistant.GetStatusDump().c_str());
550}
551
552// Return an array specifying the optimization status of the given file.
553// The array specification is [compiler_filter, compiler_reason].
554static jobjectArray DexFile_getDexFileOptimizationStatus(JNIEnv* env,
555                                                         jclass,
556                                                         jstring javaFilename,
557                                                         jstring javaInstructionSet) {
558  ScopedUtfChars filename(env, javaFilename);
559  if (env->ExceptionCheck()) {
560    return nullptr;
561  }
562
563  ScopedUtfChars instruction_set(env, javaInstructionSet);
564  if (env->ExceptionCheck()) {
565    return nullptr;
566  }
567
568  const InstructionSet target_instruction_set = GetInstructionSetFromString(
569      instruction_set.c_str());
570  if (target_instruction_set == InstructionSet::kNone) {
571    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
572    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
573    env->ThrowNew(iae.get(), message.c_str());
574    return nullptr;
575  }
576
577  std::string compilation_filter;
578  std::string compilation_reason;
579  OatFileAssistant::GetOptimizationStatus(
580      filename.c_str(), target_instruction_set, &compilation_filter, &compilation_reason);
581
582  ScopedLocalRef<jstring> j_compilation_filter(env, env->NewStringUTF(compilation_filter.c_str()));
583  if (j_compilation_filter.get() == nullptr) {
584    return nullptr;
585  }
586  ScopedLocalRef<jstring> j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str()));
587  if (j_compilation_reason.get() == nullptr) {
588    return nullptr;
589  }
590
591  // Now create output array and copy the set into it.
592  jobjectArray result = env->NewObjectArray(2,
593                                            WellKnownClasses::java_lang_String,
594                                            nullptr);
595  env->SetObjectArrayElement(result, 0, j_compilation_filter.get());
596  env->SetObjectArrayElement(result, 1, j_compilation_reason.get());
597
598  return result;
599}
600
601static jint DexFile_getDexOptNeeded(JNIEnv* env,
602                                    jclass,
603                                    jstring javaFilename,
604                                    jstring javaInstructionSet,
605                                    jstring javaTargetCompilerFilter,
606                                    jstring javaClassLoaderContext,
607                                    jboolean newProfile,
608                                    jboolean downgrade) {
609  ScopedUtfChars filename(env, javaFilename);
610  if (env->ExceptionCheck()) {
611    return -1;
612  }
613
614  ScopedUtfChars instruction_set(env, javaInstructionSet);
615  if (env->ExceptionCheck()) {
616    return -1;
617  }
618
619  ScopedUtfChars target_compiler_filter(env, javaTargetCompilerFilter);
620  if (env->ExceptionCheck()) {
621    return -1;
622  }
623
624  NullableScopedUtfChars class_loader_context(env, javaClassLoaderContext);
625  if (env->ExceptionCheck()) {
626    return -1;
627  }
628
629  return GetDexOptNeeded(env,
630                         filename.c_str(),
631                         instruction_set.c_str(),
632                         target_compiler_filter.c_str(),
633                         class_loader_context.c_str(),
634                         newProfile == JNI_TRUE,
635                         downgrade == JNI_TRUE);
636}
637
638// public API
639static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
640  ScopedUtfChars filename_utf(env, javaFilename);
641  if (env->ExceptionCheck()) {
642    return JNI_FALSE;
643  }
644
645  const char* filename = filename_utf.c_str();
646  if ((filename == nullptr) || !OS::FileExists(filename)) {
647    LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist";
648    ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
649    const char* message = (filename == nullptr) ? "<empty file name>" : filename;
650    env->ThrowNew(fnfe.get(), message);
651    return JNI_FALSE;
652  }
653
654  OatFileAssistant oat_file_assistant(filename, kRuntimeISA, false);
655  return oat_file_assistant.IsUpToDate() ? JNI_FALSE : JNI_TRUE;
656}
657
658static jboolean DexFile_isValidCompilerFilter(JNIEnv* env,
659                                            jclass javeDexFileClass ATTRIBUTE_UNUSED,
660                                            jstring javaCompilerFilter) {
661  ScopedUtfChars compiler_filter(env, javaCompilerFilter);
662  if (env->ExceptionCheck()) {
663    return -1;
664  }
665
666  CompilerFilter::Filter filter;
667  return CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)
668      ? JNI_TRUE : JNI_FALSE;
669}
670
671static jboolean DexFile_isProfileGuidedCompilerFilter(JNIEnv* env,
672                                                      jclass javeDexFileClass ATTRIBUTE_UNUSED,
673                                                      jstring javaCompilerFilter) {
674  ScopedUtfChars compiler_filter(env, javaCompilerFilter);
675  if (env->ExceptionCheck()) {
676    return -1;
677  }
678
679  CompilerFilter::Filter filter;
680  if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
681    return JNI_FALSE;
682  }
683  return CompilerFilter::DependsOnProfile(filter) ? JNI_TRUE : JNI_FALSE;
684}
685
686static jstring DexFile_getNonProfileGuidedCompilerFilter(JNIEnv* env,
687                                                         jclass javeDexFileClass ATTRIBUTE_UNUSED,
688                                                         jstring javaCompilerFilter) {
689  ScopedUtfChars compiler_filter(env, javaCompilerFilter);
690  if (env->ExceptionCheck()) {
691    return nullptr;
692  }
693
694  CompilerFilter::Filter filter;
695  if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
696    return javaCompilerFilter;
697  }
698
699  CompilerFilter::Filter new_filter = CompilerFilter::GetNonProfileDependentFilterFrom(filter);
700
701  // Filter stayed the same, return input.
702  if (filter == new_filter) {
703    return javaCompilerFilter;
704  }
705
706  // Create a new string object and return.
707  std::string new_filter_str = CompilerFilter::NameOfFilter(new_filter);
708  return env->NewStringUTF(new_filter_str.c_str());
709}
710
711static jstring DexFile_getSafeModeCompilerFilter(JNIEnv* env,
712                                                 jclass javeDexFileClass ATTRIBUTE_UNUSED,
713                                                 jstring javaCompilerFilter) {
714  ScopedUtfChars compiler_filter(env, javaCompilerFilter);
715  if (env->ExceptionCheck()) {
716    return nullptr;
717  }
718
719  CompilerFilter::Filter filter;
720  if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
721    return javaCompilerFilter;
722  }
723
724  CompilerFilter::Filter new_filter = CompilerFilter::GetSafeModeFilterFrom(filter);
725
726  // Filter stayed the same, return input.
727  if (filter == new_filter) {
728    return javaCompilerFilter;
729  }
730
731  // Create a new string object and return.
732  std::string new_filter_str = CompilerFilter::NameOfFilter(new_filter);
733  return env->NewStringUTF(new_filter_str.c_str());
734}
735
736static jboolean DexFile_isBackedByOatFile(JNIEnv* env, jclass, jobject cookie) {
737  const OatFile* oat_file = nullptr;
738  std::vector<const DexFile*> dex_files;
739  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
740    DCHECK(env->ExceptionCheck());
741    return false;
742  }
743  return oat_file != nullptr;
744}
745
746static jobjectArray DexFile_getDexFileOutputPaths(JNIEnv* env,
747                                            jclass,
748                                            jstring javaFilename,
749                                            jstring javaInstructionSet) {
750  ScopedUtfChars filename(env, javaFilename);
751  if (env->ExceptionCheck()) {
752    return nullptr;
753  }
754
755  ScopedUtfChars instruction_set(env, javaInstructionSet);
756  if (env->ExceptionCheck()) {
757    return nullptr;
758  }
759
760  const InstructionSet target_instruction_set = GetInstructionSetFromString(
761      instruction_set.c_str());
762  if (target_instruction_set == InstructionSet::kNone) {
763    ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
764    std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set.c_str()));
765    env->ThrowNew(iae.get(), message.c_str());
766    return nullptr;
767  }
768
769  OatFileAssistant oat_file_assistant(filename.c_str(),
770                                      target_instruction_set,
771                                      false /* load_executable */);
772
773  std::unique_ptr<OatFile> best_oat_file = oat_file_assistant.GetBestOatFile();
774  if (best_oat_file == nullptr) {
775    return nullptr;
776  }
777
778  std::string oat_filename = best_oat_file->GetLocation();
779  std::string vdex_filename = GetVdexFilename(best_oat_file->GetLocation());
780
781  ScopedLocalRef<jstring> jvdexFilename(env, env->NewStringUTF(vdex_filename.c_str()));
782  if (jvdexFilename.get() == nullptr) {
783    return nullptr;
784  }
785  ScopedLocalRef<jstring> joatFilename(env, env->NewStringUTF(oat_filename.c_str()));
786  if (joatFilename.get() == nullptr) {
787    return nullptr;
788  }
789
790  // Now create output array and copy the set into it.
791  jobjectArray result = env->NewObjectArray(2,
792                                            WellKnownClasses::java_lang_String,
793                                            nullptr);
794  env->SetObjectArrayElement(result, 0, jvdexFilename.get());
795  env->SetObjectArrayElement(result, 1, joatFilename.get());
796
797  return result;
798}
799
800static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) {
801  const OatFile* oat_file = nullptr;
802  std::vector<const DexFile*> dex_files;
803  if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
804    DCHECK(env->ExceptionCheck());
805    return 0;
806  }
807
808  uint64_t file_size = 0;
809  for (auto& dex_file : dex_files) {
810    if (dex_file) {
811      file_size += dex_file->GetHeader().file_size_;
812    }
813  }
814  return static_cast<jlong>(file_size);
815}
816
817static JNINativeMethod gMethods[] = {
818  NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
819  NATIVE_METHOD(DexFile,
820                defineClassNative,
821                "(Ljava/lang/String;"
822                "Ljava/lang/ClassLoader;"
823                "Ljava/lang/Object;"
824                "Ldalvik/system/DexFile;"
825                ")Ljava/lang/Class;"),
826  NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
827  NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
828  NATIVE_METHOD(DexFile, getDexOptNeeded,
829                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
830  NATIVE_METHOD(DexFile, openDexFileNative,
831                "(Ljava/lang/String;"
832                "Ljava/lang/String;"
833                "I"
834                "Ljava/lang/ClassLoader;"
835                "[Ldalvik/system/DexPathList$Element;"
836                ")Ljava/lang/Object;"),
837  NATIVE_METHOD(DexFile, createCookieWithDirectBuffer,
838                "(Ljava/nio/ByteBuffer;II)Ljava/lang/Object;"),
839  NATIVE_METHOD(DexFile, createCookieWithArray, "([BII)Ljava/lang/Object;"),
840  NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
841  NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
842  NATIVE_METHOD(DexFile,
843                getNonProfileGuidedCompilerFilter,
844                "(Ljava/lang/String;)Ljava/lang/String;"),
845  NATIVE_METHOD(DexFile,
846                getSafeModeCompilerFilter,
847                "(Ljava/lang/String;)Ljava/lang/String;"),
848  NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
849  NATIVE_METHOD(DexFile, getDexFileStatus,
850                "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
851  NATIVE_METHOD(DexFile, getDexFileOutputPaths,
852                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
853  NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
854  NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
855                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;")
856};
857
858void register_dalvik_system_DexFile(JNIEnv* env) {
859  REGISTER_NATIVE_METHODS("dalvik/system/DexFile");
860}
861
862}  // namespace art
863