1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * Perform some simple bytecode optimizations, chiefly "quickening" of
19 * opcodes.
20 */
21 #include "Dalvik.h"
22 #include "libdex/InstrUtils.h"
23
24 #include <zlib.h>
25
26 #include <stdlib.h>
27
28 /*
29 * Virtual/direct calls to "method" are replaced with an execute-inline
30 * instruction with index "idx".
31 */
32 struct InlineSub {
33 Method* method;
34 int inlineIdx;
35 };
36
37
38 /* fwd */
39 static void optimizeMethod(Method* method, bool essentialOnly);
40 static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
41 OpCode volatileOpc);
42 static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc);
43 static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
44 static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
45 static bool rewriteExecuteInline(Method* method, u2* insns,
46 MethodType methodType);
47 static bool rewriteExecuteInlineRange(Method* method, u2* insns,
48 MethodType methodType);
49
50
51 /*
52 * Create a table of inline substitutions.
53 *
54 * TODO: this is currently just a linear array. We will want to put this
55 * into a hash table as the list size increases.
56 */
dvmCreateInlineSubsTable(void)57 InlineSub* dvmCreateInlineSubsTable(void)
58 {
59 const InlineOperation* ops = dvmGetInlineOpsTable();
60 const int count = dvmGetInlineOpsTableLength();
61 InlineSub* table;
62 Method* method;
63 ClassObject* clazz;
64 int i, tableIndex;
65
66 /*
67 * Allocate for optimism: one slot per entry, plus an end-of-list marker.
68 */
69 table = malloc(sizeof(InlineSub) * (count+1));
70
71 tableIndex = 0;
72 for (i = 0; i < count; i++) {
73 clazz = dvmFindClassNoInit(ops[i].classDescriptor, NULL);
74 if (clazz == NULL) {
75 LOGV("DexOpt: can't inline for class '%s': not found\n",
76 ops[i].classDescriptor);
77 dvmClearOptException(dvmThreadSelf());
78 } else {
79 /*
80 * Method could be virtual or direct. Try both. Don't use
81 * the "hier" versions.
82 */
83 method = dvmFindDirectMethodByDescriptor(clazz, ops[i].methodName,
84 ops[i].methodSignature);
85 if (method == NULL)
86 method = dvmFindVirtualMethodByDescriptor(clazz, ops[i].methodName,
87 ops[i].methodSignature);
88 if (method == NULL) {
89 LOGW("DexOpt: can't inline %s.%s %s: method not found\n",
90 ops[i].classDescriptor, ops[i].methodName,
91 ops[i].methodSignature);
92 } else {
93 if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) {
94 LOGW("DexOpt: WARNING: inline op on non-final class/method "
95 "%s.%s\n",
96 clazz->descriptor, method->name);
97 /* fail? */
98 }
99 if (dvmIsSynchronizedMethod(method) ||
100 dvmIsDeclaredSynchronizedMethod(method))
101 {
102 LOGW("DexOpt: WARNING: inline op on synchronized method "
103 "%s.%s\n",
104 clazz->descriptor, method->name);
105 /* fail? */
106 }
107
108 table[tableIndex].method = method;
109 table[tableIndex].inlineIdx = i;
110 tableIndex++;
111
112 LOGV("DexOpt: will inline %d: %s.%s %s\n", i,
113 ops[i].classDescriptor, ops[i].methodName,
114 ops[i].methodSignature);
115 }
116 }
117 }
118
119 /* mark end of table */
120 table[tableIndex].method = NULL;
121 LOGV("DexOpt: inline table has %d entries\n", tableIndex);
122
123 return table;
124 }
125
126 /*
127 * Release inline sub data structure.
128 */
dvmFreeInlineSubsTable(InlineSub * inlineSubs)129 void dvmFreeInlineSubsTable(InlineSub* inlineSubs)
130 {
131 free(inlineSubs);
132 }
133
134
135 /*
136 * Optimize the specified class.
137 *
138 * If "essentialOnly" is true, we only do essential optimizations. For
139 * example, accesses to volatile 64-bit fields must be replaced with
140 * "-wide-volatile" instructions or the program could behave incorrectly.
141 * (Skipping non-essential optimizations makes us a little bit faster, and
142 * more importantly avoids dirtying DEX pages.)
143 */
dvmOptimizeClass(ClassObject * clazz,bool essentialOnly)144 void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly)
145 {
146 int i;
147
148 for (i = 0; i < clazz->directMethodCount; i++) {
149 optimizeMethod(&clazz->directMethods[i], essentialOnly);
150 }
151 for (i = 0; i < clazz->virtualMethodCount; i++) {
152 optimizeMethod(&clazz->virtualMethods[i], essentialOnly);
153 }
154 }
155
156 /*
157 * Optimize instructions in a method.
158 *
159 * This does a single pass through the code, examining each instruction.
160 *
161 * This is not expected to fail if the class was successfully verified.
162 * The only significant failure modes occur when an "essential" update fails,
163 * but we can't generally identify those: if we can't look up a field,
164 * we can't know if the field access was supposed to be handled as volatile.
165 *
166 * Instead, we give it our best effort, and hope for the best. For 100%
167 * reliability, only optimize a class after verification succeeds.
168 */
optimizeMethod(Method * method,bool essentialOnly)169 static void optimizeMethod(Method* method, bool essentialOnly)
170 {
171 u4 insnsSize;
172 u2* insns;
173 u2 inst;
174
175 if (!gDvm.optimizing && !essentialOnly) {
176 /* unexpected; will force copy-on-write of a lot of pages */
177 LOGD("NOTE: doing full bytecode optimization outside dexopt\n");
178 }
179
180 if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
181 return;
182
183 insns = (u2*) method->insns;
184 assert(insns != NULL);
185 insnsSize = dvmGetMethodInsnsSize(method);
186
187 while (insnsSize > 0) {
188 OpCode quickOpc, volatileOpc = OP_NOP;
189 int width;
190 bool notMatched = false;
191
192 inst = *insns & 0xff;
193
194 /* "essential" substitutions, always checked */
195 switch (inst) {
196 case OP_IGET:
197 case OP_IGET_BOOLEAN:
198 case OP_IGET_BYTE:
199 case OP_IGET_CHAR:
200 case OP_IGET_SHORT:
201 quickOpc = OP_IGET_QUICK;
202 if (gDvm.dexOptForSmp)
203 volatileOpc = OP_IGET_VOLATILE;
204 goto rewrite_inst_field;
205 case OP_IGET_WIDE:
206 quickOpc = OP_IGET_WIDE_QUICK;
207 volatileOpc = OP_IGET_WIDE_VOLATILE;
208 goto rewrite_inst_field;
209 case OP_IGET_OBJECT:
210 quickOpc = OP_IGET_OBJECT_QUICK;
211 if (gDvm.dexOptForSmp)
212 volatileOpc = OP_IGET_OBJECT_VOLATILE;
213 goto rewrite_inst_field;
214 case OP_IPUT:
215 case OP_IPUT_BOOLEAN:
216 case OP_IPUT_BYTE:
217 case OP_IPUT_CHAR:
218 case OP_IPUT_SHORT:
219 quickOpc = OP_IPUT_QUICK;
220 if (gDvm.dexOptForSmp)
221 volatileOpc = OP_IPUT_VOLATILE;
222 goto rewrite_inst_field;
223 case OP_IPUT_WIDE:
224 quickOpc = OP_IPUT_WIDE_QUICK;
225 volatileOpc = OP_IPUT_WIDE_VOLATILE;
226 goto rewrite_inst_field;
227 case OP_IPUT_OBJECT:
228 quickOpc = OP_IPUT_OBJECT_QUICK;
229 if (gDvm.dexOptForSmp)
230 volatileOpc = OP_IPUT_OBJECT_VOLATILE;
231 rewrite_inst_field:
232 if (essentialOnly)
233 quickOpc = OP_NOP;
234 if (quickOpc != OP_NOP || volatileOpc != OP_NOP)
235 rewriteInstField(method, insns, quickOpc, volatileOpc);
236 break;
237
238 case OP_SGET_WIDE:
239 volatileOpc = OP_SGET_WIDE_VOLATILE;
240 goto rewrite_static_field;
241 case OP_SPUT_WIDE:
242 volatileOpc = OP_SPUT_WIDE_VOLATILE;
243 rewrite_static_field:
244 rewriteStaticField(method, insns, volatileOpc);
245 break;
246 default:
247 notMatched = true;
248 break;
249 }
250
251 if (notMatched && gDvm.dexOptForSmp) {
252 /* additional "essential" substitutions for an SMP device */
253 switch (inst) {
254 case OP_SGET:
255 case OP_SGET_BOOLEAN:
256 case OP_SGET_BYTE:
257 case OP_SGET_CHAR:
258 case OP_SGET_SHORT:
259 volatileOpc = OP_SGET_VOLATILE;
260 goto rewrite_static_field2;
261 case OP_SGET_OBJECT:
262 volatileOpc = OP_SGET_OBJECT_VOLATILE;
263 goto rewrite_static_field2;
264 case OP_SPUT:
265 case OP_SPUT_BOOLEAN:
266 case OP_SPUT_BYTE:
267 case OP_SPUT_CHAR:
268 case OP_SPUT_SHORT:
269 volatileOpc = OP_SPUT_VOLATILE;
270 goto rewrite_static_field2;
271 case OP_SPUT_OBJECT:
272 volatileOpc = OP_SPUT_OBJECT_VOLATILE;
273 rewrite_static_field2:
274 rewriteStaticField(method, insns, volatileOpc);
275 notMatched = false;
276 break;
277 default:
278 assert(notMatched);
279 break;
280 }
281 }
282
283 /* non-essential substitutions */
284 if (notMatched && !essentialOnly) {
285 switch (inst) {
286 case OP_INVOKE_VIRTUAL:
287 if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
288 rewriteVirtualInvoke(method, insns,
289 OP_INVOKE_VIRTUAL_QUICK);
290 }
291 break;
292 case OP_INVOKE_VIRTUAL_RANGE:
293 if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
294 rewriteVirtualInvoke(method, insns,
295 OP_INVOKE_VIRTUAL_QUICK_RANGE);
296 }
297 break;
298 case OP_INVOKE_SUPER:
299 rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
300 break;
301 case OP_INVOKE_SUPER_RANGE:
302 rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE);
303 break;
304
305 case OP_INVOKE_DIRECT:
306 if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) {
307 rewriteEmptyDirectInvoke(method, insns);
308 }
309 break;
310 case OP_INVOKE_DIRECT_RANGE:
311 rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
312 break;
313
314 case OP_INVOKE_STATIC:
315 rewriteExecuteInline(method, insns, METHOD_STATIC);
316 break;
317 case OP_INVOKE_STATIC_RANGE:
318 rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
319 break;
320
321 default:
322 /* nothing to do for this instruction */
323 ;
324 }
325 }
326
327 width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
328 assert(width > 0);
329
330 insns += width;
331 insnsSize -= width;
332 }
333
334 assert(insnsSize == 0);
335 }
336
337 /*
338 * Update a 16-bit code unit in "meth".
339 */
updateCode(const Method * meth,u2 * ptr,u2 newVal)340 static inline void updateCode(const Method* meth, u2* ptr, u2 newVal)
341 {
342 if (gDvm.optimizing) {
343 /* dexopt time, alter the output directly */
344 *ptr = newVal;
345 } else {
346 /* runtime, toggle the page read/write status */
347 dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal);
348 }
349 }
350
351 /*
352 * If "referrer" and "resClass" don't come from the same DEX file, and
353 * the DEX we're working on is not destined for the bootstrap class path,
354 * tweak the class loader so package-access checks work correctly.
355 *
356 * Only do this if we're doing pre-verification or optimization.
357 */
tweakLoader(ClassObject * referrer,ClassObject * resClass)358 static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
359 {
360 if (!gDvm.optimizing)
361 return;
362 assert(referrer->classLoader == NULL);
363 assert(resClass->classLoader == NULL);
364
365 if (!gDvm.optimizingBootstrapClass) {
366 /* class loader for an array class comes from element type */
367 if (dvmIsArrayClass(resClass))
368 resClass = resClass->elementClass;
369 if (referrer->pDvmDex != resClass->pDvmDex)
370 resClass->classLoader = (Object*) 0xdead3333;
371 }
372 }
373
374 /*
375 * Undo the effects of tweakLoader.
376 */
untweakLoader(ClassObject * referrer,ClassObject * resClass)377 static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
378 {
379 if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
380 return;
381
382 if (dvmIsArrayClass(resClass))
383 resClass = resClass->elementClass;
384 resClass->classLoader = NULL;
385 }
386
387
388 /*
389 * Alternate version of dvmResolveClass for use with verification and
390 * optimization. Performs access checks on every resolve, and refuses
391 * to acknowledge the existence of classes defined in more than one DEX
392 * file.
393 *
394 * Exceptions caused by failures are cleared before returning.
395 *
396 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
397 */
dvmOptResolveClass(ClassObject * referrer,u4 classIdx,VerifyError * pFailure)398 ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
399 VerifyError* pFailure)
400 {
401 DvmDex* pDvmDex = referrer->pDvmDex;
402 ClassObject* resClass;
403
404 /*
405 * Check the table first. If not there, do the lookup by name.
406 */
407 resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
408 if (resClass == NULL) {
409 const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
410 if (className[0] != '\0' && className[1] == '\0') {
411 /* primitive type */
412 resClass = dvmFindPrimitiveClass(className[0]);
413 } else {
414 resClass = dvmFindClassNoInit(className, referrer->classLoader);
415 }
416 if (resClass == NULL) {
417 /* not found, exception should be raised */
418 LOGV("DexOpt: class %d (%s) not found\n",
419 classIdx,
420 dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
421 if (pFailure != NULL) {
422 /* dig through the wrappers to find the original failure */
423 Object* excep = dvmGetException(dvmThreadSelf());
424 while (true) {
425 Object* cause = dvmGetExceptionCause(excep);
426 if (cause == NULL)
427 break;
428 excep = cause;
429 }
430 if (strcmp(excep->clazz->descriptor,
431 "Ljava/lang/IncompatibleClassChangeError;") == 0)
432 {
433 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
434 } else {
435 *pFailure = VERIFY_ERROR_NO_CLASS;
436 }
437 }
438 dvmClearOptException(dvmThreadSelf());
439 return NULL;
440 }
441
442 /*
443 * Add it to the resolved table so we're faster on the next lookup.
444 */
445 dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
446 }
447
448 /* multiple definitions? */
449 if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
450 LOGI("DexOpt: not resolving ambiguous class '%s'\n",
451 resClass->descriptor);
452 if (pFailure != NULL)
453 *pFailure = VERIFY_ERROR_NO_CLASS;
454 return NULL;
455 }
456
457 /* access allowed? */
458 tweakLoader(referrer, resClass);
459 bool allowed = dvmCheckClassAccess(referrer, resClass);
460 untweakLoader(referrer, resClass);
461 if (!allowed) {
462 LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
463 referrer->descriptor, resClass->descriptor);
464 if (pFailure != NULL)
465 *pFailure = VERIFY_ERROR_ACCESS_CLASS;
466 return NULL;
467 }
468
469 return resClass;
470 }
471
472 /*
473 * Alternate version of dvmResolveInstField().
474 *
475 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
476 */
dvmOptResolveInstField(ClassObject * referrer,u4 ifieldIdx,VerifyError * pFailure)477 InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
478 VerifyError* pFailure)
479 {
480 DvmDex* pDvmDex = referrer->pDvmDex;
481 InstField* resField;
482
483 resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
484 if (resField == NULL) {
485 const DexFieldId* pFieldId;
486 ClassObject* resClass;
487
488 pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
489
490 /*
491 * Find the field's class.
492 */
493 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
494 if (resClass == NULL) {
495 //dvmClearOptException(dvmThreadSelf());
496 assert(!dvmCheckException(dvmThreadSelf()));
497 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
498 return NULL;
499 }
500
501 resField = (InstField*)dvmFindFieldHier(resClass,
502 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
503 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
504 if (resField == NULL) {
505 LOGD("DexOpt: couldn't find field %s.%s\n",
506 resClass->descriptor,
507 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
508 if (pFailure != NULL)
509 *pFailure = VERIFY_ERROR_NO_FIELD;
510 return NULL;
511 }
512 if (dvmIsStaticField(&resField->field)) {
513 LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
514 resClass->descriptor,
515 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
516 if (pFailure != NULL)
517 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
518 return NULL;
519 }
520
521 /*
522 * Add it to the resolved table so we're faster on the next lookup.
523 */
524 dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
525 }
526
527 /* access allowed? */
528 tweakLoader(referrer, resField->field.clazz);
529 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
530 untweakLoader(referrer, resField->field.clazz);
531 if (!allowed) {
532 LOGI("DexOpt: access denied from %s to field %s.%s\n",
533 referrer->descriptor, resField->field.clazz->descriptor,
534 resField->field.name);
535 if (pFailure != NULL)
536 *pFailure = VERIFY_ERROR_ACCESS_FIELD;
537 return NULL;
538 }
539
540 return resField;
541 }
542
543 /*
544 * Alternate version of dvmResolveStaticField().
545 *
546 * Does not force initialization of the resolved field's class.
547 *
548 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
549 */
dvmOptResolveStaticField(ClassObject * referrer,u4 sfieldIdx,VerifyError * pFailure)550 StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
551 VerifyError* pFailure)
552 {
553 DvmDex* pDvmDex = referrer->pDvmDex;
554 StaticField* resField;
555
556 resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
557 if (resField == NULL) {
558 const DexFieldId* pFieldId;
559 ClassObject* resClass;
560
561 pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
562
563 /*
564 * Find the field's class.
565 */
566 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
567 if (resClass == NULL) {
568 //dvmClearOptException(dvmThreadSelf());
569 assert(!dvmCheckException(dvmThreadSelf()));
570 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
571 return NULL;
572 }
573
574 resField = (StaticField*)dvmFindFieldHier(resClass,
575 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
576 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
577 if (resField == NULL) {
578 LOGD("DexOpt: couldn't find static field\n");
579 if (pFailure != NULL)
580 *pFailure = VERIFY_ERROR_NO_FIELD;
581 return NULL;
582 }
583 if (!dvmIsStaticField(&resField->field)) {
584 LOGD("DexOpt: wanted static, got instance for field %s.%s\n",
585 resClass->descriptor,
586 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
587 if (pFailure != NULL)
588 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
589 return NULL;
590 }
591
592 /*
593 * Add it to the resolved table so we're faster on the next lookup.
594 *
595 * We can only do this if we're in "dexopt", because the presence
596 * of a valid value in the resolution table implies that the class
597 * containing the static field has been initialized.
598 */
599 if (gDvm.optimizing)
600 dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
601 }
602
603 /* access allowed? */
604 tweakLoader(referrer, resField->field.clazz);
605 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
606 untweakLoader(referrer, resField->field.clazz);
607 if (!allowed) {
608 LOGI("DexOpt: access denied from %s to field %s.%s\n",
609 referrer->descriptor, resField->field.clazz->descriptor,
610 resField->field.name);
611 if (pFailure != NULL)
612 *pFailure = VERIFY_ERROR_ACCESS_FIELD;
613 return NULL;
614 }
615
616 return resField;
617 }
618
619
620 /*
621 * Rewrite an iget/iput instruction. These all have the form:
622 * op vA, vB, field@CCCC
623 *
624 * Where vA holds the value, vB holds the object reference, and CCCC is
625 * the field reference constant pool offset. For a non-volatile field,
626 * we want to replace the opcode with "quickOpc" and replace CCCC with
627 * the byte offset from the start of the object. For a volatile field,
628 * we just want to replace the opcode with "volatileOpc".
629 *
630 * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile
631 * field. If "quickOpc" is OP_NOP, and this is a non-volatile field,
632 * we don't do anything.
633 *
634 * "method" is the referring method.
635 */
rewriteInstField(Method * method,u2 * insns,OpCode quickOpc,OpCode volatileOpc)636 static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
637 OpCode volatileOpc)
638 {
639 ClassObject* clazz = method->clazz;
640 u2 fieldIdx = insns[1];
641 InstField* instField;
642
643 instField = dvmOptResolveInstField(clazz, fieldIdx, NULL);
644 if (instField == NULL) {
645 LOGI("DexOpt: unable to optimize instance field ref "
646 "0x%04x at 0x%02x in %s.%s\n",
647 fieldIdx, (int) (insns - method->insns), clazz->descriptor,
648 method->name);
649 return false;
650 }
651
652 if (instField->byteOffset >= 65536) {
653 LOGI("DexOpt: field offset exceeds 64K (%d)\n", instField->byteOffset);
654 return false;
655 }
656
657 if (volatileOpc != OP_NOP && dvmIsVolatileField(&instField->field)) {
658 updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
659 LOGV("DexOpt: rewrote ifield access %s.%s --> volatile\n",
660 instField->field.clazz->descriptor, instField->field.name);
661 } else if (quickOpc != OP_NOP) {
662 updateCode(method, insns, (insns[0] & 0xff00) | (u2) quickOpc);
663 updateCode(method, insns+1, (u2) instField->byteOffset);
664 LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n",
665 instField->field.clazz->descriptor, instField->field.name,
666 instField->byteOffset);
667 } else {
668 LOGV("DexOpt: no rewrite of ifield access %s.%s\n",
669 instField->field.clazz->descriptor, instField->field.name);
670 }
671
672 return true;
673 }
674
675 /*
676 * Rewrite an sget/sput instruction. These all have the form:
677 * op vAA, field@BBBB
678 *
679 * Where vAA holds the value, and BBBB is the field reference constant
680 * pool offset. There is no "quick" form of static field accesses, so
681 * this is only useful for volatile fields.
682 *
683 * "method" is the referring method.
684 */
rewriteStaticField(Method * method,u2 * insns,OpCode volatileOpc)685 static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc)
686 {
687 ClassObject* clazz = method->clazz;
688 u2 fieldIdx = insns[1];
689 StaticField* staticField;
690
691 assert(volatileOpc != OP_NOP);
692
693 staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL);
694 if (staticField == NULL) {
695 LOGI("DexOpt: unable to optimize static field ref "
696 "0x%04x at 0x%02x in %s.%s\n",
697 fieldIdx, (int) (insns - method->insns), clazz->descriptor,
698 method->name);
699 return false;
700 }
701
702 if (dvmIsVolatileField(&staticField->field)) {
703 updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
704 LOGV("DexOpt: rewrote sfield access %s.%s --> volatile\n",
705 staticField->field.clazz->descriptor, staticField->field.name);
706 }
707
708 return true;
709 }
710
711 /*
712 * Alternate version of dvmResolveMethod().
713 *
714 * Doesn't throw exceptions, and checks access on every lookup.
715 *
716 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
717 */
dvmOptResolveMethod(ClassObject * referrer,u4 methodIdx,MethodType methodType,VerifyError * pFailure)718 Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
719 MethodType methodType, VerifyError* pFailure)
720 {
721 DvmDex* pDvmDex = referrer->pDvmDex;
722 Method* resMethod;
723
724 assert(methodType == METHOD_DIRECT ||
725 methodType == METHOD_VIRTUAL ||
726 methodType == METHOD_STATIC);
727
728 LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
729 referrer->descriptor);
730
731 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
732 if (resMethod == NULL) {
733 const DexMethodId* pMethodId;
734 ClassObject* resClass;
735
736 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
737
738 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
739 if (resClass == NULL) {
740 /*
741 * Can't find the class that the method is a part of, or don't
742 * have permission to access the class.
743 */
744 LOGV("DexOpt: can't find called method's class (?.%s)\n",
745 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
746 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
747 return NULL;
748 }
749 if (dvmIsInterfaceClass(resClass)) {
750 /* method is part of an interface; this is wrong method for that */
751 LOGW("DexOpt: method is in an interface\n");
752 if (pFailure != NULL)
753 *pFailure = VERIFY_ERROR_GENERIC;
754 return NULL;
755 }
756
757 /*
758 * We need to chase up the class hierarchy to find methods defined
759 * in super-classes. (We only want to check the current class
760 * if we're looking for a constructor.)
761 */
762 DexProto proto;
763 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
764
765 if (methodType == METHOD_DIRECT) {
766 resMethod = dvmFindDirectMethod(resClass,
767 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
768 } else {
769 /* METHOD_STATIC or METHOD_VIRTUAL */
770 resMethod = dvmFindMethodHier(resClass,
771 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
772 }
773
774 if (resMethod == NULL) {
775 LOGV("DexOpt: couldn't find method '%s'\n",
776 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
777 if (pFailure != NULL)
778 *pFailure = VERIFY_ERROR_NO_METHOD;
779 return NULL;
780 }
781 if (methodType == METHOD_STATIC) {
782 if (!dvmIsStaticMethod(resMethod)) {
783 LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
784 resClass->descriptor, resMethod->name);
785 if (pFailure != NULL)
786 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
787 return NULL;
788 }
789 } else if (methodType == METHOD_VIRTUAL) {
790 if (dvmIsStaticMethod(resMethod)) {
791 LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
792 resClass->descriptor, resMethod->name);
793 if (pFailure != NULL)
794 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
795 return NULL;
796 }
797 }
798
799 /* see if this is a pure-abstract method */
800 if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
801 LOGW("DexOpt: pure-abstract method '%s' in %s\n",
802 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
803 resClass->descriptor);
804 if (pFailure != NULL)
805 *pFailure = VERIFY_ERROR_GENERIC;
806 return NULL;
807 }
808
809 /*
810 * Add it to the resolved table so we're faster on the next lookup.
811 *
812 * We can only do this for static methods if we're not in "dexopt",
813 * because the presence of a valid value in the resolution table
814 * implies that the class containing the static field has been
815 * initialized.
816 */
817 if (methodType != METHOD_STATIC || gDvm.optimizing)
818 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
819 }
820
821 LOGVV("--- found method %d (%s.%s)\n",
822 methodIdx, resMethod->clazz->descriptor, resMethod->name);
823
824 /* access allowed? */
825 tweakLoader(referrer, resMethod->clazz);
826 bool allowed = dvmCheckMethodAccess(referrer, resMethod);
827 untweakLoader(referrer, resMethod->clazz);
828 if (!allowed) {
829 IF_LOGI() {
830 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
831 LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
832 resMethod->clazz->descriptor, resMethod->name, desc,
833 referrer->descriptor);
834 free(desc);
835 }
836 if (pFailure != NULL)
837 *pFailure = VERIFY_ERROR_ACCESS_METHOD;
838 return NULL;
839 }
840
841 return resMethod;
842 }
843
844 /*
845 * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
846 * invoke-super/range. These all have the form:
847 * op vAA, meth@BBBB, reg stuff @CCCC
848 *
849 * We want to replace the method constant pool index BBBB with the
850 * vtable index.
851 */
rewriteVirtualInvoke(Method * method,u2 * insns,OpCode newOpc)852 static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
853 {
854 ClassObject* clazz = method->clazz;
855 Method* baseMethod;
856 u2 methodIdx = insns[1];
857
858 baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
859 if (baseMethod == NULL) {
860 LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
861 methodIdx,
862 (int) (insns - method->insns), clazz->descriptor,
863 method->name);
864 return false;
865 }
866
867 assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
868 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
869 (insns[0] & 0xff) == OP_INVOKE_SUPER ||
870 (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
871
872 /*
873 * Note: Method->methodIndex is a u2 and is range checked during the
874 * initial load.
875 */
876 updateCode(method, insns, (insns[0] & 0xff00) | (u2) newOpc);
877 updateCode(method, insns+1, baseMethod->methodIndex);
878
879 //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
880 // method->clazz->descriptor, method->name,
881 // baseMethod->clazz->descriptor, baseMethod->name);
882
883 return true;
884 }
885
886 /*
887 * Rewrite invoke-direct, which has the form:
888 * op vAA, meth@BBBB, reg stuff @CCCC
889 *
890 * There isn't a lot we can do to make this faster, but in some situations
891 * we can make it go away entirely.
892 *
893 * This must only be used when the invoked method does nothing and has
894 * no return value (the latter being very important for verification).
895 */
rewriteEmptyDirectInvoke(Method * method,u2 * insns)896 static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
897 {
898 ClassObject* clazz = method->clazz;
899 Method* calledMethod;
900 u2 methodIdx = insns[1];
901
902 calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
903 if (calledMethod == NULL) {
904 LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
905 methodIdx,
906 (int) (insns - method->insns), clazz->descriptor,
907 method->name);
908 return false;
909 }
910
911 /* TODO: verify that java.lang.Object() is actually empty! */
912 if (calledMethod->clazz == gDvm.classJavaLangObject &&
913 dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
914 {
915 /*
916 * Replace with "empty" instruction. DO NOT disturb anything
917 * else about it, as we want it to function the same as
918 * OP_INVOKE_DIRECT when debugging is enabled.
919 */
920 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
921 updateCode(method, insns,
922 (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY);
923
924 //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
925 // method->clazz->descriptor, method->name,
926 // calledMethod->clazz->descriptor, calledMethod->name);
927 }
928
929 return true;
930 }
931
932 /*
933 * Resolve an interface method reference.
934 *
935 * No method access check here -- interface methods are always public.
936 *
937 * Returns NULL if the method was not found. Does not throw an exception.
938 */
dvmOptResolveInterfaceMethod(ClassObject * referrer,u4 methodIdx)939 Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
940 {
941 DvmDex* pDvmDex = referrer->pDvmDex;
942 Method* resMethod;
943 int i;
944
945 LOGVV("--- resolving interface method %d (referrer=%s)\n",
946 methodIdx, referrer->descriptor);
947
948 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
949 if (resMethod == NULL) {
950 const DexMethodId* pMethodId;
951 ClassObject* resClass;
952
953 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
954
955 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
956 if (resClass == NULL) {
957 /* can't find the class that the method is a part of */
958 dvmClearOptException(dvmThreadSelf());
959 return NULL;
960 }
961 if (!dvmIsInterfaceClass(resClass)) {
962 /* whoops */
963 LOGI("Interface method not part of interface class\n");
964 return NULL;
965 }
966
967 const char* methodName =
968 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
969 DexProto proto;
970 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
971
972 LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
973 methodName, methodSig, resClass->descriptor);
974 resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
975 if (resMethod == NULL) {
976 /* scan superinterfaces and superclass interfaces */
977 LOGVV("+++ did not resolve immediately\n");
978 for (i = 0; i < resClass->iftableCount; i++) {
979 resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
980 methodName, &proto);
981 if (resMethod != NULL)
982 break;
983 }
984
985 if (resMethod == NULL) {
986 LOGVV("+++ unable to resolve method %s\n", methodName);
987 return NULL;
988 }
989 } else {
990 LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
991 resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
992 }
993
994 /* we're expecting this to be abstract */
995 if (!dvmIsAbstractMethod(resMethod)) {
996 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
997 LOGW("Found non-abstract interface method %s.%s %s\n",
998 resMethod->clazz->descriptor, resMethod->name, desc);
999 free(desc);
1000 return NULL;
1001 }
1002
1003 /*
1004 * Add it to the resolved table so we're faster on the next lookup.
1005 */
1006 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
1007 }
1008
1009 LOGVV("--- found interface method %d (%s.%s)\n",
1010 methodIdx, resMethod->clazz->descriptor, resMethod->name);
1011
1012 /* interface methods are always public; no need to check access */
1013
1014 return resMethod;
1015 }
1016
1017 /*
1018 * See if the method being called can be rewritten as an inline operation.
1019 * Works for invoke-virtual, invoke-direct, and invoke-static.
1020 *
1021 * Returns "true" if we replace it.
1022 */
rewriteExecuteInline(Method * method,u2 * insns,MethodType methodType)1023 static bool rewriteExecuteInline(Method* method, u2* insns,
1024 MethodType methodType)
1025 {
1026 const InlineSub* inlineSubs = gDvm.inlineSubs;
1027 ClassObject* clazz = method->clazz;
1028 Method* calledMethod;
1029 u2 methodIdx = insns[1];
1030
1031 //return false;
1032
1033 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
1034 if (calledMethod == NULL) {
1035 LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
1036 return false;
1037 }
1038
1039 while (inlineSubs->method != NULL) {
1040 /*
1041 if (extra) {
1042 LOGI("comparing %p vs %p %s.%s %s\n",
1043 inlineSubs->method, calledMethod,
1044 inlineSubs->method->clazz->descriptor,
1045 inlineSubs->method->name,
1046 inlineSubs->method->signature);
1047 }
1048 */
1049 if (inlineSubs->method == calledMethod) {
1050 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
1051 (insns[0] & 0xff) == OP_INVOKE_STATIC ||
1052 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
1053 updateCode(method, insns,
1054 (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE);
1055 updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
1056
1057 //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
1058 // method->clazz->descriptor, method->name,
1059 // calledMethod->clazz->descriptor, calledMethod->name);
1060 return true;
1061 }
1062
1063 inlineSubs++;
1064 }
1065
1066 return false;
1067 }
1068
1069 /*
1070 * See if the method being called can be rewritten as an inline operation.
1071 * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
1072 *
1073 * Returns "true" if we replace it.
1074 */
rewriteExecuteInlineRange(Method * method,u2 * insns,MethodType methodType)1075 static bool rewriteExecuteInlineRange(Method* method, u2* insns,
1076 MethodType methodType)
1077 {
1078 const InlineSub* inlineSubs = gDvm.inlineSubs;
1079 ClassObject* clazz = method->clazz;
1080 Method* calledMethod;
1081 u2 methodIdx = insns[1];
1082
1083 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
1084 if (calledMethod == NULL) {
1085 LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
1086 return false;
1087 }
1088
1089 while (inlineSubs->method != NULL) {
1090 if (inlineSubs->method == calledMethod) {
1091 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
1092 (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
1093 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
1094 updateCode(method, insns,
1095 (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE);
1096 updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
1097
1098 //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
1099 // method->clazz->descriptor, method->name,
1100 // calledMethod->clazz->descriptor, calledMethod->name);
1101 return true;
1102 }
1103
1104 inlineSubs++;
1105 }
1106
1107 return false;
1108 }
1109