Doc review on newmodel.
[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 and pollutes 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 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 has the disadvantage 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 # RuntimeTypeAnalysis looks for alive runtime types in application.
86 # The entry point of the analysis is the mainmodule of the application.
87 class RuntimeTypeAnalysis
88 var modelbuilder: ModelBuilder
89 var mainmodule: MModule
90 var live_types: HashSet[MClassType] = new HashSet[MClassType]
91 var live_cast_types: HashSet[MClassType] = new HashSet[MClassType]
92 var polymorphic_methods: HashSet[MMethod] = new HashSet[MMethod]
93 var live_methoddefs: HashSet[MMethodDef] = new HashSet[MMethodDef]
94 var runtime_methods: HashSet[RuntimeMethod] = new HashSet[RuntimeMethod]
95 var live_send_site: HashSet[RuntimeSendSite] = new HashSet[RuntimeSendSite]
96
97 private var todo: List[RuntimeMethod] = new List[RuntimeMethod]
98
99 fun add_type(mtype: MClassType)
100 do
101 if self.live_types.has(mtype) then return
102
103 assert not mtype.need_anchor
104 self.live_types.add(mtype)
105
106 for rss in self.live_send_site do
107 if not mtype.is_subtype(self.mainmodule, null, rss.receiver) then continue
108 if mtype.has_mproperty(self.mainmodule, rss.mmethod) then
109 self.add_monomorphic_send(mtype, rss.mmethod)
110 end
111 end
112 end
113
114 fun add_send(mtype: MClassType, mmethod: MMethod)
115 do
116 var rss = new RuntimeSendSite(mmethod, mtype)
117 if self.live_send_site.has(rss) then return
118
119 self.live_send_site.add(rss)
120 self.polymorphic_methods.add(mmethod)
121
122 for mtype2 in self.live_types do
123 if not mtype2.is_subtype(self.mainmodule, null, mtype) then continue
124 if mtype2.has_mproperty(self.mainmodule, mmethod) then
125 self.add_monomorphic_send(mtype2, mmethod)
126 end
127 end
128 end
129
130 fun add_monomorphic_send(mtype: MClassType, mmethod: MMethod)
131 do
132 assert self.live_types.has(mtype)
133 var defs = mmethod.lookup_definitions(self.mainmodule, mtype)
134 if defs.is_empty then return
135 assert defs.length == 1 else print "conflict on {mtype} for {mmethod}: {defs.join(" ")}"
136 self.add_static_call(mtype, defs.first)
137 end
138
139 fun add_static_call(mtype: MClassType, mmethoddef: MMethodDef)
140 do
141 assert self.live_types.has(mtype)
142 var rm = new RuntimeMethod(mmethoddef, mtype)
143 if self.runtime_methods.has(rm) then return
144 self.runtime_methods.add(rm)
145 self.todo.add(rm)
146 self.live_methoddefs.add(mmethoddef)
147 end
148
149 fun add_cast_type(mtype: MClassType)
150 do
151 if self.live_cast_types.has(mtype) then return
152
153 assert not mtype.need_anchor
154 self.live_cast_types.add(mtype)
155 end
156
157 # Start the analysis.
158 fun run_analysis
159 do
160 while not todo.is_empty do
161 var mr = todo.shift
162 if not self.modelbuilder.mpropdef2npropdef.has_key(mr.mmethoddef) then
163 # It is an init for a class?
164 if mr.mmethoddef.mproperty.name == "init" then
165 var nclassdef = self.modelbuilder.mclassdef2nclassdef[mr.mmethoddef.mclassdef]
166 var super_inits = nclassdef.super_inits
167 if super_inits != null then
168 #assert args.length == 1
169 for su in super_inits do
170 self.add_monomorphic_send(mr.receiver, su)
171 end
172 end
173
174 else
175 abort
176 end
177 continue
178 end
179 var npropdef = self.modelbuilder.mpropdef2npropdef[mr.mmethoddef]
180 if npropdef isa AConcreteMethPropdef then
181 #npropdef.debug("Visit {mr.mmethoddef} for {mr.receiver}")
182 var nclassdef = npropdef.parent.as(AClassdef)
183 var mmethoddef = npropdef.mpropdef.as(not null)
184 var auto_super_inits = npropdef.auto_super_inits
185 if auto_super_inits != null then
186 for auto_super_init in auto_super_inits do
187 self.add_monomorphic_send(mr.receiver, auto_super_init)
188 end
189 end
190 var v = new RuntimeTypeVisitor(self, nclassdef, mmethoddef, mr.receiver)
191 v.enter_visit(npropdef.n_block)
192 else if npropdef isa ADeferredMethPropdef then
193 # nothing to do (maybe add a waring?)
194 else if npropdef isa AAttrPropdef then
195 # nothing to do
196 else if npropdef isa AInternMethPropdef or npropdef isa AExternMethPropdef then
197 # UGLY: We force the "instantation" of the concrete return type if any
198 var ret = mr.mmethoddef.msignature.return_mtype
199 if ret != null and ret isa MClassType and ret.mclass.kind != abstract_kind and ret.mclass.kind != interface_kind then
200 ret = ret.anchor_to(self.mainmodule, mr.receiver)
201 self.add_type(ret)
202 end
203 else if npropdef isa AExternInitPropdef then
204 self.add_type(mr.receiver)
205 else
206 npropdef.debug("Not yet implemented")
207 abort
208 end
209 end
210 end
211 end
212
213 class RuntimeMethod
214 var mmethoddef: MMethodDef
215 var receiver: MClassType
216
217 redef fun ==(o)
218 do
219 return o isa RuntimeMethod and o.mmethoddef == self.mmethoddef and o.receiver == self.receiver
220 end
221
222 redef fun hash
223 do
224 return self.mmethoddef.hash + self.receiver.hash
225 end
226 end
227
228 class RuntimeSendSite
229 var mmethod: MMethod
230 var receiver: MClassType
231
232 redef fun ==(o)
233 do
234 return o isa RuntimeSendSite and o.mmethod == self.mmethod and o.receiver == self.receiver
235 end
236
237 redef fun hash
238 do
239 return self.mmethod.hash + self.receiver.hash
240 end
241 end
242
243 private class RuntimeTypeVisitor
244 super Visitor
245
246 var analysis: RuntimeTypeAnalysis
247
248 var nclassdef: AClassdef
249
250 var mmethoddef: MMethodDef
251
252 var receiver: MClassType
253
254 init(analysis: RuntimeTypeAnalysis, nclassdef: AClassdef, mmethoddef: MMethodDef, receiver: MClassType)
255 do
256 self.analysis = analysis
257 self.nclassdef = nclassdef
258 self.mmethoddef = mmethoddef
259 self.receiver = receiver
260 end
261
262 # Adapt and remove nullable
263 # return null if we got the null type
264 fun cleanup_type(mtype: MType): nullable MClassType
265 do
266 mtype = mtype.anchor_to(self.analysis.mainmodule, self.receiver)
267 if mtype isa MNullType then return null
268 if mtype isa MNullableType then mtype = mtype.mtype
269 assert mtype isa MClassType
270 assert not mtype.need_anchor
271 return mtype
272 end
273
274 fun add_type(mtype: MType)
275 do
276 var runtimetype = cleanup_type(mtype)
277 if runtimetype == null then return # we do not care about null
278
279 self.analysis.add_type(runtimetype)
280 #self.current_node.debug("add_type {runtimetype}")
281 end
282
283 fun add_send(mtype: MType, mproperty: MMethod)
284 do
285 var runtimetype = cleanup_type(mtype)
286 if runtimetype == null then return # we do not care about null
287
288 analysis.add_send(runtimetype, mproperty)
289 #self.current_node.debug("add_send {mproperty}")
290 end
291
292 fun add_monomorphic_send(mtype: MType, mproperty: MMethod)
293 do
294 var runtimetype = cleanup_type(mtype)
295 if runtimetype == null then return # we do not care about null
296
297 self.analysis.add_monomorphic_send(runtimetype, mproperty)
298 #self.current_node.debug("add_static call {runtimetype} {mproperty}")
299 end
300
301 fun add_cast_type(mtype: MType)
302 do
303 var runtimetype = cleanup_type(mtype)
304 if runtimetype == null then return # we do not care about null
305
306 self.analysis.add_cast_type(runtimetype)
307 #self.current_node.debug("add_cast_type {runtimetype}")
308 end
309
310 redef fun visit(node)
311 do
312 if node == null then return
313 node.accept_runtime_type_vistor(self)
314 node.visit_all(self)
315 end
316
317 # Force to get the primitive class named `name' or abort
318 fun get_class(name: String): MClass
319 do
320 var cla = analysis.mainmodule.model.get_mclasses_by_name(name)
321 if cla == null then
322 if name == "Bool" then
323 var c = new MClass(analysis.mainmodule, name, 0, enum_kind, public_visibility)
324 var cladef = new MClassDef(analysis.mainmodule, c.mclass_type, new Location(null, 0,0,0,0), new Array[String])
325 return c
326 end
327 self.current_node.debug("Fatal Error: no primitive class {name}")
328 abort
329 end
330 assert cla.length == 1 else print cla.join(", ")
331 return cla.first
332 end
333
334 # Force to get the primitive property named `name' in the instance `recv' or abort
335 fun get_method(recv: MType, name: String): MMethod
336 do
337 var runtimetype = cleanup_type(recv)
338 if runtimetype == null then abort
339
340 var props = self.analysis.mainmodule.model.get_mproperties_by_name(name)
341 if props == null then
342 self.current_node.debug("Fatal Error: no primitive property {name} on {runtimetype}")
343 abort
344 end
345 var res: nullable MMethod = null
346 for mprop in props do
347 assert mprop isa MMethod
348 if not runtimetype.has_mproperty(self.analysis.mainmodule, mprop) then continue
349 if mprop.is_init and mprop.intro_mclassdef.mclass != runtimetype.mclass then continue
350 if res == null then
351 res = mprop
352 else
353 self.current_node.debug("Fatal Error: ambigous property name '{name}'; conflict between {mprop.full_name} and {res.full_name}")
354 abort
355 end
356 end
357 if res == null then
358 self.current_node.debug("Fatal Error: no primitive property {name} on {runtimetype}")
359 abort
360 end
361 return res
362 end
363 end
364
365 ###
366
367 redef class ANode
368 private fun accept_runtime_type_vistor(v: RuntimeTypeVisitor)
369 do
370 end
371 end
372
373 redef class AIntExpr
374 redef fun accept_runtime_type_vistor(v)
375 do
376 v.add_type(self.mtype.as(not null))
377 end
378 end
379
380 redef class AFloatExpr
381 redef fun accept_runtime_type_vistor(v)
382 do
383 v.add_type(self.mtype.as(not null))
384 end
385 end
386
387 redef class ACharExpr
388 redef fun accept_runtime_type_vistor(v)
389 do
390 v.add_type(self.mtype.as(not null))
391 end
392 end
393
394 redef class AArrayExpr
395 redef fun accept_runtime_type_vistor(v)
396 do
397 var mtype = self.mtype.as(not null)
398 v.add_type(mtype)
399 var native = v.get_class("NativeArray").get_mtype([mtype.as(MGenericType).arguments.first])
400 v.add_type(native)
401 var prop = v.get_method(mtype, "with_native")
402 v.add_monomorphic_send(mtype, prop)
403 end
404 end
405
406 redef class AStringFormExpr
407 redef fun accept_runtime_type_vistor(v)
408 do
409 var mtype = self.mtype.as(not null)
410 v.add_type(mtype)
411 var native = v.get_class("NativeString").mclass_type
412 v.add_type(native)
413 var prop = v.get_method(mtype, "from_cstring")
414 v.add_monomorphic_send(mtype, prop)
415 end
416 end
417
418 redef class ASuperstringExpr
419 redef fun accept_runtime_type_vistor(v)
420 do
421 var arraytype = v.get_class("Array").get_mtype([v.get_class("Object").mclass_type])
422 v.add_type(arraytype)
423 var prop = v.get_method(arraytype, "join")
424 v.add_monomorphic_send(arraytype, prop)
425 end
426 end
427
428 redef class ACrangeExpr
429 redef fun accept_runtime_type_vistor(v)
430 do
431 var mtype = self.mtype.as(not null)
432 v.add_type(mtype)
433 var prop = v.get_method(mtype, "init")
434 v.add_monomorphic_send(mtype, prop)
435 end
436 end
437
438 redef class AOrangeExpr
439 redef fun accept_runtime_type_vistor(v)
440 do
441 var mtype = self.mtype.as(not null)
442 v.add_type(mtype)
443 var prop = v.get_method(mtype, "without_last")
444 v.add_monomorphic_send(mtype, prop)
445 end
446 end
447
448 redef class ATrueExpr
449 redef fun accept_runtime_type_vistor(v)
450 do
451 v.add_type(self.mtype.as(not null))
452 end
453 end
454
455 redef class AFalseExpr
456 redef fun accept_runtime_type_vistor(v)
457 do
458 v.add_type(self.mtype.as(not null))
459 end
460 end
461
462 redef class AIsaExpr
463 redef fun accept_runtime_type_vistor(v)
464 do
465 v.add_cast_type(self.cast_type.as(not null))
466 end
467 end
468
469 redef class AAsCastExpr
470 redef fun accept_runtime_type_vistor(v)
471 do
472 v.add_cast_type(self.mtype.as(not null))
473 end
474 end
475
476 #
477
478 redef class ASendExpr
479 redef fun accept_runtime_type_vistor(v)
480 do
481 var mproperty = self.mproperty.as(not null)
482 if n_expr isa ASelfExpr then
483 v.add_monomorphic_send(v.receiver, mproperty)
484 else
485 var recvtype = self.n_expr.mtype.as(not null)
486 v.add_send(recvtype, mproperty)
487 end
488 end
489 end
490
491 redef class ASendReassignFormExpr
492 redef fun accept_runtime_type_vistor(v)
493 do
494 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
495 var mproperty = self.mproperty.as(not null)
496 var write_mproperty = self.write_mproperty.as(not null)
497 if n_expr isa ASelfExpr then
498 v.add_monomorphic_send(v.receiver, mproperty)
499 v.add_monomorphic_send(v.receiver, write_mproperty)
500 else
501 var recvtype = self.n_expr.mtype.as(not null)
502 v.add_send(recvtype, mproperty)
503 v.add_send(recvtype, write_mproperty)
504 end
505 end
506 end
507
508 redef class AVarReassignExpr
509 redef fun accept_runtime_type_vistor(v)
510 do
511 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
512 end
513 end
514
515 redef class AAttrReassignExpr
516 redef fun accept_runtime_type_vistor(v)
517 do
518 v.add_send(self.read_type.as(not null), self.reassign_property.mproperty)
519 end
520 end
521
522 redef class ASuperExpr
523 redef fun accept_runtime_type_vistor(v)
524 do
525 var mproperty = self.mproperty
526 if mproperty != null then
527 v.add_monomorphic_send(v.receiver, mproperty)
528 return
529 end
530
531 #FIXME: we do not want an ugly static call!
532 var mpropdef = v.mmethoddef
533 var mpropdefs = mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype)
534 if mpropdefs.length != 1 then
535 debug("MPRODFEFS for super {mpropdef} for {v.receiver}: {mpropdefs.join(", ")}")
536 end
537 var msuperpropdef = mpropdefs.first
538 assert msuperpropdef isa MMethodDef
539 v.analysis.add_static_call(v.receiver, msuperpropdef)
540 end
541 end
542
543 redef class AForExpr
544 redef fun accept_runtime_type_vistor(v)
545 do
546 var recvtype = self.n_expr.mtype.as(not null)
547 var colltype = v.get_class("Collection").mclassdefs.first.bound_mtype
548 v.add_send(recvtype, v.get_method(colltype, "iterator"))
549 var iteratortype = v.get_class("Iterator").mclassdefs.first.bound_mtype
550 var objtype = v.get_class("Object").mclass_type
551 v.add_send(objtype, v.get_method(iteratortype, "is_ok"))
552 v.add_send(objtype, v.get_method(iteratortype, "item"))
553 v.add_send(objtype, v.get_method(iteratortype, "next"))
554 end
555 end
556
557 redef class ANewExpr
558 redef fun accept_runtime_type_vistor(v)
559 do
560 var recvtype = self.mtype.as(not null)
561 v.add_type(recvtype)
562 v.add_monomorphic_send(recvtype, mproperty.as(not null))
563 end
564 end