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, 2008, 2009, 2010 Apple Inc. All rights reserved. 7 # Copyright (C) 2009 Cameron McCormack <cam (at] mcc.id.au> 8 # Copyright (C) Research In Motion Limited 2010. All rights reserved. 9 # 10 # This library is free software; you can redistribute it and/or 11 # modify it under the terms of the GNU Library General Public 12 # License as published by the Free Software Foundation; either 13 # version 2 of the License, or (at your option) any later version. 14 # 15 # This library is distributed in the hope that it will be useful, 16 # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 # Library General Public License for more details. 19 # 20 # You should have received a copy of the GNU Library General Public License 21 # along with this library; see the file COPYING.LIB. If not, write to 22 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 # Boston, MA 02110-1301, USA. 24 # 25 26 package CodeGenerator; 27 28 use strict; 29 30 use File::Find; 31 32 my $useDocument = ""; 33 my $useGenerator = ""; 34 my $useOutputDir = ""; 35 my $useOutputHeadersDir = ""; 36 my $useDirectories = ""; 37 my $useLayerOnTop = 0; 38 my $preprocessor; 39 my $writeDependencies = 0; 40 my $defines = ""; 41 42 my $codeGenerator = 0; 43 44 my $verbose = 0; 45 46 my %numericTypeHash = ("int" => 1, "short" => 1, "long" => 1, "long long" => 1, 47 "unsigned int" => 1, "unsigned short" => 1, 48 "unsigned long" => 1, "unsigned long long" => 1, 49 "float" => 1, "double" => 1); 50 51 my %primitiveTypeHash = ( "boolean" => 1, "void" => 1, "Date" => 1); 52 53 my %stringTypeHash = ("DOMString" => 1, "AtomicString" => 1); 54 55 my %nonPointerTypeHash = ("DOMTimeStamp" => 1, "CompareHow" => 1); 56 57 my %svgAnimatedTypeHash = ("SVGAnimatedAngle" => 1, "SVGAnimatedBoolean" => 1, 58 "SVGAnimatedEnumeration" => 1, "SVGAnimatedInteger" => 1, 59 "SVGAnimatedLength" => 1, "SVGAnimatedLengthList" => 1, 60 "SVGAnimatedNumber" => 1, "SVGAnimatedNumberList" => 1, 61 "SVGAnimatedPreserveAspectRatio" => 1, 62 "SVGAnimatedRect" => 1, "SVGAnimatedString" => 1, 63 "SVGAnimatedTransformList" => 1); 64 65 my %svgAttributesInHTMLHash = ("class" => 1, "id" => 1, "onabort" => 1, "onclick" => 1, 66 "onerror" => 1, "onload" => 1, "onmousedown" => 1, 67 "onmousemove" => 1, "onmouseout" => 1, "onmouseover" => 1, 68 "onmouseup" => 1, "onresize" => 1, "onscroll" => 1, 69 "onunload" => 1); 70 71 my %svgTypeNeedingTearOff = ( 72 "SVGAngle" => "SVGPropertyTearOff<SVGAngle>", 73 "SVGLength" => "SVGPropertyTearOff<SVGLength>", 74 "SVGLengthList" => "SVGListPropertyTearOff<SVGLengthList>", 75 "SVGMatrix" => "SVGPropertyTearOff<SVGMatrix>", 76 "SVGNumber" => "SVGPropertyTearOff<float>", 77 "SVGNumberList" => "SVGListPropertyTearOff<SVGNumberList>", 78 "SVGPathSegList" => "SVGPathSegListPropertyTearOff", 79 "SVGPoint" => "SVGPropertyTearOff<FloatPoint>", 80 "SVGPointList" => "SVGListPropertyTearOff<SVGPointList>", 81 "SVGPreserveAspectRatio" => "SVGPropertyTearOff<SVGPreserveAspectRatio>", 82 "SVGRect" => "SVGPropertyTearOff<FloatRect>", 83 "SVGStringList" => "SVGStaticListPropertyTearOff<SVGStringList>", 84 "SVGTransform" => "SVGPropertyTearOff<SVGTransform>", 85 "SVGTransformList" => "SVGTransformListPropertyTearOff" 86 ); 87 88 my %svgTypeWithWritablePropertiesNeedingTearOff = ( 89 "SVGPoint" => 1, 90 "SVGMatrix" => 1 91 ); 92 93 # Cache of IDL file pathnames. 94 my $idlFiles; 95 96 # Default constructor 97 sub new 98 { 99 my $object = shift; 100 my $reference = { }; 101 102 $useDirectories = shift; 103 $useGenerator = shift; 104 $useOutputDir = shift; 105 $useOutputHeadersDir = shift; 106 $useLayerOnTop = shift; 107 $preprocessor = shift; 108 $writeDependencies = shift; 109 $verbose = shift; 110 111 bless($reference, $object); 112 return $reference; 113 } 114 115 sub StripModule($) 116 { 117 my $object = shift; 118 my $name = shift; 119 $name =~ s/[a-zA-Z0-9]*:://; 120 return $name; 121 } 122 123 sub ProcessDocument 124 { 125 my $object = shift; 126 $useDocument = shift; 127 $defines = shift; 128 129 my $ifaceName = "CodeGenerator" . $useGenerator; 130 require $ifaceName . ".pm"; 131 132 # Dynamically load external code generation perl module 133 $codeGenerator = $ifaceName->new($object, $useOutputDir, $useOutputHeadersDir, $useLayerOnTop, $preprocessor, $writeDependencies, $verbose); 134 unless (defined($codeGenerator)) { 135 my $classes = $useDocument->classes; 136 foreach my $class (@$classes) { 137 print "Skipping $useGenerator code generation for IDL interface \"" . $class->name . "\".\n" if $verbose; 138 } 139 return; 140 } 141 142 # Start the actual code generation! 143 $codeGenerator->GenerateModule($useDocument, $defines); 144 145 my $classes = $useDocument->classes; 146 foreach my $class (@$classes) { 147 print "Generating $useGenerator bindings code for IDL interface \"" . $class->name . "\"...\n" if $verbose; 148 $codeGenerator->GenerateInterface($class, $defines); 149 } 150 151 $codeGenerator->finish(); 152 } 153 154 sub ForAllParents 155 { 156 my $object = shift; 157 my $dataNode = shift; 158 my $beforeRecursion = shift; 159 my $afterRecursion = shift; 160 my $parentsOnly = shift; 161 162 my $recurse; 163 $recurse = sub { 164 my $interface = shift; 165 166 for (@{$interface->parents}) { 167 my $interfaceName = $object->StripModule($_); 168 my $parentInterface = $object->ParseInterface($interfaceName, $parentsOnly); 169 170 if ($beforeRecursion) { 171 &$beforeRecursion($parentInterface) eq 'prune' and next; 172 } 173 &$recurse($parentInterface); 174 &$afterRecursion($parentInterface) if $afterRecursion; 175 } 176 }; 177 178 &$recurse($dataNode); 179 } 180 181 sub AddMethodsConstantsAndAttributesFromParentClasses 182 { 183 # Add to $dataNode all of its inherited interface members, except for those 184 # inherited through $dataNode's first listed parent. If an array reference 185 # is passed in as $parents, the names of all ancestor interfaces visited 186 # will be appended to the array. If $collectDirectParents is true, then 187 # even the names of $dataNode's first listed parent and its ancestors will 188 # be appended to $parents. 189 190 my $object = shift; 191 my $dataNode = shift; 192 my $parents = shift; 193 my $collectDirectParents = shift; 194 195 my $first = 1; 196 197 $object->ForAllParents($dataNode, sub { 198 my $interface = shift; 199 200 if ($first) { 201 # Ignore first parent class, already handled by the generation itself. 202 $first = 0; 203 204 if ($collectDirectParents) { 205 # Just collect the names of the direct ancestor interfaces, 206 # if necessary. 207 push(@$parents, $interface->name); 208 $object->ForAllParents($interface, sub { 209 my $interface = shift; 210 push(@$parents, $interface->name); 211 }, undef, 1); 212 } 213 214 # Prune the recursion here. 215 return 'prune'; 216 } 217 218 # Collect the name of this additional parent. 219 push(@$parents, $interface->name) if $parents; 220 221 print " | |> -> Inheriting " 222 . @{$interface->constants} . " constants, " 223 . @{$interface->functions} . " functions, " 224 . @{$interface->attributes} . " attributes...\n | |>\n" if $verbose; 225 226 # Add this parent's members to $dataNode. 227 push(@{$dataNode->constants}, @{$interface->constants}); 228 push(@{$dataNode->functions}, @{$interface->functions}); 229 push(@{$dataNode->attributes}, @{$interface->attributes}); 230 }); 231 } 232 233 sub GetMethodsAndAttributesFromParentClasses 234 { 235 # For the passed interface, recursively parse all parent 236 # IDLs in order to find out all inherited properties/methods. 237 238 my $object = shift; 239 my $dataNode = shift; 240 241 my @parentList = (); 242 243 $object->ForAllParents($dataNode, undef, sub { 244 my $interface = shift; 245 246 my $hash = { 247 "name" => $interface->name, 248 "functions" => $interface->functions, 249 "attributes" => $interface->attributes 250 }; 251 252 unshift(@parentList, $hash); 253 }); 254 255 return @parentList; 256 } 257 258 sub IDLFileForInterface 259 { 260 my $object = shift; 261 my $interfaceName = shift; 262 263 unless ($idlFiles) { 264 my $sourceRoot = $ENV{SOURCE_ROOT}; 265 my @directories = map { $_ = "$sourceRoot/$_" if $sourceRoot && -d "$sourceRoot/$_"; $_ } @$useDirectories; 266 267 $idlFiles = { }; 268 269 my $wanted = sub { 270 $idlFiles->{$1} = $File::Find::name if /^([A-Z].*)\.idl$/; 271 $File::Find::prune = 1 if /^\../; 272 }; 273 find($wanted, @directories); 274 } 275 276 return $idlFiles->{$interfaceName}; 277 } 278 279 sub ParseInterface 280 { 281 my $object = shift; 282 my $interfaceName = shift; 283 my $parentsOnly = shift; 284 285 return undef if $interfaceName eq 'Object'; 286 287 # Step #1: Find the IDL file associated with 'interface' 288 my $filename = $object->IDLFileForInterface($interfaceName) 289 or die("Could NOT find IDL file for interface \"$interfaceName\"!\n"); 290 291 print " | |> Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose; 292 293 # Step #2: Parse the found IDL file (in quiet mode). 294 my $parser = IDLParser->new(1); 295 my $document = $parser->Parse($filename, $defines, $preprocessor, $parentsOnly); 296 297 foreach my $interface (@{$document->classes}) { 298 return $interface if $interface->name eq $interfaceName; 299 } 300 301 die("Could NOT find interface definition for $interfaceName in $filename"); 302 } 303 304 # Helpers for all CodeGenerator***.pm modules 305 306 sub AvoidInclusionOfType 307 { 308 my $object = shift; 309 my $type = shift; 310 311 # Special case: SVGPoint.h / SVGNumber.h do not exist. 312 return 1 if $type eq "SVGPoint" or $type eq "SVGNumber"; 313 return 0; 314 } 315 316 sub IsNumericType 317 { 318 my $object = shift; 319 my $type = shift; 320 321 return 1 if $numericTypeHash{$type}; 322 return 0; 323 } 324 325 sub IsPrimitiveType 326 { 327 my $object = shift; 328 my $type = shift; 329 330 return 1 if $primitiveTypeHash{$type}; 331 return 1 if $numericTypeHash{$type}; 332 return 0; 333 } 334 335 sub IsStringType 336 { 337 my $object = shift; 338 my $type = shift; 339 340 return 1 if $stringTypeHash{$type}; 341 return 0; 342 } 343 344 sub IsNonPointerType 345 { 346 my $object = shift; 347 my $type = shift; 348 349 return 1 if $nonPointerTypeHash{$type} or $primitiveTypeHash{$type} or $numericTypeHash{$type}; 350 return 0; 351 } 352 353 sub IsSVGTypeNeedingTearOff 354 { 355 my $object = shift; 356 my $type = shift; 357 358 return 1 if exists $svgTypeNeedingTearOff{$type}; 359 return 0; 360 } 361 362 sub IsSVGTypeWithWritablePropertiesNeedingTearOff 363 { 364 my $object = shift; 365 my $type = shift; 366 367 return 1 if $svgTypeWithWritablePropertiesNeedingTearOff{$type}; 368 return 0; 369 } 370 371 sub GetSVGTypeNeedingTearOff 372 { 373 my $object = shift; 374 my $type = shift; 375 376 return $svgTypeNeedingTearOff{$type} if exists $svgTypeNeedingTearOff{$type}; 377 return undef; 378 } 379 380 sub GetSVGWrappedTypeNeedingTearOff 381 { 382 my $object = shift; 383 my $type = shift; 384 385 my $svgTypeNeedingTearOff = $object->GetSVGTypeNeedingTearOff($type); 386 return $svgTypeNeedingTearOff if not $svgTypeNeedingTearOff; 387 388 if ($svgTypeNeedingTearOff =~ /SVGPropertyTearOff/) { 389 $svgTypeNeedingTearOff =~ s/SVGPropertyTearOff<//; 390 } elsif ($svgTypeNeedingTearOff =~ /SVGListPropertyTearOff/) { 391 $svgTypeNeedingTearOff =~ s/SVGListPropertyTearOff<//; 392 } elsif ($svgTypeNeedingTearOff =~ /SVGStaticListPropertyTearOff/) { 393 $svgTypeNeedingTearOff =~ s/SVGStaticListPropertyTearOff<//; 394 } elsif ($svgTypeNeedingTearOff =~ /SVGTransformListPropertyTearOff/) { 395 $svgTypeNeedingTearOff =~ s/SVGTransformListPropertyTearOff<//; 396 } 397 398 $svgTypeNeedingTearOff =~ s/>//; 399 return $svgTypeNeedingTearOff; 400 } 401 402 sub IsSVGAnimatedType 403 { 404 my $object = shift; 405 my $type = shift; 406 407 return 1 if $svgAnimatedTypeHash{$type}; 408 return 0; 409 } 410 411 # Uppercase the first letter while respecting WebKit style guidelines. 412 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang. 413 sub WK_ucfirst 414 { 415 my ($object, $param) = @_; 416 my $ret = ucfirst($param); 417 $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/; 418 419 return $ret; 420 } 421 422 # Lowercase the first letter while respecting WebKit style guidelines. 423 # URL becomes url, but SetURL becomes setURL. 424 sub WK_lcfirst 425 { 426 my ($object, $param) = @_; 427 my $ret = lcfirst($param); 428 $ret =~ s/hTML/html/ if $ret =~ /^hTML/; 429 $ret =~ s/uRL/url/ if $ret =~ /^uRL/; 430 $ret =~ s/jS/js/ if $ret =~ /^jS/; 431 $ret =~ s/xML/xml/ if $ret =~ /^xML/; 432 $ret =~ s/xSLT/xslt/ if $ret =~ /^xSLT/; 433 434 # For HTML5 FileSystem API Flags attributes. 435 # (create is widely used to instantiate an object and must be avoided.) 436 $ret =~ s/^create/isCreate/ if $ret =~ /^create$/; 437 $ret =~ s/^exclusive/isExclusive/ if $ret =~ /^exclusive$/; 438 439 return $ret; 440 } 441 442 # Return the C++ namespace that a given attribute name string is defined in. 443 sub NamespaceForAttributeName 444 { 445 my ($object, $interfaceName, $attributeName) = @_; 446 return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName}; 447 return "HTMLNames"; 448 } 449 450 # Identifies overloaded functions and for each function adds an array with 451 # links to its respective overloads (including itself). 452 sub LinkOverloadedFunctions 453 { 454 my ($object, $dataNode) = @_; 455 456 my %nameToFunctionsMap = (); 457 foreach my $function (@{$dataNode->functions}) { 458 my $name = $function->signature->name; 459 $nameToFunctionsMap{$name} = [] if !exists $nameToFunctionsMap{$name}; 460 push(@{$nameToFunctionsMap{$name}}, $function); 461 $function->{overloads} = $nameToFunctionsMap{$name}; 462 $function->{overloadIndex} = @{$nameToFunctionsMap{$name}}; 463 } 464 } 465 466 sub AttributeNameForGetterAndSetter 467 { 468 my ($generator, $attribute) = @_; 469 470 my $attributeName = $attribute->signature->name; 471 my $attributeType = $generator->StripModule($attribute->signature->type); 472 473 # Avoid clash with C++ keyword. 474 $attributeName = "_operator" if $attributeName eq "operator"; 475 476 # SVGAElement defines a non-virtual "String& target() const" method which clashes with "virtual String target() const" in Element. 477 # To solve this issue the SVGAElement method was renamed to "svgTarget", take care of that when calling this method. 478 $attributeName = "svgTarget" if $attributeName eq "target" and $attributeType eq "SVGAnimatedString"; 479 480 # SVG animated types need to use a special attribute name. 481 # The rest of the special casing for SVG animated types is handled in the language-specific code generators. 482 $attributeName .= "Animated" if $generator->IsSVGAnimatedType($attributeType); 483 484 return $attributeName; 485 } 486 487 sub ContentAttributeName 488 { 489 my ($generator, $implIncludes, $interfaceName, $attribute) = @_; 490 491 my $contentAttributeName = $attribute->signature->extendedAttributes->{"Reflect"}; 492 return undef if !$contentAttributeName; 493 494 $contentAttributeName = lc $generator->AttributeNameForGetterAndSetter($attribute) if $contentAttributeName eq "1"; 495 496 my $namespace = $generator->NamespaceForAttributeName($interfaceName, $contentAttributeName); 497 498 $implIncludes->{"${namespace}.h"} = 1; 499 return "WebCore::${namespace}::${contentAttributeName}Attr"; 500 } 501 502 sub GetterExpressionPrefix 503 { 504 my ($generator, $implIncludes, $interfaceName, $attribute) = @_; 505 506 my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute); 507 508 if (!$contentAttributeName) { 509 return $generator->WK_lcfirst($generator->AttributeNameForGetterAndSetter($attribute)) . "("; 510 } 511 512 my $functionName; 513 if ($attribute->signature->extendedAttributes->{"URL"}) { 514 if ($attribute->signature->extendedAttributes->{"NonEmpty"}) { 515 $functionName = "getNonEmptyURLAttribute"; 516 } else { 517 $functionName = "getURLAttribute"; 518 } 519 } elsif ($attribute->signature->type eq "boolean") { 520 $functionName = "hasAttribute"; 521 } elsif ($attribute->signature->type eq "long") { 522 $functionName = "getIntegralAttribute"; 523 } elsif ($attribute->signature->type eq "unsigned long") { 524 $functionName = "getUnsignedIntegralAttribute"; 525 } else { 526 $functionName = "getAttribute"; 527 } 528 529 return "$functionName($contentAttributeName" 530 } 531 532 sub SetterExpressionPrefix 533 { 534 my ($generator, $implIncludes, $interfaceName, $attribute) = @_; 535 536 my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute); 537 538 if (!$contentAttributeName) { 539 return "set" . $generator->WK_ucfirst($generator->AttributeNameForGetterAndSetter($attribute)) . "("; 540 } 541 542 my $functionName; 543 if ($attribute->signature->type eq "boolean") { 544 $functionName = "setBooleanAttribute"; 545 } elsif ($attribute->signature->type eq "long") { 546 $functionName = "setIntegralAttribute"; 547 } elsif ($attribute->signature->type eq "unsigned long") { 548 $functionName = "setUnsignedIntegralAttribute"; 549 } else { 550 $functionName = "setAttribute"; 551 } 552 553 return "$functionName($contentAttributeName, " 554 } 555 556 sub ShouldCheckEnums 557 { 558 my $dataNode = shift; 559 return not $dataNode->extendedAttributes->{"DontCheckEnums"}; 560 } 561 562 sub GenerateCompileTimeCheckForEnumsIfNeeded 563 { 564 my ($object, $dataNode) = @_; 565 my $interfaceName = $dataNode->name; 566 my @checks = (); 567 # If necessary, check that all constants are available as enums with the same value. 568 if (ShouldCheckEnums($dataNode) && @{$dataNode->constants}) { 569 push(@checks, "\n"); 570 foreach my $constant (@{$dataNode->constants}) { 571 my $name = $constant->name; 572 my $value = $constant->value; 573 push(@checks, "COMPILE_ASSERT($value == ${interfaceName}::$name, ${interfaceName}Enum${name}IsWrongUseDontCheckEnums);\n"); 574 } 575 push(@checks, "\n"); 576 } 577 return @checks; 578 } 579 580 1; 581