3 # xpidl2cs.pl : Generates C# interfaces from idl
5 # Author: Andreia Gaita <shana.ufie@gmail.com>
7 # Copyright (c) 2007 Novell, Inc.
9 # This program is free software; you can redistribute it and/or
10 # modify it under the terms of version 2 of the GNU General Public
11 # License as published by the Free Software Foundation.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public
19 # License along with this program; if not, write to the
20 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 # Boston, MA 02111-1307, USA.
22 ##############################################################
24 die "Usage: xpidl2cs.pl file.idl [/path/to/idl/]" if scalar(@ARGV) < 1;
27 my $path = shift if scalar(@ARGV) > 0;
28 my $nosig = shift if scalar(@ARGV) > 0;
29 $nosig = "" if !$nosig;
31 open FILE, '<', $path.$file or die "Can't open file $path$file";
47 $types{"short"} = {name => "short", out => "out", marshal => ""};
48 $types{"PRUint8"} = {name => "char", out => "out", marshal => ""};
49 $types{"PRInt8"} = {name => "char", out => "out", marshal => ""};
50 $types{"unsigned,short"} = {name => "ushort", out => "out", marshal => ""};
51 $types{"PRUint16"} = {name => "ushort", out => "out", marshal => ""};
52 $types{"PRInt16"} = {name => "short", out => "out", marshal => ""};
53 $types{"int"} = {name => "int", out => "out", marshal => ""};
54 $types{"nsresult"} = {name => "int", out => "out", marshal => ""};
55 $types{"unsigned,int"} = {name => "uint", out => "out", marshal => ""};
56 $types{"PRUint32"} = {name => "UInt32", out => "out", marshal => ""};
57 $types{"PRInt32"} = {name => "Int32", out => "out", marshal => ""};
58 $types{"PRInt64"} = {name => "long", out => "out", marshal => ""};
59 $types{"long"} = {name => "int", out => "out", marshal => ""};
60 $types{"size_t"} = {name => "int", out => "out", marshal => ""};
61 $types{"unsigned,long"} = {name => "uint", out => "out", marshal => ""};
62 $types{"float"} = {name => "float", out => "out", marshal => ""};
63 $types{"boolean"} = {name => "bool", out => "out", marshal => ""};
64 $types{"PRBool"} = {name => "bool", out => "out", marshal => ""};
65 $types{"void"} = {name => "", out => "", marshal => ""};
66 $types{"octet"} = {name => "byte", out => "out", marshal => ""};
67 $types{"octet[]"} = {name => "IntPtr", out => "out", marshal => " "};
68 $types{"byte"} = {name => "byte", out => "out", marshal => ""};
69 $types{"DOMString"} = {name => "/*DOMString*/ HandleRef", out => "", marshal => ""};
70 $types{"AUTF8String"} = {name => "/*AUTF8String*/ HandleRef", out => "", marshal => ""};
71 $types{"ACString"} = {name => "/*ACString*/ HandleRef", out => "", marshal => ""};
72 $types{"AString"} = {name => "/*AString*/ HandleRef", out => "", marshal => ""};
73 $types{"wstring"} = {name => "string", out => "", marshal => "MarshalAs(UnmanagedType.LPWStr)"};
74 $types{"nsCIDRef"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
75 $types{"nsIIDRef"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
76 $types{"Guid"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
77 $types{"nsCID"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
78 $types{"nsCIDPtr"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
79 $types{"string"} = {name => "string", out => "ref", marshal => "MarshalAs (UnmanagedType.LPStr)"};
80 $types{"refstring"} = {name => "IntPtr", out => "ref", marshal => ""};
81 $types{"charPtr"} = {name => "StringBuilder", out => "", marshal => ""};
82 $types{"voidPtr"} = {name => "IntPtr", out => "", marshal => ""};
83 $types{"nsISupports"} = {name => "IntPtr", out => "out", marshal =>"MarshalAs (UnmanagedType.Interface)"};
84 $types{"DOMTimeStamp"} = {name => "int", out => "out", marshal => ""};
85 $types{"nsWriteSegmentFun"} = {name => "nsIWriteSegmentFunDelegate", out => "", marshal => ""};
86 $types{"nsReadSegmentFun"} = {name => "nsIReadSegmentFunDelegate", out => "", marshal => ""};
87 $types{"nsTimerCallbackFunc"} = {name => "nsITimerCallbackDelegate", out => "", marshal => ""};
88 $types{"nsLoadFlags"} = {name => "ulong", out => "out", marshal => ""};
89 $types{"nsQIResult"} = {name => "IntPtr", out => "out", marshal => ""};
90 $types{"nsIIDPtr[]"} = {name => "IntPtr", out => "out", marshal => ""};
91 $types{"PRFileDescStar"} = {name => "IntPtr", out => "out", marshal => ""};
92 $types{"PRLibraryStar"} = {name => "IntPtr", out => "out", marshal => ""};
93 $types{"FILE"} = {name => "IntPtr", out => "out", marshal => ""};
94 $types{"nsIPresShell"} = {name => "/*nsIPresShell*/ IntPtr", out => "out", marshal => ""};
95 $types{"nsIDocument"} = {name => "/*nsIDocument*/ IntPtr", out => "out", marshal => ""};
96 $types{"nsIFrame"} = {name => "/*nsIFrame*/ IntPtr", out => "out", marshal => ""};
97 $types{"nsObjectFrame"} = {name => "/*nsObjectFrame*/ IntPtr", out => "out", marshal => ""};
98 $types{"nsIContent"} = {name => "/*nsIContent*/ IntPtr", out => "out", marshal => ""};
99 $types{"others"} = {name => "", out => "out", marshal => "MarshalAs (UnmanagedType.Interface)"};
102 $returnvalues{"short"} = {value => "0"};
103 $returnvalues{"ushort"} = {value => "0"};
104 $returnvalues{"int"} = {value => "0"};
105 $returnvalues{"uint"} = {value => "0"};
106 $returnvalues{"UInt32"} = {value => "0"};
107 $returnvalues{"Int32"} = {value => "0"};
108 $returnvalues{"long"} = {value => "0"};
109 $returnvalues{"ulong"} = {value => "0"};
110 $returnvalues{"IntPtr"} = {value => "0"};
111 $returnvalues{"float"} = {value => "0"};
112 $returnvalues{"byte"} = {value => "0"};
113 $returnvalues{"IntPtr"} = {value => "IntPtr.Zero"};
114 $returnvalues{"string"} = {value => "String.Empty"};
115 $returnvalues{"bool"} = {value => "false"};
116 $returnvalues{"/*DOMString*/ HandleRef"} = {value => "null"};
117 $returnvalues{"/*AUTF8String*/ HandleRef"} = {value => "null"};
118 $returnvalues{"ACString*/ HandleRef"} = {value => "null"};
119 $returnvalues{"/*AString*/ HandleRef"} = {value => "null"};
120 $returnvalues{""} = {value => ""};
121 $returnvalues{"others"} = {value => "null"};
124 $names{"event"} = {name => "_event"};
125 $names{"lock"} = {name => "_lock"};
129 my $class_implementation;
139 #print "parse_parent\n";
142 print "Parsing parent $x\n";
143 `perl xpidl2cs.pl $x.idl $path $nosig`;
145 open my $f, '<', "$x.cs";
148 while (my $line = <$f>) {
151 if ($line =~ /#region/) {
153 $out .= $line . "\n";
156 elsif ($line =~ /\}/) {
160 $out .= $line . "\n";
168 #print "has_setter\n";
170 return !$properties{$x}->{"setter"};
177 if (exists $names{$x}) {
178 return $names{$x}->{"name"};
189 # print "arr = $arr ; out = $out ; name = $x\n";
192 if ($arr && exists $types{"$out$x\[\]"}) {
193 return $types{"$out$x\[\]"}->{"name"};
194 } elsif ($arr && exists $types{"$out$x"}) {
195 return $types{"$out$x"}->{"name"}."[]";
196 } elsif (exists $types{"$out$x"}) {
197 return $types{"$out$x"}->{"name"};
201 if (exists $types{$x} || ($arr && exists $types{"$x\[\]"})) {
202 if ($arr && exists $types{"$x\[\]"}) {
203 return $types{"$x\[\]"}->{"name"};
205 return $types{$x}->{"name"}."[]";
207 return $types{$x}->{"name"};
216 if (exists $types{$x}) {
217 return $types{$x}->{"out"};
219 return $types{"others"}->{"out"};
223 #print "get_marshal\n";
229 if ($arr && exists $types{"$out$x\[\]"}) {
230 return $types{"$out$x\[\]"}->{"marshal"};
231 } elsif (exists $types{"$out$x"}) {
232 return $types{"$out$x"}->{"marshal"};
236 if (exists $types{$x} || ($arr && exists $types{"$x\[\]"})) {
237 if ($arr && exists $types{"$x\[\]"}) {
238 return $types{"$x\[\]"}->{"marshal"};
240 return $types{$x}->{"marshal"};
244 return $types{"others"}->{"marshal"};
247 sub get_return_value {
248 #print "get_return_value\n";
250 if (exists $returnvalues{$x}) {
251 return $returnvalues{$x}->{"value"};
253 return $returnvalues{"others"}->{"value"};
258 #print "is_property\n";
260 return (exists $properties{$x});
264 #print "add_external\n";
266 if ($x !~ /nsISupports/ && !exists $types{$x} && !exists $dependents{$x}) {
267 $dependents{$x} = $x;
269 # print "add_external $x\n";
273 #print "get_params\n";
276 #print $methods{$x}->{"params"}."\n";
277 my @params = split /,/, $methods{$x}->{"params"};
278 my $lastoutparam = "";
281 #print "params:@params:\n";
282 for my $param (@params) {
290 # print "param:$param:\n";
291 my @p = split (" ", $param);
293 # need to backtrack to a previous parameter defined by iid_is(name) and
294 # replace the type of this one with that. who the $%#@ came up with this idea? le sigh.
296 if (@p[0] =~ m/iid_is/) {
298 $name = &get_name (@p[0]);
300 $type = $list{$name}->{"type"};
301 $marshal = $list{$name}->{"marshal"};
302 $marshal = " " if !$marshal;
304 until (scalar(@p) == 3) {
309 if (@p[0] =~ m/array/ || @p[1] =~ m/array/) {
310 until (scalar(@p) == 3) {
313 $isout = 1 if (@p[0] =~ m/out/);
315 $marshal = &get_marshal (@p[0], "", 1);
316 $type = &get_type(@p[0], "", 1);
319 shift @p unless @p[0] =~ /(in|out)/;
320 $isout = 1 if (@p[0] =~ m/out/);
321 shift @p unless scalar(@p) <= 2;
323 # if an out parameter is of type nsQIResult, that means
324 # it will return a pointer to an interface (that can be anything).
325 # That means we want to return an IntPtr, and later cast it to
326 # the proper type, so reset type and marshalling
327 if ($isout && @p[0] =~ /nsQIResult/) {
333 $type = join ",", @p[0..@p-2];
335 until (scalar(@p) == 1) {
339 $marshal = &get_marshal ($type);
340 $marshal = " " if !$marshal;
341 $type = &get_type ($type);
342 $name = &get_name (@p[0]);
344 #print "marshal:$marshal\ttype:$type\tname:$name\n";
345 $out = &get_out($type) if $isout;
347 $type = &get_type (@p[0]) unless $type;
348 shift @p unless scalar(@p) == 1;
349 $marshal = &get_marshal ($type) unless $marshal;
350 $name = &get_name (@p[0]) unless $name;
352 #print "marshal:$marshal\ttype:$type\tname:$name\n";
362 &add_external ($type);
364 $marshal = "" if $marshal eq " ";
366 # my $tmp = "\n\t\t\t\t";
368 $tmp .= "[$marshal] " if $marshal;
369 $tmp .= "$out $type $name";
371 $lastoutparam = $name if $isout;
375 #print "$methods{$x}->{\"type\"}\n";
376 #print "nosig:$nosig;x:$x;type:" . &get_type ($methods{$x}->{"type"}) . ";\n";
377 if (!$nosig && $x !~ /void/ && &get_type ($methods{$x}->{"type"}) ne "") {
378 $type = $methods{$x}->{"type"};
379 $type =~ s/\[.*\],//;
380 $marshal = &get_marshal ($type);
383 $tmp = "[$marshal] " if $marshal;
384 $tmp .= &get_out($type);
385 $tmp .= " " . &get_type ($type);
387 #print "tmp 2:$tmp\n";
390 &add_external ($type);
393 if ($nosig && &get_type ($methods{$x}->{"type"}) eq "" && $lastoutparam) {
394 $methods{$x}->{"type"} = $list{$lastoutparam}->{"type"};
399 return join (",\n\t\t\t\t", @ret);
403 #print "parse_file\n";
411 while (my $line = <FILE>) {
414 next if !$start && $line !~ /uuid\(/;
416 last if $start && $line =~ /\};/;
420 if (index($line, "/*") > -1) {
424 if ($comment && index($line, "*/") > -1) {
431 if (index($line, "*") == -1 && index ($line, "//") == -1 && index ($line, "#include") == -1) {
433 $line =~ s/\[noscript\] //;
435 if (index ($line, "uuid(") != -1) {
437 $uuid =~ s/\[.*uuid\((.*)\)\]/\1/;
438 $interface->{"uuid"} = $uuid;
441 elsif (index($line, "interface") != -1) {
443 $class =~ s/interface ([^\:|\s]+)\s*:\s*(.*)/\1/;
444 # print "\t\tclass:$class\n";
446 $parent =~ s/([^\:]+):\s*(.*)[\s|\{]/\2/;
447 # print "\t\tparent:$parent\n";
448 $interface->{"class"} = $class;
449 $interface->{"parent"} = $parent;
451 elsif (index ($line, "const") != -1 && index ($line, "[") == -1) {
454 elsif (index ($line, "attribute") != -1) {
455 my $att = substr($line, index($line, "attribute") + 10);
457 my @atts = split / /, $att;
459 my $name = pop @atts;
461 # print $name . "\n";
462 my @nospaces = grep /[^ ]/, @atts;
463 my $type = join ",", @nospaces;
466 if (index ($line, "readonly") != -1) {
469 # print $type . "\n";
470 $properties{$name} = {type => $type, setter => $setter};
471 $interface->{"items"} .= $name . ",";
473 elsif ($line !~ m/[{|}]/ && $line =~ m/./) {
474 # print $line . "\n";
477 my $m = substr($line, 0, index($line, "("));
478 my @atts = split / /, $m;
482 # print "name=$mname\n";
483 my @nospaces = grep /[^ ]/, @atts;
484 $mtype = join ",", @nospaces;
485 $mtype =~ s/\[.*\],//;
486 # print "type=$mtype\n";
487 $mparams .= substr($line, index($line, "(") + 1);
491 @atts = split / /, $mparams;
492 @nospaces = grep /[^ ]/, @atts;
493 $mparams = join " ", @nospaces;
494 # print "params=>$mparams\n";
497 elsif (index ($line, "raises") == -1) {
501 my @atts = split / /, $mparams;
502 my @nospaces = grep /[^ ]/, @atts;
503 $mparams = join " ", @nospaces;
504 # print "params=>$mparams\n";
506 if (index ($line, ";") != -1) {
508 $mparams =~ s/\[([^\]]+),([^\]]+),([^\]]+)\]/\1 \2 \3/;
509 $mparams =~ s/\[([^\]]+),([^\]]+)\]/\1 \2/;
512 $mparams =~ s/retval//;
514 $methods{$mname} = {type => $mtype, params => $mparams};
515 $interface->{"items"} .= $mname . ",";
516 # print "params=>$mparams\n";
529 my $name = $interface->{"class"};
532 print X "// THIS FILE AUTOMATICALLY GENERATED BY xpidl2cs.pl\n";
533 print X "// EDITING IS PROBABLY UNWISE\n";
534 print X "// Permission is hereby granted, free of charge, to any person obtaining\n";
535 print X "// a copy of this software and associated documentation files (the\n";
536 print X "// \"Software\"), to deal in the Software without restriction, including\n";
537 print X "// without limitation the rights to use, copy, modify, merge, publish,\n";
538 print X "// distribute, sublicense, and/or sell copies of the Software, and to\n";
539 print X "// permit persons to whom the Software is furnished to do so, subject to\n";
540 print X "// the following conditions:\n";
542 print X "// The above copyright notice and this permission notice shall be\n";
543 print X "// included in all copies or substantial portions of the Software.\n";
545 print X "// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n";
546 print X "// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n";
547 print X "// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n";
548 print X "// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n";
549 print X "// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n";
550 print X "// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n";
551 print X "// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n";
553 print X "// Copyright (c) 2007, 2008 Novell, Inc.\n";
555 print X "// Authors:\n";
556 print X "// Andreia Gaita (avidigal\@novell.com)\n";
559 print X "using System;\n";
560 print X "using System.Runtime.InteropServices;\n";
561 print X "using System.Runtime.CompilerServices;\n";
562 print X "using System.Text;\n";
564 print X "namespace Mono.Mozilla {\n";
567 my $uuid = $interface->{"uuid"};
568 my $parent = $interface->{"parent"};
569 print X "\t[Guid (\"$uuid\")]\n";
570 print X "\t[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]\n";
571 print X "\t[ComImport ()]\n";
572 print X "\tinternal interface $name";
573 print X " : $parent" if $parent !~ /nsISupports/;
576 if ($parent !~ /nsISupports/) {
577 print X &parse_parent ($parent);
580 print X "#region $name\n";
582 my @items = split ",", $interface->{"items"};
583 for my $item (@items) {
586 print X "\t\t[PreserveSigAttribute]\n";
588 print X "\t\t[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]\n";
590 if (&is_property ($item)) {
591 my $out = &get_out($properties{$item}->{"type"});
592 my $marshal = &get_marshal($properties{$item}->{"type"}, $out);
593 my $type = &get_type ($properties{$item}->{"type"}, $out);
594 my $name = ucfirst ($item);
596 &add_external ($properties{$item}->{"type"});
600 print X "[return: $marshal] " if $marshal;
601 print X "$type get$name ();\n";
603 print X "int get$name (";
604 print X "[$marshal] " if $marshal;
605 print X "$out $type ret);\n";
609 $type = &get_type ($properties{$item}->{"type"});
610 $marshal = &get_marshal($properties{$item}->{"type"});
613 if (&has_setter($item)) {
615 print X "\t\t[PreserveSigAttribute]\n";
617 print X "\t\t[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]\n";
623 print X " set$name (";
624 print X "[$marshal] " if $marshal;
625 print X "$type value);\n";
630 my $type = &get_type ($methods{$item}->{"type"});
631 my $out = &get_out($methods{$item}->{"type"}) if $type;
632 my $marshal = &get_marshal($methods{$item}->{"type"}, $out) if $type;
633 $type = "void" if !$type;
637 print X "[return: $marshal] " if $marshal;
638 print X "$type $item (";
639 print X &get_params($item);
642 print X "int $item (";
643 print X &get_params($item);
649 print X "#endregion\n";
653 # mozilla-specific helper classes to proxy objects between threads
654 # remove if you're not running this for mono.mozilla
658 $helpername =~ s/nsI/ns/;
659 print X "\tinternal class $helpername";
661 print X "\t\tpublic static $name GetProxy (Mono.WebBrowser.IWebBrowser control, $name obj)\n";
663 print X "\t\t\tobject o = Base.GetProxyForObject (control, typeof($name).GUID, obj);\n";
664 print X "\t\t\treturn o as $name;\n";
668 #end of mozilla-specific helper classes
673 &generate_class_implementation_example ();
679 sub generate_dependents {
680 #print "generate_dependents\n";
681 for my $dependent (keys %dependents) {
682 if (! (-e "$dependent.cs") && -e "$path$dependent.idl" && $file != $dependent) {
683 print "generating $path$dependent.idl\n";
684 my $ret = `perl xpidl2cs.pl $dependent.idl $path $nosig`;
690 sub generate_class_implementation_example {
691 #print "generate_class_implementation_example\n";
692 my $name = $interface->{"class"};
693 my $interfacename = $interface->{"class"};
694 my $helpername = $name;
695 $helpername =~ s/nsI//;
696 my $parent = $interface->{"parent"};
698 print X "#if example\n\n";
699 print X "using System;\n";
700 print X "using System.Runtime.InteropServices;\n";
701 print X "using System.Runtime.CompilerServices;\n";
702 print X "using System.Text;\n";
705 print X "\tinternal class $helpername";
706 print X " : $interfacename";
710 print X "#region $interfacename\n";
712 my @items = split ",", $interface->{"items"};
713 for my $item (@items) {
715 if (&is_property ($item)) {
716 my $out = &get_out($properties{$item}->{"type"});
717 my $marshal = &get_marshal($properties{$item}->{"type"}, $out);
718 my $type = &get_type ($properties{$item}->{"type"}, $out);
720 my $retval = &get_return_value($type);
721 my $name = ucfirst ($item);
726 print X "[return: $marshal] " if $marshal;
727 print X "$type $interfacename.get$name ()\n";
729 print X "int $interfacename.get$name (";
730 print X "[$marshal] " if $marshal;
731 print X "$out $type ret)\n";
737 print X "return $retval;\n";
743 $type = &get_type ($properties{$item}->{"type"});
744 $retval = &get_return_value($type);
745 $marshal = &get_marshal($properties{$item}->{"type"});
748 if (&has_setter($item)) {
754 print X " $interfacename.set$name (";
755 print X "[$marshal] " if $marshal;
756 print X "$type value)\n";
762 print X "return $retval;\n";
770 my $type = &get_type ($methods{$item}->{"type"});
771 my $out = &get_out($methods{$item}->{"type"}) if $type;
772 my $marshal = &get_marshal($methods{$item}->{"type"}, $out) if $type;
773 $type = "void" if !$type;
777 print X "[return: $marshal] " if $marshal;
778 print X "$type $interfacename.$item (";
779 print X &get_params($item);
782 print X "int $interfacename.$item (";
783 print X &get_params($item);
790 print X "return $retval;\n";
798 print X "#endregion\n";
809 &generate_dependents ();