Skip to content

Commit 6e6036e

Browse files
committed
allow custom allocate function for attached classes
1 parent 2f64f19 commit 6e6036e

File tree

7 files changed

+117
-14
lines changed

7 files changed

+117
-14
lines changed

REFERENCE.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,25 @@ The `xfree` function, which is the standard memory freeing function provided by
644644

645645
### allocate
646646

647+
The `allocate` function is called by the Ruby interpreter before it initializes the class
648+
using `initialize`. It is here that the C level memory allocations that are assigned to a
649+
particular class via attached structs should be `xmalloc`'d. The correponding data should be
650+
freed using `xfree` inside the `deallocate` function that is documented below.
651+
652+
Keep in mind that if you choose to define your own `allocate` function, it must have a return
653+
type `object`. An example `deallocate` would look like so:
654+
``` ruby
655+
struct test
656+
int *a
657+
end
658+
659+
class A attach test
660+
cfunc object allocate
661+
data$.test.a = <int*>xmalloc(sizeof(int)*100)
662+
end
663+
end
664+
```
665+
647666
### memcount
648667

649668
### get_struct

lib/rubex/ast/node.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ def outside_statement?(stmt)
7878
stmt.is_a?(Statement::Expression)
7979
end
8080

81+
def declare_unique_types header
82+
types = []
83+
@statements.grep(Rubex::AST::TopStatement::Klass).each do |klass|
84+
types.concat klass.scope.type_entries
85+
end
86+
types.uniq!
87+
declare_types header, types
88+
end
89+
8190
def generate_header_file(target_name, header)
8291
header_def_name = target_name.gsub("/", "_").gsub(".", "_").upcase + "_H"
8392
header.in_header_guard(header_def_name) do
@@ -90,9 +99,7 @@ def generate_header_file(target_name, header)
9099
"#include \"#{name}\"\n"
91100
end
92101
end
93-
@statements.grep(Rubex::AST::TopStatement::Klass).each do |klass|
94-
declare_types header, klass.scope
95-
end
102+
declare_unique_types header
96103
write_user_klasses header
97104
write_global_variable_declarations header
98105
write_function_declarations header

lib/rubex/ast/top_statement/klass/attached_klass.rb

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def detach_and_modify_auxillary_c_functions_from_statements
7272
if stmt.name == ALLOC_FUNC_NAME
7373
@auxillary_c_functions[ALLOC_FUNC_NAME] = stmt
7474
@user_defined_alloc = true
75+
modify_alloc_func stmt
7576
indexes << idx
7677
elsif stmt.name == DEALLOC_FUNC_NAME
7778
@auxillary_c_functions[DEALLOC_FUNC_NAME] = stmt
@@ -90,8 +91,8 @@ def detach_and_modify_auxillary_c_functions_from_statements
9091
end
9192
end
9293

93-
indexes.each do |idx|
94-
@statements.delete_at idx
94+
@statements.delete_if.with_index do |s, i|
95+
indexes.include? i
9596
end
9697
end
9798

@@ -100,10 +101,22 @@ def write_auxillary_c_functions(code)
100101
write_memcount_c_function code
101102
end
102103

104+
# FIXME : change this. its too ugly.
105+
def ugly_code_mod_hack code
106+
ret = ""
107+
ret << 'return TypedData_Wrap_Struct('
108+
ret << "#{@alloc_c_func.type.arg_list[0].entry.c_name},"
109+
ret << "&#{@data_type_t}, #{Rubex::POINTER_PREFIX}data);\n"
110+
111+
c = code.instance_variable_get(:@code)
112+
c.insert -4, ret
113+
end
114+
103115
# Actually write the alloc function into C code.
104-
def write_alloc_c_function(code)
116+
def write_alloc_c_function(code)
105117
if user_defined_alloc?
106118
@auxillary_c_functions[ALLOC_FUNC_NAME].generate_code code
119+
ugly_code_mod_hack code
107120
else
108121
code.write_c_method_header(
109122
type: @alloc_c_func.type.type.to_s,
@@ -122,7 +135,7 @@ def write_alloc_c_function(code)
122135
lines << 'return TypedData_Wrap_Struct('
123136
lines << "#{@alloc_c_func.type.arg_list[0].entry.c_name},"
124137
lines << "&#{@data_type_t}, data);\n"
125-
138+
126139
code << lines
127140
end
128141
end
@@ -368,6 +381,71 @@ def prepare_get_struct_c_function
368381
end
369382
end
370383

384+
# In case of user supplied allocate function, add a statement at the
385+
# beginning to declare and obtain the data variable and also add a
386+
# return statement involving TypedDataStruct at the end so that the
387+
# user is freed from needing to write these.
388+
def modify_alloc_func(func)
389+
stmts = []
390+
stmts << data_var_cptr_decl(nil)
391+
stmts.concat data_struct_allocations
392+
stmts.reverse.each do |s|
393+
func.statements.unshift s
394+
end
395+
end
396+
397+
# declare a pointer to a data struct.
398+
def data_var_cptr_decl(value = nil)
399+
Statement::CPtrDecl.new(
400+
@data_struct.entry.name, 'data', value, '*', @location)
401+
end
402+
403+
def data_struct_allocations
404+
stmts = []
405+
# assign malloc for top level data struct
406+
rhs = Expression::CommandCall.new(
407+
nil, 'xmalloc',
408+
Expression::ActualArgList.new(
409+
[
410+
Expression::SizeOf.new(@data_struct.entry.name, '')
411+
])
412+
)
413+
rhs.typecast = Expression::Typecast.new(@data_struct.entry.name, '*')
414+
assign_data_struct = Statement::Assign.new(
415+
Expression::Name.new('data'), rhs, @location
416+
)
417+
418+
stmts << assign_data_struct
419+
stmts << xmalloc_inner_struct_member
420+
stmts
421+
end
422+
423+
def xmalloc_inner_struct_member
424+
t = @scope.find(@attached_type).name
425+
lhs = Expression::CommandCall.new(
426+
Expression::ElementRef.new(
427+
'data',
428+
Expression::ActualArgList.new(
429+
[
430+
Expression::Literal::Int.new('0')
431+
]
432+
)
433+
),
434+
t,
435+
nil
436+
)
437+
rhs = Expression::CommandCall.new(
438+
nil, 'xmalloc',
439+
Expression::ActualArgList.new(
440+
[
441+
Expression::SizeOf.new(t, '')
442+
]
443+
)
444+
)
445+
rhs.typecast = Expression::Typecast.new(t, '*')
446+
Statement::Assign.new(lhs, rhs, @location)
447+
end
448+
371449
# Modify the dealloc function by adding an argument of type void* so
372450
# that it is compatible with what Ruby expects. This is done so that
373451
# the user is not burdened with additional knowledge of knowing the

lib/rubex/ast/top_statement/method_def.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def rescan_declarations(_scope)
5353
private
5454

5555
def generate_function_definition(code, c_function:)
56-
declare_types code, @scope
56+
declare_types code, @scope.type_entries
5757
declare_args code unless c_function
5858
declare_vars code, @scope
5959
declare_carrays code, @scope

lib/rubex/helpers/writers.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ def declare_carrays(code, scope)
2929
end
3030
end
3131

32-
def declare_types(code, scope)
33-
scope.type_entries.each do |entry|
32+
def declare_types(code, type_entries)
33+
type_entries.each do |entry|
3434
type = entry.type
3535

3636
if type.alias_type?

spec/c_struct_interface_spec.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
context ".compile", hell: true do
1919
it "generates valid C code" do
20-
t, c, e = Rubex::Compiler.compile @path + ".rubex", test: true
21-
puts c
20+
t, c, e, h = Rubex::Compiler.compile @path + ".rubex", test: true
2221
end
2322
end
2423

spec/fixtures/c_struct_interface/c_struct_interface.rubex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ struct test
4747
end
4848

4949
class A attach test
50-
cfunc void allocate
50+
cfunc object allocate
5151
data$.test.a = <int*>xmalloc(sizeof(int)*100)
5252
end
5353

5454
def foo
5555
data$.test.a[3] = 55
56-
return data$.test.a
56+
return data$.test.a[3]
5757
end
5858

5959
cfunc void deallocate

0 commit comments

Comments
 (0)