model: new metamodel
[nit.git] / src / runtime_type.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
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
18 # Rapid type analysis on the AST with heterogenous generics and customization
19 #
20 # Rapid type analysis is an analyse that aproximates the set of live classes
21 # and the set of live methods starting from the entry point of the program.
22 # These two sets are interdependant and computed together.
23 # It is quite efficient but the type set is global such pollute each call site.
24 #
25 # Heterogenous generics means that each intancied generic class is associated
26 # to a distinct runtime type.
27 # Heterogenous generics has the advantage to resolve the the formal generic
28 # parameters types but increase the number of types.
29 # More important, heterogenous generics cannot deal with infinite number of runtime
30 # types since the analyse tries to list them all (so some programs will be badly refused)
31 #
32 # Customization means that each method definition is analyzed one per runtime
33 # type of receiver.
34 # Customization have the following advantages:
35 # * `self' is monomorphic
36 # * virtual types are all resolved
37 # * live attributes can be determined on each class
38 # But the big disavantage to explode the number of runtime method: each method
39 # definition for each runtime type that need it
40 module runtime_type
41
42 import model
43 import modelbuilder
44 import typing
45 import auto_super_init
46
47 redef class MModule
48 var main_type: nullable MClassType
49 var main_init: nullable MMethod
50 var main_method: nullable MMethod
51 end
52
53 redef class ModelBuilder
54 fun do_runtime_type(mainmodule: MModule): RuntimeTypeAnalysis
55 do
56 var model = self.model
57 var analysis = new RuntimeTypeAnalysis(self, mainmodule)
58 var nmodule = self.nmodules.first
59 var mainclass = self.try_get_mclass_by_name(nmodule, mainmodule, "Sys")
60 assert mainclass != null
61 var props = model.get_mproperties_by_name("main")
62 assert props.length == 1
63 var methods = props.first.lookup_definitions(mainmodule, mainclass.mclass_type)
64 assert methods.length == 1 else print methods.join(", ")
65 var maintype = mainclass.mclass_type
66 analysis.add_type(maintype)
67 mainmodule.main_type = maintype
68 var initprop = self.try_get_mproperty_by_name2(nmodule, mainmodule, maintype, "init")
69 if initprop != null then
70 assert initprop isa MMethod
71 analysis.add_monomorphic_send(maintype, initprop)
72 end
73 mainmodule.main_init = initprop
74 var mainprop = self.try_get_mproperty_by_name2(nmodule, mainmodule, maintype, "main")
75 if mainprop != null then
76 assert mainprop isa MMethod
77 analysis.add_monomorphic_send(maintype, mainprop)
78 end
79 mainmodule.main_method = mainprop
80 analysis.run_analysis
81 return analysis
82 end
83 end
84
85 class RuntimeTypeAnalysis
86 var modelbuilder: ModelBuilder
87 var mainmodule: MModule
88 var live_types: HashSet[MClassType] = new HashSet[MClassType]
89 var live_cast_types: HashSet[MClassType] = new HashSet[MClassType]
90 var polymorphic_methods: HashSet[MMethod] = new HashSet[MMethod]
91 var live_methoddefs: HashSet[MMethodDef] = new HashSet[MMethodDef]
92 var runtime_methods: HashSet[RuntimeMethod] = new HashSet[RuntimeMethod]
93 var live_send_site: HashSet[RuntimeSendSite] = new HashSet[RuntimeSendSite]
94
95 private var todo: List[RuntimeMethod] = new List[RuntimeMethod]
96
97 fun add_type(mtype: MClassType)
98 do
99 if self.live_types.has(mtype) then return
100
101 assert not mtype.need_anchor
102 self.live_types.add(mtype)
103
104 for rss in self.live_send_site do
105 if not mtype.is_subtype(self.mainmodule, null, rss.receiver) then continue
106 if mtype.has_mproperty(self.mainmodule, rss.mmethod) then
107 self.add_monomorphic_send(mtype, rss.mmethod)
108 end
109 end
110 end
111
112 fun add_send(mtype: MClassType, mmethod: MMethod)
113 do
114 var rss = new RuntimeSendSite(mmethod, mtype)
115 if self.live_send_site.has(rss) then return
116
117 self.live_send_site.add(rss)
118 self.polymorphic_methods.add(mmethod)
119
120 for mtype2 in self.live_types do
121 if not mtype2.is_subtype(self.mainmodule, null, mtype) then continue
122 if mtype2.has_mproperty(self.mainmodule, mmethod) then
123 self.add_monomorphic_send(mtype2, mmethod)
124 end
125 end
126 end
127
128 fun add_monomorphic_send(mtype: MClassType, mmethod: MMethod)
129 do
130 assert self.live_types.has(mtype)
131 var defs = mmethod.lookup_definitions(self.mainmodule, mtype)
132 if defs.is_empty then return
133 assert defs.length == 1 else print "conflict on {mtype} for {mmethod}: {defs.join(" ")}"
134 self.add_static_call(mtype, defs.first)
135 end
136
137 fun add_static_call(mtype: MClassType, mmethoddef: MMethodDef)
138 do
139 assert self.live_types.has(mtype)
140 var rm = new RuntimeMethod(mmethoddef, mtype)
141 if self.runtime_methods.has(rm) then return
142 self.runtime_methods.add(rm)
143 self.todo.add(rm)
144 self.live_methoddefs.add(mmethoddef)
145 end
146
147 fun add_cast_type(mtype: MClassType)
148 do
149 if self.live_cast_types.has(mtype) then return
150
151 assert not mtype.need_anchor
152 self.live_cast_types.add(mtype)
153 end
154
155 fun run_analysis
156 do
157 while not todo.is_empty do
158 var mr = todo.shift
159 if not self.modelbuilder.mpropdef2npropdef.has_key(mr.mmethoddef) then
160 # It is an init for a class?
161 if mr.mmethoddef.mproperty.name == "init" then
162 var nclassdef = self.modelbuilder.mclassdef2nclassdef[mr.mmethoddef.mclassdef]
163 var super_inits = nclassdef.super_inits
164 if super_inits != null then
165 #assert args.length == 1
166 for su in super_inits do
167 self.add_monomorphic_send(mr.receiver, su)
168 end
169 end
170
171 else
172 abort
173 end
174 continue
175 end
176 var npropdef = self.modelbuilder.mpropdef2npropdef[mr.mmethoddef]
177 if npropdef isa AConcreteMethPropdef then
178 #npropdef.debug("Visit {mr.mmethoddef} for {mr.receiver}")
179 var nclassdef = npropdef.parent.as(AClassdef)
180 var mmethoddef = npropdef.mpropdef.as(not null)
181 var auto_super_inits = npropdef.auto_super_inits
182 if auto_super_inits != null then
183 for auto_super_init in auto_super_inits do
184 self.add_monomorphic_send(mr.receiver, auto_super_init)
185 end
186 end
187 var v = new RuntimeTypeVisitor(self, nclassdef, mmethoddef, mr.receiver)
188 v.enter_visit(npropdef.n_block)
189 else if npropdef isa ADeferredMethPropdef then
190 # nothing to do (maybe add a waring?)
191 else if npropdef isa AAttrPropdef then
192 # nothing to do
193 else if npropdef isa AInternMethPropdef or npropdef isa AExternMethPropdef then
194 # UGLY: We force the "instantation" of the concrete return type if any
195 var ret = mr.mmethoddef.msignature.return_mtype
196 if ret != null and ret isa MClassType and ret.mclass.kind != abstract_kind and ret.mclass.kind != interface_kind then
197 ret = ret.anchor_to(self.mainmodule, mr.receiver)
198 self.add_type(ret)
199 end
200 else if npropdef isa AExternInitPropdef then
201 self.add_type(mr.receiver)
202 else
203 npropdef.debug("Not yet implemented")
204 abort
205 end
206 end
207 end
208 end
209
210 class RuntimeMethod
211 var mmethoddef: MMethodDef
212 var receiver: MClassType
213
214 redef fun ==(o)
215 do
216 return o isa RuntimeMethod and o.mmethoddef == self.mmethoddef and o.receiver == self.receiver
217 end
218
219 redef fun hash
220 do
221 return self.mmethoddef.hash + self.receiver.hash
222 end
223 end
224
225 class RuntimeSendSite
226 var mmethod: MMethod
227 var receiver: MClassType
228
229 redef fun ==(o)
230 do
231 return o isa RuntimeSendSite and o.mmethod == self.mmethod and o.receiver == self.receiver
232 end
233
234 redef fun hash
235 do
236 return self.mmethod.hash + self.receiver.hash
237 end
238 end
239
240 private class RuntimeTypeVisitor
241 super Visitor
242
243 var analysis: RuntimeTypeAnalysis
244
245 var nclassdef: AClassdef
246
247 var mmethoddef: MMethodDef
248
249 var receiver: MClassType
250
251 init(analysis: RuntimeTypeAnalysis, nclassdef: AClassdef, mmethoddef: MMethodDef, receiver: MClassType)
252 do
253 self.analysis = analysis
254 self.nclassdef = nclassdef
255 self.mmethoddef = mmethoddef
256 self.receiver = receiver
257 end
258
259 # Adapt and remove nullable
260 # return null if we got the null type
261 fun cleanup_type(mtype: MType): nullable MClassType
262 do
263 mtype = mtype.anchor_to(self.analysis.mainmodule, self.receiver)
264 if mtype isa MNullType then return null
265 if mtype isa MNullableType then mtype = mtype.mtype
266 assert mtype isa MClassType
267 assert not mtype.need_anchor
268 return mtype
269 end
270
271 fun add_type(mtype: MType)
272 do
273 var runtimetype = cleanup_type(mtype)
274 if runtimetype == null then return # we do not care about null
275
276 self.analysis.add_type(runtimetype)
277 #self.current_node.debug("add_type {runtimetype}")
278 end
279
280 fun add_send(mtype: MType, mproperty: MMethod)
281 do
282 var runtimetype = cleanup_type(mtype)
283 if runtimetype == null then return # we do not care about null
284
285 analysis.add_send(runtimetype, mproperty)
286 #self.current_node.debug("add_send {mproperty}")
287 end
288
289 fun add_monomorphic_send(mtype: MType, mproperty: MMethod)
290 do
291 var runtimetype = cleanup_type(mtype)
292 if runtimetype == null then return # we do not care about null
293
294 self.analysis.add_monomorphic_send(runtimetype, mproperty)
295 #self.current_node.debug("add_static call {runtimetype} {mproperty}")
296 end
297
298 fun add_cast_type(mtype: MType)
299 do
300 var runtimetype = cleanup_type(mtype)
301 if runtimetype == null then return # we do not care about null
302
303 self.analysis.add_cast_type(runtimetype)
304 #self.current_node.debug("add_cast_type {runtimetype}")
305 end
306
307 redef fun visit(node)
308 do
309 if node == null then return
310 node.accept_runtime_type_vistor(self)
311 node.visit_all(self)
312 end
313
314 # Force to get the primitive class named `name' or abort
315 fun get_class(name: String): MClass
316 do
317 var cla = analysis.mainmodule.model.get_mclasses_by_name(name)
318 if cla == null then
319 if name == "Bool" then
320 var c = new MClass(analysis.mainmodule, name, 0, enum_kind, public_visibility)
321 var cladef = new MClassDef(analysis.mainmodule, c.mclass_type, new Location(null, 0,0,0,0), new Array[String])
322 return c
323 end
324 self.current_node.debug("Fatal Error: no primitive class {name}")
325 abort
326 end
327 assert cla.length == 1 else print cla.join(", ")
328 return cla.first
329 end
330
331 # Force to get the primitive property named `name' in the instance `recv' or abort
332 fun get_method(recv: MType, name: String): MMethod
333 do
334 var runtimetype = cleanup_type(recv)
335 if runtimetype == null then abort
336
337 var props = self.analysis.mainmodule.model.get_mproperties_by_name(name)
338 if props == null then
339 self.current_node.debug("Fatal Error: no primitive property {name} on {runtimetype}")
340 abort
341 end
342 var res: nullable MMethod = null
343 for mprop in props do
344 assert mprop isa MMethod
345 if not runtimetype.has_mproperty(self.analysis.mainmodule, mprop) then continue
346 if mprop.is_init and mprop.intro_mclassdef.mclass != runtimetype.mclass then continue
347 if res == null then
348 res = mprop
349 else
350 self.current_node.debug("Fatal Error: ambigous property name '{name}'; conflict between {mprop.full_name} and {res.full_name}")
351 abort
352 end
353 end
354 if res == null then
355 self.current_node.debug("Fatal Error: no primitive property {name} on {runtimetype}")
356 abort
357 end
358 return res
359 end
360 end
361
362 ###
363
364 redef class ANode
365 private fun accept_runtime_type_vistor(v: RuntimeTypeVisitor)
366 do
367 end
368 end
369
370 redef class AIntExpr
371 redef fun accept_runtime_type_vistor(v)
372 do
373 v.add_type(self.mtype.as(not null))
374 end
375 end
376
377 redef class AFloatExpr
378 redef fun accept_runtime_type_vistor(v)
379 do
380 v.add_type(self.mtype.as(not null))
381 end
382 end
383
384 redef class ACharExpr
385 redef fun accept_runtime_type_vistor(v)
386 do
387 v.add_type(self.mtype.as(not null))
388 end
389 end
390
391 redef class AArrayExpr
392 redef fun accept_runtime_type_vistor(v)
393 do
394 var mtype = self.mtype.as(not null)
395 v.add_type(mtype)
396 var native = v.get_class("NativeArray").get_mtype([mtype.as(MGenericType).arguments.first])
397 v.add_type(native)
398 var prop = v.get_method(mtype, "with_native")
399 v.add_monomorphic_send(mtype, prop)
400 end
401 end
402
403 redef class AStringFormExpr
404 redef fun accept_runtime_type_vistor(v)
405 do
406 var mtype = self.mtype.as(not null)
407 v.add_type(mtype)
408 var native = v.get_class("NativeString").mclass_type
409 v.add_type(native)
410 var prop = v.get_method(mtype, "from_cstring")
411 v.add_monomorphic_send(mtype, prop)
412 end
413 end
414
415 redef class ASuperstringExpr
416 redef fun accept_runtime_type_vistor(v)
417 do
418 var arraytype = v.get_class("Array").get_mtype([v.get_class("Object").mclass_type])
419 v.add_type(arraytype)
420 var prop = v.get_method(arraytype, "join")
421 v.add_monomorphic_send(arraytype, prop)
422 end
423 end
424
425 redef class ACrangeExpr
426 redef fun accept_runtime_type_vistor(v)
427 do
428 var mtype = self.mtype.as(not null)
429 v.add_type(mtype)
430 var prop = v.get_method(mtype, "init")
431 v.add_monomorphic_send(mtype, prop)
432 end
433 end
434
435 redef class AOrangeExpr
436 redef fun accept_runtime_type_vistor(v)
437 do
438 var mtype = self.mtype.as(not null)
439 v.add_type(mtype)
440 var prop = v.get_method(mtype, "without_last")
441 v.add_monomorphic_send(mtype, prop)
442 end
443 end
444
445 redef class ATrueExpr
446 redef fun accept_runtime_type_vistor(v)
447 do
448 v.add_type(self.mtype.as(not null))
449 end
450 end
451
452 redef class AFalseExpr
453 redef fun accept_runtime_type_vistor(v)
454 do
455 v.add_type(self.mtype.as(not null))
456 end
457 end
458
459 redef class AIsaExpr
460 redef fun accept_runtime_type_vistor(v)
461 do
462 v.add_cast_type(self.cast_type.as(not null))
463 end
464 end
465
466 redef class AAsCastExpr
467 redef fun accept_runtime_type_vistor(v)
468 do
469 v.add_cast_type(self.mtype.as(not null))
470 end
471 end
472
473 #
474
475 redef class ASendExpr
476 redef fun accept_runtime_type_vistor(v)
477 do
478 var mproperty = self.mproperty.as(not null)
479 if n_expr isa ASelfExpr then
480 v.add_monomorphic_send(v.receiver, mproperty)
481 else
482 var recvtype = self.n_expr.mtype.as(not null)
483 v.add_send(recvtype, mproperty)
484 end
485 end
486 end
487
488 redef class ASendReassignFormExpr
489 redef fun accept_runtime_type_vistor(v)
490 do
491 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
492 var mproperty = self.mproperty.as(not null)
493 var write_mproperty = self.write_mproperty.as(not null)
494 if n_expr isa ASelfExpr then
495 v.add_monomorphic_send(v.receiver, mproperty)
496 v.add_monomorphic_send(v.receiver, write_mproperty)
497 else
498 var recvtype = self.n_expr.mtype.as(not null)
499 v.add_send(recvtype, mproperty)
500 v.add_send(recvtype, write_mproperty)
501 end
502 end
503 end
504
505 redef class AVarReassignExpr
506 redef fun accept_runtime_type_vistor(v)
507 do
508 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
509 end
510 end
511
512 redef class AAttrReassignExpr
513 redef fun accept_runtime_type_vistor(v)
514 do
515 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
516 end
517 end
518
519 redef class ASuperExpr
520 redef fun accept_runtime_type_vistor(v)
521 do
522 var mproperty = self.mproperty
523 if mproperty != null then
524 v.add_monomorphic_send(v.receiver, mproperty)
525 return
526 end
527
528 #FIXME: we do not want an ugly static call!
529 var mpropdef = v.mmethoddef
530 var mpropdefs = mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype)
531 if mpropdefs.length != 1 then
532 debug("MPRODFEFS for super {mpropdef} for {v.receiver}: {mpropdefs.join(", ")}")
533 end
534 var msuperpropdef = mpropdefs.first
535 assert msuperpropdef isa MMethodDef
536 v.analysis.add_static_call(v.receiver, msuperpropdef)
537 end
538 end
539
540 redef class AForExpr
541 redef fun accept_runtime_type_vistor(v)
542 do
543 var recvtype = self.n_expr.mtype.as(not null)
544 var colltype = v.get_class("Collection").mclassdefs.first.bound_mtype
545 v.add_send(recvtype, v.get_method(colltype, "iterator"))
546 var iteratortype = v.get_class("Iterator").mclassdefs.first.bound_mtype
547 var objtype = v.get_class("Object").mclass_type
548 v.add_send(objtype, v.get_method(iteratortype, "is_ok"))
549 v.add_send(objtype, v.get_method(iteratortype, "item"))
550 v.add_send(objtype, v.get_method(iteratortype, "next"))
551 end
552 end
553
554 redef class ANewExpr
555 redef fun accept_runtime_type_vistor(v)
556 do
557 var recvtype = self.mtype.as(not null)
558 v.add_type(recvtype)
559 v.add_monomorphic_send(recvtype, mproperty.as(not null))
560 end
561 end