1/* valacodewriter.vala 2 * 3 * Copyright (C) 2006-2014 Jürg Billeter 4 * Copyright (C) 2006-2008 Raffaele Sandrini 5 * Copyright (C) 2014 Richard Wiedenhöft 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 * 21 * Author: 22 * Jürg Billeter <j@bitron.ch> 23 * Raffaele Sandrini <raffaele@sandrini.ch> 24 */ 25 26 27/** 28 * Code visitor generating Vala API file for the public interface. 29 */ 30public class Vala.CodeWriter : CodeVisitor { 31 static GLib.Regex fix_indent_regex; 32 33 private CodeContext context; 34 35 FileStream stream; 36 37 int indent; 38 /* at begin of line */ 39 bool bol = true; 40 41 Scope current_scope; 42 43 CodeWriterType type; 44 45 string? override_header = null; 46 string? header_to_override = null; 47 48 public CodeWriter (CodeWriterType type = CodeWriterType.EXTERNAL) { 49 this.type = type; 50 } 51 52 /** 53 * Allows overriding of a specific cheader in the output 54 * @param original original cheader to override 55 * @param replacement cheader to replace original with 56 */ 57 public void set_cheader_override (string original, string replacement) 58 { 59 header_to_override = original; 60 override_header = replacement; 61 } 62 63 /** 64 * Writes the public interface of the specified code context into the 65 * specified file. 66 * 67 * @param context a code context 68 * @param filename a relative or absolute filename 69 */ 70 public void write_file (CodeContext context, string filename) { 71 var file_exists = FileUtils.test (filename, FileTest.EXISTS); 72 var temp_filename = "%s.valatmp".printf (filename); 73 this.context = context; 74 75 if (file_exists) { 76 stream = FileStream.open (temp_filename, "w"); 77 } else { 78 stream = FileStream.open (filename, "w"); 79 } 80 81 if (stream == null) { 82 Report.error (null, "unable to open `%s' for writing".printf (filename)); 83 return; 84 } 85 86 var header = context.version_header ? 87 "/* %s generated by %s %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname (), Vala.BUILD_VERSION) : 88 "/* %s generated by %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname ()); 89 write_string (header); 90 write_newline (); 91 write_newline (); 92 93 current_scope = context.root.scope; 94 95 context.accept (this); 96 97 current_scope = null; 98 99 stream = null; 100 101 if (file_exists) { 102 var changed = true; 103 104 try { 105 var old_file = new MappedFile (filename, false); 106 var new_file = new MappedFile (temp_filename, false); 107 var len = old_file.get_length (); 108 if (len == new_file.get_length ()) { 109 if (Memory.cmp (old_file.get_contents (), new_file.get_contents (), len) == 0) { 110 changed = false; 111 } 112 } 113 old_file = null; 114 new_file = null; 115 } catch (FileError e) { 116 // assume changed if mmap comparison doesn't work 117 } 118 119 if (changed) { 120 FileUtils.rename (temp_filename, filename); 121 } else { 122 FileUtils.unlink (temp_filename); 123 } 124 } 125 126 } 127 128 public override void visit_using_directive (UsingDirective ns) { 129 if (type == CodeWriterType.FAST) { 130 write_string ("using "); 131 132 var symbols = new GLib.List<UnresolvedSymbol> (); 133 var sym = (UnresolvedSymbol) ns.namespace_symbol; 134 symbols.prepend (sym); 135 136 while ((sym = sym.inner) != null) { 137 symbols.prepend (sym); 138 } 139 140 write_string (symbols.nth_data (0).name); 141 142 for (int i = 1; i < symbols.length (); i++) { 143 write_string ("."); 144 write_string (symbols.nth_data (i).name); 145 } 146 147 write_string (";\n"); 148 } 149 } 150 151 public override void visit_namespace (Namespace ns) { 152 if (ns.external_package) { 153 return; 154 } 155 156 if (ns.name == null) { 157 ns.accept_children (this); 158 return; 159 } 160 161 var comments = ns.get_comments (); 162 if (context.vapi_comments && comments.size > 0) { 163 bool first = true; 164 SourceReference? first_reference = null; 165 foreach (Comment comment in comments) { 166 if (comment.source_reference.file.file_type == SourceFileType.SOURCE) { 167 if (first) { 168 write_comment (comment); 169 first = false; 170 first_reference = comment.source_reference; 171 } else { 172 Report.warning (comment.source_reference, "Comment describes namespace, that was already described by another comment."); 173 Report.notice (first_reference, "Previous comment was here."); 174 } 175 } 176 } 177 } 178 179 write_attributes (ns); 180 181 write_indent (); 182 write_string ("namespace "); 183 write_identifier (ns.name); 184 write_begin_block (); 185 186 current_scope = ns.scope; 187 188 visit_sorted (ns.get_namespaces ()); 189 visit_sorted (ns.get_classes ()); 190 visit_sorted (ns.get_interfaces ()); 191 visit_sorted (ns.get_structs ()); 192 visit_sorted (ns.get_enums ()); 193 visit_sorted (ns.get_error_domains ()); 194 visit_sorted (ns.get_delegates ()); 195 visit_sorted (ns.get_fields ()); 196 visit_sorted (ns.get_constants ()); 197 visit_sorted (ns.get_methods ()); 198 199 current_scope = current_scope.parent_scope; 200 201 write_end_block (); 202 write_newline (); 203 } 204 205 private string get_cheaders (Symbol sym) { 206 string cheaders = ""; 207 if (type != CodeWriterType.FAST && !sym.external_package) { 208 cheaders = sym.get_attribute_string ("CCode", "cheader_filename") ?? ""; 209 if (cheaders == "" && sym.parent_symbol != null && sym.parent_symbol != context.root) { 210 cheaders = get_cheaders (sym.parent_symbol); 211 } 212 if (cheaders == "" && sym.source_reference != null && !sym.external_package) { 213 cheaders = sym.source_reference.file.get_cinclude_filename (); 214 } 215 216 if (header_to_override != null) { 217 var cheaders_array = cheaders.split (","); 218 for (int i = 0; i < cheaders_array.length; i++) { 219 if (cheaders_array[i] == header_to_override) { 220 cheaders_array[i] = override_header; 221 } 222 } 223 cheaders = string.joinv (",", cheaders_array); 224 } 225 } 226 return cheaders; 227 } 228 229 public override void visit_class (Class cl) { 230 if (cl.external_package) { 231 return; 232 } 233 234 if (!check_accessibility (cl)) { 235 return; 236 } 237 238 if (context.vapi_comments && cl.comment != null) { 239 write_comment (cl.comment); 240 } 241 242 write_attributes (cl); 243 244 write_indent (); 245 write_accessibility (cl); 246 if (cl.is_abstract) { 247 write_string ("abstract "); 248 } 249 if (cl.is_sealed) { 250 write_string ("sealed "); 251 } 252 write_string ("class "); 253 write_identifier (cl.name); 254 255 write_type_parameters (cl.get_type_parameters ()); 256 257 var base_types = cl.get_base_types (); 258 if (base_types.size > 0) { 259 write_string (" : "); 260 261 bool first = true; 262 foreach (DataType base_type in base_types) { 263 if (!first) { 264 write_string (", "); 265 } else { 266 first = false; 267 } 268 write_type (base_type); 269 } 270 } 271 write_begin_block (); 272 273 current_scope = cl.scope; 274 275 visit_sorted (cl.get_classes ()); 276 visit_sorted (cl.get_interfaces ()); 277 visit_sorted (cl.get_structs ()); 278 visit_sorted (cl.get_enums ()); 279 visit_sorted (cl.get_delegates ()); 280 visit_sorted (cl.get_fields ()); 281 visit_sorted (cl.get_constants ()); 282 visit_sorted (cl.get_methods ()); 283 visit_sorted (cl.get_properties ()); 284 visit_sorted (cl.get_signals ()); 285 286 if (cl.constructor != null) { 287 cl.constructor.accept (this); 288 } 289 290 if (cl.class_constructor != null) { 291 cl.class_constructor.accept (this); 292 } 293 294 if (cl.static_constructor != null) { 295 cl.static_constructor.accept (this); 296 } 297 298 if (cl.destructor != null) { 299 cl.destructor.accept (this); 300 } 301 302 if (cl.static_destructor != null) { 303 cl.static_destructor.accept (this); 304 } 305 306 if (cl.class_destructor != null) { 307 cl.class_destructor.accept (this); 308 } 309 310 current_scope = current_scope.parent_scope; 311 312 write_end_block (); 313 write_newline (); 314 } 315 316 void visit_sorted (List<Symbol> symbols) { 317 if (type != CodeWriterType.EXTERNAL && type != CodeWriterType.VAPIGEN) { 318 // order of virtual methods matters for fast vapis 319 foreach (Symbol sym in symbols) { 320 sym.accept (this); 321 } 322 return; 323 } 324 325 var sorted_symbols = new ArrayList<Symbol> (); 326 sorted_symbols.add_all (symbols); 327 sorted_symbols.sort ((a, b) => strcmp (a.name, b.name)); 328 foreach (Symbol sym in sorted_symbols) { 329 sym.accept (this); 330 } 331 } 332 333 public override void visit_struct (Struct st) { 334 if (st.external_package) { 335 return; 336 } 337 338 if (!check_accessibility (st)) { 339 return; 340 } 341 342 if (context.vapi_comments && st.comment != null) { 343 write_comment (st.comment); 344 } 345 346 write_attributes (st); 347 348 write_indent (); 349 write_accessibility (st); 350 write_string ("struct "); 351 write_identifier (st.name); 352 353 write_type_parameters (st.get_type_parameters ()); 354 355 if (st.base_type != null) { 356 write_string (" : "); 357 write_type (st.base_type); 358 } 359 360 write_begin_block (); 361 362 current_scope = st.scope; 363 364 foreach (Field field in st.get_fields ()) { 365 field.accept (this); 366 } 367 visit_sorted (st.get_constants ()); 368 visit_sorted (st.get_methods ()); 369 visit_sorted (st.get_properties ()); 370 371 current_scope = current_scope.parent_scope; 372 373 write_end_block (); 374 write_newline (); 375 } 376 377 public override void visit_interface (Interface iface) { 378 if (iface.external_package) { 379 return; 380 } 381 382 if (!check_accessibility (iface)) { 383 return; 384 } 385 386 if (context.vapi_comments && iface.comment != null) { 387 write_comment (iface.comment); 388 } 389 390 write_attributes (iface); 391 392 write_indent (); 393 write_accessibility (iface); 394 write_string ("interface "); 395 write_identifier (iface.name); 396 397 write_type_parameters (iface.get_type_parameters ()); 398 399 var prerequisites = iface.get_prerequisites (); 400 if (prerequisites.size > 0) { 401 write_string (" : "); 402 403 bool first = true; 404 foreach (DataType prerequisite in prerequisites) { 405 if (!first) { 406 write_string (", "); 407 } else { 408 first = false; 409 } 410 write_type (prerequisite); 411 } 412 } 413 write_begin_block (); 414 415 current_scope = iface.scope; 416 417 visit_sorted (iface.get_classes ()); 418 visit_sorted (iface.get_interfaces ()); 419 visit_sorted (iface.get_structs ()); 420 visit_sorted (iface.get_enums ()); 421 visit_sorted (iface.get_delegates ()); 422 visit_sorted (iface.get_fields ()); 423 visit_sorted (iface.get_constants ()); 424 visit_sorted (iface.get_methods ()); 425 visit_sorted (iface.get_properties ()); 426 visit_sorted (iface.get_signals ()); 427 428 current_scope = current_scope.parent_scope; 429 430 write_end_block (); 431 write_newline (); 432 } 433 434 public override void visit_enum (Enum en) { 435 if (en.external_package) { 436 return; 437 } 438 439 if (!check_accessibility (en)) { 440 return; 441 } 442 443 if (context.vapi_comments && en.comment != null) { 444 write_comment (en.comment); 445 } 446 447 write_attributes (en); 448 449 write_indent (); 450 write_accessibility (en); 451 write_string ("enum "); 452 write_identifier (en.name); 453 write_begin_block (); 454 455 bool first = true; 456 foreach (EnumValue ev in en.get_values ()) { 457 if (first) { 458 first = false; 459 } else { 460 write_string (","); 461 write_newline (); 462 } 463 464 if (context.vapi_comments && ev.comment != null) { 465 write_comment (ev.comment); 466 } 467 468 write_attributes (ev); 469 470 write_indent (); 471 write_identifier (ev.name); 472 473 if (type == CodeWriterType.FAST && ev.value != null && ev.value.is_constant ()) { 474 write_string(" = "); 475 ev.value.accept (this); 476 } 477 } 478 479 if (!first) { 480 if (en.get_methods ().size > 0 || en.get_constants ().size > 0) { 481 write_string (";"); 482 } 483 write_newline (); 484 } 485 486 current_scope = en.scope; 487 foreach (Method m in en.get_methods ()) { 488 m.accept (this); 489 } 490 foreach (Constant c in en.get_constants ()) { 491 c.accept (this); 492 } 493 current_scope = current_scope.parent_scope; 494 495 write_end_block (); 496 write_newline (); 497 } 498 499 public override void visit_error_domain (ErrorDomain edomain) { 500 if (edomain.external_package) { 501 return; 502 } 503 504 if (!check_accessibility (edomain)) { 505 return; 506 } 507 508 if (context.vapi_comments && edomain.comment != null) { 509 write_comment (edomain.comment); 510 } 511 512 write_attributes (edomain); 513 514 write_indent (); 515 write_accessibility (edomain); 516 write_string ("errordomain "); 517 write_identifier (edomain.name); 518 write_begin_block (); 519 520 bool first = true; 521 foreach (ErrorCode ecode in edomain.get_codes ()) { 522 if (first) { 523 first = false; 524 } else { 525 write_string (","); 526 write_newline (); 527 } 528 529 if (context.vapi_comments && ecode.comment != null) { 530 write_comment (ecode.comment); 531 } 532 533 write_attributes (ecode); 534 535 write_indent (); 536 write_identifier (ecode.name); 537 } 538 539 if (!first) { 540 if (edomain.get_methods ().size > 0) { 541 write_string (";"); 542 } 543 write_newline (); 544 } 545 546 current_scope = edomain.scope; 547 foreach (Method m in edomain.get_methods ()) { 548 m.accept (this); 549 } 550 current_scope = current_scope.parent_scope; 551 552 write_end_block (); 553 write_newline (); 554 } 555 556 public override void visit_constant (Constant c) { 557 if (c.external_package) { 558 return; 559 } 560 561 if (!check_accessibility (c)) { 562 return; 563 } 564 565 if (context.vapi_comments && c.comment != null) { 566 write_comment (c.comment); 567 } 568 569 write_attributes (c); 570 571 write_indent (); 572 write_accessibility (c); 573 574 if (c.hides) { 575 write_string ("new "); 576 } 577 578 write_string ("const "); 579 580 write_type (c.type_reference); 581 582 write_string (" "); 583 write_identifier (c.name); 584 write_type_suffix (c.type_reference); 585 if (type == CodeWriterType.FAST && c.value != null && c.value.is_constant ()) { 586 write_string(" = "); 587 c.value.accept (this); 588 } 589 write_string (";"); 590 write_newline (); 591 } 592 593 public override void visit_field (Field f) { 594 if (f.external_package) { 595 return; 596 } 597 598 if (!check_accessibility (f)) { 599 return; 600 } 601 602 if (context.vapi_comments && f.comment != null) { 603 write_comment (f.comment); 604 } 605 606 write_attributes (f); 607 608 write_indent (); 609 write_accessibility (f); 610 611 if (f.hides) { 612 write_string ("new "); 613 } 614 615 if (f.binding == MemberBinding.STATIC) { 616 write_string ("static "); 617 } else if (f.binding == MemberBinding.CLASS) { 618 write_string ("class "); 619 } 620 621 if (f.variable_type.is_weak ()) { 622 write_string ("weak "); 623 } 624 625 write_type (f.variable_type); 626 627 write_string (" "); 628 write_identifier (f.name); 629 write_type_suffix (f.variable_type); 630 write_string (";"); 631 write_newline (); 632 } 633 634 private void write_error_domains (List<DataType> error_domains) { 635 if (error_domains.size > 0) { 636 write_string (" throws "); 637 638 bool first = true; 639 foreach (DataType type in error_domains) { 640 if (!first) { 641 write_string (", "); 642 } else { 643 first = false; 644 } 645 646 write_type (type); 647 } 648 } 649 } 650 651 private void write_params (List<Parameter> params) { 652 write_string ("("); 653 654 int i = 1; 655 foreach (Parameter param in params) { 656 if (i > 1) { 657 write_string (", "); 658 } 659 660 if (param.ellipsis) { 661 write_string ("..."); 662 continue; 663 } 664 665 write_attributes (param); 666 667 if (param.params_array) { 668 write_string ("params "); 669 } 670 671 if (param.direction == ParameterDirection.IN) { 672 if (param.variable_type.value_owned) { 673 write_string ("owned "); 674 } 675 } else { 676 if (param.direction == ParameterDirection.REF) { 677 write_string ("ref "); 678 } else if (param.direction == ParameterDirection.OUT) { 679 write_string ("out "); 680 } 681 if (param.variable_type.is_weak ()) { 682 write_string ("unowned "); 683 } 684 } 685 686 write_type (param.variable_type); 687 688 write_string (" "); 689 write_identifier (param.name); 690 write_type_suffix (param.variable_type); 691 692 if (param.initializer != null) { 693 write_string (" = "); 694 param.initializer.accept (this); 695 } 696 697 i++; 698 } 699 700 write_string (")"); 701 } 702 703 public override void visit_delegate (Delegate cb) { 704 if (cb.external_package) { 705 return; 706 } 707 708 if (!check_accessibility (cb)) { 709 return; 710 } 711 712 if (context.vapi_comments && cb.comment != null) { 713 write_comment (cb.comment); 714 } 715 716 write_attributes (cb); 717 718 write_indent (); 719 720 write_accessibility (cb); 721 write_string ("delegate "); 722 723 write_return_type (cb.return_type); 724 725 write_string (" "); 726 write_identifier (cb.name); 727 728 write_type_parameters (cb.get_type_parameters ()); 729 730 write_string (" "); 731 732 write_params (cb.get_parameters ()); 733 734 var error_types = new ArrayList<DataType> (); 735 cb.get_error_types (error_types); 736 write_error_domains (error_types); 737 738 write_string (";"); 739 740 write_newline (); 741 } 742 743 public override void visit_constructor (Constructor c) { 744 if (type != CodeWriterType.DUMP) { 745 return; 746 } 747 748 if (context.vapi_comments && c.comment != null) { 749 write_comment (c.comment); 750 } 751 752 write_indent (); 753 if (c.binding == MemberBinding.STATIC) { 754 write_string ("static "); 755 } else if (c.binding == MemberBinding.CLASS) { 756 write_string ("class "); 757 } 758 write_string ("construct"); 759 write_code_block (c.body); 760 write_newline (); 761 } 762 763 public override void visit_destructor (Destructor d) { 764 if (type != CodeWriterType.DUMP) { 765 return; 766 } 767 768 if (context.vapi_comments && d.comment != null) { 769 write_comment (d.comment); 770 } 771 772 write_indent (); 773 if (d.binding == MemberBinding.STATIC) { 774 write_string ("static "); 775 } else if (d.binding == MemberBinding.CLASS) { 776 write_string ("class "); 777 } 778 write_string ("~"); 779 var datatype = (TypeSymbol) d.parent_symbol; 780 write_identifier (datatype.name); 781 write_string (" () "); 782 write_code_block (d.body); 783 write_newline (); 784 } 785 786 public override void visit_method (Method m) { 787 if (m.external_package) { 788 return; 789 } 790 791 // don't write interface implementation unless it's an abstract or virtual method 792 if (!check_accessibility (m) || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) { 793 if (type != CodeWriterType.DUMP) { 794 return; 795 } 796 } 797 798 if (context.vapi_comments && m.comment != null) { 799 write_comment (m.comment); 800 } 801 802 write_attributes (m); 803 804 write_indent (); 805 write_accessibility (m); 806 807 if (m is CreationMethod) { 808 if (m.coroutine) { 809 write_string ("async "); 810 } 811 812 var datatype = (TypeSymbol) m.parent_symbol; 813 write_identifier (datatype.name); 814 if (m.name != ".new") { 815 write_string ("."); 816 write_identifier (m.name); 817 } 818 write_string (" "); 819 } else { 820 if (m.hides) { 821 write_string ("new "); 822 } 823 824 if (m.binding == MemberBinding.STATIC) { 825 write_string ("static "); 826 } else if (m.binding == MemberBinding.CLASS) { 827 write_string ("class "); 828 } else if (m.is_abstract) { 829 write_string ("abstract "); 830 } else if (m.is_virtual) { 831 write_string ("virtual "); 832 } else if (m.overrides) { 833 write_string ("override "); 834 } 835 836 if (m.coroutine) { 837 write_string ("async "); 838 } 839 840 write_return_type (m.return_type); 841 write_string (" "); 842 843 write_identifier (m.name); 844 845 write_type_parameters (m.get_type_parameters ()); 846 847 write_string (" "); 848 } 849 850 write_params (m.get_parameters ()); 851 852 var error_types = new ArrayList<DataType> (); 853 m.get_error_types (error_types); 854 write_error_domains (error_types); 855 856 write_code_block (m.body); 857 858 write_newline (); 859 } 860 861 public override void visit_creation_method (CreationMethod m) { 862 visit_method (m); 863 } 864 865 public override void visit_property (Property prop) { 866 if (!check_accessibility (prop) || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) { 867 return; 868 } 869 870 if (context.vapi_comments && prop.comment != null) { 871 write_comment (prop.comment); 872 } 873 874 write_attributes (prop); 875 876 write_indent (); 877 write_accessibility (prop); 878 879 if (prop.hides) { 880 write_string ("new "); 881 } 882 883 if (prop.binding == MemberBinding.STATIC) { 884 write_string ("static "); 885 } else if (prop.is_abstract) { 886 write_string ("abstract "); 887 } else if (prop.is_virtual) { 888 write_string ("virtual "); 889 } else if (prop.overrides) { 890 write_string ("override "); 891 } 892 893 if (prop.property_type.is_weak ()) { 894 write_string ("weak "); 895 } 896 897 write_type (prop.property_type); 898 899 write_string (" "); 900 write_identifier (prop.name); 901 write_string (" {"); 902 if (prop.get_accessor != null) { 903 write_attributes (prop.get_accessor); 904 905 write_property_accessor_accessibility (prop.get_accessor); 906 907 if (prop.get_accessor.value_type.value_owned) { 908 write_string (" owned"); 909 } 910 911 write_string (" get"); 912 write_code_block (prop.get_accessor.body); 913 } 914 if (prop.set_accessor != null) { 915 write_attributes (prop.set_accessor); 916 917 write_property_accessor_accessibility (prop.set_accessor); 918 919 if (prop.set_accessor.value_type.value_owned) { 920 write_string (" owned"); 921 } 922 923 if (prop.set_accessor.writable) { 924 write_string (" set"); 925 } 926 if (prop.set_accessor.construction) { 927 write_string (" construct"); 928 } 929 write_code_block (prop.set_accessor.body); 930 } 931 write_string (" }"); 932 write_newline (); 933 } 934 935 public override void visit_signal (Signal sig) { 936 if (!check_accessibility (sig)) { 937 return; 938 } 939 940 if (context.vapi_comments && sig.comment != null) { 941 write_comment (sig.comment); 942 } 943 944 write_attributes (sig); 945 946 write_indent (); 947 write_accessibility (sig); 948 949 if (sig.hides) { 950 write_string ("new "); 951 } 952 953 if (sig.is_virtual) { 954 write_string ("virtual "); 955 } 956 957 write_string ("signal "); 958 959 write_return_type (sig.return_type); 960 961 write_string (" "); 962 write_identifier (sig.name); 963 964 write_string (" "); 965 966 write_params (sig.get_parameters ()); 967 968 write_string (";"); 969 970 write_newline (); 971 } 972 973 public override void visit_block (Block b) { 974 write_begin_block (); 975 976 foreach (Statement stmt in b.get_statements ()) { 977 stmt.accept (this); 978 } 979 980 write_end_block (); 981 if (b.parent_node is Block) { 982 write_newline (); 983 } 984 } 985 986 public override void visit_empty_statement (EmptyStatement stmt) { 987 } 988 989 public override void visit_declaration_statement (DeclarationStatement stmt) { 990 write_indent (); 991 stmt.declaration.accept (this); 992 write_string (";"); 993 write_newline (); 994 } 995 996 public override void visit_local_variable (LocalVariable local) { 997 if (local.variable_type.is_weak ()) { 998 write_string ("unowned "); 999 } 1000 write_type (local.variable_type); 1001 write_string (" "); 1002 write_identifier (local.name); 1003 write_type_suffix (local.variable_type); 1004 if (local.initializer != null) { 1005 write_string (" = "); 1006 local.initializer.accept (this); 1007 } 1008 } 1009 1010 public override void visit_initializer_list (InitializerList list) { 1011 write_string ("{"); 1012 1013 bool first = true; 1014 foreach (Expression initializer in list.get_initializers ()) { 1015 if (!first) { 1016 write_string (", "); 1017 } else { 1018 write_string (" "); 1019 } 1020 first = false; 1021 initializer.accept (this); 1022 } 1023 write_string (" }"); 1024 } 1025 1026 public override void visit_expression_statement (ExpressionStatement stmt) { 1027 write_indent (); 1028 stmt.expression.accept (this); 1029 write_string (";"); 1030 write_newline (); 1031 } 1032 1033 public override void visit_if_statement (IfStatement stmt) { 1034 write_indent (); 1035 write_string ("if ("); 1036 stmt.condition.accept (this); 1037 write_string (")"); 1038 stmt.true_statement.accept (this); 1039 if (stmt.false_statement != null) { 1040 write_string (" else"); 1041 stmt.false_statement.accept (this); 1042 } 1043 write_newline (); 1044 } 1045 1046 public override void visit_switch_statement (SwitchStatement stmt) { 1047 write_indent (); 1048 write_string ("switch ("); 1049 stmt.expression.accept (this); 1050 write_string (") {"); 1051 write_newline (); 1052 1053 foreach (SwitchSection section in stmt.get_sections ()) { 1054 section.accept (this); 1055 } 1056 1057 write_indent (); 1058 write_string ("}"); 1059 write_newline (); 1060 } 1061 1062 public override void visit_switch_section (SwitchSection section) { 1063 foreach (SwitchLabel label in section.get_labels ()) { 1064 label.accept (this); 1065 } 1066 1067 visit_block (section); 1068 } 1069 1070 public override void visit_switch_label (SwitchLabel label) { 1071 if (label.expression != null) { 1072 write_indent (); 1073 write_string ("case "); 1074 label.expression.accept (this); 1075 write_string (":"); 1076 write_newline (); 1077 } else { 1078 write_indent (); 1079 write_string ("default:"); 1080 write_newline (); 1081 } 1082 } 1083 1084 public override void visit_loop (Loop stmt) { 1085 write_indent (); 1086 write_string ("loop"); 1087 stmt.body.accept (this); 1088 write_newline (); 1089 } 1090 1091 public override void visit_while_statement (WhileStatement stmt) { 1092 write_indent (); 1093 write_string ("while ("); 1094 stmt.condition.accept (this); 1095 write_string (")"); 1096 stmt.body.accept (this); 1097 write_newline (); 1098 } 1099 1100 public override void visit_do_statement (DoStatement stmt) { 1101 write_indent (); 1102 write_string ("do"); 1103 stmt.body.accept (this); 1104 write_string ("while ("); 1105 stmt.condition.accept (this); 1106 write_string (");"); 1107 write_newline (); 1108 } 1109 1110 public override void visit_for_statement (ForStatement stmt) { 1111 write_indent (); 1112 write_string ("for ("); 1113 1114 bool first = true; 1115 foreach (Expression initializer in stmt.get_initializer ()) { 1116 if (!first) { 1117 write_string (", "); 1118 } 1119 first = false; 1120 initializer.accept (this); 1121 } 1122 write_string ("; "); 1123 1124 stmt.condition.accept (this); 1125 write_string ("; "); 1126 1127 first = true; 1128 foreach (Expression iterator in stmt.get_iterator ()) { 1129 if (!first) { 1130 write_string (", "); 1131 } 1132 first = false; 1133 iterator.accept (this); 1134 } 1135 1136 write_string (")"); 1137 stmt.body.accept (this); 1138 write_newline (); 1139 } 1140 1141 public override void visit_foreach_statement (ForeachStatement stmt) { 1142 } 1143 1144 public override void visit_break_statement (BreakStatement stmt) { 1145 write_indent (); 1146 write_string ("break;"); 1147 write_newline (); 1148 } 1149 1150 public override void visit_continue_statement (ContinueStatement stmt) { 1151 write_indent (); 1152 write_string ("continue;"); 1153 write_newline (); 1154 } 1155 1156 public override void visit_return_statement (ReturnStatement stmt) { 1157 write_indent (); 1158 write_string ("return"); 1159 if (stmt.return_expression != null) { 1160 write_string (" "); 1161 stmt.return_expression.accept (this); 1162 } 1163 write_string (";"); 1164 write_newline (); 1165 } 1166 1167 public override void visit_yield_statement (YieldStatement y) { 1168 write_indent (); 1169 write_string ("yield"); 1170 write_string (";"); 1171 write_newline (); 1172 } 1173 1174 public override void visit_throw_statement (ThrowStatement stmt) { 1175 write_indent (); 1176 write_string ("throw"); 1177 if (stmt.error_expression != null) { 1178 write_string (" "); 1179 stmt.error_expression.accept (this); 1180 } 1181 write_string (";"); 1182 write_newline (); 1183 } 1184 1185 public override void visit_try_statement (TryStatement stmt) { 1186 write_indent (); 1187 write_string ("try"); 1188 stmt.body.accept (this); 1189 foreach (var clause in stmt.get_catch_clauses ()) { 1190 clause.accept (this); 1191 } 1192 if (stmt.finally_body != null) { 1193 write_string (" finally"); 1194 stmt.finally_body.accept (this); 1195 } 1196 write_newline (); 1197 } 1198 1199 public override void visit_catch_clause (CatchClause clause) { 1200 var type_name = clause.error_type == null ? "GLib.Error" : clause.error_type.to_string (); 1201 var var_name = clause.variable_name == null ? "_" : clause.variable_name; 1202 write_string (" catch (%s %s)".printf (type_name, var_name)); 1203 clause.body.accept (this); 1204 } 1205 1206 public override void visit_lock_statement (LockStatement stmt) { 1207 write_indent (); 1208 write_string ("lock ("); 1209 stmt.resource.accept (this); 1210 write_string (")"); 1211 if (stmt.body == null) { 1212 write_string (";"); 1213 } else { 1214 stmt.body.accept (this); 1215 } 1216 write_newline (); 1217 } 1218 1219 public override void visit_unlock_statement (UnlockStatement stmt) { 1220 write_indent (); 1221 write_string ("unlock ("); 1222 stmt.resource.accept (this); 1223 write_string (");"); 1224 write_newline (); 1225 } 1226 1227 public override void visit_delete_statement (DeleteStatement stmt) { 1228 write_indent (); 1229 write_string ("delete "); 1230 stmt.expression.accept (this); 1231 write_string (";"); 1232 write_newline (); 1233 } 1234 1235 public override void visit_array_creation_expression (ArrayCreationExpression expr) { 1236 write_string ("new "); 1237 write_type (expr.element_type); 1238 write_string ("["); 1239 1240 bool first = true; 1241 foreach (Expression size in expr.get_sizes ()) { 1242 if (!first) { 1243 write_string (", "); 1244 } 1245 first = false; 1246 1247 size.accept (this); 1248 } 1249 1250 write_string ("]"); 1251 1252 if (expr.initializer_list != null) { 1253 write_string (" "); 1254 expr.initializer_list.accept (this); 1255 } 1256 } 1257 1258 public override void visit_boolean_literal (BooleanLiteral lit) { 1259 write_string (lit.value.to_string ()); 1260 } 1261 1262 public override void visit_character_literal (CharacterLiteral lit) { 1263 write_string (lit.value); 1264 } 1265 1266 public override void visit_integer_literal (IntegerLiteral lit) { 1267 write_string (lit.value); 1268 } 1269 1270 public override void visit_real_literal (RealLiteral lit) { 1271 write_string (lit.value); 1272 } 1273 1274 public override void visit_string_literal (StringLiteral lit) { 1275 write_string (lit.value); 1276 } 1277 1278 public override void visit_null_literal (NullLiteral lit) { 1279 write_string ("null"); 1280 } 1281 1282 public override void visit_member_access (MemberAccess expr) { 1283 if (expr.inner != null) { 1284 expr.inner.accept (this); 1285 write_string ("."); 1286 } 1287 write_identifier (expr.member_name); 1288 } 1289 1290 public override void visit_method_call (MethodCall expr) { 1291 if (expr.is_yield_expression) { 1292 write_string ("yield "); 1293 } 1294 1295 expr.call.accept (this); 1296 write_string (" ("); 1297 1298 bool first = true; 1299 foreach (Expression arg in expr.get_argument_list ()) { 1300 if (!first) { 1301 write_string (", "); 1302 } 1303 first = false; 1304 1305 arg.accept (this); 1306 } 1307 1308 write_string (")"); 1309 } 1310 1311 public override void visit_element_access (ElementAccess expr) { 1312 expr.container.accept (this); 1313 write_string ("["); 1314 1315 bool first = true; 1316 foreach (Expression index in expr.get_indices ()) { 1317 if (!first) { 1318 write_string (", "); 1319 } 1320 first = false; 1321 1322 index.accept (this); 1323 } 1324 1325 write_string ("]"); 1326 } 1327 1328 public override void visit_slice_expression (SliceExpression expr) { 1329 expr.container.accept (this); 1330 write_string ("["); 1331 expr.start.accept (this); 1332 write_string (":"); 1333 expr.stop.accept (this); 1334 write_string ("]"); 1335 } 1336 1337 public override void visit_base_access (BaseAccess expr) { 1338 write_string ("base"); 1339 } 1340 1341 public override void visit_postfix_expression (PostfixExpression expr) { 1342 expr.inner.accept (this); 1343 if (expr.increment) { 1344 write_string ("++"); 1345 } else { 1346 write_string ("--"); 1347 } 1348 } 1349 1350 public override void visit_object_creation_expression (ObjectCreationExpression expr) { 1351 if (expr.is_yield_expression) { 1352 write_string ("yield "); 1353 } 1354 1355 if (!expr.struct_creation) { 1356 write_string ("new "); 1357 } 1358 1359 write_type (expr.type_reference); 1360 1361 if (expr.symbol_reference.name != ".new") { 1362 write_string ("."); 1363 write_string (expr.symbol_reference.name); 1364 } 1365 1366 write_string (" ("); 1367 1368 bool first = true; 1369 foreach (Expression arg in expr.get_argument_list ()) { 1370 if (!first) { 1371 write_string (", "); 1372 } 1373 first = false; 1374 1375 arg.accept (this); 1376 } 1377 1378 write_string (")"); 1379 } 1380 1381 public override void visit_sizeof_expression (SizeofExpression expr) { 1382 write_string ("sizeof ("); 1383 write_type (expr.type_reference); 1384 write_string (")"); 1385 } 1386 1387 public override void visit_typeof_expression (TypeofExpression expr) { 1388 write_string ("typeof ("); 1389 write_type (expr.type_reference); 1390 write_string (")"); 1391 } 1392 1393 public override void visit_unary_expression (UnaryExpression expr) { 1394 write_string (expr.operator.to_string ()); 1395 expr.inner.accept (this); 1396 } 1397 1398 public override void visit_cast_expression (CastExpression expr) { 1399 if (expr.is_non_null_cast) { 1400 write_string ("(!) "); 1401 expr.inner.accept (this); 1402 return; 1403 } 1404 1405 if (!expr.is_silent_cast) { 1406 write_string ("("); 1407 write_type (expr.type_reference); 1408 write_string (") "); 1409 } 1410 1411 expr.inner.accept (this); 1412 1413 if (expr.is_silent_cast) { 1414 write_string (" as "); 1415 write_type (expr.type_reference); 1416 } 1417 } 1418 1419 public override void visit_pointer_indirection (PointerIndirection expr) { 1420 write_string ("*"); 1421 expr.inner.accept (this); 1422 } 1423 1424 public override void visit_addressof_expression (AddressofExpression expr) { 1425 write_string ("&"); 1426 expr.inner.accept (this); 1427 } 1428 1429 public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) { 1430 write_string ("(owned) "); 1431 expr.inner.accept (this); 1432 } 1433 1434 public override void visit_binary_expression (BinaryExpression expr) { 1435 expr.left.accept (this); 1436 write_string (" "); 1437 write_string (expr.operator.to_string ()); 1438 write_string (" "); 1439 expr.right.accept (this); 1440 } 1441 1442 public override void visit_type_check (TypeCheck expr) { 1443 expr.expression.accept (this); 1444 write_string (" is "); 1445 write_type (expr.type_reference); 1446 } 1447 1448 public override void visit_conditional_expression (ConditionalExpression expr) { 1449 expr.condition.accept (this); 1450 write_string ("?"); 1451 expr.true_expression.accept (this); 1452 write_string (":"); 1453 expr.false_expression.accept (this); 1454 } 1455 1456 public override void visit_lambda_expression (LambdaExpression expr) { 1457 write_string ("("); 1458 var params = expr.get_parameters (); 1459 int i = 1; 1460 foreach (var param in params) { 1461 if (i > 1) { 1462 write_string (", "); 1463 } 1464 1465 if (param.direction == ParameterDirection.REF) { 1466 write_string ("ref "); 1467 } else if (param.direction == ParameterDirection.OUT) { 1468 write_string ("out "); 1469 } 1470 1471 write_identifier (param.name); 1472 1473 i++; 1474 } 1475 write_string (") =>"); 1476 if (expr.statement_body != null) { 1477 expr.statement_body.accept (this); 1478 } else if (expr.expression_body != null) { 1479 expr.expression_body.accept (this); 1480 } 1481 } 1482 1483 public override void visit_assignment (Assignment a) { 1484 a.left.accept (this); 1485 write_string (" = "); 1486 a.right.accept (this); 1487 } 1488 1489 private void write_indent () { 1490 if (!bol) { 1491 stream.putc ('\n'); 1492 } 1493 1494 stream.puts (string.nfill (indent, '\t')); 1495 bol = false; 1496 } 1497 1498 private void write_comment (Comment comment) { 1499 try { 1500 if (fix_indent_regex == null) 1501 fix_indent_regex = new Regex ("\\n[\\t ]*"); 1502 } catch (Error e) { 1503 assert_not_reached (); 1504 } 1505 1506 string replacement = "\n%s ".printf (string.nfill (indent, '\t')); 1507 string fixed_content; 1508 try { 1509 fixed_content = fix_indent_regex.replace (comment.content, comment.content.length, 0, replacement); 1510 } catch (Error e) { 1511 assert_not_reached(); 1512 } 1513 1514 write_indent (); 1515 write_string ("/*"); 1516 write_string (fixed_content); 1517 write_string ("*/"); 1518 } 1519 1520 private void write_identifier (string s) { 1521 char* id = (char*)s; 1522 int id_length = (int)s.length; 1523 if (Vala.Scanner.get_identifier_or_keyword (id, id_length) != Vala.TokenType.IDENTIFIER || 1524 s.get_char ().isdigit ()) { 1525 stream.putc ('@'); 1526 } 1527 write_string (s); 1528 } 1529 1530 private void write_return_type (DataType type) { 1531 if (type.is_weak ()) { 1532 write_string ("unowned "); 1533 } 1534 1535 write_type (type); 1536 } 1537 1538 private void write_type (DataType type) { 1539 write_string (type.to_qualified_string (current_scope)); 1540 } 1541 1542 private void write_type_suffix (DataType type) { 1543 unowned ArrayType? array_type = type as ArrayType; 1544 if (array_type != null && array_type.fixed_length) { 1545 write_string ("["); 1546 array_type.length.accept (this); 1547 write_string ("]"); 1548 } 1549 } 1550 1551 private void write_string (string s) { 1552 stream.puts (s); 1553 bol = false; 1554 } 1555 1556 private void write_newline () { 1557 stream.putc ('\n'); 1558 bol = true; 1559 } 1560 1561 void write_code_block (Block? block) { 1562 if (block == null || (type != CodeWriterType.DUMP && type != CodeWriterType.VAPIGEN)) { 1563 write_string (";"); 1564 return; 1565 } 1566 1567 block.accept (this); 1568 } 1569 1570 private void write_begin_block () { 1571 if (!bol) { 1572 stream.putc (' '); 1573 } else { 1574 write_indent (); 1575 } 1576 stream.putc ('{'); 1577 write_newline (); 1578 indent++; 1579 } 1580 1581 private void write_end_block () { 1582 indent--; 1583 write_indent (); 1584 stream.putc ('}'); 1585 } 1586 1587 private bool check_accessibility (Symbol sym) { 1588 switch (type) { 1589 case CodeWriterType.EXTERNAL: 1590 case CodeWriterType.VAPIGEN: 1591 return sym.access == SymbolAccessibility.PUBLIC || 1592 sym.access == SymbolAccessibility.PROTECTED; 1593 1594 case CodeWriterType.INTERNAL: 1595 case CodeWriterType.FAST: 1596 return sym.access == SymbolAccessibility.INTERNAL || 1597 sym.access == SymbolAccessibility.PUBLIC || 1598 sym.access == SymbolAccessibility.PROTECTED; 1599 1600 case CodeWriterType.DUMP: 1601 return true; 1602 1603 default: 1604 assert_not_reached (); 1605 } 1606 } 1607 1608 private bool skip_since_tag_check (Symbol sym, string since_val) { 1609 Symbol parent_symbol = sym; 1610 1611 while (parent_symbol.parent_symbol != null) { 1612 parent_symbol = parent_symbol.parent_symbol; 1613 if (parent_symbol.version.since == since_val) { 1614 return true; 1615 } 1616 } 1617 1618 return false; 1619 } 1620 1621 private void write_attributes (CodeNode node) { 1622 unowned Symbol? sym = node as Symbol; 1623 1624 var need_cheaders = type != CodeWriterType.FAST && sym != null && !(sym is Namespace) && sym.parent_symbol is Namespace; 1625 1626 var attributes = new GLib.Sequence<Attribute> (); 1627 foreach (var attr in node.attributes) { 1628 attributes.insert_sorted (attr, (a, b) => strcmp (a.name, b.name)); 1629 } 1630 if (need_cheaders && node.get_attribute ("CCode") == null) { 1631 attributes.insert_sorted (new Attribute ("CCode"), (a, b) => strcmp (a.name, b.name)); 1632 } 1633 1634 var iter = attributes.get_begin_iter (); 1635 while (!iter.is_end ()) { 1636 unowned Attribute attr = iter.get (); 1637 iter = iter.next (); 1638 1639 var keys = new GLib.Sequence<string> (); 1640 foreach (var key in attr.args.get_keys ()) { 1641 if (key == "cheader_filename" && sym is Namespace) { 1642 continue; 1643 } 1644 keys.insert_sorted (key, (CompareDataFunc<string>) strcmp); 1645 } 1646 if (need_cheaders && attr.name == "CCode" && !attr.has_argument ("cheader_filename")) { 1647 keys.insert_sorted ("cheader_filename", (CompareDataFunc<string>) strcmp); 1648 } 1649 1650 if (attr.name == "CCode" && keys.get_length () == 0) { 1651 // only cheader_filename on namespace 1652 continue; 1653 } 1654 1655 if (attr.name == "Source") { 1656 continue; 1657 } 1658 1659 if (sym != null && attr.args.size == 1 && attr.name == "Version") { 1660 string since_val = attr.get_string ("since"); 1661 if (since_val != null && skip_since_tag_check (sym, since_val)) { 1662 continue; 1663 } 1664 } 1665 1666 if (!(node is Parameter) && !(node is PropertyAccessor)) { 1667 write_indent (); 1668 } 1669 1670 stream.printf ("[%s", attr.name); 1671 if (keys.get_length () > 0) { 1672 stream.puts (" ("); 1673 1674 unowned string separator = ""; 1675 var arg_iter = keys.get_begin_iter (); 1676 while (!arg_iter.is_end ()) { 1677 unowned string arg_name = arg_iter.get (); 1678 arg_iter = arg_iter.next (); 1679 if (arg_name == "cheader_filename") { 1680 stream.printf ("%scheader_filename = \"%s\"", separator, get_cheaders (sym)); 1681 } else { 1682 stream.printf ("%s%s = %s", separator, arg_name, attr.args.get (arg_name)); 1683 } 1684 separator = ", "; 1685 } 1686 1687 stream.puts (")"); 1688 } 1689 stream.puts ("]"); 1690 if (node is Parameter || node is PropertyAccessor) { 1691 write_string (" "); 1692 } else { 1693 write_newline (); 1694 } 1695 } 1696 1697 if (type == CodeWriterType.FAST && !(node is Parameter || node is PropertyAccessor)) { 1698 var source_reference = node.source_reference; 1699 if (source_reference != null) { 1700 write_indent (); 1701 string filename = source_reference.file.filename; 1702 if (filename.has_prefix (context.basedir)) { 1703 filename = filename.substring (context.basedir.length + 1); 1704 } 1705 stream.puts ("[Source (filename = \"%s\", line = %i, column = %i)]".printf (filename, source_reference.begin.line, source_reference.begin.column)); 1706 write_newline (); 1707 } 1708 } 1709 } 1710 1711 private void write_accessibility (Symbol sym) { 1712 write_string (sym.access.to_string ()); 1713 write_string (" "); 1714 1715 if (type != CodeWriterType.EXTERNAL && type != CodeWriterType.VAPIGEN && sym.external && !sym.external_package) { 1716 write_string ("extern "); 1717 } 1718 } 1719 1720 void write_property_accessor_accessibility (Symbol sym) { 1721 if (sym.access == SymbolAccessibility.PUBLIC) { 1722 return; 1723 } 1724 1725 write_string (" "); 1726 write_string (sym.access.to_string ()); 1727 } 1728 1729 void write_type_parameters (List<TypeParameter> type_params) { 1730 if (type_params.size > 0) { 1731 write_string ("<"); 1732 bool first = true; 1733 foreach (TypeParameter type_param in type_params) { 1734 if (first) { 1735 first = false; 1736 } else { 1737 write_string (","); 1738 } 1739 write_identifier (type_param.name); 1740 } 1741 write_string (">"); 1742 } 1743 } 1744} 1745 1746public enum Vala.CodeWriterType { 1747 EXTERNAL, 1748 INTERNAL, 1749 FAST, 1750 DUMP, 1751 VAPIGEN 1752} 1753