blob: a5b8b6fa0c8bcef115ce80d4bc28bfe5b2fe341a [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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/*
18 * Convert the output from "dx" into a locally-optimized DEX file.
19 *
20 * TODO: the format of the optimized header is currently "whatever we
21 * happen to write", since the VM that writes it is by definition the same
22 * as the VM that reads it. Still, it should be better documented and
23 * more rigorously structured.
24 */
25#include "Dalvik.h"
26#include "libdex/InstrUtils.h"
27#include "libdex/OptInvocation.h"
The Android Open Source Project99409882009-03-18 22:20:24 -070028#include "analysis/RegisterMap.h"
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080029
30#include <zlib.h>
31
32#include <stdlib.h>
33#include <unistd.h>
34#include <sys/mman.h>
35#include <sys/stat.h>
36#include <sys/file.h>
37#include <sys/wait.h>
38#include <fcntl.h>
39#include <errno.h>
40
41/*
42 * Virtual/direct calls to "method" are replaced with an execute-inline
43 * instruction with index "idx".
44 */
45typedef struct InlineSub {
46 Method* method;
47 int inlineIdx;
48} InlineSub;
49
50
51/* fwd */
52static int writeDependencies(int fd, u4 modWhen, u4 crc);
53static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,\
The Android Open Source Project99409882009-03-18 22:20:24 -070054 const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080055static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
56 int err);
Andy McFadden7d18e382009-12-03 16:08:36 -080057static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080058
59static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,\
60 u4* pHeaderFlags, DexClassLookup** ppClassLookup);
61static void updateChecksum(u1* addr, int len, DexHeader* pHeader);
62static bool loadAllClasses(DvmDex* pDvmDex);
63static void optimizeLoadedClasses(DexFile* pDexFile);
64static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs);
65static bool optimizeMethod(Method* method, const InlineSub* inlineSubs);
66static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
67static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
Andy McFaddenb0a05412009-11-19 10:23:41 -080068static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080069static bool rewriteExecuteInline(Method* method, u2* insns,
70 MethodType methodType, const InlineSub* inlineSubs);
Andy McFaddenb0a05412009-11-19 10:23:41 -080071static bool rewriteExecuteInlineRange(Method* method, u2* insns,
72 MethodType methodType, const InlineSub* inlineSubs);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080073
74
75/*
76 * Return the fd of an open file in the DEX file cache area. If the cache
77 * file doesn't exist or is out of date, this will remove the old entry,
78 * create a new one (writing only the file header), and return with the
79 * "new file" flag set.
80 *
81 * It's possible to execute from an unoptimized DEX file directly,
82 * assuming the byte ordering and structure alignment is correct, but
83 * disadvantageous because some significant optimizations are not possible.
84 * It's not generally possible to do the same from an uncompressed Jar
85 * file entry, because we have to guarantee 32-bit alignment in the
86 * memory-mapped file.
87 *
88 * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
89 * and "crc32" come from the Zip directory entry. For a stand-alone DEX
90 * file, it's the modification date of the file and the Adler32 from the
91 * DEX header (which immediately follows the magic). If these don't
92 * match what's stored in the opt header, we reject the file immediately.
93 *
94 * On success, the file descriptor will be positioned just past the "opt"
95 * file header, and will be locked with flock. "*pCachedName" will point
96 * to newly-allocated storage.
97 */
98int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
99 u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
100{
101 int fd, cc;
102 struct stat fdStat, fileStat;
103 bool readOnly = false;
104
105 *pNewFile = false;
106
107retry:
108 /*
109 * Try to open the cache file. If we've been asked to,
110 * create it if it doesn't exist.
111 */
112 fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
113 if (fd < 0) {
114 fd = open(cacheFileName, O_RDONLY, 0);
115 if (fd < 0) {
116 if (createIfMissing) {
117 LOGE("Can't open dex cache '%s': %s\n",
118 cacheFileName, strerror(errno));
119 }
120 return fd;
121 }
122 readOnly = true;
123 }
124
125 /*
126 * Grab an exclusive lock on the cache file. If somebody else is
127 * working on it, we'll block here until they complete. Because
128 * we're waiting on an external resource, we go into VMWAIT mode.
129 */
130 int oldStatus;
131 LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
132 cacheFileName, fd, isBootstrap);
133 oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
134 cc = flock(fd, LOCK_EX | LOCK_NB);
135 if (cc != 0) {
136 LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
137 cc = flock(fd, LOCK_EX);
138 }
139 dvmChangeStatus(NULL, oldStatus);
140 if (cc != 0) {
141 LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
142 close(fd);
143 return -1;
144 }
145 LOGV("DexOpt: locked cache file\n");
146
147 /*
148 * Check to see if the fd we opened and locked matches the file in
149 * the filesystem. If they don't, then somebody else unlinked ours
150 * and created a new file, and we need to use that one instead. (If
151 * we caught them between the unlink and the create, we'll get an
152 * ENOENT from the file stat.)
153 */
154 cc = fstat(fd, &fdStat);
155 if (cc != 0) {
156 LOGE("Can't stat open file '%s'\n", cacheFileName);
157 LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
158 goto close_fail;
159 }
160 cc = stat(cacheFileName, &fileStat);
161 if (cc != 0 ||
162 fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
163 {
164 LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
165 LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
166 flock(fd, LOCK_UN);
167 close(fd);
168 usleep(250 * 1000); /* if something is hosed, don't peg machine */
169 goto retry;
170 }
171
172 /*
173 * We have the correct file open and locked. If the file size is zero,
174 * then it was just created by us, and we want to fill in some fields
175 * in the "opt" header and set "*pNewFile". Otherwise, we want to
176 * verify that the fields in the header match our expectations, and
177 * reset the file if they don't.
178 */
179 if (fdStat.st_size == 0) {
180 if (readOnly) {
181 LOGW("DexOpt: file has zero length and isn't writable\n");
182 goto close_fail;
183 }
184 cc = dexOptCreateEmptyHeader(fd);
185 if (cc != 0)
186 goto close_fail;
187 *pNewFile = true;
188 LOGV("DexOpt: successfully initialized new cache file\n");
189 } else {
190 bool expectVerify, expectOpt;
191
192 if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
193 expectVerify = false;
194 else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
195 expectVerify = !isBootstrap;
196 else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
197 expectVerify = true;
198
199 if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
200 expectOpt = false;
201 else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
202 expectOpt = expectVerify;
203 else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
204 expectOpt = true;
205
206 LOGV("checking deps, expecting vfy=%d opt=%d\n",
207 expectVerify, expectOpt);
208
209 if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
210 expectVerify, expectOpt))
211 {
212 if (readOnly) {
213 /*
214 * We could unlink and rewrite the file if we own it or
215 * the "sticky" bit isn't set on the directory. However,
216 * we're not able to truncate it, which spoils things. So,
217 * give up now.
218 */
219 if (createIfMissing) {
220 LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
221 fileName, cacheFileName);
222 }
223 goto close_fail;
224 }
225
226 /*
227 * If we truncate the existing file before unlinking it, any
228 * process that has it mapped will fail when it tries to touch
229 * the pages.
230 *
231 * This is very important. The zygote process will have the
232 * boot DEX files (core, framework, etc.) mapped early. If
233 * (say) core.dex gets updated, and somebody launches an app
234 * that uses App.dex, then App.dex gets reoptimized because it's
235 * dependent upon the boot classes. However, dexopt will be
236 * using the *new* core.dex to do the optimizations, while the
237 * app will actually be running against the *old* core.dex
238 * because it starts from zygote.
239 *
240 * Even without zygote, it's still possible for a class loader
241 * to pull in an APK that was optimized against an older set
242 * of DEX files. We must ensure that everything fails when a
243 * boot DEX gets updated, and for general "why aren't my
244 * changes doing anything" purposes its best if we just make
245 * everything crash when a DEX they're using gets updated.
246 */
247 LOGD("Stale deps in cache file; removing and retrying\n");
248 if (ftruncate(fd, 0) != 0) {
249 LOGW("Warning: unable to truncate cache file '%s': %s\n",
250 cacheFileName, strerror(errno));
251 /* keep going */
252 }
253 if (unlink(cacheFileName) != 0) {
254 LOGW("Warning: unable to remove cache file '%s': %d %s\n",
255 cacheFileName, errno, strerror(errno));
256 /* keep going; permission failure should probably be fatal */
257 }
258 LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
259 flock(fd, LOCK_UN);
260 close(fd);
261 goto retry;
262 } else {
263 LOGV("DexOpt: good deps in cache file\n");
264 }
265 }
266
267 assert(fd >= 0);
268 return fd;
269
270close_fail:
271 flock(fd, LOCK_UN);
272 close(fd);
273 return -1;
274}
275
276/*
277 * Unlock the file descriptor.
278 *
279 * Returns "true" on success.
280 */
281bool dvmUnlockCachedDexFile(int fd)
282{
283 LOGVV("DexOpt: unlocking cache file fd=%d\n", fd);
284 return (flock(fd, LOCK_UN) == 0);
285}
286
287
288/*
289 * Given a descriptor for a file with DEX data in it, produce an
290 * optimized version.
291 *
292 * The file pointed to by "fd" is expected to be a locked shared resource
293 * (or private); we make no efforts to enforce multi-process correctness
294 * here.
295 *
296 * "fileName" is only used for debug output. "modWhen" and "crc" are stored
297 * in the dependency set.
298 *
299 * The "isBootstrap" flag determines how the optimizer and verifier handle
300 * package-scope access checks. When optimizing, we only load the bootstrap
301 * class DEX files and the target DEX, so the flag determines whether the
302 * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
303 * This only really matters if the target DEX contains classes that claim to
304 * be in the same package as bootstrap classes.
305 *
306 * The optimizer will need to load every class in the target DEX file.
307 * This is generally undesirable, so we start a subprocess to do the
308 * work and wait for it to complete.
309 *
310 * Returns "true" on success. All data will have been written to "fd".
311 */
312bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
313 const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
314{
315 const char* lastPart = strrchr(fileName, '/');
316 if (lastPart != NULL)
317 lastPart++;
318 else
319 lastPart = fileName;
320
321 /*
322 * For basic optimizations (byte-swapping and structure aligning) we
323 * don't need to fork(). It looks like fork+exec is causing problems
324 * with gdb on our bewildered Linux distro, so in some situations we
325 * want to avoid this.
326 *
327 * For optimization and/or verification, we need to load all the classes.
328 *
329 * We don't check gDvm.generateRegisterMaps, since that is dependent
330 * upon the verifier state.
331 */
332 if (gDvm.classVerifyMode == VERIFY_MODE_NONE &&
333 (gDvm.dexOptMode == OPTIMIZE_MODE_NONE ||
334 gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED))
335 {
336 LOGD("DexOpt: --- BEGIN (quick) '%s' ---\n", lastPart);
337 return dvmContinueOptimization(fd, dexOffset, dexLength,
338 fileName, modWhen, crc, isBootstrap);
339 }
340
341
342 LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);
343
344 pid_t pid;
345
346 /*
347 * This could happen if something in our bootclasspath, which we thought
348 * was all optimized, got rejected.
349 */
350 if (gDvm.optimizing) {
351 LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
352 return false;
353 }
354
355 pid = fork();
356 if (pid == 0) {
357 static const int kUseValgrind = 0;
358 static const char* kDexOptBin = "/bin/dexopt";
359 static const char* kValgrinder = "/usr/bin/valgrind";
360 static const int kFixedArgCount = 10;
361 static const int kValgrindArgCount = 5;
362 static const int kMaxIntLen = 12; // '-'+10dig+'\0' -OR- 0x+8dig
363 int bcpSize = dvmGetBootPathSize();
364 int argc = kFixedArgCount + bcpSize
365 + (kValgrindArgCount * kUseValgrind);
366 char* argv[argc+1]; // last entry is NULL
367 char values[argc][kMaxIntLen];
368 char* execFile;
369 char* androidRoot;
370 int flags;
371
Andy McFadden074afd62009-04-08 12:51:10 -0700372 /* change process groups, so we don't clash with ProcessManager */
373 setpgid(0, 0);
374
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800375 /* full path to optimizer */
376 androidRoot = getenv("ANDROID_ROOT");
377 if (androidRoot == NULL) {
378 LOGW("ANDROID_ROOT not set, defaulting to /system\n");
379 androidRoot = "/system";
380 }
381 execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
382 strcpy(execFile, androidRoot);
383 strcat(execFile, kDexOptBin);
384
385 /*
386 * Create arg vector.
387 */
388 int curArg = 0;
389
390 if (kUseValgrind) {
391 /* probably shouldn't ship the hard-coded path */
392 argv[curArg++] = (char*)kValgrinder;
393 argv[curArg++] = "--tool=memcheck";
394 argv[curArg++] = "--leak-check=yes"; // check for leaks too
395 argv[curArg++] = "--leak-resolution=med"; // increase from 2 to 4
396 argv[curArg++] = "--num-callers=16"; // default is 12
397 assert(curArg == kValgrindArgCount);
398 }
399 argv[curArg++] = execFile;
400
401 argv[curArg++] = "--dex";
402
403 sprintf(values[2], "%d", DALVIK_VM_BUILD);
404 argv[curArg++] = values[2];
405
406 sprintf(values[3], "%d", fd);
407 argv[curArg++] = values[3];
408
409 sprintf(values[4], "%d", (int) dexOffset);
410 argv[curArg++] = values[4];
411
412 sprintf(values[5], "%d", (int) dexLength);
413 argv[curArg++] = values[5];
414
415 argv[curArg++] = (char*)fileName;
416
417 sprintf(values[7], "%d", (int) modWhen);
418 argv[curArg++] = values[7];
419
420 sprintf(values[8], "%d", (int) crc);
421 argv[curArg++] = values[8];
422
423 flags = 0;
424 if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
425 flags |= DEXOPT_OPT_ENABLED;
426 if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
427 flags |= DEXOPT_OPT_ALL;
428 }
429 if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
430 flags |= DEXOPT_VERIFY_ENABLED;
431 if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
432 flags |= DEXOPT_VERIFY_ALL;
433 }
434 if (isBootstrap)
435 flags |= DEXOPT_IS_BOOTSTRAP;
436 if (gDvm.generateRegisterMaps)
437 flags |= DEXOPT_GEN_REGISTER_MAP;
438 sprintf(values[9], "%d", flags);
439 argv[curArg++] = values[9];
440
441 assert(((!kUseValgrind && curArg == kFixedArgCount) ||
442 ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));
443
444 ClassPathEntry* cpe;
445 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
446 argv[curArg++] = cpe->fileName;
447 }
448 assert(curArg == argc);
449
450 argv[curArg] = NULL;
451
452 if (kUseValgrind)
453 execv(kValgrinder, argv);
454 else
455 execv(execFile, argv);
456
457 LOGE("execv '%s'%s failed: %s\n", execFile,
458 kUseValgrind ? " [valgrind]" : "", strerror(errno));
459 exit(1);
460 } else {
461 LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
462 int status;
463 pid_t gotPid;
464 int oldStatus;
465
466 /*
467 * Wait for the optimization process to finish. We go into VMWAIT
468 * mode here so GC suspension won't have to wait for us.
469 */
470 oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
471 while (true) {
472 gotPid = waitpid(pid, &status, 0);
473 if (gotPid == -1 && errno == EINTR) {
474 LOGD("waitpid interrupted, retrying\n");
475 } else {
476 break;
477 }
478 }
479 dvmChangeStatus(NULL, oldStatus);
480 if (gotPid != pid) {
481 LOGE("waitpid failed: wanted %d, got %d: %s\n",
482 (int) pid, (int) gotPid, strerror(errno));
483 return false;
484 }
485
486 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
487 LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
488 return true;
489 } else {
490 LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
491 lastPart, status);
492 return false;
493 }
494 }
495}
496
497/*
498 * Do the actual optimization. This is called directly for "minimal"
499 * optimization, or from a newly-created process for "full" optimization.
500 *
501 * For best use of disk/memory, we want to extract once and perform
502 * optimizations in place. If the file has to expand or contract
503 * to match local structure padding/alignment expectations, we want
504 * to do the rewrite as part of the extract, rather than extracting
505 * into a temp file and slurping it back out. (The structure alignment
506 * is currently correct for all platforms, and this isn't expected to
507 * change, so we should be okay with having it already extracted.)
508 *
509 * Returns "true" on success.
510 */
511bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength,
512 const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
513{
514 DexClassLookup* pClassLookup = NULL;
515 IndexMapSet* pIndexMapSet = NULL;
The Android Open Source Project99409882009-03-18 22:20:24 -0700516 RegisterMapBuilder* pRegMapBuilder = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800517 bool doVerify, doOpt;
518 u4 headerFlags = 0;
519
520 if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
521 doVerify = false;
522 else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
523 doVerify = !isBootstrap;
524 else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
525 doVerify = true;
526
527 if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
528 doOpt = false;
529 else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
530 doOpt = doVerify;
531 else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
532 doOpt = true;
533
534 LOGV("Continuing optimization (%s, isb=%d, vfy=%d, opt=%d)\n",
535 fileName, isBootstrap, doVerify, doOpt);
536
537 assert(dexOffset >= 0);
538
539 /* quick test so we don't blow up on empty file */
540 if (dexLength < (int) sizeof(DexHeader)) {
541 LOGE("too small to be DEX\n");
542 return false;
543 }
544 if (dexOffset < (int) sizeof(DexOptHeader)) {
545 LOGE("not enough room for opt header\n");
546 return false;
547 }
548
549 bool result = false;
550
551 /*
552 * Drop this into a global so we don't have to pass it around. We could
553 * also add a field to DexFile, but since it only pertains to DEX
554 * creation that probably doesn't make sense.
555 */
556 gDvm.optimizingBootstrapClass = isBootstrap;
557
558 {
559 /*
560 * Map the entire file (so we don't have to worry about page
561 * alignment). The expectation is that the output file contains
562 * our DEX data plus room for a small header.
563 */
564 bool success;
565 void* mapAddr;
566 mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE,
567 MAP_SHARED, fd, 0);
568 if (mapAddr == MAP_FAILED) {
569 LOGE("unable to mmap DEX cache: %s\n", strerror(errno));
570 goto bail;
571 }
572
573 /*
574 * Rewrite the file. Byte reordering, structure realigning,
575 * class verification, and bytecode optimization are all performed
576 * here.
The Android Open Source Project99409882009-03-18 22:20:24 -0700577 *
578 * In theory the file could change size and bits could shift around.
579 * In practice this would be annoying to deal with, so the file
580 * layout is designed so that it can always be rewritten in place.
581 *
582 * This sets "headerFlags" and creates the class lookup table as
583 * part of doing the processing.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800584 */
585 success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength,
586 doVerify, doOpt, &headerFlags, &pClassLookup);
587
588 if (success) {
589 DvmDex* pDvmDex = NULL;
590 u1* dexAddr = ((u1*) mapAddr) + dexOffset;
591
592 if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) {
593 LOGE("Unable to create DexFile\n");
The Android Open Source Project99409882009-03-18 22:20:24 -0700594 success = false;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800595 } else {
596 /*
597 * If configured to do so, scan the instructions, looking
598 * for ways to reduce the size of the resolved-constant table.
599 * This is done post-optimization, across the instructions
600 * in all methods in all classes (even the ones that failed
601 * to load).
602 */
603 pIndexMapSet = dvmRewriteConstants(pDvmDex);
604
The Android Open Source Project99409882009-03-18 22:20:24 -0700605 /*
606 * If configured to do so, generate a full set of register
607 * maps for all verified classes.
608 */
609 if (gDvm.generateRegisterMaps) {
610 pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex);
611 if (pRegMapBuilder == NULL) {
612 LOGE("Failed generating register maps\n");
613 success = false;
614 }
615 }
616
Andy McFaddenb51ea112009-05-08 16:50:17 -0700617 DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader;
618 updateChecksum(dexAddr, dexLength, pHeader);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800619
620 dvmDexFileFree(pDvmDex);
621 }
622 }
623
624 /* unmap the read-write version, forcing writes to disk */
625 if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) {
626 LOGW("msync failed: %s\n", strerror(errno));
627 // weird, but keep going
628 }
629#if 1
630 /*
631 * This causes clean shutdown to fail, because we have loaded classes
632 * that point into it. For the optimizer this isn't a problem,
633 * because it's more efficient for the process to simply exit.
634 * Exclude this code when doing clean shutdown for valgrind.
635 */
636 if (munmap(mapAddr, dexOffset + dexLength) != 0) {
637 LOGE("munmap failed: %s\n", strerror(errno));
638 goto bail;
639 }
640#endif
641
642 if (!success)
643 goto bail;
644 }
645
646 /* get start offset, and adjust deps start for 64-bit alignment */
647 off_t depsOffset, auxOffset, endOffset, adjOffset;
648 int depsLength, auxLength;
Andy McFadden7d18e382009-12-03 16:08:36 -0800649 u4 optChecksum;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800650
651 depsOffset = lseek(fd, 0, SEEK_END);
652 if (depsOffset < 0) {
653 LOGE("lseek to EOF failed: %s\n", strerror(errno));
654 goto bail;
655 }
656 adjOffset = (depsOffset + 7) & ~(0x07);
657 if (adjOffset != depsOffset) {
658 LOGV("Adjusting deps start from %d to %d\n",
659 (int) depsOffset, (int) adjOffset);
660 depsOffset = adjOffset;
661 lseek(fd, depsOffset, SEEK_SET);
662 }
663
664 /*
665 * Append the dependency list.
666 */
667 if (writeDependencies(fd, modWhen, crc) != 0) {
668 LOGW("Failed writing dependencies\n");
669 goto bail;
670 }
671
The Android Open Source Project99409882009-03-18 22:20:24 -0700672 /* compute deps length, then adjust aux start for 64-bit alignment */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800673 auxOffset = lseek(fd, 0, SEEK_END);
674 depsLength = auxOffset - depsOffset;
675
676 adjOffset = (auxOffset + 7) & ~(0x07);
677 if (adjOffset != auxOffset) {
678 LOGV("Adjusting aux start from %d to %d\n",
679 (int) auxOffset, (int) adjOffset);
680 auxOffset = adjOffset;
681 lseek(fd, auxOffset, SEEK_SET);
682 }
683
684 /*
685 * Append any auxillary pre-computed data structures.
686 */
The Android Open Source Project99409882009-03-18 22:20:24 -0700687 if (!writeAuxData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800688 LOGW("Failed writing aux data\n");
689 goto bail;
690 }
691
692 endOffset = lseek(fd, 0, SEEK_END);
693 auxLength = endOffset - auxOffset;
694
Andy McFadden7d18e382009-12-03 16:08:36 -0800695 /* compute checksum from start of deps to end of aux area */
696 if (!computeFileChecksum(fd, depsOffset,
697 (auxOffset+auxLength) - depsOffset, &optChecksum))
698 {
699 goto bail;
700 }
701
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800702 /*
703 * Output the "opt" header with all values filled in and a correct
704 * magic number.
705 */
706 DexOptHeader optHdr;
707 memset(&optHdr, 0xff, sizeof(optHdr));
708 memcpy(optHdr.magic, DEX_OPT_MAGIC, 4);
709 memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4);
710 optHdr.dexOffset = (u4) dexOffset;
711 optHdr.dexLength = (u4) dexLength;
712 optHdr.depsOffset = (u4) depsOffset;
713 optHdr.depsLength = (u4) depsLength;
714 optHdr.auxOffset = (u4) auxOffset;
715 optHdr.auxLength = (u4) auxLength;
716
717 optHdr.flags = headerFlags;
Andy McFadden7d18e382009-12-03 16:08:36 -0800718 optHdr.checksum = optChecksum;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800719
720 ssize_t actual;
721 lseek(fd, 0, SEEK_SET);
722 actual = write(fd, &optHdr, sizeof(optHdr));
723 if (actual != sizeof(optHdr)) {
724 logFailedWrite(sizeof(optHdr), actual, "opt header", errno);
725 goto bail;
726 }
727
728 LOGV("Successfully wrote DEX header\n");
729 result = true;
730
The Android Open Source Project99409882009-03-18 22:20:24 -0700731 //dvmRegisterMapDumpStats();
732
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800733bail:
734 dvmFreeIndexMapSet(pIndexMapSet);
The Android Open Source Project99409882009-03-18 22:20:24 -0700735 dvmFreeRegisterMapBuilder(pRegMapBuilder);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800736 free(pClassLookup);
737 return result;
738}
739
740
741/*
742 * Get the cache file name from a ClassPathEntry.
743 */
744static const char* getCacheFileName(const ClassPathEntry* cpe)
745{
746 switch (cpe->kind) {
747 case kCpeJar:
748 return dvmGetJarFileCacheFileName((JarFile*) cpe->ptr);
749 case kCpeDex:
750 return dvmGetRawDexFileCacheFileName((RawDexFile*) cpe->ptr);
751 default:
752 LOGE("DexOpt: unexpected cpe kind %d\n", cpe->kind);
753 dvmAbort();
754 return NULL;
755 }
756}
757
758/*
759 * Get the SHA-1 signature.
760 */
761static const u1* getSignature(const ClassPathEntry* cpe)
762{
763 DvmDex* pDvmDex;
764
765 switch (cpe->kind) {
766 case kCpeJar:
767 pDvmDex = dvmGetJarFileDex((JarFile*) cpe->ptr);
768 break;
769 case kCpeDex:
770 pDvmDex = dvmGetRawDexFileDex((RawDexFile*) cpe->ptr);
771 break;
772 default:
773 LOGE("unexpected cpe kind %d\n", cpe->kind);
774 dvmAbort();
775 pDvmDex = NULL; // make gcc happy
776 }
777
778 assert(pDvmDex != NULL);
779 return pDvmDex->pDexFile->pHeader->signature;
780}
781
782
783/*
784 * Dependency layout:
785 * 4b Source file modification time, in seconds since 1970 UTC
786 * 4b CRC-32 from Zip entry, or Adler32 from source DEX header
787 * 4b Dalvik VM build number
788 * 4b Number of dependency entries that follow
789 * Dependency entries:
790 * 4b Name length (including terminating null)
791 * var Full path of cache entry (null terminated)
792 * 20b SHA-1 signature from source DEX file
793 *
794 * If this changes, update DEX_OPT_MAGIC_VERS.
795 */
796static const size_t kMinDepSize = 4 * 4;
797static const size_t kMaxDepSize = 4 * 4 + 1024; // sanity check
798
799/*
800 * Read the "opt" header, verify it, then read the dependencies section
801 * and verify that data as well.
802 *
803 * If "sourceAvail" is "true", this will verify that "modWhen" and "crc"
804 * match up with what is stored in the header. If they don't, we reject
805 * the file so that it can be recreated from the updated original. If
806 * "sourceAvail" isn't set, e.g. for a .odex file, we ignore these arguments.
807 *
808 * On successful return, the file will be seeked immediately past the
809 * "opt" header.
810 */
811bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen,
812 u4 crc, bool expectVerify, bool expectOpt)
813{
814 DexOptHeader optHdr;
815 u1* depData = NULL;
816 const u1* magic;
817 off_t posn;
818 int result = false;
819 ssize_t actual;
820
821 /*
822 * Start at the start. The "opt" header, when present, will always be
823 * the first thing in the file.
824 */
825 if (lseek(fd, 0, SEEK_SET) != 0) {
826 LOGE("DexOpt: failed to seek to start of file: %s\n", strerror(errno));
827 goto bail;
828 }
829
830 /*
831 * Read and do trivial verification on the opt header. The header is
832 * always in host byte order.
833 */
834 if (read(fd, &optHdr, sizeof(optHdr)) != sizeof(optHdr)) {
835 LOGE("DexOpt: failed reading opt header: %s\n", strerror(errno));
836 goto bail;
837 }
838
839 magic = optHdr.magic;
840 if (memcmp(magic, DEX_OPT_MAGIC, 4) != 0) {
841 /* not a DEX file, or previous attempt was interrupted */
842 LOGD("DexOpt: incorrect opt magic number (0x%02x %02x %02x %02x)\n",
843 magic[0], magic[1], magic[2], magic[3]);
844 goto bail;
845 }
846 if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
847 LOGW("DexOpt: stale opt version (0x%02x %02x %02x %02x)\n",
848 magic[4], magic[5], magic[6], magic[7]);
849 goto bail;
850 }
851 if (optHdr.depsLength < kMinDepSize || optHdr.depsLength > kMaxDepSize) {
852 LOGW("DexOpt: weird deps length %d, bailing\n", optHdr.depsLength);
853 goto bail;
854 }
855
856 /*
857 * Do the header flags match up with what we want?
858 *
859 * This is useful because it allows us to automatically regenerate
860 * a file when settings change (e.g. verification is now mandatory),
861 * but can cause difficulties if the bootstrap classes we depend upon
862 * were handled differently than the current options specify. We get
863 * upset because they're not verified or optimized, but we're not able
864 * to regenerate them because the installer won't let us.
865 *
866 * (This is also of limited value when !sourceAvail.)
867 *
868 * So, for now, we essentially ignore "expectVerify" and "expectOpt"
869 * by limiting the match mask.
870 *
871 * The only thing we really can't handle is incorrect byte-ordering.
872 */
873 const u4 matchMask = DEX_OPT_FLAG_BIG;
874 u4 expectedFlags = 0;
875#if __BYTE_ORDER != __LITTLE_ENDIAN
876 expectedFlags |= DEX_OPT_FLAG_BIG;
877#endif
878 if (expectVerify)
879 expectedFlags |= DEX_FLAG_VERIFIED;
880 if (expectOpt)
881 expectedFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
882 if ((expectedFlags & matchMask) != (optHdr.flags & matchMask)) {
883 LOGI("DexOpt: header flag mismatch (0x%02x vs 0x%02x, mask=0x%02x)\n",
884 expectedFlags, optHdr.flags, matchMask);
885 goto bail;
886 }
887
888 posn = lseek(fd, optHdr.depsOffset, SEEK_SET);
889 if (posn < 0) {
890 LOGW("DexOpt: seek to deps failed: %s\n", strerror(errno));
891 goto bail;
892 }
893
894 /*
895 * Read all of the dependency stuff into memory.
896 */
897 depData = (u1*) malloc(optHdr.depsLength);
898 if (depData == NULL) {
899 LOGW("DexOpt: unable to allocate %d bytes for deps\n",
900 optHdr.depsLength);
901 goto bail;
902 }
903 actual = read(fd, depData, optHdr.depsLength);
904 if (actual != (ssize_t) optHdr.depsLength) {
905 LOGW("DexOpt: failed reading deps: %d of %d (err=%s)\n",
906 (int) actual, optHdr.depsLength, strerror(errno));
907 goto bail;
908 }
909
910 /*
911 * Verify simple items.
912 */
913 const u1* ptr;
914 u4 val;
915
916 ptr = depData;
917 val = read4LE(&ptr);
918 if (sourceAvail && val != modWhen) {
919 LOGI("DexOpt: source file mod time mismatch (%08x vs %08x)\n",
920 val, modWhen);
921 goto bail;
922 }
923 val = read4LE(&ptr);
924 if (sourceAvail && val != crc) {
925 LOGI("DexOpt: source file CRC mismatch (%08x vs %08x)\n", val, crc);
926 goto bail;
927 }
928 val = read4LE(&ptr);
929 if (val != DALVIK_VM_BUILD) {
Andy McFadden23e84b12009-06-18 14:22:22 -0700930 LOGD("DexOpt: VM build version mismatch (%d vs %d)\n",
931 val, DALVIK_VM_BUILD);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800932 goto bail;
933 }
934
935 /*
936 * Verify dependencies on other cached DEX files. It must match
937 * exactly with what is currently defined in the bootclasspath.
938 */
939 ClassPathEntry* cpe;
940 u4 numDeps;
941
942 numDeps = read4LE(&ptr);
943 LOGV("+++ DexOpt: numDeps = %d\n", numDeps);
944 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
945 const char* cacheFileName = getCacheFileName(cpe);
946 const u1* signature = getSignature(cpe);
947 size_t len = strlen(cacheFileName) +1;
948 u4 storedStrLen;
949
950 if (numDeps == 0) {
951 /* more entries in bootclasspath than in deps list */
952 LOGI("DexOpt: not all deps represented\n");
953 goto bail;
954 }
955
956 storedStrLen = read4LE(&ptr);
957 if (len != storedStrLen ||
958 strcmp(cacheFileName, (const char*) ptr) != 0)
959 {
960 LOGI("DexOpt: mismatch dep name: '%s' vs. '%s'\n",
961 cacheFileName, ptr);
962 goto bail;
963 }
964
965 ptr += storedStrLen;
966
967 if (memcmp(signature, ptr, kSHA1DigestLen) != 0) {
968 LOGI("DexOpt: mismatch dep signature for '%s'\n", cacheFileName);
969 goto bail;
970 }
971 ptr += kSHA1DigestLen;
972
973 LOGV("DexOpt: dep match on '%s'\n", cacheFileName);
974
975 numDeps--;
976 }
977
978 if (numDeps != 0) {
979 /* more entries in deps list than in classpath */
980 LOGI("DexOpt: Some deps went away\n");
981 goto bail;
982 }
983
984 // consumed all data and no more?
985 if (ptr != depData + optHdr.depsLength) {
986 LOGW("DexOpt: Spurious dep data? %d vs %d\n",
987 (int) (ptr - depData), optHdr.depsLength);
988 assert(false);
989 }
990
991 result = true;
992
993bail:
994 free(depData);
995 return result;
996}
997
998/*
999 * Write the dependency info to "fd" at the current file position.
1000 */
1001static int writeDependencies(int fd, u4 modWhen, u4 crc)
1002{
1003 u1* buf = NULL;
1004 ssize_t actual;
1005 int result = -1;
1006 ssize_t bufLen;
1007 ClassPathEntry* cpe;
1008 int i, numDeps;
1009
1010 /*
1011 * Count up the number of completed entries in the bootclasspath.
1012 */
1013 numDeps = 0;
1014 bufLen = 0;
1015 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
1016 const char* cacheFileName = getCacheFileName(cpe);
1017 LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName);
1018
1019 numDeps++;
1020 bufLen += strlen(cacheFileName) +1;
1021 }
1022
1023 bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
1024
1025 buf = malloc(bufLen);
1026
1027 set4LE(buf+0, modWhen);
1028 set4LE(buf+4, crc);
1029 set4LE(buf+8, DALVIK_VM_BUILD);
1030 set4LE(buf+12, numDeps);
1031
1032 // TODO: do we want to add dvmGetInlineOpsTableLength() here? Won't
1033 // help us if somebody replaces an existing entry, but it'd catch
1034 // additions/removals.
1035
1036 u1* ptr = buf + 4*4;
1037 for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
1038 const char* cacheFileName = getCacheFileName(cpe);
1039 const u1* signature = getSignature(cpe);
1040 int len = strlen(cacheFileName) +1;
1041
1042 if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) {
1043 LOGE("DexOpt: overran buffer\n");
1044 dvmAbort();
1045 }
1046
1047 set4LE(ptr, len);
1048 ptr += 4;
1049 memcpy(ptr, cacheFileName, len);
1050 ptr += len;
1051 memcpy(ptr, signature, kSHA1DigestLen);
1052 ptr += kSHA1DigestLen;
1053 }
1054
1055 assert(ptr == buf + bufLen);
1056
1057 actual = write(fd, buf, bufLen);
1058 if (actual != bufLen) {
1059 result = (errno != 0) ? errno : -1;
1060 logFailedWrite(bufLen, actual, "dep info", errno);
1061 } else {
1062 result = 0;
1063 }
1064
1065 free(buf);
1066 return result;
1067}
1068
1069
1070/*
1071 * Write a block of data in "chunk" format.
1072 *
1073 * The chunk header fields are always in "native" byte order. If "size"
1074 * is not a multiple of 8 bytes, the data area is padded out.
1075 */
1076static bool writeChunk(int fd, u4 type, const void* data, size_t size)
1077{
1078 ssize_t actual;
1079 union { /* save a syscall by grouping these together */
1080 char raw[8];
1081 struct {
1082 u4 type;
1083 u4 size;
1084 } ts;
1085 } header;
1086
1087 assert(sizeof(header) == 8);
1088
1089 LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size);
1090
1091 header.ts.type = type;
1092 header.ts.size = (u4) size;
1093 actual = write(fd, &header, sizeof(header));
1094 if (actual != sizeof(header)) {
1095 logFailedWrite(size, actual, "aux chunk header write", errno);
1096 return false;
1097 }
1098
1099 if (size > 0) {
1100 actual = write(fd, data, size);
1101 if (actual != (ssize_t) size) {
1102 logFailedWrite(size, actual, "aux chunk write", errno);
1103 return false;
1104 }
1105 }
1106
1107 /* if necessary, pad to 64-bit alignment */
1108 if ((size & 7) != 0) {
1109 int padSize = 8 - (size & 7);
1110 LOGV("size was %d, inserting %d pad bytes\n", size, padSize);
1111 lseek(fd, padSize, SEEK_CUR);
1112 }
1113
1114 assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0);
1115
1116 return true;
1117}
1118
1119/*
1120 * Write aux data.
1121 *
1122 * We have different pieces, some of which may be optional. To make the
1123 * most effective use of space, we use a "chunk" format, with a 4-byte
1124 * type and a 4-byte length. We guarantee 64-bit alignment for the data,
1125 * so it can be used directly when the file is mapped for reading.
1126 */
1127static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,
The Android Open Source Project99409882009-03-18 22:20:24 -07001128 const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001129{
1130 /* pre-computed class lookup hash table */
The Android Open Source Project99409882009-03-18 22:20:24 -07001131 if (!writeChunk(fd, (u4) kDexChunkClassLookup,
1132 pClassLookup, pClassLookup->size))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001133 {
1134 return false;
1135 }
1136
1137 /* remapped constants (optional) */
1138 if (pIndexMapSet != NULL) {
The Android Open Source Project99409882009-03-18 22:20:24 -07001139 if (!writeChunk(fd, pIndexMapSet->chunkType,
1140 pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen))
1141 {
1142 return false;
1143 }
1144 }
1145
1146 /* register maps (optional) */
1147 if (pRegMapBuilder != NULL) {
1148 if (!writeChunk(fd, (u4) kDexChunkRegisterMaps,
1149 pRegMapBuilder->data, pRegMapBuilder->size))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001150 {
1151 return false;
1152 }
1153 }
1154
1155 /* write the end marker */
1156 if (!writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)) {
1157 return false;
1158 }
1159
1160 return true;
1161}
1162
1163/*
1164 * Log a failed write.
1165 */
1166static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
1167 int err)
1168{
1169 LOGE("Write failed: %s (%d of %d): %s\n",
1170 msg, (int)actual, (int)expected, strerror(err));
1171}
1172
Andy McFadden7d18e382009-12-03 16:08:36 -08001173/*
1174 * Compute a checksum on a piece of an open file.
1175 *
1176 * File will be positioned at end of checksummed area.
1177 *
1178 * Returns "true" on success.
1179 */
1180static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
1181{
1182 unsigned char readBuf[8192];
1183 ssize_t actual;
1184 uLong adler;
1185
1186 if (lseek(fd, start, SEEK_SET) != start) {
1187 LOGE("Unable to seek to start of checksum area (%ld): %s\n",
1188 (long) start, strerror(errno));
1189 return false;
1190 }
1191
1192 adler = adler32(0L, Z_NULL, 0);
1193
1194 while (length != 0) {
1195 size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
1196 actual = read(fd, readBuf, wanted);
1197 if (actual <= 0) {
1198 LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
1199 (int) actual, length, strerror(errno));
1200 return false;
1201 }
1202
1203 adler = adler32(adler, readBuf, actual);
1204
1205 length -= actual;
1206 }
1207
1208 *pSum = adler;
1209 return true;
1210}
1211
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001212
1213/*
1214 * ===========================================================================
1215 * Optimizations
1216 * ===========================================================================
1217 */
1218
1219/*
1220 * Perform in-place rewrites on a memory-mapped DEX file.
1221 *
1222 * This happens in a short-lived child process, so we can go nutty with
1223 * loading classes and allocating memory.
1224 */
1225static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,
1226 u4* pHeaderFlags, DexClassLookup** ppClassLookup)
1227{
1228 u8 prepWhen, loadWhen, verifyWhen, optWhen;
1229 DvmDex* pDvmDex = NULL;
1230 bool result = false;
1231
1232 *pHeaderFlags = 0;
1233
1234 LOGV("+++ swapping bytes\n");
1235 if (dexFixByteOrdering(addr, len) != 0)
1236 goto bail;
1237#if __BYTE_ORDER != __LITTLE_ENDIAN
1238 *pHeaderFlags |= DEX_OPT_FLAG_BIG;
1239#endif
1240
1241 /*
1242 * Now that the DEX file can be read directly, create a DexFile for it.
1243 */
1244 if (dvmDexFileOpenPartial(addr, len, &pDvmDex) != 0) {
1245 LOGE("Unable to create DexFile\n");
1246 goto bail;
1247 }
1248
1249 /*
1250 * Create the class lookup table.
1251 */
1252 //startWhen = dvmGetRelativeTimeUsec();
1253 *ppClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
1254 if (*ppClassLookup == NULL)
1255 goto bail;
1256
1257 /*
1258 * Bail out early if they don't want The Works. The current implementation
1259 * doesn't fork a new process if this flag isn't set, so we really don't
1260 * want to continue on with the crazy class loading.
1261 */
1262 if (!doVerify && !doOpt) {
1263 result = true;
1264 goto bail;
1265 }
1266
1267 /* this is needed for the next part */
1268 pDvmDex->pDexFile->pClassLookup = *ppClassLookup;
1269
1270 prepWhen = dvmGetRelativeTimeUsec();
1271
1272 /*
1273 * Load all classes found in this DEX file. If they fail to load for
1274 * some reason, they won't get verified (which is as it should be).
1275 */
1276 if (!loadAllClasses(pDvmDex))
1277 goto bail;
1278 loadWhen = dvmGetRelativeTimeUsec();
1279
1280 /*
1281 * Verify all classes in the DEX file. Export the "is verified" flag
1282 * to the DEX file we're creating.
1283 */
1284 if (doVerify) {
1285 dvmVerifyAllClasses(pDvmDex->pDexFile);
1286 *pHeaderFlags |= DEX_FLAG_VERIFIED;
1287 }
1288 verifyWhen = dvmGetRelativeTimeUsec();
1289
1290 /*
1291 * Optimize the classes we successfully loaded. If the opt mode is
1292 * OPTIMIZE_MODE_VERIFIED, each class must have been successfully
1293 * verified or we'll skip it.
1294 */
1295#ifndef PROFILE_FIELD_ACCESS
1296 if (doOpt) {
1297 optimizeLoadedClasses(pDvmDex->pDexFile);
1298 *pHeaderFlags |= DEX_OPT_FLAG_FIELDS | DEX_OPT_FLAG_INVOCATIONS;
1299 }
1300#endif
1301 optWhen = dvmGetRelativeTimeUsec();
1302
1303 LOGD("DexOpt: load %dms, verify %dms, opt %dms\n",
1304 (int) (loadWhen - prepWhen) / 1000,
1305 (int) (verifyWhen - loadWhen) / 1000,
1306 (int) (optWhen - verifyWhen) / 1000);
1307
1308 result = true;
1309
1310bail:
1311 /* free up storage */
1312 dvmDexFileFree(pDvmDex);
1313
1314 return result;
1315}
1316
1317/*
1318 * Update the Adler-32 checksum stored in the DEX file. This covers the
1319 * swapped and optimized DEX data, but does not include the opt header
1320 * or auxillary data.
1321 */
1322static void updateChecksum(u1* addr, int len, DexHeader* pHeader)
1323{
1324 /*
1325 * Rewrite the checksum. We leave the SHA-1 signature alone.
1326 */
1327 uLong adler = adler32(0L, Z_NULL, 0);
1328 const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
1329
1330 adler = adler32(adler, addr + nonSum, len - nonSum);
1331 pHeader->checksum = adler;
1332}
1333
1334/*
1335 * Try to load all classes in the specified DEX. If they have some sort
1336 * of broken dependency, e.g. their superclass lives in a different DEX
1337 * that wasn't previously loaded into the bootstrap class path, loading
1338 * will fail. This is the desired behavior.
1339 *
1340 * We have no notion of class loader at this point, so we load all of
1341 * the classes with the bootstrap class loader. It turns out this has
1342 * exactly the behavior we want, and has no ill side effects because we're
1343 * running in a separate process and anything we load here will be forgotten.
1344 *
1345 * We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
1346 * This works because we only call here as part of optimization / pre-verify,
1347 * not during verification as part of loading a class into a running VM.
1348 *
1349 * This returns "false" if the world is too screwed up to do anything
1350 * useful at all.
1351 */
1352static bool loadAllClasses(DvmDex* pDvmDex)
1353{
1354 u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
1355 u4 idx;
1356 int loaded = 0;
1357
1358 LOGV("DexOpt: +++ trying to load %d classes\n", count);
1359
1360 dvmSetBootPathExtraDex(pDvmDex);
1361
1362 /*
1363 * We have some circularity issues with Class and Object that are most
1364 * easily avoided by ensuring that Object is never the first thing we
1365 * try to find. Take care of that here. (We only need to do this when
1366 * loading classes from the DEX file that contains Object, and only
1367 * when Object comes first in the list, but it costs very little to
1368 * do it in all cases.)
1369 */
1370 if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
1371 LOGE("ERROR: java.lang.Class does not exist!\n");
1372 return false;
1373 }
1374
1375 for (idx = 0; idx < count; idx++) {
1376 const DexClassDef* pClassDef;
1377 const char* classDescriptor;
1378 ClassObject* newClass;
1379
1380 pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
1381 classDescriptor =
1382 dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
1383
1384 LOGV("+++ loading '%s'", classDescriptor);
1385 //newClass = dvmDefineClass(pDexFile, classDescriptor,
1386 // NULL);
1387 newClass = dvmFindSystemClassNoInit(classDescriptor);
1388 if (newClass == NULL) {
1389 LOGV("DexOpt: failed loading '%s'\n", classDescriptor);
1390 dvmClearOptException(dvmThreadSelf());
1391 } else if (newClass->pDvmDex != pDvmDex) {
1392 /*
1393 * We don't load the new one, and we tag the first one found
1394 * with the "multiple def" flag so the resolver doesn't try
1395 * to make it available.
1396 */
1397 LOGD("DexOpt: '%s' has an earlier definition; blocking out\n",
1398 classDescriptor);
1399 SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
1400 } else {
1401 loaded++;
1402 }
1403 }
1404 LOGV("DexOpt: +++ successfully loaded %d classes\n", loaded);
1405
1406 dvmSetBootPathExtraDex(NULL);
1407 return true;
1408}
1409
1410
1411/*
1412 * Create a table of inline substitutions.
1413 *
1414 * TODO: this is currently just a linear array. We will want to put this
1415 * into a hash table as the list size increases.
1416 */
1417static InlineSub* createInlineSubsTable(void)
1418{
1419 const InlineOperation* ops = dvmGetInlineOpsTable();
1420 const int count = dvmGetInlineOpsTableLength();
1421 InlineSub* table;
1422 Method* method;
1423 ClassObject* clazz;
1424 int i, tableIndex;
1425
1426 /*
1427 * Allocate for optimism: one slot per entry, plus an end-of-list marker.
1428 */
1429 table = malloc(sizeof(InlineSub) * (count+1));
1430
1431 tableIndex = 0;
1432 for (i = 0; i < count; i++) {
1433 clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
1434 if (clazz == NULL) {
1435 LOGV("DexOpt: can't inline for class '%s': not found\n",
1436 ops[i].classDescriptor);
1437 dvmClearOptException(dvmThreadSelf());
1438 } else {
1439 /*
1440 * Method could be virtual or direct. Try both. Don't use
1441 * the "hier" versions.
1442 */
1443 method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
1444 ops[i].methodSignature);
1445 if (method == NULL)
1446 method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
1447 ops[i].methodSignature);
1448 if (method == NULL) {
1449 LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
1450 ops[i].classDescriptor, ops[i].methodName,
1451 ops[i].methodSignature);
1452 } else {
1453 if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
1454 LOGW("DexOpt: WARNING: inline op on non-final class/method "
1455 "%s.%s\n",
1456 clazz->descriptor, method->name);
1457 /* fail? */
1458 }
1459 if (dvmIsSynchronizedMethod(method) ||
1460 dvmIsDeclaredSynchronizedMethod(method))
1461 {
1462 LOGW("DexOpt: WARNING: inline op on synchronized method "
1463 "%s.%s\n",
1464 clazz->descriptor, method->name);
1465 /* fail? */
1466 }
1467
1468 table[tableIndex].method = method;
1469 table[tableIndex].inlineIdx = i;
1470 tableIndex++;
1471
1472 LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
1473 ops[i].classDescriptor, ops[i].methodName,
1474 ops[i].methodSignature);
1475 }
1476 }
1477 }
1478
1479 /* mark end of table */
1480 table[tableIndex].method = NULL;
1481 LOGV("DexOpt: inline table has %d entries\n", tableIndex);
1482
1483 return table;
1484}
1485
1486/*
1487 * Run through all classes that were successfully loaded from this DEX
1488 * file and optimize their code sections.
1489 */
1490static void optimizeLoadedClasses(DexFile* pDexFile)
1491{
1492 u4 count = pDexFile->pHeader->classDefsSize;
1493 u4 idx;
1494 InlineSub* inlineSubs = NULL;
1495
1496 LOGV("DexOpt: +++ optimizing up to %d classes\n", count);
1497 assert(gDvm.dexOptMode != OPTIMIZE_MODE_NONE);
1498
1499 inlineSubs = createInlineSubsTable();
1500
1501 for (idx = 0; idx < count; idx++) {
1502 const DexClassDef* pClassDef;
1503 const char* classDescriptor;
1504 ClassObject* clazz;
1505
1506 pClassDef = dexGetClassDef(pDexFile, idx);
1507 classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
1508
1509 /* all classes are loaded into the bootstrap class loader */
1510 clazz = dvmLookupClass(classDescriptor, NULL, false);
1511 if (clazz != NULL) {
1512 if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) == 0 &&
1513 gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
1514 {
1515 LOGV("DexOpt: not optimizing '%s': not verified\n",
1516 classDescriptor);
1517 } else if (clazz->pDvmDex->pDexFile != pDexFile) {
1518 /* shouldn't be here -- verifier should have caught */
1519 LOGD("DexOpt: not optimizing '%s': multiple definitions\n",
1520 classDescriptor);
1521 } else {
1522 optimizeClass(clazz, inlineSubs);
1523
1524 /* set the flag whether or not we actually did anything */
1525 ((DexClassDef*)pClassDef)->accessFlags |=
1526 CLASS_ISOPTIMIZED;
1527 }
1528 } else {
1529 LOGV("DexOpt: not optimizing unavailable class '%s'\n",
1530 classDescriptor);
1531 }
1532 }
1533
1534 free(inlineSubs);
1535}
1536
1537/*
1538 * Optimize the specified class.
1539 */
1540static void optimizeClass(ClassObject* clazz, const InlineSub* inlineSubs)
1541{
1542 int i;
1543
1544 for (i = 0; i < clazz->directMethodCount; i++) {
1545 if (!optimizeMethod(&clazz->directMethods[i], inlineSubs))
1546 goto fail;
1547 }
1548 for (i = 0; i < clazz->virtualMethodCount; i++) {
1549 if (!optimizeMethod(&clazz->virtualMethods[i], inlineSubs))
1550 goto fail;
1551 }
1552
1553 return;
1554
1555fail:
1556 LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
1557}
1558
1559/*
1560 * Optimize instructions in a method.
1561 *
1562 * Returns "true" if all went well, "false" if we bailed out early when
1563 * something failed.
1564 */
1565static bool optimizeMethod(Method* method, const InlineSub* inlineSubs)
1566{
1567 u4 insnsSize;
1568 u2* insns;
1569 u2 inst;
1570
1571 if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
1572 return true;
1573
1574 insns = (u2*) method->insns;
1575 assert(insns != NULL);
1576 insnsSize = dvmGetMethodInsnsSize(method);
1577
1578 while (insnsSize > 0) {
1579 int width;
1580
1581 inst = *insns & 0xff;
1582
1583 switch (inst) {
1584 case OP_IGET:
1585 case OP_IGET_BOOLEAN:
1586 case OP_IGET_BYTE:
1587 case OP_IGET_CHAR:
1588 case OP_IGET_SHORT:
1589 rewriteInstField(method, insns, OP_IGET_QUICK);
1590 break;
1591 case OP_IGET_WIDE:
1592 rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
1593 break;
1594 case OP_IGET_OBJECT:
1595 rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
1596 break;
1597 case OP_IPUT:
1598 case OP_IPUT_BOOLEAN:
1599 case OP_IPUT_BYTE:
1600 case OP_IPUT_CHAR:
1601 case OP_IPUT_SHORT:
1602 rewriteInstField(method, insns, OP_IPUT_QUICK);
1603 break;
1604 case OP_IPUT_WIDE:
1605 rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
1606 break;
1607 case OP_IPUT_OBJECT:
1608 rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
1609 break;
1610
1611 case OP_INVOKE_VIRTUAL:
1612 if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL,inlineSubs))
1613 {
1614 if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
1615 return false;
1616 }
1617 break;
1618 case OP_INVOKE_VIRTUAL_RANGE:
Andy McFaddenb0a05412009-11-19 10:23:41 -08001619 if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL,
1620 inlineSubs))
1621 {
1622 if (!rewriteVirtualInvoke(method, insns,
1623 OP_INVOKE_VIRTUAL_QUICK_RANGE))
1624 {
1625 return false;
1626 }
1627 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001628 break;
1629 case OP_INVOKE_SUPER:
1630 if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
1631 return false;
1632 break;
1633 case OP_INVOKE_SUPER_RANGE:
1634 if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
1635 return false;
1636 break;
1637
1638 case OP_INVOKE_DIRECT:
1639 if (!rewriteExecuteInline(method, insns, METHOD_DIRECT, inlineSubs))
1640 {
Andy McFaddenb0a05412009-11-19 10:23:41 -08001641 if (!rewriteEmptyDirectInvoke(method, insns))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001642 return false;
1643 }
1644 break;
Andy McFaddenb0a05412009-11-19 10:23:41 -08001645 case OP_INVOKE_DIRECT_RANGE:
1646 rewriteExecuteInlineRange(method, insns, METHOD_DIRECT, inlineSubs);
1647 break;
1648
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001649 case OP_INVOKE_STATIC:
1650 rewriteExecuteInline(method, insns, METHOD_STATIC, inlineSubs);
1651 break;
Andy McFaddenb0a05412009-11-19 10:23:41 -08001652 case OP_INVOKE_STATIC_RANGE:
1653 rewriteExecuteInlineRange(method, insns, METHOD_STATIC, inlineSubs);
1654 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001655
1656 default:
1657 // ignore this instruction
1658 ;
1659 }
1660
1661 if (*insns == kPackedSwitchSignature) {
1662 width = 4 + insns[1] * 2;
1663 } else if (*insns == kSparseSwitchSignature) {
1664 width = 2 + insns[1] * 4;
1665 } else if (*insns == kArrayDataSignature) {
1666 u2 elemWidth = insns[1];
1667 u4 len = insns[2] | (((u4)insns[3]) << 16);
1668 width = 4 + (elemWidth * len + 1) / 2;
1669 } else {
1670 width = dexGetInstrWidth(gDvm.instrWidth, inst);
1671 }
1672 assert(width > 0);
1673
1674 insns += width;
1675 insnsSize -= width;
1676 }
1677
1678 assert(insnsSize == 0);
1679 return true;
1680}
1681
1682
1683/*
1684 * If "referrer" and "resClass" don't come from the same DEX file, and
1685 * the DEX we're working on is not destined for the bootstrap class path,
1686 * tweak the class loader so package-access checks work correctly.
1687 *
1688 * Only do this if we're doing pre-verification or optimization.
1689 */
1690static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
1691{
1692 if (!gDvm.optimizing)
1693 return;
1694 assert(referrer->classLoader == NULL);
1695 assert(resClass->classLoader == NULL);
1696
1697 if (!gDvm.optimizingBootstrapClass) {
1698 /* class loader for an array class comes from element type */
1699 if (dvmIsArrayClass(resClass))
1700 resClass = resClass->elementClass;
1701 if (referrer->pDvmDex != resClass->pDvmDex)
1702 resClass->classLoader = (Object*) 0xdead3333;
1703 }
1704}
1705
1706/*
1707 * Undo the effects of tweakLoader.
1708 */
1709static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
1710{
1711 if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
1712 return;
1713
1714 if (dvmIsArrayClass(resClass))
1715 resClass = resClass->elementClass;
1716 resClass->classLoader = NULL;
1717}
1718
1719
1720/*
1721 * Alternate version of dvmResolveClass for use with verification and
1722 * optimization. Performs access checks on every resolve, and refuses
1723 * to acknowledge the existence of classes defined in more than one DEX
1724 * file.
1725 *
1726 * Exceptions caused by failures are cleared before returning.
Andy McFadden62a75162009-04-17 17:23:37 -07001727 *
1728 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001729 */
Andy McFadden62a75162009-04-17 17:23:37 -07001730ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
1731 VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001732{
1733 DvmDex* pDvmDex = referrer->pDvmDex;
1734 ClassObject* resClass;
1735
1736 /*
1737 * Check the table first. If not there, do the lookup by name.
1738 */
1739 resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
1740 if (resClass == NULL) {
1741 const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
1742 if (className[0] != '\0' && className[1] == '\0') {
1743 /* primitive type */
1744 resClass = dvmFindPrimitiveClass(className[0]);
1745 } else {
1746 resClass = dvmFindClassNoInit(className, referrer->classLoader);
1747 }
1748 if (resClass == NULL) {
1749 /* not found, exception should be raised */
1750 LOGV("DexOpt: class %d (%s) not found\n",
1751 classIdx,
1752 dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
Andy McFadden686e1e22009-05-26 16:56:30 -07001753 if (pFailure != NULL) {
1754 /* dig through the wrappers to find the original failure */
1755 Object* excep = dvmGetException(dvmThreadSelf());
1756 while (true) {
1757 Object* cause = dvmGetExceptionCause(excep);
1758 if (cause == NULL)
1759 break;
1760 excep = cause;
1761 }
1762 if (strcmp(excep->clazz->descriptor,
1763 "Ljava/lang/IncompatibleClassChangeError;") == 0)
1764 {
1765 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
1766 } else {
1767 *pFailure = VERIFY_ERROR_NO_CLASS;
1768 }
1769 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001770 dvmClearOptException(dvmThreadSelf());
1771 return NULL;
1772 }
1773
1774 /*
1775 * Add it to the resolved table so we're faster on the next lookup.
1776 */
1777 dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
1778 }
1779
1780 /* multiple definitions? */
1781 if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
1782 LOGI("DexOpt: not resolving ambiguous class '%s'\n",
1783 resClass->descriptor);
Andy McFadden62a75162009-04-17 17:23:37 -07001784 if (pFailure != NULL)
1785 *pFailure = VERIFY_ERROR_NO_CLASS;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001786 return NULL;
1787 }
1788
1789 /* access allowed? */
1790 tweakLoader(referrer, resClass);
1791 bool allowed = dvmCheckClassAccess(referrer, resClass);
1792 untweakLoader(referrer, resClass);
1793 if (!allowed) {
1794 LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
1795 referrer->descriptor, resClass->descriptor);
Andy McFadden62a75162009-04-17 17:23:37 -07001796 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07001797 *pFailure = VERIFY_ERROR_ACCESS_CLASS;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001798 return NULL;
1799 }
1800
1801 return resClass;
1802}
1803
1804/*
1805 * Alternate version of dvmResolveInstField().
Andy McFadden62a75162009-04-17 17:23:37 -07001806 *
1807 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001808 */
Andy McFadden62a75162009-04-17 17:23:37 -07001809InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
1810 VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001811{
1812 DvmDex* pDvmDex = referrer->pDvmDex;
1813 InstField* resField;
1814
1815 resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
1816 if (resField == NULL) {
1817 const DexFieldId* pFieldId;
1818 ClassObject* resClass;
1819
1820 pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
1821
1822 /*
1823 * Find the field's class.
1824 */
Andy McFadden62a75162009-04-17 17:23:37 -07001825 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001826 if (resClass == NULL) {
1827 //dvmClearOptException(dvmThreadSelf());
1828 assert(!dvmCheckException(dvmThreadSelf()));
Andy McFaddenb51ea112009-05-08 16:50:17 -07001829 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001830 return NULL;
1831 }
1832
Andy McFadden1d9206d2009-07-15 14:34:49 -07001833 resField = (InstField*)dvmFindFieldHier(resClass,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001834 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
1835 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
1836 if (resField == NULL) {
1837 LOGD("DexOpt: couldn't find field %s.%s\n",
1838 resClass->descriptor,
1839 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
Andy McFadden62a75162009-04-17 17:23:37 -07001840 if (pFailure != NULL)
1841 *pFailure = VERIFY_ERROR_NO_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001842 return NULL;
1843 }
Andy McFadden1d9206d2009-07-15 14:34:49 -07001844 if (dvmIsStaticField(&resField->field)) {
1845 LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
1846 resClass->descriptor,
1847 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
1848 if (pFailure != NULL)
1849 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
1850 return NULL;
1851 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001852
1853 /*
1854 * Add it to the resolved table so we're faster on the next lookup.
1855 */
1856 dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
1857 }
1858
1859 /* access allowed? */
1860 tweakLoader(referrer, resField->field.clazz);
1861 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
1862 untweakLoader(referrer, resField->field.clazz);
1863 if (!allowed) {
1864 LOGI("DexOpt: access denied from %s to field %s.%s\n",
1865 referrer->descriptor, resField->field.clazz->descriptor,
1866 resField->field.name);
Andy McFadden62a75162009-04-17 17:23:37 -07001867 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07001868 *pFailure = VERIFY_ERROR_ACCESS_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001869 return NULL;
1870 }
1871
1872 return resField;
1873}
1874
1875/*
1876 * Alternate version of dvmResolveStaticField().
1877 *
1878 * Does not force initialization of the resolved field's class.
Andy McFadden62a75162009-04-17 17:23:37 -07001879 *
1880 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001881 */
Andy McFadden62a75162009-04-17 17:23:37 -07001882StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
1883 VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001884{
1885 DvmDex* pDvmDex = referrer->pDvmDex;
1886 StaticField* resField;
1887
1888 resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
1889 if (resField == NULL) {
1890 const DexFieldId* pFieldId;
1891 ClassObject* resClass;
1892
1893 pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
1894
1895 /*
1896 * Find the field's class.
1897 */
Andy McFadden62a75162009-04-17 17:23:37 -07001898 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001899 if (resClass == NULL) {
1900 //dvmClearOptException(dvmThreadSelf());
1901 assert(!dvmCheckException(dvmThreadSelf()));
Andy McFaddenb51ea112009-05-08 16:50:17 -07001902 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001903 return NULL;
1904 }
1905
Andy McFadden1d9206d2009-07-15 14:34:49 -07001906 resField = (StaticField*)dvmFindFieldHier(resClass,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001907 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
1908 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
1909 if (resField == NULL) {
1910 LOGD("DexOpt: couldn't find static field\n");
Andy McFadden62a75162009-04-17 17:23:37 -07001911 if (pFailure != NULL)
1912 *pFailure = VERIFY_ERROR_NO_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001913 return NULL;
1914 }
Andy McFadden1d9206d2009-07-15 14:34:49 -07001915 if (!dvmIsStaticField(&resField->field)) {
1916 LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
1917 resClass->descriptor,
1918 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
1919 if (pFailure != NULL)
1920 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
1921 return NULL;
1922 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001923
1924 /*
1925 * Add it to the resolved table so we're faster on the next lookup.
1926 *
1927 * We can only do this if we're in "dexopt", because the presence
1928 * of a valid value in the resolution table implies that the class
1929 * containing the static field has been initialized.
1930 */
1931 if (gDvm.optimizing)
1932 dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
1933 }
1934
1935 /* access allowed? */
1936 tweakLoader(referrer, resField->field.clazz);
1937 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
1938 untweakLoader(referrer, resField->field.clazz);
1939 if (!allowed) {
1940 LOGI("DexOpt: access denied from %s to field %s.%s\n",
1941 referrer->descriptor, resField->field.clazz->descriptor,
1942 resField->field.name);
Andy McFadden62a75162009-04-17 17:23:37 -07001943 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07001944 *pFailure = VERIFY_ERROR_ACCESS_FIELD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001945 return NULL;
1946 }
1947
1948 return resField;
1949}
1950
1951
1952/*
1953 * Rewrite an iget/iput instruction. These all have the form:
1954 * op vA, vB, field@CCCC
1955 *
1956 * Where vA holds the value, vB holds the object reference, and CCCC is
1957 * the field reference constant pool offset. We want to replace CCCC
1958 * with the byte offset from the start of the object.
1959 *
1960 * "clazz" is the referring class. We need this because we verify
1961 * access rights here.
1962 */
1963static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
1964{
1965 ClassObject* clazz = method->clazz;
1966 u2 fieldIdx = insns[1];
1967 InstField* field;
1968 int byteOffset;
1969
Andy McFadden62a75162009-04-17 17:23:37 -07001970 field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001971 if (field == NULL) {
1972 LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
1973 fieldIdx, (int) (insns - method->insns), clazz->descriptor,
1974 method->name);
1975 return;
1976 }
1977
1978 if (field->byteOffset >= 65536) {
1979 LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
1980 return;
1981 }
1982
1983 insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
1984 insns[1] = (u2) field->byteOffset;
1985 LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
1986 field->field.clazz->descriptor, field->field.name,
1987 field->byteOffset);
1988}
1989
1990/*
1991 * Alternate version of dvmResolveMethod().
1992 *
1993 * Doesn't throw exceptions, and checks access on every lookup.
Andy McFadden62a75162009-04-17 17:23:37 -07001994 *
1995 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001996 */
1997Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
Andy McFadden62a75162009-04-17 17:23:37 -07001998 MethodType methodType, VerifyError* pFailure)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001999{
2000 DvmDex* pDvmDex = referrer->pDvmDex;
2001 Method* resMethod;
2002
Andy McFadden1d9206d2009-07-15 14:34:49 -07002003 assert(methodType == METHOD_DIRECT ||
2004 methodType == METHOD_VIRTUAL ||
2005 methodType == METHOD_STATIC);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002006
2007 LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
2008 referrer->descriptor);
2009
2010 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
2011 if (resMethod == NULL) {
2012 const DexMethodId* pMethodId;
2013 ClassObject* resClass;
2014
2015 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
2016
Andy McFadden62a75162009-04-17 17:23:37 -07002017 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002018 if (resClass == NULL) {
Andy McFaddenb51ea112009-05-08 16:50:17 -07002019 /*
2020 * Can't find the class that the method is a part of, or don't
2021 * have permission to access the class.
2022 */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002023 LOGV("DexOpt: can't find called method's class (?.%s)\n",
2024 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
Andy McFaddenb51ea112009-05-08 16:50:17 -07002025 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002026 return NULL;
2027 }
2028 if (dvmIsInterfaceClass(resClass)) {
2029 /* method is part of an interface; this is wrong method for that */
2030 LOGW("DexOpt: method is in an interface\n");
Andy McFadden62a75162009-04-17 17:23:37 -07002031 if (pFailure != NULL)
2032 *pFailure = VERIFY_ERROR_GENERIC;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002033 return NULL;
2034 }
2035
2036 /*
2037 * We need to chase up the class hierarchy to find methods defined
2038 * in super-classes. (We only want to check the current class
2039 * if we're looking for a constructor.)
2040 */
2041 DexProto proto;
2042 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
2043
2044 if (methodType == METHOD_DIRECT) {
2045 resMethod = dvmFindDirectMethod(resClass,
2046 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002047 } else {
Andy McFadden1d9206d2009-07-15 14:34:49 -07002048 /* METHOD_STATIC or METHOD_VIRTUAL */
2049 resMethod = dvmFindMethodHier(resClass,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002050 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
2051 }
2052
2053 if (resMethod == NULL) {
2054 LOGV("DexOpt: couldn't find method '%s'\n",
2055 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
Andy McFadden62a75162009-04-17 17:23:37 -07002056 if (pFailure != NULL)
2057 *pFailure = VERIFY_ERROR_NO_METHOD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002058 return NULL;
2059 }
Andy McFadden1d9206d2009-07-15 14:34:49 -07002060 if (methodType == METHOD_STATIC) {
2061 if (!dvmIsStaticMethod(resMethod)) {
2062 LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
2063 resClass->descriptor, resMethod->name);
2064 if (pFailure != NULL)
2065 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
2066 return NULL;
2067 }
2068 } else if (methodType == METHOD_VIRTUAL) {
2069 if (dvmIsStaticMethod(resMethod)) {
2070 LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
2071 resClass->descriptor, resMethod->name);
2072 if (pFailure != NULL)
2073 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
2074 return NULL;
2075 }
2076 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002077
2078 /* see if this is a pure-abstract method */
2079 if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
2080 LOGW("DexOpt: pure-abstract method '%s' in %s\n",
2081 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
2082 resClass->descriptor);
Andy McFadden62a75162009-04-17 17:23:37 -07002083 if (pFailure != NULL)
2084 *pFailure = VERIFY_ERROR_GENERIC;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002085 return NULL;
2086 }
2087
2088 /*
2089 * Add it to the resolved table so we're faster on the next lookup.
2090 *
2091 * We can only do this for static methods if we're not in "dexopt",
2092 * because the presence of a valid value in the resolution table
2093 * implies that the class containing the static field has been
2094 * initialized.
2095 */
2096 if (methodType != METHOD_STATIC || gDvm.optimizing)
2097 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
2098 }
2099
2100 LOGVV("--- found method %d (%s.%s)\n",
2101 methodIdx, resMethod->clazz->descriptor, resMethod->name);
2102
2103 /* access allowed? */
2104 tweakLoader(referrer, resMethod->clazz);
2105 bool allowed = dvmCheckMethodAccess(referrer, resMethod);
2106 untweakLoader(referrer, resMethod->clazz);
2107 if (!allowed) {
2108 IF_LOGI() {
2109 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
2110 LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
2111 resMethod->clazz->descriptor, resMethod->name, desc,
2112 referrer->descriptor);
2113 free(desc);
2114 }
Andy McFadden62a75162009-04-17 17:23:37 -07002115 if (pFailure != NULL)
Andy McFaddenb51ea112009-05-08 16:50:17 -07002116 *pFailure = VERIFY_ERROR_ACCESS_METHOD;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002117 return NULL;
2118 }
2119
2120 return resMethod;
2121}
2122
2123/*
2124 * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
2125 * invoke-super/range. These all have the form:
2126 * op vAA, meth@BBBB, reg stuff @CCCC
2127 *
2128 * We want to replace the method constant pool index BBBB with the
2129 * vtable index.
2130 */
2131static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
2132{
2133 ClassObject* clazz = method->clazz;
2134 Method* baseMethod;
2135 u2 methodIdx = insns[1];
2136
Andy McFadden62a75162009-04-17 17:23:37 -07002137 baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002138 if (baseMethod == NULL) {
2139 LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
2140 methodIdx,
2141 (int) (insns - method->insns), clazz->descriptor,
2142 method->name);
2143 return false;
2144 }
2145
2146 assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
2147 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
2148 (insns[0] & 0xff) == OP_INVOKE_SUPER ||
2149 (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
2150
2151 /*
2152 * Note: Method->methodIndex is a u2 and is range checked during the
2153 * initial load.
2154 */
2155 insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
2156 insns[1] = baseMethod->methodIndex;
2157
2158 //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
2159 // method->clazz->descriptor, method->name,
2160 // baseMethod->clazz->descriptor, baseMethod->name);
2161
2162 return true;
2163}
2164
2165/*
2166 * Rewrite invoke-direct, which has the form:
2167 * op vAA, meth@BBBB, reg stuff @CCCC
2168 *
2169 * There isn't a lot we can do to make this faster, but in some situations
2170 * we can make it go away entirely.
2171 *
2172 * This must only be used when the invoked method does nothing and has
2173 * no return value (the latter being very important for verification).
2174 */
Andy McFaddenb0a05412009-11-19 10:23:41 -08002175static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002176{
2177 ClassObject* clazz = method->clazz;
2178 Method* calledMethod;
2179 u2 methodIdx = insns[1];
2180
Andy McFadden62a75162009-04-17 17:23:37 -07002181 calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002182 if (calledMethod == NULL) {
2183 LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
2184 methodIdx,
2185 (int) (insns - method->insns), clazz->descriptor,
2186 method->name);
2187 return false;
2188 }
2189
2190 /* TODO: verify that java.lang.Object() is actually empty! */
2191 if (calledMethod->clazz == gDvm.classJavaLangObject &&
2192 dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
2193 {
2194 /*
2195 * Replace with "empty" instruction. DO NOT disturb anything
2196 * else about it, as we want it to function the same as
2197 * OP_INVOKE_DIRECT when debugging is enabled.
2198 */
2199 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
2200 insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
2201
2202 //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
2203 // method->clazz->descriptor, method->name,
2204 // calledMethod->clazz->descriptor, calledMethod->name);
2205 }
2206
2207 return true;
2208}
2209
2210/*
2211 * Resolve an interface method reference.
2212 *
Andy McFadden62a75162009-04-17 17:23:37 -07002213 * No method access check here -- interface methods are always public.
2214 *
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002215 * Returns NULL if the method was not found. Does not throw an exception.
2216 */
2217Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
2218{
2219 DvmDex* pDvmDex = referrer->pDvmDex;
2220 Method* resMethod;
2221 int i;
2222
2223 LOGVV("--- resolving interface method %d (referrer=%s)\n",
2224 methodIdx, referrer->descriptor);
2225
2226 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
2227 if (resMethod == NULL) {
2228 const DexMethodId* pMethodId;
2229 ClassObject* resClass;
2230
2231 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
2232
Andy McFadden62a75162009-04-17 17:23:37 -07002233 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002234 if (resClass == NULL) {
2235 /* can't find the class that the method is a part of */
2236 dvmClearOptException(dvmThreadSelf());
2237 return NULL;
2238 }
2239 if (!dvmIsInterfaceClass(resClass)) {
2240 /* whoops */
2241 LOGI("Interface method not part of interface class\n");
2242 return NULL;
2243 }
2244
2245 const char* methodName =
2246 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
2247 DexProto proto;
2248 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
2249
2250 LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
2251 methodName, methodSig, resClass->descriptor);
2252 resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
2253 if (resMethod == NULL) {
2254 /* scan superinterfaces and superclass interfaces */
2255 LOGVV("+++ did not resolve immediately\n");
2256 for (i = 0; i < resClass->iftableCount; i++) {
2257 resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
2258 methodName, &proto);
2259 if (resMethod != NULL)
2260 break;
2261 }
2262
2263 if (resMethod == NULL) {
2264 LOGVV("+++ unable to resolve method %s\n", methodName);
2265 return NULL;
2266 }
2267 } else {
2268 LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
2269 resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
2270 }
2271
2272 /* we're expecting this to be abstract */
2273 if (!dvmIsAbstractMethod(resMethod)) {
2274 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
2275 LOGW("Found non-abstract interface method %s.%s %s\n",
2276 resMethod->clazz->descriptor, resMethod->name, desc);
2277 free(desc);
2278 return NULL;
2279 }
2280
2281 /*
2282 * Add it to the resolved table so we're faster on the next lookup.
2283 */
2284 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
2285 }
2286
2287 LOGVV("--- found interface method %d (%s.%s)\n",
2288 methodIdx, resMethod->clazz->descriptor, resMethod->name);
2289
2290 /* interface methods are always public; no need to check access */
2291
2292 return resMethod;
2293}
Andy McFaddenb0a05412009-11-19 10:23:41 -08002294
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002295/*
2296 * See if the method being called can be rewritten as an inline operation.
2297 * Works for invoke-virtual, invoke-direct, and invoke-static.
2298 *
2299 * Returns "true" if we replace it.
2300 */
2301static bool rewriteExecuteInline(Method* method, u2* insns,
2302 MethodType methodType, const InlineSub* inlineSubs)
2303{
2304 ClassObject* clazz = method->clazz;
2305 Method* calledMethod;
2306 u2 methodIdx = insns[1];
2307
2308 //return false;
2309
Andy McFadden62a75162009-04-17 17:23:37 -07002310 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002311 if (calledMethod == NULL) {
2312 LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
2313 return false;
2314 }
2315
2316 while (inlineSubs->method != NULL) {
2317 /*
2318 if (extra) {
2319 LOGI("comparing %p vs %p %s.%s %s\n",
2320 inlineSubs->method, calledMethod,
2321 inlineSubs->method->clazz->descriptor,
2322 inlineSubs->method->name,
2323 inlineSubs->method->signature);
2324 }
2325 */
2326 if (inlineSubs->method == calledMethod) {
2327 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
2328 (insns[0] & 0xff) == OP_INVOKE_STATIC ||
2329 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
2330 insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
2331 insns[1] = (u2) inlineSubs->inlineIdx;
2332
2333 //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
2334 // method->clazz->descriptor, method->name,
2335 // calledMethod->clazz->descriptor, calledMethod->name);
2336 return true;
2337 }
2338
2339 inlineSubs++;
2340 }
2341
2342 return false;
2343}
2344
Andy McFaddenb0a05412009-11-19 10:23:41 -08002345/*
2346 * See if the method being called can be rewritten as an inline operation.
2347 * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
2348 *
2349 * Returns "true" if we replace it.
2350 */
2351static bool rewriteExecuteInlineRange(Method* method, u2* insns,
2352 MethodType methodType, const InlineSub* inlineSubs)
2353{
2354 ClassObject* clazz = method->clazz;
2355 Method* calledMethod;
2356 u2 methodIdx = insns[1];
2357
2358 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
2359 if (calledMethod == NULL) {
2360 LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
2361 return false;
2362 }
2363
2364 while (inlineSubs->method != NULL) {
2365 if (inlineSubs->method == calledMethod) {
2366 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
2367 (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
2368 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
2369 insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
2370 insns[1] = (u2) inlineSubs->inlineIdx;
2371
2372 //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
2373 // method->clazz->descriptor, method->name,
2374 // calledMethod->clazz->descriptor, calledMethod->name);
2375 return true;
2376 }
2377
2378 inlineSubs++;
2379 }
2380
2381 return false;
2382}
2383