ffi: ASuperExternCall set mpropdef.has_supercall as true
[nit.git] / src / nitni / nitni_callbacks.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012-2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # nitni services related to callbacks (used underneath the FFI)
18 module nitni_callbacks
19
20 import modelbuilder
21 intrude import rapid_type_analysis
22
23 import nitni_base
24
25 redef class ToolContext
26 var verify_nitni_callback_phase: Phase = new VerifyNitniCallbacksPhase(self, [typing_phase])
27 end
28
29 # * checks for the validity of callbacks
30 # * store the callbacks on each method
31 class VerifyNitniCallbacksPhase
32 super Phase
33
34 redef fun process_npropdef(npropdef)
35 do
36 if not npropdef isa AExternPropdef then return
37
38 npropdef.verify_nitni_callbacks(toolcontext)
39 end
40 end
41
42 # Provides a better API but mainly the same content as AExternCalls
43 class ForeignCallbackSet
44 # set of imported functions, cached to avoid repetitions
45 var callbacks: Set[ MExplicitCall ] = new HashSet[ MExplicitCall ]
46
47 # set of imported functions, cached to avoid repetitions
48 var supers: Set[ MExplicitSuper ] = new HashSet[ MExplicitSuper ]
49
50 # set of relevant types, cached to avoid repetitions
51 var types: Set[ MType ] = new HashSet[ MType ]
52
53 # set of imported casts and as, cached to avoid repetitions
54 var casts: Set[ MExplicitCast ] = new HashSet[ MExplicitCast ]
55
56 # Utility function, must be called only when all other maps are filled
57 private var all_cached: nullable Set[NitniCallback] = null
58 fun all: Set[NitniCallback]
59 do
60 var cached = all_cached
61 if cached != null then return cached
62
63 var set = new HashSet[NitniCallback]
64 set.add_all(callbacks)
65 set.add_all(supers)
66 set.add_all(types)
67 set.add_all(casts)
68
69 self.all_cached = set
70 return set
71 end
72
73 # Integrate content from the `other` set into this one
74 fun join(other: ForeignCallbackSet)
75 do
76 callbacks.add_all( other.callbacks )
77 supers.add_all( other.supers )
78 types.add_all( other.types )
79 casts.add_all( other.casts )
80 end
81 end
82
83 redef class AExternPropdef
84 private var foreign_callbacks_cache: nullable ForeignCallbackSet = null
85
86 # All foreign callbacks from this method
87 fun foreign_callbacks: ForeignCallbackSet
88 do
89 var fcs = foreign_callbacks_cache
90 assert fcs != null else print "Error: attempting to access nitni callbacks before phase 'verify_nitni_callback_phase'."
91 return fcs
92 end
93
94 # Verifiy the validity of the explicit callbacks to Nit
95 # also fills the set returned by foreign_callbacks
96 fun verify_nitni_callbacks(toolcontext: ToolContext)
97 do
98 if foreign_callbacks_cache != null then return
99
100 var fcs = new ForeignCallbackSet
101
102 var mmodule = mpropdef.mclassdef.mmodule
103
104 # receiver
105 var recv_type = mpropdef.mclassdef.bound_mtype
106 fcs.types.add(recv_type)
107
108 # return type
109 var rmt = mpropdef.msignature.return_mtype
110 if rmt != null then
111 if rmt isa MParameterType or rmt isa MVirtualType then
112 var mclass_type = mpropdef.mclassdef.bound_mtype
113 rmt = rmt.anchor_to(mmodule, mclass_type)
114 end
115 var mtype = rmt.resolve_for(recv_type, recv_type, mmodule, true)
116 fcs.types.add(mtype)
117 end
118
119 # params
120 for p in mpropdef.msignature.mparameters do
121 var mtype = p.mtype.resolve_for(recv_type, recv_type, mmodule, true)
122 if mtype isa MParameterType or mtype isa MVirtualType then
123 var mclass_type = mpropdef.mclassdef.bound_mtype
124 mtype = mtype.anchor_to(mmodule, mclass_type)
125 end
126 fcs.types.add( mtype )
127 end
128
129 # explicit callbacks
130 if n_extern_calls != null then
131 for ec in n_extern_calls.n_extern_calls do
132 ec.verify_and_collect(self, fcs, toolcontext)
133 end
134 end
135
136 # store result
137 foreign_callbacks_cache = fcs
138 end
139
140 redef fun accept_rapid_type_visitor(v)
141 do
142 for cb in foreign_callbacks.callbacks do v.add_send(cb.recv_mtype, cb.mproperty.as(MMethod))
143 for cast in foreign_callbacks.casts do v.add_cast_type(cast.to)
144 for sup in foreign_callbacks.supers do
145 v.analysis.add_super_send(sup.from.mclassdef.mclass.mclass_type, sup.from.as(MMethodDef))
146 end
147 for t in foreign_callbacks.types do if t isa MClassType then v.add_type t
148 end
149 end
150
151 # Classification for all nitni callbacks
152 interface NitniCallback
153 end
154
155 redef class MType
156 super NitniCallback
157 end
158
159 # A prossible call from C, declared explictly after the `import` keyword
160 class MExplicitCall
161 super NitniCallback
162
163 # Previously resolved mtype
164 var recv_mtype: MClassType
165 var mproperty: MProperty
166 var from_mmodule: MModule
167
168 fun fill_type_for( callback_set: ForeignCallbackSet, from: MModule )
169 do
170 var first = mproperty.lookup_first_definition( from, recv_mtype )
171 var mclassdef = first.mclassdef
172 var bound_mtype = mclassdef.bound_mtype
173
174 # receiver / constructor return
175 recv_mtype = recv_mtype.resolve_for(bound_mtype, bound_mtype, from, true)
176 recv_mtype = recv_mtype.anchor_to(from, bound_mtype)
177 callback_set.types.add( recv_mtype )
178
179 if first isa MMethodDef then
180 var rmt = first.msignature.return_mtype
181 if rmt != null then
182 rmt = rmt.resolve_for(bound_mtype, bound_mtype, from, true)
183 rmt = rmt.anchor_to(from, bound_mtype)
184 callback_set.types.add( rmt )
185 end
186
187 for p in first.msignature.mparameters do
188 var param_mtype = p.mtype.resolve_for(recv_mtype, recv_mtype, from, true)
189 param_mtype = param_mtype.resolve_for(bound_mtype, bound_mtype, from, true)
190 param_mtype = param_mtype.anchor_to(from, bound_mtype)
191 callback_set.types.add( param_mtype )
192 end
193 end
194 end
195
196 # Signature of this call in C as seen by user
197 fun csignature: String
198 do
199 var mproperty = self.mproperty
200 if mproperty isa MMethod then
201 var signature = mproperty.intro.msignature
202 assert signature != null
203
204 var creturn_type
205 if mproperty.is_init then
206 creturn_type = recv_mtype.cname
207 else if signature.return_mtype != null then
208 var ret_mtype = signature.return_mtype
209 ret_mtype = ret_mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true)
210 creturn_type = ret_mtype.cname
211 else
212 creturn_type = "void"
213 end
214
215 var cname
216 if mproperty.is_init then
217 if mproperty.name == "init" or mproperty.name == "new" then
218 cname = "new_{recv_mtype.mangled_cname}"
219 else
220 cname = "new_{recv_mtype.mangled_cname}_{mproperty.short_cname}"
221 end
222 else
223 cname = "{recv_mtype.mangled_cname}_{mproperty.short_cname}"
224 end
225
226 var cparams = new List[String]
227 if not mproperty.is_init then
228 cparams.add( "{recv_mtype.cname} self" )
229 end
230 for p in signature.mparameters do
231 var param_mtype = p.mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true)
232 cparams.add( "{param_mtype.cname} {p.name}" )
233 end
234
235 return "{creturn_type} {cname}( {cparams.join(", ")} )"
236 else
237 print "Type of callback from foreign code not yet supported."
238 abort
239 end
240 end
241
242 redef fun hash do return recv_mtype.hash + 1024 * mproperty.hash
243 redef fun ==(o) do return o isa MExplicitCall and recv_mtype == o.recv_mtype and mproperty == o.mproperty
244 end
245
246 class MExplicitSuper
247 super NitniCallback
248
249 var from: MPropDef
250
251 redef fun hash do return from.hash
252 redef fun ==(o) do return o isa MExplicitSuper and from == o.from
253 end
254
255 class MExplicitCast
256 super NitniCallback
257
258 var from: MType
259 var to: MType
260
261 fun check_cname: String do return "{from.mangled_cname}_is_a_{to.mangled_cname}"
262
263 fun cast_cname: String do return "{from.mangled_cname}_as_{to.mangled_cname}"
264
265 redef fun hash do return from.hash + 1024 * to.hash
266 redef fun ==(o) do return o isa MExplicitCast and from == o.from and to == o.to
267 end
268
269 redef class AExternCall
270 # Verify this explicit declaration of call from C and collect all relevant callbacks
271 fun verify_and_collect(npropdef: AExternPropdef, callback_set: ForeignCallbackSet,
272 toolcontext: ToolContext) is abstract
273 end
274
275 redef class ALocalPropExternCall
276 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
277 do
278 var mmodule = npropdef.mpropdef.mclassdef.mmodule
279 var mclass_type = npropdef.mpropdef.mclassdef.bound_mtype
280 var m_name = n_methid.collect_text
281 var method = toolcontext.modelbuilder.try_get_mproperty_by_name2( self,
282 mmodule, mclass_type, m_name )
283
284 if method == null then
285 toolcontext.error(location, "Local method {m_name} not found.")
286 return
287 end
288
289 var explicit_call = new MExplicitCall(mclass_type, method, mmodule)
290 callback_set.callbacks.add(explicit_call)
291
292 explicit_call.fill_type_for(callback_set, mmodule)
293 end
294 end
295
296 redef class AFullPropExternCall
297 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
298 do
299 var mmodule = npropdef.mpropdef.mclassdef.mmodule
300 var mclassdef = npropdef.mpropdef.mclassdef
301 var nclassdef = toolcontext.modelbuilder.mclassdef2nclassdef[mclassdef]
302 var mclass_type = mclassdef.bound_mtype
303 var mtype = toolcontext.modelbuilder.resolve_mtype(nclassdef, n_type)
304
305 if mtype == null then return
306
307 if mtype isa MParameterType or mtype isa MVirtualType then
308 mtype = mtype.anchor_to(mmodule, mclass_type)
309 end
310
311 if mtype isa MNullableType then
312 toolcontext.error(location, "Type {n_type.collect_text} is nullable and thus cannot be the receiver." )
313 return
314 end
315
316 var m_name = n_methid.collect_text
317 var method = toolcontext.modelbuilder.try_get_mproperty_by_name2( self,
318 mmodule, mtype, m_name )
319
320 if method == null then
321 toolcontext.error(location, "Method {m_name} not found in {n_type.collect_text}." )
322 return
323 end
324
325 var explicit_call = new MExplicitCall(mtype.as(MClassType), method, mmodule)
326 callback_set.callbacks.add(explicit_call)
327 explicit_call.fill_type_for(callback_set, mmodule)
328 end
329 end
330
331 redef class AInitPropExternCall
332 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
333 do
334 var mmodule = npropdef.mpropdef.mclassdef.mmodule
335 var mclassdef = npropdef.mpropdef.mclassdef
336 var nclassdef = toolcontext.modelbuilder.mclassdef2nclassdef[mclassdef]
337 var mtype = toolcontext.modelbuilder.resolve_mtype(nclassdef, n_type)
338 if mtype == null then return
339
340 if not mtype isa MClassType then
341 toolcontext.error(location, "Type {n_type.collect_text} is not a class and thus cannot be used to instanciate a new instance." )
342 return
343 end
344
345 var meth_name = "init"
346 var meth = toolcontext.modelbuilder.try_get_mproperty_by_name2( self,
347 mmodule, mtype, meth_name )
348
349 if meth == null then
350 toolcontext.error(location, "Method {meth_name} not found in {n_type.collect_text}." )
351 return
352 end
353
354 var explicit_call = new MExplicitCall(mtype, meth, mmodule)
355 callback_set.callbacks.add(explicit_call)
356 explicit_call.fill_type_for(callback_set, mmodule)
357 end
358 end
359
360 redef class ASuperExternCall
361 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
362 do
363 callback_set.supers.add( new MExplicitSuper( npropdef.mpropdef.as(not null) ) )
364 callback_set.types.add( npropdef.mpropdef.mclassdef.mclass.mclass_type )
365 npropdef.mpropdef.has_supercall = true
366 end
367 end
368
369 redef class ACastExternCall
370 fun from_mtype: MType is abstract
371 fun to_mtype: MType is abstract
372
373 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
374 do
375 var from = from_mtype
376 var to = to_mtype
377
378 callback_set.types.add(from)
379 callback_set.types.add(to)
380
381 callback_set.casts.add(new MExplicitCast(from, to))
382 end
383 end
384
385 redef class ACastAsExternCall
386 redef fun from_mtype do return n_from_type.mtype.as(not null)
387 redef fun to_mtype do return n_to_type.mtype.as(not null)
388
389 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
390 do
391 var parent_aclassdef = npropdef.parent.as(AClassdef)
392 toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_from_type, true)
393 toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_to_type, true)
394 super
395 end
396 end
397
398 redef class AAsNullableExternCall
399 redef fun from_mtype do return n_type.mtype.as(not null)
400 redef fun to_mtype do return n_type.mtype.as_nullable
401
402 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
403 do
404 var parent_aclassdef = npropdef.parent.as(AClassdef)
405 toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_type, true)
406 super
407 end
408 end
409
410 redef class AAsNotNullableExternCall
411 redef fun from_mtype do return n_type.mtype.as_nullable
412 redef fun to_mtype do
413 var mtype = n_type.mtype.as(not null)
414 if mtype isa MNullableType then return mtype.mtype
415 return mtype
416 end
417
418 redef fun verify_and_collect(npropdef, callback_set, toolcontext)
419 do
420 var parent_aclassdef = npropdef.parent.as(AClassdef)
421 toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_type, true)
422 super
423 end
424 end