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