/** * \file * Handle to object in native code * * Authors: * - Ludovic Henry * - Aleksey Klieger * - Rodrigo Kumpera * * Copyright 2016 Dot net foundation. * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #ifndef __MONO_HANDLE_H__ #define __MONO_HANDLE_H__ #include #include #include #include #include #include #include G_BEGIN_DECLS /* Handle stack. The handle stack is designed so it's efficient to pop a large amount of entries at once. The stack is made out of a series of fixed size segments. To do bulk operations you use a stack mark. */ /* 3 is the number of fields besides the data in the struct; 128 words makes each chunk 512 or 1024 bytes each */ #define OBJECTS_PER_HANDLES_CHUNK (128 - 3) /* Whether this config needs stack watermark recording to know where to start scanning from. */ #ifdef HOST_WATCHOS #define MONO_NEEDS_STACK_WATERMARK 1 #endif typedef struct _HandleChunk HandleChunk; /* define MONO_HANDLE_TRACK_OWNER to store the file and line number of each call to MONO_HANDLE_NEW * in the handle stack. (This doubles the amount of memory used for handles, so it's only useful for debugging). */ /*#define MONO_HANDLE_TRACK_OWNER*/ /* define MONO_HANDLE_TRACK_SP to record the C stack pointer at the time of each HANDLE_FUNCTION_ENTER and * to ensure that when a new handle is allocated the previous newest handle is not lower in the stack. * This is useful to catch missing HANDLE_FUNCTION_ENTER / HANDLE_FUNCTION_RETURN pairs which could cause * handle leaks. */ /*#define MONO_HANDLE_TRACK_SP*/ typedef struct { gpointer o; /* MonoObject ptr or interior ptr */ #ifdef MONO_HANDLE_TRACK_OWNER const char *owner; gpointer backtrace_ips[7]; /* result of backtrace () at time of allocation */ #endif #ifdef MONO_HANDLE_TRACK_SP gpointer alloc_sp; /* sp from HandleStack:stackmark_sp at time of allocation */ #endif } HandleChunkElem; struct _HandleChunk { int size; //number of handles HandleChunk *prev, *next; HandleChunkElem elems [OBJECTS_PER_HANDLES_CHUNK]; }; typedef struct { HandleChunk *top; //alloc from here HandleChunk *bottom; //scan from here #ifdef MONO_HANDLE_TRACK_SP gpointer stackmark_sp; // C stack pointer top when from most recent mono_stack_mark_init #endif /* Chunk for storing interior pointers. Not extended right now */ HandleChunk *interior; } HandleStack; // Keep this in sync with RuntimeStructs.cs typedef struct { int size, interior_size; HandleChunk *chunk; #ifdef MONO_HANDLE_TRACK_SP gpointer prev_sp; // C stack pointer from prior mono_stack_mark_init #endif } HandleStackMark; typedef void *MonoRawHandle; typedef void (*GcScanFunc) (gpointer*, gpointer); #ifndef MONO_HANDLE_TRACK_OWNER MonoRawHandle mono_handle_new (MonoObject *object); MonoRawHandle mono_handle_new_full (gpointer rawptr, gboolean interior); MonoRawHandle mono_handle_new_interior (gpointer rawptr); #else MonoRawHandle mono_handle_new (MonoObject *object, const char* owner); MonoRawHandle mono_handle_new_full (gpointer rawptr, gboolean interior, const char *owner); MonoRawHandle mono_handle_new_interior (gpointer rawptr, const char *owner); #endif void mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise); gboolean mono_handle_stack_is_empty (HandleStack *stack); HandleStack* mono_handle_stack_alloc (void); void mono_handle_stack_free (HandleStack *handlestack); MonoRawHandle mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value); void mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name); void mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain); #ifdef MONO_HANDLE_TRACK_SP void mono_handle_chunk_leak_check (HandleStack *handles); #endif static inline void mono_stack_mark_init (MonoThreadInfo *info, HandleStackMark *stackmark) { #ifdef MONO_HANDLE_TRACK_SP gpointer sptop = (gpointer)(intptr_t)&stackmark; #endif HandleStack *handles = (HandleStack *)info->handle_stack; stackmark->size = handles->top->size; stackmark->chunk = handles->top; stackmark->interior_size = handles->interior->size; #ifdef MONO_HANDLE_TRACK_SP stackmark->prev_sp = handles->stackmark_sp; handles->stackmark_sp = sptop; #endif } static inline void mono_stack_mark_pop (MonoThreadInfo *info, HandleStackMark *stackmark) { HandleStack *handles = (HandleStack *)info->handle_stack; HandleChunk *old_top = stackmark->chunk; old_top->size = stackmark->size; mono_memory_write_barrier (); handles->top = old_top; handles->interior->size = stackmark->interior_size; #ifdef MONO_HANDLE_TRACK_SP mono_memory_write_barrier (); /* write to top before prev_sp */ handles->stackmark_sp = stackmark->prev_sp; #endif } /* Icall macros */ #define SETUP_ICALL_COMMON \ do { \ MonoError error; \ MonoThreadInfo *__info = mono_thread_info_current (); \ error_init (&error); \ #define CLEAR_ICALL_COMMON \ mono_error_set_pending_exception (&error); #define SETUP_ICALL_FRAME \ HandleStackMark __mark; \ mono_stack_mark_init (__info, &__mark); #define CLEAR_ICALL_FRAME \ mono_stack_mark_record_size (__info, &__mark, __FUNCTION__); \ mono_stack_mark_pop (__info, &__mark); #define CLEAR_ICALL_FRAME_VALUE(RESULT, HANDLE) \ mono_stack_mark_record_size (__info, &__mark, __FUNCTION__); \ (RESULT) = mono_stack_mark_pop_value (__info, &__mark, (HANDLE)); #define HANDLE_FUNCTION_ENTER() do { \ MonoThreadInfo *__info = mono_thread_info_current (); \ SETUP_ICALL_FRAME \ #define HANDLE_FUNCTION_RETURN() \ CLEAR_ICALL_FRAME; \ } while (0) #define HANDLE_FUNCTION_RETURN_VAL(VAL) \ CLEAR_ICALL_FRAME; \ return (VAL); \ } while (0) #define HANDLE_FUNCTION_RETURN_OBJ(HANDLE) \ do { \ void* __result = (MONO_HANDLE_RAW (HANDLE)); \ CLEAR_ICALL_FRAME; \ return __result; \ } while (0); } while (0); #define HANDLE_FUNCTION_RETURN_REF(TYPE, HANDLE) \ do { \ MonoRawHandle __result; \ CLEAR_ICALL_FRAME_VALUE (__result, ((MonoRawHandle) (HANDLE))); \ return MONO_HANDLE_CAST (TYPE, __result); \ } while (0); } while (0); #ifdef MONO_NEEDS_STACK_WATERMARK static void mono_thread_info_pop_stack_mark (MonoThreadInfo *info, void *old_mark) { info->stack_mark = old_mark; } static void* mono_thread_info_push_stack_mark (MonoThreadInfo *info, void *mark) { void *old = info->stack_mark; info->stack_mark = mark; return old; } #define SETUP_STACK_WATERMARK \ int __dummy; \ __builtin_unwind_init (); \ void *__old_stack_mark = mono_thread_info_push_stack_mark (__info, &__dummy); #define CLEAR_STACK_WATERMARK \ mono_thread_info_pop_stack_mark (__info, __old_stack_mark); #else #define SETUP_STACK_WATERMARK #define CLEAR_STACK_WATERMARK #endif #define ICALL_ENTRY() \ SETUP_ICALL_COMMON \ SETUP_ICALL_FRAME \ SETUP_STACK_WATERMARK #define ICALL_RETURN() \ do { \ CLEAR_STACK_WATERMARK \ CLEAR_ICALL_COMMON \ CLEAR_ICALL_FRAME \ return; \ } while (0); } while (0) #define ICALL_RETURN_VAL(VAL) \ do { \ CLEAR_STACK_WATERMARK \ CLEAR_ICALL_COMMON \ CLEAR_ICALL_FRAME \ return VAL; \ } while (0); } while (0) #define ICALL_RETURN_OBJ(HANDLE) \ do { \ CLEAR_STACK_WATERMARK \ CLEAR_ICALL_COMMON \ void* __ret = MONO_HANDLE_RAW (HANDLE); \ CLEAR_ICALL_FRAME \ return __ret; \ } while (0); } while (0) /* Handle macros/functions */ #ifdef ENABLE_CHECKED_BUILD void mono_handle_verify (MonoRawHandle handle); #define HANDLE_INVARIANTS(H) mono_handle_verify((void*)(H)) #else #define HANDLE_INVARIANTS(H) (0) #endif #define TYPED_HANDLE_PAYLOAD_NAME(TYPE) TYPE ## HandlePayload #define TYPED_HANDLE_NAME(TYPE) TYPE ## Handle #define TYPED_OUT_HANDLE_NAME(TYPE) TYPE ## HandleOut #ifdef MONO_HANDLE_TRACK_OWNER #define STRINGIFY_(x) #x #define STRINGIFY(x) STRINGIFY_(x) #define HANDLE_OWNER_STRINGIFY(file,lineno) (const char*) (file ":" STRINGIFY(lineno)) #endif /* * TYPED_HANDLE_DECL(SomeType): * Expands to a decl for handles to SomeType and to an internal payload struct. * * For example, TYPED_HANDLE_DECL(MonoObject) (see below) expands to: * * typedef struct { * MonoObject *__raw; * } MonoObjectHandlePayload; * * typedef MonoObjectHandlePayload* MonoObjectHandle; * typedef MonoObjectHandlePayload* MonoObjectHandleOut; */ #define TYPED_HANDLE_DECL(TYPE) \ typedef struct { TYPE *__raw; } TYPED_HANDLE_PAYLOAD_NAME (TYPE) ; \ typedef TYPED_HANDLE_PAYLOAD_NAME (TYPE) * TYPED_HANDLE_NAME (TYPE); \ typedef TYPED_HANDLE_PAYLOAD_NAME (TYPE) * TYPED_OUT_HANDLE_NAME (TYPE) /* * TYPED_VALUE_HANDLE_DECL(SomeType): * Expands to a decl for handles to SomeType (which is a managed valuetype (likely a struct) of some sort) and to an internal payload struct. * For example TYPED_HANDLE_DECL(MonoMethodInfo) expands to: * * typedef struct { * MonoMethodInfo *__raw; * } MonoMethodInfoHandlePayload; * typedef MonoMethodInfoHandlePayload* MonoMethodInfoHandle; */ #define TYPED_VALUE_HANDLE_DECL(TYPE) TYPED_HANDLE_DECL(TYPE) /* Have to double expand because MONO_STRUCT_OFFSET is doing token pasting on cross-compilers. */ #define MONO_HANDLE_PAYLOAD_OFFSET_(PayloadType) MONO_STRUCT_OFFSET(PayloadType, __raw) #define MONO_HANDLE_PAYLOAD_OFFSET(TYPE) MONO_HANDLE_PAYLOAD_OFFSET_(TYPED_HANDLE_PAYLOAD_NAME (TYPE)) #define MONO_HANDLE_INIT ((void*) mono_null_value_handle) #define NULL_HANDLE mono_null_value_handle //XXX add functions to get/set raw, set field, set field to null, set array, set array to null #define MONO_HANDLE_RAW(HANDLE) (HANDLE_INVARIANTS (HANDLE), ((HANDLE)->__raw)) #define MONO_HANDLE_DCL(TYPE, NAME) TYPED_HANDLE_NAME(TYPE) NAME = MONO_HANDLE_NEW (TYPE, (NAME ## _raw)) #ifndef MONO_HANDLE_TRACK_OWNER #define MONO_HANDLE_NEW(TYPE, VALUE) (TYPED_HANDLE_NAME(TYPE))( mono_handle_new ((MonoObject*)(VALUE)) ) #else #define MONO_HANDLE_NEW(TYPE, VALUE) (TYPED_HANDLE_NAME(TYPE))( mono_handle_new ((MonoObject*)(VALUE), HANDLE_OWNER_STRINGIFY(__FILE__, __LINE__))) #endif #define MONO_HANDLE_CAST(TYPE, VALUE) (TYPED_HANDLE_NAME(TYPE))( VALUE ) #define MONO_HANDLE_IS_NULL(HANDLE) (MONO_HANDLE_RAW(HANDLE) == NULL) /* WARNING WARNING WARNING The following functions require a particular evaluation ordering to ensure correctness. We must not have exposed handles while any sort of evaluation is happening as that very evaluation might trigger a safepoint and break us. This is why we evaluate index and value before any call to MONO_HANDLE_RAW or other functions that deal with naked objects. */ #define MONO_HANDLE_SETRAW(HANDLE, FIELD, VALUE) do { \ MonoObject *__val = (MonoObject*)(VALUE); \ MONO_OBJECT_SETREF (MONO_HANDLE_RAW (HANDLE), FIELD, __val); \ } while (0) #define MONO_HANDLE_SET(HANDLE, FIELD, VALUE) do { \ MonoObjectHandle __val = MONO_HANDLE_CAST (MonoObject, VALUE); \ MONO_OBJECT_SETREF (MONO_HANDLE_RAW (HANDLE), FIELD, MONO_HANDLE_RAW (__val)); \ } while (0) /* N.B. RESULT is evaluated before HANDLE */ #define MONO_HANDLE_GET(RESULT, HANDLE, FIELD) do { \ MonoObjectHandle __dest = MONO_HANDLE_CAST(MonoObject, RESULT); \ mono_gc_wbarrier_generic_store (&__dest->__raw, (MonoObject*)(MONO_HANDLE_RAW(HANDLE)->FIELD)); \ } while (0) #define MONO_HANDLE_NEW_GET(TYPE,HANDLE,FIELD) (MONO_HANDLE_NEW(TYPE,MONO_HANDLE_RAW(HANDLE)->FIELD)) #define MONO_HANDLE_GETVAL(HANDLE, FIELD) (MONO_HANDLE_RAW(HANDLE)->FIELD) /* VS doesn't support typeof :( :( :( */ #define MONO_HANDLE_SETVAL(HANDLE, FIELD, TYPE, VALUE) do { \ TYPE __val = (VALUE); \ MONO_HANDLE_RAW (HANDLE)->FIELD = __val; \ } while (0) #define MONO_HANDLE_ARRAY_SETREF(HANDLE, IDX, VALUE) do { \ int __idx = (IDX); \ MonoObjectHandle __val = MONO_HANDLE_CAST (MonoObject, VALUE); \ mono_array_setref_fast (MONO_HANDLE_RAW (HANDLE), __idx, MONO_HANDLE_RAW (__val)); \ } while (0) #define MONO_HANDLE_ARRAY_SETVAL(HANDLE, TYPE, IDX, VALUE) do { \ int __idx = (IDX); \ TYPE __val = (VALUE); \ mono_array_set (MONO_HANDLE_RAW (HANDLE), TYPE, __idx, __val); \ } while (0) #define MONO_HANDLE_ARRAY_SETRAW(HANDLE, IDX, VALUE) do { \ int __idx = (IDX); \ MonoObject *__val = (MonoObject*)(VALUE); \ mono_array_setref_fast (MONO_HANDLE_RAW (HANDLE), __idx, __val); \ } while (0) /* N.B. DEST is evaluated AFTER all the other arguments */ #define MONO_HANDLE_ARRAY_GETVAL(DEST, HANDLE, TYPE, IDX) do { \ MonoArrayHandle __arr = (HANDLE); \ int __idx = (IDX); \ TYPE __result = mono_array_get (MONO_HANDLE_RAW(__arr), TYPE, __idx); \ (DEST) = __result; \ } while (0) #define MONO_HANDLE_ARRAY_GETREF(DEST, HANDLE, IDX) do { \ mono_handle_array_getref (MONO_HANDLE_CAST(MonoObject, (DEST)), (HANDLE), (IDX)); \ } while (0) #define MONO_HANDLE_ASSIGN(DESTH, SRCH) \ mono_handle_assign (MONO_HANDLE_CAST (MonoObject, (DESTH)), MONO_HANDLE_CAST(MonoObject, (SRCH))) #define MONO_HANDLE_DOMAIN(HANDLE) (mono_object_domain (MONO_HANDLE_RAW (MONO_HANDLE_CAST (MonoObject, HANDLE)))) /* Given an object and a MonoClassField, return the value (must be non-object) * of the field. It's the caller's responsibility to check that the object is * of the correct class. */ #define MONO_HANDLE_GET_FIELD_VAL(HANDLE,TYPE,FIELD) *(TYPE *)(mono_handle_unsafe_field_addr (MONO_HANDLE_CAST (MonoObject, (HANDLE)), (FIELD))) #define MONO_HANDLE_NEW_GET_FIELD(HANDLE,TYPE,FIELD) MONO_HANDLE_NEW (TYPE, *(TYPE**)(mono_handle_unsafe_field_addr (MONO_HANDLE_CAST (MonoObject, (HANDLE)), (FIELD)))) #define MONO_HANDLE_SET_FIELD_VAL(HANDLE,TYPE,FIELD,VAL) do { \ MonoObjectHandle __obj = (HANDLE); \ MonoClassField *__field = (FIELD); \ TYPE __value = (VAL); \ *(TYPE*)(mono_handle_unsafe_field_addr (__obj, __field)) = __value; \ } while (0) #define MONO_HANDLE_SET_FIELD_REF(HANDLE,FIELD,VALH) do { \ MonoObjectHandle __obj = MONO_HANDLE_CAST (MonoObject, (HANDLE)); \ MonoClassField *__field = (FIELD); \ MonoObjectHandle __value = MONO_HANDLE_CAST (MonoObject, (VALH)); \ mono_gc_wbarrier_generic_store (mono_handle_unsafe_field_addr (__obj, __field), MONO_HANDLE_RAW (__value)); \ } while (0) /* Baked typed handles we all want */ TYPED_HANDLE_DECL (MonoString); TYPED_HANDLE_DECL (MonoArray); TYPED_HANDLE_DECL (MonoObject); TYPED_HANDLE_DECL (MonoException); TYPED_HANDLE_DECL (MonoAppContext); /* Unfortunately MonoThreadHandle is already a typedef used for something unrelated. So * the coop handle for MonoThread* is MonoThreadObjectHandle. */ typedef MonoThread MonoThreadObject; TYPED_HANDLE_DECL (MonoThreadObject); #define NULL_HANDLE_STRING MONO_HANDLE_CAST(MonoString, NULL_HANDLE) /* This is the constant for a handle that points nowhere. Init values to it. */ extern const MonoObjectHandle mono_null_value_handle; static inline void mono_handle_assign (MonoObjectHandleOut dest, MonoObjectHandle src) { mono_gc_wbarrier_generic_store (&dest->__raw, src ? MONO_HANDLE_RAW(src) : NULL); } /* It is unsafe to call this function directly - it does not pin the handle! Use MONO_HANDLE_GET_FIELD_VAL(). */ static inline gchar* mono_handle_unsafe_field_addr (MonoObjectHandle h, MonoClassField *field) { return ((gchar *)MONO_HANDLE_RAW (h)) + field->offset; } //FIXME this should go somewhere else MonoStringHandle mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error); MonoArrayHandle mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error); MonoArrayHandle mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error); uintptr_t mono_array_handle_length (MonoArrayHandle arr); static inline void mono_handle_array_getref (MonoObjectHandleOut dest, MonoArrayHandle array, uintptr_t index) { mono_gc_wbarrier_generic_store (&dest->__raw, mono_array_get (MONO_HANDLE_RAW (array),gpointer, index)); } #define mono_handle_class(o) mono_object_class (MONO_HANDLE_RAW (o)) /* Local handles to global GC handles and back */ uint32_t mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned); MonoObjectHandle mono_gchandle_get_target_handle (uint32_t gchandle); void mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len); /* Pins the MonoArray using a gchandle and returns a pointer to the * element with the given index (where each element is of the given * size. Call mono_gchandle_free to unpin. */ gpointer mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t index, uint32_t *gchandle); #define MONO_ARRAY_HANDLE_PIN(handle,type,index,gchandle_out) mono_array_handle_pin_with_size (MONO_HANDLE_CAST(MonoArray,(handle)), sizeof (type), (index), (gchandle_out)) gunichar2 * mono_string_handle_pin_chars (MonoStringHandle s, uint32_t *gchandle_out); gpointer mono_object_handle_pin_unbox (MonoObjectHandle boxed_valuetype_obj, uint32_t *gchandle_out); void mono_error_set_exception_handle (MonoError *error, MonoExceptionHandle exc); MonoAppContextHandle mono_context_get_handle (void); void mono_context_set_handle (MonoAppContextHandle new_context); G_END_DECLS #endif /* __MONO_HANDLE_H__ */