| /* |
| * 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. |
| */ |
| /* |
| * instanceof, checkcast, etc. |
| */ |
| #include "Dalvik.h" |
| |
| #include <stdlib.h> |
| |
| /* |
| * I think modern C mandates that the results of a boolean expression are |
| * 0 or 1. If not, or we suddenly turn into C++ and bool != int, use this. |
| */ |
| #define BOOL_TO_INT(x) (x) |
| //#define BOOL_TO_INT(x) ((x) ? 1 : 0) |
| |
| /* |
| * Number of entries in instanceof cache. MUST be a power of 2. |
| */ |
| #define INSTANCEOF_CACHE_SIZE 1024 |
| |
| |
| /* |
| * Allocate cache. |
| */ |
| bool dvmInstanceofStartup() |
| { |
| gDvm.instanceofCache = dvmAllocAtomicCache(INSTANCEOF_CACHE_SIZE); |
| if (gDvm.instanceofCache == NULL) |
| return false; |
| return true; |
| } |
| |
| /* |
| * Discard the cache. |
| */ |
| void dvmInstanceofShutdown() |
| { |
| dvmFreeAtomicCache(gDvm.instanceofCache); |
| } |
| |
| |
| /* |
| * Determine whether "sub" is an instance of "clazz", where both of these |
| * are array classes. |
| * |
| * Consider an array class, e.g. Y[][], where Y is a subclass of X. |
| * Y[][] instanceof Y[][] --> true (identity) |
| * Y[][] instanceof X[][] --> true (element superclass) |
| * Y[][] instanceof Y --> false |
| * Y[][] instanceof Y[] --> false |
| * Y[][] instanceof Object --> true (everything is an object) |
| * Y[][] instanceof Object[] --> true |
| * Y[][] instanceof Object[][] --> true |
| * Y[][] instanceof Object[][][] --> false (too many []s) |
| * Y[][] instanceof Serializable --> true (all arrays are Serializable) |
| * Y[][] instanceof Serializable[] --> true |
| * Y[][] instanceof Serializable[][] --> false (unless Y is Serializable) |
| * |
| * Don't forget about primitive types. |
| * int[] instanceof Object[] --> false |
| * |
| * "subElemClass" is sub->elementClass. |
| * |
| * "subDim" is usually just sub->dim, but for some kinds of checks we want |
| * to pass in a non-array class and pretend that it's an array. |
| */ |
| static int isArrayInstanceOfArray(const ClassObject* subElemClass, int subDim, |
| const ClassObject* clazz) |
| { |
| //assert(dvmIsArrayClass(sub)); |
| assert(dvmIsArrayClass(clazz)); |
| |
| /* "If T is an array type TC[]... one of the following must be true: |
| * TC and SC are the same primitive type. |
| * TC and SC are reference types and type SC can be cast to TC [...]." |
| * |
| * We need the class objects for the array elements. For speed we |
| * tucked them into the class object. |
| */ |
| assert(subDim > 0 && clazz->arrayDim > 0); |
| if (subDim == clazz->arrayDim) { |
| /* |
| * See if "sub" is an instance of "clazz". This handles the |
| * interfaces, java.lang.Object, superclassing, etc. |
| */ |
| return dvmInstanceof(subElemClass, clazz->elementClass); |
| } else if (subDim > clazz->arrayDim) { |
| /* |
| * The thing we might be an instance of has fewer dimensions. It |
| * must be an Object or array of Object, or a standard array |
| * interface or array of standard array interfaces (the standard |
| * interfaces being java/lang/Cloneable and java/io/Serializable). |
| */ |
| if (dvmIsInterfaceClass(clazz->elementClass)) { |
| /* |
| * See if the class implements its base element. We know the |
| * base element is an interface; if the array class implements |
| * it, we know it's a standard array interface. |
| */ |
| return dvmImplements(clazz, clazz->elementClass); |
| } else { |
| /* |
| * See if this is an array of Object, Object[], etc. We know |
| * that the superclass of an array is always Object, so we |
| * just compare the element type to that. |
| */ |
| return (clazz->elementClass == clazz->super); |
| } |
| } else { |
| /* |
| * Too many []s. |
| */ |
| return false; |
| } |
| } |
| |
| /* |
| * Determine whether "sub" is a sub-class of "clazz", where "sub" is an |
| * array class. |
| * |
| * "clazz" could be an array class, interface, or simple class. |
| */ |
| static int isArrayInstanceOf(const ClassObject* sub, const ClassObject* clazz) |
| { |
| assert(dvmIsArrayClass(sub)); |
| |
| /* "If T is an interface type, T must be one of the interfaces |
| * implemented by arrays." |
| * |
| * I'm not checking that here, because dvmInstanceof tests for |
| * interfaces first, and the generic dvmImplements stuff should |
| * work correctly. |
| */ |
| assert(!dvmIsInterfaceClass(clazz)); /* make sure */ |
| |
| /* "If T is a class type, then T must be Object." |
| * |
| * The superclass of an array is always java.lang.Object, so just |
| * compare against that. |
| */ |
| if (!dvmIsArrayClass(clazz)) |
| return BOOL_TO_INT(clazz == sub->super); |
| |
| /* |
| * If T is an array type TC[] ... |
| */ |
| return isArrayInstanceOfArray(sub->elementClass, sub->arrayDim, clazz); |
| } |
| |
| |
| /* |
| * Returns 1 (true) if "clazz" is an implementation of "interface". |
| * |
| * "clazz" could be a class or an interface. |
| */ |
| int dvmImplements(const ClassObject* clazz, const ClassObject* interface) |
| { |
| int i; |
| |
| assert(dvmIsInterfaceClass(interface)); |
| |
| /* |
| * All interfaces implemented directly and by our superclass, and |
| * recursively all super-interfaces of those interfaces, are listed |
| * in "iftable", so we can just do a linear scan through that. |
| */ |
| for (i = 0; i < clazz->iftableCount; i++) { |
| if (clazz->iftable[i].clazz == interface) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Determine whether or not we can put an object into an array, based on |
| * the class hierarchy. The object might itself by an array, which means |
| * we have to pay attention to the array instanceof rules. |
| * |
| * Note that "objectClass" could be an array, but objectClass->elementClass |
| * is always a non-array type. |
| */ |
| bool dvmCanPutArrayElement(const ClassObject* objectClass, |
| const ClassObject* arrayClass) |
| { |
| if (dvmIsArrayClass(objectClass)) { |
| /* |
| * We're stuffing an array into an array. We want to see if the |
| * elements of "arrayClass" are compatible with "objectClass". |
| * We bump up the number of dimensions in "objectClass" so that we |
| * can compare the two directly. |
| */ |
| return isArrayInstanceOfArray(objectClass->elementClass, |
| objectClass->arrayDim + 1, arrayClass); |
| } else { |
| /* |
| * We're putting a non-array element into an array. We need to |
| * test to see if the elements are compatible. The easiest way |
| * to do that is to "arrayify" it and use the standard array |
| * compatibility check. |
| */ |
| return isArrayInstanceOfArray(objectClass, 1, arrayClass); |
| } |
| } |
| |
| |
| /* |
| * Perform the instanceof calculation. |
| */ |
| static inline int isInstanceof(const ClassObject* instance, |
| const ClassObject* clazz) |
| { |
| if (dvmIsInterfaceClass(clazz)) { |
| return dvmImplements(instance, clazz); |
| } else if (dvmIsArrayClass(instance)) { |
| return isArrayInstanceOf(instance, clazz); |
| } else { |
| return dvmIsSubClass(instance, clazz); |
| } |
| } |
| |
| |
| /* |
| * Do the instanceof calculation, pulling the result from the cache if |
| * possible. |
| */ |
| int dvmInstanceofNonTrivial(const ClassObject* instance, |
| const ClassObject* clazz) |
| { |
| #define ATOMIC_CACHE_CALC isInstanceof(instance, clazz) |
| #define ATOMIC_CACHE_NULL_ALLOWED true |
| return ATOMIC_CACHE_LOOKUP(gDvm.instanceofCache, |
| INSTANCEOF_CACHE_SIZE, instance, clazz); |
| #undef ATOMIC_CACHE_CALC |
| } |