1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # This package implements Reachable Type Analysis(RTA)
20 import instantiated_type_analysis
21 import reachable_method_analysis
26 special ReachableMethodAnalysis
27 special InstantiatedTypeAnalysis
29 readable var _instanciated_classes
: HashSet[MMLocalClass] = new HashSet[MMLocalClass]
31 redef fun is_class_instantiated
(local_class
: MMLocalClass): Bool do return instanciated_classes
.has
(local_class
)
33 readable var _reachable_iroutines
: HashSet[IRoutine] = new HashSet[IRoutine]
35 redef fun is_iroutine_reachable
(ir
: nullable IRoutine): Bool do
36 return ir
!= null and reachable_iroutines
.has
(ir
)
39 redef fun is_method_reachable
(method
: MMMethod): Bool do
40 return is_iroutine_reachable
(method
.iroutine
)
45 readable var _context
: RtaContext
46 readable var _program
: Program
47 readable var _iroutine_to_search
: List[IRoutine] = new List[IRoutine]
48 readable var _call_sites
: HashSet[IAbsCall] = new HashSet[IAbsCall]
49 readable var _called_methods
: HashSet[MMMethod] = new HashSet[MMMethod]
52 _context
= new RtaContext()
56 # Check if the class where the method was introduced is instantiated
57 # Also check every subclasses if they use the same 'version' of the
59 fun check_method
(m
: MMMethod): Bool do
60 var m_cls
= m
.local_class
.for_module
(program
.module)
62 if context
.is_class_instantiated
(m_cls
) then return true
64 for cls
in m_cls
.cshe
.smallers
do
65 if context
.is_class_instantiated
(cls
) then return true
70 # Check all call sites to find those that just became 'activated'
71 # by some new type instantiation
72 fun check_call_sites
do
73 var calls_to_remove
= new List[IAbsCall]
74 for call
in call_sites
do
78 # Check for this method
79 if check_method
(m
) then
80 add_reachable_iroutine
(m
.iroutine
)
85 # Check all methods 'under' this one in the hierarchy
86 for other
in m
.prhe
.smallers
do
87 if not other
isa MMMethod then continue
88 if check_method
(other
) then
89 add_reachable_iroutine
(other
.iroutine
)
95 if all_added
then calls_to_remove
.add
(call
)
98 # If all sub-methods are added, no need to keep checking this call !
99 for call
in calls_to_remove
do
100 call_sites
.remove
(call
)
104 fun add_instantiated_class
(cls
: MMLocalClass) do
105 if context
.is_class_instantiated
(cls
) then return
106 context
.instanciated_classes
.add
(cls
)
111 fun add_reachable_call
(call
: IAbsCall) do
112 var m
= call
.property
113 if called_methods
.has
(m
) then return
114 if call_sites
.has
(call
) then return
116 if m
.global
.is_init
then
117 called_methods
.add
(m
)
118 add_reachable_iroutine
(m
.iroutine
)
120 # No need to add the call site or to process other
121 # call sites when it is an initializer: we know
122 # exactly which one will get called
125 called_methods
.add
(m
)
130 fun add_reachable_iroutine
(ir
: nullable IRoutine) do
131 if ir
== null or context
.is_iroutine_reachable
(ir
) then return
132 context
.reachable_iroutines
.add
(ir
)
133 iroutine_to_search
.add
(ir
)
136 # Need to hard-code some automaticaly instanciated types !
137 private fun force_some_type_analysis
do
138 var forced_types
= ["Object", "Bool", "Float", "Int", "String", "NativeString", "Range", "Array", "ArrayIterator"]
140 for some_type
in forced_types
do
141 if not program
.module.has_global_class_named
(some_type
.to_symbol
) then continue
142 var cls_type
= program
.module.class_by_name
(some_type
.to_symbol
)
143 add_instantiated_class
(cls_type
)
146 if program
.module.has_global_class_named
("Pointer".to_symbol
) then
147 var ptr_class
= program
.module.class_by_name
("Pointer".to_symbol
)
148 # Assume that all classes that are subclasses of Pointer
149 # can be returned by the native interface
150 # and are always counted as instantiated
151 for ptr_sub_class
in ptr_class
.cshe
.smallers
do
152 add_instantiated_class
(ptr_sub_class
)
156 for cls
in program
.module.global_classes
do
157 if not cls
.is_universal
then continue
158 add_instantiated_class
(program
.module[cls
])
162 # Build the context associated with this builder
164 if program
.main_method
== null then
165 # Add primitive type (so that compiling works)
166 for t
in ["Int","Char","Bool"] do
167 if program
.module.has_global_class_named
(t
.to_symbol
) then
168 add_instantiated_class
(program
.module.class_by_name
(t
.to_symbol
))
174 add_instantiated_class
(program
.main_class
.as(not null))
175 add_reachable_iroutine
(program
.main_method
.as(not null).iroutine
)
176 force_some_type_analysis
178 while not iroutine_to_search
.is_empty
do
179 var v
= new RtaVisitor(self)
180 var iroutine
= iroutine_to_search
.pop
181 v
.visit_icode
(iroutine
.body
)
188 readable var _builder
: RtaBuilder
190 redef fun visit_icode
(ic
)
192 if ic
isa IStaticCall then
193 # FIXME: take only the last property on the redef. hierarchie
194 builder
.add_reachable_iroutine
(ic
.property
.iroutine
)
195 else if ic
isa INew then
196 # FIXME: take only the last property on the redef. hierarchie
198 var cls
= t
.for_module
(builder
.program
.module).local_class
199 var m
= cls
[ic
.property
.global
].as(MMMethod)
200 var r
= cls
.new_instance_iroutine
[m
]
201 builder
.add_instantiated_class
(cls
)
202 builder
.add_reachable_iroutine
(r
)
203 else if ic
isa ISuper then
204 # Add every parents ...
205 for p
in ic
.property
.prhe
.greaters_and_self
do
206 if p
isa MMMethod then
207 builder
.add_reachable_iroutine
(p
.iroutine
)
210 else if ic
isa ICall then
211 builder
.add_reachable_call
(ic
)
212 else if ic
isa ICheckInstance then
214 var cls
= t
.for_module
(builder
.program
.module).local_class
215 var ir
= cls
.checknew_iroutine
216 builder
.add_reachable_iroutine
(ir
)
217 else if ic
isa IInitAttributes then
219 var cls
= t
.for_module
(builder
.program
.module).local_class
220 var ir
= cls
.init_var_iroutine
221 builder
.add_reachable_iroutine
(ir
)