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