summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/ffi_c/DynamicLibrary.c28
-rw-r--r--ext/ffi_c/Function.c33
-rw-r--r--ext/ffi_c/MappedType.c21
-rw-r--r--ext/ffi_c/MappedType.h1
-rw-r--r--ext/ffi_c/Struct.c79
-rw-r--r--ext/ffi_c/StructByValue.c21
-rw-r--r--ext/ffi_c/Type.c39
-rw-r--r--ext/ffi_c/Type.h2
-rw-r--r--ext/ffi_c/Variadic.c25
-rw-r--r--spec/ffi/function_spec.rb8
-rw-r--r--spec/ffi/library_spec.rb17
-rw-r--r--spec/ffi/struct_spec.rb11
-rw-r--r--spec/ffi/type_spec.rb43
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