Skip to content

Commit

Permalink
Improved reftable usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Hadeweka authored and Hadeweka committed Sep 25, 2020
1 parent cdcbcb3 commit 37ad5e3
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 24 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ The term 'anyoli' means 'green' in the Maasai language, thus naming 'anyolite'.

## Releases

### Version 0.2.2

#### Usability

* Added more debugging methods
* Allowed for custom object IDs by defining `mruby_object_id` for a class

#### Bugfixes

* Fixed problems with struct wrapping

### Version 0.2.1

#### Usability
Expand Down Expand Up @@ -234,7 +245,11 @@ The term 'anyoli' means 'green' in the Maasai language, thus naming 'anyolite'.

## Upcoming releases

None planned yet
### Version 0.3.0

### Features

* [ ] Copy constructor wrapping

### Future updates

Expand Down
3 changes: 3 additions & 0 deletions examples/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
a.x = 123
puts "Value getter returns #{a.x}"

ts = TestModule::TestStruct.new
puts "Struct value: #{ts.value}"

puts "Values of Test: #{TestModule::Test.counter}"

puts "Test constant is: #{TestModule::SOME_CONSTANT}"
Expand Down
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: anyolite
version: 0.2.1
version: 0.2.2

authors:
- Hadeweka
Expand Down
8 changes: 3 additions & 5 deletions src/MrbCast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,9 @@ module MrbCast
new_ruby_object = MrbInternal.new_empty_object(mrb, ruby_class, ptr.as(Void*), MrbTypeCache.register(typeof(value), destructor))
MrbMacro.convert_from_ruby_object(mrb, new_ruby_object, typeof(value)).value = value

if typeof(value) <= Reference
MrbRefTable.add(value.object_id, ptr.as(Void*))
elsif typeof(value) <= Struct
MrbRefTable.add(value.hash, ptr.as(Void*))
end
MrbRefTable.add(MrbRefTable.get_object_id(value), ptr.as(Void*))

puts "> #{value.class}: #{value}" if MrbRefTable.logging

return new_ruby_object
end
Expand Down
16 changes: 6 additions & 10 deletions src/MrbMacro.cr
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,9 @@ module MrbMacro

# Allocate memory so we do not lose this object
new_obj_ptr = Pointer({{crystal_class}}).malloc(size: 1, value: new_obj)
if {{crystal_class}} <= Reference
MrbRefTable.add(new_obj_ptr.value.object_id, new_obj_ptr.as(Void*))
elsif {{crystal_class}} <= Struct
MrbRefTable.add(new_obj_ptr.value.hash, new_obj_ptr.as(Void*))
end
MrbRefTable.add(MrbRefTable.get_object_id(new_obj_ptr.value), new_obj_ptr.as(Void*))

puts "> {{crystal_class}}: #{new_obj_ptr.value.inspect}" if MrbRefTable.logging

destructor = MrbTypeCache.destructor_method({{crystal_class}})
MrbInternal.set_data_ptr_and_type(obj, new_obj_ptr, MrbTypeCache.register({{crystal_class}}, destructor))
Expand Down Expand Up @@ -419,11 +417,9 @@ module MrbMacro

# Allocate memory so we do not lose this object
new_obj_ptr = Pointer({{crystal_class}}).malloc(size: 1, value: new_obj)
if {{crystal_class}} <= Reference
MrbRefTable.add(new_obj_ptr.value.object_id, new_obj_ptr.as(Void*))
elsif {{crystal_class}} <= Struct
MrbRefTable.add(new_obj_ptr.value.hash, new_obj_ptr.as(Void*))
end
MrbRefTable.add(MrbRefTable.get_object_id(new_obj_ptr.value), new_obj_ptr.as(Void*))

puts "> {{crystal_class}}: #{new_obj_ptr.value.inspect}" if MrbRefTable.logging

destructor = MrbTypeCache.destructor_method({{crystal_class}})
MrbInternal.set_data_ptr_and_type(obj, new_obj_ptr, MrbTypeCache.register({{crystal_class}}, destructor))
Expand Down
42 changes: 36 additions & 6 deletions src/MrbRefTable.cr
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
# This is a very simple approach to generate artificial references to the wrapped objects.
# Therefore, the GC won't delete the wrapped objects until necessary.
# Note that this is currently one-directional, so Mruby might still delete Crystal objects generated from Crystal itself.
# Note that this is currently one-directional, so mruby might still delete Crystal objects generated from Crystal itself.
# Furthermore, this is only possible as a module due to C closure limitations.
#
# TODO: Add reset method or transform this module into a class
# TODO: Add compilation option for ignoring entry checks
module MrbRefTable
@@content = {} of UInt64 => Tuple(Void*, UInt64)
@@content = {} of UInt64 => Tuple(Void*, Int64)
@@logging = false
@@warnings = true

def self.get(identification)
return @@content[identification][0]
end

def self.add(identification, value)
puts "> Added reference #{identification} -> #{value}" if @@logging
if @@content[identification]?
if value != @@content[identification][0]
puts "WARNING: Value #{identification} replaced pointers."
puts "WARNING: Value #{identification} replaced pointers." if @@warnings
end
@@content[identification] = {value, @@content[identification][1] + 1}
end
@@content[identification] = {value, 1u64}
@@content[identification] = {value, 1i64}
end

def self.delete(identification)
puts "> Removed reference #{identification}" if @@logging
if @@content[identification]?
@@content[identification] = {@@content[identification][0], @@content[identification][1] - 1}
if @@content[identification][1] <= 0
@@content.delete(identification)
end
else
puts "WARNING: Tried to remove unregistered object #{identification} from reference table."
puts "WARNING: Tried to remove unregistered object #{identification} from reference table." if @@warnings
end
nil
end
Expand All @@ -41,7 +45,33 @@ module MrbRefTable
@@content.inspect
end

def self.logging
@@logging
end

def self.warnings
@@warnings
end

def self.logging=(value)
@@logging = value
end

def self.warnings=(value)
@@warnings = value
end

def self.reset
@@content.clear
end

def self.get_object_id(value)
if value.responds_to?(:mruby_ref_id)
value.mruby_ref_id.to_u64
elsif value.responds_to?(:object_id)
value.object_id.to_u64
else
value.hash.to_u64
end
end
end
2 changes: 1 addition & 1 deletion src/MrbTypeCache.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module MrbTypeCache
end

# Delete the Crystal reference to this object
MrbRefTable.delete(crystal_ptr.value.object_id)
MrbRefTable.delete(MrbRefTable.get_object_id(crystal_ptr.value))
}
end
end
17 changes: 17 additions & 0 deletions test.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ module SomeModule
end
end

struct TestStruct

property value : Int32 = -1
property test : Test = Test.new(-2)

def mruby_ref_id
return 12
end

end

class Test
property x : Int32 = 0

Expand Down Expand Up @@ -61,6 +72,8 @@ class Test
end
end

MrbRefTable.logging = true

MrbState.create do |mrb|
MrbWrap.wrap_module(mrb, SomeModule, "TestModule")
MrbWrap.wrap_module_function_with_keywords(mrb, MrbModuleCache.get(SomeModule), "test_method", SomeModule.test_method, {:int => Int32, :str => String})
Expand All @@ -69,6 +82,10 @@ MrbState.create do |mrb|
MrbWrap.wrap_class(mrb, Bla, "Bla", under: MrbModuleCache.get(SomeModule))
MrbWrap.wrap_constructor(mrb, Bla)

MrbWrap.wrap_class(mrb, TestStruct, "TestStruct", under: MrbModuleCache.get(SomeModule))
MrbWrap.wrap_constructor(mrb, TestStruct)
MrbWrap.wrap_property(mrb, TestStruct, "value", value, [Int32])

MrbWrap.wrap_class(mrb, Test, "Test", under: MrbModuleCache.get(SomeModule))
MrbWrap.wrap_class_method(mrb, Test, "counter", Test.counter)
MrbWrap.wrap_constructor_with_keywords(mrb, Test, {:x => {Int32, 0}})
Expand Down

0 comments on commit 37ad5e3

Please sign in to comment.