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