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 ##############################################################
33 #open FILE, '<', $path.$file or die "Can't open file $path$file";
49 $types{"short"} = {name => "short", out => "out", marshal => ""};
50 $types{"PRUint8"} = {name => "char", out => "out", marshal => ""};
51 $types{"PRInt8"} = {name => "char", out => "out", marshal => ""};
52 $types{"unsigned,short"} = {name => "ushort", out => "out", marshal => ""};
53 $types{"PRUint16"} = {name => "ushort", out => "out", marshal => ""};
54 $types{"PRInt16"} = {name => "short", out => "out", marshal => ""};
55 $types{"int"} = {name => "int", out => "out", marshal => ""};
56 $types{"nsresult"} = {name => "int", out => "out", marshal => ""};
57 $types{"unsigned,int"} = {name => "uint", out => "out", marshal => ""};
58 $types{"PRUint32"} = {name => "UInt32", out => "out", marshal => ""};
59 $types{"PRInt32"} = {name => "Int32", out => "out", marshal => ""};
60 $types{"PRInt64"} = {name => "long", out => "out", marshal => ""};
61 $types{"long"} = {name => "int", out => "out", marshal => ""};
62 $types{"size_t"} = {name => "int", out => "out", marshal => ""};
63 $types{"unsigned,long"} = {name => "uint", out => "out", marshal => ""};
64 $types{"float"} = {name => "float", out => "out", marshal => ""};
65 $types{"boolean"} = {name => "bool", out => "out", marshal => ""};
66 $types{"PRBool"} = {name => "bool", out => "out", marshal => ""};
67 $types{"void"} = {name => "", out => "", marshal => ""};
68 $types{"octet"} = {name => "byte", out => "out", marshal => ""};
69 $types{"octet[]"} = {name => "IntPtr", out => "out", marshal => " "};
70 $types{"byte"} = {name => "byte", out => "out", marshal => ""};
71 $types{"DOMString"} = {name => "/*DOMString*/ HandleRef", out => "", marshal => ""};
72 $types{"AUTF8String"} = {name => "/*AUTF8String*/ HandleRef", out => "", marshal => ""};
73 $types{"ACString"} = {name => "/*ACString*/ HandleRef", out => "", marshal => ""};
74 $types{"AString"} = {name => "/*AString*/ HandleRef", out => "", marshal => ""};
75 $types{"wstring"} = {name => "string", out => "", marshal => "MarshalAs(UnmanagedType.LPWStr)"};
76 $types{"nsCIDRef"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
77 $types{"nsIIDRef"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
78 $types{"Guid"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
79 $types{"nsCID"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
80 $types{"nsCIDPtr"} = {name => "Guid", out => "out", marshal => "MarshalAs (UnmanagedType.LPStruct)"};
81 $types{"string"} = {name => "string", out => "ref", marshal => "MarshalAs (UnmanagedType.LPStr)"};
82 $types{"refstring"} = {name => "IntPtr", out => "ref", marshal => ""};
83 $types{"charPtr"} = {name => "StringBuilder", out => "", marshal => ""};
84 $types{"voidPtr"} = {name => "IntPtr", out => "", marshal => ""};
85 $types{"nsISupports"} = {name => "IntPtr", out => "out", marshal =>"MarshalAs (UnmanagedType.Interface)"};
86 $types{"DOMTimeStamp"} = {name => "int", out => "out", marshal => ""};
87 $types{"nsWriteSegmentFun"} = {name => "nsIWriteSegmentFunDelegate", out => "", marshal => ""};
88 $types{"nsReadSegmentFun"} = {name => "nsIReadSegmentFunDelegate", out => "", marshal => ""};
89 $types{"nsTimerCallbackFunc"} = {name => "nsITimerCallbackDelegate", out => "", marshal => ""};
90 $types{"nsLoadFlags"} = {name => "ulong", out => "out", marshal => ""};
91 $types{"nsQIResult"} = {name => "IntPtr", out => "out", marshal => ""};
92 $types{"nsIIDPtr[]"} = {name => "IntPtr", out => "out", marshal => ""};
93 $types{"PRFileDescStar"} = {name => "IntPtr", out => "out", marshal => ""};
94 $types{"PRLibraryStar"} = {name => "IntPtr", out => "out", marshal => ""};
95 $types{"FILE"} = {name => "IntPtr", out => "out", marshal => ""};
96 $types{"nsIPresShell"} = {name => "/*nsIPresShell*/ IntPtr", out => "out", marshal => ""};
97 $types{"nsIDocument"} = {name => "/*nsIDocument*/ IntPtr", out => "out", marshal => ""};
98 $types{"nsIFrame"} = {name => "/*nsIFrame*/ IntPtr", out => "out", marshal => ""};
99 $types{"nsObjectFrame"} = {name => "/*nsObjectFrame*/ IntPtr", out => "out", marshal => ""};
100 $types{"nsIContent"} = {name => "/*nsIContent*/ IntPtr", out => "out", marshal => ""};
101 $types{"others"} = {name => "", out => "out", marshal => "MarshalAs (UnmanagedType.Interface)"};
104 $returnvalues{"short"} = {value => "0"};
105 $returnvalues{"ushort"} = {value => "0"};
106 $returnvalues{"int"} = {value => "0"};
107 $returnvalues{"uint"} = {value => "0"};
108 $returnvalues{"UInt32"} = {value => "0"};
109 $returnvalues{"Int32"} = {value => "0"};
110 $returnvalues{"long"} = {value => "0"};
111 $returnvalues{"ulong"} = {value => "0"};
112 $returnvalues{"IntPtr"} = {value => "0"};
113 $returnvalues{"float"} = {value => "0"};
114 $returnvalues{"byte"} = {value => "0"};
115 $returnvalues{"IntPtr"} = {value => "IntPtr.Zero"};
116 $returnvalues{"string"} = {value => "String.Empty"};
117 $returnvalues{"bool"} = {value => "false"};
118 $returnvalues{"/*DOMString*/ HandleRef"} = {value => "null"};
119 $returnvalues{"/*AUTF8String*/ HandleRef"} = {value => "null"};
120 $returnvalues{"ACString*/ HandleRef"} = {value => "null"};
121 $returnvalues{"/*AString*/ HandleRef"} = {value => "null"};
122 $returnvalues{""} = {value => ""};
123 $returnvalues{"others"} = {value => "null"};
126 $names{"event"} = {name => "_event"};
127 $names{"lock"} = {name => "_lock"};
131 my $class_implementation;
137 print STDERR << "EOF";
138 Usage: xpidl2cs.pl -f file -p path/to/idl [-nh -c class]
140 -f : idl file to parse, with extension
141 -p : path to the idl file directory
142 -n : generate files with no PreserveSig attribute (optional, defaults to adding the attribute)
143 -c : specific class to use inside the idl file (optional)
151 my $opts = 'f:p:c:n';
152 getopts( "$opts", \%opt ) or usage();
155 usage() if !$opt{f} or !$opt{p};
159 open FILE, '<', $path.$file or die "Can't open file $path$file";
161 $nosig = 1 if $opt{n};
174 #print "parse_parent\n";
177 print "Parsing parent $x\n";
178 `perl xpidl2cs.pl $x.idl $path $nosig`;
180 open my $f, '<', "$x.cs";
183 while (my $line = <$f>) {
186 if ($line =~ /#region/) {
188 $out .= $line . "\n";
191 elsif ($line =~ /\}/) {
195 $out .= $line . "\n";
203 #print "has_setter\n";
205 return !$properties{$x}->{"setter"};
212 if (exists $names{$x}) {
213 return $names{$x}->{"name"};
224 # print "arr = $arr ; out = $out ; name = $x\n";
227 if ($arr && exists $types{"$out$x\[\]"}) {
228 return $types{"$out$x\[\]"}->{"name"};
229 } elsif ($arr && exists $types{"$out$x"}) {
230 return $types{"$out$x"}->{"name"}."[]";
231 } elsif (exists $types{"$out$x"}) {
232 return $types{"$out$x"}->{"name"};
236 if (exists $types{$x} || ($arr && exists $types{"$x\[\]"})) {
237 if ($arr && exists $types{"$x\[\]"}) {
238 return $types{"$x\[\]"}->{"name"};
240 return $types{$x}->{"name"}."[]";
242 return $types{$x}->{"name"};
251 if (exists $types{$x}) {
252 return $types{$x}->{"out"};
254 return $types{"others"}->{"out"};
258 #print "get_marshal\n";
264 if ($arr && exists $types{"$out$x\[\]"}) {
265 return $types{"$out$x\[\]"}->{"marshal"};
266 } elsif (exists $types{"$out$x"}) {
267 return $types{"$out$x"}->{"marshal"};
271 if (exists $types{$x} || ($arr && exists $types{"$x\[\]"})) {
272 if ($arr && exists $types{"$x\[\]"}) {
273 return $types{"$x\[\]"}->{"marshal"};
275 return $types{$x}->{"marshal"};
279 return $types{"others"}->{"marshal"};
282 sub get_return_value {
283 #print "get_return_value\n";
285 if (exists $returnvalues{$x}) {
286 return $returnvalues{$x}->{"value"};
288 return $returnvalues{"others"}->{"value"};
293 #print "is_property\n";
295 return (exists $properties{$x});
299 #print "add_external\n";
301 if ($x !~ /nsISupports/ && !exists $types{$x} && !exists $dependents{$x}) {
302 $dependents{$x} = $x;
304 # print "add_external $x\n";
308 #print "get_params\n";
311 #print $methods{$x}->{"params"}."\n";
312 my @params = split /,/, $methods{$x}->{"params"};
313 my $lastoutparam = "";
316 #print "params:@params:\n";
317 for my $param (@params) {
325 # print "param:$param:\n";
326 my @p = split (" ", $param);
328 # need to backtrack to a previous parameter defined by iid_is(name) and
329 # replace the type of this one with that. who the $%#@ came up with this idea? le sigh.
331 if (@p[0] =~ m/iid_is/) {
333 $name = &get_name (@p[0]);
335 $type = $list{$name}->{"type"};
336 $marshal = $list{$name}->{"marshal"};
337 $marshal = " " if !$marshal;
339 until (scalar(@p) == 3) {
344 if (@p[0] =~ m/array/ || @p[1] =~ m/array/) {
345 until (scalar(@p) == 3) {
348 $isout = 1 if (@p[0] =~ m/out/);
350 $marshal = &get_marshal (@p[0], "", 1);
351 $type = &get_type(@p[0], "", 1);
354 shift @p unless @p[0] =~ /(in|out)/;
355 $isout = 1 if (@p[0] =~ m/out/);
356 shift @p unless scalar(@p) <= 2;
358 # if an out parameter is of type nsQIResult, that means
359 # it will return a pointer to an interface (that can be anything).
360 # That means we want to return an IntPtr, and later cast it to
361 # the proper type, so reset type and marshalling
362 if ($isout && @p[0] =~ /nsQIResult/) {
368 $type = join ",", @p[0..@p-2];
370 until (scalar(@p) == 1) {
374 $marshal = &get_marshal ($type);
375 $marshal = " " if !$marshal;
376 $type = &get_type ($type);
377 $name = &get_name (@p[0]);
379 #print "marshal:$marshal\ttype:$type\tname:$name\n";
380 $out = &get_out($type) if $isout;
382 $type = &get_type (@p[0]) unless $type;
383 shift @p unless scalar(@p) == 1;
384 $marshal = &get_marshal ($type) unless $marshal;
385 $name = &get_name (@p[0]) unless $name;
387 #print "marshal:$marshal\ttype:$type\tname:$name\n";
397 &add_external ($type);
399 $marshal = "" if $marshal eq " ";
401 # my $tmp = "\n\t\t\t\t";
403 $tmp .= "[$marshal] " if $marshal;
404 $tmp .= "$out $type $name";
406 $lastoutparam = $name if $isout;
410 #print "$methods{$x}->{\"type\"}\n";
411 #print "nosig:$nosig;x:$x;type:" . &get_type ($methods{$x}->{"type"}) . ";\n";
412 if (!$nosig && $x !~ /void/ && &get_type ($methods{$x}->{"type"}) ne "") {
413 $type = $methods{$x}->{"type"};
414 $type =~ s/\[.*\],//;
415 $marshal = &get_marshal ($type);
418 $tmp = "[$marshal] " if $marshal;
419 $tmp .= &get_out($type);
420 $tmp .= " " . &get_type ($type);
422 #print "tmp 2:$tmp\n";
425 &add_external ($type);
428 if ($nosig && &get_type ($methods{$x}->{"type"}) eq "" && $lastoutparam) {
429 $methods{$x}->{"type"} = $list{$lastoutparam}->{"type"};
434 return join (",\n\t\t\t\t", @ret);
438 #print "parse_file\n";
446 while (my $line = <FILE>) {
449 next if !$start && $line !~ /uuid\(/;
451 last if $start && $line =~ /\};/;
455 if (index($line, "/*") > -1) {
459 if ($comment && index($line, "*/") > -1) {
466 if (index($line, "*") == -1 && index ($line, "//") == -1 && index ($line, "#include") == -1) {
468 $line =~ s/\[noscript\] //;
470 if (index ($line, "uuid(") != -1) {
472 $uuid =~ s/\[.*uuid\((.*)\)\]/\1/;
473 $interface->{"uuid"} = $uuid;
476 elsif (index($line, "interface") != -1) {
478 $class =~ s/interface ([^\:|\s]+)\s*:\s*(.*)/\1/;
479 # print "\t\tclass:$class\n";
480 # print "\t\t_class:$_class\n";
481 if ($_class && $_class !~ $class) {
494 $parent =~ s/([^\:]+):\s*(.*)[\s|\{]/\2/;
495 # print "\t\tparent:$parent\n";
496 $interface->{"class"} = $class;
497 $interface->{"parent"} = $parent;
499 elsif (index ($line, "const") != -1 && index ($line, "[") == -1) {
502 elsif (index ($line, "attribute") != -1) {
503 my $att = substr($line, index($line, "attribute") + 10);
505 my @atts = split / /, $att;
507 my $name = pop @atts;
509 # print $name . "\n";
510 my @nospaces = grep /[^ ]/, @atts;
511 my $type = join ",", @nospaces;
514 if (index ($line, "readonly") != -1) {
517 # print $type . "\n";
518 $properties{$name} = {type => $type, setter => $setter};
519 $interface->{"items"} .= $name . ",";
521 elsif ($line !~ m/[{|}]/ && $line =~ m/./) {
522 # print $line . "\n";
525 my $m = substr($line, 0, index($line, "("));
526 my @atts = split / /, $m;
530 # print "name=$mname\n";
531 my @nospaces = grep /[^ ]/, @atts;
532 $mtype = join ",", @nospaces;
533 $mtype =~ s/\[.*\],//;
534 # print "type=$mtype\n";
535 $mparams .= substr($line, index($line, "(") + 1);
539 @atts = split / /, $mparams;
540 @nospaces = grep /[^ ]/, @atts;
541 $mparams = join " ", @nospaces;
542 # print "params=>$mparams\n";
545 elsif (index ($line, "raises") == -1) {
549 my @atts = split / /, $mparams;
550 my @nospaces = grep /[^ ]/, @atts;
551 $mparams = join " ", @nospaces;
552 # print "params=>$mparams\n";
554 if (index ($line, ";") != -1) {
556 $mparams =~ s/\[([^\]]+),([^\]]+),([^\]]+)\]/\1 \2 \3/;
557 $mparams =~ s/\[([^\]]+),([^\]]+)\]/\1 \2/;
560 $mparams =~ s/retval//;
562 $methods{$mname} = {type => $mtype, params => $mparams};
563 $interface->{"items"} .= $mname . ",";
564 # print "params=>$mparams\n";
577 my $name = $interface->{"class"};
580 print X "// THIS FILE AUTOMATICALLY GENERATED BY xpidl2cs.pl\n";
581 print X "// EDITING IS PROBABLY UNWISE\n";
582 print X "// Permission is hereby granted, free of charge, to any person obtaining\n";
583 print X "// a copy of this software and associated documentation files (the\n";
584 print X "// \"Software\"), to deal in the Software without restriction, including\n";
585 print X "// without limitation the rights to use, copy, modify, merge, publish,\n";
586 print X "// distribute, sublicense, and/or sell copies of the Software, and to\n";
587 print X "// permit persons to whom the Software is furnished to do so, subject to\n";
588 print X "// the following conditions:\n";
590 print X "// The above copyright notice and this permission notice shall be\n";
591 print X "// included in all copies or substantial portions of the Software.\n";
593 print X "// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n";
594 print X "// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n";
595 print X "// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n";
596 print X "// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n";
597 print X "// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n";
598 print X "// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n";
599 print X "// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n";
601 print X "// Copyright (c) 2007, 2008 Novell, Inc.\n";
603 print X "// Authors:\n";
604 print X "// Andreia Gaita (avidigal\@novell.com)\n";
607 print X "using System;\n";
608 print X "using System.Runtime.InteropServices;\n";
609 print X "using System.Runtime.CompilerServices;\n";
610 print X "using System.Text;\n";
612 print X "namespace Mono.Mozilla {\n";
615 my $uuid = $interface->{"uuid"};
616 my $parent = $interface->{"parent"};
617 print X "\t[Guid (\"$uuid\")]\n";
618 print X "\t[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]\n";
619 print X "\t[ComImport ()]\n";
620 print X "\tinternal interface $name";
621 print X " : $parent" if $parent !~ /nsISupports/;
624 if ($parent !~ /nsISupports/) {
625 print X &parse_parent ($parent);
628 print X "#region $name\n";
630 my @items = split ",", $interface->{"items"};
631 for my $item (@items) {
634 print X "\t\t[PreserveSigAttribute]\n";
636 print X "\t\t[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]\n";
638 if (&is_property ($item)) {
639 my $out = &get_out($properties{$item}->{"type"});
640 my $marshal = &get_marshal($properties{$item}->{"type"}, $out);
641 my $type = &get_type ($properties{$item}->{"type"}, $out);
642 my $name = ucfirst ($item);
644 &add_external ($properties{$item}->{"type"});
648 print X "[return: $marshal] " if $marshal;
649 print X "$type get$name ();\n";
651 print X "int get$name (";
652 print X "[$marshal] " if $marshal;
653 print X "$out $type ret);\n";
657 $type = &get_type ($properties{$item}->{"type"});
658 $marshal = &get_marshal($properties{$item}->{"type"});
661 if (&has_setter($item)) {
663 print X "\t\t[PreserveSigAttribute]\n";
665 print X "\t\t[MethodImpl (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]\n";
671 print X " set$name (";
672 print X "[$marshal] " if $marshal;
673 print X "$type value);\n";
678 my $type = &get_type ($methods{$item}->{"type"});
679 my $out = &get_out($methods{$item}->{"type"}) if $type;
680 my $marshal = &get_marshal($methods{$item}->{"type"}, $out) if $type;
681 $type = "void" if !$type;
685 print X "[return: $marshal] " if $marshal;
686 print X "$type $item (";
687 print X &get_params($item);
690 print X "int $item (";
691 print X &get_params($item);
697 print X "#endregion\n";
701 # mozilla-specific helper classes to proxy objects between threads
702 # remove if you're not running this for mono.mozilla
706 $helpername =~ s/nsI/ns/;
707 print X "\tinternal class $helpername";
709 print X "\t\tpublic static $name GetProxy (Mono.WebBrowser.IWebBrowser control, $name obj)\n";
711 print X "\t\t\tobject o = Base.GetProxyForObject (control, typeof($name).GUID, obj);\n";
712 print X "\t\t\treturn o as $name;\n";
716 #end of mozilla-specific helper classes
721 &generate_class_implementation_example ();
727 sub generate_dependents {
728 #print "generate_dependents\n";
729 for my $dependent (keys %dependents) {
730 if (! (-e "$dependent.cs") && -e "$path$dependent.idl" && $file != $dependent) {
731 print "generating $path$dependent.idl\n";
732 my $cmd = "perl xpidl2cs.pl -f $dependent.idl -p $path";
733 $cmd .= "-n" if $nosig;
740 sub generate_class_implementation_example {
741 #print "generate_class_implementation_example\n";
742 my $name = $interface->{"class"};
743 my $interfacename = $interface->{"class"};
744 my $helpername = $name;
745 $helpername =~ s/nsI//;
746 my $parent = $interface->{"parent"};
748 print X "#if example\n\n";
749 print X "using System;\n";
750 print X "using System.Runtime.InteropServices;\n";
751 print X "using System.Runtime.CompilerServices;\n";
752 print X "using System.Text;\n";
755 print X "\tinternal class $helpername";
756 print X " : $interfacename";
760 print X "#region $interfacename\n";
762 my @items = split ",", $interface->{"items"};
763 for my $item (@items) {
765 if (&is_property ($item)) {
766 my $out = &get_out($properties{$item}->{"type"});
767 my $marshal = &get_marshal($properties{$item}->{"type"}, $out);
768 my $type = &get_type ($properties{$item}->{"type"}, $out);
770 my $retval = &get_return_value($type);
771 my $name = ucfirst ($item);
776 print X "[return: $marshal] " if $marshal;
777 print X "$type $interfacename.get$name ()\n";
779 print X "int $interfacename.get$name (";
780 print X "[$marshal] " if $marshal;
781 print X "$out $type ret)\n";
787 print X "return $retval;\n";
793 $type = &get_type ($properties{$item}->{"type"});
794 $retval = &get_return_value($type);
795 $marshal = &get_marshal($properties{$item}->{"type"});
798 if (&has_setter($item)) {
804 print X " $interfacename.set$name (";
805 print X "[$marshal] " if $marshal;
806 print X "$type value)\n";
812 print X "return $retval;\n";
820 my $type = &get_type ($methods{$item}->{"type"});
821 my $out = &get_out($methods{$item}->{"type"}) if $type;
822 my $marshal = &get_marshal($methods{$item}->{"type"}, $out) if $type;
823 $type = "void" if !$type;
827 print X "[return: $marshal] " if $marshal;
828 print X "$type $interfacename.$item (";
829 print X &get_params($item);
832 print X "int $interfacename.$item (";
833 print X &get_params($item);
840 print X "return $retval;\n";
848 print X "#endregion\n";
859 &generate_dependents ();