1 # This file contains some of the code that drives the .NET EXE/DLL to PIR
6 # This sub is the way translation from .NET to PIR is started.
12 .local string pir_output, src, summary, tmp, emsg
13 .local pmc assembly, classes, class_order, type, e, entry_meth, entry_class
14 .local int is_dll, i, max_class, class_id, total_types, done_types
16 # Instantiate a new assembly class.
18 assembly = new "DotNetAssembly"
20 # Set filename and attempt to load.
24 # Load the escaper library, which we will be using.
25 load_bytecode "library/Data/Escape.pir"
27 # Initialize PIR output string.
30 # Output HLL directive.
31 pir_output = concat ".HLL 'dotnet', ''\n"
33 # Put in ops loader code.
34 pir_output = concat <<"PIR"
36 .sub __LOAD_DOTNET_OPS :load
37 loadlib $P0, "dotnet_runtime"
41 # If standalone flag is set, jump over adding import code.
42 if standalone == 1 goto NO_IMPORTS
43 src = assembly_imports_sub(assembly)
44 pir_output = concat src
47 # Insert code to make call-boxing classes used to make MMD work on the
48 # non-reference types Parrot doesn't recognize.
49 pir_output = concat <<"PIR"
50 .sub __CREATE_DOTNET_MMDBOXES :load
53 $P0 = get_class "Integer"
54 class = get_class "@@DOTNET_MMDBOX_I1"
55 unless null class goto EXISTS_I1
56 subclass $P1, $P0, "@@DOTNET_MMDBOX_I1"
58 class = get_class "@@DOTNET_MMDBOX_I2"
59 unless null class goto EXISTS_I2
60 subclass $P1, $P0, "@@DOTNET_MMDBOX_I2"
62 class = get_class "@@DOTNET_MMDBOX_U1"
63 unless null class goto EXISTS_U1
64 subclass $P1, $P0, "@@DOTNET_MMDBOX_U1"
66 class = get_class "@@DOTNET_MMDBOX_U2"
67 unless null class goto EXISTS_U2
68 subclass $P1, $P0, "@@DOTNET_MMDBOX_U2"
70 class = get_class "@@DOTNET_MMDBOX_U4"
71 unless null class goto EXISTS_U4
72 subclass $P1, $P0, "@@DOTNET_MMDBOX_U4"
74 $P0 = get_class "Float"
75 class = get_class "@@DOTNET_MMDBOX_R4"
76 unless null class goto EXISTS_R4
77 subclass $P1, $P0, "@@DOTNET_MMDBOX_R4"
82 # We'll only put fake System.Object in when we're in standalone mode.
83 if standalone == 0 goto NO_STANDALONE_CLASSES
84 pir_output = concat <<"PIR"
85 .namespace [ "System"; "Object" ]
86 .sub __FAKE_SYSTEM_OBJECT :load
87 $P0 = get_class [ "System" ; "Object" ]
88 unless null $P0 goto EXISTS
89 $P0 = newclass [ "System" ; "Object" ]
95 NO_STANDALONE_CLASSES:
97 # Put in fake temporary System.Exception and System.String.
98 pir_output = concat <<"PIR"
99 .namespace [ "System" ; "Exception" ]
100 .sub __FAKE_SYSTEM_EXCEPTION :load
101 $P0 = get_class [ "System" ; "Exception" ]
102 unless null $P0 goto EXISTS
103 $P0 = newclass [ "System" ; "Exception" ]
108 .namespace [ "System" ; "String" ]
109 .sub __FAKE_SYSTEM_STRING :load
110 $P0 = get_class [ "System" ; "String" ]
111 unless null $P0 goto EXISTS
112 $P0 = newclass [ "System" ; "String" ]
113 addattribute $P0, "Chars"
116 .sub ".ctor" :method :multi("System.String", string)
120 setattribute self, "Chars", $P0
122 .sub __get_string :method
123 $P0 = getattribute self, "Chars"
127 .namespace [ "System" ; "ValueType" ]
128 .sub __FAKE_SYSTEM_VALUETYPE :load
129 $P0 = get_class [ "System" ; "ValueType" ]
130 unless null $P0 goto EXISTS
131 $P0 = newclass [ "System" ; "ValueType" ]
134 .namespace [ "System" ; "Enum" ]
135 .sub __FAKE_SYSTEM_ENUM :load
136 $P0 = get_class [ "System" ; "Enum" ]
137 unless null $P0 goto EXISTS
138 $P0 = newclass [ "System" ; "Enum" ]
143 # Translate global stuff.
144 # XXX TODO: Translate globals.
146 # Translate each class according to the ordering.
147 classes = assembly.get_classes()
148 class_order = assembly.get_class_order()
149 max_class = elements classes
154 if i >= max_class goto CEND
156 class_id = class_order[i]
157 type = classes[class_id]
158 if continue == 0 goto NO_EH
159 push_eh trans_failure_handler
161 src = trans_class(assembly, type, trace)
162 pir_output = concat src
164 if continue == 0 goto RESUME
169 trans_failure_handler:
170 .get_results (e, emsg)
171 # Emit trace message.
172 unless trace goto NOTRACE
173 printerr " **FAILED** ("
181 # If it's an EXE, do entry point stuff.
182 is_dll = assembly."is_dll"()
183 if is_dll > 0 goto ISEXE
185 entry_meth = assembly.get_entry_method()
186 entry_class = entry_meth.get_class()
187 pir_output = ".sub __ENTRY_POINT\n__DO_IMPORTS()\n$P0 = get_hll_global \""
188 tmp = entry_class.get_fullname()
189 pir_output = concat tmp
190 pir_output = concat "\", \""
192 pir_output = concat tmp
193 pir_output = concat "\"\n$P0()\n.end\n"
194 pir_output = concat src
198 summary = "Translated "
201 summary = concat " types out of "
204 summary = concat " from "
205 summary = concat filename
206 summary = concat "\n"
209 .return (pir_output, summary)
213 # This produces a sub that loads libraries that we need to import for this one
215 .sub assembly_imports_sub
217 .local pmc assrefs, assref
218 .local int assref_count, i
219 .local string name, pir_output
221 # Emit start of load sub.
222 pir_output = ".sub __DO_IMPORTS :load\n"
224 # Loop over assembly refs.
225 assrefs = assembly.get_assemblyrefs()
226 assref_count = elements assrefs
229 if i == assref_count goto AR_LOOP_END
233 # Get name and emit load code.
235 pir_output = concat "load_bytecode \""
236 pir_output = concat name
237 pir_output = concat ".pbc\"\n"
241 pir_output = concat ".end\n"
246 # This sub translates an individual class.
251 .local string pir_output, name, namespace, internal_name, tmp, p_name, name_key
252 .local pmc fields, field, methods, meth, ex, int_types, int_ids
253 .local int i, max_field, max_method, parent_id, parent_type
254 .local int flags, is_interface, is_abstract, num_interfaces, done_init
258 # Get class name and namespace and build combo of them.
260 namespace = class.get_namespace()
261 internal_name = class.get_fullname()
263 # Emit trace message.
264 unless trace goto NOTRACE
266 printerr internal_name
270 # Emit a namespace directive.
271 name_key = namespace_to_key(internal_name)
272 pir_output = concat ".namespace "
273 pir_output = concat name_key
274 pir_output = concat "\n\n"
276 # Emit start of on load type setup.
277 pir_output = concat ".sub \"__onload\" :load\n"
278 pir_output = concat " .local pmc type, parent\n"
279 pir_output = concat " push_eh FAILED\n" # XXX Ignoring missing parents
280 pir_output = concat " type = newclass "
281 pir_output = concat name_key
282 pir_output = concat "\n"
284 # Add any interfaces that this class implements.
285 int_types = class.get_interface_types()
286 int_ids = class.get_interface_ids()
287 num_interfaces = elements int_types
290 if i == num_interfaces goto END_INT_LOOP
291 parent_type = int_types[i]
292 parent_id = int_ids[i]
294 (tmp, p_name) = add_parent(assembly, parent_type, parent_id)
295 pir_output = concat tmp
299 # Inherit the parent class. Note System.Object has ID 0, and jump over this stuff.
300 parent_id = class.get_parent_id()
301 if parent_id == 0 goto NO_PARENT
303 parent_type = class.get_parent_type()
304 (tmp, p_name) = add_parent(assembly, parent_type, parent_id)
305 pir_output = concat tmp
309 fields = class.get_fields()
310 max_field = elements fields
313 if i >= max_field goto FEND
315 tmp = trans_field(assembly, class, field)
316 pir_output = concat tmp
321 # Add code to run constructor.
322 pir_output = concat "push_eh FAILED\n"
323 pir_output = concat "$P0 = get_hll_global "
324 tmp = namespace_to_key(internal_name)
325 pir_output = concat tmp
326 pir_output = concat ", \".cctor\"\n$P0()\n"
328 # This is the end of the on load type setup sub.
329 pir_output = concat "FAILED:\n.end\n\n"
331 # If it's an interface, emit code to prevent it being instantiated.
332 flags = class.get_flags()
333 is_interface = band flags, 0x20
334 if is_interface == 0 goto NOT_INTERFACE
335 pir_output = concat <<"PIR"
340 pir_output = concat " if $S0 != \""
341 pir_output = concat internal_name
342 pir_output = concat "\" goto INIT_OK\n"
343 pir_output = concat <<"PIR"
344 $P1 = new 'Exception'
345 $P1 = "You can not instantiate an interface"
354 # If it's an abstract class, emit code to prevent it being instantiated.
355 is_abstract = band flags, 0x80
356 if is_abstract == 0 goto NOT_ABSTRACT
357 pir_output = concat <<"PIR"
362 pir_output = concat " if $S0 != \""
363 pir_output = concat internal_name
364 pir_output = concat "\" goto INIT_OK\n"
365 pir_output = concat <<"PIR"
366 $P1 = new 'Exception'
367 $P1 = "You can not instantiate an abstract class"
375 # If it is a value type, add the __init and __clone v-table methods.
376 if p_name == "[ \"System\" ; \"ValueType\" ]" goto VAL_TYPE
377 if p_name == "[ \"System\" ; \"Enum\" ]" goto VAL_TYPE
380 tmp = value_type_methods(assembly, class, p_name)
381 pir_output = concat tmp
386 methods = class.get_methods()
387 max_method = elements methods
390 if i >= max_method goto MEND
392 tmp = trans_method(assembly, class, meth, 1, trace)
393 pir_output = concat tmp
398 # Return PIR that was generated.
403 # This emits the code to add a parent to a class.
406 .param int parent_type
408 .local pmc ex, classes, pclass
409 .local string pclass_ns, pir_output, tmp
411 # Find out what type of parent we have.
412 pir_output = " parent = get_class "
413 if parent_type == 0 goto PARENT_DEF
414 if parent_type == 1 goto PARENT_REF
416 ex = "Can not subclass a TypeSpec parent."
419 # Parent may be a type in this file.
421 dec parent_id # Because row 2 = element 0 here, thanks to the global class
422 classes = assembly.get_classes()
423 pclass = classes[parent_id]
424 pclass_ns = pclass.get_fullname()
425 pclass_ns = namespace_to_key(pclass_ns)
426 pir_output = concat pclass_ns
429 # Parent may be a type in another file.
431 classes = assembly.get_typerefs()
432 pclass = classes[parent_id]
433 pclass_ns = pclass.get_namespace()
434 pclass_ns = clone pclass_ns
435 if pclass_ns == "" goto PARENT_NO_NS
436 pclass_ns = concat "."
439 pclass_ns = concat tmp
440 pclass_ns = namespace_to_key(pclass_ns)
441 pir_output = concat pclass_ns
443 # Finally, do code to add parent to the class and return.
445 pir_output = concat "\n addparent type, parent\n"
446 .return (pir_output, pclass_ns)
450 # This translates a field into an addattribute op.
455 .local int flags, static
456 .local string pir_output, name
458 # Check it's an instance field.
459 flags = field.get_flags()
460 static = band flags, 0x10
461 if static != 0 goto STATIC
463 # Generate add attribute instruction provided it's an instance field.
465 pir_output = " addattribute type, \""
466 pir_output = concat name
467 pir_output = concat "\"\n"
469 # Return generated string.
475 # Generate the special __init and __clone v-table methods for value types.
476 .sub value_type_methods
480 .local pmc fields, field, sig, sig_info
481 .local int i, sig_id, type, flags, static
482 .local string pir_output, init_body, clone_body, name, sig_data
483 .const int ELEMENT_TYPE_I1 = 0x04
484 .const int ELEMENT_TYPE_U1 = 0x05
485 .const int ELEMENT_TYPE_I2 = 0x06
486 .const int ELEMENT_TYPE_U2 = 0x07
487 .const int ELEMENT_TYPE_I4 = 0x08
488 .const int ELEMENT_TYPE_U4 = 0x09
489 .const int ELEMENT_TYPE_R4 = 0x0C
490 .const int ELEMENT_TYPE_R8 = 0x0D
491 .const int ELEMENT_TYPE_I = 0x18
492 .const int ELEMENT_TYPE_U = 0x19
493 .const int ELEMENT_TYPE_VALUETYPE = 0x11
495 # The __init method needs to zero or null out any attributes.
496 # The __clone method needs to clone each attribute.
497 fields = class.get_fields()
502 if i == 0 goto ILOOP_END
506 # Skip if field is static.
507 flags = field.get_flags()
508 static = band flags, 0x10
509 if static != 0 goto ILOOP
511 # For clone, emit code to just copy the attribute.
513 clone_body = concat "$P0 = getattribute self, \""
514 clone_body = concat name
515 clone_body = concat "\"\n$P0 = clone $P0\nsetattribute cpy, \""
516 clone_body = concat name
517 clone_body = concat "\", $P0\n"
519 # Need to look at signature to initialize attributes by type.
520 sig_id = field.get_signature()
521 sig_data = assembly.get_blob(sig_id)
522 sig = new "DotNetSignature"
524 sig_info = get_signature_Field(sig)
525 type = sig_info["type"]
526 if type == ELEMENT_TYPE_I4 goto INT_TYPE
527 if type == ELEMENT_TYPE_U4 goto INT_TYPE
528 if type == ELEMENT_TYPE_I2 goto INT_TYPE
529 if type == ELEMENT_TYPE_U2 goto INT_TYPE
530 if type == ELEMENT_TYPE_I1 goto INT_TYPE
531 if type == ELEMENT_TYPE_U1 goto INT_TYPE
532 if type == ELEMENT_TYPE_I goto INT_TYPE
533 if type == ELEMENT_TYPE_U goto INT_TYPE
534 if type == ELEMENT_TYPE_R4 goto FLOAT_TYPE
535 if type == ELEMENT_TYPE_R8 goto FLOAT_TYPE
536 if type == ELEMENT_TYPE_VALUETYPE goto VALUE_TYPE
540 init_body = concat "$P0 = new 'Integer'\n$P0 = 0\nsetattribute self, \""
541 init_body = concat name
542 init_body = concat "\", $P0\n"
546 init_body = concat "$P0 = new .Float\n$P0 = 0.0\nsetattribute self, \""
547 init_body = concat name
548 init_body = concat "\", $P0\n"
552 init_body = concat "$P0 = null\nsetattribute self, \""
553 init_body = concat name
554 init_body = concat "\", $P0\n"
558 $P0 = new 'Exception'
559 $P0 = "Not doing nested value types yet!"
569 pir_output = ".sub __init :method\n"
570 pir_output = concat init_body
571 pir_output = concat <<"PIR"
579 pir_output = concat clone_body
580 pir_output = concat ".return(cpy)\n.end\n"
582 # If we have an enum, provide get and set integer and float v-table
583 # methods to provide or hand back first field. This is for supporting
585 if parent != "[ \"System\" ; \"Enum\" ]" goto NOT_ENUM
586 pir_output = concat <<"PIR"
589 $P0 = getattribute s, "value__"
593 .sub __set_integer_native
598 setattribute s, "value__", $P0
602 $P0 = getattribute s, "value__"
606 .sub __set_number_native
611 setattribute s, "value__", $P0
616 # Return generated code.
621 # Takes a .Net namespace separated by dots and makes a Parrot namespace key.
622 .sub namespace_to_key
624 .local string ns_key, tmp
628 # Initial bracket of key.
631 # Split up and make key sequence.
632 keys = split ".", in_ns
636 if i == max goto LOOP_END
643 ns_key = concat "\" "
648 # End and return key.
657 # vim: expandtab shiftwidth=4 ft=pir: