blob: 6e0a923672569b734122d880beded08e170e4c3e [file] [log] [blame]
Brian Carlstromf91c8c32011-09-21 17:30:34 -07001/*
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
Brian Carlstrom1d9f52b2011-10-13 10:50:45 -070017#include <unistd.h>
18
Brian Carlstromaded5f72011-10-07 17:15:04 -070019#include "class_loader.h"
20#include "class_linker.h"
21#include "dex_file.h"
jeffhaoc393a4f2011-10-19 13:46:09 -070022#include "file.h"
Brian Carlstromf91c8c32011-09-21 17:30:34 -070023#include "logging.h"
Brian Carlstrom1d9f52b2011-10-13 10:50:45 -070024#include "os.h"
Brian Carlstromaded5f72011-10-07 17:15:04 -070025#include "runtime.h"
jeffhaoc393a4f2011-10-19 13:46:09 -070026#include "space.h"
27#include "zip_archive.h"
Brian Carlstrom03a20ba2011-10-13 10:24:13 -070028#include "toStringArray.h"
Brian Carlstromf91c8c32011-09-21 17:30:34 -070029#include "ScopedUtfChars.h"
30
31#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
32
33namespace art {
34
35namespace {
36
37// A smart pointer that provides read-only access to a Java string's UTF chars.
38// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if
39// passed a null jstring. The correct idiom is:
40//
41// NullableScopedUtfChars name(env, javaName);
Brian Carlstromc252c3e2011-10-16 23:21:02 -070042// if (env->ExceptionCheck()) {
Brian Carlstromf91c8c32011-09-21 17:30:34 -070043// return NULL;
44// }
45// // ... use name.c_str()
46//
47// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option.
48class NullableScopedUtfChars {
49public:
50 NullableScopedUtfChars(JNIEnv* env, jstring s)
51 : mEnv(env), mString(s)
52 {
53 mUtfChars = (s != NULL) ? env->GetStringUTFChars(s, NULL) : NULL;
54 }
55
56 ~NullableScopedUtfChars() {
57 if (mUtfChars) {
58 mEnv->ReleaseStringUTFChars(mString, mUtfChars);
59 }
60 }
61
62 const char* c_str() const {
63 return mUtfChars;
64 }
65
66 size_t size() const {
67 return strlen(mUtfChars);
68 }
69
70 // Element access.
71 const char& operator[](size_t n) const {
72 return mUtfChars[n];
73 }
74
75private:
76 JNIEnv* mEnv;
77 jstring mString;
78 const char* mUtfChars;
79
80 // Disallow copy and assignment.
81 NullableScopedUtfChars(const NullableScopedUtfChars&);
82 void operator=(const NullableScopedUtfChars&);
83};
84
jeffhaoc393a4f2011-10-19 13:46:09 -070085static jint DexFile_openDexFile(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName, jint) {
Brian Carlstromf91c8c32011-09-21 17:30:34 -070086 ScopedUtfChars sourceName(env, javaSourceName);
87 if (sourceName.c_str() == NULL) {
88 return 0;
89 }
90 NullableScopedUtfChars outputName(env, javaOutputName);
Brian Carlstromc252c3e2011-10-16 23:21:02 -070091 if (env->ExceptionCheck()) {
Brian Carlstromf91c8c32011-09-21 17:30:34 -070092 return 0;
93 }
Brian Carlstromc252c3e2011-10-16 23:21:02 -070094 if (outputName.c_str() != NULL) {
jeffhaoc393a4f2011-10-19 13:46:09 -070095 // Check that output name ends in .dex.
96 if (!StringPiece(outputName.c_str()).ends_with(".dex")) {
97 LOG(ERROR) << "Output filename '" << outputName.c_str() << "' does not end with .dex";
98 return 0;
99 }
100
101 // Extract the dex file.
102 UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(sourceName.c_str()));
103 if (zip_archive.get() == NULL) {
104 LOG(ERROR) << "Failed to open " << sourceName.c_str() << " when looking for classes.dex";
105 return 0;
106 }
107
108 UniquePtr<ZipEntry> zip_entry(zip_archive->Find(DexFile::kClassesDex));
109 if (zip_entry.get() == NULL) {
110 LOG(ERROR) << "Failed to find classes.dex within " << sourceName.c_str();
111 return 0;
112 }
113
114 UniquePtr<File> file(OS::OpenFile(outputName.c_str(), true));
115 bool success = zip_entry->Extract(*file);
116 if (!success) {
117 LOG(ERROR) << "Failed to extract classes.dex from " << sourceName.c_str();
118 return 0;
119 }
120
121 // Fork and exec dex2oat.
122 pid_t pid = fork();
123 if (pid == 0) {
124 std::string boot_image_option("--boot-image=");
125 boot_image_option += Heap::GetSpaces()[0]->GetImageFilename();
126
127 std::string dex_file_option("--dex-file=");
128 dex_file_option += outputName.c_str();
129
130 std::string oat_file_option("--oat=");
131 oat_file_option += outputName.c_str();
132 oat_file_option.erase(oat_file_option.size() - 3);
133 oat_file_option += "oat";
134
135 execl("/system/bin/dex2oatd",
136 "/system/bin/dex2oatd",
137 "-Xms64m",
138 "-Xmx64m",
139 boot_image_option.c_str(),
140 dex_file_option.c_str(),
141 oat_file_option.c_str(),
142 NULL);
143
144 PLOG(FATAL) << "execl(dex2oatd) failed";
145 return 0;
146 } else {
147 // Wait for dex2oat to finish.
148 int status;
149 pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
150 if (got_pid != pid) {
151 PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
152 return 0;
153 }
154 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
155 LOG(ERROR) << "dex2oatd failed with dex-file=" << outputName.c_str();
156 return 0;
157 }
158 }
Brian Carlstromc252c3e2011-10-16 23:21:02 -0700159 }
jeffhaoc393a4f2011-10-19 13:46:09 -0700160
161 const DexFile* dex_file;
162 if (outputName.c_str() == NULL) {
163 dex_file = DexFile::Open(sourceName.c_str(), "");
164 } else {
165 dex_file = DexFile::Open(outputName.c_str(), "");
166 }
Brian Carlstromaded5f72011-10-07 17:15:04 -0700167 if (dex_file == NULL) {
168 jniThrowExceptionFmt(env, "java/io/IOException", "unable to open DEX file: %s",
169 sourceName.c_str());
jeffhaoc393a4f2011-10-19 13:46:09 -0700170 return 0;
Brian Carlstromaded5f72011-10-07 17:15:04 -0700171 }
172 return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file));
173}
174
175static const DexFile* toDexFile(JNIEnv* env, int dex_file_address) {
176 const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address));
177 if ((dex_file == NULL)) {
178 jniThrowNullPointerException(env, "dex_file == null");
179 }
180 return dex_file;
Brian Carlstromf91c8c32011-09-21 17:30:34 -0700181}
182
183void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) {
Brian Carlstromaded5f72011-10-07 17:15:04 -0700184 const DexFile* dex_file = toDexFile(env, cookie);
185 if (dex_file == NULL) {
186 return;
187 }
188 if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
189 return;
190 }
191 delete dex_file;
Brian Carlstromf91c8c32011-09-21 17:30:34 -0700192}
193
194jclass DexFile_defineClass(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jint cookie) {
Brian Carlstromaded5f72011-10-07 17:15:04 -0700195 const DexFile* dex_file = toDexFile(env, cookie);
196 if (dex_file == NULL) {
197 return NULL;
198 }
Brian Carlstromdf143242011-10-10 18:05:34 -0700199 ScopedUtfChars class_name(env, javaName);
200 if (class_name.c_str() == NULL) {
201 return NULL;
202 }
203 const std::string descriptor = DotToDescriptor(class_name.c_str());
Brian Carlstromaded5f72011-10-07 17:15:04 -0700204 const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
205 if (dex_class_def == NULL) {
Brian Carlstromaded5f72011-10-07 17:15:04 -0700206 return NULL;
207 }
208
209 Object* class_loader_object = Decode<Object*>(env, javaLoader);
210 ClassLoader* class_loader = down_cast<ClassLoader*>(class_loader_object);
211 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
212 class_linker->RegisterDexFile(*dex_file);
213 Class* result = class_linker->DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
Brian Carlstrom41ec4772011-10-11 01:35:58 -0700214 if (env->ExceptionCheck()) {
215 env->ExceptionClear();
216 return NULL;
217 }
Brian Carlstromaded5f72011-10-07 17:15:04 -0700218 return AddLocalReference<jclass>(env, result);
Brian Carlstromf91c8c32011-09-21 17:30:34 -0700219}
220
221jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
Brian Carlstromaded5f72011-10-07 17:15:04 -0700222 const DexFile* dex_file = toDexFile(env, cookie);
223 if (dex_file == NULL) {
224 return NULL;
225 }
Brian Carlstrom03a20ba2011-10-13 10:24:13 -0700226
227 std::vector<std::string> class_names;
228 for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
229 const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
230 const char* descriptor = dex_file->GetClassDescriptor(class_def);
231 class_names.push_back(DescriptorToDot(descriptor));
232 }
233 return toStringArray(env, class_names);
Brian Carlstromf91c8c32011-09-21 17:30:34 -0700234}
235
236jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
Brian Carlstrom03a20ba2011-10-13 10:24:13 -0700237 ScopedUtfChars filename(env, javaFilename);
238 if (filename.c_str() == NULL) {
Brian Carlstrom1d9f52b2011-10-13 10:50:45 -0700239 return JNI_TRUE;
Brian Carlstrom03a20ba2011-10-13 10:24:13 -0700240 }
Brian Carlstrom1d9f52b2011-10-13 10:50:45 -0700241
242 if (!OS::FileExists(filename.c_str())) {
243 jniThrowExceptionFmt(env, "java/io/FileNotFoundException", "%s", filename.c_str());
244 return JNI_TRUE;
245 }
246
247 // Always treat elements of the bootclasspath as up-to-date. The
248 // fact that code is running at all means that this should be true.
249 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
250 const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath();
251 for (size_t i = 0; i < boot_class_path.size(); i++) {
252 if (boot_class_path[i]->GetLocation() == filename.c_str()) {
253 return JNI_FALSE;
254 }
255 }
256
257 UniquePtr<const DexFile> dex_file(DexFile::Open(filename.c_str(), ""));
258 if (dex_file.get() == NULL) {
259 return JNI_TRUE;
260 }
261
Brian Carlstromfad71432011-10-16 20:25:10 -0700262 const OatFile* oat_file = class_linker->FindOatFile(*dex_file.get());
263 if (oat_file == NULL) {
Brian Carlstrom1d9f52b2011-10-13 10:50:45 -0700264 return JNI_TRUE;
265 }
Brian Carlstromfad71432011-10-16 20:25:10 -0700266 const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation());
267 if (oat_dex_file == NULL) {
268 return JNI_TRUE;
269 }
270 if (oat_dex_file->GetDexFileChecksum() != dex_file->GetHeader().checksum_) {
271 return JNI_TRUE;
272 }
Brian Carlstromf91c8c32011-09-21 17:30:34 -0700273 return JNI_FALSE;
274}
275
276static JNINativeMethod gMethods[] = {
277 NATIVE_METHOD(DexFile, closeDexFile, "(I)V"),
278 NATIVE_METHOD(DexFile, defineClass, "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;"),
279 NATIVE_METHOD(DexFile, getClassNameList, "(I)[Ljava/lang/String;"),
280 NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
281 NATIVE_METHOD(DexFile, openDexFile, "(Ljava/lang/String;Ljava/lang/String;I)I"),
282};
283
284} // namespace
285
286void register_dalvik_system_DexFile(JNIEnv* env) {
287 jniRegisterNativeMethods(env, "dalvik/system/DexFile", gMethods, NELEM(gMethods));
288}
289
290} // namespace art