@@ -35,8 +35,11 @@ const C_friendly_types = Base.IdSet{Any}([ # a few of these are redundant to
35
35
])
36
36
37
37
function recursively_add_types! (types:: Base.IdSet{DataType} , @nospecialize (T:: DataType ))
38
- while T <: Ptr
38
+ T in types && return types
39
+ while T. name === Ptr. body. name
40
+ push! (types, T)
39
41
T = T. parameters[1 ] # unwrap Ptr{...}
42
+ T in types && return types
40
43
end
41
44
if T. name. module === Core && T ∉ C_friendly_types
42
45
error (" invalid type for juliac: " , T) # exclude internals (they may change)
@@ -50,37 +53,198 @@ function recursively_add_types!(types::Base.IdSet{DataType}, @nospecialize(T::Da
50
53
return types
51
54
end
52
55
56
+ struct TypeEmitter
57
+ io:: IO
58
+ type_ids:: IdDict{Any,Int}
59
+ end
60
+
61
+ function escape_string_json (s:: AbstractString )
62
+ iob = IOBuffer ()
63
+ print (iob, ' "' )
64
+ for c in s
65
+ if c == ' "'
66
+ print (iob, " \\\" " )
67
+ elseif c == ' \\ '
68
+ print (iob, " \\\\ " )
69
+ elseif c == ' \b '
70
+ print (iob, " \\ b" )
71
+ elseif c == ' \f '
72
+ print (iob, " \\ f" )
73
+ elseif c == ' \n '
74
+ print (iob, " \\ n" )
75
+ elseif c == ' \r '
76
+ print (iob, " \\ r" )
77
+ elseif c == ' \t '
78
+ print (iob, " \\ t" )
79
+ elseif ' \x 00' <= c <= ' \x 1f'
80
+ print (iob, " \\ u" , lpad (string (UInt16 (c), base= 16 ), 4 , ' 0' ))
81
+ else
82
+ @assert isvalid (c) " invalid unicode character"
83
+ print (iob, c)
84
+ end
85
+ end
86
+ print (iob, ' "' )
87
+ return String (take! (iob))
88
+ end
89
+
90
+ function type_name_json (@nospecialize (dt:: DataType ))
91
+ return escape_string_json (repr (dt))
92
+ end
93
+
94
+ function field_name_json (@nospecialize (dt:: DataType ), field:: Int )
95
+ name = String (fieldname (dt, field))
96
+ return escape_string_json (name)
97
+ end
98
+
99
+ function emit_pointer_info! (ctx:: TypeEmitter , @nospecialize (dt:: DataType ); indent:: Int = 0 )
100
+ pointee_type_id = ctx. type_ids[dt. parameters[1 ]]
101
+ let indented_println (args... ) = println (ctx. io, " " ^ indent, args... )
102
+ indented_println (" {" )
103
+ indented_println (" \" id\" : " , ctx. type_ids[dt], " ," )
104
+ indented_println (" \" kind\" : \" pointer\" ," )
105
+ indented_println (" \" name\" : " , type_name_json (dt), " ," )
106
+ indented_println (" \" pointee\" : " , pointee_type_id)
107
+ print (ctx. io, " " ^ indent, " }" )
108
+ end
109
+ end
110
+
111
+ function emit_field_info! (ctx:: TypeEmitter , @nospecialize (dt:: DataType ), field:: Int ; indent:: Int = 0 )
112
+ desc = Base. DataTypeFieldDesc (dt)[field]
113
+ type_id = ctx. type_ids[fieldtype (dt, field)]
114
+ let indented_println (args... ) = println (ctx. io, " " ^ indent, args... )
115
+ indented_println (" {" )
116
+ indented_println (" \" name\" : " , field_name_json (dt, field), " ," )
117
+ indented_println (" \" offset\" : " , desc. offset, " ," )
118
+ indented_println (" \" type\" : " , type_id)
119
+ print (ctx. io, " " ^ indent, " }" )
120
+ end
121
+ end
122
+
123
+ function emit_struct_info! (ctx:: TypeEmitter , @nospecialize (dt:: DataType ); indent:: Int = 0 )
124
+ type_id = ctx. type_ids[dt]
125
+ let indented_println (args... ) = println (ctx. io, " " ^ indent, args... )
126
+ indented_println (" {" )
127
+ indented_println (" \" id\" : " , type_id, " ," )
128
+ indented_println (" \" kind\" : \" struct\" ," )
129
+ indented_println (" \" name\" : " , type_name_json (dt), " ," )
130
+ indented_println (" \" size\" : " , Core. sizeof (dt), " ," )
131
+ indented_println (" \" alignment\" : " , Base. datatype_alignment (dt), " ," )
132
+ indented_println (" \" fields\" : [" )
133
+ for i = 1 : Base. datatype_nfields (dt)
134
+ emit_field_info! (ctx, dt, i; indent = indent + 4 )
135
+ println (ctx. io, i == Base. datatype_nfields (dt) ? " " : " ," )
136
+ end
137
+ indented_println (" ]" )
138
+ print (ctx. io, " " ^ indent, " }" )
139
+ end
140
+ end
141
+
142
+ function emit_primitive_type! (ctx:: TypeEmitter , @nospecialize (dt:: DataType ); indent:: Int = 0 )
143
+ type_id = ctx. type_ids[dt]
144
+ let indented_println (args... ) = println (ctx. io, " " ^ indent, args... )
145
+ indented_println (" {" )
146
+ indented_println (" \" id\" : " , type_id, " ," )
147
+ indented_println (" \" kind\" : \" primitive\" ," )
148
+ indented_println (" \" name\" : " , type_name_json (dt), " ," )
149
+ indented_println (" \" size\" : " , Core. sizeof (dt), " ," )
150
+ indented_println (" \" alignment\" : " , Base. datatype_alignment (dt))
151
+ print (ctx. io, " " ^ indent, " }" )
152
+ end
153
+ end
154
+
155
+ function emit_type_info! (ctx:: TypeEmitter , @nospecialize (dt:: DataType ); indent:: Int = 0 )
156
+ if dt. name === Ptr. body. name
157
+ emit_pointer_info! (ctx, dt; indent)
158
+ elseif Base. isprimitivetype (dt)
159
+ emit_primitive_type! (ctx, dt; indent)
160
+ else
161
+ emit_struct_info! (ctx, dt; indent)
162
+ end
163
+ end
164
+
165
+ function emit_method_info! (ctx:: TypeEmitter , method:: Core.Method ; indent:: Int = 0 )
166
+ (rt, sig) = method. ccallable
167
+ (name, symbol) = let
168
+ symbol = length (method. ccallable) > 2 ? Symbol (method. ccallable[3 ]) : method. name
169
+ iob = IOBuffer ()
170
+ print (IOContext (iob, :print_method_signature_only => true ), method)
171
+ str = String (take! (iob))
172
+ if symbol != = method. name && startswith (str, String (method. name))
173
+ # Make a best-effort attempt to use the exported name
174
+ #
175
+ # Note: the `startswith` check is to make sure we support 'functor's in arg0,
176
+ # which Base.@ccallable supports as long as they are singletons.
177
+ str = replace (str, String (method. name) => String (symbol); count = 1 )
178
+ end
179
+ (str, String (symbol))
180
+ end
181
+
182
+ argnames = String .(Base. method_argnames (method))
183
+ let indented_println (args... ) = println (ctx. io, " " ^ indent, args... )
184
+ indented_println (" {" )
185
+ indented_println (" \" symbol\" : " , escape_string_json (symbol), " ," )
186
+ indented_println (" \" name\" : " , escape_string_json (name), " ," )
187
+ indented_println (" \" arguments\" : [" )
188
+ for i in 2 : length (sig. parameters)
189
+ indented_println (" {" )
190
+ indented_println (" \" name\" : " , escape_string_json (argnames[i]), " ," )
191
+ indented_println (" \" type\" : " , ctx. type_ids[sig. parameters[i]])
192
+ indented_println (" }" , i == length (sig. parameters) ? " " : " ," )
193
+ end
194
+ indented_println (" ]," )
195
+ indented_println (" \" returns\" : {" )
196
+ indented_println (" \" type\" : " , ctx. type_ids[rt])
197
+ indented_println (" }" )
198
+ print (ctx. io, " " ^ indent, " }" )
199
+ end
200
+ end
201
+
202
+ function emit_abi_info! (ctx:: TypeEmitter , exported:: Vector{Core.Method} , types:: IdSet{DataType} )
203
+ println (ctx. io, " {" )
204
+
205
+ # assign an ID to each type, so that we can refer to them
206
+ for (i, T) in enumerate (types)
207
+ ctx. type_ids[T] = i
208
+ end
209
+
210
+ # print exported functions
211
+ println (ctx. io, " \" functions\" : [" )
212
+ for (i, method) in enumerate (exported)
213
+ emit_method_info! (ctx, method; indent = 4 )
214
+ println (ctx. io, i == length (exported) ? " " : " ," )
215
+ end
216
+ println (ctx. io, " ]," )
217
+
218
+ # print type / structure information
219
+ println (ctx. io, " \" types\" : [" )
220
+ for (i, T) in enumerate (types)
221
+ emit_type_info! (ctx, T; indent = 4 )
222
+ println (ctx. io, i == length (types) ? " " : " ," )
223
+ end
224
+ println (ctx. io, " ]" )
225
+
226
+ println (ctx. io, " }" )
227
+ end
228
+
53
229
function write_logfile (io:: IO )
54
- iotmp = IOBuffer ()
55
230
types = Base. IdSet {DataType} ()
231
+
232
+ # discover all exported methods + any types they reference
233
+ exported = Core. Method[]
56
234
Base. visit (Core. GlobalMethods) do method
57
235
if isdefined (method, :ccallable )
58
- rt, sig = method. ccallable
59
- name = length (method. ccallable) > 2 ? Symbol (method. ccallable[3 ]) : method. name
60
- print (IOContext (iotmp, :print_method_signature_only => true ), method)
61
- methodstr = String (take! (iotmp))
62
- if name != = method. name
63
- methodstr = replace (methodstr, String (method. name) => String (name))
64
- end
65
- println (io, methodstr, " ::" , rt)
236
+ push! (exported, method)
237
+ (rt, sig) = method. ccallable
66
238
for T in sig. parameters[2 : end ]
67
239
recursively_add_types! (types, T)
68
240
end
241
+ recursively_add_types! (types, rt)
69
242
end
70
243
end
71
- println (io) # blank line separates methods from types
72
- for T in types
73
- println (io, T)
74
- dtfd = Base. DataTypeFieldDesc (T)
75
- local fd
76
- for i = 1 : Base. datatype_nfields (T)
77
- fd = dtfd[i]
78
- fn = fieldname (T, i)
79
- ft = fieldtype (T, i)
80
- println (io, " " , fn, " ::" , ft, " [" , fd. offset, " ]" )
81
- end
82
- println (io, fd. offset + fd. size, " bytes" )
83
- end
244
+
245
+ # print the discovered ABI info
246
+ ctx = TypeEmitter (io, IdDict {Any,Int} ())
247
+ emit_abi_info! (ctx, exported, types)
84
248
end
85
249
86
250
# Load user code
0 commit comments