Skip to content

Commit

Permalink
Update code comments and add more explanations.
Browse files Browse the repository at this point in the history
  • Loading branch information
hugopl committed Jun 28, 2024
1 parent 3c4ebca commit daf25a0
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 19 deletions.
10 changes: 10 additions & 0 deletions spec/c_born_crystal_objects_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ end
private class NonAbstractUserObj < AbstractUserObj
end

# private class NonDefaultCtorObj < GObject::Object
# def initialize(int)
# super()
# end
# end

describe "Crystal GObjects" do
it "can born in C land" do
ptr = LibGObject.g_object_new(UserObj.g_type, "crystal_prop1", "value", Pointer(Void).null)
Expand All @@ -46,4 +52,8 @@ describe "Crystal GObjects" do
it "works with abstract classes in hierarchy" do
NonAbstractUserObj.new
end

pending "doesn't require classes to have a default cosntructor" do
# NonDefaultCtorObj.new(42)
end
end
33 changes: 14 additions & 19 deletions src/bindings/g_object/object.cr
Original file line number Diff line number Diff line change
Expand Up @@ -138,36 +138,31 @@ module GObject

# :nodoc:
#
# GObject instance initialization, this can be called when a GObject is created from Crystal `MyObj.new` or by
# C `g_object_new(MyObject.g_type)`, in both cases some tasks are always done here:
#
# - INSTANCE_QDATA_KEY is always set here, so Crystal properties can be fetched.
# - Floating refs are sank
#
# So, if the object is created from C
#
# - Set `GICrystal.g_object_being_created` with the C object instance pointer.
# - Call the Crystal object constructor, that will look the flag set above and use it instead of call `g_object_new`.
#
# If the object is created from Crystal
#
# - Set `GICrystal.crystal_object_being_created` with the Crystal object instance pointer.
# - Use the Crystal object instead of calling the Crystal object constructor.
# GObject instance initialization, creates the Crystal instance if there's no one created yet.
def self._instance_init(instance : Pointer(LibGObject::TypeInstance), type : Pointer(LibGObject::TypeClass)) : Nil
g_type = type.value.g_type

# Return if the Crystal instance is already set up.
crystal_instance = LibGObject.g_object_get_qdata(instance, GICrystal::INSTANCE_QDATA_KEY)
return if crystal_instance

crystal_instance ||= GICrystal.crystal_object_being_created
# Check if this was called from a Crystal constructor
crystal_instance = GICrystal.crystal_object_being_created
# If not, this comes from a C call, so a Crystal instance needs to be created, however
{% unless @type.abstract? %}
if !crystal_instance
ctor_ptr = LibGObject.g_type_get_qdata(g_type, GICrystal::INSTANCE_USERTYPE_FACTORY)
# we need to create the right type, and the instance_init from all type hierarchy is called,
# so the instance factory set on GType registration also as a qdata is used.
ctor_ptr = LibGObject.g_type_get_qdata(type.value.g_type, GICrystal::INSTANCE_USERTYPE_FACTORY)
# Set the g_object_being_created, so the Crystal code wont call g_object_new again.
GICrystal.g_object_being_created = instance.as(Void*)
crystal_instance = Proc(Void*).new(ctor_ptr, Pointer(Void).null).call
GICrystal.g_object_being_created = Pointer(Void).null
end
{% end %}

# Now we have a Crystal object instance, let's set it up:
# - Set the INSTANCE_QDATA_KEY, so if someone read a property the get_property callback can
# know what's the Crystal instance.
# - Set the Crystal instance @pointer variable, so Crystal code can run without a dangling pointer.
if crystal_instance
crystal_instance.as(GObject::Object)._gobj_pointer = instance.as(Void*)
LibGObject.g_object_set_qdata(instance, GICrystal::INSTANCE_QDATA_KEY, crystal_instance)
Expand Down

0 comments on commit daf25a0

Please sign in to comment.