/*
* 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
}