Access GCController instance via GCController::singleton() instead of a free function
[WebKit-https.git] / Source / WebCore / bindings / scripts / preprocess-idls.pl
1 #!/usr/bin/perl -w
2 #
3 # Copyright (C) 2011 Google Inc.  All rights reserved.
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Library General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Library General Public License for more details.
14 #
15 # You should have received a copy of the GNU Library General Public License
16 # along with this library; see the file COPYING.LIB.  If not, write to
17 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 # Boston, MA 02110-1301, USA.
19 #
20
21 use strict;
22
23 use File::Basename;
24 use Getopt::Long;
25 use Cwd;
26
27 my $defines;
28 my $preprocessor;
29 my $idlFilesList;
30 my $supplementalDependencyFile;
31 my $windowConstructorsFile;
32 my $workerGlobalScopeConstructorsFile;
33 my $dedicatedWorkerGlobalScopeConstructorsFile;
34 my $supplementalMakefileDeps;
35
36 GetOptions('defines=s' => \$defines,
37            'preprocessor=s' => \$preprocessor,
38            'idlFilesList=s' => \$idlFilesList,
39            'supplementalDependencyFile=s' => \$supplementalDependencyFile,
40            'windowConstructorsFile=s' => \$windowConstructorsFile,
41            'workerGlobalScopeConstructorsFile=s' => \$workerGlobalScopeConstructorsFile,
42            'dedicatedWorkerGlobalScopeConstructorsFile=s' => \$dedicatedWorkerGlobalScopeConstructorsFile,
43            'supplementalMakefileDeps=s' => \$supplementalMakefileDeps);
44
45 die('Must specify #define macros using --defines.') unless defined($defines);
46 die('Must specify an output file using --supplementalDependencyFile.') unless defined($supplementalDependencyFile);
47 die('Must specify an output file using --windowConstructorsFile.') unless defined($windowConstructorsFile);
48 die('Must specify an output file using --workerGlobalScopeConstructorsFile.') unless defined($workerGlobalScopeConstructorsFile);
49 die('Must specify an output file using --dedicatedWorkerGlobalScopeConstructorsFile.') unless defined($dedicatedWorkerGlobalScopeConstructorsFile);
50 die('Must specify the file listing all IDLs using --idlFilesList.') unless defined($idlFilesList);
51
52 open FH, "< $idlFilesList" or die "Cannot open $idlFilesList\n";
53 my @idlFiles = <FH>;
54 chomp(@idlFiles);
55 close FH;
56
57 my %interfaceNameToIdlFile;
58 my %idlFileToInterfaceName;
59 my %supplementalDependencies;
60 my %supplementals;
61 my $windowConstructorsCode = "";
62 my $workerGlobalScopeConstructorsCode = "";
63 my $dedicatedWorkerGlobalScopeConstructorsCode = "";
64
65 # Get rid of duplicates in idlFiles array.
66 my %idlFileHash = map { $_, 1 } @idlFiles;
67
68 # Populate $idlFileToInterfaceName and $interfaceNameToIdlFile.
69 foreach my $idlFile (keys %idlFileHash) {
70     my $fullPath = Cwd::realpath($idlFile);
71     my $interfaceName = fileparse(basename($idlFile), ".idl");
72     $idlFileToInterfaceName{$fullPath} = $interfaceName;
73     $interfaceNameToIdlFile{$interfaceName} = $fullPath;
74 }
75
76 # Parse all IDL files.
77 foreach my $idlFile (sort keys %idlFileHash) {
78     my $fullPath = Cwd::realpath($idlFile);
79     my $idlFileContents = getFileContents($fullPath);
80     # Handle partial interfaces.
81     my $partialInterfaceName = getPartialInterfaceNameFromIDL($idlFileContents);
82     if ($partialInterfaceName) {
83         $supplementalDependencies{$fullPath} = [$partialInterfaceName];
84         next;
85     }
86     my $interfaceName = fileparse(basename($idlFile), ".idl");
87     # Handle implements statements.
88     my $implementedInterfaces = getImplementedInterfacesFromIDL($idlFileContents, $interfaceName);
89     foreach my $implementedInterface (@{$implementedInterfaces}) {
90         my $implementedIdlFile = $interfaceNameToIdlFile{$implementedInterface};
91         die "Could not find a the IDL file where the following implemented interface is defined: $implementedInterface" unless $implementedIdlFile;
92         if ($supplementalDependencies{$implementedIdlFile}) {
93             push(@{$supplementalDependencies{$implementedIdlFile}}, $interfaceName);
94         } else {
95             $supplementalDependencies{$implementedIdlFile} = [$interfaceName];
96         }
97     }
98     # Handle [NoInterfaceObject].
99     unless (isCallbackInterfaceFromIDL($idlFileContents)) {
100         my $extendedAttributes = getInterfaceExtendedAttributesFromIDL($idlFileContents);
101         unless ($extendedAttributes->{"NoInterfaceObject"}) {
102             my @globalContexts = split("&", $extendedAttributes->{"GlobalContext"} || "DOMWindow");
103             my $attributeCode = GenerateConstructorAttribute($interfaceName, $extendedAttributes);
104             $windowConstructorsCode .= $attributeCode if grep(/^DOMWindow$/, @globalContexts);
105             $workerGlobalScopeConstructorsCode .= $attributeCode if grep(/^WorkerGlobalScope$/, @globalContexts);
106             $dedicatedWorkerGlobalScopeConstructorsCode .= $attributeCode if grep(/^DedicatedWorkerGlobalScope$/, @globalContexts);
107         }
108     }
109     $supplementals{$fullPath} = [];
110 }
111
112 # Generate partial interfaces for Constructors.
113 GeneratePartialInterface("DOMWindow", $windowConstructorsCode, $windowConstructorsFile);
114 GeneratePartialInterface("WorkerGlobalScope", $workerGlobalScopeConstructorsCode, $workerGlobalScopeConstructorsFile);
115 GeneratePartialInterface("DedicatedWorkerGlobalScope", $dedicatedWorkerGlobalScopeConstructorsCode, $dedicatedWorkerGlobalScopeConstructorsFile);
116
117 # Resolves partial interfaces and implements dependencies.
118 foreach my $idlFile (keys %supplementalDependencies) {
119     my $baseFiles = $supplementalDependencies{$idlFile};
120     foreach my $baseFile (@{$baseFiles}) {
121         my $targetIdlFile = $interfaceNameToIdlFile{$baseFile};
122         push(@{$supplementals{$targetIdlFile}}, $idlFile);
123     }
124     delete $supplementals{$idlFile};
125 }
126
127 # Outputs the dependency.
128 # The format of a supplemental dependency file:
129 #
130 # DOMWindow.idl P.idl Q.idl R.idl
131 # Document.idl S.idl
132 # Event.idl
133 # ...
134 #
135 # The above indicates that DOMWindow.idl is supplemented by P.idl, Q.idl and R.idl,
136 # Document.idl is supplemented by S.idl, and Event.idl is supplemented by no IDLs.
137 # The IDL that supplements another IDL (e.g. P.idl) never appears in the dependency file.
138 my $dependencies = "";
139 foreach my $idlFile (sort keys %supplementals) {
140     $dependencies .= "$idlFile @{$supplementals{$idlFile}}\n";
141 }
142 WriteFileIfChanged($supplementalDependencyFile, $dependencies);
143
144 if ($supplementalMakefileDeps) {
145     my $makefileDeps = "";
146     foreach my $idlFile (sort keys %supplementals) {
147         my $basename = $idlFileToInterfaceName{$idlFile};
148
149         my @dependencies = map { basename($_) } @{$supplementals{$idlFile}};
150
151         $makefileDeps .= "JS${basename}.h: @{dependencies}\n";
152         $makefileDeps .= "DOM${basename}.h: @{dependencies}\n";
153         $makefileDeps .= "WebDOM${basename}.h: @{dependencies}\n";
154         foreach my $dependency (@dependencies) {
155             $makefileDeps .= "${dependency}:\n";
156         }
157     }
158
159     WriteFileIfChanged($supplementalMakefileDeps, $makefileDeps);
160 }
161
162 sub WriteFileIfChanged
163 {
164     my $fileName = shift;
165     my $contents = shift;
166
167     if (-f $fileName) {
168         open FH, "<", $fileName or die "Couldn't open $fileName: $!\n";
169         my @lines = <FH>;
170         my $oldContents = join "", @lines;
171         close FH;
172         return if $contents eq $oldContents;
173     }
174     open FH, ">", $fileName or die "Couldn't open $fileName: $!\n";
175     print FH $contents;
176     close FH;
177 }
178
179 sub GeneratePartialInterface
180 {
181     my $interfaceName = shift;
182     my $attributesCode = shift;
183     my $destinationFile = shift;
184
185     my $contents = "partial interface ${interfaceName} {\n$attributesCode};\n";
186     WriteFileIfChanged($destinationFile, $contents);
187
188     my $fullPath = Cwd::realpath($destinationFile);
189     $supplementalDependencies{$fullPath} = [$interfaceName] if $interfaceNameToIdlFile{$interfaceName};
190 }
191
192 sub GenerateConstructorAttribute
193 {
194     my $interfaceName = shift;
195     my $extendedAttributes = shift;
196
197     my $code = "    ";
198     my @extendedAttributesList;
199     foreach my $attributeName (keys %{$extendedAttributes}) {
200       next unless ($attributeName eq "Conditional" || $attributeName eq "EnabledAtRuntime" || $attributeName eq "EnabledBySetting");
201       my $extendedAttribute = $attributeName;
202       $extendedAttribute .= "=" . $extendedAttributes->{$attributeName} unless $extendedAttributes->{$attributeName} eq "VALUE_IS_MISSING";
203       push(@extendedAttributesList, $extendedAttribute);
204     }
205     $code .= "[" . join(', ', @extendedAttributesList) . "] " if @extendedAttributesList;
206
207     my $originalInterfaceName = $interfaceName;
208     $interfaceName = $extendedAttributes->{"InterfaceName"} if $extendedAttributes->{"InterfaceName"};
209     $code .= "attribute " . $originalInterfaceName . "Constructor $interfaceName;\n";
210
211     # In addition to the regular property, for every [NamedConstructor] extended attribute on an interface,
212     # a corresponding property MUST exist on the ECMAScript global object.
213     if ($extendedAttributes->{"NamedConstructor"}) {
214         my $constructorName = $extendedAttributes->{"NamedConstructor"};
215         $constructorName =~ s/\(.*//g; # Extract function name.
216         $code .= "    ";
217         $code .= "[" . join(', ', @extendedAttributesList) . "] " if @extendedAttributesList;
218         $code .= "attribute " . $originalInterfaceName . "NamedConstructor $constructorName;\n";
219     }
220     return $code;
221 }
222
223 sub getFileContents
224 {
225     my $idlFile = shift;
226
227     open FILE, "<", $idlFile;
228     my @lines = <FILE>;
229     close FILE;
230
231     # Filter out preprocessor lines.
232     @lines = grep(!/^\s*#/, @lines);
233
234     return join('', @lines);
235 }
236
237 sub getPartialInterfaceNameFromIDL
238 {
239     my $fileContents = shift;
240
241     if ($fileContents =~ /partial\s+interface\s+(\w+)/gs) {
242         return $1;
243     }
244 }
245
246 # identifier-A implements identifier-B;
247 # http://www.w3.org/TR/WebIDL/#idl-implements-statements
248 sub getImplementedInterfacesFromIDL
249 {
250     my $fileContents = shift;
251     my $interfaceName = shift;
252
253     my @implementedInterfaces = ();
254     while ($fileContents =~ /^\s*(\w+)\s+implements\s+(\w+)\s*;/mg) {
255         die "Identifier on the left of the 'implements' statement should be $interfaceName in $interfaceName.idl, but found $1" if $1 ne $interfaceName;
256         push(@implementedInterfaces, $2);
257     }
258     return \@implementedInterfaces
259 }
260
261 sub isCallbackInterfaceFromIDL
262 {
263     my $fileContents = shift;
264     return ($fileContents =~ /callback\s+interface\s+\w+/gs);
265 }
266
267 sub trim
268 {
269     my $string = shift;
270     $string =~ s/^\s+|\s+$//g;
271     return $string;
272 }
273
274 sub getInterfaceExtendedAttributesFromIDL
275 {
276     my $fileContents = shift;
277
278     my $extendedAttributes = {};
279
280     if ($fileContents =~ /\[(.*)\]\s+(interface|exception)\s+(\w+)/gs) {
281         my @parts = split(',', $1);
282         foreach my $part (@parts) {
283             my @keyValue = split('=', $part);
284             my $key = trim($keyValue[0]);
285             next unless length($key);
286             my $value = "VALUE_IS_MISSING";
287             $value = trim($keyValue[1]) if @keyValue > 1;
288             $extendedAttributes->{$key} = $value;
289         }
290     }
291
292     return $extendedAttributes;
293 }