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