blob: 1376c31e5960557af7cf7e91d0990f4882949aaf [file] [log] [blame]
/*
* 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.
*/
/*
* Internal-native initialization and some common utility functions.
*/
#include "Dalvik.h"
#include "native/InternalNativePriv.h"
/*
* Set of classes for which we provide methods.
*
* The last field, classNameHash, is filled in at startup.
*/
static DalvikNativeClass gDvmNativeMethodSet[] = {
{ "Ljava/lang/Object;", dvm_java_lang_Object, 0 },
{ "Ljava/lang/Class;", dvm_java_lang_Class, 0 },
{ "Ljava/lang/Double;", dvm_java_lang_Double, 0 },
{ "Ljava/lang/Float;", dvm_java_lang_Float, 0 },
{ "Ljava/lang/Math;", dvm_java_lang_Math, 0 },
{ "Ljava/lang/Runtime;", dvm_java_lang_Runtime, 0 },
{ "Ljava/lang/String;", dvm_java_lang_String, 0 },
{ "Ljava/lang/System;", dvm_java_lang_System, 0 },
{ "Ljava/lang/Throwable;", dvm_java_lang_Throwable, 0 },
{ "Ljava/lang/VMClassLoader;", dvm_java_lang_VMClassLoader, 0 },
{ "Ljava/lang/VMThread;", dvm_java_lang_VMThread, 0 },
{ "Ljava/lang/reflect/AccessibleObject;",
dvm_java_lang_reflect_AccessibleObject, 0 },
{ "Ljava/lang/reflect/Array;", dvm_java_lang_reflect_Array, 0 },
{ "Ljava/lang/reflect/Constructor;",
dvm_java_lang_reflect_Constructor, 0 },
{ "Ljava/lang/reflect/Field;", dvm_java_lang_reflect_Field, 0 },
{ "Ljava/lang/reflect/Method;", dvm_java_lang_reflect_Method, 0 },
{ "Ljava/lang/reflect/Proxy;", dvm_java_lang_reflect_Proxy, 0 },
{ "Ljava/security/AccessController;",
dvm_java_security_AccessController, 0 },
{ "Ljava/util/concurrent/atomic/AtomicLong;",
dvm_java_util_concurrent_atomic_AtomicLong, 0 },
{ "Ldalvik/bytecode/OpcodeInfo;", dvm_dalvik_bytecode_OpcodeInfo, 0 },
{ "Ldalvik/system/VMDebug;", dvm_dalvik_system_VMDebug, 0 },
{ "Ldalvik/system/DexFile;", dvm_dalvik_system_DexFile, 0 },
{ "Ldalvik/system/VMRuntime;", dvm_dalvik_system_VMRuntime, 0 },
{ "Ldalvik/system/Zygote;", dvm_dalvik_system_Zygote, 0 },
{ "Ldalvik/system/VMStack;", dvm_dalvik_system_VMStack, 0 },
{ "Lorg/apache/harmony/dalvik/ddmc/DdmServer;",
dvm_org_apache_harmony_dalvik_ddmc_DdmServer, 0 },
{ "Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;",
dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal, 0 },
{ "Lorg/apache/harmony/dalvik/NativeTestTarget;",
dvm_org_apache_harmony_dalvik_NativeTestTarget, 0 },
{ "Lsun/misc/Unsafe;", dvm_sun_misc_Unsafe, 0 },
{ NULL, NULL, 0 },
};
/*
* Set up hash values on the class names.
*/
bool dvmInternalNativeStartup(void)
{
DalvikNativeClass* classPtr = gDvmNativeMethodSet;
while (classPtr->classDescriptor != NULL) {
classPtr->classDescriptorHash =
dvmComputeUtf8Hash(classPtr->classDescriptor);
classPtr++;
}
gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar);
if (gDvm.userDexFiles == NULL)
return false;
return true;
}
/*
* Clean up.
*/
void dvmInternalNativeShutdown(void)
{
dvmHashTableFree(gDvm.userDexFiles);
}
/*
* Search the internal native set for a match.
*/
DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method)
{
const char* classDescriptor = method->clazz->descriptor;
const DalvikNativeClass* pClass;
u4 hash;
hash = dvmComputeUtf8Hash(classDescriptor);
pClass = gDvmNativeMethodSet;
while (true) {
if (pClass->classDescriptor == NULL)
break;
if (pClass->classDescriptorHash == hash &&
strcmp(pClass->classDescriptor, classDescriptor) == 0)
{
const DalvikNativeMethod* pMeth = pClass->methodInfo;
while (true) {
if (pMeth->name == NULL)
break;
if (dvmCompareNameDescriptorAndMethod(pMeth->name,
pMeth->signature, method) == 0)
{
/* match */
//LOGV("+++ match on %s.%s %s at %p\n",
// className, methodName, methodSignature, pMeth->fnPtr);
return pMeth->fnPtr;
}
pMeth++;
}
}
pClass++;
}
return NULL;
}
/*
* Magic "internal native" code stub, inserted into abstract method
* definitions when a class is first loaded. This throws the expected
* exception so we don't have to explicitly check for it in the interpreter.
*/
void dvmAbstractMethodStub(const u4* args, JValue* pResult)
{
LOGD("--- called into dvmAbstractMethodStub\n");
dvmThrowAbstractMethodError("abstract method not implemented");
}
/*
* Verify that "obj" is non-null and is an instance of "clazz".
* Used to implement reflection on fields and methods.
*
* Returns "false" and throws an exception if not.
*/
bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz)
{
const char* exceptionClass = NULL;
if (obj == NULL) {
exceptionClass = "Ljava/lang/NullPointerException;";
} else if (!dvmInstanceof(obj->clazz, clazz)) {
exceptionClass = "Ljava/lang/IllegalArgumentException;";
}
if (exceptionClass != NULL) {
char* expectedClassName = dvmHumanReadableDescriptor(clazz->descriptor);
char* actualClassName = (obj != NULL)
? dvmHumanReadableDescriptor(obj->clazz->descriptor)
: strdup("null");
dvmThrowExceptionFmt(exceptionClass,
"expected receiver of type %s, not %s",
expectedClassName, actualClassName);
free(expectedClassName);
free(actualClassName);
return false;
}
return true;
}
/*
* Validate a "binary" class name, e.g. "java.lang.String" or "[I".
*/
static bool validateClassName(const char* name)
{
int len = strlen(name);
int i = 0;
/* check for reasonable array types */
if (name[0] == '[') {
while (name[i] == '[')
i++;
if (name[i] == 'L') {
/* array of objects, make sure it ends well */
if (name[len-1] != ';')
return false;
} else if (strchr(PRIM_TYPE_TO_LETTER, name[i]) != NULL) {
if (i != len-1)
return false;
} else {
return false;
}
}
/* quick check for illegal chars */
for ( ; i < len; i++) {
if (name[i] == '/')
return false;
}
return true;
}
/*
* Find a class by name, initializing it if requested.
*/
ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
bool doInit)
{
ClassObject* clazz = NULL;
char* name = NULL;
char* descriptor = NULL;
if (nameObj == NULL) {
dvmThrowNullPointerException(NULL);
goto bail;
}
name = dvmCreateCstrFromString(nameObj);
/*
* We need to validate and convert the name (from x.y.z to x/y/z). This
* is especially handy for array types, since we want to avoid
* auto-generating bogus array classes.
*/
if (!validateClassName(name)) {
LOGW("dvmFindClassByName rejecting '%s'\n", name);
dvmThrowClassNotFoundException(name);
goto bail;
}
descriptor = dvmDotToDescriptor(name);
if (descriptor == NULL) {
goto bail;
}
if (doInit)
clazz = dvmFindClass(descriptor, loader);
else
clazz = dvmFindClassNoInit(descriptor, loader);
if (clazz == NULL) {
LOGVV("FAIL: load %s (%d)\n", descriptor, doInit);
Thread* self = dvmThreadSelf();
Object* oldExcep = dvmGetException(self);
dvmAddTrackedAlloc(oldExcep, self); /* don't let this be GCed */
dvmClearException(self);
dvmThrowChainedException("Ljava/lang/ClassNotFoundException;",
name, oldExcep);
dvmReleaseTrackedAlloc(oldExcep, self);
} else {
LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n",
descriptor, doInit, clazz, clazz->classLoader);
}
bail:
free(name);
free(descriptor);
return clazz;
}
/*
* We insert native method stubs for abstract methods so we don't have to
* check the access flags at the time of the method call. This results in
* "native abstract" methods, which can't exist. If we see the "abstract"
* flag set, clear the "native" flag.
*
* We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED
* position, because the callers of this function are trying to convey
* the "traditional" meaning of the flags to their callers.
*/
u4 dvmFixMethodFlags(u4 flags)
{
if ((flags & ACC_ABSTRACT) != 0) {
flags &= ~ACC_NATIVE;
}
flags &= ~ACC_SYNCHRONIZED;
if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) {
flags |= ACC_SYNCHRONIZED;
}
return flags & JAVA_FLAGS_MASK;
}
#define NUM_DOPRIV_FUNCS 4
/*
* Determine if "method" is a "privileged" invocation, i.e. is it one
* of the variations of AccessController.doPrivileged().
*
* Because the security stuff pulls in a pile of stuff that we may not
* want or need, we don't do the class/method lookups at init time, but
* instead on first use.
*/
bool dvmIsPrivilegedMethod(const Method* method)
{
int i;
assert(method != NULL);
if (!gDvm.javaSecurityAccessControllerReady) {
/*
* Populate on first use. No concurrency risk since we're just
* finding pointers to fixed structures.
*/
static const char* kSignatures[NUM_DOPRIV_FUNCS] = {
"(Ljava/security/PrivilegedAction;)Ljava/lang/Object;",
"(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;",
"(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
"(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
};
ClassObject* clazz;
clazz = dvmFindClassNoInit("Ljava/security/AccessController;", NULL);
if (clazz == NULL) {
LOGW("Couldn't find java/security/AccessController\n");
return false;
}
assert(NELEM(gDvm.methJavaSecurityAccessController_doPrivileged) ==
NELEM(kSignatures));
/* verify init */
for (i = 0; i < NUM_DOPRIV_FUNCS; i++) {
gDvm.methJavaSecurityAccessController_doPrivileged[i] =
dvmFindDirectMethodByDescriptor(clazz, "doPrivileged", kSignatures[i]);
if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == NULL) {
LOGW("Warning: couldn't find java/security/AccessController"
".doPrivileged %s\n", kSignatures[i]);
return false;
}
}
/* all good, raise volatile readiness flag */
android_atomic_release_store(true,
&gDvm.javaSecurityAccessControllerReady);
}
for (i = 0; i < NUM_DOPRIV_FUNCS; i++) {
if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == method) {
//LOGI("+++ doPriv match\n");
return true;
}
}
return false;
}