blob: f1b4205f330de059b817b8d09b8e683a8d8355c5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Intel, Gregory Shimansky
*/
#define LOG_DOMAIN "jni"
#include "cxxlog.h"
#include "open/vm_method_access.h"
#include "Class.h"
#include "classloader.h"
#include "ini.h"
#include "native_utils.h"
#include "jni_utils.h"
#include "jni_direct.h"
#include "object_handles.h"
#include "vm_arrays.h"
#include "open/vm_util.h"
#include "properties.h"
#include "environment.h"
#include "exceptions.h"
#include "nogc.h"
#include "m2n.h"
#include "stack_trace.h"
Class_Handle jni_get_class_handle(JNIEnv* UNREF jenv, jclass clazz)
{
return jclass_to_struct_Class(clazz);
}
jclass jni_class_from_handle(JNIEnv* UNREF jenv, Class_Handle clss)
{
if (!clss) return NULL;
assert(hythread_is_suspend_enabled());
return struct_Class_to_jclass((Class*)clss);
}
jobject jni_class_loader_from_handle(JNIEnv*, Class_Loader_Handle clh)
{
if (!clh) return NULL;
hythread_suspend_disable();
ManagedObject* obj = clh->GetLoader();
if( !obj ) {
hythread_suspend_enable();
return NULL;
}
ObjectHandle res = oh_allocate_local_handle_from_jni();
if (res) {
res->object = obj;
}
hythread_suspend_enable();
return (jobject)res;
}
Class_Loader_Handle class_loader_lookup(jobject loader)
{
if (!loader) return NULL;
ObjectHandle h = (ObjectHandle)loader;
hythread_suspend_disable(); //---------------------------------v
ClassLoader* cl = ClassLoader::LookupLoader(h->object);
hythread_suspend_enable(); //---------------------------------^
return cl;
} //class_loader_lookup
void class_loader_load_native_lib(const char* lib,
Class_Loader_Handle cl)
{
cl->LoadNativeLibrary(lib);
}
VMEXPORT
jvalue *get_jvalue_arg_array(Method *method, va_list args)
{
unsigned num_args = method->get_num_args();
if(!num_args) {
return NULL;
}
jvalue *jvalue_args = (jvalue *)STD_MALLOC(num_args * sizeof(jvalue));
Arg_List_Iterator iter = method->get_argument_list();
unsigned arg_number = 0;
Java_Type typ;
while((typ = curr_arg(iter)) != JAVA_TYPE_END) {
switch(typ) {
case JAVA_TYPE_CLASS:
case JAVA_TYPE_ARRAY:
jvalue_args[arg_number].l = va_arg(args, jobject);
break;
case JAVA_TYPE_INT:
jvalue_args[arg_number].i = va_arg(args, int);
break;
case JAVA_TYPE_BYTE:
jvalue_args[arg_number].b = (jbyte)va_arg(args, int);
break;
case JAVA_TYPE_BOOLEAN:
jvalue_args[arg_number].z = (jboolean)va_arg(args, int);
break;
case JAVA_TYPE_CHAR:
jvalue_args[arg_number].c = (jchar)va_arg(args, int);
break;
case JAVA_TYPE_SHORT:
jvalue_args[arg_number].s = (jshort)va_arg(args, int);
break;
case JAVA_TYPE_LONG:
jvalue_args[arg_number].j = va_arg(args, jlong);
break;
case JAVA_TYPE_FLOAT:
jvalue_args[arg_number].f = (float)va_arg(args, jdouble);
break;
case JAVA_TYPE_DOUBLE:
jvalue_args[arg_number].d = va_arg(args, jdouble);
break;
default:
LDIE(53, "Unexpected java type");
break;
}
iter = advance_arg_iterator(iter);
arg_number++;
}
return jvalue_args;
} //get_jvalue_arg_array
jclass* GetMethodParameterTypes (JNIEnv* env, const char* sig, int *nparams, ClassLoader *class_loader)
{
int _parsed =0;
char* param;
const char* sparray[256 * sizeof(char*)];
jclass* parray;
const char* s_ptr = sig;
size_t sz = strlen(sig) + 257;
char* p_ptr = (char*)STD_MALLOC(sz);
char* o_ptr = p_ptr;
assert (sig); assert (nparams);
*nparams = 0;
s_ptr++; //skip leading '('
while (*s_ptr != ')') {
param = p_ptr;
while (!_parsed) {
switch (*s_ptr) {
case 'L':
while ((*p_ptr++ = *s_ptr++) != ';');
*p_ptr++ = '\0';
_parsed = 1;
break;
case '[':
while (*s_ptr == '[')
*p_ptr++ = *s_ptr++;
break;
case 'B': case 'C': case 'D':
case 'F': case 'I': case 'J':
case 'S': case 'V': case 'Z':
*p_ptr++ = *s_ptr++;
*p_ptr++ = '\0';
_parsed = 1;
break;
}
}
(*nparams)++;
sparray[*nparams-1] = param;
_parsed = 0;
}
sparray[(*nparams)++] = ++s_ptr;
parray = (jclass*) STD_MALLOC (*nparams * sizeof(jclass));
assert (parray);
// Now convert parameter and return type signatures to class objects
// and construct the parameterTypes array.
int ii = 0;
for (ii =0; ii < *nparams; ii++) {
parray[ii] = SignatureToClass (env, sparray[ii], class_loader);
if (exn_raised()) {
// class loading error occurred
break;
}
}
assert((size_t)(p_ptr - o_ptr) <= sz);
STD_FREE(o_ptr);
return parray;
} // GetMethodParameterTypes
char* ParameterTypesToMethodSignature (JNIEnv* env, jobjectArray parameterTypes, const char *name)
{
if (!parameterTypes) {
return (char*)0;
}
jsize nelem = GetArrayLength (env, parameterTypes);
size_t len = 3; // Parentness and zero byte
jsize i;
for ( i = 0; i < nelem; i++) {
jclass pclazz = (jclass) GetObjectArrayElement (env, parameterTypes, i);
if (NULL == pclazz) {
throw_exception_from_jni (env, "java/lang/NoSuchMethodException", name);
return (char *)0;
}
// Access the internal class handle (Class) for the parameter type:
Class* pcl = jclass_to_struct_Class(pclazz);
len += GetClassSignatureLength(pcl);
}
char* sig = (char *) STD_MALLOC(len);
if (NULL == sig) {
//throw_exception_from_jni (env, "java/lang/OutOfMemoryError", name);
exn_raise_object(VM_Global_State::loader_env->java_lang_OutOfMemoryError);
return (char *)0;
}
sig[0] = '(';
sig[1] = '\0';
char *asig = sig + 1; // Point to zero byte
for (i =0; i < nelem; i++) {
jclass pclazz = (jclass) GetObjectArrayElement (env, parameterTypes, i);
// Access the internal class handle (Class) for the parameter type:
Class* pcl = jclass_to_struct_Class(pclazz);
GetClassSignature (pcl, asig);
asig += strlen(asig); // point to zero byte
}
asig[0] = ')';
asig[1] = '\0';
assert(strlen(sig) < len);
return sig;
} // ParameterTypesToMethodSignature
/**
* This function resembles strlen. It returns number
* of characters in a signature before terminating zero.
* If you allocate memory, you should ask for
* GetClassSignatureLength() + 1 bytes.
*/
//should we care about buffer overflow and return len?
size_t GetClassSignatureLength(Class* cl)
{
assert(cl);
const String* name = cl->get_name();
if (name->bytes[0] == '[') {
return name->len;
}
else if (cl->is_primitive()) {
return 1;
}
else {
return 2 + name->len; // 2 bytes are for L and ;
}
} // GetClassSignatureLength
//should we care about buffer overflow and return len?
void GetClassSignature(Class* cl, char* sig)
{
assert (cl);
const char* name = cl->get_name()->bytes;
if (name[0] == '[') {
sprintf (sig, "%s",name);
}
else if (cl->is_primitive()) {
sig[0] = PrimitiveNameToSignature (name);
sig[1] = '\0';
}
else {
sprintf (sig, "L%s;", name);
}
} // GetClassSignature
//should we care about buffer overflow and return len?
void ClassSignatureToName (const char* sig, char *name)
{
assert (sig);
const char* sig_ptr = sig;
if (*sig_ptr++ == 'L') { // if signature for reference type:
while (*sig_ptr != ';') {
*name++ = *sig_ptr++;
}
*name = '\0';
} else {
strcpy(name, sig);
}
} // ClassSignatureToName
//should we care about buffer overflow and return len?
void PrimitiveSignatureToName (const char sig, char *classname)
{
switch (sig) {
case 'Z':
strcpy(classname, "boolean");
break;
case 'B':
strcpy(classname, "byte");
break;
case 'C':
strcpy(classname, "char");
break;
case 'S':
strcpy(classname, "short");
break;
case 'I':
strcpy(classname, "int");
break;
case 'J':
strcpy(classname, "long");
break;
case 'F':
strcpy(classname, "float");
break;
case 'D':
strcpy(classname, "double");
break;
case 'V':
strcpy(classname, "void");
break;
default:
DIE(("Invalid type descriptor"));
break;
}
} // PrimitiveSignatureToName
//
// Given its name, lookup for a field in the class hierarchy:
//
Field* LookupField (Class *clss, const char *name)
{
Field *f = 0;
for(; clss && !f; clss = clss->get_super_class()) {
if((f = LookupDeclaredField(clss, name)))
return f;
}
return NULL;
} // LookupField
Method* LookupMethod(Class* clss, const char* mname, const char* mdesc)
{
Method* m = 0;
Class* oclss = clss;
for (; clss; clss = clss->get_super_class()) { // for each superclass
if((m = LookupDeclaredMethod(clss, mname, mdesc)))
return m;
}
for(int i = 0; i < oclss->get_number_of_superinterfaces(); i++)
if((m = LookupMethod(oclss->get_superinterface(i), mname, mdesc)))
return m;
return NULL; // method not found
} // LookupMethod
char PrimitiveNameToSignature (const char* name)
{
char sig = '\0';
switch (name[0]) {
case 'b':
if (name[1] == 'o') sig = 'Z';
else sig = 'B';
break;
case 'c': sig = 'C'; break;
case 'd': sig = 'D'; break;
case 'f': sig = 'F'; break;
case 's': sig = 'S'; break;
case 'i': sig = 'I'; break;
case 'l': sig = 'J'; break;
case 'v': sig = 'V'; break;
default: DIE(("Invalid type name"));
}
return sig;
} // PrimitiveNameToSignature
//should we care about buffer overflow and return len?
void SignatureToName (const char* sig, char *name)
{
assert (sig);
if (sig[0] == 'L') { // declared type
ClassSignatureToName (sig, name);
} else if (sig[0] == '[') { // array type
strcpy(name, sig);
} else { // primitive type
PrimitiveSignatureToName (sig[0], name);
}
} // SignatureToName
jclass SignatureToClass (JNIEnv* env, const char* sig)
{
assert (sig);
char classname[512];
SignatureToName(sig, classname);
jclass clazz = FindClass (env, classname);
return clazz;
} // SignatureToClass
//We'd better use this routine
jclass SignatureToClass (JNIEnv* env_ext, const char* sig, ClassLoader *class_loader)
{
assert(hythread_is_suspend_enabled());
assert (sig);
if (sig[0] == 'L' || sig[0] == '[') {
char classname[512];
SignatureToName(sig, classname);
jclass clazz = FindClassWithClassLoader(env_ext, classname, class_loader);
return clazz;
}
Global_Env *ge = jni_get_vm_env(env_ext);
Class* clss = NULL;
switch (sig[0]) {
case 'Z':
clss = ge->Boolean_Class;
break;
case 'B':
clss = ge->Byte_Class;
break;
case 'C':
clss = ge->Char_Class;
break;
case 'D':
clss = ge->Double_Class;
break;
case 'F':
clss = ge->Float_Class;
break;
case 'I':
clss = ge->Int_Class;
break;
case 'J':
clss = ge->Long_Class;
break;
case 'S':
clss = ge->Short_Class;
break;
case 'V':
clss = ge->Void_Class;
break;
default:
DIE(("Invalid type descriptor"));
break;
}
return jni_class_from_handle(env_ext, clss);
} // SignatureToClass
//////////////////////// add stuff VVVVVVVVVVVv
char* JavaStringToCharArray (JNIEnv * UNREF env, jstring jstr, jint* UNREF len)
{
unsigned count = env->GetStringLength(jstr);
jboolean isCopy;
const jchar* chars = env->GetStringChars(jstr, &isCopy);
size_t sz = count+1;
char* cstr = (char*)STD_MALLOC(sz);
assert(cstr);
for(unsigned i=0; i<count; i++)
cstr[i] = (char)((char)chars[i] & 0xff);
cstr[count] = '\0';
if (isCopy) env->ReleaseStringChars(jstr, chars);
assert(strlen(cstr) < sz);
return cstr;
} // JavaStringToCharArray
//
// Given its name, lookup for a field in the given class
//
Field* LookupDeclaredField (Class *clss, const char *name)
{
String *field_name = VM_Global_State::loader_env->string_pool.lookup(name);
assert (clss);
for (unsigned i =0; i < clss->get_number_of_fields(); i++) {
if (clss->get_field(i)->get_name() == field_name) {
return clss->get_field(i);
}
}
return NULL;
} // LookupDeclaredField
void VerifyArray (JNIEnv* env, jarray array)
{
if (!array) {
ThrowNew_Quick (env, "java/lang/NullPointerException", 0);
return;
}
jclass aclazz = GetObjectClass (env, array);
// Acquire handle to internal class handle (Class):
Class* clss = jclass_to_struct_Class(aclazz);
if (!clss->is_array()) {
ThrowNew_Quick (env, "java/lang/IllegalArgumentException", 0);
return;
}
} // VerifyArray
char GetComponentSignature (JNIEnv *env, jarray array)
{
jclass aclazz = GetObjectClass (env, array);
// Acquire handle to internal class handle (Class):
Class* clss = jclass_to_struct_Class(aclazz);
return clss->get_name()->bytes[1]; // return component first character
}
Method* LookupDeclaredMethod(Class *clss, const char *mname, const char *mdesc)
{
String *method_name = VM_Global_State::loader_env->string_pool.lookup(mname);
size_t len = strlen (mdesc);
Method *m = 0;
for (unsigned i =0; i < clss->get_number_of_methods(); i++) { // for each method
m = clss->get_method(i);
if (m->is_fake_method()) {
continue; // ignore fake methods
}
const char* desc = m->get_descriptor()->bytes;
if ((m->get_name() == method_name) // if names and signatures
&& (strncmp (mdesc, desc, len) == 0)) // (excluding return types) match
{
return m; // return method
}
}
return NULL; // method not found
} // LookupDeclaredMethod
//Checks if the object is null.
jboolean IsNullRef(jobject jobj)
{
if (!jobj) return JNI_FALSE;
ObjectHandle h = (ObjectHandle)jobj;
hythread_suspend_disable(); //---------------------------------v
jboolean ret = (h->object == NULL) ? true : false;
hythread_suspend_enable(); //---------------------------------^
return ret;
}
jclass FindClassWithClassLoader(JNIEnv* jenv, const char *name, ClassLoader *loader)
{
String *str = VM_Global_State::loader_env->string_pool.lookup(name);
return FindClassWithClassLoader(jenv, str, loader);
}
jclass FindClassWithClassLoader(JNIEnv* jenv, String *name, ClassLoader *loader)
{
assert(hythread_is_suspend_enabled());
Class* c = loader->LoadVerifyAndPrepareClass( VM_Global_State::loader_env, name);
// if loading failed - exception should be raised
if(!c) {
assert(exn_raised());
return NULL;
}
jclass clazz = struct_Class_to_jclass(c);
return (jclass)clazz;
}
/*
* Utility function for throwing exceptions from JNI
*/
void throw_exception_from_jni(JNIEnv *jenv, const char *exc, const char *msg)
{
jclass exclazz = jenv->FindClass(exc);
assert(exclazz);
// Clear pending exceptions
if (jenv->ExceptionOccurred())
jenv->ExceptionClear();
jenv->ThrowNew(exclazz, msg);
} //throw_exception_from_jni
void array_copy_jni(JNIEnv* jenv, jobject src, jint src_off, jobject dst, jint dst_off, jint count)
{
ArrayCopyResult res;
hythread_suspend_disable();
if (src && dst) {
res = array_copy(((ObjectHandle)src)->object, src_off, ((ObjectHandle)dst)->object, dst_off, count);
} else {
res = ACR_NullPointer;
}
hythread_suspend_enable();
jclass tclass = NULL;
switch (res) {
case ACR_Okay:
return;
case ACR_NullPointer:
tclass = FindClass(jenv, VM_Global_State::loader_env->JavaLangNullPointerException_String);
break;
case ACR_TypeMismatch:
tclass = jenv->FindClass("java/lang/ArrayStoreException");
break;
case ACR_BadIndices:
tclass = FindClass(jenv, VM_Global_State::loader_env->JavaLangArrayIndexOutOfBoundsException_String);
break;
}
jenv->ThrowNew(tclass, "bad arrayCopy");
}
jclass FindClass(JNIEnv* env_ext, String* name)
{
ASSERT_RAISE_AREA;
TRACE2("jni", "FindClass called, name = " << name->bytes);
#ifdef _DEBUG
// must already be checked by JNI FindClass - when this function is
// used inside VM - developer is to check this
char *ch = strchr(name->bytes, '.');
if (NULL != ch)
{
DIE(("Class name should not contain dots"));
}
#endif
JNIEnv_Internal *env = (JNIEnv_Internal *)env_ext;
assert(hythread_is_suspend_enabled());
// Determine loader
StackTraceFrame stf;
ClassLoader* loader = p_TLS_vmthread->onload_caller;
if(loader == NULL) {
bool res = st_get_frame(0, &stf);
if (res)
loader = stf.method->get_class()->get_class_loader();
else
loader = env->vm->vm_env->system_class_loader;
}
String *s = name;
Class* clss =
class_load_verify_prepare_by_loader_jni(VM_Global_State::loader_env, s, loader);
if(clss) {
class_initialize_from_jni(clss);
if (exn_raised()) return NULL;
assert(hythread_is_suspend_enabled());
return struct_Class_to_jclass(clss);
} else {
assert(exn_raised());
return NULL;
}
}
jobject CreateNewThrowable(JNIEnv* jenv, Class* clazz,
const char * message, jthrowable cause = 0)
{
assert(hythread_is_suspend_enabled());
if (!clazz) {
return NULL;
}
Global_Env* genv = VM_Global_State::loader_env;
jvalue args[1];
jstring str = NULL;
if(message) {
str = NewStringUTF(jenv, message);
if(!str) {
return NULL;
}
args[0].l = str;
}
Method* ctor;
if (message)
ctor = clazz->lookup_method(genv->Init_String, genv->FromStringConstructorDescriptor_String);
else
ctor = clazz->lookup_method(genv->Init_String, genv->VoidVoidDescriptor_String);
assert(ctor);
jclass jclazz = struct_Class_to_jclass(clazz);
assert(jclazz);
jobject obj = NewObjectA(jenv, jclazz, (jmethodID)ctor, args);
if (str) {
jenv->DeleteLocalRef(str);
}
if (cause && obj && !exn_raised()) {
//call initCause
Method* initCause = class_lookup_method_recursive(clazz, genv->InitCause_String, genv->InitCauseDescriptor_String);
assert(initCause);
jvalue initArgs[2];
initArgs[0].l = obj;
initArgs[1].l = cause;
jvalue res;
assert(hythread_is_suspend_enabled());
hythread_suspend_disable();
vm_execute_java_method_array((jmethodID) initCause, &res, initArgs);
hythread_suspend_enable();
}
return obj;
}
jobject create_default_instance(Class* clss)
{
hythread_suspend_disable();
ManagedObject *new_obj = class_alloc_new_object_and_run_default_constructor(clss);
if (new_obj == NULL) {
hythread_suspend_enable();
assert(exn_raised());
return NULL;
}
jobject h = oh_allocate_local_handle_from_jni();
if (h == NULL) {
hythread_suspend_enable();
return NULL;
}
h->object = new_obj;
hythread_suspend_enable();
return h;
}
bool ensure_initialised(JNIEnv* env, Class* clss)
{
ASSERT_RAISE_AREA;
assert(hythread_is_suspend_enabled());
if(!clss->is_initialized()) {
class_initialize_from_jni(clss);
if(clss->in_error()) {
assert(exn_raised());
return false;
}
}
return true;
}
JavaVM * jni_get_java_vm(JNIEnv * jni_env) {
return ((JNIEnv_Internal *)jni_env)->vm;
}
Global_Env * jni_get_vm_env(JNIEnv * jni_env) {
return ((JNIEnv_Internal *)jni_env)->vm->vm_env;
}