Home | History | Annotate | Download | only in analysis
      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  */
     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  * 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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
   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  */
   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