| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Perform some simple bytecode optimizations, chiefly "quickening" of |
| * opcodes. |
| */ |
| #include "Dalvik.h" |
| #include "libdex/InstrUtils.h" |
| #include "Optimize.h" |
| |
| #include <zlib.h> |
| |
| #include <stdlib.h> |
| |
| /* |
| * Virtual/direct calls to "method" are replaced with an execute-inline |
| * instruction with index "idx". |
| */ |
| struct InlineSub { |
| Method* method; |
| int inlineIdx; |
| }; |
| |
| |
| /* fwd */ |
| static void optimizeMethod(Method* method, bool essentialOnly); |
| static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc, |
| Opcode volatileOpc); |
| static void rewriteJumboInstField(Method* method, u2* insns, |
| Opcode volatileOpc); |
| static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc); |
| static void rewriteJumboStaticField(Method* method, u2* insns, |
| Opcode volatileOpc); |
| static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc); |
| static bool rewriteInvokeObjectInit(Method* method, u2* insns); |
| static bool rewriteJumboInvokeObjectInit(Method* method, u2* insns); |
| static bool rewriteExecuteInline(Method* method, u2* insns, |
| MethodType methodType); |
| static bool rewriteExecuteInlineRange(Method* method, u2* insns, |
| MethodType methodType); |
| static void rewriteReturnVoid(Method* method, u2* insns); |
| static bool needsReturnBarrier(Method* method); |
| |
| |
| /* |
| * Create a table of inline substitutions. Sets gDvm.inlineSubs. |
| * |
| * TODO: this is currently just a linear array. We will want to put this |
| * into a hash table as the list size increases. |
| */ |
| bool dvmCreateInlineSubsTable() |
| { |
| const InlineOperation* ops = dvmGetInlineOpsTable(); |
| const int count = dvmGetInlineOpsTableLength(); |
| InlineSub* table; |
| int i, tableIndex; |
| |
| assert(gDvm.inlineSubs == NULL); |
| |
| /* |
| * One slot per entry, plus an end-of-list marker. |
| */ |
| table = (InlineSub*) calloc(count + 1, sizeof(InlineSub)); |
| |
| tableIndex = 0; |
| for (i = 0; i < count; i++) { |
| Method* method = dvmFindInlinableMethod(ops[i].classDescriptor, |
| ops[i].methodName, ops[i].methodSignature); |
| if (method == NULL) { |
| /* |
| * Not expected. We only use this for key methods in core |
| * classes, so we should always be able to find them. |
| */ |
| LOGE("Unable to find method for inlining: %s.%s:%s", |
| ops[i].classDescriptor, ops[i].methodName, |
| ops[i].methodSignature); |
| return false; |
| } |
| |
| table[tableIndex].method = method; |
| table[tableIndex].inlineIdx = i; |
| tableIndex++; |
| } |
| |
| /* mark end of table */ |
| table[tableIndex].method = NULL; |
| |
| gDvm.inlineSubs = table; |
| return true; |
| } |
| |
| /* |
| * Release inline sub data structure. |
| */ |
| void dvmFreeInlineSubsTable() |
| { |
| free(gDvm.inlineSubs); |
| gDvm.inlineSubs = NULL; |
| } |
| |
| |
| /* |
| * Optimize the specified class. |
| * |
| * If "essentialOnly" is true, we only do essential optimizations. For |
| * example, accesses to volatile 64-bit fields must be replaced with |
| * "-wide-volatile" instructions or the program could behave incorrectly. |
| * (Skipping non-essential optimizations makes us a little bit faster, and |
| * more importantly avoids dirtying DEX pages.) |
| */ |
| void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly) |
| { |
| int i; |
| |
| for (i = 0; i < clazz->directMethodCount; i++) { |
| optimizeMethod(&clazz->directMethods[i], essentialOnly); |
| } |
| for (i = 0; i < clazz->virtualMethodCount; i++) { |
| optimizeMethod(&clazz->virtualMethods[i], essentialOnly); |
| } |
| } |
| |
| /* |
| * Optimize instructions in a method. |
| * |
| * This does a single pass through the code, examining each instruction. |
| * |
| * This is not expected to fail if the class was successfully verified. |
| * The only significant failure modes on unverified code occur when an |
| * "essential" update fails, but we can't generally identify those: if we |
| * can't look up a field, we can't know if the field access was supposed |
| * to be handled as volatile. |
| * |
| * Instead, we give it our best effort, and hope for the best. For 100% |
| * reliability, only optimize a class after verification succeeds. |
| */ |
| static void optimizeMethod(Method* method, bool essentialOnly) |
| { |
| bool needRetBar, forSmp; |
| u4 insnsSize; |
| u2* insns; |
| |
| if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method)) |
| return; |
| |
| forSmp = gDvm.dexOptForSmp; |
| needRetBar = needsReturnBarrier(method); |
| |
| insns = (u2*) method->insns; |
| assert(insns != NULL); |
| insnsSize = dvmGetMethodInsnsSize(method); |
| |
| while (insnsSize > 0) { |
| Opcode opc, quickOpc, volatileOpc; |
| size_t width; |
| bool matched = true; |
| |
| opc = dexOpcodeFromCodeUnit(*insns); |
| width = dexGetWidthFromInstruction(insns); |
| volatileOpc = OP_NOP; |
| |
| /* |
| * Each instruction may have: |
| * - "volatile" replacement |
| * - may be essential or essential-on-SMP |
| * - correctness replacement |
| * - may be essential or essential-on-SMP |
| * - performance replacement |
| * - always non-essential |
| * |
| * Replacements are considered in the order shown, and the first |
| * match is applied. For example, iget-wide will convert to |
| * iget-wide-volatile rather than iget-wide-quick if the target |
| * field is volatile. |
| */ |
| |
| /* |
| * essential substitutions: |
| * {iget,iput,sget,sput}-wide[/jumbo] --> {op}-wide-volatile |
| * invoke-direct[/jumbo][/range] --> invoke-object-init/range |
| * |
| * essential-on-SMP substitutions: |
| * {iget,iput,sget,sput}-*[/jumbo] --> {op}-volatile |
| * return-void --> return-void-barrier |
| * |
| * non-essential substitutions: |
| * {iget,iput}-* --> {op}-quick |
| * |
| * TODO: might be time to merge this with the other two switches |
| */ |
| switch (opc) { |
| case OP_IGET: |
| case OP_IGET_BOOLEAN: |
| case OP_IGET_BYTE: |
| case OP_IGET_CHAR: |
| case OP_IGET_SHORT: |
| quickOpc = OP_IGET_QUICK; |
| if (forSmp) |
| volatileOpc = OP_IGET_VOLATILE; |
| goto rewrite_inst_field; |
| case OP_IGET_WIDE: |
| quickOpc = OP_IGET_WIDE_QUICK; |
| volatileOpc = OP_IGET_WIDE_VOLATILE; |
| goto rewrite_inst_field; |
| case OP_IGET_OBJECT: |
| quickOpc = OP_IGET_OBJECT_QUICK; |
| if (forSmp) |
| volatileOpc = OP_IGET_OBJECT_VOLATILE; |
| goto rewrite_inst_field; |
| case OP_IPUT: |
| case OP_IPUT_BOOLEAN: |
| case OP_IPUT_BYTE: |
| case OP_IPUT_CHAR: |
| case OP_IPUT_SHORT: |
| quickOpc = OP_IPUT_QUICK; |
| if (forSmp) |
| volatileOpc = OP_IPUT_VOLATILE; |
| goto rewrite_inst_field; |
| case OP_IPUT_WIDE: |
| quickOpc = OP_IPUT_WIDE_QUICK; |
| volatileOpc = OP_IPUT_WIDE_VOLATILE; |
| goto rewrite_inst_field; |
| case OP_IPUT_OBJECT: |
| quickOpc = OP_IPUT_OBJECT_QUICK; |
| if (forSmp) |
| volatileOpc = OP_IPUT_OBJECT_VOLATILE; |
| /* fall through */ |
| rewrite_inst_field: |
| if (essentialOnly) |
| quickOpc = OP_NOP; /* if essential-only, no "-quick" sub */ |
| if (quickOpc != OP_NOP || volatileOpc != OP_NOP) |
| rewriteInstField(method, insns, quickOpc, volatileOpc); |
| break; |
| |
| case OP_IGET_JUMBO: |
| case OP_IGET_BOOLEAN_JUMBO: |
| case OP_IGET_BYTE_JUMBO: |
| case OP_IGET_CHAR_JUMBO: |
| case OP_IGET_SHORT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_IGET_VOLATILE_JUMBO; |
| goto rewrite_jumbo_inst_field; |
| case OP_IGET_WIDE_JUMBO: |
| volatileOpc = OP_IGET_WIDE_VOLATILE_JUMBO; |
| goto rewrite_jumbo_inst_field; |
| case OP_IGET_OBJECT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_IGET_OBJECT_VOLATILE_JUMBO; |
| goto rewrite_jumbo_inst_field; |
| case OP_IPUT_JUMBO: |
| case OP_IPUT_BOOLEAN_JUMBO: |
| case OP_IPUT_BYTE_JUMBO: |
| case OP_IPUT_CHAR_JUMBO: |
| case OP_IPUT_SHORT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_IPUT_VOLATILE_JUMBO; |
| goto rewrite_jumbo_inst_field; |
| case OP_IPUT_WIDE_JUMBO: |
| volatileOpc = OP_IPUT_WIDE_VOLATILE_JUMBO; |
| goto rewrite_jumbo_inst_field; |
| case OP_IPUT_OBJECT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_IPUT_OBJECT_VOLATILE_JUMBO; |
| /* fall through */ |
| rewrite_jumbo_inst_field: |
| if (volatileOpc != OP_NOP) |
| rewriteJumboInstField(method, insns, volatileOpc); |
| break; |
| |
| case OP_SGET: |
| case OP_SGET_BOOLEAN: |
| case OP_SGET_BYTE: |
| case OP_SGET_CHAR: |
| case OP_SGET_SHORT: |
| if (forSmp) |
| volatileOpc = OP_SGET_VOLATILE; |
| goto rewrite_static_field; |
| case OP_SGET_WIDE: |
| volatileOpc = OP_SGET_WIDE_VOLATILE; |
| goto rewrite_static_field; |
| case OP_SGET_OBJECT: |
| if (forSmp) |
| volatileOpc = OP_SGET_OBJECT_VOLATILE; |
| goto rewrite_static_field; |
| case OP_SPUT: |
| case OP_SPUT_BOOLEAN: |
| case OP_SPUT_BYTE: |
| case OP_SPUT_CHAR: |
| case OP_SPUT_SHORT: |
| if (forSmp) |
| volatileOpc = OP_SPUT_VOLATILE; |
| goto rewrite_static_field; |
| case OP_SPUT_WIDE: |
| volatileOpc = OP_SPUT_WIDE_VOLATILE; |
| goto rewrite_static_field; |
| case OP_SPUT_OBJECT: |
| if (forSmp) |
| volatileOpc = OP_SPUT_OBJECT_VOLATILE; |
| /* fall through */ |
| rewrite_static_field: |
| if (volatileOpc != OP_NOP) |
| rewriteStaticField(method, insns, volatileOpc); |
| break; |
| |
| case OP_SGET_JUMBO: |
| case OP_SGET_BOOLEAN_JUMBO: |
| case OP_SGET_BYTE_JUMBO: |
| case OP_SGET_CHAR_JUMBO: |
| case OP_SGET_SHORT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_SGET_VOLATILE_JUMBO; |
| goto rewrite_jumbo_static_field; |
| case OP_SGET_WIDE_JUMBO: |
| volatileOpc = OP_SGET_WIDE_VOLATILE_JUMBO; |
| goto rewrite_jumbo_static_field; |
| case OP_SGET_OBJECT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_SGET_OBJECT_VOLATILE_JUMBO; |
| goto rewrite_jumbo_static_field; |
| case OP_SPUT_JUMBO: |
| case OP_SPUT_BOOLEAN_JUMBO: |
| case OP_SPUT_BYTE_JUMBO: |
| case OP_SPUT_CHAR_JUMBO: |
| case OP_SPUT_SHORT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_SPUT_VOLATILE_JUMBO; |
| goto rewrite_jumbo_static_field; |
| case OP_SPUT_WIDE_JUMBO: |
| volatileOpc = OP_SPUT_WIDE_VOLATILE_JUMBO; |
| goto rewrite_jumbo_static_field; |
| case OP_SPUT_OBJECT_JUMBO: |
| if (forSmp) |
| volatileOpc = OP_SPUT_OBJECT_VOLATILE_JUMBO; |
| /* fall through */ |
| rewrite_jumbo_static_field: |
| if (volatileOpc != OP_NOP) |
| rewriteJumboStaticField(method, insns, volatileOpc); |
| break; |
| |
| case OP_INVOKE_DIRECT: |
| case OP_INVOKE_DIRECT_RANGE: |
| if (!rewriteInvokeObjectInit(method, insns)) { |
| /* may want to try execute-inline, below */ |
| matched = false; |
| } |
| break; |
| case OP_INVOKE_DIRECT_JUMBO: |
| rewriteJumboInvokeObjectInit(method, insns); |
| break; |
| case OP_RETURN_VOID: |
| if (needRetBar) |
| rewriteReturnVoid(method, insns); |
| break; |
| default: |
| matched = false; |
| break; |
| } |
| |
| |
| /* |
| * non-essential substitutions: |
| * invoke-{virtual,direct,static}[/range] --> execute-inline |
| * invoke-{virtual,super}[/range] --> invoke-*-quick |
| */ |
| if (!matched && !essentialOnly) { |
| switch (opc) { |
| case OP_INVOKE_VIRTUAL: |
| if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) { |
| rewriteVirtualInvoke(method, insns, |
| OP_INVOKE_VIRTUAL_QUICK); |
| } |
| break; |
| case OP_INVOKE_VIRTUAL_RANGE: |
| if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) { |
| rewriteVirtualInvoke(method, insns, |
| OP_INVOKE_VIRTUAL_QUICK_RANGE); |
| } |
| break; |
| case OP_INVOKE_SUPER: |
| rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK); |
| break; |
| case OP_INVOKE_SUPER_RANGE: |
| rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE); |
| break; |
| case OP_INVOKE_DIRECT: |
| rewriteExecuteInline(method, insns, METHOD_DIRECT); |
| break; |
| case OP_INVOKE_DIRECT_RANGE: |
| rewriteExecuteInlineRange(method, insns, METHOD_DIRECT); |
| break; |
| case OP_INVOKE_STATIC: |
| rewriteExecuteInline(method, insns, METHOD_STATIC); |
| break; |
| case OP_INVOKE_STATIC_RANGE: |
| rewriteExecuteInlineRange(method, insns, METHOD_STATIC); |
| break; |
| default: |
| /* nothing to do for this instruction */ |
| ; |
| } |
| } |
| |
| assert(width > 0); |
| assert(width <= insnsSize); |
| assert(width == dexGetWidthFromInstruction(insns)); |
| |
| insns += width; |
| insnsSize -= width; |
| } |
| |
| assert(insnsSize == 0); |
| } |
| |
| /* |
| * Update a 16-bit code unit in "meth". The way in which the DEX data was |
| * loaded determines how we go about the write. |
| * |
| * This will be operating on post-byte-swap DEX data, so values will |
| * be in host order. |
| */ |
| void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal) |
| { |
| DvmDex* pDvmDex = meth->clazz->pDvmDex; |
| |
| if (!pDvmDex->isMappedReadOnly) { |
| /* in-memory DEX (dexopt or byte[]), alter the output directly */ |
| *ptr = newVal; |
| } else { |
| /* memory-mapped file, toggle the page read/write status */ |
| dvmDexChangeDex2(pDvmDex, ptr, newVal); |
| } |
| } |
| |
| /* |
| * Update an instruction's opcode. |
| * |
| * If "opcode" is an 8-bit op, we just replace that portion. If it's a |
| * 16-bit op, we convert the opcode from "packed" form (e.g. 0x0108) to |
| * bytecode form (e.g. 0x08ff). |
| */ |
| static inline void updateOpcode(const Method* meth, u2* ptr, Opcode opcode) |
| { |
| if (opcode >= 256) { |
| /* opcode low byte becomes high byte, low byte becomes 0xff */ |
| assert((ptr[0] & 0xff) == 0xff); |
| dvmUpdateCodeUnit(meth, ptr, (u2) (opcode << 8) | 0x00ff); |
| } else { |
| /* 8-bit op, just replace the low byte */ |
| assert((ptr[0] & 0xff) != 0xff); |
| dvmUpdateCodeUnit(meth, ptr, (ptr[0] & 0xff00) | (u2) opcode); |
| } |
| } |
| |
| /* |
| * If "referrer" and "resClass" don't come from the same DEX file, and |
| * the DEX we're working on is not destined for the bootstrap class path, |
| * tweak the class loader so package-access checks work correctly. |
| * |
| * Only do this if we're doing pre-verification or optimization. |
| */ |
| static void tweakLoader(ClassObject* referrer, ClassObject* resClass) |
| { |
| if (!gDvm.optimizing) |
| return; |
| assert(referrer->classLoader == NULL); |
| assert(resClass->classLoader == NULL); |
| |
| if (!gDvm.optimizingBootstrapClass) { |
| /* class loader for an array class comes from element type */ |
| if (dvmIsArrayClass(resClass)) |
| resClass = resClass->elementClass; |
| if (referrer->pDvmDex != resClass->pDvmDex) |
| resClass->classLoader = (Object*) 0xdead3333; |
| } |
| } |
| |
| /* |
| * Undo the effects of tweakLoader. |
| */ |
| static void untweakLoader(ClassObject* referrer, ClassObject* resClass) |
| { |
| if (!gDvm.optimizing || gDvm.optimizingBootstrapClass) |
| return; |
| |
| if (dvmIsArrayClass(resClass)) |
| resClass = resClass->elementClass; |
| resClass->classLoader = NULL; |
| } |
| |
| |
| /* |
| * Alternate version of dvmResolveClass for use with verification and |
| * optimization. Performs access checks on every resolve, and refuses |
| * to acknowledge the existence of classes defined in more than one DEX |
| * file. |
| * |
| * Exceptions caused by failures are cleared before returning. |
| * |
| * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| */ |
| ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx, |
| VerifyError* pFailure) |
| { |
| DvmDex* pDvmDex = referrer->pDvmDex; |
| ClassObject* resClass; |
| |
| /* |
| * Check the table first. If not there, do the lookup by name. |
| */ |
| resClass = dvmDexGetResolvedClass(pDvmDex, classIdx); |
| if (resClass == NULL) { |
| const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx); |
| if (className[0] != '\0' && className[1] == '\0') { |
| /* primitive type */ |
| resClass = dvmFindPrimitiveClass(className[0]); |
| } else { |
| resClass = dvmFindClassNoInit(className, referrer->classLoader); |
| } |
| if (resClass == NULL) { |
| /* not found, exception should be raised */ |
| ALOGV("DexOpt: class %d (%s) not found", |
| classIdx, |
| dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); |
| if (pFailure != NULL) { |
| /* dig through the wrappers to find the original failure */ |
| Object* excep = dvmGetException(dvmThreadSelf()); |
| while (true) { |
| Object* cause = dvmGetExceptionCause(excep); |
| if (cause == NULL) |
| break; |
| excep = cause; |
| } |
| if (strcmp(excep->clazz->descriptor, |
| "Ljava/lang/IncompatibleClassChangeError;") == 0) |
| { |
| *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| } else { |
| *pFailure = VERIFY_ERROR_NO_CLASS; |
| } |
| } |
| dvmClearOptException(dvmThreadSelf()); |
| return NULL; |
| } |
| |
| /* |
| * Add it to the resolved table so we're faster on the next lookup. |
| */ |
| dvmDexSetResolvedClass(pDvmDex, classIdx, resClass); |
| } |
| |
| /* multiple definitions? */ |
| if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) { |
| LOGI("DexOpt: not resolving ambiguous class '%s'", |
| resClass->descriptor); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_NO_CLASS; |
| return NULL; |
| } |
| |
| /* access allowed? */ |
| tweakLoader(referrer, resClass); |
| bool allowed = dvmCheckClassAccess(referrer, resClass); |
| untweakLoader(referrer, resClass); |
| if (!allowed) { |
| LOGW("DexOpt: resolve class illegal access: %s -> %s", |
| referrer->descriptor, resClass->descriptor); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_ACCESS_CLASS; |
| return NULL; |
| } |
| |
| return resClass; |
| } |
| |
| /* |
| * Alternate version of dvmResolveInstField(). |
| * |
| * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| */ |
| InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx, |
| VerifyError* pFailure) |
| { |
| DvmDex* pDvmDex = referrer->pDvmDex; |
| InstField* resField; |
| |
| resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx); |
| if (resField == NULL) { |
| const DexFieldId* pFieldId; |
| ClassObject* resClass; |
| |
| pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); |
| |
| /* |
| * Find the field's class. |
| */ |
| resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); |
| if (resClass == NULL) { |
| //dvmClearOptException(dvmThreadSelf()); |
| assert(!dvmCheckException(dvmThreadSelf())); |
| if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } |
| return NULL; |
| } |
| |
| resField = (InstField*)dvmFindFieldHier(resClass, |
| dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), |
| dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); |
| if (resField == NULL) { |
| ALOGD("DexOpt: couldn't find field %s.%s", |
| resClass->descriptor, |
| dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_NO_FIELD; |
| return NULL; |
| } |
| if (dvmIsStaticField(resField)) { |
| ALOGD("DexOpt: wanted instance, got static for field %s.%s", |
| resClass->descriptor, |
| dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| return NULL; |
| } |
| |
| /* |
| * Add it to the resolved table so we're faster on the next lookup. |
| */ |
| dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField); |
| } |
| |
| /* access allowed? */ |
| tweakLoader(referrer, resField->clazz); |
| bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); |
| untweakLoader(referrer, resField->clazz); |
| if (!allowed) { |
| LOGI("DexOpt: access denied from %s to field %s.%s", |
| referrer->descriptor, resField->clazz->descriptor, |
| resField->name); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_ACCESS_FIELD; |
| return NULL; |
| } |
| |
| return resField; |
| } |
| |
| /* |
| * Alternate version of dvmResolveStaticField(). |
| * |
| * Does not force initialization of the resolved field's class. |
| * |
| * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| */ |
| StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx, |
| VerifyError* pFailure) |
| { |
| DvmDex* pDvmDex = referrer->pDvmDex; |
| StaticField* resField; |
| |
| resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx); |
| if (resField == NULL) { |
| const DexFieldId* pFieldId; |
| ClassObject* resClass; |
| |
| pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); |
| |
| /* |
| * Find the field's class. |
| */ |
| resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); |
| if (resClass == NULL) { |
| //dvmClearOptException(dvmThreadSelf()); |
| assert(!dvmCheckException(dvmThreadSelf())); |
| if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } |
| return NULL; |
| } |
| |
| const char* fieldName = |
| dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx); |
| |
| resField = (StaticField*)dvmFindFieldHier(resClass, fieldName, |
| dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); |
| if (resField == NULL) { |
| ALOGD("DexOpt: couldn't find static field %s.%s", |
| resClass->descriptor, fieldName); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_NO_FIELD; |
| return NULL; |
| } |
| if (!dvmIsStaticField(resField)) { |
| ALOGD("DexOpt: wanted static, got instance for field %s.%s", |
| resClass->descriptor, fieldName); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| return NULL; |
| } |
| |
| /* |
| * Add it to the resolved table so we're faster on the next lookup. |
| * |
| * We can only do this if we're in "dexopt", because the presence |
| * of a valid value in the resolution table implies that the class |
| * containing the static field has been initialized. |
| */ |
| if (gDvm.optimizing) |
| dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); |
| } |
| |
| /* access allowed? */ |
| tweakLoader(referrer, resField->clazz); |
| bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); |
| untweakLoader(referrer, resField->clazz); |
| if (!allowed) { |
| LOGI("DexOpt: access denied from %s to field %s.%s", |
| referrer->descriptor, resField->clazz->descriptor, |
| resField->name); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_ACCESS_FIELD; |
| return NULL; |
| } |
| |
| return resField; |
| } |
| |
| |
| /* |
| * Rewrite an iget/iput instruction if appropriate. These all have the form: |
| * op vA, vB, field@CCCC |
| * |
| * Where vA holds the value, vB holds the object reference, and CCCC is |
| * the field reference constant pool offset. For a non-volatile field, |
| * we want to replace the opcode with "quickOpc" and replace CCCC with |
| * the byte offset from the start of the object. For a volatile field, |
| * we just want to replace the opcode with "volatileOpc". |
| * |
| * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile |
| * field. If "quickOpc" is OP_NOP, and this is a non-volatile field, |
| * we don't do anything. |
| * |
| * "method" is the referring method. |
| */ |
| static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc, |
| Opcode volatileOpc) |
| { |
| ClassObject* clazz = method->clazz; |
| u2 fieldIdx = insns[1]; |
| InstField* instField; |
| |
| instField = dvmOptResolveInstField(clazz, fieldIdx, NULL); |
| if (instField == NULL) { |
| LOGI("DexOpt: unable to optimize instance field ref " |
| "0x%04x at 0x%02x in %s.%s", |
| fieldIdx, (int) (insns - method->insns), clazz->descriptor, |
| method->name); |
| return; |
| } |
| |
| if (volatileOpc != OP_NOP && dvmIsVolatileField(instField)) { |
| updateOpcode(method, insns, volatileOpc); |
| ALOGV("DexOpt: rewrote ifield access %s.%s --> volatile", |
| instField->clazz->descriptor, instField->name); |
| } else if (quickOpc != OP_NOP && instField->byteOffset < 65536) { |
| updateOpcode(method, insns, quickOpc); |
| dvmUpdateCodeUnit(method, insns+1, (u2) instField->byteOffset); |
| ALOGV("DexOpt: rewrote ifield access %s.%s --> %d", |
| instField->clazz->descriptor, instField->name, |
| instField->byteOffset); |
| } else { |
| ALOGV("DexOpt: no rewrite of ifield access %s.%s", |
| instField->clazz->descriptor, instField->name); |
| } |
| |
| return; |
| } |
| |
| /* |
| * Rewrite a jumbo instance field access instruction if appropriate. If |
| * the target field is volatile, we replace the opcode with "volatileOpc". |
| * |
| * "method" is the referring method. |
| */ |
| static void rewriteJumboInstField(Method* method, u2* insns, Opcode volatileOpc) |
| { |
| ClassObject* clazz = method->clazz; |
| u4 fieldIdx = insns[1] | (u4) insns[2] << 16; |
| InstField* instField; |
| |
| assert(volatileOpc != OP_NOP); |
| |
| instField = dvmOptResolveInstField(clazz, fieldIdx, NULL); |
| if (instField == NULL) { |
| LOGI("DexOpt: unable to optimize instance field ref " |
| "0x%04x at 0x%02x in %s.%s", |
| fieldIdx, (int) (insns - method->insns), clazz->descriptor, |
| method->name); |
| return; |
| } |
| |
| if (dvmIsVolatileField(instField)) { |
| updateOpcode(method, insns, volatileOpc); |
| ALOGV("DexOpt: rewrote jumbo ifield access %s.%s --> volatile", |
| instField->clazz->descriptor, instField->name); |
| } else { |
| ALOGV("DexOpt: no rewrite of jumbo ifield access %s.%s", |
| instField->clazz->descriptor, instField->name); |
| } |
| } |
| |
| /* |
| * Rewrite a static [jumbo] field access instruction if appropriate. If |
| * the target field is volatile, we replace the opcode with "volatileOpc". |
| * |
| * "method" is the referring method. |
| */ |
| static void rewriteStaticField0(Method* method, u2* insns, Opcode volatileOpc, |
| u4 fieldIdx) |
| { |
| ClassObject* clazz = method->clazz; |
| StaticField* staticField; |
| |
| assert(volatileOpc != OP_NOP); |
| |
| staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL); |
| if (staticField == NULL) { |
| LOGI("DexOpt: unable to optimize static field ref " |
| "0x%04x at 0x%02x in %s.%s", |
| fieldIdx, (int) (insns - method->insns), clazz->descriptor, |
| method->name); |
| return; |
| } |
| |
| if (dvmIsVolatileField(staticField)) { |
| updateOpcode(method, insns, volatileOpc); |
| ALOGV("DexOpt: rewrote sfield access %s.%s --> volatile", |
| staticField->clazz->descriptor, staticField->name); |
| } |
| } |
| |
| static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc) |
| { |
| u2 fieldIdx = insns[1]; |
| rewriteStaticField0(method, insns, volatileOpc, fieldIdx); |
| } |
| static void rewriteJumboStaticField(Method* method, u2* insns, |
| Opcode volatileOpc) |
| { |
| u4 fieldIdx = insns[1] | (u4) insns[2] << 16; |
| rewriteStaticField0(method, insns, volatileOpc, fieldIdx); |
| } |
| |
| |
| /* |
| * Alternate version of dvmResolveMethod(). |
| * |
| * Doesn't throw exceptions, and checks access on every lookup. |
| * |
| * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. |
| */ |
| Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, |
| MethodType methodType, VerifyError* pFailure) |
| { |
| DvmDex* pDvmDex = referrer->pDvmDex; |
| Method* resMethod; |
| |
| assert(methodType == METHOD_DIRECT || |
| methodType == METHOD_VIRTUAL || |
| methodType == METHOD_STATIC); |
| |
| LOGVV("--- resolving method %u (referrer=%s)", methodIdx, |
| referrer->descriptor); |
| |
| resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx); |
| if (resMethod == NULL) { |
| const DexMethodId* pMethodId; |
| ClassObject* resClass; |
| |
| pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); |
| |
| resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure); |
| if (resClass == NULL) { |
| /* |
| * Can't find the class that the method is a part of, or don't |
| * have permission to access the class. |
| */ |
| ALOGV("DexOpt: can't find called method's class (?.%s)", |
| dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); |
| if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } |
| return NULL; |
| } |
| if (dvmIsInterfaceClass(resClass)) { |
| /* method is part of an interface; this is wrong method for that */ |
| LOGW("DexOpt: method is in an interface"); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_GENERIC; |
| return NULL; |
| } |
| |
| /* |
| * We need to chase up the class hierarchy to find methods defined |
| * in super-classes. (We only want to check the current class |
| * if we're looking for a constructor.) |
| */ |
| DexProto proto; |
| dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); |
| |
| if (methodType == METHOD_DIRECT) { |
| resMethod = dvmFindDirectMethod(resClass, |
| dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); |
| } else { |
| /* METHOD_STATIC or METHOD_VIRTUAL */ |
| resMethod = dvmFindMethodHier(resClass, |
| dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); |
| } |
| |
| if (resMethod == NULL) { |
| ALOGV("DexOpt: couldn't find method '%s'", |
| dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_NO_METHOD; |
| return NULL; |
| } |
| if (methodType == METHOD_STATIC) { |
| if (!dvmIsStaticMethod(resMethod)) { |
| ALOGD("DexOpt: wanted static, got instance for method %s.%s", |
| resClass->descriptor, resMethod->name); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| return NULL; |
| } |
| } else if (methodType == METHOD_VIRTUAL) { |
| if (dvmIsStaticMethod(resMethod)) { |
| ALOGD("DexOpt: wanted instance, got static for method %s.%s", |
| resClass->descriptor, resMethod->name); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_CLASS_CHANGE; |
| return NULL; |
| } |
| } |
| |
| /* see if this is a pure-abstract method */ |
| if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) { |
| LOGW("DexOpt: pure-abstract method '%s' in %s", |
| dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), |
| resClass->descriptor); |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_GENERIC; |
| return NULL; |
| } |
| |
| /* |
| * Add it to the resolved table so we're faster on the next lookup. |
| * |
| * We can only do this for static methods if we're not in "dexopt", |
| * because the presence of a valid value in the resolution table |
| * implies that the class containing the static field has been |
| * initialized. |
| */ |
| if (methodType != METHOD_STATIC || gDvm.optimizing) |
| dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); |
| } |
| |
| LOGVV("--- found method %d (%s.%s)", |
| methodIdx, resMethod->clazz->descriptor, resMethod->name); |
| |
| /* access allowed? */ |
| tweakLoader(referrer, resMethod->clazz); |
| bool allowed = dvmCheckMethodAccess(referrer, resMethod); |
| untweakLoader(referrer, resMethod->clazz); |
| if (!allowed) { |
| IF_LOGI() { |
| char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); |
| LOGI("DexOpt: illegal method access (call %s.%s %s from %s)", |
| resMethod->clazz->descriptor, resMethod->name, desc, |
| referrer->descriptor); |
| free(desc); |
| } |
| if (pFailure != NULL) |
| *pFailure = VERIFY_ERROR_ACCESS_METHOD; |
| return NULL; |
| } |
| |
| return resMethod; |
| } |
| |
| /* |
| * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and |
| * invoke-super/range if appropriate. These all have the form: |
| * op vAA, meth@BBBB, reg stuff @CCCC |
| * |
| * We want to replace the method constant pool index BBBB with the |
| * vtable index. |
| */ |
| static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc) |
| { |
| ClassObject* clazz = method->clazz; |
| Method* baseMethod; |
| u2 methodIdx = insns[1]; |
| |
| baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL); |
| if (baseMethod == NULL) { |
| ALOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s", |
| methodIdx, |
| (int) (insns - method->insns), clazz->descriptor, |
| method->name); |
| return; |
| } |
| |
| assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL || |
| (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE || |
| (insns[0] & 0xff) == OP_INVOKE_SUPER || |
| (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE); |
| |
| /* |
| * Note: Method->methodIndex is a u2 and is range checked during the |
| * initial load. |
| */ |
| updateOpcode(method, insns, newOpc); |
| dvmUpdateCodeUnit(method, insns+1, baseMethod->methodIndex); |
| |
| //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s", |
| // method->clazz->descriptor, method->name, |
| // baseMethod->clazz->descriptor, baseMethod->name); |
| |
| return; |
| } |
| |
| /* |
| * Rewrite invoke-direct[/range] if the target is Object.<init>. |
| * |
| * This is useful as an optimization, because otherwise every object |
| * instantiation will cause us to call a method that does nothing. |
| * It also allows us to inexpensively mark objects as finalizable at the |
| * correct time. |
| * |
| * TODO: verifier should ensure Object.<init> contains only return-void, |
| * and issue a warning if not. |
| */ |
| static bool rewriteInvokeObjectInit(Method* method, u2* insns) |
| { |
| ClassObject* clazz = method->clazz; |
| Method* calledMethod; |
| u2 methodIdx = insns[1]; |
| |
| calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL); |
| if (calledMethod == NULL) { |
| ALOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s", |
| methodIdx, (int) (insns - method->insns), |
| clazz->descriptor, method->name); |
| return false; |
| } |
| |
| if (calledMethod->clazz == gDvm.classJavaLangObject && |
| dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0) |
| { |
| /* |
| * Replace the instruction. If the debugger is attached, the |
| * interpreter will forward execution to the invoke-direct/range |
| * handler. If this was an invoke-direct/range instruction we can |
| * just replace the opcode, but if it was an invoke-direct we |
| * have to set the argument count (high 8 bits of first code unit) |
| * to 1. |
| */ |
| u1 origOp = insns[0] & 0xff; |
| if (origOp == OP_INVOKE_DIRECT) { |
| dvmUpdateCodeUnit(method, insns, |
| OP_INVOKE_OBJECT_INIT_RANGE | 0x100); |
| } else { |
| assert(origOp == OP_INVOKE_DIRECT_RANGE); |
| assert((insns[0] >> 8) == 1); |
| updateOpcode(method, insns, OP_INVOKE_OBJECT_INIT_RANGE); |
| } |
| |
| LOGVV("DexOpt: replaced Object.<init> in %s.%s", |
| method->clazz->descriptor, method->name); |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Rewrite invoke-direct/jumbo if the target is Object.<init>. |
| */ |
| static bool rewriteJumboInvokeObjectInit(Method* method, u2* insns) |
| { |
| ClassObject* clazz = method->clazz; |
| Method* calledMethod; |
| u4 methodIdx = insns[1] | (u4) insns[2] << 16; |
| |
| calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL); |
| if (calledMethod == NULL) { |
| ALOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s", |
| methodIdx, (int) (insns - method->insns), |
| clazz->descriptor, method->name); |
| return false; |
| } |
| |
| if (calledMethod->clazz == gDvm.classJavaLangObject && |
| dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0) |
| { |
| assert(insns[0] == ((u2) (OP_INVOKE_DIRECT_JUMBO << 8) | 0xff)); |
| updateOpcode(method, insns, OP_INVOKE_OBJECT_INIT_JUMBO); |
| |
| LOGVV("DexOpt: replaced jumbo Object.<init> in %s.%s", |
| method->clazz->descriptor, method->name); |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Resolve an interface method reference. |
| * |
| * No method access check here -- interface methods are always public. |
| * |
| * Returns NULL if the method was not found. Does not throw an exception. |
| */ |
| Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx) |
| { |
| DvmDex* pDvmDex = referrer->pDvmDex; |
| Method* resMethod; |
| |
| LOGVV("--- resolving interface method %d (referrer=%s)", |
| methodIdx, referrer->descriptor); |
| |
| resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx); |
| if (resMethod == NULL) { |
| const DexMethodId* pMethodId; |
| ClassObject* resClass; |
| |
| pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); |
| |
| resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL); |
| if (resClass == NULL) { |
| /* can't find the class that the method is a part of */ |
| dvmClearOptException(dvmThreadSelf()); |
| return NULL; |
| } |
| if (!dvmIsInterfaceClass(resClass)) { |
| /* whoops */ |
| LOGI("Interface method not part of interface class"); |
| return NULL; |
| } |
| |
| const char* methodName = |
| dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx); |
| DexProto proto; |
| dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); |
| |
| LOGVV("+++ looking for '%s' '%s' in resClass='%s'", |
| methodName, methodSig, resClass->descriptor); |
| resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto); |
| if (resMethod == NULL) { |
| return NULL; |
| } |
| |
| /* we're expecting this to be abstract */ |
| if (!dvmIsAbstractMethod(resMethod)) { |
| char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); |
| LOGW("Found non-abstract interface method %s.%s %s", |
| resMethod->clazz->descriptor, resMethod->name, desc); |
| free(desc); |
| return NULL; |
| } |
| |
| /* |
| * Add it to the resolved table so we're faster on the next lookup. |
| */ |
| dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); |
| } |
| |
| LOGVV("--- found interface method %d (%s.%s)", |
| methodIdx, resMethod->clazz->descriptor, resMethod->name); |
| |
| /* interface methods are always public; no need to check access */ |
| |
| return resMethod; |
| } |
| |
| /* |
| * Replace invoke-virtual, invoke-direct, or invoke-static with an |
| * execute-inline operation if appropriate. |
| * |
| * Returns "true" if we replace it. |
| */ |
| static bool rewriteExecuteInline(Method* method, u2* insns, |
| MethodType methodType) |
| { |
| const InlineSub* inlineSubs = gDvm.inlineSubs; |
| ClassObject* clazz = method->clazz; |
| Method* calledMethod; |
| u2 methodIdx = insns[1]; |
| |
| //return false; |
| |
| calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL); |
| if (calledMethod == NULL) { |
| ALOGV("+++ DexOpt inline: can't find %d", methodIdx); |
| return false; |
| } |
| |
| while (inlineSubs->method != NULL) { |
| /* |
| if (extra) { |
| LOGI("comparing %p vs %p %s.%s %s", |
| inlineSubs->method, calledMethod, |
| inlineSubs->method->clazz->descriptor, |
| inlineSubs->method->name, |
| inlineSubs->method->signature); |
| } |
| */ |
| if (inlineSubs->method == calledMethod) { |
| assert((insns[0] & 0xff) == OP_INVOKE_DIRECT || |
| (insns[0] & 0xff) == OP_INVOKE_STATIC || |
| (insns[0] & 0xff) == OP_INVOKE_VIRTUAL); |
| updateOpcode(method, insns, OP_EXECUTE_INLINE); |
| dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx); |
| |
| //LOGI("DexOpt: execute-inline %s.%s --> %s.%s", |
| // method->clazz->descriptor, method->name, |
| // calledMethod->clazz->descriptor, calledMethod->name); |
| return true; |
| } |
| |
| inlineSubs++; |
| } |
| |
| return false; |
| } |
| |
| /* |
| * Replace invoke-virtual/range, invoke-direct/range, or invoke-static/range |
| * with an execute-inline operation if appropriate. |
| * |
| * Returns "true" if we replace it. |
| */ |
| static bool rewriteExecuteInlineRange(Method* method, u2* insns, |
| MethodType methodType) |
| { |
| const InlineSub* inlineSubs = gDvm.inlineSubs; |
| ClassObject* clazz = method->clazz; |
| Method* calledMethod; |
| u2 methodIdx = insns[1]; |
| |
| calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL); |
| if (calledMethod == NULL) { |
| ALOGV("+++ DexOpt inline/range: can't find %d", methodIdx); |
| return false; |
| } |
| |
| while (inlineSubs->method != NULL) { |
| if (inlineSubs->method == calledMethod) { |
| assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE || |
| (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE || |
| (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE); |
| updateOpcode(method, insns, OP_EXECUTE_INLINE_RANGE); |
| dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx); |
| |
| //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s", |
| // method->clazz->descriptor, method->name, |
| // calledMethod->clazz->descriptor, calledMethod->name); |
| return true; |
| } |
| |
| inlineSubs++; |
| } |
| |
| return false; |
| } |
| |
| /* |
| * Returns "true" if the return-void instructions in this method should |
| * be converted to return-void-barrier. |
| * |
| * This is needed to satisfy a Java Memory Model requirement regarding |
| * the construction of objects with final fields. (This does not apply |
| * to <clinit> or static fields, since appropriate barriers are guaranteed |
| * by the class initialization process.) |
| */ |
| static bool needsReturnBarrier(Method* method) |
| { |
| if (!gDvm.dexOptForSmp) |
| return false; |
| if (strcmp(method->name, "<init>") != 0) |
| return false; |
| |
| /* |
| * Check to see if the class is finalizable. The loader sets a flag |
| * if the class or one of its superclasses overrides finalize(). |
| */ |
| const ClassObject* clazz = method->clazz; |
| if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) |
| return true; |
| |
| /* |
| * Check to see if the class has any final fields. If not, we don't |
| * need to generate a barrier instruction. |
| * |
| * In theory, we only need to do this if the method actually modifies |
| * a final field. In practice, non-constructor methods are allowed |
| * to modify final fields, and there are 3rd-party tools that rely on |
| * this behavior. (The compiler does not allow it, but the VM does.) |
| * |
| * If we alter the verifier to restrict final-field updates to |
| * constructors, we can tighten this up as well. |
| */ |
| int idx = clazz->ifieldCount; |
| while (--idx >= 0) { |
| if (dvmIsFinalField(&clazz->ifields[idx])) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* |
| * Convert a return-void to a return-void-barrier. |
| */ |
| static void rewriteReturnVoid(Method* method, u2* insns) |
| { |
| assert((insns[0] & 0xff) == OP_RETURN_VOID); |
| updateOpcode(method, insns, OP_RETURN_VOID_BARRIER); |
| } |