4 # Copyright (C) 2005 Nikolas Zimmermann <wildfox@kde.org>
5 # Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
6 # Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
7 # Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
8 # Copyright (C) Research In Motion Limited 2010. All rights reserved.
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.
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.
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.
26 package CodeGenerator;
33 my $useGenerator = "";
34 my $useOutputDir = "";
35 my $useOutputHeadersDir = "";
36 my $useDirectories = "";
37 my $useLayerOnTop = 0;
39 my $writeDependencies = 0;
42 my $codeGenerator = 0;
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);
51 my %primitiveTypeHash = ( "boolean" => 1, "void" => 1, "Date" => 1);
53 my %stringTypeHash = ("DOMString" => 1, "AtomicString" => 1);
55 my %nonPointerTypeHash = ("DOMTimeStamp" => 1, "CompareHow" => 1);
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);
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,
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"
88 my %svgTypeWithWritablePropertiesNeedingTearOff = (
93 # Cache of IDL file pathnames.
102 $useDirectories = shift;
103 $useGenerator = shift;
104 $useOutputDir = shift;
105 $useOutputHeadersDir = shift;
106 $useLayerOnTop = shift;
107 $preprocessor = shift;
108 $writeDependencies = shift;
111 bless($reference, $object);
119 $name =~ s/[a-zA-Z0-9]*:://;
126 $useDocument = shift;
129 my $ifaceName = "CodeGenerator" . $useGenerator;
130 require $ifaceName . ".pm";
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;
142 # Start the actual code generation!
143 $codeGenerator->GenerateModule($useDocument, $defines);
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);
151 $codeGenerator->finish();
157 my $dataNode = shift;
158 my $beforeRecursion = shift;
159 my $afterRecursion = shift;
160 my $parentsOnly = shift;
164 my $interface = shift;
166 for (@{$interface->parents}) {
167 my $interfaceName = $object->StripModule($_);
168 my $parentInterface = $object->ParseInterface($interfaceName, $parentsOnly);
170 if ($beforeRecursion) {
171 &$beforeRecursion($parentInterface) eq 'prune' and next;
173 &$recurse($parentInterface);
174 &$afterRecursion($parentInterface) if $afterRecursion;
178 &$recurse($dataNode);
181 sub AddMethodsConstantsAndAttributesFromParentClasses
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.
191 my $dataNode = shift;
193 my $collectDirectParents = shift;
197 $object->ForAllParents($dataNode, sub {
198 my $interface = shift;
201 # Ignore first parent class, already handled by the generation itself.
204 if ($collectDirectParents) {
205 # Just collect the names of the direct ancestor interfaces,
207 push(@$parents, $interface->name);
208 $object->ForAllParents($interface, sub {
209 my $interface = shift;
210 push(@$parents, $interface->name);
214 # Prune the recursion here.
218 # Collect the name of this additional parent.
219 push(@$parents, $interface->name) if $parents;
221 print " | |> -> Inheriting "
222 . @{$interface->constants} . " constants, "
223 . @{$interface->functions} . " functions, "
224 . @{$interface->attributes} . " attributes...\n | |>\n" if $verbose;
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});
233 sub GetMethodsAndAttributesFromParentClasses
235 # For the passed interface, recursively parse all parent
236 # IDLs in order to find out all inherited properties/methods.
239 my $dataNode = shift;
243 $object->ForAllParents($dataNode, undef, sub {
244 my $interface = shift;
247 "name" => $interface->name,
248 "functions" => $interface->functions,
249 "attributes" => $interface->attributes
252 unshift(@parentList, $hash);
258 sub IDLFileForInterface
261 my $interfaceName = shift;
264 my $sourceRoot = $ENV{SOURCE_ROOT};
265 my @directories = map { $_ = "$sourceRoot/$_" if $sourceRoot && -d "$sourceRoot/$_"; $_ } @$useDirectories;
270 $idlFiles->{$1} = $File::Find::name if /^([A-Z].*)\.idl$/;
271 $File::Find::prune = 1 if /^\../;
273 find($wanted, @directories);
276 return $idlFiles->{$interfaceName};
282 my $interfaceName = shift;
283 my $parentsOnly = shift;
285 return undef if $interfaceName eq 'Object';
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");
291 print " | |> Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose;
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);
297 foreach my $interface (@{$document->classes}) {
298 return $interface if $interface->name eq $interfaceName;
301 die("Could NOT find interface definition for $interfaceName in $filename");
304 # Helpers for all CodeGenerator***.pm modules
306 sub AvoidInclusionOfType
311 # Special case: SVGPoint.h / SVGNumber.h do not exist.
312 return 1 if $type eq "SVGPoint" or $type eq "SVGNumber";
321 return 1 if $numericTypeHash{$type};
330 return 1 if $primitiveTypeHash{$type};
331 return 1 if $numericTypeHash{$type};
340 return 1 if $stringTypeHash{$type};
349 return 1 if $nonPointerTypeHash{$type} or $primitiveTypeHash{$type} or $numericTypeHash{$type};
353 sub IsSVGTypeNeedingTearOff
358 return 1 if exists $svgTypeNeedingTearOff{$type};
362 sub IsSVGTypeWithWritablePropertiesNeedingTearOff
367 return 1 if $svgTypeWithWritablePropertiesNeedingTearOff{$type};
371 sub GetSVGTypeNeedingTearOff
376 return $svgTypeNeedingTearOff{$type} if exists $svgTypeNeedingTearOff{$type};
380 sub GetSVGWrappedTypeNeedingTearOff
385 my $svgTypeNeedingTearOff = $object->GetSVGTypeNeedingTearOff($type);
386 return $svgTypeNeedingTearOff if not $svgTypeNeedingTearOff;
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<//;
398 $svgTypeNeedingTearOff =~ s/>//;
399 return $svgTypeNeedingTearOff;
402 sub IsSVGAnimatedType
407 return 1 if $svgAnimatedTypeHash{$type};
411 # Uppercase the first letter while respecting WebKit style guidelines.
412 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang.
415 my ($object, $param) = @_;
416 my $ret = ucfirst($param);
417 $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/;
422 # Lowercase the first letter while respecting WebKit style guidelines.
423 # URL becomes url, but SetURL becomes setURL.
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/;
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$/;
442 # Return the C++ namespace that a given attribute name string is defined in.
443 sub NamespaceForAttributeName
445 my ($object, $interfaceName, $attributeName) = @_;
446 return "SVGNames" if $interfaceName =~ /^SVG/ && !$svgAttributesInHTMLHash{$attributeName};
450 # Identifies overloaded functions and for each function adds an array with
451 # links to its respective overloads (including itself).
452 sub LinkOverloadedFunctions
454 my ($object, $dataNode) = @_;
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}};
466 sub AttributeNameForGetterAndSetter
468 my ($generator, $attribute) = @_;
470 my $attributeName = $attribute->signature->name;
471 my $attributeType = $generator->StripModule($attribute->signature->type);
473 # Avoid clash with C++ keyword.
474 $attributeName = "_operator" if $attributeName eq "operator";
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";
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);
484 return $attributeName;
487 sub ContentAttributeName
489 my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
491 my $contentAttributeName = $attribute->signature->extendedAttributes->{"Reflect"};
492 return undef if !$contentAttributeName;
494 $contentAttributeName = lc $generator->AttributeNameForGetterAndSetter($attribute) if $contentAttributeName eq "1";
496 my $namespace = $generator->NamespaceForAttributeName($interfaceName, $contentAttributeName);
498 $implIncludes->{"${namespace}.h"} = 1;
499 return "WebCore::${namespace}::${contentAttributeName}Attr";
502 sub GetterExpressionPrefix
504 my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
506 my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
508 if (!$contentAttributeName) {
509 return $generator->WK_lcfirst($generator->AttributeNameForGetterAndSetter($attribute)) . "(";
513 if ($attribute->signature->extendedAttributes->{"URL"}) {
514 if ($attribute->signature->extendedAttributes->{"NonEmpty"}) {
515 $functionName = "getNonEmptyURLAttribute";
517 $functionName = "getURLAttribute";
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";
526 $functionName = "getAttribute";
529 return "$functionName($contentAttributeName"
532 sub SetterExpressionPrefix
534 my ($generator, $implIncludes, $interfaceName, $attribute) = @_;
536 my $contentAttributeName = $generator->ContentAttributeName($implIncludes, $interfaceName, $attribute);
538 if (!$contentAttributeName) {
539 return "set" . $generator->WK_ucfirst($generator->AttributeNameForGetterAndSetter($attribute)) . "(";
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";
550 $functionName = "setAttribute";
553 return "$functionName($contentAttributeName, "
558 my $dataNode = shift;
559 return not $dataNode->extendedAttributes->{"DontCheckEnums"};
562 sub GenerateConditionalStringFromAttributeValue
564 my $generator = shift;
565 my $conditional = shift;
567 my $operator = ($conditional =~ /&/ ? '&' : ($conditional =~ /\|/ ? '|' : ''));
569 # Avoid duplicated conditions.
571 map { $conditions{$_} = 1 } split('\\' . $operator, $conditional);
572 return "ENABLE(" . join(") $operator$operator ENABLE(", sort keys %conditions) . ")";
574 return "ENABLE(" . $conditional . ")";
578 sub GenerateCompileTimeCheckForEnumsIfNeeded
580 my ($generator, $dataNode) = @_;
581 my $interfaceName = $dataNode->name;
583 # If necessary, check that all constants are available as enums with the same value.
584 if (ShouldCheckEnums($dataNode) && @{$dataNode->constants}) {
586 foreach my $constant (@{$dataNode->constants}) {
587 my $reflect = $constant->extendedAttributes->{"Reflect"};
588 my $name = $reflect ? $reflect : $constant->name;
589 my $value = $constant->value;
590 my $conditional = $constant->extendedAttributes->{"Conditional"};
593 my $conditionalString = $generator->GenerateConditionalStringFromAttributeValue($conditional);
594 push(@checks, "#if ${conditionalString}\n");
597 push(@checks, "COMPILE_ASSERT($value == ${interfaceName}::$name, ${interfaceName}Enum${name}IsWrongUseDontCheckEnums);\n");
600 push(@checks, "#endif\n");