diff options
-rw-r--r-- | ext/ffi_c/DynamicLibrary.c | 28 | ||||
-rw-r--r-- | ext/ffi_c/Function.c | 33 | ||||
-rw-r--r-- | ext/ffi_c/MappedType.c | 21 | ||||
-rw-r--r-- | ext/ffi_c/MappedType.h | 1 | ||||
-rw-r--r-- | ext/ffi_c/Struct.c | 79 | ||||
-rw-r--r-- | ext/ffi_c/StructByValue.c | 21 | ||||
-rw-r--r-- | ext/ffi_c/Type.c | 39 | ||||
-rw-r--r-- | ext/ffi_c/Type.h | 2 | ||||
-rw-r--r-- | ext/ffi_c/Variadic.c | 25 | ||||
-rw-r--r-- | spec/ffi/function_spec.rb | 8 | ||||
-rw-r--r-- | spec/ffi/library_spec.rb | 17 | ||||
-rw-r--r-- | spec/ffi/struct_spec.rb | 11 | ||||
-rw-r--r-- | spec/ffi/type_spec.rb | 43 |
13 files changed, 243 insertions, 85 deletions
diff --git a/ext/ffi_c/DynamicLibrary.c b/ext/ffi_c/DynamicLibrary.c index 108394c..c69074a 100644 --- a/ext/ffi_c/DynamicLibrary.c +++ b/ext/ffi_c/DynamicLibrary.c @@ -50,7 +50,6 @@ typedef struct LibrarySymbol_ { Pointer base; - VALUE library; VALUE name; } LibrarySymbol; @@ -62,6 +61,7 @@ static void library_free(void *); static VALUE symbol_allocate(VALUE klass); static VALUE symbol_new(VALUE library, void* address, VALUE name); static void symbol_mark(void *data); +static size_t symbol_memsize(const void *data); static const rb_data_type_t rbffi_library_data_type = { .wrap_struct_name = "FFI::DynamicLibrary", @@ -78,10 +78,12 @@ static const rb_data_type_t library_symbol_data_type = { .function = { .dmark = symbol_mark, .dfree = RUBY_TYPED_DEFAULT_FREE, - .dsize = NULL, + .dsize = symbol_memsize, }, .parent = &rbffi_pointer_data_type, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; static VALUE LibraryClass = Qnil, SymbolClass = Qnil; @@ -223,9 +225,8 @@ symbol_allocate(VALUE klass) { LibrarySymbol* sym; VALUE obj = TypedData_Make_Struct(klass, LibrarySymbol, &library_symbol_data_type, sym); - sym->name = Qnil; - sym->library = Qnil; - sym->base.rbParent = Qnil; + RB_OBJ_WRITE(obj, &sym->base.rbParent, Qnil); + RB_OBJ_WRITE(obj, &sym->name, Qnil); return obj; } @@ -254,8 +255,8 @@ symbol_new(VALUE library, void* address, VALUE name) sym->base.memory.size = LONG_MAX; sym->base.memory.typeSize = 1; sym->base.memory.flags = MEM_RD | MEM_WR; - sym->library = library; - sym->name = name; + RB_OBJ_WRITE(obj, &sym->base.rbParent, library); + RB_OBJ_WRITE(obj, &sym->name, name); return obj; } @@ -264,10 +265,16 @@ static void symbol_mark(void *data) { LibrarySymbol *sym = (LibrarySymbol *)data; - rb_gc_mark(sym->library); + rb_gc_mark(sym->base.rbParent); rb_gc_mark(sym->name); } +static size_t +symbol_memsize(const void *data) +{ + return sizeof(LibrarySymbol); +} + /* * call-seq: inspect * @return [String] @@ -280,7 +287,7 @@ symbol_inspect(VALUE self) char buf[256]; TypedData_Get_Struct(self, LibrarySymbol, &library_symbol_data_type, sym); - snprintf(buf, sizeof(buf), "#<FFI::Library::Symbol name=%s address=%p>", + snprintf(buf, sizeof(buf), "#<FFI::DynamicLibrary::Symbol name=%s address=%p>", StringValueCStr(sym->name), sym->base.memory.address); return rb_str_new2(buf); } @@ -356,4 +363,3 @@ rbffi_DynamicLibrary_Init(VALUE moduleFFI) #undef DEF } - diff --git a/ext/ffi_c/Function.c b/ext/ffi_c/Function.c index 20822da..b73e8b7 100644 --- a/ext/ffi_c/Function.c +++ b/ext/ffi_c/Function.c @@ -77,6 +77,7 @@ typedef struct Function_ { static void function_mark(void *data); static void function_free(void *data); +static size_t function_memsize(const void *data); static VALUE function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc); static void callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data); static bool callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize); @@ -100,10 +101,12 @@ static const rb_data_type_t function_data_type = { .function = { .dmark = function_mark, .dfree = function_free, - .dsize = NULL, + .dsize = function_memsize, }, .parent = &rbffi_pointer_data_type, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; VALUE rbffi_FunctionClass = Qnil; @@ -153,9 +156,9 @@ function_allocate(VALUE klass) obj = TypedData_Make_Struct(klass, Function, &function_data_type, fn); fn->base.memory.flags = MEM_RD; - fn->base.rbParent = Qnil; - fn->rbProc = Qnil; - fn->rbFunctionInfo = Qnil; + RB_OBJ_WRITE(obj, &fn->base.rbParent, Qnil); + RB_OBJ_WRITE(obj, &fn->rbProc, Qnil); + RB_OBJ_WRITE(obj, &fn->rbFunctionInfo, Qnil); fn->autorelease = true; return obj; @@ -185,6 +188,20 @@ function_free(void *data) xfree(fn); } +static size_t +function_memsize(const void *data) +{ + const Function *fn = (const Function *)data; + size_t memsize = sizeof(Function); + + // Would be nice to better account for MethodHandle and Closure too. + if (fn->closure) { + memsize += sizeof(Closure); + } + + return memsize; +} + /* * @param [Type, Symbol] return_type return type for the function * @param [Array<Type, Symbol>] param_types array of parameters types @@ -313,7 +330,7 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc) TypedData_Get_Struct(self, Function, &function_data_type, fn); - fn->rbFunctionInfo = rbFunctionInfo; + RB_OBJ_WRITE(self, &fn->rbFunctionInfo, rbFunctionInfo); TypedData_Get_Struct(fn->rbFunctionInfo, FunctionType, &rbffi_fntype_data_type, fn->info); @@ -321,7 +338,7 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc) Pointer* orig; TypedData_Get_Struct(rbProc, Pointer, &rbffi_pointer_data_type, orig); fn->base.memory = orig->memory; - fn->base.rbParent = rbProc; + RB_OBJ_WRITE(self, &fn->base.rbParent, rbProc); } else if (rb_obj_is_kind_of(rbProc, rb_cProc) || rb_respond_to(rbProc, id_call)) { if (fn->info->closurePool == NULL) { @@ -357,7 +374,7 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc) rb_obj_classname(rbProc)); } - fn->rbProc = rbProc; + RB_OBJ_WRITE(self, &fn->rbProc, rbProc); return self; } diff --git a/ext/ffi_c/MappedType.c b/ext/ffi_c/MappedType.c index 304de86..ed05c9d 100644 --- a/ext/ffi_c/MappedType.c +++ b/ext/ffi_c/MappedType.c @@ -39,6 +39,7 @@ static VALUE mapped_allocate(VALUE); static VALUE mapped_initialize(VALUE, VALUE); static void mapped_mark(void *); +static size_t mapped_memsize(const void *); static ID id_native_type, id_to_native, id_from_native; VALUE rbffi_MappedTypeClass = Qnil; @@ -48,10 +49,12 @@ static const rb_data_type_t mapped_type_data_type = { .function = { .dmark = mapped_mark, .dfree = RUBY_TYPED_DEFAULT_FREE, - .dsize = NULL, + .dsize = mapped_memsize, }, .parent = &rbffi_type_data_type, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; @@ -62,8 +65,8 @@ mapped_allocate(VALUE klass) VALUE obj = TypedData_Make_Struct(klass, MappedType, &mapped_type_data_type, m); - m->rbConverter = Qnil; - m->rbType = Qnil; + RB_OBJ_WRITE(obj, &m->rbConverter, Qnil); + RB_OBJ_WRITE(obj, &m->rbType, Qnil); m->type = NULL; m->base.nativeType = NATIVE_MAPPED; m->base.ffiType = &ffi_type_void; @@ -95,12 +98,12 @@ mapped_initialize(VALUE self, VALUE rbConverter) } TypedData_Get_Struct(self, MappedType, &mapped_type_data_type, m); - m->rbType = rb_funcall2(rbConverter, id_native_type, 0, NULL); + RB_OBJ_WRITE(self, &m->rbType, rb_funcall2(rbConverter, id_native_type, 0, NULL)); if (!(rb_obj_is_kind_of(m->rbType, rbffi_TypeClass))) { rb_raise(rb_eTypeError, "native_type did not return instance of FFI::Type"); } - m->rbConverter = rbConverter; + RB_OBJ_WRITE(self, &m->rbConverter, rbConverter); TypedData_Get_Struct(m->rbType, Type, &rbffi_type_data_type, m->type); m->base.ffiType = m->type->ffiType; @@ -115,6 +118,12 @@ mapped_mark(void* data) rb_gc_mark(m->rbConverter); } +static size_t +mapped_memsize(const void *data) +{ + return sizeof(MappedType); +} + /* * call-seq: mapped_type.native_type * @return [Type] diff --git a/ext/ffi_c/MappedType.h b/ext/ffi_c/MappedType.h index ac86a3c..9f6f9ee 100644 --- a/ext/ffi_c/MappedType.h +++ b/ext/ffi_c/MappedType.h @@ -43,7 +43,6 @@ typedef struct MappedType_ { Type* type; VALUE rbConverter; VALUE rbType; - } MappedType; void rbffi_MappedType_Init(VALUE moduleFFI); diff --git a/ext/ffi_c/Struct.c b/ext/ffi_c/Struct.c index 6144018..4f57cf3 100644 --- a/ext/ffi_c/Struct.c +++ b/ext/ffi_c/Struct.c @@ -63,19 +63,23 @@ typedef struct InlineArray_ { static void struct_mark(void *data); static void struct_free(void *data); +static size_t struct_memsize(const void *); static VALUE struct_class_layout(VALUE klass); -static void struct_malloc(Struct* s); +static void struct_malloc(VALUE self, Struct* s); static void inline_array_mark(void *); -static void store_reference_value(StructField* f, Struct* s, VALUE value); +static size_t inline_array_memsize(const void *); +static void store_reference_value(VALUE self, StructField* f, Struct* s, VALUE value); const rb_data_type_t rbffi_struct_data_type = { /* extern */ .wrap_struct_name = "FFI::Struct", .function = { .dmark = struct_mark, .dfree = struct_free, - .dsize = NULL, + .dsize = struct_memsize, }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; VALUE rbffi_StructClass = Qnil; @@ -99,8 +103,8 @@ struct_allocate(VALUE klass) Struct* s; VALUE obj = TypedData_Make_Struct(klass, Struct, &rbffi_struct_data_type, s); - s->rbPointer = Qnil; - s->rbLayout = Qnil; + RB_OBJ_WRITE(obj, &s->rbPointer, Qnil); + RB_OBJ_WRITE(obj, &s->rbLayout, Qnil); return obj; } @@ -125,9 +129,10 @@ struct_initialize(int argc, VALUE* argv, VALUE self) /* Call up into ruby code to adjust the layout */ if (nargs > 1) { - s->rbLayout = rb_funcall2(CLASS_OF(self), id_layout, (int) RARRAY_LEN(rest), RARRAY_PTR(rest)); + VALUE rbLayout = rb_funcall2(CLASS_OF(self), id_layout, (int) RARRAY_LEN(rest), RARRAY_PTR(rest)); + RB_OBJ_WRITE(self, &s->rbLayout, rbLayout); } else { - s->rbLayout = struct_class_layout(klass); + RB_OBJ_WRITE(self, &s->rbLayout, struct_class_layout(klass)); } if (!rb_obj_is_kind_of(s->rbLayout, rbffi_StructLayoutClass)) { @@ -138,9 +143,9 @@ struct_initialize(int argc, VALUE* argv, VALUE self) if (rbPointer != Qnil) { s->pointer = MEMORY(rbPointer); - s->rbPointer = rbPointer; + RB_OBJ_WRITE(self, &s->rbPointer, rbPointer); } else { - struct_malloc(s); + struct_malloc(self, s); } return self; @@ -163,7 +168,7 @@ struct_initialize_copy(VALUE self, VALUE other) return self; } - dst->rbLayout = src->rbLayout; + RB_OBJ_WRITE(self, &dst->rbLayout, src->rbLayout); dst->layout = src->layout; /* @@ -172,17 +177,20 @@ struct_initialize_copy(VALUE self, VALUE other) * be longer than just this struct. */ if (src->pointer->address != NULL) { - dst->rbPointer = rbffi_MemoryPointer_NewInstance(1, src->layout->size, false); + RB_OBJ_WRITE(self, &dst->rbPointer, rbffi_MemoryPointer_NewInstance(1, src->layout->size, false)); dst->pointer = MEMORY(dst->rbPointer); memcpy(dst->pointer->address, src->pointer->address, src->layout->size); } else { - dst->rbPointer = src->rbPointer; + RB_OBJ_WRITE(self, &dst->rbPointer, src->rbPointer); dst->pointer = src->pointer; } if (src->layout->referenceFieldCount > 0) { dst->rbReferences = ALLOC_N(VALUE, dst->layout->referenceFieldCount); memcpy(dst->rbReferences, src->rbReferences, dst->layout->referenceFieldCount * sizeof(VALUE)); + for (size_t index = 0; index < dst->layout->referenceFieldCount; index++) { + RB_OBJ_WRITTEN(self, Qundef, &dst->rbReferences[index]); + } } return self; @@ -214,7 +222,7 @@ struct_layout(VALUE self) } if (s->layout == NULL) { - s->rbLayout = struct_class_layout(CLASS_OF(self)); + RB_OBJ_WRITE(self, &s->rbLayout, struct_class_layout(CLASS_OF(self))); TypedData_Get_Struct(s->rbLayout, StructLayout, &rbffi_struct_layout_data_type, s->layout); } @@ -232,18 +240,17 @@ struct_validate(VALUE self) } if (s->pointer == NULL) { - struct_malloc(s); + struct_malloc(self, s); } return s; } static void -struct_malloc(Struct* s) +struct_malloc(VALUE self, Struct* s) { if (s->rbPointer == Qnil) { - s->rbPointer = rbffi_MemoryPointer_NewInstance(s->layout->size, 1, true); - + RB_OBJ_WRITE(self, &s->rbPointer, rbffi_MemoryPointer_NewInstance(s->layout->size, 1, true)); } else if (!rb_obj_is_kind_of(s->rbPointer, rbffi_AbstractMemoryClass)) { rb_raise(rb_eRuntimeError, "invalid pointer in struct"); } @@ -270,9 +277,15 @@ struct_free(void *data) xfree(s); } +static size_t +struct_memsize(const void *data) +{ + const Struct *s = (const Struct *)data; + return sizeof(Struct) + (s->layout->referenceFieldCount * sizeof(VALUE)); +} static void -store_reference_value(StructField* f, Struct* s, VALUE value) +store_reference_value(VALUE self, StructField* f, Struct* s, VALUE value) { if (unlikely(f->referenceIndex == -1)) { rb_raise(rb_eRuntimeError, "put_reference_value called for non-reference type"); @@ -282,11 +295,11 @@ store_reference_value(StructField* f, Struct* s, VALUE value) int i; s->rbReferences = ALLOC_N(VALUE, s->layout->referenceFieldCount); for (i = 0; i < s->layout->referenceFieldCount; ++i) { - s->rbReferences[i] = Qnil; + RB_OBJ_WRITE(self, &s->rbReferences[i], Qnil); } } - s->rbReferences[f->referenceIndex] = value; + RB_OBJ_WRITE(self, &s->rbReferences[f->referenceIndex], value); } @@ -371,7 +384,7 @@ struct_aset(VALUE self, VALUE fieldName, VALUE value) } if (f->referenceRequired) { - store_reference_value(f, s, value); + store_reference_value(self, f, s, value); } return value; @@ -407,7 +420,7 @@ struct_set_pointer(VALUE self, VALUE pointer) } s->pointer = MEMORY(pointer); - s->rbPointer = pointer; + RB_OBJ_WRITE(self, &s->rbPointer, pointer); rb_ivar_set(self, id_pointer_ivar, pointer); return self; @@ -508,9 +521,11 @@ static const rb_data_type_t inline_array_data_type = { .function = { .dmark = inline_array_mark, .dfree = RUBY_TYPED_DEFAULT_FREE, - .dsize = NULL, + .dsize = inline_array_memsize, }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; static VALUE @@ -520,8 +535,8 @@ inline_array_allocate(VALUE klass) VALUE obj; obj = TypedData_Make_Struct(klass, InlineArray, &inline_array_data_type, array); - array->rbField = Qnil; - array->rbMemory = Qnil; + RB_OBJ_WRITE(obj, &array->rbMemory, Qnil); + RB_OBJ_WRITE(obj, &array->rbField, Qnil); return obj; } @@ -534,6 +549,12 @@ inline_array_mark(void *data) rb_gc_mark(array->rbMemory); } +static size_t +inline_array_memsize(const void *data) +{ + return sizeof(InlineArray); +} + /* * Document-method: FFI::Struct::InlineArray#initialize * call-seq: initialize(memory, field) @@ -547,8 +568,8 @@ inline_array_initialize(VALUE self, VALUE rbMemory, VALUE rbField) InlineArray* array; TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array); - array->rbMemory = rbMemory; - array->rbField = rbField; + RB_OBJ_WRITE(self, &array->rbMemory, rbMemory); + RB_OBJ_WRITE(self, &array->rbField, rbField); TypedData_Get_Struct(rbMemory, AbstractMemory, &rbffi_abstract_memory_data_type, array->memory); TypedData_Get_Struct(rbField, StructField, &rbffi_struct_field_data_type, array->field); diff --git a/ext/ffi_c/StructByValue.c b/ext/ffi_c/StructByValue.c index b4927e6..e2c589d 100644 --- a/ext/ffi_c/StructByValue.c +++ b/ext/ffi_c/StructByValue.c @@ -51,6 +51,7 @@ static VALUE sbv_allocate(VALUE); static VALUE sbv_initialize(VALUE, VALUE); static void sbv_mark(void *); static void sbv_free(void *); +static size_t sbv_memsize(const void *); VALUE rbffi_StructByValueClass = Qnil; @@ -59,10 +60,12 @@ static const rb_data_type_t sbv_type_data_type = { .function = { .dmark = sbv_mark, .dfree = sbv_free, - .dsize = NULL, + .dsize = sbv_memsize, }, .parent = &rbffi_type_data_type, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; static VALUE @@ -72,8 +75,8 @@ sbv_allocate(VALUE klass) VALUE obj = TypedData_Make_Struct(klass, StructByValue, &sbv_type_data_type, sbv); - sbv->rbStructClass = Qnil; - sbv->rbStructLayout = Qnil; + RB_OBJ_WRITE(obj, &sbv->rbStructClass, Qnil); + RB_OBJ_WRITE(obj, &sbv->rbStructLayout, Qnil); sbv->base.nativeType = NATIVE_STRUCT; sbv->base.ffiType = xcalloc(1, sizeof(*sbv->base.ffiType)); @@ -98,8 +101,8 @@ sbv_initialize(VALUE self, VALUE rbStructClass) TypedData_Get_Struct(rbLayout, StructLayout, &rbffi_struct_layout_data_type, layout); TypedData_Get_Struct(self, StructByValue, &sbv_type_data_type, sbv); - sbv->rbStructClass = rbStructClass; - sbv->rbStructLayout = rbLayout; + RB_OBJ_WRITE(self, &sbv->rbStructClass, rbStructClass); + RB_OBJ_WRITE(self, &sbv->rbStructLayout, rbLayout); /* We can just use everything from the ffi_type directly */ *sbv->base.ffiType = *layout->base.ffiType; @@ -123,6 +126,12 @@ sbv_free(void *data) xfree(sbv); } +static size_t +sbv_memsize(const void *data) +{ + const StructByValue *sbv = (const StructByValue *)data; + return sizeof(StructByValue) + sizeof(*sbv->base.ffiType); +} static VALUE sbv_layout(VALUE self) diff --git a/ext/ffi_c/Type.c b/ext/ffi_c/Type.c index d940e94..6089a14 100644 --- a/ext/ffi_c/Type.c +++ b/ext/ffi_c/Type.c @@ -39,13 +39,14 @@ #include "Types.h" #include "Type.h" +static size_t type_memsize(const void *); typedef struct BuiltinType_ { Type type; - char* name; + const char* name; } BuiltinType; -static void builtin_type_free(void *); +static size_t builtin_type_memsize(const void *); VALUE rbffi_TypeClass = Qnil; @@ -59,22 +60,32 @@ const rb_data_type_t rbffi_type_data_type = { /* extern */ .function = { .dmark = NULL, .dfree = RUBY_TYPED_DEFAULT_FREE, - .dsize = NULL, + .dsize = type_memsize, }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; static const rb_data_type_t builtin_type_data_type = { .wrap_struct_name = "FFI::Type::Builtin", .function = { .dmark = NULL, - .dfree = builtin_type_free, - .dsize = NULL, + .dfree = RUBY_TYPED_DEFAULT_FREE, + .dsize = builtin_type_memsize, }, .parent = &rbffi_type_data_type, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; +static size_t +type_memsize(const void *data) +{ + return sizeof(Type); +} + static VALUE type_allocate(VALUE klass) { @@ -83,7 +94,7 @@ type_allocate(VALUE klass) type->nativeType = -1; type->ffiType = &ffi_type_void; - + return obj; } @@ -170,20 +181,18 @@ builtin_type_new(VALUE klass, int nativeType, ffi_type* ffiType, const char* nam VALUE obj = Qnil; obj = TypedData_Make_Struct(klass, BuiltinType, &builtin_type_data_type, type); - - type->name = strdup(name); + + type->name = name; type->type.nativeType = nativeType; type->type.ffiType = ffiType; return obj; } -static void -builtin_type_free(void *data) +static size_t +builtin_type_memsize(const void *data) { - BuiltinType *type = (BuiltinType *)data; - free(type->name); - xfree(type); + return sizeof(BuiltinType) + sizeof(ffi_type); } /* diff --git a/ext/ffi_c/Type.h b/ext/ffi_c/Type.h index 74a931b..b6de634 100644 --- a/ext/ffi_c/Type.h +++ b/ext/ffi_c/Type.h @@ -44,7 +44,7 @@ extern "C" { typedef struct Type_ Type; #include "Types.h" - + struct Type_ { NativeType nativeType; ffi_type* ffiType; diff --git a/ext/ffi_c/Variadic.c b/ext/ffi_c/Variadic.c index e379f4b..014fac2 100644 --- a/ext/ffi_c/Variadic.c +++ b/ext/ffi_c/Variadic.c @@ -66,6 +66,7 @@ static VALUE variadic_allocate(VALUE klass); static VALUE variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE rbReturnType, VALUE options); static void variadic_mark(void *); +static size_t variadic_memsize(const void *); static VALUE classVariadicInvoker = Qnil; @@ -74,9 +75,11 @@ static const rb_data_type_t variadic_data_type = { .function = { .dmark = variadic_mark, .dfree = RUBY_TYPED_DEFAULT_FREE, - .dsize = NULL, + .dsize = variadic_memsize, }, - .flags = RUBY_TYPED_FREE_IMMEDIATELY + // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE() + // macro to update VALUE references, as to trigger write barriers. + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; @@ -86,9 +89,9 @@ variadic_allocate(VALUE klass) VariadicInvoker *invoker; VALUE obj = TypedData_Make_Struct(klass, VariadicInvoker, &variadic_data_type, invoker); - invoker->rbAddress = Qnil; - invoker->rbEnums = Qnil; - invoker->rbReturnType = Qnil; + RB_OBJ_WRITE(obj, &invoker->rbAddress, Qnil); + RB_OBJ_WRITE(obj, &invoker->rbEnums, Qnil); + RB_OBJ_WRITE(obj, &invoker->rbReturnType, Qnil); invoker->blocking = false; return obj; @@ -103,6 +106,12 @@ variadic_mark(void *data) rb_gc_mark(invoker->rbReturnType); } +static size_t +variadic_memsize(const void *data) +{ + return sizeof(VariadicInvoker); +} + static VALUE variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE rbReturnType, VALUE options) { @@ -119,8 +128,8 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE convention = rb_hash_aref(options, ID2SYM(rb_intern("convention"))); TypedData_Get_Struct(self, VariadicInvoker, &variadic_data_type, invoker); - invoker->rbEnums = rb_hash_aref(options, ID2SYM(rb_intern("enums"))); - invoker->rbAddress = rbFunction; + RB_OBJ_WRITE(self, &invoker->rbEnums, rb_hash_aref(options, ID2SYM(rb_intern("enums")))); + RB_OBJ_WRITE(self, &invoker->rbAddress, rbFunction); invoker->function = rbffi_AbstractMemory_Cast(rbFunction, rbffi_PointerClass)->address; invoker->blocking = RTEST(rb_hash_aref(options, ID2SYM(rb_intern("blocking")))); @@ -132,7 +141,7 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE invoker->abi = FFI_DEFAULT_ABI; #endif - invoker->rbReturnType = rbffi_Type_Lookup(rbReturnType); + RB_OBJ_WRITE(self, &invoker->rbReturnType, rbffi_Type_Lookup(rbReturnType)); if (!RTEST(invoker->rbReturnType)) { VALUE typeName = rb_funcall2(rbReturnType, rb_intern("inspect"), 0, NULL); rb_raise(rb_eTypeError, "Invalid return type (%s)", RSTRING_PTR(typeName)); diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb index 5bc2421..a1a50f3 100644 --- a/spec/ffi/function_spec.rb +++ b/spec/ffi/function_spec.rb @@ -103,4 +103,12 @@ describe FFI::Function do fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd')) expect { fp.free }.to raise_error RuntimeError end + + it 'has a memsize function', skip: RUBY_ENGINE != "ruby" do + base_size = ObjectSpace.memsize_of(Object.new) + + function = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd')) + size = ObjectSpace.memsize_of(function) + expect(size).to be > base_size + end end diff --git a/spec/ffi/library_spec.rb b/spec/ffi/library_spec.rb index 5b806bb..a17d673 100644 --- a/spec/ffi/library_spec.rb +++ b/spec/ffi/library_spec.rb @@ -322,4 +322,21 @@ describe "Library" do expect(val[:data]).to eq(i) end end + + describe "Symbol" do + before do + @libtest = FFI::DynamicLibrary.open( + TestLibrary::PATH, + FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL, + ) + end + + it "has a memsize function", skip: RUBY_ENGINE != "ruby" do + base_size = ObjectSpace.memsize_of(Object.new) + + symbol = @libtest.find_symbol("gvar_gstruct_set") + size = ObjectSpace.memsize_of(symbol) + expect(size).to be > base_size + end + end end diff --git a/spec/ffi/struct_spec.rb b/spec/ffi/struct_spec.rb index c348db7..0ce5273 100644 --- a/spec/ffi/struct_spec.rb +++ b/spec/ffi/struct_spec.rb @@ -1028,6 +1028,17 @@ describe "variable-length arrays" do end describe "Struct memsize functions", skip: RUBY_ENGINE != "ruby" do + it "has a memsize function" do + base_size = ObjectSpace.memsize_of(Object.new) + + c = Class.new(FFI::Struct) do + layout :b, :bool + end + struct = c.new + size = ObjectSpace.memsize_of(struct) + expect(size).to be > base_size + end + class SmallCustomStruct < FFI::Struct layout :pointer, :pointer end diff --git a/spec/ffi/type_spec.rb b/spec/ffi/type_spec.rb new file mode 100644 index 0000000..eb48a43 --- /dev/null +++ b/spec/ffi/type_spec.rb @@ -0,0 +1,43 @@ +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) + +describe "FFI::Type" do + it 'has a memsize function', skip: RUBY_ENGINE != "ruby" do + base_size = ObjectSpace.memsize_of(Object.new) + + size = ObjectSpace.memsize_of(FFI::Type.new(42)) + expect(size).to be > base_size + base_size = size + + converter = Module.new do + extend FFI::DataConverter + + def self.native_type + @native_type_called = true + FFI::Type::INT32 + end + + def self.to_native(val, ctx) + @to_native_called = true + ToNativeMap[val] + end + + def self.from_native(val, ctx) + @from_native_called = true + FromNativeMap[val] + end + end + + size = ObjectSpace.memsize_of(FFI::Type::Mapped.new(converter)) + expect(size).to be > base_size + base_size = size + + # Builtin types are larger as they also have a name and own ffi_type + size = ObjectSpace.memsize_of(FFI::Type::Builtin::CHAR) + expect(size).to be > base_size + end +end |