#include "il2cpp-config.h" #include #include #include "gc/WriteBarrier.h" #include "icalls/mscorlib/System.Reflection/MonoMethod.h" #include "il2cpp-tabledefs.h" #include "il2cpp-class-internals.h" #include "metadata/Il2CppTypeVector.h" #include "vm/Array.h" #include "vm/Class.h" #include "vm/Exception.h" #include "vm/GenericContainer.h" #include "vm/MetadataCache.h" #include "vm/Method.h" #include "vm/Object.h" #include "vm/Reflection.h" #include "vm/Runtime.h" #include "vm/String.h" #include "vm/Reflection.h" #include "vm/Type.h" #include "vm/GenericClass.h" using il2cpp::metadata::Il2CppTypeVector; namespace il2cpp { namespace icalls { namespace mscorlib { namespace System { namespace Reflection { Il2CppReflectionMethod* MonoMethod::get_base_definition(Il2CppReflectionMethod *m) { const MethodInfo *method = m->method; Il2CppClass *klass = method->klass; if (klass == NULL) return m; if (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) || vm::Class::IsInterface(klass) || method->flags & METHOD_ATTRIBUTE_NEW_SLOT) return m; /*if(klass->generic_class) klass = klass->generic_class->container_class;*/ for (Il2CppClass* parent = klass->parent; parent != NULL; parent = parent->parent) { if (parent->vtable_count <= method->slot) break; klass = parent; } if (klass == method->klass) return m; il2cpp::vm::Class::Init(klass); const MethodInfo *result = klass->vtable[method->slot].method; if (result == NULL) { void *iterator = NULL; for (result = vm::Class::GetMethods(klass, &iterator); result != NULL; result = vm::Class::GetMethods(klass, &iterator)) if (result->slot == method->slot) break; } if (result == NULL) return m; return il2cpp::vm::Reflection::GetMethodObject(result, klass); } bool MonoMethod::get_IsGenericMethod(Il2CppReflectionMethod* method) { // if we are a generic method definition if (method->method->is_generic) return true; // is_inflated is true when a method is a generic instance or it's declaring type is a generic instance type. // Only return true here if we are a generic instance method if (method->method->is_inflated) { const Il2CppGenericContext* context = vm::MetadataCache::GetMethodGenericContext(method->method); return context != NULL && context->method_inst != NULL; } return false; } Il2CppString * MonoMethod::get_name(Il2CppReflectionMethod * m) { const MethodInfo *method = m->method; IL2CPP_OBJECT_SETREF(m, name, vm::String::New(method->name)); return m->name; } mscorlib_System_Runtime_InteropServices_DllImportAttribute * MonoMethod::GetDllImportAttribute(intptr_t) { //Todo: [DllImport] is an pseudo attribute. it doesn't exist in the metadata as an attribute, but as a flag on a method. //however, if you use reflection to ask for attributes, it does get reported, so what needs to happen is we create an attribute //instanec on the fly and populate it with the data in the metadata. Turns out that if you call GetCustomAttributes() that ends //up calling this function. For now, we will just return an attribute, but not yet populate it with the correct data. Il2CppClass* typeInfo = vm::Class::FromName(il2cpp_defaults.corlib, "System.Runtime.InteropServices", "DllImportAttribute"); IL2CPP_ASSERT(typeInfo != NULL); return (mscorlib_System_Runtime_InteropServices_DllImportAttribute*)il2cpp::vm::Object::New(typeInfo); } Il2CppArray* MonoMethod::GetGenericArguments(Il2CppReflectionMethod* method) { uint32_t count = 0; Il2CppArray* res = NULL; const MethodInfo* methodInfo = method->method; if (methodInfo->is_inflated) { const Il2CppGenericContext* context = vm::MetadataCache::GetMethodGenericContext(methodInfo); if (context && context->method_inst) { const Il2CppGenericInst *inst = context->method_inst; count = inst->type_argc; res = vm::Array::New(il2cpp_defaults.systemtype_class, count); for (uint32_t i = 0; i < count; i++) il2cpp_array_setref(res, i, il2cpp::vm::Reflection::GetTypeObject(inst->type_argv[i])); return res; } // method is inflated because it's owner is a generic instance type, extract method definition out of the method IL2CPP_ASSERT(methodInfo->is_generic || methodInfo->is_inflated); methodInfo = methodInfo->genericMethod->methodDefinition; } const Il2CppGenericContainer *container = vm::MetadataCache::GetMethodGenericContainer(methodInfo); count = container != NULL ? container->type_argc : 0; res = vm::Array::New(il2cpp_defaults.systemtype_class, count); for (uint32_t i = 0; i < count; i++) { const Il2CppGenericParameter *param = vm::GenericContainer::GetGenericParameter(container, i); Il2CppClass *pklass = vm::Class::FromGenericParameter(param); il2cpp_array_setref(res, i, il2cpp::vm::Reflection::GetTypeObject(&pklass->byval_arg)); } return res; } Il2CppObject * MonoMethod::InternalInvoke(Il2CppReflectionMethod * method, Il2CppObject * thisPtr, Il2CppArray * params, Il2CppException * * exc) { IL2CPP_NOT_IMPLEMENTED_ICALL_NO_ASSERT(MonoMethod::InternalInvoke, "Audit and look over commented code. Work in progress."); /* * Invoke from reflection is supposed to always be a virtual call (the API * is stupid), mono_runtime_invoke_*() calls the provided method, allowing * greater flexibility. */ const MethodInfo *m = method->method; int pcount; void *obj = thisPtr; *exc = NULL; if (!(m->flags & METHOD_ATTRIBUTE_STATIC)) { if (thisPtr) { //if (!mono_class_vtable_full (mono_object_domain (method), m->klass, FALSE)) { // mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_class_get_exception_for_failure (m->klass)); // return NULL; //} if (!vm::Object::IsInst(thisPtr, m->klass)) { IL2CPP_ASSERT(0); //mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name_msg (mono_defaults.corlib, "System.Reflection", "TargetException", "Object does not match target type.")); return NULL; } m = vm::Object::GetVirtualMethod(thisPtr, m); } else #if IL2CPP_ENABLE_MONO_BUG_EMULATION // Mono doesn't throw on null 'this' if it's an instance constructor, and class libs depend on this behaviour if (strcmp(m->name, ".ctor")) #endif { vm::Exception::Raise(vm::Exception::GetTargetException("Non-static method requires a target")); } } pcount = params ? il2cpp::vm::Array::GetLength(params) : 0; if (pcount != m->parameters_count) { IL2CPP_ASSERT(0); //mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name (mono_defaults.corlib, "System.Reflection", "TargetParameterCountException")); return NULL; } // TODO: Add check for abstract once types have flags //if ((m->klass->flags & TYPE_ATTRIBUTE_ABSTRACT) && !strcmp (m->name, ".ctor") && !this) { // mono_gc_wbarrier_generic_store (exc, (MonoObject*) mono_exception_from_name_msg (mono_defaults.corlib, "System.Reflection", "TargetException", "Cannot invoke constructor of an abstract class.")); // return NULL; //} if (m->klass->rank && !strcmp(m->name, ".ctor")) { int i; il2cpp_array_size_t *lengths; il2cpp_array_size_t *lower_bounds; pcount = il2cpp::vm::Array::GetLength(params); lengths = (il2cpp_array_size_t*)alloca(sizeof(il2cpp_array_size_t) * pcount); for (i = 0; i < pcount; ++i) lengths[i] = *(il2cpp_array_size_t*)((char*)il2cpp_array_get(params, void*, i) + sizeof(Il2CppObject)); if (m->klass->rank == pcount) { /* Only lengths provided. */ lower_bounds = NULL; } else { IL2CPP_ASSERT(pcount == (m->klass->rank * 2)); /* lower bounds are first. */ lower_bounds = lengths; lengths += m->klass->rank; } return (Il2CppObject*)il2cpp::vm::Array::NewFull(m->klass, lengths, lower_bounds); } // If a managed exception was thrown, we need raise it here because Runtime::Invoke catches the exception and returns a pointer to it. Il2CppException* exception = NULL; Il2CppObject *result = il2cpp::vm::Runtime::InvokeArray(m, obj, params, &exception); if (exception) { *exc = exception; vm::Exception::Raise(exception); } return result; } bool MonoMethod::get_IsGenericMethodDefinition(Il2CppReflectionMethod* method) { return method->method->is_generic; } static std::string FormatExceptionMessageForNonConstructableGenericMethod(const MethodInfo* method, const Il2CppTypeVector& genericArguments) { std::string message; message += "Failed to construct generic method '"; message += vm::Type::GetName(&method->klass->byval_arg, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME); message += "::"; message += vm::Method::GetName(method); message += "' with generic arguments ["; for (Il2CppTypeVector::const_iterator iter = genericArguments.begin(); iter != genericArguments.end(); ++iter) { if (iter != genericArguments.begin()) message += ", "; message += vm::Type::GetName(*iter, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME); } message += "] at runtime."; return message; } static std::string FormatExceptionMessageForNonGenericMethod(const MethodInfo* method) { std::string message; message += "The method '"; message += vm::Type::GetName(&method->klass->byval_arg, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME); message += "::"; message += vm::Method::GetName(method); message += "' is not a generic method."; return message; } Il2CppReflectionMethod* MonoMethod::MakeGenericMethod_impl(Il2CppReflectionMethod* method, Il2CppArray* genericArgumentTypes) { const MethodInfo* genericMethodDefinition = method->method; if (!genericMethodDefinition->is_generic) vm::Exception::Raise(vm::Exception::GetInvalidOperationException(FormatExceptionMessageForNonGenericMethod(genericMethodDefinition).c_str())); uint32_t arrayLength = vm::Array::GetLength(genericArgumentTypes); Il2CppTypeVector genericArguments; genericArguments.reserve(arrayLength); for (uint32_t i = 0; i < arrayLength; i++) { Il2CppReflectionType* genericArgumentType = il2cpp_array_get(genericArgumentTypes, Il2CppReflectionType*, i); genericArguments.push_back(genericArgumentType->type); } const MethodInfo* genericInstanceMethod = vm::MetadataCache::GetGenericInstanceMethod(genericMethodDefinition, genericArguments); if (!genericInstanceMethod) { vm::Exception::Raise(vm::Exception::GetNotSupportedException(FormatExceptionMessageForNonConstructableGenericMethod(genericMethodDefinition, genericArguments).c_str())); return NULL; } return il2cpp::vm::Reflection::GetMethodObject(genericInstanceMethod, NULL); } Il2CppReflectionMethod* MonoMethod::GetGenericMethodDefinition_impl(Il2CppReflectionMethod* method) { if (method->method->is_generic) return method; if (!method->method->is_inflated) return NULL; const MethodInfo* methodDefinition = vm::MetadataCache::GetGenericMethodDefinition(method->method); IL2CPP_ASSERT(methodDefinition); if (!methodDefinition->is_generic) return NULL; const Il2CppGenericContext* methodContext = vm::MetadataCache::GetMethodGenericContext(method->method); IL2CPP_ASSERT(methodContext); if (methodContext->class_inst) { IL2CPP_NOT_IMPLEMENTED_ICALL(MonoMethod::GetGenericMethodDefinition_impl); } return il2cpp::vm::Reflection::GetMethodObject(const_cast(methodDefinition), NULL); } int32_t MonoMethod::get_core_clr_security_level(Il2CppObject* _this) { IL2CPP_NOT_IMPLEMENTED_ICALL(MonoMethod::get_core_clr_security_level); IL2CPP_UNREACHABLE; return 0; } Il2CppReflectionMethod* MonoMethod::get_base_method(Il2CppReflectionMethod* method, bool definition) { // On the C# side, MonoMethod.GetBaseDefinition is the only caller of this icall that passes definition=true, // so we can do the equivalent call that net20 would do, which would be to call get_base_definition. // // When definition=false. This is called by MonoMethod.GetBaseMethod, which is internal, and seems to // only be called by some GetCustomAttributes(true) calls. There is only a small difference in the behavior of this method // when definition is false. const MethodInfo *method2 = method->method; Il2CppClass *klass = method2->klass; if (klass == NULL) return method; if (!(method2->flags & METHOD_ATTRIBUTE_VIRTUAL) || vm::Class::IsInterface(klass) || method2->flags & METHOD_ATTRIBUTE_NEW_SLOT) return method; /*if(klass->generic_class) klass = klass->generic_class->container_class;*/ const MethodInfo *result; bool found = true; do { if (definition) { for (Il2CppClass* parent = klass->parent; parent != NULL; parent = parent->parent) { if (parent->vtable_count <= method2->slot) break; klass = parent; } } else { if (!klass->parent) { IL2CPP_ASSERT(klass == il2cpp_defaults.object_class); return method; } klass = klass->parent; } if (klass == method2->klass) return method; il2cpp::vm::Class::Init(klass); result = klass->vtable[method2->slot].method; if (result == NULL) { void *iterator = NULL; found = false; for (result = vm::Class::GetMethods(klass, &iterator); result != NULL; result = vm::Class::GetMethods(klass, &iterator)) { if (result->slot == method2->slot) { found = true; break; } } IL2CPP_ASSERT(!(definition && !found)); } } while (!found); return il2cpp::vm::Reflection::GetMethodObject(result, klass); } void MonoMethod::GetPInvoke(Il2CppReflectionMethod* _this, int32_t* flags, Il2CppString** entryPoint, Il2CppString** dllName) { // we don't keep these around in metadata *flags = 0; gc::WriteBarrier::GenericStore(dllName, vm::String::Empty()); gc::WriteBarrier::GenericStore(entryPoint, vm::String::Empty()); } } /* namespace Reflection */ } /* namespace System */ } /* namespace mscorlib */ } /* namespace icalls */ } /* namespace il2cpp */