r19585: Add support for some more standard IDL instructions:
[ira/wip.git] / source / pidl / lib / Parse / Pidl / NDR.pm
1 ###################################################
2 # Samba4 NDR info tree generator
3 # Copyright tridge@samba.org 2000-2003
4 # Copyright tpot@samba.org 2001
5 # Copyright jelmer@samba.org 2004-2006
6 # released under the GNU GPL
7
8 =pod
9
10 =head1 NAME
11
12 Parse::Pidl::NDR - NDR parsing information generator
13
14 =head1 DESCRIPTION
15
16 Return a table describing the order in which the parts of an element
17 should be parsed
18 Possible level types:
19  - POINTER
20  - ARRAY
21  - SUBCONTEXT
22  - SWITCH
23  - DATA
24
25 =head1 AUTHOR
26
27 Jelmer Vernooij <jelmer@samba.org>
28
29 =cut
30
31 package Parse::Pidl::NDR;
32
33 require Exporter;
34 use vars qw($VERSION);
35 $VERSION = '0.01';
36 @ISA = qw(Exporter);
37 @EXPORT = qw(GetPrevLevel GetNextLevel ContainsDeferred ContainsString);
38
39 use strict;
40 use Parse::Pidl::Typelist qw(hasType getType expandAlias);
41 use Parse::Pidl::Util qw(has_property property_matches);
42
43 # Alignment of the built-in scalar types
44 my $scalar_alignment = {
45         'void' => 0,
46         'char' => 1,
47         'int8' => 1,
48         'uint8' => 1,
49         'int16' => 2,
50         'uint16' => 2,
51         'int32' => 4,
52         'uint32' => 4,
53         'hyper' => 8,
54         'pointer' => 8,
55         'dlong' => 4,
56         'udlong' => 4,
57         'udlongr' => 4,
58         'DATA_BLOB' => 4,
59         'string' => 4,
60         'string_array' => 4, #???
61         'time_t' => 4,
62         'NTTIME' => 4,
63         'NTTIME_1sec' => 4,
64         'NTTIME_hyper' => 8,
65         'WERROR' => 4,
66         'NTSTATUS' => 4,
67         'COMRESULT' => 4,
68         'nbt_string' => 4,
69         'wrepl_nbt_name' => 4,
70         'ipv4address' => 4
71 };
72
73
74
75 sub nonfatal($$)
76 {
77         my ($e,$s) = @_;
78         warn ("$e->{FILE}:$e->{LINE}: warning: $s\n");
79 }
80
81 #####################################################################
82 # signal a fatal validation error
83 sub fatal($$)
84 {
85         my ($pos,$s) = @_;
86         die("$pos->{FILE}:$pos->{LINE}:$s\n");
87 }
88
89 sub GetElementLevelTable($)
90 {
91         my $e = shift;
92
93         my $order = [];
94         my $is_deferred = 0;
95         my @bracket_array = ();
96         my @length_is = ();
97         my @size_is = ();
98         my $pointer_idx = 0;
99
100         if (has_property($e, "size_is")) {
101                 @size_is = split /,/, has_property($e, "size_is");
102         }
103
104         if (has_property($e, "length_is")) {
105                 @length_is = split /,/, has_property($e, "length_is");
106         }
107
108         if (defined($e->{ARRAY_LEN})) {
109                 @bracket_array = @{$e->{ARRAY_LEN}};
110         }
111
112         if (has_property($e, "out")) {
113                 my $needptrs = 1;
114
115                 if (has_property($e, "string")) { $needptrs++; }
116                 if ($#bracket_array >= 0) { $needptrs = 0; }
117
118                 nonfatal($e, "[out] argument `$e->{NAME}' not a pointer") if ($needptrs > $e->{POINTERS});
119         }
120
121         # Parse the [][][][] style array stuff
122         for my $i (0 .. $#bracket_array) {
123                 my $d = $bracket_array[$#bracket_array - $i];
124                 my $size = $d;
125                 my $length = $d;
126                 my $is_surrounding = 0;
127                 my $is_varying = 0;
128                 my $is_conformant = 0;
129                 my $is_string = 0;
130
131                 if ($d eq "*") {
132                         $is_conformant = 1;
133                         if ($size = shift @size_is) {
134                         } elsif ((scalar(@size_is) == 0) and has_property($e, "string")) {
135                                 $is_string = 1;
136                                 delete($e->{PROPERTIES}->{string});
137                         } else {
138                                 print "$e->{FILE}:$e->{LINE}: Must specify size_is() for conformant array!\n";
139                                 exit 1;
140                         }
141
142                         if (($length = shift @length_is) or $is_string) {
143                                 $is_varying = 1;
144                         } else {
145                                 $length = $size;
146                         }
147
148                         if ($e == $e->{PARENT}->{ELEMENTS}[-1] 
149                                 and $e->{PARENT}->{TYPE} ne "FUNCTION") {
150                                 $is_surrounding = 1;
151                         }
152                 }
153
154                 push (@$order, {
155                         TYPE => "ARRAY",
156                         SIZE_IS => $size,
157                         LENGTH_IS => $length,
158                         IS_DEFERRED => "$is_deferred",
159                         IS_SURROUNDING => "$is_surrounding",
160                         IS_ZERO_TERMINATED => "$is_string",
161                         IS_VARYING => "$is_varying",
162                         IS_CONFORMANT => "$is_conformant",
163                         IS_FIXED => (not $is_conformant and Parse::Pidl::Util::is_constant($size)),
164                         IS_INLINE => (not $is_conformant and not Parse::Pidl::Util::is_constant($size))
165                 });
166         }
167
168         # Next, all the pointers
169         foreach my $i (1..$e->{POINTERS}) {
170                 my $pt = pointer_type($e);
171
172                 my $level = "EMBEDDED";
173                 # Top level "ref" pointers do not have a referrent identifier
174                 $level = "TOP" if ( defined($pt) 
175                                 and $i == 1
176                                 and $e->{PARENT}->{TYPE} eq "FUNCTION");
177
178                 push (@$order, { 
179                         TYPE => "POINTER",
180                         # for now, there can only be one pointer type per element
181                         POINTER_TYPE => pointer_type($e),
182                         POINTER_INDEX => $pointer_idx,
183                         IS_DEFERRED => "$is_deferred",
184                         LEVEL => $level
185                 });
186
187                 $pointer_idx++;
188                 
189                 # everything that follows will be deferred
190                 $is_deferred = 1 if ($e->{PARENT}->{TYPE} ne "FUNCTION");
191
192                 my $array_size = shift @size_is;
193                 my $array_length;
194                 my $is_varying;
195                 my $is_conformant;
196                 my $is_string = 0;
197                 if ($array_size) {
198                         $is_conformant = 1;
199                         if ($array_length = shift @length_is) {
200                                 $is_varying = 1;
201                         } else {
202                                 $array_length = $array_size;
203                                 $is_varying =0;
204                         }
205                 } 
206                 
207                 if (scalar(@size_is) == 0 and has_property($e, "string") and 
208                     $i == $e->{POINTERS}) {
209                         $is_string = 1;
210                         $is_varying = $is_conformant = has_property($e, "noheader")?0:1;
211                         delete($e->{PROPERTIES}->{string});
212                 }
213
214                 if ($array_size or $is_string) {
215                         push (@$order, {
216                                 TYPE => "ARRAY",
217                                 IS_ZERO_TERMINATED => "$is_string",
218                                 SIZE_IS => $array_size,
219                                 LENGTH_IS => $array_length,
220                                 IS_DEFERRED => "$is_deferred",
221                                 IS_SURROUNDING => 0,
222                                 IS_VARYING => "$is_varying",
223                                 IS_CONFORMANT => "$is_conformant",
224                                 IS_FIXED => 0,
225                                 IS_INLINE => 0,
226                         });
227
228                         $is_deferred = 0;
229                 } 
230         }
231
232         if (defined(has_property($e, "subcontext"))) {
233                 my $hdr_size = has_property($e, "subcontext");
234                 my $subsize = has_property($e, "subcontext_size");
235                 if (not defined($subsize)) { 
236                         $subsize = -1; 
237                 }
238                 
239                 push (@$order, {
240                         TYPE => "SUBCONTEXT",
241                         HEADER_SIZE => $hdr_size,
242                         SUBCONTEXT_SIZE => $subsize,
243                         IS_DEFERRED => $is_deferred,
244                         COMPRESSION => has_property($e, "compression"),
245                 });
246         }
247
248         if (my $switch = has_property($e, "switch_is")) {
249                 push (@$order, {
250                         TYPE => "SWITCH", 
251                         SWITCH_IS => $switch,
252                         IS_DEFERRED => $is_deferred
253                 });
254         }
255
256         if (scalar(@size_is) > 0) {
257                 nonfatal($e, "size_is() on non-array element");
258         }
259
260         if (scalar(@length_is) > 0) {
261                 nonfatal($e, "length_is() on non-array element");
262         }
263
264         if (has_property($e, "string")) {
265                 nonfatal($e, "string() attribute on non-array element");
266         }
267
268         push (@$order, {
269                 TYPE => "DATA",
270                 CONVERT_TO => has_property($e, ""),
271                 CONVERT_FROM => has_property($e, ""),
272                 DATA_TYPE => $e->{TYPE},
273                 IS_DEFERRED => $is_deferred,
274                 CONTAINS_DEFERRED => can_contain_deferred($e),
275                 IS_SURROUNDING => 0 #FIXME
276         });
277
278         my $i = 0;
279         foreach (@$order) { $_->{LEVEL_INDEX} = $i; $i+=1; }
280
281         return $order;
282 }
283
284 #####################################################################
285 # see if a type contains any deferred data 
286 sub can_contain_deferred
287 {
288         my $e = shift;
289
290         return 0 if (Parse::Pidl::Typelist::is_scalar($e->{TYPE}));
291         return 1 unless (hasType($e->{TYPE})); # assume the worst
292
293         my $type = getType($e->{TYPE});
294
295         foreach my $x (@{$type->{DATA}->{ELEMENTS}}) {
296                 return 1 if ($x->{POINTERS});
297                 return 1 if (can_contain_deferred ($x));
298         }
299         
300         return 0;
301 }
302
303 sub pointer_type($)
304 {
305         my $e = shift;
306
307         return undef unless $e->{POINTERS};
308         
309         return "ref" if (has_property($e, "ref"));
310         return "ptr" if (has_property($e, "ptr"));
311         return "sptr" if (has_property($e, "sptr"));
312         return "unique" if (has_property($e, "unique"));
313         return "relative" if (has_property($e, "relative"));
314         return "ignore" if (has_property($e, "ignore"));
315
316         return undef;
317 }
318
319 #####################################################################
320 # work out the correct alignment for a structure or union
321 sub find_largest_alignment($)
322 {
323         my $s = shift;
324
325         my $align = 1;
326         for my $e (@{$s->{ELEMENTS}}) {
327                 my $a = 1;
328
329                 if ($e->{POINTERS}) {
330                         $a = 4; 
331                 } elsif (has_property($e, "subcontext")) { 
332                         $a = 1;
333                 } elsif (has_property($e, "transmit_as")) {
334                         $a = align_type($e->{PROPERTIES}->{transmit_as});
335                 } else {
336                         $a = align_type($e->{TYPE}); 
337                 }
338
339                 $align = $a if ($align < $a);
340         }
341
342         return $align;
343 }
344
345 #####################################################################
346 # align a type
347 sub align_type($)
348 {
349         sub align_type($);
350         my $e = shift;
351
352         unless (hasType($e)) {
353             # it must be an external type - all we can do is guess 
354                 # print "Warning: assuming alignment of unknown type '$e' is 4\n";
355             return 4;
356         }
357
358         my $dt = getType($e)->{DATA};
359
360         if ($dt->{TYPE} eq "ENUM") {
361                 return align_type(Parse::Pidl::Typelist::enum_type_fn($dt));
362         } elsif ($dt->{TYPE} eq "BITMAP") {
363                 return align_type(Parse::Pidl::Typelist::bitmap_type_fn($dt));
364         } elsif (($dt->{TYPE} eq "STRUCT") or ($dt->{TYPE} eq "UNION")) {
365                 return find_largest_alignment($dt);
366         } elsif ($dt->{TYPE} eq "SCALAR") {
367                 return $scalar_alignment->{$dt->{NAME}};
368         }
369
370         die("Unknown data type type $dt->{TYPE}");
371 }
372
373 sub ParseElement($)
374 {
375         my $e = shift;
376
377         $e->{TYPE} = expandAlias($e->{TYPE});
378
379         return {
380                 NAME => $e->{NAME},
381                 TYPE => $e->{TYPE},
382                 PROPERTIES => $e->{PROPERTIES},
383                 LEVELS => GetElementLevelTable($e),
384                 REPRESENTATION_TYPE => $e->{PROPERTIES}->{represent_as},
385                 ALIGN => align_type($e->{TYPE}),
386                 ORIGINAL => $e
387         };
388 }
389
390 sub ParseStruct($$)
391 {
392         my ($ndr,$struct) = @_;
393         my @elements = ();
394         my $surrounding = undef;
395
396         foreach my $x (@{$struct->{ELEMENTS}}) 
397         {
398                 my $e = ParseElement($x);
399                 if ($x != $struct->{ELEMENTS}[-1] and 
400                         $e->{LEVELS}[0]->{IS_SURROUNDING}) {
401                         print "$x->{FILE}:$x->{LINE}: error: conformant member not at end of struct\n";
402                 }
403                 push @elements, $e;
404         }
405
406         my $e = $elements[-1];
407         if (defined($e) and defined($e->{LEVELS}[0]->{IS_SURROUNDING}) and
408                 $e->{LEVELS}[0]->{IS_SURROUNDING}) {
409                 $surrounding = $e;
410         }
411
412         if (defined $e->{TYPE} && $e->{TYPE} eq "string"
413             &&  property_matches($e, "flag", ".*LIBNDR_FLAG_STR_CONFORMANT.*")) {
414                 $surrounding = $struct->{ELEMENTS}[-1];
415         }
416                 
417         return {
418                 TYPE => "STRUCT",
419                 SURROUNDING_ELEMENT => $surrounding,
420                 ELEMENTS => \@elements,
421                 PROPERTIES => $struct->{PROPERTIES},
422                 ORIGINAL => $struct
423         };
424 }
425
426 sub ParseUnion($$)
427 {
428         my ($ndr,$e) = @_;
429         my @elements = ();
430         my $switch_type = has_property($e, "switch_type");
431         unless (defined($switch_type)) { $switch_type = "uint32"; }
432
433         if (has_property($e, "nodiscriminant")) { $switch_type = undef; }
434         
435         my $hasdefault = 0;
436         foreach my $x (@{$e->{ELEMENTS}}) 
437         {
438                 my $t;
439                 if ($x->{TYPE} eq "EMPTY") {
440                         $t = { TYPE => "EMPTY" };
441                 } else {
442                         $t = ParseElement($x);
443                 }
444                 if (has_property($x, "default")) {
445                         $t->{CASE} = "default";
446                         $hasdefault = 1;
447                 } elsif (defined($x->{PROPERTIES}->{case})) {
448                         $t->{CASE} = "case $x->{PROPERTIES}->{case}";
449                 } else {
450                         die("Union element $x->{NAME} has neither default nor case property");
451                 }
452                 push @elements, $t;
453         }
454
455         return {
456                 TYPE => "UNION",
457                 SWITCH_TYPE => $switch_type,
458                 ELEMENTS => \@elements,
459                 PROPERTIES => $e->{PROPERTIES},
460                 HAS_DEFAULT => $hasdefault,
461                 ORIGINAL => $e
462         };
463 }
464
465 sub ParseEnum($$)
466 {
467         my ($ndr,$e) = @_;
468
469         return {
470                 TYPE => "ENUM",
471                 BASE_TYPE => Parse::Pidl::Typelist::enum_type_fn($e),
472                 ELEMENTS => $e->{ELEMENTS},
473                 PROPERTIES => $e->{PROPERTIES},
474                 ORIGINAL => $e
475         };
476 }
477
478 sub ParseBitmap($$)
479 {
480         my ($ndr,$e) = @_;
481
482         return {
483                 TYPE => "BITMAP",
484                 BASE_TYPE => Parse::Pidl::Typelist::bitmap_type_fn($e),
485                 ELEMENTS => $e->{ELEMENTS},
486                 PROPERTIES => $e->{PROPERTIES},
487                 ORIGINAL => $e
488         };
489 }
490
491 sub ParseType($$)
492 {
493         my ($ndr, $d) = @_;
494
495         if ($d->{TYPE} eq "STRUCT" or $d->{TYPE} eq "UNION") {
496                 CheckPointerTypes($d, $ndr->{PROPERTIES}->{pointer_default});
497         }
498
499         my $data = {
500                 STRUCT => \&ParseStruct,
501                 UNION => \&ParseUnion,
502                 ENUM => \&ParseEnum,
503                 BITMAP => \&ParseBitmap,
504                 TYPEDEF => \&ParseTypedef,
505         }->{$d->{TYPE}}->($ndr, $d);
506
507         return $data;
508 }
509
510 sub ParseTypedef($$)
511 {
512         my ($ndr,$d) = @_;
513
514         if (defined($d->{PROPERTIES}) && !defined($d->{DATA}->{PROPERTIES})) {
515                 $d->{DATA}->{PROPERTIES} = $d->{PROPERTIES};
516         }
517
518         my $data = ParseType($ndr, $d->{DATA});
519         $data->{ALIGN} = align_type($d->{NAME});
520
521         return {
522                 NAME => $d->{NAME},
523                 TYPE => $d->{TYPE},
524                 PROPERTIES => $d->{PROPERTIES},
525                 DATA => $data,
526                 ORIGINAL => $d
527         };
528 }
529
530 sub ParseConst($$)
531 {
532         my ($ndr,$d) = @_;
533
534         return $d;
535 }
536
537 sub ParseFunction($$$)
538 {
539         my ($ndr,$d,$opnum) = @_;
540         my @elements = ();
541         my $rettype = undef;
542         my $thisopnum = undef;
543
544         CheckPointerTypes($d, $ndr->{PROPERTIES}->{pointer_default_top});
545
546         if (not defined($d->{PROPERTIES}{noopnum})) {
547                 $thisopnum = ${$opnum};
548                 ${$opnum}++;
549         }
550
551         foreach my $x (@{$d->{ELEMENTS}}) {
552                 my $e = ParseElement($x);
553                 push (@{$e->{DIRECTION}}, "in") if (has_property($x, "in"));
554                 push (@{$e->{DIRECTION}}, "out") if (has_property($x, "out"));
555
556                 push (@elements, $e);
557         }
558
559         if ($d->{RETURN_TYPE} ne "void") {
560                 $rettype = expandAlias($d->{RETURN_TYPE});
561         }
562         
563         my $async = 0;
564         if (has_property($d, "async")) { $async = 1; }
565         
566         return {
567                         NAME => $d->{NAME},
568                         TYPE => "FUNCTION",
569                         OPNUM => $thisopnum,
570                         ASYNC => $async,
571                         RETURN_TYPE => $rettype,
572                         PROPERTIES => $d->{PROPERTIES},
573                         ELEMENTS => \@elements,
574                         ORIGINAL => $d
575                 };
576 }
577
578 sub CheckPointerTypes($$)
579 {
580         my ($s,$default) = @_;
581
582         foreach my $e (@{$s->{ELEMENTS}}) {
583                 if ($e->{POINTERS} and not defined(pointer_type($e))) {
584                         $e->{PROPERTIES}->{$default} = 1;
585                 }
586         }
587 }
588
589 sub ParseInterface($)
590 {
591         my $idl = shift;
592         my @types = ();
593         my @consts = ();
594         my @functions = ();
595         my @endpoints;
596         my @declares = ();
597         my $opnum = 0;
598         my $version;
599
600         if (not has_property($idl, "pointer_default")) {
601                 # MIDL defaults to "ptr" in DCE compatible mode (/osf)
602                 # and "unique" in Microsoft Extensions mode (default)
603                 $idl->{PROPERTIES}->{pointer_default} = "unique";
604         }
605
606         if (not has_property($idl, "pointer_default_top")) {
607                 $idl->{PROPERTIES}->{pointer_default_top} = "ref";
608         }
609
610         foreach my $d (@{$idl->{DATA}}) {
611                 if ($d->{TYPE} eq "DECLARE") {
612                         push (@declares, $d);
613                 } elsif ($d->{TYPE} eq "FUNCTION") {
614                         push (@functions, ParseFunction($idl, $d, \$opnum));
615                 } elsif ($d->{TYPE} eq "CONST") {
616                         push (@consts, ParseConst($idl, $d));
617                 } else {
618                         push (@types, ParseType($idl, $d));
619                 }
620         }
621
622         $version = "0.0";
623
624         if(defined $idl->{PROPERTIES}->{version}) { 
625                 $version = $idl->{PROPERTIES}->{version}; 
626         }
627
628         # If no endpoint is set, default to the interface name as a named pipe
629         if (!defined $idl->{PROPERTIES}->{endpoint}) {
630                 push @endpoints, "\"ncacn_np:[\\\\pipe\\\\" . $idl->{NAME} . "]\"";
631         } else {
632                 @endpoints = split / /, $idl->{PROPERTIES}->{endpoint};
633         }
634
635         return { 
636                 NAME => $idl->{NAME},
637                 UUID => lc(has_property($idl, "uuid")),
638                 VERSION => $version,
639                 TYPE => "INTERFACE",
640                 PROPERTIES => $idl->{PROPERTIES},
641                 FUNCTIONS => \@functions,
642                 CONSTS => \@consts,
643                 TYPES => \@types,
644                 DECLARES => \@declares,
645                 ENDPOINTS => \@endpoints
646         };
647 }
648
649 # Convert a IDL tree to a NDR tree
650 # Gives a result tree describing all that's necessary for easily generating
651 # NDR parsers / generators
652 sub Parse($)
653 {
654         my $idl = shift;
655
656         return undef unless (defined($idl));
657
658         Parse::Pidl::NDR::Validate($idl);
659         
660         my @ndr = ();
661
662         foreach (@{$idl}) {
663                 ($_->{TYPE} eq "INTERFACE") && push(@ndr, ParseInterface($_));
664                 ($_->{TYPE} eq "IMPORT") && push(@ndr, $_);
665         }
666
667         return \@ndr;
668 }
669
670 sub GetNextLevel($$)
671 {
672         my $e = shift;
673         my $fl = shift;
674
675         my $seen = 0;
676
677         foreach my $l (@{$e->{LEVELS}}) {
678                 return $l if ($seen);
679                 ($seen = 1) if ($l == $fl);
680         }
681
682         return undef;
683 }
684
685 sub GetPrevLevel($$)
686 {
687         my ($e,$fl) = @_;
688         my $prev = undef;
689
690         foreach my $l (@{$e->{LEVELS}}) {
691                 (return $prev) if ($l == $fl);
692                 $prev = $l;
693         }
694
695         return undef;
696 }
697
698 sub ContainsString($)
699 {
700         my ($e) = @_;
701
702         foreach my $l (@{$e->{LEVELS}}) {
703                 return 1 if ($l->{TYPE} eq "ARRAY" and $l->{IS_ZERO_TERMINATED});
704         }
705
706         return 0;
707 }
708
709 sub ContainsDeferred($$)
710 {
711         my ($e,$l) = @_;
712
713         return 1 if ($l->{CONTAINS_DEFERRED});
714
715         while ($l = GetNextLevel($e,$l))
716         {
717                 return 1 if ($l->{IS_DEFERRED}); 
718                 return 1 if ($l->{CONTAINS_DEFERRED});
719         } 
720         
721         return 0;
722 }
723
724 sub el_name($)
725 {
726         my $e = shift;
727
728         if ($e->{PARENT} && $e->{PARENT}->{NAME}) {
729                 return "$e->{PARENT}->{NAME}.$e->{NAME}";
730         }
731
732         if ($e->{PARENT} && $e->{PARENT}->{PARENT}->{NAME}) {
733                 return "$e->{PARENT}->{PARENT}->{NAME}.$e->{NAME}";
734         }
735
736         if ($e->{PARENT}) {
737                 return "$e->{PARENT}->{NAME}.$e->{NAME}";
738         }
739
740         return $e->{NAME};
741 }
742
743 ###################################
744 # find a sibling var in a structure
745 sub find_sibling($$)
746 {
747         my($e,$name) = @_;
748         my($fn) = $e->{PARENT};
749
750         if ($name =~ /\*(.*)/) {
751                 $name = $1;
752         }
753
754         for my $e2 (@{$fn->{ELEMENTS}}) {
755                 return $e2 if ($e2->{NAME} eq $name);
756         }
757
758         return undef;
759 }
760
761 my %property_list = (
762         # interface
763         "helpstring"            => ["INTERFACE", "FUNCTION"],
764         "version"               => ["INTERFACE"],
765         "uuid"                  => ["INTERFACE"],
766         "endpoint"              => ["INTERFACE"],
767         "pointer_default"       => ["INTERFACE"],
768         "pointer_default_top"   => ["INTERFACE"],
769         "depends"               => ["INTERFACE"],
770         "helper"                => ["INTERFACE"],
771         "authservice"           => ["INTERFACE"],
772
773         # dcom
774         "object"                => ["INTERFACE"],
775         "local"                 => ["INTERFACE", "FUNCTION"],
776         "iid_is"                => ["ELEMENT"],
777         "call_as"               => ["FUNCTION"],
778         "idempotent"            => ["FUNCTION"],
779
780         # function
781         "noopnum"               => ["FUNCTION"],
782         "in"                    => ["ELEMENT"],
783         "out"                   => ["ELEMENT"],
784         "async"                 => ["FUNCTION"],
785
786         # pointer
787         "ref"                   => ["ELEMENT"],
788         "ptr"                   => ["ELEMENT"],
789         "sptr"                  => ["ELEMENT"],
790         "unique"                => ["ELEMENT"],
791         "ignore"                => ["ELEMENT"],
792         "relative"              => ["ELEMENT"],
793         "relative_base"         => ["TYPEDEF"],
794
795         "gensize"               => ["TYPEDEF"],
796         "value"                 => ["ELEMENT"],
797         "flag"                  => ["ELEMENT", "TYPEDEF"],
798
799         # generic
800         "public"                => ["FUNCTION", "TYPEDEF"],
801         "nopush"                => ["FUNCTION", "TYPEDEF"],
802         "nopull"                => ["FUNCTION", "TYPEDEF"],
803         "nosize"                => ["FUNCTION", "TYPEDEF"],
804         "noprint"               => ["FUNCTION", "TYPEDEF"],
805         "noejs"                 => ["FUNCTION", "TYPEDEF"],
806
807         # union
808         "switch_is"             => ["ELEMENT"],
809         "switch_type"           => ["ELEMENT", "TYPEDEF"],
810         "nodiscriminant"        => ["TYPEDEF"],
811         "case"                  => ["ELEMENT"],
812         "default"               => ["ELEMENT"],
813
814         "represent_as"          => ["ELEMENT"],
815         "transmit_as"           => ["ELEMENT"],
816
817         # subcontext
818         "subcontext"            => ["ELEMENT"],
819         "subcontext_size"       => ["ELEMENT"],
820         "compression"           => ["ELEMENT"],
821
822         # enum
823         "enum8bit"              => ["TYPEDEF"],
824         "enum16bit"             => ["TYPEDEF"],
825         "v1_enum"               => ["TYPEDEF"],
826
827         # bitmap
828         "bitmap8bit"            => ["TYPEDEF"],
829         "bitmap16bit"           => ["TYPEDEF"],
830         "bitmap32bit"           => ["TYPEDEF"],
831         "bitmap64bit"           => ["TYPEDEF"],
832
833         # array
834         "range"                 => ["ELEMENT"],
835         "size_is"               => ["ELEMENT"],
836         "string"                => ["ELEMENT"],
837         "noheader"              => ["ELEMENT"],
838         "charset"               => ["ELEMENT"],
839         "length_is"             => ["ELEMENT"],
840 );
841
842 #####################################################################
843 # check for unknown properties
844 sub ValidProperties($$)
845 {
846         my ($e,$t) = @_;
847
848         return unless defined $e->{PROPERTIES};
849
850         foreach my $key (keys %{$e->{PROPERTIES}}) {
851                 fatal($e, el_name($e) . ": unknown property '$key'\n")
852                         unless defined($property_list{$key});
853
854                 fatal($e, el_name($e) . ": property '$key' not allowed on '$t'\n")
855                         unless grep($t, @{$property_list{$key}});
856         }
857 }
858
859 sub mapToScalar($)
860 {
861         my $t = shift;
862         my $ti = getType($t);
863
864         if (not defined ($ti)) {
865                 return undef;
866         } elsif ($ti->{DATA}->{TYPE} eq "ENUM") {
867                 return Parse::Pidl::Typelist::enum_type_fn($ti->{DATA});
868         } elsif ($ti->{DATA}->{TYPE} eq "BITMAP") {
869                 return Parse::Pidl::Typelist::enum_type_fn($ti->{DATA});
870         } elsif ($ti->{DATA}->{TYPE} eq "SCALAR") {
871                 return $t;
872         }
873
874         return undef;
875 }
876
877 #####################################################################
878 # parse a struct
879 sub ValidElement($)
880 {
881         my $e = shift;
882
883         ValidProperties($e,"ELEMENT");
884
885         if (has_property($e, "ptr")) {
886                 fatal($e, el_name($e) . " : pidl does not support full NDR pointers yet\n");
887         }
888
889         # Check whether switches are used correctly.
890         if (my $switch = has_property($e, "switch_is")) {
891                 my $e2 = find_sibling($e, $switch);
892                 my $type = getType($e->{TYPE});
893
894                 if (defined($type) and $type->{DATA}->{TYPE} ne "UNION") {
895                         fatal($e, el_name($e) . ": switch_is() used on non-union type $e->{TYPE} which is a $type->{DATA}->{TYPE}");
896                 }
897
898                 if (!has_property($type, "nodiscriminant") and defined($e2)) {
899                         my $discriminator_type = has_property($type, "switch_type");
900                         $discriminator_type = "uint32" unless defined ($discriminator_type);
901
902                         my $t1 = mapToScalar($discriminator_type);
903
904                         if (not defined($t1)) {
905                                 fatal($e, el_name($e) . ": unable to map discriminator type '$discriminator_type' to scalar");
906                         }
907
908                         my $t2 = mapToScalar($e2->{TYPE});
909                         if (not defined($t2)) {
910                                 fatal($e, el_name($e) . ": unable to map variable used for switch_is() to scalar");
911                         }
912
913                         if ($t1 ne $t2) {
914                                 nonfatal($e, el_name($e) . ": switch_is() is of type $e2->{TYPE} ($t2), while discriminator type for union $type->{NAME} is $discriminator_type ($t1)");
915                         }
916                 }
917         }
918
919
920         if (has_property($e, "subcontext") and has_property($e, "represent_as")) {
921                 fatal($e, el_name($e) . " : subcontext() and represent_as() can not be used on the same element");
922         }
923
924         if (has_property($e, "subcontext") and has_property($e, "transmit_as")) {
925                 fatal($e, el_name($e) . " : subcontext() and transmit_as() can not be used on the same element");
926         }
927
928         if (has_property($e, "represent_as") and has_property($e, "transmit_as")) {
929                 fatal($e, el_name($e) . " : represent_as() and transmit_as() can not be used on the same element");
930         }
931
932         if (has_property($e, "represent_as") and has_property($e, "value")) {
933                 fatal($e, el_name($e) . " : represent_as() and value() can not be used on the same element");
934         }
935
936         if (defined (has_property($e, "subcontext_size")) and not defined(has_property($e, "subcontext"))) {
937                 fatal($e, el_name($e) . " : subcontext_size() on non-subcontext element");
938         }
939
940         if (defined (has_property($e, "compression")) and not defined(has_property($e, "subcontext"))) {
941                 fatal($e, el_name($e) . " : compression() on non-subcontext element");
942         }
943
944         if (!$e->{POINTERS} && (
945                 has_property($e, "ptr") or
946                 has_property($e, "sptr") or
947                 has_property($e, "unique") or
948                 has_property($e, "relative") or
949                 has_property($e, "ref"))) {
950                 fatal($e, el_name($e) . " : pointer properties on non-pointer element\n");      
951         }
952 }
953
954 #####################################################################
955 # parse a struct
956 sub ValidStruct($)
957 {
958         my($struct) = shift;
959
960         ValidProperties($struct,"STRUCT");
961
962         foreach my $e (@{$struct->{ELEMENTS}}) {
963                 $e->{PARENT} = $struct;
964                 ValidElement($e);
965         }
966 }
967
968 #####################################################################
969 # parse a union
970 sub ValidUnion($)
971 {
972         my($union) = shift;
973
974         ValidProperties($union,"UNION");
975
976         if (has_property($union->{PARENT}, "nodiscriminant") and has_property($union->{PARENT}, "switch_type")) {
977                 fatal($union->{PARENT}, $union->{PARENT}->{NAME} . ": switch_type() on union without discriminant");
978         }
979         
980         foreach my $e (@{$union->{ELEMENTS}}) {
981                 $e->{PARENT} = $union;
982
983                 if (defined($e->{PROPERTIES}->{default}) and 
984                         defined($e->{PROPERTIES}->{case})) {
985                         fatal $e, "Union member $e->{NAME} can not have both default and case properties!\n";
986                 }
987                 
988                 unless (defined ($e->{PROPERTIES}->{default}) or 
989                                 defined ($e->{PROPERTIES}->{case})) {
990                         fatal $e, "Union member $e->{NAME} must have default or case property\n";
991                 }
992
993                 if (has_property($e, "ref")) {
994                         fatal($e, el_name($e) . " : embedded ref pointers are not supported yet\n");
995                 }
996
997
998                 ValidElement($e);
999         }
1000 }
1001
1002 #####################################################################
1003 # parse a typedef
1004 sub ValidTypedef($)
1005 {
1006         my($typedef) = shift;
1007         my $data = $typedef->{DATA};
1008
1009         ValidProperties($typedef,"TYPEDEF");
1010
1011         $data->{PARENT} = $typedef;
1012
1013         if (ref($data) eq "HASH") {
1014                 if ($data->{TYPE} eq "STRUCT") {
1015                         ValidStruct($data);
1016                 }
1017
1018                 if ($data->{TYPE} eq "UNION") {
1019                         ValidUnion($data);
1020                 }
1021         }
1022 }
1023
1024 #####################################################################
1025 # parse a function
1026 sub ValidFunction($)
1027 {
1028         my($fn) = shift;
1029
1030         ValidProperties($fn,"FUNCTION");
1031
1032         foreach my $e (@{$fn->{ELEMENTS}}) {
1033                 $e->{PARENT} = $fn;
1034                 if (has_property($e, "ref") && !$e->{POINTERS}) {
1035                         fatal $e, "[ref] variables must be pointers ($fn->{NAME}/$e->{NAME})\n";
1036                 }
1037                 ValidElement($e);
1038         }
1039 }
1040
1041 #####################################################################
1042 # parse the interface definitions
1043 sub ValidInterface($)
1044 {
1045         my($interface) = shift;
1046         my($data) = $interface->{DATA};
1047
1048         if (has_property($interface, "depends")) {
1049                 nonfatal $interface, "depends() is pidl-specific and deprecated. Use `import' instead";
1050         }
1051
1052         if (has_property($interface, "helper")) {
1053                 nonfatal $interface, "helper() is pidl-specific and deprecated. Use `include' instead";
1054         }
1055
1056         ValidProperties($interface,"INTERFACE");
1057
1058         if (has_property($interface, "pointer_default") && 
1059                 $interface->{PROPERTIES}->{pointer_default} eq "ptr") {
1060                 fatal $interface, "Full pointers are not supported yet\n";
1061         }
1062
1063         if (has_property($interface, "object")) {
1064                 if (has_property($interface, "version") && 
1065                         $interface->{PROPERTIES}->{version} != 0) {
1066                         fatal $interface, "Object interfaces must have version 0.0 ($interface->{NAME})\n";
1067                 }
1068
1069                 if (!defined($interface->{BASE}) && 
1070                         not ($interface->{NAME} eq "IUnknown")) {
1071                         fatal $interface, "Object interfaces must all derive from IUnknown ($interface->{NAME})\n";
1072                 }
1073         }
1074                 
1075         foreach my $d (@{$data}) {
1076                 ($d->{TYPE} eq "TYPEDEF") &&
1077                     ValidTypedef($d);
1078                 ($d->{TYPE} eq "FUNCTION") && 
1079                     ValidFunction($d);
1080         }
1081
1082 }
1083
1084 #####################################################################
1085 # Validate an IDL structure
1086 sub Validate($)
1087 {
1088         my($idl) = shift;
1089
1090         foreach my $x (@{$idl}) {
1091                 ($x->{TYPE} eq "INTERFACE") && 
1092                     ValidInterface($x);
1093                 ($x->{TYPE} eq "IMPORTLIB") &&
1094                         nonfatal($x, "importlib() not supported");
1095         }
1096 }
1097
1098 1;