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