• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005 Apple Computer, Inc.
4# Copyright (C) 2006 Anders Carlsson <andersca@mac.com>
5#
6# This file is part of WebKit
7#
8# This library is free software; you can redistribute it and/or
9# modify it under the terms of the GNU Library General Public
10# License as published by the Free Software Foundation; either
11# version 2 of the License, or (at your option) any later version.
12#
13# This library is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16# Library General Public License for more details.
17#
18# You should have received a copy of the GNU Library General Public License
19# along with this library; see the file COPYING.LIB.  If not, write to
20# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21# Boston, MA 02110-1301, USA.
22#
23
24use strict;
25
26use File::Path;
27use File::Basename;
28use Getopt::Long;
29use Text::ParseWords;
30use Cwd;
31
32use idl_parser;
33use code_generator_v8;
34use idl_serializer;
35
36my @idlDirectories;
37my $outputDirectory;
38my $preprocessor;
39my $verbose;
40my $interfaceDependenciesFile;
41my $additionalIdlFiles;
42my $idlAttributesFile;
43my $writeFileOnlyIfChanged;
44
45GetOptions('include=s@' => \@idlDirectories,
46           'outputDir=s' => \$outputDirectory,
47           'preprocessor=s' => \$preprocessor,
48           'verbose' => \$verbose,
49           'interfaceDependenciesFile=s' => \$interfaceDependenciesFile,
50           'additionalIdlFiles=s' => \$additionalIdlFiles,
51           'idlAttributesFile=s' => \$idlAttributesFile,
52           'write-file-only-if-changed=s' => \$writeFileOnlyIfChanged);
53
54my $targetIdlFile = $ARGV[0];
55
56die('Must specify input file.') unless defined($targetIdlFile);
57die('Must specify output directory.') unless defined($outputDirectory);
58
59$targetIdlFile = Cwd::realpath($targetIdlFile);
60if ($verbose) {
61    print "$targetIdlFile\n";
62}
63my $targetInterfaceName = fileparse(basename($targetIdlFile), ".idl");
64
65my $idlFound = 0;
66my @dependencyIdlFiles;
67if ($interfaceDependenciesFile) {
68    # The format of the interface dependencies file:
69    #
70    # Window.idl P.idl Q.idl R.idl
71    # Document.idl S.idl
72    # Event.idl
73    # ...
74    #
75    # The above indicates that Window.idl depends on P.idl, Q.idl, and R.idl,
76    # Document.idl depends on S.idl, and Event.idl depends on no IDLs.
77    # A dependency IDL file (one that is depended on by another IDL, e.g. P.idl
78    # in the above) does not have its own entry in the dependency file.
79    open FH, "< $interfaceDependenciesFile" or die "Cannot open $interfaceDependenciesFile\n";
80    while (my $line = <FH>) {
81        my ($idlFile, @followingIdlFiles) = split(/\s+/, $line);
82        if ($idlFile and basename($idlFile) eq basename($targetIdlFile)) {
83            $idlFound = 1;
84            # We sort the dependency IDL files so that the corresponding code is generated
85            # in a consistent order. This is important for the bindings tests.
86            @dependencyIdlFiles = sort @followingIdlFiles;
87        }
88    }
89    close FH;
90
91    # $additionalIdlFiles is for IDL files not listed in the interface
92    # dependencies file, namely generated IDL files for interfaces
93    # (not partial interfaces), so we need to generate .h and .cpp files.
94    if (!$idlFound and $additionalIdlFiles) {
95        my @idlFiles = shellwords($additionalIdlFiles);
96        $idlFound = grep { $_ and basename($_) eq basename($targetIdlFile) } @idlFiles;
97    }
98
99    if (!$idlFound) {
100        # IDL files for dependencies (partial interfaces and interfaces
101        # implemented elsewhere). We generate empty .h and .cpp files just to
102        # tell build scripts that outputs have been created.
103        generateEmptyHeaderAndCpp($targetInterfaceName, $outputDirectory);
104        exit 0;
105    }
106}
107
108# Parse the target IDL file.
109my $targetParser = idl_parser->new(!$verbose);
110my $targetDocument = $targetParser->Parse($targetIdlFile, $preprocessor);
111
112if ($idlAttributesFile) {
113    my $idlAttributes = loadIDLAttributes($idlAttributesFile);
114    checkIDLAttributes($idlAttributes, $targetDocument, basename($targetIdlFile));
115}
116
117foreach my $idlFile (@dependencyIdlFiles) {
118    next if $idlFile eq $targetIdlFile;
119
120    my $interfaceName = fileparse(basename($idlFile), ".idl");
121    my $parser = idl_parser->new(!$verbose);
122    my $document = $parser->Parse($idlFile, $preprocessor);
123
124    foreach my $interface (@{$document->interfaces}) {
125        if (!$interface->isPartial || $interface->name eq $targetInterfaceName) {
126            my $targetDataNode;
127            foreach my $interface (@{$targetDocument->interfaces}) {
128                if ($interface->name eq $targetInterfaceName) {
129                    $targetDataNode = $interface;
130                    last;
131                }
132            }
133            die "Not found an interface ${targetInterfaceName} in ${targetInterfaceName}.idl." unless defined $targetDataNode;
134
135            # Support for attributes of partial interfaces.
136            foreach my $attribute (@{$interface->attributes}) {
137                # Record that this attribute is implemented by $interfaceName.
138                $attribute->extendedAttributes->{"ImplementedBy"} = $interfaceName unless $interface->extendedAttributes->{"LegacyImplementedInBaseClass"};
139
140                # Add interface-wide extended attributes to each attribute.
141                applyInterfaceExtendedAttributes($interface, $attribute->extendedAttributes);
142
143                push(@{$targetDataNode->attributes}, $attribute);
144            }
145
146            # Support for methods of partial interfaces.
147            foreach my $function (@{$interface->functions}) {
148                # Record that this method is implemented by $interfaceName.
149                $function->extendedAttributes->{"ImplementedBy"} = $interfaceName unless $interface->extendedAttributes->{"LegacyImplementedInBaseClass"};
150
151                # Add interface-wide extended attributes to each method.
152                applyInterfaceExtendedAttributes($interface, $function->extendedAttributes);
153
154                push(@{$targetDataNode->functions}, $function);
155            }
156
157            # Support for constants of partial interfaces.
158            foreach my $constant (@{$interface->constants}) {
159                # Record that this constant is implemented by $interfaceName.
160                $constant->extendedAttributes->{"ImplementedBy"} = $interfaceName unless $interface->extendedAttributes->{"LegacyImplementedInBaseClass"};
161
162                # Add interface-wide extended attributes to each constant.
163                applyInterfaceExtendedAttributes($interface, $constant->extendedAttributes);
164
165                push(@{$targetDataNode->constants}, $constant);
166            }
167        } else {
168            die "$idlFile is not a dependency of $targetIdlFile. There maybe a bug in the dependency computer (compute_dependencies.py).\n";
169        }
170    }
171}
172
173# Serialize to and from JSON to ensure Perl and Python parsers are equivalent,
174# as part of porting compiler to Python. See http://crbug.com/242795
175$targetDocument = deserializeJSON(serializeJSON($targetDocument));
176
177# Generate desired output for the target IDL file.
178my @interfaceIdlFiles = ($targetDocument->fileName(), @dependencyIdlFiles);
179my $codeGenerator = code_generator_v8->new($targetDocument, \@idlDirectories, $preprocessor, $verbose, \@interfaceIdlFiles, $writeFileOnlyIfChanged);
180my $interfaces = $targetDocument->interfaces;
181foreach my $interface (@$interfaces) {
182    print "Generating bindings code for IDL interface \"" . $interface->name . "\"...\n" if $verbose;
183    $codeGenerator->GenerateInterface($interface);
184    $codeGenerator->WriteData($interface, $outputDirectory);
185}
186
187sub generateEmptyHeaderAndCpp
188{
189    my ($targetInterfaceName, $outputDirectory) = @_;
190
191    my $headerName = "V8${targetInterfaceName}.h";
192    my $cppName = "V8${targetInterfaceName}.cpp";
193    my $contents = "/*
194    This file is generated just to tell build scripts that $headerName and
195    $cppName are created for ${targetInterfaceName}.idl, and thus
196    prevent the build scripts from trying to generate $headerName and
197    $cppName at every build. This file must not be tried to compile.
198*/
199";
200    open FH, "> ${outputDirectory}/${headerName}" or die "Cannot open $headerName\n";
201    print FH $contents;
202    close FH;
203
204    open FH, "> ${outputDirectory}/${cppName}" or die "Cannot open $cppName\n";
205    print FH $contents;
206    close FH;
207}
208
209sub loadIDLAttributes
210{
211    my $idlAttributesFile = shift;
212
213    my %idlAttributes;
214    open FH, "<", $idlAttributesFile or die "Couldn't open $idlAttributesFile: $!";
215    while (my $line = <FH>) {
216        chomp $line;
217        next if $line =~ /^\s*#/;
218        next if $line =~ /^\s*$/;
219
220        if ($line =~ /^\s*([^=\s]*)\s*=?\s*(.*)/) {
221            my $name = $1;
222            $idlAttributes{$name} = {};
223            if ($2) {
224                foreach my $rightValue (split /\|/, $2) {
225                    $rightValue =~ s/^\s*|\s*$//g;
226                    $rightValue = "VALUE_IS_MISSING" unless $rightValue;
227                    $idlAttributes{$name}{$rightValue} = 1;
228                }
229            } else {
230                $idlAttributes{$name}{"VALUE_IS_MISSING"} = 1;
231            }
232        } else {
233            die "The format of " . basename($idlAttributesFile) . " is wrong: line $.\n";
234        }
235    }
236    close FH;
237
238    return \%idlAttributes;
239}
240
241sub checkIDLAttributes
242{
243    my $idlAttributes = shift;
244    my $document = shift;
245    my $idlFile = shift;
246
247    foreach my $interface (@{$document->interfaces}) {
248        checkIfIDLAttributesExists($idlAttributes, $interface->extendedAttributes, $idlFile);
249
250        foreach my $attribute (@{$interface->attributes}) {
251            checkIfIDLAttributesExists($idlAttributes, $attribute->extendedAttributes, $idlFile);
252        }
253
254        foreach my $function (@{$interface->functions}) {
255            checkIfIDLAttributesExists($idlAttributes, $function->extendedAttributes, $idlFile);
256            foreach my $parameter (@{$function->parameters}) {
257                checkIfIDLAttributesExists($idlAttributes, $parameter->extendedAttributes, $idlFile);
258            }
259        }
260    }
261}
262
263sub applyInterfaceExtendedAttributes
264{
265    my $interface = shift;
266    my $extendedAttributes = shift;
267
268    foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) {
269        next if $extendedAttributeName eq "ImplementedAs";
270        $extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName};
271    }
272}
273
274sub checkIfIDLAttributesExists
275{
276    my $idlAttributes = shift;
277    my $extendedAttributes = shift;
278    my $idlFile = shift;
279
280    my $error;
281    OUTER: for my $name (keys %$extendedAttributes) {
282        if (!exists $idlAttributes->{$name}) {
283            $error = "Invalid IDL attribute [$name] found in $idlFile.";
284            last;
285        }
286        # Check no argument first, since "*" means "some argument (not missing)".
287        if ($extendedAttributes->{$name} eq "VALUE_IS_MISSING" and not exists $idlAttributes->{$name}{"VALUE_IS_MISSING"}) {
288            $error = "Missing required argument for IDL attribute [$name] in file $idlFile.";
289            last;
290        }
291        if (exists $idlAttributes->{$name}{"*"}) {
292            next;
293        }
294        for my $rightValue (split /\s*[|&]\s*/, $extendedAttributes->{$name}) {
295            if (!(exists $idlAttributes->{$name}{$rightValue})) {
296                $error = "Invalid IDL attribute value [$name=" . $extendedAttributes->{$name} . "] found in $idlFile.";
297                last OUTER;
298            }
299        }
300    }
301    if ($error) {
302        die "IDL ATTRIBUTE CHECKER ERROR: $error
303If you want to add a new IDL extended attribute, please add it to:
304    bindings/IDLExtendedAttributes.txt
305and add an explanation to the Blink IDL documentation at:
306    http://www.chromium.org/blink/webidl/blink-idl-extended-attributes
307";
308    }
309}
310