tagged release 0.6.4
[parrot.git] / languages / dotnet / src / method.pir
blobb360e0e9c34e90517dab8e322735d8f386e020cd
1 # This file contains code relating to method translation.
3 .HLL '_dotnet', ''
5 # This is the starting point for translating a method.
6 .sub trans_method
7     .param pmc assembly
8     .param pmc class
9     .param pmc meth
10     .param int real_method
11     .param int trace
12     .local pmc rettype, ptypes, ltypes, ex
13     .local string pir_output, name, pir_params, pir_multi, pir_locals, pir_body, ns
14     .local int flags, impl_flags, static_check, abstract_check, runtime_check
16     # Check if method is static, abstract and/or runtime provided.
17     flags = meth.get_flags()
18     impl_flags = meth.get_impl_flags()
19     static_check = band flags, 0x10
20     abstract_check = band flags, 0x0400
21     runtime_check = band impl_flags, 0x1002 # Internal or runtime
23     # Get details of parameter info and multi-method dispatch markup.
24     ns = class.get_fullname()
25     (rettype, ptypes, pir_params, pir_multi) = trans_method_params(assembly, meth, ns)
27     # Emit top of method.
28     pir_output = ".sub \""
29     name = meth
30     pir_output = concat name
31     pir_output = concat "\""
32     if real_method == 0 goto ISMETHOD
33     if static_check == 0 goto ISMETHOD
34     goto ENDTOPSTUB
35 ISMETHOD:
36     pir_output = concat " :method"
37 ENDTOPSTUB:
38     pir_output = concat " :multi("
39     pir_output = concat pir_multi
40     pir_output = concat ")\n"
42     # Emit trace message.
43     unless trace goto NOTRACE
44     printerr "  Method "
45     printerr name
46     printerr "\n"
47 NOTRACE:
49     # If it is runtime provided, try to get body and add it, then go to end.
50     if runtime_check == 0 goto NOT_RUNTIME
51     pir_body = internal_method_body(class, meth)
52     pir_output = concat pir_body
53     goto METHOD_END
54 NOT_RUNTIME:
56     # Add in parameters.
57     pir_output = concat pir_params
59     # If it's an abstract method, need to throw an exception if it's called
60     # then not bother with locals, the instruction translator etc.
61     if abstract_check == 0 goto NOT_ABSTRACT
62     pir_output = concat <<"PIR"
63 $P0 = new .Exception
64 $P0["_message"] = "Ooh, naughty! A non-abstract class didn't implement an abstract method!"
65 throw $P0
66 PIR
67     goto METHOD_END
68 NOT_ABSTRACT:
70     # Locals.
71     (ltypes, pir_locals) = trans_method_locals(assembly, meth)
72     pir_output = concat pir_locals
74     # Need to set arg0 = self if it's an instance method.
75     if static_check != 0 goto NOSELF
76     pir_output = concat ".local pmc arg0\narg0 = self\n"
77 NOSELF:
79     # Body.
80     pir_body = trans_instructions(assembly, class, meth, ptypes, ltypes, rettype, trace)
81     pir_output = concat pir_body
83     # Emit end of method.
84 METHOD_END:
85     pir_output = concat ".end\n\n"
87     # Return generated string.
88     .return (pir_output)
89 .end
92 # This translates the method parameters signature.
93 .sub trans_method_params
94     .param pmc assembly
95     .param pmc meth
96     .param string self_ns
97     .param int no_pir :optional
98     .local int sig_pos, sig_type
99     .local string sig_data
100     .local pmc signature, classes, class, ex
101     .local int flags, param_count, i, static_check, this_offset, type, token
102     .local int class_type, class_id, explicit_check, vararg_check
103     .local string pir_output, pir_multi, pir_mmdunbox, param_reg_type, tmp
104     .local pmc rettype, ptypes, ptype
106     # Get signature.
107     sig_pos = meth.get_signature()
108     sig_data = assembly.get_blob(sig_pos)
109     signature = new "DotNetSignature"
110     signature = sig_data
112     # Get flags.
113     flags = signature.read_uint8()
115     # Check if it's a static method, has this specified explicitly or is
116     # var arg.
117     static_check = band flags, 0x20
118     explicit_check = band flags, 0x40
119     vararg_check = band flags, 0x7
121     # Get number of parameters.
122     param_count = signature.read_compressed()
124     # Return type.
125     rettype = get_signature_RetType_or_Param(signature)
126     annotate_reg_type(rettype)
128     # Create parameter types array.
129     ptypes = new .ResizablePMCArray
131     # If it's an instance method and explicit not set, add "this" to the list.
132     this_offset = 0
133     pir_multi = ""
134     if static_check == 0 goto NOSELF
135     if explicit_check != 0 goto NOSELF
136     ptype = new Hash
137     ptype["type"] = 0x1C
138     ptype["byref"] = 0
139     annotate_reg_type(ptype)
140     push ptypes, ptype
141     this_offset = 1
143     # Also generate MMD entry.
144     self_ns = namespace_to_key(self_ns)
145     pir_multi = concat self_ns
146 NOSELF:
148     # Loop over parameters and produce list.
149     i = 0
150     pir_output = ""
151     pir_mmdunbox = ""
152 PARAM:
153     if i >= param_count goto ENDPARAM
155     # Emit first bit of PIR.
156     pir_output = concat "    .param "
158     # Parameter type.
159     ptype = get_signature_RetType_or_Param(signature)
160     annotate_reg_type(ptype)
161     push ptypes, ptype
162     param_reg_type = ptype["reg_type_long"]
163     pir_output = concat param_reg_type
165     # Emit argument name.
166     pir_output = concat " arg"
167     $I0 = i + this_offset
168     $S0 = $I0
169     pir_output = concat $S0
170     pir_output = concat "\n"
172     # Emit MMD info.
173     if pir_multi == "" goto NOCOMMA
174     pir_multi = concat ", "
175 NOCOMMA:
176     type = ptype["type"]
177     if type == 0x12 goto MMD_CLASS
178     if type == 0x4 goto MMD_I1
179     if type == 0x5 goto MMD_U1
180     if type == 0x6 goto MMD_I2
181     if type == 0x7 goto MMD_U2
182     if type == 0x9 goto MMD_U4
183     if type == 0xC goto MMD_R4
184     pir_multi = concat param_reg_type
185     goto MMD_DONE
186 MMD_CLASS:
187     token = ptype["token"]
188     class_type = band token, 0x3
189     class_id = token >> 2
190     dec class_id
191     if class_type == 0 goto MMD_CLASS_DEF
192     if class_type == 1 goto MMD_CLASS_REF
193     ex = new .Exception
194     ex["_message"] = "Unknown class type to build MMD signature for."
195     throw ex
196 MMD_CLASS_DEF:
197     classes = assembly.get_classes()
198     dec class_id
199     class = classes[class_id]
200     tmp = class.get_fullname()
201     tmp = namespace_to_key(tmp)
202     pir_multi = concat tmp
203     goto MMD_DONE
204 MMD_CLASS_REF:
205     classes = assembly.get_typerefs()
206     class = classes[class_id]
207     tmp = class
208     tmp = namespace_to_key(tmp)
209     pir_multi = concat tmp
210     goto MMD_DONE
211 MMD_I1:
212     pir_multi = concat "\"@@DOTNET_MMDBOX_I1\""
213     goto MMD_DONE
214 MMD_U1:
215     pir_multi = concat "\"@@DOTNET_MMDBOX_U1\""
216     goto MMD_DONE
217 MMD_I2:
218     pir_multi = concat "\"@@DOTNET_MMDBOX_I2\""
219     goto MMD_DONE
220 MMD_U2:
221     pir_multi = concat "\"@@DOTNET_MMDBOX_U2\""
222     goto MMD_DONE
223 MMD_U4:
224     pir_multi = concat "\"@@DOTNET_MMDBOX_U4\""
225     goto MMD_DONE
226 MMD_R4:
227     pir_multi = concat "\"@@DOTNET_MMDBOX_R4\""
228     goto MMD_DONE
229 MMD_DONE:
231     # Loop.
232     inc i
233     goto PARAM
234 ENDPARAM:
236     # If it's var arg, add slurply parameter too.
237     if vararg_check != 5 goto NOT_VARARG
238     pir_output = concat ".param pmc varargs :slurpy\n"
239 NOT_VARARG:
241     # Return stuff.
242     pir_output = concat pir_mmdunbox
243     .return(rettype, ptypes, pir_output, pir_multi)
244 .end
247 # This translates the method's locals signature.
248 .sub trans_method_locals
249     .param pmc assembly
250     .param pmc meth
251     .local int sig_pos, sig_type
252     .local string sig_data
253     .local pmc bc, signature, ex
254     .local int sig_type, count, i, type
255     .local string pir_output :unique_reg
256     .local string param_reg_type
257     .local pmc ltypes, ltype
259     pir_output = ""
260     ltypes = new .ResizablePMCArray
262     # Get signature.
263     bc = meth.get_bytecode()
264     sig_pos = bc.get_locals_sig()
265     if sig_pos == 0 goto EXIT
266     sig_data = assembly.get_blob(sig_pos)
267     signature = new "DotNetSignature"
268     signature = sig_data
270     # Ensure it's a local sig. XXX Right thing to do when it's not?
271     sig_type = signature.read_uint8()
272     if sig_type == 0x7 goto SIG_OK
273     ex = new .Exception
274     ex["_message"] = "Locals signature is not a locals signature."
275     throw ex
276 SIG_OK:
278     # Read locals count.
279     count = signature.read_compressed()
281     # Loop over local variabless.
282     i = 0
283 LOCAL:
284     if i == count goto EXIT
286     # Emit first bit of PIR.
287     pir_output = concat "    .local "
289     # Parameter type.
290     ltype = get_signature_Local(signature)
291     annotate_reg_type(ltype)
292     push ltypes, ltype
293     param_reg_type = ltype["reg_type_long"]
294     pir_output = concat param_reg_type
296     # Emit argument name.
297     pir_output = concat " local"
298     $S0 = i
299     pir_output = concat $S0
300     pir_output = concat "\n"
302     # If it's a value type, need to instantiate it.
303     type = ltype["type"]
304     if type != 0x11 goto NOT_VT
305     type = ltype["token"]
306     pir_output = concat "local"
307     pir_output = concat $S0
308     pir_output = concat " = new "
309     ($P0, $S0) = class_info_from_sig(assembly, type)
310     $S0 = namespace_to_key($S0)
311     pir_output = concat $S0
312     pir_output = concat "\n"
313 NOT_VT:
315     # Loop.
316     inc i
317     goto LOCAL
319     # Return.
320 EXIT:
321     .return (ltypes, pir_output)
322 .end
325 # This gets method info from a method token.
326 .sub method_info_from_token
327     .param pmc assembly
328     .param int token
329     .local pmc class, meth, memberrefs, memberref, rettype, ptypes
330     .local pmc typerefs, ex
331     .local int test, meth_id, class_type, class_id
332     .local string ns, tmp
334     # Check whether it's internal or external.
335     test = band token, 0x8000000
336     if test goto EXTERNAL
338     # If we're here, it's an internal method. Get method PMC, namespace, parse
339     # its sig etc.
340     meth_id = band token, 0x1FFFFFF
341     meth = assembly.get_method(meth_id)
342     class = meth.get_class()
343     ns = class.get_fullname()
344     (rettype, ptypes, tmp) = trans_method_params(assembly, meth, ns)
345     goto DONE
347     # If we get here, it's an external method. Get member semantics PMC.
348 EXTERNAL:
349     meth_id = band token, 0x1FFFFFF
350     dec meth_id
351     memberrefs = assembly.get_memberrefs()
352     memberref = memberrefs[meth_id]
353     class_type = memberref.get_class_type()
354     class_id = memberref.get_class_id()
355     dec class_id
356     if class_type == 1 goto TYPEREF
357     if class_type == 3 goto METHODDEF
358     ex = new .Exception
359     ex["message"] = "Unsupported member reference type for method"
360     throw ex
362     # If we have a typeref, get it.
363 TYPEREF:
364     typerefs = assembly.get_typerefs()
365     class = typerefs[class_id]
366     tmp = class.get_namespace()
367     ns = clone tmp
368     if ns == "" goto NO_DOT
369     ns = concat "."
370 NO_DOT:
371     tmp = class
372     ns = concat tmp
373     (rettype, ptypes, tmp) = trans_method_params(assembly, memberref, ns)
374     meth = memberref
375     goto DONE
377     # If we have a methoddef, grab it.
378 METHODDEF:
379     meth = assembly.get_method(class_id)
380     class = meth.get_class()
381     ns = class.get_fullname()
382     (rettype, ptypes, tmp) = trans_method_params(assembly, meth, ns)
383     goto DONE
385     # Return stuff.
386 DONE:
387     .return(meth, ns, rettype, ptypes, class)
388 .end
391 # This generates code to box up primitive types for MMD dispatch.
392 .sub method_call_mmd_box
393     .param pmc assembly
394     .param pmc ptypes
395     .param pmc params
396     .local pmc ptype
397     .local string pir_output, box_name, tmp, reg_name
398     .local int i, num_params, type
400     # Loop over parameters.
401     pir_output = ""
402     i = 0
403     num_params = elements params
404 PLOOP:
405     if i == num_params goto PLOOP_END
407     # Get parameter type and see if it's one that needs boxing, and if so
408     # what the box is called.
409     ptype = ptypes[i]
410     type = ptype["type"]
411     if type == 0x4 goto MMD_I1
412     if type == 0x5 goto MMD_U1
413     if type == 0x6 goto MMD_I2
414     if type == 0x7 goto MMD_U2
415     if type == 0x9 goto MMD_U4
416     if type == 0xC goto MMD_R4
417     goto NO_MMD
418 MMD_I1:
419     box_name = "@@DOTNET_MMDBOX_I1"
420     goto DO_MMD
421 MMD_U1:
422     box_name = "@@DOTNET_MMDBOX_U1"
423     goto DO_MMD
424 MMD_I2:
425     box_name = "@@DOTNET_MMDBOX_I2"
426     goto DO_MMD
427 MMD_U2:
428     box_name = "@@DOTNET_MMDBOX_U2"
429     goto DO_MMD
430 MMD_U4:
431     box_name = "@@DOTNET_MMDBOX_U4"
432     goto DO_MMD
433 MMD_R4:
434     box_name = "@@DOTNET_MMDBOX_R4"
435     goto DO_MMD
437     # Now generate box instruction and rename parameter.
438 DO_MMD:
439     reg_name = "$P200000"
440     tmp = i
441     reg_name = concat tmp
442     pir_output = concat reg_name
443     pir_output = concat " = new \""
444     pir_output = concat box_name
445     pir_output = concat "\"\n"
446     pir_output = concat reg_name
447     pir_output = concat " = "
448     tmp = params[i]
449     pir_output = concat tmp
450     pir_output = concat "\n"
451     params[i] = reg_name
453     # Go to next parameter.
454 NO_MMD:
455     inc i
456     goto PLOOP
457 PLOOP_END:
459     # Return generated PIR.
460     .return(pir_output)
461 .end
464 # This gets info from a type def or ref in a signature.
465 .sub class_info_from_sig
466     .param pmc assembly
467     .param int token
468     .local pmc ex, classes, pclass
469     .local string pclass_ns, tmp
470     .local int class_type, class_id
472     # Get class type and class id.
473     class_id = token >> 2
474     class_type = band token, 3
476     # Find out what type of class we have.
477     dec class_id
478     if class_type == 0 goto DEF
479     if class_type == 1 goto REF
480     ex = new .Exception
481     ex["_message"] = "Unknown class type."
482     throw ex
484     # A type in this file.
485 DEF:
486     dec class_id # Because row 2 = element 0 here, thanks to the global class
487     classes = assembly.get_classes()
488     pclass = classes[class_id]
489     pclass_ns = pclass.get_fullname()
490     goto DONE
492     # A type in another file.
493 REF:
494     classes = assembly.get_typerefs()
495     pclass = classes[class_id]
496     pclass_ns = pclass.get_namespace()
497     pclass_ns = clone pclass_ns
498     if pclass_ns == "" goto NO_NS
499     pclass_ns = concat "."
500 NO_NS:
501     tmp = pclass
502     pclass_ns = concat tmp
504     # Return class and its namespace.
505 DONE:
506     .return (pclass, pclass_ns)
507 .end
509 # Local Variables:
510 #   mode: pir
511 #   fill-column: 100
512 # End:
513 # vim: expandtab shiftwidth=4 ft=pir: