OSDN Git Service

am cfb42287: am b6c0adc1: am b40ffdb4: am 57cf2608: Adapt to new location of libcore
[android-x86/dalvik.git] / vm / analysis / Optimize.c
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 optimizeLoadedClasses(DexFile* pDexFile);
40 static void optimizeClass(ClassObject* clazz);
41 static bool optimizeMethod(Method* method);
42 static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
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  */
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  */
129 void dvmFreeInlineSubsTable(InlineSub* inlineSubs)
130 {
131     free(inlineSubs);
132 }
133
134
135 /*
136  * Optimize the specified class.
137  */
138 void dvmOptimizeClass(ClassObject* clazz)
139 {
140     int i;
141
142     for (i = 0; i < clazz->directMethodCount; i++) {
143         if (!optimizeMethod(&clazz->directMethods[i] ))
144             goto fail;
145     }
146     for (i = 0; i < clazz->virtualMethodCount; i++) {
147         if (!optimizeMethod(&clazz->virtualMethods[i]))
148             goto fail;
149     }
150
151     return;
152
153 fail:
154     // TODO: show when in "verbose" mode
155     LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
156 }
157
158 /*
159  * Optimize instructions in a method.
160  *
161  * Returns "true" if all went well, "false" if we bailed out early when
162  * something failed.
163  */
164 static bool optimizeMethod(Method* method)
165 {
166     u4 insnsSize;
167     u2* insns;
168     u2 inst;
169
170     if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
171         return true;
172
173     insns = (u2*) method->insns;
174     assert(insns != NULL);
175     insnsSize = dvmGetMethodInsnsSize(method);
176
177     while (insnsSize > 0) {
178         int width;
179
180         inst = *insns & 0xff;
181
182         switch (inst) {
183 #ifndef PROFILE_FIELD_ACCESS    /* quickened instructions not instrumented */
184         case OP_IGET:
185         case OP_IGET_BOOLEAN:
186         case OP_IGET_BYTE:
187         case OP_IGET_CHAR:
188         case OP_IGET_SHORT:
189             rewriteInstField(method, insns, OP_IGET_QUICK);
190             break;
191         case OP_IGET_WIDE:
192             rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
193             break;
194         case OP_IGET_OBJECT:
195             rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
196             break;
197         case OP_IPUT:
198         case OP_IPUT_BOOLEAN:
199         case OP_IPUT_BYTE:
200         case OP_IPUT_CHAR:
201         case OP_IPUT_SHORT:
202             rewriteInstField(method, insns, OP_IPUT_QUICK);
203             break;
204         case OP_IPUT_WIDE:
205             rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
206             break;
207         case OP_IPUT_OBJECT:
208             rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
209             break;
210 #endif
211
212         case OP_INVOKE_VIRTUAL:
213             if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
214                 if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
215                     return false;
216             }
217             break;
218         case OP_INVOKE_VIRTUAL_RANGE:
219             if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
220                 if (!rewriteVirtualInvoke(method, insns,
221                         OP_INVOKE_VIRTUAL_QUICK_RANGE))
222                 {
223                     return false;
224                 }
225             }
226             break;
227         case OP_INVOKE_SUPER:
228             if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
229                 return false;
230             break;
231         case OP_INVOKE_SUPER_RANGE:
232             if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
233                 return false;
234             break;
235
236         case OP_INVOKE_DIRECT:
237             if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) {
238                 if (!rewriteEmptyDirectInvoke(method, insns))
239                     return false;
240             }
241             break;
242         case OP_INVOKE_DIRECT_RANGE:
243             rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
244             break;
245
246         case OP_INVOKE_STATIC:
247             rewriteExecuteInline(method, insns, METHOD_STATIC);
248             break;
249         case OP_INVOKE_STATIC_RANGE:
250             rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
251             break;
252
253         default:
254             // ignore this instruction
255             ;
256         }
257
258         if (*insns == kPackedSwitchSignature) {
259             width = 4 + insns[1] * 2;
260         } else if (*insns == kSparseSwitchSignature) {
261             width = 2 + insns[1] * 4;
262         } else if (*insns == kArrayDataSignature) {
263             u2 elemWidth = insns[1];
264             u4 len = insns[2] | (((u4)insns[3]) << 16);
265             width = 4 + (elemWidth * len + 1) / 2;
266         } else {
267             width = dexGetInstrWidthAbs(gDvm.instrWidth, inst);
268         }
269         assert(width > 0);
270
271         insns += width;
272         insnsSize -= width;
273     }
274
275     assert(insnsSize == 0);
276     return true;
277 }
278
279
280 /*
281  * If "referrer" and "resClass" don't come from the same DEX file, and
282  * the DEX we're working on is not destined for the bootstrap class path,
283  * tweak the class loader so package-access checks work correctly.
284  *
285  * Only do this if we're doing pre-verification or optimization.
286  */
287 static void tweakLoader(ClassObject* referrer, ClassObject* resClass)
288 {
289     if (!gDvm.optimizing)
290         return;
291     assert(referrer->classLoader == NULL);
292     assert(resClass->classLoader == NULL);
293
294     if (!gDvm.optimizingBootstrapClass) {
295         /* class loader for an array class comes from element type */
296         if (dvmIsArrayClass(resClass))
297             resClass = resClass->elementClass;
298         if (referrer->pDvmDex != resClass->pDvmDex)
299             resClass->classLoader = (Object*) 0xdead3333;
300     }
301 }
302
303 /*
304  * Undo the effects of tweakLoader.
305  */
306 static void untweakLoader(ClassObject* referrer, ClassObject* resClass)
307 {
308     if (!gDvm.optimizing || gDvm.optimizingBootstrapClass)
309         return;
310
311     if (dvmIsArrayClass(resClass))
312         resClass = resClass->elementClass;
313     resClass->classLoader = NULL;
314 }
315
316
317 /*
318  * Alternate version of dvmResolveClass for use with verification and
319  * optimization.  Performs access checks on every resolve, and refuses
320  * to acknowledge the existence of classes defined in more than one DEX
321  * file.
322  *
323  * Exceptions caused by failures are cleared before returning.
324  *
325  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
326  */
327 ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx,
328     VerifyError* pFailure)
329 {
330     DvmDex* pDvmDex = referrer->pDvmDex;
331     ClassObject* resClass;
332
333     /*
334      * Check the table first.  If not there, do the lookup by name.
335      */
336     resClass = dvmDexGetResolvedClass(pDvmDex, classIdx);
337     if (resClass == NULL) {
338         const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx);
339         if (className[0] != '\0' && className[1] == '\0') {
340             /* primitive type */
341             resClass = dvmFindPrimitiveClass(className[0]);
342         } else {
343             resClass = dvmFindClassNoInit(className, referrer->classLoader);
344         }
345         if (resClass == NULL) {
346             /* not found, exception should be raised */
347             LOGV("DexOpt: class %d (%s) not found\n",
348                 classIdx,
349                 dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
350             if (pFailure != NULL) {
351                 /* dig through the wrappers to find the original failure */
352                 Object* excep = dvmGetException(dvmThreadSelf());
353                 while (true) {
354                     Object* cause = dvmGetExceptionCause(excep);
355                     if (cause == NULL)
356                         break;
357                     excep = cause;
358                 }
359                 if (strcmp(excep->clazz->descriptor,
360                     "Ljava/lang/IncompatibleClassChangeError;") == 0)
361                 {
362                     *pFailure = VERIFY_ERROR_CLASS_CHANGE;
363                 } else {
364                     *pFailure = VERIFY_ERROR_NO_CLASS;
365                 }
366             }
367             dvmClearOptException(dvmThreadSelf());
368             return NULL;
369         }
370
371         /*
372          * Add it to the resolved table so we're faster on the next lookup.
373          */
374         dvmDexSetResolvedClass(pDvmDex, classIdx, resClass);
375     }
376
377     /* multiple definitions? */
378     if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) {
379         LOGI("DexOpt: not resolving ambiguous class '%s'\n",
380             resClass->descriptor);
381         if (pFailure != NULL)
382             *pFailure = VERIFY_ERROR_NO_CLASS;
383         return NULL;
384     }
385
386     /* access allowed? */
387     tweakLoader(referrer, resClass);
388     bool allowed = dvmCheckClassAccess(referrer, resClass);
389     untweakLoader(referrer, resClass);
390     if (!allowed) {
391         LOGW("DexOpt: resolve class illegal access: %s -> %s\n",
392             referrer->descriptor, resClass->descriptor);
393         if (pFailure != NULL)
394             *pFailure = VERIFY_ERROR_ACCESS_CLASS;
395         return NULL;
396     }
397
398     return resClass;
399 }
400
401 /*
402  * Alternate version of dvmResolveInstField().
403  *
404  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
405  */
406 InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx,
407     VerifyError* pFailure)
408 {
409     DvmDex* pDvmDex = referrer->pDvmDex;
410     InstField* resField;
411
412     resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx);
413     if (resField == NULL) {
414         const DexFieldId* pFieldId;
415         ClassObject* resClass;
416
417         pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx);
418
419         /*
420          * Find the field's class.
421          */
422         resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
423         if (resClass == NULL) {
424             //dvmClearOptException(dvmThreadSelf());
425             assert(!dvmCheckException(dvmThreadSelf()));
426             if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
427             return NULL;
428         }
429
430         resField = (InstField*)dvmFindFieldHier(resClass,
431             dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
432             dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
433         if (resField == NULL) {
434             LOGD("DexOpt: couldn't find field %s.%s\n",
435                 resClass->descriptor,
436                 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
437             if (pFailure != NULL)
438                 *pFailure = VERIFY_ERROR_NO_FIELD;
439             return NULL;
440         }
441         if (dvmIsStaticField(&resField->field)) {
442             LOGD("DexOpt: wanted instance, got static for field %s.%s\n",
443                 resClass->descriptor,
444                 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx));
445             if (pFailure != NULL)
446                 *pFailure = VERIFY_ERROR_CLASS_CHANGE;
447             return NULL;
448         }
449
450         /*
451          * Add it to the resolved table so we're faster on the next lookup.
452          */
453         dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField);
454     }
455
456     /* access allowed? */
457     tweakLoader(referrer, resField->field.clazz);
458     bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
459     untweakLoader(referrer, resField->field.clazz);
460     if (!allowed) {
461         LOGI("DexOpt: access denied from %s to field %s.%s\n",
462             referrer->descriptor, resField->field.clazz->descriptor,
463             resField->field.name);
464         if (pFailure != NULL)
465             *pFailure = VERIFY_ERROR_ACCESS_FIELD;
466         return NULL;
467     }
468
469     return resField;
470 }
471
472 /*
473  * Alternate version of dvmResolveStaticField().
474  *
475  * Does not force initialization of the resolved field's class.
476  *
477  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
478  */
479 StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx,
480     VerifyError* pFailure)
481 {
482     DvmDex* pDvmDex = referrer->pDvmDex;
483     StaticField* resField;
484
485     resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx);
486     if (resField == NULL) {
487         const DexFieldId* pFieldId;
488         ClassObject* resClass;
489
490         pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx);
491
492         /*
493          * Find the field's class.
494          */
495         resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure);
496         if (resClass == NULL) {
497             //dvmClearOptException(dvmThreadSelf());
498             assert(!dvmCheckException(dvmThreadSelf()));
499             if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
500             return NULL;
501         }
502
503         resField = (StaticField*)dvmFindFieldHier(resClass,
504                     dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx),
505                     dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx));
506         if (resField == NULL) {
507             LOGD("DexOpt: couldn't find static field\n");
508             if (pFailure != NULL)
509                 *pFailure = VERIFY_ERROR_NO_FIELD;
510             return NULL;
511         }
512         if (!dvmIsStaticField(&resField->field)) {
513             LOGD("DexOpt: wanted static, got instance 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          * We can only do this if we're in "dexopt", because the presence
525          * of a valid value in the resolution table implies that the class
526          * containing the static field has been initialized.
527          */
528         if (gDvm.optimizing)
529             dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField);
530     }
531
532     /* access allowed? */
533     tweakLoader(referrer, resField->field.clazz);
534     bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField);
535     untweakLoader(referrer, resField->field.clazz);
536     if (!allowed) {
537         LOGI("DexOpt: access denied from %s to field %s.%s\n",
538             referrer->descriptor, resField->field.clazz->descriptor,
539             resField->field.name);
540         if (pFailure != NULL)
541             *pFailure = VERIFY_ERROR_ACCESS_FIELD;
542         return NULL;
543     }
544
545     return resField;
546 }
547
548
549 /*
550  * Rewrite an iget/iput instruction.  These all have the form:
551  *   op vA, vB, field@CCCC
552  *
553  * Where vA holds the value, vB holds the object reference, and CCCC is
554  * the field reference constant pool offset.  We want to replace CCCC
555  * with the byte offset from the start of the object.
556  *
557  * "clazz" is the referring class.  We need this because we verify
558  * access rights here.
559  */
560 static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
561 {
562     ClassObject* clazz = method->clazz;
563     u2 fieldIdx = insns[1];
564     InstField* field;
565     int byteOffset;
566
567     field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
568     if (field == NULL) {
569         LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
570             fieldIdx, (int) (insns - method->insns), clazz->descriptor,
571             method->name);
572         return;
573     }
574
575     if (field->byteOffset >= 65536) {
576         LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
577         return;
578     }
579
580     insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
581     insns[1] = (u2) field->byteOffset;
582     LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
583         field->field.clazz->descriptor, field->field.name,
584         field->byteOffset);
585 }
586
587 /*
588  * Alternate version of dvmResolveMethod().
589  *
590  * Doesn't throw exceptions, and checks access on every lookup.
591  *
592  * On failure, returns NULL, and sets *pFailure if pFailure is not NULL.
593  */
594 Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx,
595     MethodType methodType, VerifyError* pFailure)
596 {
597     DvmDex* pDvmDex = referrer->pDvmDex;
598     Method* resMethod;
599
600     assert(methodType == METHOD_DIRECT ||
601            methodType == METHOD_VIRTUAL ||
602            methodType == METHOD_STATIC);
603
604     LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx,
605         referrer->descriptor);
606
607     resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
608     if (resMethod == NULL) {
609         const DexMethodId* pMethodId;
610         ClassObject* resClass;
611
612         pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
613
614         resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure);
615         if (resClass == NULL) {
616             /*
617              * Can't find the class that the method is a part of, or don't
618              * have permission to access the class.
619              */
620             LOGV("DexOpt: can't find called method's class (?.%s)\n",
621                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
622             if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); }
623             return NULL;
624         }
625         if (dvmIsInterfaceClass(resClass)) {
626             /* method is part of an interface; this is wrong method for that */
627             LOGW("DexOpt: method is in an interface\n");
628             if (pFailure != NULL)
629                 *pFailure = VERIFY_ERROR_GENERIC;
630             return NULL;
631         }
632
633         /*
634          * We need to chase up the class hierarchy to find methods defined
635          * in super-classes.  (We only want to check the current class
636          * if we're looking for a constructor.)
637          */
638         DexProto proto;
639         dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
640
641         if (methodType == METHOD_DIRECT) {
642             resMethod = dvmFindDirectMethod(resClass,
643                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
644         } else {
645             /* METHOD_STATIC or METHOD_VIRTUAL */
646             resMethod = dvmFindMethodHier(resClass,
647                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto);
648         }
649
650         if (resMethod == NULL) {
651             LOGV("DexOpt: couldn't find method '%s'\n",
652                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx));
653             if (pFailure != NULL)
654                 *pFailure = VERIFY_ERROR_NO_METHOD;
655             return NULL;
656         }
657         if (methodType == METHOD_STATIC) {
658             if (!dvmIsStaticMethod(resMethod)) {
659                 LOGD("DexOpt: wanted static, got instance for method %s.%s\n",
660                     resClass->descriptor, resMethod->name);
661                 if (pFailure != NULL)
662                     *pFailure = VERIFY_ERROR_CLASS_CHANGE;
663                 return NULL;
664             }
665         } else if (methodType == METHOD_VIRTUAL) {
666             if (dvmIsStaticMethod(resMethod)) {
667                 LOGD("DexOpt: wanted instance, got static for method %s.%s\n",
668                     resClass->descriptor, resMethod->name);
669                 if (pFailure != NULL)
670                     *pFailure = VERIFY_ERROR_CLASS_CHANGE;
671                 return NULL;
672             }
673         }
674
675         /* see if this is a pure-abstract method */
676         if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) {
677             LOGW("DexOpt: pure-abstract method '%s' in %s\n",
678                 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx),
679                 resClass->descriptor);
680             if (pFailure != NULL)
681                 *pFailure = VERIFY_ERROR_GENERIC;
682             return NULL;
683         }
684
685         /*
686          * Add it to the resolved table so we're faster on the next lookup.
687          *
688          * We can only do this for static methods if we're not in "dexopt",
689          * because the presence of a valid value in the resolution table
690          * implies that the class containing the static field has been
691          * initialized.
692          */
693         if (methodType != METHOD_STATIC || gDvm.optimizing)
694             dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
695     }
696
697     LOGVV("--- found method %d (%s.%s)\n",
698         methodIdx, resMethod->clazz->descriptor, resMethod->name);
699
700     /* access allowed? */
701     tweakLoader(referrer, resMethod->clazz);
702     bool allowed = dvmCheckMethodAccess(referrer, resMethod);
703     untweakLoader(referrer, resMethod->clazz);
704     if (!allowed) {
705         IF_LOGI() {
706             char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
707             LOGI("DexOpt: illegal method access (call %s.%s %s from %s)\n",
708                 resMethod->clazz->descriptor, resMethod->name, desc,
709                 referrer->descriptor);
710             free(desc);
711         }
712         if (pFailure != NULL)
713             *pFailure = VERIFY_ERROR_ACCESS_METHOD;
714         return NULL;
715     }
716
717     return resMethod;
718 }
719
720 /*
721  * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and
722  * invoke-super/range.  These all have the form:
723  *   op vAA, meth@BBBB, reg stuff @CCCC
724  *
725  * We want to replace the method constant pool index BBBB with the
726  * vtable index.
727  */
728 static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc)
729 {
730     ClassObject* clazz = method->clazz;
731     Method* baseMethod;
732     u2 methodIdx = insns[1];
733
734     baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL);
735     if (baseMethod == NULL) {
736         LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n",
737             methodIdx,
738             (int) (insns - method->insns), clazz->descriptor,
739             method->name);
740         return false;
741     }
742
743     assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL ||
744            (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE ||
745            (insns[0] & 0xff) == OP_INVOKE_SUPER ||
746            (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE);
747
748     /*
749      * Note: Method->methodIndex is a u2 and is range checked during the
750      * initial load.
751      */
752     insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
753     insns[1] = baseMethod->methodIndex;
754
755     //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
756     //    method->clazz->descriptor, method->name,
757     //    baseMethod->clazz->descriptor, baseMethod->name);
758
759     return true;
760 }
761
762 /*
763  * Rewrite invoke-direct, which has the form:
764  *   op vAA, meth@BBBB, reg stuff @CCCC
765  *
766  * There isn't a lot we can do to make this faster, but in some situations
767  * we can make it go away entirely.
768  *
769  * This must only be used when the invoked method does nothing and has
770  * no return value (the latter being very important for verification).
771  */
772 static bool rewriteEmptyDirectInvoke(Method* method, u2* insns)
773 {
774     ClassObject* clazz = method->clazz;
775     Method* calledMethod;
776     u2 methodIdx = insns[1];
777
778     calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL);
779     if (calledMethod == NULL) {
780         LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n",
781             methodIdx,
782             (int) (insns - method->insns), clazz->descriptor,
783             method->name);
784         return false;
785     }
786
787     /* TODO: verify that java.lang.Object() is actually empty! */
788     if (calledMethod->clazz == gDvm.classJavaLangObject &&
789         dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0)
790     {
791         /*
792          * Replace with "empty" instruction.  DO NOT disturb anything
793          * else about it, as we want it to function the same as
794          * OP_INVOKE_DIRECT when debugging is enabled.
795          */
796         assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
797         insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
798
799         //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
800         //    method->clazz->descriptor, method->name,
801         //    calledMethod->clazz->descriptor, calledMethod->name);
802     }
803
804     return true;
805 }
806
807 /*
808  * Resolve an interface method reference.
809  *
810  * No method access check here -- interface methods are always public.
811  *
812  * Returns NULL if the method was not found.  Does not throw an exception.
813  */
814 Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx)
815 {
816     DvmDex* pDvmDex = referrer->pDvmDex;
817     Method* resMethod;
818     int i;
819
820     LOGVV("--- resolving interface method %d (referrer=%s)\n",
821         methodIdx, referrer->descriptor);
822
823     resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx);
824     if (resMethod == NULL) {
825         const DexMethodId* pMethodId;
826         ClassObject* resClass;
827
828         pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx);
829
830         resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL);
831         if (resClass == NULL) {
832             /* can't find the class that the method is a part of */
833             dvmClearOptException(dvmThreadSelf());
834             return NULL;
835         }
836         if (!dvmIsInterfaceClass(resClass)) {
837             /* whoops */
838             LOGI("Interface method not part of interface class\n");
839             return NULL;
840         }
841
842         const char* methodName =
843             dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);
844         DexProto proto;
845         dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId);
846
847         LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
848             methodName, methodSig, resClass->descriptor);
849         resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
850         if (resMethod == NULL) {
851             /* scan superinterfaces and superclass interfaces */
852             LOGVV("+++ did not resolve immediately\n");
853             for (i = 0; i < resClass->iftableCount; i++) {
854                 resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
855                                 methodName, &proto);
856                 if (resMethod != NULL)
857                     break;
858             }
859
860             if (resMethod == NULL) {
861                 LOGVV("+++ unable to resolve method %s\n", methodName);
862                 return NULL;
863             }
864         } else {
865             LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
866                 resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
867         }
868
869         /* we're expecting this to be abstract */
870         if (!dvmIsAbstractMethod(resMethod)) {
871             char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
872             LOGW("Found non-abstract interface method %s.%s %s\n",
873                 resMethod->clazz->descriptor, resMethod->name, desc);
874             free(desc);
875             return NULL;
876         }
877
878         /*
879          * Add it to the resolved table so we're faster on the next lookup.
880          */
881         dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod);
882     }
883
884     LOGVV("--- found interface method %d (%s.%s)\n",
885         methodIdx, resMethod->clazz->descriptor, resMethod->name);
886
887     /* interface methods are always public; no need to check access */
888
889     return resMethod;
890 }
891
892 /*
893  * See if the method being called can be rewritten as an inline operation.
894  * Works for invoke-virtual, invoke-direct, and invoke-static.
895  *
896  * Returns "true" if we replace it.
897  */
898 static bool rewriteExecuteInline(Method* method, u2* insns,
899     MethodType methodType)
900 {
901     const InlineSub* inlineSubs = gDvm.inlineSubs;
902     ClassObject* clazz = method->clazz;
903     Method* calledMethod;
904     u2 methodIdx = insns[1];
905
906     //return false;
907
908     calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
909     if (calledMethod == NULL) {
910         LOGV("+++ DexOpt inline: can't find %d\n", methodIdx);
911         return false;
912     }
913
914     while (inlineSubs->method != NULL) {
915         /*
916         if (extra) {
917             LOGI("comparing %p vs %p %s.%s %s\n",
918                 inlineSubs->method, calledMethod,
919                 inlineSubs->method->clazz->descriptor,
920                 inlineSubs->method->name,
921                 inlineSubs->method->signature);
922         }
923         */
924         if (inlineSubs->method == calledMethod) {
925             assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
926                    (insns[0] & 0xff) == OP_INVOKE_STATIC ||
927                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
928             insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
929             insns[1] = (u2) inlineSubs->inlineIdx;
930
931             //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
932             //    method->clazz->descriptor, method->name,
933             //    calledMethod->clazz->descriptor, calledMethod->name);
934             return true;
935         }
936
937         inlineSubs++;
938     }
939
940     return false;
941 }
942
943 /*
944  * See if the method being called can be rewritten as an inline operation.
945  * Works for invoke-virtual/range, invoke-direct/range, and invoke-static/range.
946  *
947  * Returns "true" if we replace it.
948  */
949 static bool rewriteExecuteInlineRange(Method* method, u2* insns,
950     MethodType methodType)
951 {
952     const InlineSub* inlineSubs = gDvm.inlineSubs;
953     ClassObject* clazz = method->clazz;
954     Method* calledMethod;
955     u2 methodIdx = insns[1];
956
957     calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL);
958     if (calledMethod == NULL) {
959         LOGV("+++ DexOpt inline/range: can't find %d\n", methodIdx);
960         return false;
961     }
962
963     while (inlineSubs->method != NULL) {
964         if (inlineSubs->method == calledMethod) {
965             assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
966                    (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
967                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
968             insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
969             insns[1] = (u2) inlineSubs->inlineIdx;
970
971             //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
972             //    method->clazz->descriptor, method->name,
973             //    calledMethod->clazz->descriptor, calledMethod->name);
974             return true;
975         }
976
977         inlineSubs++;
978     }
979
980     return false;
981 }
982