1 #!/usr/bin/env python 2 3 """ 4 Generate C code from object layouts and other deduced information. 5 6 Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from common import CommonOutput 23 from encoders import encode_bound_reference, encode_function_pointer, \ 24 encode_instantiator_pointer, encode_path, encode_symbol 25 from os import listdir 26 from os.path import isdir, join, split 27 from referencing import Reference 28 29 def copy(source, target): 30 31 "Copy a text file from 'source' to 'target'." 32 33 if isdir(target): 34 target = join(target, split(source)[-1]) 35 infile = open(source) 36 outfile = open(target, "w") 37 try: 38 outfile.write(infile.read()) 39 finally: 40 outfile.close() 41 infile.close() 42 43 class Generator(CommonOutput): 44 45 "A code generator." 46 47 function_type = "__builtins__.core.function" 48 49 table_name_prefixes = { 50 "<class>" : "Class", 51 "<module>" : "Module", 52 "<instance>" : "Instance" 53 } 54 55 structure_size_prefixes = { 56 "<class>" : "c", 57 "<module>" : "m", 58 "<instance>" : "i" 59 } 60 61 def __init__(self, importer, optimiser, output): 62 self.importer = importer 63 self.optimiser = optimiser 64 self.output = output 65 66 def to_output(self): 67 68 "Write the generated code." 69 70 self.check_output() 71 self.write_structures() 72 self.copy_templates() 73 74 def copy_templates(self): 75 76 "Copy template files to the generated output directory." 77 78 templates = join(split(__file__)[0], "templates") 79 80 for filename in listdir(templates): 81 copy(join(templates, filename), self.output) 82 83 def write_structures(self): 84 85 "Write structures used by the program." 86 87 f_consts = open(join(self.output, "progconsts.h"), "w") 88 f_defs = open(join(self.output, "progtypes.c"), "w") 89 f_decls = open(join(self.output, "progtypes.h"), "w") 90 f_signatures = open(join(self.output, "main.h"), "w") 91 f_code = open(join(self.output, "main.c"), "w") 92 93 try: 94 # Output boilerplate. 95 96 print >>f_consts, """\ 97 #ifndef __PROGCONSTS_H__ 98 #define __PROGCONSTS_H__ 99 """ 100 print >>f_decls, """\ 101 #ifndef __PROGTYPES_H__ 102 #define __PROGTYPES_H__ 103 104 #include "progconsts.h" 105 #include "types.h" 106 """ 107 print >>f_defs, """\ 108 #include "progtypes.h" 109 #include "main.h" 110 """ 111 print >>f_signatures, """\ 112 #ifndef __MAIN_H__ 113 #define __MAIN_H__ 114 115 #include "types.h" 116 """ 117 print >>f_code, """\ 118 #include <string.h> 119 #include "types.h" 120 #include "ops.h" 121 #include "progconsts.h" 122 #include "progtypes.h" 123 #include "progops.h" 124 #include "main.h" 125 """ 126 127 # Generate structure size data. 128 129 size_tables = {} 130 131 for kind in ["<class>", "<module>", "<instance>"]: 132 size_tables[kind] = {} 133 134 for ref, structure in self.optimiser.structures.items(): 135 size_tables[ref.get_kind()][ref.get_origin()] = len(structure) 136 137 size_tables = size_tables.items() 138 size_tables.sort() 139 140 for kind, sizes in size_tables: 141 self.write_size_constants(f_consts, self.structure_size_prefixes[kind], sizes, 0) 142 143 # Generate parameter table size data. 144 145 min_sizes = {} 146 max_sizes = {} 147 148 for path, parameters in self.optimiser.parameters.items(): 149 argmin, argmax = self.get_argument_limits(path) 150 min_sizes[path] = argmin 151 max_sizes[path] = argmax 152 153 # Record instantiator limits. 154 155 if path.endswith(".__init__"): 156 path = path[:-len(".__init__")] 157 min_sizes[path] = argmin - 1 158 max_sizes[path] = argmax - 1 159 160 self.write_size_constants(f_consts, "pmin", min_sizes, 0) 161 self.write_size_constants(f_consts, "pmax", max_sizes, 0) 162 163 # Generate attribute codes. 164 165 self.write_code_constants(f_consts, self.optimiser.all_attrnames, self.optimiser.locations) 166 167 # Generate table and structure data. 168 169 function_instance_attrs = None 170 objects = self.optimiser.attr_table.items() 171 objects.sort() 172 173 for ref, indexes in objects: 174 attrnames = self.get_attribute_names(indexes) 175 176 kind = ref.get_kind() 177 path = ref.get_origin() 178 table_name = encode_tablename(self.table_name_prefixes[kind], path) 179 structure_size = encode_size(self.structure_size_prefixes[kind], path) 180 181 # Generate structures for classes and modules. 182 183 if kind != "<instance>": 184 structure = [] 185 attrs = self.get_static_attributes(kind, path, attrnames) 186 187 # Set a special instantiator on the class. 188 189 if kind == "<class>": 190 attrs["__fn__"] = path 191 attrs["__args__"] = encode_size("pmin", path) 192 193 # Write instantiator declarations based on the 194 # applicable initialiser. 195 196 init_ref = attrs["__init__"] 197 198 # Signature: __attr __new_<name>(__attr[]); 199 200 print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) 201 202 # Write instantiator definitions. 203 204 self.write_instantiator(f_code, path, init_ref) 205 206 # Write parameter table. 207 208 self.make_parameter_table(f_decls, f_defs, path, init_ref.get_origin()) 209 210 self.populate_structure(Reference(kind, path), attrs, kind, structure) 211 self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure) 212 213 # Record function instance details for function generation below. 214 215 else: 216 attrs = self.get_instance_attributes(path, attrnames) 217 if path == self.function_type: 218 function_instance_attrs = attrs 219 220 # Write a table for all objects. 221 222 table = [] 223 self.populate_table(Reference(kind, path), table) 224 self.write_table(f_decls, f_defs, table_name, structure_size, table) 225 226 # Generate function instances. 227 228 functions = set() 229 230 for ref in self.importer.objects.values(): 231 if ref.has_kind("<function>"): 232 functions.add(ref.get_origin()) 233 234 functions = list(functions) 235 functions.sort() 236 237 for path in functions: 238 cls = self.function_type 239 table_name = encode_tablename("Instance", cls) 240 structure_size = encode_size(self.structure_size_prefixes["<instance>"], cls) 241 242 # Set a special callable attribute on the instance. 243 244 function_instance_attrs["__fn__"] = path 245 function_instance_attrs["__args__"] = encode_size("pmin", path) 246 247 # Produce two structures where a method is involved. 248 249 ref = self.importer.get_object(path) 250 parent_ref = self.importer.get_object(ref.parent()) 251 parent_kind = parent_ref and parent_ref.get_kind() 252 253 # Populate and write each structure. 254 255 if parent_kind == "<class>": 256 257 # An unbound version of a method. 258 259 structure = self.populate_function(path, function_instance_attrs, True) 260 self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure) 261 262 # A bound version of a method. 263 264 structure = self.populate_function(path, function_instance_attrs, False) 265 self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure_size, structure) 266 267 # A normal function. 268 269 structure = self.populate_function(path, function_instance_attrs, False) 270 self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure) 271 272 # Write function declarations. 273 # Signature: __attr <name>(__attr[]); 274 275 print >>f_signatures, "__attr %s(__attr args[]);" % encode_function_pointer(path) 276 277 # Write parameter table. 278 279 self.make_parameter_table(f_decls, f_defs, path, path) 280 281 # Output more boilerplate. 282 283 print >>f_consts, """\ 284 285 #endif /* __PROGCONSTS_H__ */""" 286 287 print >>f_decls, """\ 288 289 #define __FUNCTION_TYPE %s 290 #define __FUNCTION_INSTANCE_SIZE %s 291 292 #endif /* __PROGTYPES_H__ */""" % ( 293 encode_path(self.function_type), 294 encode_size(self.structure_size_prefixes["<instance>"], self.function_type) 295 ) 296 297 print >>f_signatures, """\ 298 299 #endif /* __MAIN_H__ */""" 300 301 finally: 302 f_consts.close() 303 f_defs.close() 304 f_decls.close() 305 f_signatures.close() 306 f_code.close() 307 308 def make_parameter_table(self, f_decls, f_defs, path, function_path): 309 310 """ 311 Write parameter table details to 'f_decls' (to declare a table) and to 312 'f_defs' (to define the contents) for the function with the given 313 'path', using 'function_path' to obtain the parameter details. The 314 latter two arguments may differ when describing an instantiator using 315 the details of an initialiser. 316 """ 317 318 table = [] 319 table_name = encode_tablename("Function", path) 320 structure_size = encode_size("pmax", path) 321 self.populate_parameter_table(function_path, table) 322 self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table) 323 324 def write_size_constants(self, f_consts, size_prefix, sizes, padding): 325 326 """ 327 Write size constants to 'f_consts' for the given 'size_prefix', using 328 the 'sizes' dictionary to populate the definition, adding the given 329 'padding' to the basic sizes. 330 """ 331 332 print >>f_consts, "enum %s {" % encode_size(size_prefix) 333 first = True 334 for path, size in sizes.items(): 335 if not first: 336 print >>f_consts, "," 337 else: 338 first = False 339 f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) 340 print >>f_consts, "\n };" 341 342 def write_code_constants(self, f_consts, attrnames, locations): 343 344 """ 345 Write code constants to 'f_consts' for the given 'attrnames' and 346 attribute 'locations'. 347 """ 348 349 print >>f_consts, "enum %s {" % encode_symbol("code") 350 first = True 351 for i, attrname in enumerate(attrnames): 352 if not first: 353 print >>f_consts, "," 354 else: 355 first = False 356 f_consts.write(" %s = %d" % (encode_symbol("code", attrname), i)) 357 print >>f_consts, "\n };" 358 359 print >>f_consts, "enum %s {" % encode_symbol("pos") 360 first = True 361 for i, attrnames in enumerate(locations): 362 for attrname in attrnames: 363 if not first: 364 print >>f_consts, "," 365 else: 366 first = False 367 f_consts.write(" %s = %d" % (encode_symbol("pos", attrname), i)) 368 print >>f_consts, "\n };" 369 370 def write_table(self, f_decls, f_defs, table_name, structure_size, table): 371 372 """ 373 Write the declarations to 'f_decls' and definitions to 'f_defs' for 374 the object having the given 'table_name' and the given 'structure_size', 375 with 'table' details used to populate the definition. 376 """ 377 378 print >>f_decls, "extern const __table %s;\n" % table_name 379 380 # Write the corresponding definition. 381 382 print >>f_defs, "const __table %s = {\n %s,\n {\n %s\n }\n };\n" % ( 383 table_name, structure_size, 384 ",\n ".join(table)) 385 386 def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table): 387 388 """ 389 Write the declarations to 'f_decls' and definitions to 'f_defs' for 390 the object having the given 'table_name' and the given 'structure_size', 391 with 'table' details used to populate the definition. 392 """ 393 394 print >>f_decls, "extern const __ptable %s;\n" % table_name 395 396 # Write the corresponding definition. 397 398 print >>f_defs, "const __ptable %s = {\n %s,\n {\n %s\n }\n };\n" % ( 399 table_name, structure_size, 400 ",\n ".join([("{%s, %s}" % t) for t in table])) 401 402 def write_structure(self, f_decls, f_defs, path, table_name, structure_size, structure): 403 404 """ 405 Write the declarations to 'f_decls' and definitions to 'f_defs' for 406 the object having the given 'path', the given 'table_name', and the 407 given 'structure_size', with 'structure' details used to populate the 408 definition. 409 """ 410 411 print >>f_decls, "extern __obj %s;\n" % encode_path(path) 412 413 # Write an instance-specific type definition for instances of classes. 414 # See: templates/types.h 415 416 print >>f_decls, """\ 417 typedef struct { 418 const __table * table; 419 unsigned int pos; 420 __attr attrs[%s]; 421 } %s; 422 """ % (structure_size, encode_symbol("obj", path)) 423 424 # Write the corresponding definition. 425 426 print >>f_defs, "__obj %s = {\n &%s,\n %s,\n {\n %s\n }};\n" % ( 427 encode_path(path), table_name, encode_symbol("pos", path), 428 ",\n ".join(structure)) 429 430 def get_parameters(self, ref): 431 return self.importer.function_parameters[ref.get_origin()] 432 433 def get_argument_limits(self, path): 434 parameters = self.importer.function_parameters[path] 435 defaults = self.importer.function_defaults.get(path) 436 return len(parameters) - (defaults and len(defaults) or 0), len(parameters) 437 438 def get_attribute_names(self, indexes): 439 440 """ 441 Given a list of attribute table 'indexes', return a list of attribute 442 names. 443 """ 444 445 all_attrnames = self.optimiser.all_attrnames 446 attrnames = [] 447 for i in indexes: 448 if i is None: 449 attrnames.append(None) 450 else: 451 attrnames.append(all_attrnames[i]) 452 return attrnames 453 454 def get_static_attributes(self, kind, name, attrnames): 455 456 """ 457 Return a mapping of attribute names to paths for attributes belonging 458 to objects of the given 'kind' (being "<class>" or "<module>") with 459 the given 'name' and supporting the given 'attrnames'. 460 """ 461 462 attrs = {} 463 464 for attrname in attrnames: 465 if attrname is None: 466 continue 467 if kind == "<class>": 468 path = self.importer.all_class_attrs[name][attrname] 469 elif kind == "<module>": 470 path = "%s.%s" % (name, attrname) 471 else: 472 continue 473 474 # The module may be hidden. 475 476 attr = self.importer.get_object(path) 477 if not attr: 478 module = self.importer.hidden.get(path) 479 if module: 480 attr = Reference(module.name, "<module>") 481 attrs[attrname] = attr 482 483 return attrs 484 485 def get_instance_attributes(self, name, attrnames): 486 487 """ 488 Return a mapping of attribute names to references for attributes 489 belonging to instances of the class with the given 'name', where the 490 given 'attrnames' are supported. 491 """ 492 493 consts = self.importer.all_instance_attr_constants[name] 494 attrs = {} 495 for attrname in attrnames: 496 if attrname is None: 497 continue 498 const = consts.get(attrname) 499 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 500 return attrs 501 502 def populate_table(self, key, table): 503 504 """ 505 Traverse the attributes in the determined order for the structure having 506 the given 'key', adding entries to the attribute 'table'. 507 """ 508 509 for attrname in self.optimiser.structures[key]: 510 511 # Handle gaps in the structure. 512 513 if attrname is None: 514 table.append("0") 515 else: 516 table.append(encode_symbol("code", attrname)) 517 518 def populate_parameter_table(self, key, table): 519 520 """ 521 Traverse the parameters in the determined order for the structure having 522 the given 'key', adding entries to the attribute 'table'. 523 """ 524 525 for value in self.optimiser.parameters[key]: 526 527 # Handle gaps in the structure. 528 529 if value is None: 530 table.append(("0", "0")) 531 else: 532 name, pos = value 533 table.append((encode_symbol("pcode", name), pos)) 534 535 def populate_function(self, path, function_instance_attrs, unbound=False): 536 537 """ 538 Populate a structure for the function with the given 'path'. The given 539 'attrs' provide the instance attributes, and if 'unbound' is set to a 540 true value, an unbound method structure is produced (as opposed to a 541 callable bound method structure). 542 """ 543 544 cls = self.function_type 545 structure = [] 546 self.populate_structure(Reference("<instance>", cls), function_instance_attrs, "<instance>", structure, unbound) 547 548 # Append default members. 549 550 self.append_defaults(path, structure) 551 return structure 552 553 def populate_structure(self, ref, attrs, kind, structure, unbound=False): 554 555 """ 556 Traverse the attributes in the determined order for the structure having 557 the given 'ref' whose members are provided by the 'attrs' mapping, in a 558 structure of the given 'kind', adding entries to the object 'structure'. 559 If 'unbound' is set to a true value, an unbound method function pointer 560 will be employed, with a reference to the bound method incorporated into 561 the special __fn__ attribute. 562 """ 563 564 origin = ref.get_origin() 565 566 for attrname in self.optimiser.structures[ref]: 567 568 # Handle gaps in the structure. 569 570 if attrname is None: 571 structure.append("{0, 0}") 572 573 # Handle non-constant and constant members. 574 575 else: 576 attr = attrs[attrname] 577 578 if attrname == "__fn__": 579 580 # Provide bound method references and the unbound function 581 # pointer if populating methods in a class. 582 583 bound_attr = None 584 585 # Classes offer instantiators. 586 587 if kind == "<class>": 588 attr = encode_instantiator_pointer(attr) 589 590 # Methods offers references to bound versions and an unbound 591 # method function. 592 593 elif unbound: 594 bound_attr = encode_bound_reference(attr) 595 attr = "__unbound_method" 596 597 # Other functions just offer function pointers. 598 599 else: 600 attr = encode_function_pointer(attr) 601 602 structure.append("{%s, .fn=%s}" % (bound_attr and ".b=%s" % bound_attr or "0", attr)) 603 continue 604 605 elif attrname == "__args__": 606 structure.append("{.min=%s, .ptable=%s}" % (attr, encode_tablename("Function", origin))) 607 continue 608 609 structure.append(self.encode_member(origin, attrname, attr, kind)) 610 611 def encode_member(self, path, name, ref, structure_type): 612 613 """ 614 Encode within the structure provided by 'path', the member whose 'name' 615 provides 'ref', within the given 'structure_type'. 616 """ 617 618 kind = ref.get_kind() 619 origin = ref.get_origin() 620 621 # References to constant literals. 622 623 if kind == "<instance>": 624 attr_path = "%s.%s" % (path, name) 625 626 # Obtain a constant value directly assigned to the attribute. 627 628 if self.optimiser.constant_numbers.has_key(attr_path): 629 constant_number = self.optimiser.constant_numbers[attr_path] 630 constant_value = "const%d" % constant_number 631 return "{&%s, &%s} /* %s */" % (constant_value, constant_value, name) 632 633 # General undetermined members. 634 635 if kind in ("<var>", "<instance>"): 636 return "{0, 0} /* %s */" % name 637 638 # Set the context depending on the kind of attribute. 639 # For methods: {&<path>, &<attr>} 640 # For other attributes: {&<attr>, &<attr>} 641 642 else: 643 context = (kind == "<function>" and structure_type == "<class>" and \ 644 "&%s" % encode_path(path) or "0") or \ 645 kind == "<instance>" and "&%s" % encode_path(origin) or "0" 646 return "{%s, &%s}" % (context, encode_path(origin)) 647 648 def append_defaults(self, path, structure): 649 650 """ 651 For the given 'path', append default parameter members to the given 652 'structure'. 653 """ 654 655 for name, default in self.importer.function_defaults.get(path): 656 structure.append(self.encode_member(path, name, default, "<instance>")) 657 658 def write_instantiator(self, f_code, path, init_ref): 659 660 """ 661 Write an instantiator to 'f_code' for instances of the class with the 662 given 'path', with 'init_ref' as the initialiser function reference. 663 664 NOTE: This also needs to initialise any __fn__ and __args__ members 665 NOTE: where __call__ is provided by the class. 666 """ 667 668 parameters = self.get_parameters(init_ref) 669 arg_copy = "memcpy(&__tmp_args[1], args, %d * sizeof(__attr));" % (len(parameters) - 1) 670 671 print >>f_code, """\ 672 __attr %s(__attr args[]) 673 { 674 __attr __tmp_args[%d]; 675 __tmp_args[0] = __new(&%s, &%s, sizeof(%s)); 676 %s 677 %s(__tmp_args); 678 return __tmp_args[0]; 679 } 680 """ % ( 681 encode_instantiator_pointer(path), 682 len(parameters), 683 encode_tablename("Instance", path), encode_path(path), encode_symbol("obj", path), 684 len(parameters) - 1 and arg_copy or "", 685 encode_function_pointer(init_ref.get_origin()) 686 ) 687 688 def encode_size(table_type, path=None): 689 return "__%ssize%s" % (table_type, path and "_%s" % encode_path(path) or "") 690 691 def encode_tablename(table_type, path): 692 return "__%sTable_%s" % (table_type, encode_path(path)) 693 694 # vim: tabstop=4 expandtab shiftwidth=4