File indexing completed on 2024-05-12 15:37:27

0001 #
0002 # WebKit IDL parser
0003 # 
0004 # Copyright (C) 2005 Nikolas Zimmermann <wildfox@kde.org>
0005 # Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
0006 # Copyright (C) 2007 Apple Inc. All rights reserved.
0007 # 
0008 # This library is free software; you can redistribute it and/or
0009 # modify it under the terms of the GNU Library General Public
0010 # License as published by the Free Software Foundation; either
0011 # version 2 of the License, or (at your option) any later version.
0012 # 
0013 # This library is distributed in the hope that it will be useful,
0014 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016 # Library General Public License for more details.
0017 # 
0018 # You should have received a copy of the GNU Library General Public License
0019 # aint with this library; see the file COPYING.LIB.  If not, write to
0020 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021 # Boston, MA 02110-1301, USA.
0022 # 
0023 
0024 package CodeGenerator;
0025 
0026 my $useDocument = "";
0027 my $useGenerator = "";
0028 my $useOutputDir = "";
0029 my $useDirectories = "";
0030 my $useLayerOnTop = 0;
0031 my $preprocessor;
0032 
0033 my $codeGenerator = 0;
0034 
0035 my $verbose = 0;
0036 
0037 my %primitiveTypeHash = ("int" => 1, "short" => 1, "long" => 1, 
0038                          "unsigned int" => 1, "unsigned short" => 1,
0039                          "unsigned long" => 1, "float" => 1,
0040                          "double" => 1, "boolean" => 1, "void" => 1);
0041 
0042 my %podTypeHash = ("RGBColor" => 1, "SVGLength" => 1, "SVGPoint" => 1, "SVGRect" => 1, "SVGNumber" => 1, "SVGMatrix" => 1, "SVGTransform" => 1);
0043  
0044 my %stringTypeHash = ("DOMString" => 1, "AtomicString" => 1);
0045 
0046 my %nonPointerTypeHash = ("DOMTimeStamp" => 1, "CompareHow" => 1, "SVGPaintType" => 1);
0047 
0048 my %svgAnimatedTypeHash = ("SVGAnimatedAngle" => 1, "SVGAnimatedBoolean" => 1,
0049                            "SVGAnimatedEnumeration" => 1, "SVGAnimatedInteger" => 1,
0050                            "SVGAnimatedLength" => 1, "SVGAnimatedLengthList" => 1,
0051                            "SVGAnimatedNumber" => 1, "SVGAnimatedNumberList" => 1,
0052                            "SVGAnimatedPreserveAspectRatio" => 1,
0053                            "SVGAnimatedRect" => 1, "SVGAnimatedString" => 1,
0054                            "SVGAnimatedTransformList" => 1);
0055 
0056 # Helpers for 'ScanDirectory'
0057 my $endCondition = 0;
0058 my $foundFilename = "";
0059 my @foundFilenames = ();
0060 my $ignoreParent = 1;
0061 my $defines = "";
0062 
0063 # Default constructor
0064 sub new
0065 {
0066     my $object = shift;
0067     my $reference = { };
0068 
0069     $useDirectories = shift;
0070     $useGenerator = shift;
0071     $useOutputDir = shift;
0072     $useLayerOnTop = shift;
0073     $preprocessor = shift;
0074 
0075     bless($reference, $object);
0076     return $reference;
0077 }
0078 
0079 sub StripModule($)
0080 {
0081     my $object = shift;
0082     my $name = shift;
0083     $name =~ s/[a-zA-Z0-9]*:://;
0084     return $name;
0085 }
0086 
0087 sub ProcessDocument
0088 {
0089     my $object = shift;
0090     $useDocument = shift;
0091     $defines = shift;
0092 
0093     my $ifaceName = "CodeGenerator" . $useGenerator;
0094 
0095     # Dynamically load external code generation perl module
0096     require $ifaceName . ".pm";
0097     $codeGenerator = $ifaceName->new($object, $useOutputDir, $useLayerOnTop, $preprocessor);
0098     unless (defined($codeGenerator)) {
0099         my $classes = $useDocument->classes;
0100         foreach my $class (@$classes) {
0101             print "Skipping $useGenerator code generation for IDL interface \"" . $class->name . "\".\n" if $verbose;
0102         }
0103         return;
0104     }
0105 
0106     # Start the actual code generation!
0107     $codeGenerator->GenerateModule($useDocument, $defines);
0108 
0109     my $classes = $useDocument->classes;
0110     foreach my $class (@$classes) {
0111         print "Generating $useGenerator bindings code for IDL interface \"" . $class->name . "\"...\n" if $verbose;
0112         $codeGenerator->GenerateInterface($class, $defines);
0113     }
0114 
0115     $codeGenerator->finish();
0116 }
0117 
0118 sub AddMethodsConstantsAndAttributesFromParentClasses
0119 {
0120     # For the passed interface, recursively parse all parent
0121     # IDLs in order to find out all inherited properties/methods.
0122 
0123     my $object = shift;
0124     my $dataNode = shift;
0125 
0126     my @parents = @{$dataNode->parents};
0127     my $parentsMax = @{$dataNode->parents};
0128 
0129     my $constantsRef = $dataNode->constants;
0130     my $functionsRef = $dataNode->functions;
0131     my $attributesRef = $dataNode->attributes;
0132 
0133     # Exception: For the DOM 'Node' is our topmost baseclass, not EventTargetNode.
0134     return if $parentsMax eq 1 and $parents[0] eq "EventTargetNode";
0135 
0136     foreach (@{$dataNode->parents}) {
0137         if ($ignoreParent) {
0138             # Ignore first parent class, already handled by the generation itself.
0139             $ignoreParent = 0;
0140             next;
0141         }
0142 
0143         my $interface = $object->StripModule($_);
0144 
0145         # Step #1: Find the IDL file associated with 'interface'
0146         $endCondition = 0;
0147         $foundFilename = "";
0148 
0149         foreach (@{$useDirectories}) {
0150             $object->ScanDirectory("$interface.idl", $_, $_, 0) if ($foundFilename eq "");
0151         }
0152 
0153         if ($foundFilename ne "") {
0154             print "  |  |>  Parsing parent IDL \"$foundFilename\" for interface \"$interface\"\n" if $verbose;
0155 
0156             # Step #2: Parse the found IDL file (in quiet mode).
0157             my $parser = IDLParser->new(1);
0158             my $document = $parser->Parse($foundFilename, $defines, $preprocessor);
0159 
0160             foreach my $class (@{$document->classes}) {
0161                 # Step #3: Enter recursive parent search
0162                 AddMethodsConstantsAndAttributesFromParentClasses($object, $class);
0163 
0164                 # Step #4: Collect constants & functions & attributes of this parent-class
0165                 my $constantsMax = @{$class->constants};
0166                 my $functionsMax = @{$class->functions};
0167                 my $attributesMax = @{$class->attributes};
0168 
0169                 print "  |  |>  -> Inheriting $constantsMax constants, $functionsMax functions, $attributesMax attributes...\n  |  |>\n" if $verbose;
0170 
0171                 # Step #5: Concatenate data
0172                 push(@$constantsRef, $_) foreach (@{$class->constants});
0173                 push(@$functionsRef, $_) foreach (@{$class->functions});
0174                 push(@$attributesRef, $_) foreach (@{$class->attributes});
0175             }
0176         } else {
0177             die("Could NOT find specified parent interface \"$interface\"!\n");
0178         }
0179     }
0180 }
0181 
0182 sub GetMethodsAndAttributesFromParentClasses
0183 {
0184     # For the passed interface, recursively parse all parent
0185     # IDLs in order to find out all inherited properties/methods.
0186 
0187     my $object = shift;
0188     my $dataNode = shift;
0189 
0190     my @parents = @{$dataNode->parents};
0191 
0192     return if @{$dataNode->parents} == 0;
0193 
0194     my @parentList = ();
0195 
0196     foreach (@{$dataNode->parents}) {
0197         my $interface = $object->StripModule($_);
0198         if ($interface eq "EventTargetNode") {
0199             $interface = "Node";
0200         }
0201 
0202         # Step #1: Find the IDL file associated with 'interface'
0203         $endCondition = 0;
0204         $foundFilename = "";
0205 
0206         foreach (@{$useDirectories}) {
0207             $object->ScanDirectory("${interface}.idl", $_, $_, 0) if $foundFilename eq "";
0208         }
0209 
0210         die("Could NOT find specified parent interface \"$interface\"!\n") if $foundFilename eq "";
0211 
0212         print "  |  |>  Parsing parent IDL \"$foundFilename\" for interface \"$interface\"\n" if $verbose;
0213 
0214         # Step #2: Parse the found IDL file (in quiet mode).
0215         my $parser = IDLParser->new(1);
0216         my $document = $parser->Parse($foundFilename, $defines);
0217 
0218         foreach my $class (@{$document->classes}) {
0219             # Step #3: Enter recursive parent search
0220             push(@parentList, GetMethodsAndAttributesFromParentClasses($object, $class));
0221 
0222             # Step #4: Collect constants & functions & attributes of this parent-class
0223 
0224             # print "  |  |>  -> Inheriting $functionsMax functions amd $attributesMax attributes...\n  |  |>\n" if $verbose;
0225             my $hash = {
0226                 "name" => $class->name,
0227                 "functions" => $class->functions,
0228                 "attributes" => $class->attributes
0229             };
0230 
0231             # Step #5: Concatenate data
0232             unshift(@parentList, $hash);
0233         }
0234     }
0235 
0236     return @parentList;
0237 }
0238 
0239 sub ParseInterface
0240 {
0241     my ($object, $interfaceName) = @_;
0242 
0243     # Step #1: Find the IDL file associated with 'interface'
0244     $endCondition = 0;
0245     $foundFilename = "";
0246 
0247     foreach (@{$useDirectories}) {
0248         $object->ScanDirectory("${interfaceName}.idl", $_, $_, 0) if $foundFilename eq "";
0249     }
0250     die "Could NOT find specified parent interface \"$interfaceName\"!\n" if $foundFilename eq "";
0251 
0252     print "  |  |>  Parsing parent IDL \"$foundFilename\" for interface \"$interfaceName\"\n" if $verbose;
0253 
0254     # Step #2: Parse the found IDL file (in quiet mode).
0255     my $parser = IDLParser->new(1);
0256     my $document = $parser->Parse($foundFilename, $defines);
0257 
0258     foreach my $interface (@{$document->classes}) {
0259         return $interface if $interface->name eq $interfaceName;
0260     }
0261 
0262     die "Interface definition not found";
0263 }
0264 
0265 # Helpers for all CodeGenerator***.pm modules
0266 sub IsPodType
0267 {
0268     my $object = shift;
0269     my $type = shift;
0270 
0271     return 1 if $podTypeHash{$type};
0272     return 0;
0273 }
0274 
0275 sub IsPrimitiveType
0276 {
0277     my $object = shift;
0278     my $type = shift;
0279 
0280     return 1 if $primitiveTypeHash{$type};
0281     return 0;
0282 }
0283 
0284 sub IsStringType
0285 {
0286     my $object = shift;
0287     my $type = shift;
0288 
0289     return 1 if $stringTypeHash{$type};
0290     return 0;
0291 }
0292 
0293 sub IsNonPointerType
0294 {
0295     my $object = shift;
0296     my $type = shift;
0297 
0298     return 1 if $nonPointerTypeHash{$type} or $primitiveTypeHash{$type};
0299     return 0;
0300 }
0301 
0302 sub IsSVGAnimatedType
0303 {
0304     my $object = shift;
0305     my $type = shift;
0306 
0307     return 1 if $svgAnimatedTypeHash{$type};
0308     return 0; 
0309 }
0310 
0311 # Internal Helper
0312 sub ScanDirectory
0313 {
0314     my $object = shift;
0315 
0316     my $interface = shift;
0317     my $directory = shift;
0318     my $useDirectory = shift;
0319     my $reportAllFiles = shift;
0320 
0321     return if ($endCondition eq 1) and ($reportAllFiles eq 0);
0322 
0323     my $sourceRoot = $ENV{SOURCE_ROOT};
0324     my $thisDir = $sourceRoot ? "$sourceRoot/$directory" : $directory;
0325 
0326     if (!opendir(DIR, $thisDir)) {
0327         opendir(DIR, $directory) or die "[ERROR] Can't open directory $thisDir or $directory: \"$!\"\n";
0328         $thisDir = $directory;
0329     }
0330 
0331     my @names = readdir(DIR) or die "[ERROR] Cant't read directory $thisDir \"$!\"\n";
0332     closedir(DIR);
0333 
0334     foreach my $name (@names) {
0335         # Skip if we already found the right file or
0336         # if we encounter 'exotic' stuff (ie. '.', '..', '.svn')
0337         next if ($endCondition eq 1) or ($name =~ /^\./);
0338 
0339         # Recurisvely enter directory
0340         if (-d "$thisDir/$name") {
0341             $object->ScanDirectory($interface, "$directory/$name", $useDirectory, $reportAllFiles);
0342             next;
0343         }
0344 
0345         # Check wheter we found the desired file
0346         my $condition = ($name eq $interface);
0347         $condition = 1 if ($interface eq "allidls") and ($name =~ /\.idl$/);
0348 
0349         if ($condition) {
0350             $foundFilename = "$thisDir/$name";
0351 
0352             if ($reportAllFiles eq 0) {
0353                 $endCondition = 1;
0354             } else {
0355                 push(@foundFilenames, $foundFilename);
0356             }
0357         }
0358     }
0359 }
0360 
0361 # Uppercase the first letter while respecting WebKit style guidelines. 
0362 # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang.
0363 sub WK_ucfirst
0364 {
0365     my ($object, $param) = @_;
0366     my $ret = ucfirst($param);
0367     $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/;
0368     return $ret;
0369 }
0370 
0371 # Lowercase the first letter while respecting WebKit style guidelines. 
0372 # URL becomes url, but SetURL becomes setURL.
0373 sub WK_lcfirst
0374 {
0375     my ($object, $param) = @_;
0376     my $ret = lcfirst($param);
0377     $ret =~ s/uRL/url/ if $ret =~ /^uRL/;
0378     $ret =~ s/jS/js/ if $ret =~ /^jS/;
0379     return $ret;
0380 }
0381 
0382 1;