| /* |
| * 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 Alexey V. Varlamov |
| */ |
| |
| #define LOG_DOMAIN "vm.core.reflection" |
| #include "cxxlog.h" |
| |
| #include <assert.h> |
| |
| #include "jni.h" |
| #include "jni_utils.h" |
| #include "environment.h" |
| #include "vm_strings.h" |
| #include "reflection.h" |
| #include "port_malloc.h" |
| |
| #include "open/vm_type_access.h" |
| #include "open/vm_field_access.h" |
| #include "open/vm_method_access.h" |
| #include "open/vm_class_info.h" |
| |
| #include "exceptions.h" |
| #include "heap.h" |
| #include "primitives_support.h" |
| |
| jclass descriptor_to_jclass(Type_Info_Handle desc){ |
| Class_Handle clss = type_info_get_class(desc); |
| if(!clss) { |
| assert(exn_raised()); |
| return NULL; |
| } |
| |
| return struct_Class_to_java_lang_Class_Handle(clss); |
| } |
| |
| // Set parameterTypes and exceptionTypes fields for Method or Constructor object |
| jobjectArray reflection_get_parameter_types(JNIEnv *jenv, Method* method) |
| { |
| jclass jlc_class = struct_Class_to_java_lang_Class_Handle(VM_Global_State::loader_env->JavaLangClass_Class); |
| |
| // Create an array of the argument types |
| Method_Signature_Handle msh = method_get_signature(method); |
| int nparams = method_args_get_number(msh); |
| int start = method->is_static() ? 0 : 1; |
| if (start) --nparams; |
| |
| jobjectArray arg_types = NewObjectArray(jenv, nparams, jlc_class, NULL); |
| |
| if (!arg_types) { |
| return NULL; |
| } |
| |
| int i; |
| for (i = 0; i < nparams; i++) |
| { |
| Type_Info_Handle arg_type = method_args_get_type_info(msh, i+start); |
| jclass arg_clss = descriptor_to_jclass(arg_type); |
| if (!arg_clss) return NULL; |
| SetObjectArrayElement(jenv, arg_types, i, arg_clss); |
| } |
| |
| return arg_types; |
| } |
| |
| // Construct object of member_class (Constructor, Field, Method). |
| static jobject reflect_member(JNIEnv *jenv, Class_Member* member, Class* type) |
| { |
| ASSERT_RAISE_AREA; |
| static Global_Env* genv = VM_Global_State::loader_env; |
| static String* desc = genv->string_pool.lookup( |
| "(JLjava/lang/Class;Ljava/lang/String;Ljava/lang/String;I)V"); |
| Method* member_constr = type->lookup_method(genv->Init_String, desc); |
| |
| jstring jname = String_to_interned_jstring(member->get_name()); |
| if (jname == NULL) { |
| assert(exn_raised()); |
| return NULL; |
| } |
| jstring jdesc = String_to_interned_jstring(member->get_descriptor()); |
| if (jdesc == NULL) { |
| assert(exn_raised()); |
| return NULL; |
| } |
| |
| jvalue args[5]; |
| args[0].j = (jlong) ((POINTER_SIZE_INT) member); |
| args[1].l = struct_Class_to_java_lang_Class_Handle(member->get_class()); |
| args[2].l = jname; |
| args[3].l = jdesc; |
| args[4].i = (jint)member->get_access_flags(); |
| jobject jmember = NewObjectA(jenv, struct_Class_to_java_lang_Class_Handle(type), |
| (jmethodID)member_constr, args); |
| if (!jmember) { |
| assert(exn_raised()); |
| } |
| |
| return jmember; |
| } //reflect_member |
| |
| jobject reflection_reflect_method(JNIEnv *jenv, Method_Handle method) |
| { |
| return reflect_member(jenv, method, |
| VM_Global_State::loader_env->java_lang_reflect_Method_Class); |
| } |
| |
| jobject reflection_reflect_constructor(JNIEnv *jenv, Method_Handle constructor) |
| { |
| return reflect_member(jenv, constructor, |
| VM_Global_State::loader_env->java_lang_reflect_Constructor_Class); |
| } |
| |
| jobject reflection_reflect_field(JNIEnv *jenv, Field_Handle field) |
| { |
| // We do not reflect injected fields |
| //if (field_is_injected(field)) return NULL; |
| |
| return reflect_member(jenv, field, |
| VM_Global_State::loader_env->java_lang_reflect_Field_Class); |
| } |
| |
| jobjectArray reflection_get_class_interfaces(JNIEnv* jenv, jclass clazz) |
| { |
| Class_Handle clss = jni_get_class_handle(jenv, clazz); |
| |
| unsigned intf_number = class_number_implements(clss); |
| |
| jclass cclass = struct_Class_to_java_lang_Class_Handle( |
| VM_Global_State::loader_env->JavaLangClass_Class); |
| |
| jobjectArray arr = NewObjectArray(jenv, intf_number, cclass, NULL); |
| if (! arr) |
| return NULL; |
| |
| // Fill the array |
| for (unsigned i = 0; i < intf_number; i++) { |
| jclass intf = struct_Class_to_java_lang_Class_Handle(class_get_implements(clss, i)); |
| SetObjectArrayElement(jenv, arr, i, intf); |
| } |
| |
| return arr; |
| } |
| |
| jobjectArray reflection_get_class_fields(JNIEnv* jenv, jclass clazz) |
| { |
| Class_Handle clss = jni_get_class_handle(jenv, clazz); |
| assert(clss); |
| TRACE("get class fields : " << clss->get_name()->bytes); |
| |
| unsigned num_fields = clss->get_number_of_fields(); |
| unsigned num_res_fields = 0; |
| |
| unsigned i; |
| // Determine the number of elements in the result. |
| for (i = 0; i < num_fields; i++) { |
| Field_Handle fh = clss->get_field(i); |
| if (fh->is_injected()) continue; |
| num_res_fields++; |
| } |
| |
| // Create result array |
| jclass fclazz = struct_Class_to_java_lang_Class_Handle(VM_Global_State::loader_env->java_lang_reflect_Field_Class); |
| if (! fclazz) return NULL; |
| jobjectArray farray = (jobjectArray) NewObjectArray(jenv, num_res_fields, fclazz, NULL); |
| if (! farray) return NULL; |
| |
| // Fill in the array |
| num_res_fields = 0; |
| |
| for(i = 0; i < num_fields; i++) { |
| Field_Handle fh = class_get_field(clss, i); |
| if (field_is_injected(fh)) continue; |
| //if (class_is_array(clss) && 0 == strcmp("length", field_get_name(fh))) continue; |
| |
| jobject jfield = reflection_reflect_field(jenv, fh); |
| if (!jfield){ |
| assert(exn_raised()); |
| return NULL; |
| } |
| SetObjectArrayElement(jenv, farray, num_res_fields, jfield); |
| if (exn_raised()) return NULL; |
| num_res_fields++; |
| } |
| |
| return farray; |
| } // reflection_get_class_fields |
| |
| jobjectArray reflection_get_class_constructors(JNIEnv* jenv, jclass clazz) |
| { |
| Class_Handle clss = jni_get_class_handle(jenv, clazz); |
| unsigned num_methods = clss->get_number_of_methods(); |
| unsigned n_consts = 0; |
| TRACE("get class constructors : " << clss->get_name()->bytes); |
| |
| unsigned i; |
| // Determine the number of elements in the result. Note that fake methods never have the name "<init>". |
| for (i = 0; i < num_methods; i++) { |
| Method_Handle mh = clss->get_method(i); |
| if (strcmp(mh->get_name()->bytes, "<init>") == 0) |
| n_consts++; |
| } |
| |
| // Create result array |
| jclass cclazz = struct_Class_to_java_lang_Class_Handle( |
| VM_Global_State::loader_env->java_lang_reflect_Constructor_Class); |
| if (!cclazz) return NULL; |
| jobjectArray carray = (jobjectArray) NewObjectArray(jenv, n_consts, cclazz, NULL); |
| if (!carray) return NULL; |
| |
| // Fill in the array |
| for (i = 0, n_consts = 0; i < num_methods; i++) { |
| Method_Handle mh = clss->get_method(i); |
| if (strcmp(mh->get_name()->bytes, "<init>") != 0) continue; |
| |
| jobject jconst = reflection_reflect_constructor(jenv, mh); |
| if (!jconst){ |
| assert(exn_raised()); |
| return NULL; |
| } |
| SetObjectArrayElement(jenv, carray, n_consts++, jconst); |
| if (exn_raised()) return NULL; |
| } |
| |
| return carray; |
| } // reflection_get_class_constructors |
| |
| jobjectArray reflection_get_class_methods(JNIEnv* jenv, jclass clazz) |
| { |
| Class_Handle clss = jni_get_class_handle(jenv, clazz); |
| unsigned num_methods = clss->get_number_of_methods(); |
| unsigned num_res_methods = 0; |
| TRACE("get class methods : " << clss->get_name()->bytes); |
| |
| unsigned i; |
| // Determine the number of elements in the result. |
| // Note that fake methods never have the name "<init>". |
| for (i = 0; i < num_methods; i++) { |
| Method_Handle mh = clss->get_method(i); |
| if (strcmp(mh->get_name()->bytes, "<init>") == 0 |
| || strcmp(mh->get_name()->bytes, "<clinit>") == 0 |
| || mh->is_fake_method()) |
| { |
| continue; |
| } |
| |
| num_res_methods++; |
| } |
| |
| // Create result array |
| jclass member_class = struct_Class_to_java_lang_Class_Handle(VM_Global_State::loader_env->java_lang_reflect_Method_Class); |
| if (! member_class) return NULL; |
| jobjectArray member_array = NewObjectArray(jenv, num_res_methods, member_class, NULL); |
| if (! member_array) return NULL; |
| |
| // Fill in the array |
| unsigned member_i = 0; |
| for (i = 0; i < num_methods; i++) { |
| Method_Handle mh = clss->get_method(i); |
| if (strcmp(mh->get_name()->bytes, "<init>") == 0 |
| || strcmp(mh->get_name()->bytes, "<clinit>") == 0 |
| || mh->is_fake_method()) |
| { |
| continue; |
| } |
| |
| jobject member = reflection_reflect_method(jenv, mh); |
| if (!member){ |
| assert(exn_raised()); |
| return NULL; |
| } |
| SetObjectArrayElement(jenv, member_array, member_i, member); |
| if (exn_raised()) |
| return NULL; |
| |
| member_i ++; |
| } |
| |
| return member_array; |
| } // reflection_get_class_methods |
| |
| /* |
| The following function eases conversion from jobjectArray parameters to |
| JNI friendly jvalue array. |
| */ |
| bool jobjectarray_to_jvaluearray(JNIEnv *jenv, jvalue **output, Method *method, jobjectArray input) |
| { |
| Arg_List_Iterator iter = method->get_argument_list(); |
| unsigned arg_number = 0; |
| jvalue* array = *output; |
| |
| Java_Type type; |
| while((type = curr_arg(iter)) != JAVA_TYPE_END) { |
| jobject arg = GetObjectArrayElement(jenv, input, arg_number); |
| if (type == JAVA_TYPE_ARRAY || type == JAVA_TYPE_CLASS) |
| { |
| array[arg_number].l = arg; |
| } |
| else //unwrap to primitive |
| { |
| ASSERT(arg, ("Cannot unwrap NULL")); |
| Class* arg_clss = jobject_to_struct_Class(arg); |
| char arg_sig = is_wrapper_class(arg_clss->get_name()->bytes); |
| char param_sig = (char)type; |
| |
| // actual parameter is not a wrapper |
| if (0 == arg_sig) { |
| ThrowNew_Quick(jenv, "java/lang/IllegalArgumentException", |
| "actual parameter for the primitive argument is not a wrapper object"); |
| return false; |
| } |
| |
| array[arg_number] = unwrap_primitive(jenv, arg, arg_sig); |
| |
| if (!widen_primitive_jvalue(array + arg_number, arg_sig, param_sig)) { |
| ThrowNew_Quick(jenv, "java/lang/IllegalArgumentException", |
| "widening conversion failed"); |
| return false; |
| } |
| } |
| iter = advance_arg_iterator(iter); |
| arg_number++; |
| } |
| |
| return true; |
| } //jobjectarray_to_jvaluearray |
| |
| jobject reflection_get_enum_value(JNIEnv *jenv, Class* enum_type, String* name) |
| { |
| ASSERT(enum_type->is_enum(), ("Requested Class is not ENUM: %s", |
| enum_type->get_name()->bytes)); |
| |
| for (unsigned i=0; i<enum_type->get_number_of_fields(); i++) { |
| if (enum_type->get_field(i)->get_name() == name) { |
| #ifndef NDEBUG |
| ASSERT(enum_type->get_field(i)->is_enum(), ("Requested field is not ENUM: %d", name->bytes)); |
| const String* type = enum_type->get_name(); |
| const String* desc = enum_type->get_field(i)->get_descriptor(); |
| if (desc->len != (type->len + 2) |
| || desc->bytes[0] != 'L' |
| || strncmp(desc->bytes + 1, type->bytes, type->len) |
| || desc->bytes[type->len + 1] != ';') |
| { |
| LDIE(26, "Invalid enum field descriptor: {0}" << desc->bytes); |
| } |
| #endif |
| return GetStaticObjectField(jenv, 0, (jfieldID)(enum_type->get_field(i))); |
| } |
| } |
| //public EnumConstantNotPresentException(Class<? extends Enum> enumType,String constantName) |
| //ThrowNew_Quick(jenv, "java.lang.EnumConstantNotPresentException", name->bytes); |
| return NULL; |
| } |