@@ -121,6 +121,51 @@ function render_c_arg(io, obj, typ)
121
121
end
122
122
end
123
123
124
+ # ensure that the GC can run during a ccall. this is only safe if callbacks
125
+ # into Julia transition back to GC-unsafe, which is the case on Julia 1.10+.
126
+ #
127
+ # doing so is tricky, because no GC operations are allowed after the transition,
128
+ # meaning we have to do our own argument conversion instead of relying on ccall.
129
+ #
130
+ # TODO : replace with JuliaLang/julia#49933 once merged
131
+ function make_gcsafe (ex)
132
+ # decode the ccall
133
+ if ! Meta. isexpr (ex, :call ) || ex. args[1 ] != :ccall
134
+ error (" Can only make ccall expressions GC-safe" )
135
+ end
136
+ target = ex. args[2 ]
137
+ rettyp = ex. args[3 ]
138
+ argtypes = ex. args[4 ]. args
139
+ args = ex. args[5 : end ]
140
+
141
+ code = quote
142
+ end
143
+
144
+ # assign argument values to variables
145
+ vars = [Symbol (" arg$i " ) for i in 1 : length (args)]
146
+ for (var, arg) in zip (vars, args)
147
+ push! (code. args, :($ var = $ arg))
148
+ end
149
+
150
+ # convert the arguments
151
+ converted = [Symbol (" converted_arg$i " ) for i in 1 : length (args)]
152
+ for (converted, argtyp, var) in zip (converted, argtypes, vars)
153
+ push! (code. args, :($ converted = Base. unsafe_convert ($ argtyp, Base. cconvert ($ argtyp, $ var))))
154
+ end
155
+
156
+ # emit a gcsafe ccall
157
+ append! (code. args, (quote
158
+ GC. @preserve $ (vars... ) begin
159
+ gc_state = ccall (:jl_gc_safe_enter , Int8, ())
160
+ ret = ccall ($ target, $ rettyp, ($ (argtypes... ),), $ (converted... ))
161
+ ccall (:jl_gc_safe_leave , Cvoid, (Int8,), gc_state)
162
+ ret
163
+ end
164
+ end ). args)
165
+
166
+ return code
167
+ end
168
+
124
169
function class_message (class_name, msg, rettyp, argtyps, argvals)
125
170
quote
126
171
class = Class ($ (String (class_name)))
@@ -136,18 +181,19 @@ function class_message(class_name, msg, rettyp, argtyps, argvals)
136
181
end
137
182
ret = $ (
138
183
if ABI. use_stret (rettyp)
139
- # we follow Julia's ABI implementation, so ccall will handle the sret box
140
- :(
184
+ # we follow Julia's ABI implementation,
185
+ # so ccall will handle the sret box
186
+ make_gcsafe (:(
141
187
ccall (:objc_msgSend_stret , $ rettyp,
142
188
(Ptr{Cvoid}, Ptr{Cvoid}, $ (map (esc, argtyps)... )),
143
189
class, sel, $ (map (esc, argvals)... ))
144
- )
190
+ ))
145
191
else
146
- :(
192
+ make_gcsafe ( :(
147
193
ccall (:objc_msgSend , $ rettyp,
148
194
(Ptr{Cvoid}, Ptr{Cvoid}, $ (map (esc, argtyps)... )),
149
195
class, sel, $ (map (esc, argvals)... ))
150
- )
196
+ ))
151
197
end
152
198
)
153
199
@static if $ tracing
@@ -178,18 +224,19 @@ function instance_message(instance, typ, msg, rettyp, argtyps, argvals)
178
224
end
179
225
ret = $ (
180
226
if ABI. use_stret (rettyp)
181
- # we follow Julia's ABI implementation, so ccall will handle the sret box
182
- :(
227
+ # we follow Julia's ABI implementation,
228
+ # so ccall will handle the sret box
229
+ make_gcsafe (:(
183
230
ccall (:objc_msgSend_stret , $ rettyp,
184
231
(id{Object}, Ptr{Cvoid}, $ (map (esc, argtyps)... )),
185
232
$ instance, sel, $ (map (esc, argvals)... ))
186
- )
233
+ ))
187
234
else
188
- :(
235
+ make_gcsafe ( :(
189
236
ccall (:objc_msgSend , $ rettyp,
190
237
(id{Object}, Ptr{Cvoid}, $ (map (esc, argtyps)... )),
191
238
$ instance, sel, $ (map (esc, argvals)... ))
192
- )
239
+ ))
193
240
end
194
241
)
195
242
@static if $ tracing
0 commit comments