#include "il2cpp-config.h" #include "utils/StringUtils.h" #include "gc/GarbageCollector.h" #include "gc/WriteBarrier.h" #include "vm/Class.h" #include "vm/GenericClass.h" #include "vm/Field.h" #include "vm/Object.h" #include "vm/Reflection.h" #include "vm/Runtime.h" #include "vm/String.h" #include "vm/Type.h" #include #include "il2cpp-class-internals.h" #include "il2cpp-object-internals.h" #include "il2cpp-tabledefs.h" #include "utils/MemoryRead.h" #include "vm-utils/BlobReader.h" #include "Thread.h" namespace il2cpp { namespace vm { const char* Field::GetName(FieldInfo *field) { return field->name; } Il2CppClass* Field::GetParent(FieldInfo *field) { return field->parent; } int Field::GetFlags(FieldInfo *field) { return field->type->attrs; } size_t Field::GetOffset(FieldInfo *field) { return field->offset; } void Field::GetValue(Il2CppObject *obj, FieldInfo *field, void *value) { void *src; IL2CPP_ASSERT(obj); IL2CPP_ASSERT(!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)); src = (char*)obj + field->offset; SetValueRaw(field->type, value, src, true); } uint32_t Field::GetToken(const FieldInfo *field) { return field->token; } Il2CppObject* Field::GetValueObject(FieldInfo *field, Il2CppObject *obj) { Il2CppClass* fieldType = Class::FromIl2CppType(field->type); if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL) { if (fieldType->byval_arg.valuetype) { void* value = alloca(fieldType->instance_size - sizeof(Il2CppObject)); Field::GetDefaultFieldValue(field, value); return Object::Box(fieldType, value); } else { Il2CppObject* value; Field::GetDefaultFieldValue(field, &value); return value; } } void* fieldAddress; if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) { if (field->offset == THREAD_STATIC_FIELD_OFFSET) { Runtime::ClassInit(field->parent); int threadStaticFieldOffset = MetadataCache::GetThreadLocalStaticOffsetForField(field); void* threadStaticData = Thread::GetThreadStaticData(field->parent->thread_static_fields_offset); fieldAddress = static_cast(threadStaticData) + threadStaticFieldOffset; } else { Runtime::ClassInit(field->parent); fieldAddress = static_cast(field->parent->static_fields) + field->offset; } } else { IL2CPP_ASSERT(obj); fieldAddress = reinterpret_cast(obj) + field->offset; } return Object::Box(fieldType, fieldAddress); } const Il2CppType* Field::GetType(FieldInfo *field) { return field->type; } bool Field::HasAttribute(FieldInfo *field, Il2CppClass *attr_class) { return Reflection::HasAttribute(field, attr_class); } bool Field::IsDeleted(FieldInfo *field) { return false; } void Field::SetValue(Il2CppObject *obj, const FieldInfo *field, void *value) { void *dest; IL2CPP_ASSERT(!(field->type->attrs & FIELD_ATTRIBUTE_STATIC)); dest = (char*)obj + field->offset; SetValueRaw(field->type, dest, value, false); } void Field::GetDefaultFieldValue(FieldInfo *field, void *value) { const Il2CppType* type = NULL; const char* data; data = Class::GetFieldDefaultValue(field, &type); utils::BlobReader::GetConstantValueFromBlob(field->parent->image, type->type, data, value); } void Field::StaticGetValue(FieldInfo *field, void *value) { StaticGetValueInternal(field, value, NULL); } void Field::StaticGetValueForThread(FieldInfo* field, void* value, Il2CppInternalThread* thread) { StaticGetValueInternal(field, value, thread); } void Field::StaticGetValueInternal(FieldInfo* field, void* value, Il2CppInternalThread* thread) { void *src = NULL; IL2CPP_ASSERT(field->type->attrs & FIELD_ATTRIBUTE_STATIC); if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL) { GetDefaultFieldValue(field, value); return; } // ensure parent is initialized so that static fields memory has been allocated Class::SetupFields(field->parent); if (field->offset == THREAD_STATIC_FIELD_OFFSET) { int threadStaticFieldOffset = MetadataCache::GetThreadLocalStaticOffsetForField(field); void* threadStaticData = thread == NULL ? Thread::GetThreadStaticData(field->parent->thread_static_fields_offset) : Thread::GetThreadStaticDataForThread(field->parent->thread_static_fields_offset, thread); IL2CPP_ASSERT(NULL != threadStaticData); src = ((char*)threadStaticData) + threadStaticFieldOffset; } else { src = ((char*)field->parent->static_fields) + field->offset; } SetValueRaw(field->type, value, src, true); } void Field::StaticSetValue(FieldInfo *field, void *value) { void *dest = NULL; IL2CPP_ASSERT(field->type->attrs & FIELD_ATTRIBUTE_STATIC); IL2CPP_ASSERT(!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)); // ensure parent is initialized so that static fields memory has been allocated Class::Init(field->parent); if (field->offset == THREAD_STATIC_FIELD_OFFSET) { int threadStaticFieldOffset = MetadataCache::GetThreadLocalStaticOffsetForField(field); void* threadStaticData = Thread::GetThreadStaticData(field->parent->thread_static_fields_offset); dest = ((char*)threadStaticData) + threadStaticFieldOffset; } else { dest = ((char*)field->parent->static_fields) + field->offset; } SetValueRaw(field->type, dest, value, false); } void Field::SetInstanceFieldValueObject(Il2CppObject* objectInstance, FieldInfo* field, Il2CppObject* value) { IL2CPP_ASSERT(!(field->type->attrs & FIELD_ATTRIBUTE_LITERAL)); IL2CPP_ASSERT(!field->type->valuetype); gc::WriteBarrier::GenericStore((Il2CppObject**)(reinterpret_cast(objectInstance) + field->offset), value); } void Field::SetValueRaw(const Il2CppType *type, void *dest, void *value, bool deref_pointer) { int t; if (type->byref) { /* object fields cannot be byref, so we don't need a wbarrier here */ void* *p = (void**)dest; *p = value; return; } t = type->type; handle_enum: switch (t) { case IL2CPP_TYPE_BOOLEAN: case IL2CPP_TYPE_I1: case IL2CPP_TYPE_U1: { uint8_t *p = (uint8_t*)dest; *p = value ? *(uint8_t*)value : 0; return; } case IL2CPP_TYPE_I2: case IL2CPP_TYPE_U2: { uint16_t *p = (uint16_t*)dest; *p = value ? *(uint16_t*)value : 0; return; } case IL2CPP_TYPE_CHAR: { Il2CppChar* p = (Il2CppChar*)dest; *p = value ? *(Il2CppChar*)value : 0; return; } #if IL2CPP_SIZEOF_VOID_P == 4 case IL2CPP_TYPE_I: case IL2CPP_TYPE_U: #endif case IL2CPP_TYPE_I4: case IL2CPP_TYPE_U4: { int32_t *p = (int32_t*)dest; *p = value ? *(int32_t*)value : 0; return; } #if IL2CPP_SIZEOF_VOID_P == 8 case IL2CPP_TYPE_I: case IL2CPP_TYPE_U: #endif case IL2CPP_TYPE_I8: case IL2CPP_TYPE_U8: { int64_t *p = (int64_t*)dest; *p = value ? *(int64_t*)value : 0; return; } case IL2CPP_TYPE_R4: { float *p = (float*)dest; *p = value ? *(float*)value : 0; return; } case IL2CPP_TYPE_R8: { double *p = (double*)dest; *p = value ? *(double*)value : 0; return; } case IL2CPP_TYPE_STRING: case IL2CPP_TYPE_SZARRAY: case IL2CPP_TYPE_CLASS: case IL2CPP_TYPE_OBJECT: case IL2CPP_TYPE_ARRAY: gc::WriteBarrier::GenericStore((void**)dest, (deref_pointer ? *(void**)value : value)); return; case IL2CPP_TYPE_FNPTR: case IL2CPP_TYPE_PTR: { void* *p = (void**)dest; *p = deref_pointer ? *(void**)value : value; return; } case IL2CPP_TYPE_VALUETYPE: /* note that 't' and 'type->type' can be different */ if (type->type == IL2CPP_TYPE_VALUETYPE && Type::IsEnum(type)) { t = Class::GetEnumBaseType(Type::GetClass(type))->type; goto handle_enum; } else { Il2CppClass *klass = Class::FromIl2CppType(type); int size = Class::GetValueSize(klass, NULL); if (value == NULL) { memset(dest, 0, size); } else { memcpy(dest, value, size); gc::GarbageCollector::SetWriteBarrier(reinterpret_cast(dest), size); } } return; case IL2CPP_TYPE_GENERICINST: t = GenericClass::GetTypeDefinition(type->data.generic_class)->byval_arg.type; goto handle_enum; default: IL2CPP_ASSERT(0); } } const char* Field::GetData(FieldInfo *field) { if (field->type->attrs & FIELD_ATTRIBUTE_HAS_DEFAULT) { const Il2CppType* type = NULL; return Class::GetFieldDefaultValue(field, &type); } else if (field->type->attrs & FIELD_ATTRIBUTE_HAS_FIELD_RVA) { IL2CPP_NOT_IMPLEMENTED_NO_ASSERT(Field::GetData, "This works for array initialization data. Revisit any other RVA use case."); const Il2CppType* type = NULL; return Class::GetFieldDefaultValue(field, &type); } else { return NULL; } } bool Field::IsInstance(FieldInfo* field) { return (field->type->attrs & FIELD_ATTRIBUTE_STATIC) == 0; } bool Field::IsNormalStatic(FieldInfo* field) { if ((field->type->attrs & FIELD_ATTRIBUTE_STATIC) == 0) return false; if (field->offset == THREAD_STATIC_FIELD_OFFSET) return false; if ((field->type->attrs & FIELD_ATTRIBUTE_LITERAL) != 0) return false; return true; } bool Field::IsThreadStatic(FieldInfo* field) { if ((field->type->attrs & FIELD_ATTRIBUTE_STATIC) == 0) return false; if (field->offset != THREAD_STATIC_FIELD_OFFSET) return false; if ((field->type->attrs & FIELD_ATTRIBUTE_LITERAL) != 0) return false; return true; } void* Field::GetInstanceFieldDataPointer(void* instance, FieldInfo* field) { IL2CPP_ASSERT(il2cpp::vm::Field::IsInstance(field)); uint8_t* fieldPointer = ((uint8_t*)instance) + GetOffset(field); return field->parent->byval_arg.valuetype ? fieldPointer - sizeof(Il2CppObject) : fieldPointer; } } /* namespace vm */ } /* namespace il2cpp */