1 # 2 # WebKit IDL parser 3 # 4 # Copyright (C) 2005 Nikolas Zimmermann <wildfox (at] kde.org> 5 # Copyright (C) 2006 Samuel Weinig <sam.weinig (at] gmail.com> 6 # Copyright (C) 2007 Apple Inc. All rights reserved. 7 # Copyright (C) 2009 Cameron McCormack <cam (at] mcc.id.au> 8 # 9 # This library is free software; you can redistribute it and/or 10 # modify it under the terms of the GNU Library General Public 11 # License as published by the Free Software Foundation; either 12 # version 2 of the License, or (at your option) any later version. 13 # 14 # This library is distributed in the hope that it will be useful, 15 # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 # Library General Public License for more details. 18 # 19 # You should have received a copy of the GNU Library General Public License 20 # aint with this library; see the file COPYING.LIB. If not, write to 21 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 # Boston, MA 02110-1301, USA. 23 # 24 25 package CodeGenerator; 26 27 use File::Find; 28 29 my $useDocument = ""; 30 my $useGenerator = ""; 31 my $useOutputDir = ""; 32 my $useDirectories = ""; 33 my $useLayerOnTop = 0; 34 my $preprocessor; 35 my $writeDependencies = 0; 36 my $defines = ""; 37 38 my $codeGenerator = 0; 39 40 my $verbose = 0; 41 42 my %primitiveTypeHash = ("int" => 1, "short" => 1, "long" => 1, "long long" => 1, 43 "unsigned int" => 1, "unsigned short" => 1, 44 "unsigned long" => 1, "unsigned long long" => 1, 45 "float" => 1, "double" => 1, 46 "boolean" => 1, "void" => 1, 47 "Date" => 1); 48 49 my %podTypeHash = ("SVGNumber" => 1, "SVGTransform" => 1); 50 my %podTypesWithWritablePropertiesHash = ("SVGAngle" => 1, "SVGLength" => 1, "SVGMatrix" => 1, "SVGPoint" => 1, "SVGPreserveAspectRatio" => 1, "SVGRect" => 1); 51 my %stringTypeHash = ("DOMString" => 1, "AtomicString" => 1); 52 53 my %nonPointerTypeHash = ("DOMTimeStamp" => 1, "CompareHow" => 1, "SVGPaintType" => 1); 54 55 my %svgAnimatedTypeHash = ("SVGAnimatedAngle" => 1, "SVGAnimatedBoolean" => 1, 56 "SVGAnimatedEnumeration" => 1, "SVGAnimatedInteger" => 1, 57 "SVGAnimatedLength" => 1, "SVGAnimatedLengthList" => 1, 58 "SVGAnimatedNumber" => 1, "SVGAnimatedNumberList" => 1, 59 "SVGAnimatedPreserveAspectRatio" => 1, 60 "SVGAnimatedRect" => 1, "SVGAnimatedString" => 1, 61 "SVGAnimatedTransformList" => 1); 62 63 my %svgAttributesInHTMLHash = ("class" => 1, "id" => 1, "onabort" => 1, "onclick" => 1, 64 "onerror" => 1, "onload" => 1, "onmousedown" => 1, 65 "onmousemove" => 1, "onmouseout" => 1, "onmouseover" => 1, 66 "onmouseup" => 1, "onresize" => 1, "onscroll" => 1, 67 "onunload" => 1); 68 69 # Cache of IDL file pathnames. 70 my $idlFiles; 71 72 # Default constructor 73 sub new 74 { 75 my $object = shift; 76 my $reference = { }; 77 78 $useDirectories = shift; 79 $useGenerator = shift; 80 $useOutputDir = shift; 81 $useLayerOnTop = shift; 82 $preprocessor = shift; 83 $writeDependencies = shift; 84 85 bless($reference, $object); 86 return $reference; 87 } 88 89 sub StripModule($) 90 { 91 my $object = shift; 92 my $name = shift; 93 $name =~ s/[a-zA-Z0-9]*:://; 94 return $name; 95 } 96 97 sub ProcessDocument 98 { 99 my $object = shift; 100 $useDocument = shift; 101 $defines = shift; 102 103 my $ifaceName = "CodeGenerator" . $useGenerator; 104 105 # Dynamically load external code generation perl module 106 require $ifaceName . ".pm"; 107 $codeGenerator = $ifaceName->new($object, $useOutputDir, $useLayerOnTop, $preprocessor, $writeDependencies); 108 unless (defined($codeGenerator)) { 109 my $classes = $useDocument->classes; 110 foreach my $class (@$classes) { 111 print "Skipping $useGenerator code generation for IDL interface \"" . $class->name . "\".\n" if $verbose; 112 } 113 return; 114 } 115 116 # Start the actual code generation! 117 $codeGenerator->GenerateModule($useDocument, $defines); 118 119 my $classes = $useDocument->classes; 120 foreach my $class (@$classes) { 121 print "Generating $useGenerator bindings code for IDL interface \"" . $class->name . "\"...\n" if $verbose; 122 $codeGenerator->GenerateInterface($class, $defines); 123 } 124 125 $codeGenerator->finish(); 126 } 127 128 sub ForAllParents 129 { 130 my $object = shift; 131 my $dataNode = shift; 132 my $beforeRecursion = shift; 133 my $afterRecursion = shift; 134 my $parentsOnly = shift; 135 136 my $recurse; 137 $recurse = sub { 138 my $interface = shift; 139 140 for (@{$interface->parents}) { 141 my $interfaceName = $object->StripModule($_); 142 my $parentInterface = $object->ParseInterface($interfaceName, $parentsOnly); 143 144 if ($beforeRecursion) { 145 &$beforeRecursion($parentInterface) eq 'prune' and next; 146 } 147 &$recurse($parentInterface); 148 &$afterRecursion($parentInterface) if $afterRecursion; 149 } 150 }; 151 152 &$recurse($dataNode); 153 } 154 155 sub AddMethodsConstantsAndAttributesFromParentClasses 156 { 157 # Add to $dataNode all of its inherited interface members, except for those 158 # inherited through $dataNode's first listed parent. If an array reference 159 # is passed in as $parents, the names of all ancestor interfaces visited 160 # will be appended to the array. If $collectDirectParents is true, then 161 # even the names of $dataNode's first listed parent and its ancestors will 162 # be appended to $parents. 163 164 my $object = shift; 165 my $dataNode = shift; 166 my $parents = shift; 167 my $collectDirectParents = shift; 168 169 my $first = 1; 170 171 $object->ForAllParents($dataNode, sub { 172 my $interface = shift; 173 174 if ($first) { 175 # Ignore first parent class, already handled by the generation itself. 176 $first = 0; 177 178 if ($collectDirectParents) { 179 # Just collect the names of the direct ancestor interfaces, 180 # if necessary. 181 push(@$parents, $interface->name); 182 $object->ForAllParents($interface, sub { 183 my $interface = shift; 184 push(@$parents, $interface->name); 185 }, undef, 1); 186 } 187 188 # Prune the recursion here. 189 return 'prune'; 190 } 191 192 # Collect the name of this additional parent. 193 push(@$parents, $interface->name) if $parents; 194 195 print " | |> -> Inheriting " 196 . @{$interface->constants} . " constants, " 197 . @{$interface->functions} . " functions, " 198 . @{$interface->attributes} . " attributes...\n | |>\n" if $verbose; 199 200 # Add this parent's members to $dataNode. 201 push(@{$dataNode->constants}, @{$interface->constants}); 202 push(@{$dataNode->functions}, @{$interface->functions}); 203 push(@{$dataNode->attributes}, @{$interface->attributes}); 204 }); 205 } 206 207 sub GetMethodsAndAttributesFromParentClasses 208 { 209 # For the passed interface, recursively parse all parent 210 # IDLs in order to find out all inherited properties/methods. 211 212 my $object = shift; 213 my $dataNode = shift; 214 215 my @parentList = (); 216 217 $object->ForAllParents($dataNode, undef, sub { 218 my $interface = shift; 219 220 my $hash = { 221 "name" => $interface->name, 222 "functions" => $interface->functions, 223 "attributes" => $interface->attributes 224 }; 225 226 unshift(@parentList, $hash); 227 }); 228 229 return @parentList; 230 } 231 232 sub IDLFileForInterface 233 { 234 my $object = shift; 235 my $interfaceName = shift; 236 237 unless ($idlFiles) { 238 my $sourceRoot = $ENV{SOURCE_ROOT}; 239 my @directories = map { $_ = "$sourceRoot/$_" if $sourceRoot && -d "$sourceRoot/$_"; $_ } @$useDirectories; 240 241 $idlFiles = { }; 242 243 my $wanted = sub { 244 $idlFiles->{$1} = $File::Find::name if /^([A-Z].*)\.idl$/; 245 $File::Find::prune = 1 if /^\../; 246 }; 247 find($wanted, @directories); 248 } 249 250 return $idlFiles->{$interfaceName}; 251 } 252 253 sub ParseInterface 254 { 255 my $object = shift; 256 my $interfaceName = shift; 257 my $parentsOnly = shift; 258 259 return undef if $interfaceName eq 'Object'; 260 261 # Step #1: Find the IDL file associated with 'interface' 262 my $filename = $object->IDLFileForInterface($interfaceName) 263 or die("Could NOT find IDL file for interface \"$interfaceName\"!\n"); 264 265 print " | |> Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose; 266 267 # Step #2: Parse the found IDL file (in quiet mode). 268 my $parser = IDLParser->new(1); 269 my $document = $parser->Parse($filename, $defines, $preprocessor, $parentsOnly); 270 271 foreach my $interface (@{$document->classes}) { 272 return $interface if $interface->name eq $interfaceName; 273 } 274 275 die("Could NOT find interface definition for $interface in $filename"); 276 } 277 278 # Helpers for all CodeGenerator***.pm modules 279 sub IsPodType 280 { 281 my $object = shift; 282 my $type = shift; 283 284 return 1 if $podTypeHash{$type}; 285 return 1 if $podTypesWithWritablePropertiesHash{$type}; 286 return 0; 287 } 288 289 sub IsPodTypeWithWriteableProperties 290 { 291 my $object = shift; 292 my $type = shift; 293 294 return 1 if $podTypesWithWritablePropertiesHash{$type}; 295 return 0; 296 } 297 298 sub IsPrimitiveType 299 { 300 my $object = shift; 301 my $type = shift; 302 303 return 1 if $primitiveTypeHash{$type}; 304 return 0; 305 } 306 307 sub IsStringType 308 { 309 my $object = shift; 310 my $type = shift; 311 312 return 1 if $stringTypeHash{$type}; 313 return 0; 314 } 315 316 sub IsNonPointerType 317 { 318 my $object = shift; 319 my $type = shift; 320 321 return 1 if $nonPointerTypeHash{$type} or $primitiveTypeHash{$type}; 322 return 0; 323 } 324 325 sub IsSVGAnimatedType 326 { 327 my $object = shift; 328 my $type = shift; 329 330 return 1 if $svgAnimatedTypeHash{$type}; 331 return 0; 332 } 333 334 # Uppercase the first letter while respecting WebKit style guidelines. 335 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang. 336 sub WK_ucfirst 337 { 338 my ($object, $param) = @_; 339 my $ret = ucfirst($param); 340 $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/; 341 return $ret; 342 } 343 344 # Lowercase the first letter while respecting WebKit style guidelines. 345 # URL becomes url, but SetURL becomes setURL. 346 sub WK_lcfirst 347 { 348 my ($object, $param) = @_; 349 my $ret = lcfirst($param); 350 $ret =~ s/hTML/html/ if $ret =~ /^hTML/; 351 $ret =~ s/uRL/url/ if $ret =~ /^uRL/; 352 $ret =~ s/jS/js/ if $ret =~ /^jS/; 353 $ret =~ s/xML/xml/ if $ret =~ /^xML/; 354 $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/; 355 return $ret; 356 } 357 358 # Return the C++ namespace that a given attribute name string is defined in. 359 sub NamespaceForAttributeName 360 { 361 my ($object, $interfaceName, $attributeName) = @_; 362 return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName}; 363 return "HTMLNames"; 364 } 365 366 1; 367