icode: add automatic tests for virtual type parameters in methods
[nit.git] / src / icode / icode_builder.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2009 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 # Help generation of icode
18 package icode_builder
19
20 import icode_base
21
22 # Helps to generate icodes in a iroutine
23 class ICodeBuilder
24 # Add a new statment in the current icode sequence
25 # Can be used with expression if the result is ignored of if the result is already set
26 fun stmt(s: ICode)
27 do
28 s.location = _current_location
29 _seq.icodes.add(s)
30 end
31
32 # Add a new expression in the current icode sequence and return a new associated result register
33 fun expr(e: ICode, s: MMType): IRegister
34 do
35 stmt(e)
36 assert e.result == null
37 var reg = new_register(s)
38 e.result = reg
39 return reg
40 end
41
42 # Add an assignement (IMove) in the current icode sequence
43 fun add_assignment(reg: IRegister, v: IRegister)
44 do
45 stmt(new IMove(reg, v))
46 end
47
48 # Check that the reciever e is not null (IIs + IAbort)
49 fun add_null_reciever_check(e: IRegister)
50 do
51 var nul = lit_null_reg
52 var c = expr(new IIs(e, nul), mmmodule.type_bool)
53 var iif = new IIf(c)
54 stmt(iif)
55 var old_seq = seq
56 seq = iif.then_seq
57 add_abort("Reciever is null")
58 seq = old_seq
59 end
60
61 # Add a type cast (ITypeCheck + IAbort) in the current icode sequence
62 fun add_type_cast(e: IRegister, stype: MMType)
63 do
64 var c = expr(new ITypeCheck(iroutine.params.first, e, stype), mmmodule.type_bool)
65 var iif = new IIf(c)
66 stmt(iif)
67 var old_seq = seq
68 seq = iif.else_seq
69 add_abort("Cast failed")
70 seq = old_seq
71 end
72
73 # Add an attr check (IAttrIsset + IAbort) in the current icode sequence
74 fun add_attr_check(prop: MMAttribute, e: IRegister)
75 do
76 if not prop.signature.return_type.is_nullable then
77 var cond = expr(new IAttrIsset(prop, e), mmmodule.type_bool)
78 var iif = new IIf(cond)
79 stmt(iif)
80 var seq_old = seq
81 seq = iif.else_seq
82 add_abort("Uninitialized attribute %s", prop.name.to_s)
83 seq = seq_old
84 end
85 end
86
87 # Add an IAttrRead guarded by an add_attr_check in the current icode sequence
88 fun add_attr_read(prop: MMAttribute, e: IRegister): IRegister
89 do
90 add_attr_check(prop, e)
91 return expr(new IAttrRead(prop, e), prop.signature.return_type.as(not null))
92 end
93
94 # Add a localized IAbort
95 fun add_abort(s: String...)
96 do
97 stmt(new IAbort(s, mmmodule))
98 end
99
100 # Add an assigment to the iroutine return value
101 # Beware, no jump is generated
102 fun add_return_value(reg: IRegister)
103 do
104 add_assignment(iroutine.result.as(not null), reg)
105 end
106
107 # Add an ICall with possible simple inlining in the current icode sequence
108 fun add_call(prop: MMMethod, args: Array[IRegister], closcns: nullable Array[nullable IClosureDef]): nullable IRegister
109 do
110 var ee = once "==".to_symbol
111
112 # Inline "x!=y" as "not x==y"
113 var ne = once "!=".to_symbol
114 if prop.name == ne then
115 var eqp = prop.signature.recv.local_class.select_method(ee)
116 var eqcall = add_call(eqp, args, closcns).as(not null)
117 return expr(new INot(eqcall), mmmodule.type_bool)
118 end
119
120 # TODO: Inline x==y as "x is y or (x != null and (== is not the Object one) and x.==(y))"
121 # inline "x==y" as "x is y or x != null and x.==(y)"
122 var icall = new ICall(prop, args)
123 icall.closure_defs = closcns
124 if prop.name == ee then
125 # Prepare the result
126 var reg = new_register(mmmodule.type_bool)
127 # "x is y"
128 var cond = expr(new IIs(args[0], args[1]), mmmodule.type_bool)
129 var iif = new IIf(cond)
130 stmt(iif)
131 var seq_old = seq
132 seq = iif.then_seq
133 add_assignment(reg, cond)
134 # "or"
135 seq = iif.else_seq
136 # Do the "x != null" part iff x is nullable
137 if args[0].stype.is_nullable then
138 var nul = lit_null_reg
139 cond = expr(new IIs(args[0], nul), mmmodule.type_bool)
140 iif = new IIf(cond)
141 stmt(iif)
142 seq = iif.then_seq
143 add_assignment(reg, lit_false_reg)
144 seq = iif.else_seq
145 end
146 # "x.==(y)"
147 add_assignment(reg, expr(icall, mmmodule.type_bool))
148 seq = seq_old
149 return reg
150 end
151
152 if args.first.stype.is_nullable then add_null_reciever_check(args.first)
153 var rtype = prop.signature.return_type
154 if rtype != null then
155 return expr(icall, rtype)
156 else
157 stmt(icall)
158 return null
159 end
160 end
161
162 # Add an escape to a given sequence
163 # Create a new IEscapeMark if required
164 fun add_escape(seq: ISeq)
165 do
166 var mark = seq.iescape_mark
167 if mark == null then
168 mark = new IEscapeMark
169 iroutine.escape_marks.add(mark)
170 seq.iescape_mark = mark
171 end
172 stmt(new IEscape(mark))
173 end
174
175 # Return a literal "null" value
176 fun lit_null_reg: IRegister
177 do
178 return new_register(mmmodule.type_none)
179 end
180
181 # Return a literal "true" value
182 fun lit_true_reg: IRegister
183 do
184 var e = new IBoolValue(true)
185 return expr(e, mmmodule.type_bool)
186 end
187
188 # Return a literal "false" value
189 fun lit_false_reg: IRegister
190 do
191 var e = new IBoolValue(false)
192 return expr(e, mmmodule.type_bool)
193 end
194
195 # Get a new register
196 fun new_register(s: MMType): IRegister
197 do
198 var r = new IRegister(s)
199 iroutine.registers.add(r)
200 return r
201 end
202
203 # The module where classes and types are extracted
204 readable var _mmmodule: MMModule
205
206 # The current iroutine build
207 readable var _iroutine: IRoutine
208
209 # The current sequence of icodes
210 readable writable var _seq: ISeq
211
212 init(mod: MMModule, r: IRoutine)
213 do
214 _mmmodule = mod
215 _current_location = r.location
216 _iroutine = r
217 _seq = r.body
218 end
219
220 # New ICodes are set to this location
221 readable writable var _current_location: nullable Location = null
222 end
223
224 redef class MMSignature
225 # Create an empty IRoutine that match the signature
226 fun generate_empty_iroutine: IRoutine
227 do
228 var args = new Array[IRegister]
229 args.add(new IRegister(recv)) # Self
230 for i in [0..arity[ do
231 args.add(new IRegister(self[i]))
232 end
233 var res: nullable IRegister = null
234 var rtype = return_type
235 if rtype != null then
236 res = new IRegister(rtype)
237 end
238 var iroutine = new IRoutine(args, res)
239 var clos: nullable Array[IClosureDecl] = null
240 if not closures.is_empty then
241 clos = new Array[IClosureDecl]
242 for c in closures do
243 clos.add(new IClosureDecl(c))
244 end
245 iroutine.closure_decls = clos
246 end
247 # Add automatic test for virtual types
248 var icb = new ICodeBuilder(recv.mmmodule, iroutine)
249 for i in [0..arity[ do
250 var t = self[i]
251 if t isa MMVirtualType then
252 icb.add_type_cast(args[i+1], t)
253 end
254 end
255 return iroutine
256 end
257
258 # Create an empty IClosureDef that match the signature
259 fun generate_empty_iclosuredef(icb: ICodeBuilder): IClosureDef
260 do
261 var args = new Array[IRegister]
262 for i in [0..arity[ do
263 args.add(icb.new_register(self[i]))
264 end
265 var res: nullable IRegister = null
266 var rtype = return_type
267 if rtype != null then
268 res = icb.new_register(rtype)
269 end
270 var iroutine = new IClosureDef(args, res)
271 var clos: nullable Array[IClosureDecl] = null
272 if not closures.is_empty then
273 clos = new Array[IClosureDecl]
274 for c in closures do
275 clos.add(new IClosureDecl(c))
276 end
277 iroutine.closure_decls = clos
278 end
279 # TODO: add automatic test for virtual types?
280 return iroutine
281 end
282 end
283