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