#*************************************************************************** # kalyptusCxxToEMA.pm - Generates class info for ECMA bindings in KDE # ------------------- # begin : Fri Jan 25 12:00:00 2000 # copyright : (C) 2002 Lost Highway Ltd. All Rights Reserved. # email : david@mandrakesoft.com # author : David Faure. #***************************************************************************/ #/*************************************************************************** # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU General Public License as published by * # * the Free Software Foundation; either version 2 of the License, or * # * (at your option) any later version. * # * * #***************************************************************************/ package kalyptusCxxToECMA; use File::Path; use File::Basename; use Carp; use Ast; use kdocAstUtil; use kdocUtil; use Iter; use kalyptusDataDict; use strict; no strict "subs"; use vars qw/ $libname $rootnode $outputdir $opt $debug %skippedClasses %hasHashTable %hasFunctions %hasBridge %hasGet %hasPut/; sub writeDoc { ( $libname, $rootnode, $outputdir, $opt ) = @_; print STDERR "Starting writeDoc for $libname...\n"; $debug = $main::debuggen; mkpath( $outputdir ) unless -f $outputdir; # Preparse everything, to prepare some additional data in the classes and methods Iter::LocalCompounds( $rootnode, sub { preParseClass( shift ); } ); print STDERR "Writing generateddata.cpp...\n"; writeInheritanceFile($rootnode); print STDERR "Done.\n"; } =head2 preParseClass Called for each class =cut sub preParseClass { my( $classNode ) = @_; my $className = join( "::", kdocAstUtil::heritage($classNode) ); if ( $className =~ /Proto$/ ) { my $c = $className; $c =~ s/Proto$//; #print STDERR "$c -> $className\n"; $hasFunctions{$c} = $className; # Associate class -> proto #print STDERR "Found proto $className -> skipping\n"; $skippedClasses{$className} = 1; # Skip proto return; } if( $classNode->{Access} eq "private" || $classNode->{Access} eq "protected" || # e.g. TQPixmap::TQPixmapData exists $classNode->{Tmpl} || $className eq 'KJS' || $className eq 'KSVG' || # namespaces $className =~ /^KSVG::KSVG/ || $className eq 'KSVG::CachedGlyph' || # Not DOM classes $className eq 'KSVG::ImageStreamMap' || $className eq 'KSVG::SVGBBoxTarget' || $className eq 'KSVG::SVGLoader' || $className eq 'KSVG::SVGElementImpl::MouseEvent' || $className eq 'KSVG::SVGRegisteredEventListener' || $classNode->{NodeType} eq 'union' # Skip unions for now, e.g. TQPDevCmdParam ) { print STDERR "Skipping $className "; #if ($debug); #print STDERR "(nothing in it)\n" if ( $#{$classNode->{Kids}} < 0 ); if ( exists $classNode->{Tmpl} ) { print STDERR "(template)\n"; } elsif ( $classNode->{Access} eq "private" or $classNode->{Access} eq "protected" ) { print STDERR "(not public)\n"; } elsif ( $classNode->{NodeType} eq 'union') { print STDERR "(union)\n"; } elsif ( $className =~ /^KSVG::KSVG/ || $className eq 'KSVG::CachedGlyph' ) { print STDERR "(not a DOM class)\n"; } else { print STDERR "\n"; } $skippedClasses{$className} = 1; #delete $classNode->{Compound}; # Cheat, to get it excluded from Iter::LocalCompounds # Can't do that, it's recursive (KSVG::* disappears) return; } # Iterate over methods Iter::MembersByType ( $classNode, undef, sub { my ($classNode, $methodNode ) = @_; if ( $methodNode->{NodeType} eq 'method' ) { if ( $methodNode->{astNodeName} eq 'get' ) { $hasGet{$className} = '1'; } elsif ( $methodNode->{astNodeName} eq 'getforward' ) { $hasGet{$className} = '2'; } elsif ( $methodNode->{astNodeName} eq 'put' ) { $hasPut{$className} = '1'; } elsif ( $methodNode->{astNodeName} eq 'putforward' ) { $hasPut{$className} = '2'; } elsif ( $methodNode->{astNodeName} eq 'getValueProperty' ) { $hasHashTable{$className} = '1'; } elsif ( $methodNode->{astNodeName} eq 'bridge' ) { $hasBridge{$className} = '1'; } } } ); } # List of all super-classes for a given class sub superclass_list($) { my $classNode = shift; my @super; Iter::Ancestors( $classNode, $rootnode, undef, undef, sub { push @super, @_[0]; push @super, superclass_list( @_[0] ); }, undef ); return @super; } # Adds the header for node $1 to be included in $2 if not already there # Prints out debug stuff if $3 sub addIncludeForClass($$$) { my ( $node, $addInclude, $debugMe ) = @_; my $sourcename = $node->{Source}->{astNodeName}; $sourcename =~ s!.*/(.*)!$1!m; unless ( defined $addInclude->{$sourcename} ) { print " Including $sourcename\n" if ($debugMe); $addInclude->{$sourcename} = 1; } else { print " $sourcename already included.\n" if ($debugMe); } } =head2 Write out the smokedata.cpp file containing all the arrays. =cut sub writeInheritanceFile($) { my $rootnode = shift; # Make list of classes my %allIncludes; # list of all header files for all classes my @classlist; push @classlist, ""; # Prepend empty item for "no class" Iter::LocalCompounds( $rootnode, sub { my $classNode = $_[0]; my $className = join( "::", kdocAstUtil::heritage($classNode) ); return if ( defined $skippedClasses{$className} ); push @classlist, $className; $classNode->{ClassIndex} = $#classlist; addIncludeForClass( $classNode, \%allIncludes, undef ); } ); my %classidx = do { my $i = 0; map { $_ => $i++ } @classlist }; #foreach my $debugci (keys %classidx) { # print STDERR "$debugci: $classidx{$debugci}\n"; #} my $file = "$outputdir/generateddata.cpp"; open OUT, ">$file" or die "Couldn't create $file\n"; print OUT "#include \n"; print OUT "#include \n"; foreach my $incl (keys %allIncludes) { die if $incl eq ''; print OUT "#include <$incl>\n"; } print OUT "\n"; # Prepare descendants information for each class my %descendants; # classname -> list of descendant nodes #my $SVGElementImplNode; Iter::LocalCompounds( $rootnode, sub { my $classNode = shift; my $className = join( "::", kdocAstUtil::heritage($classNode) ); # Get _all_ superclasses (up any number of levels) # and store that $classNode is a descendant of $s my @super = superclass_list($classNode); for my $s (@super) { my $superClassName = join( "::", kdocAstUtil::heritage($s) ); Ast::AddPropList( \%descendants, $superClassName, $classNode ); } # Found SVGElementImpl itself if ( $className eq 'KSVG::SVGElementImpl' ) { $classNode->{IsSVGElement} = '1'; #$SVGElementImplNode = $classNode; } } ); # Mark all SVGElementImpl descendants as svg elements if ( defined $descendants{'KSVG::SVGElementImpl'} ) { my @desc = @{$descendants{'KSVG::SVGElementImpl'}}; for my $d (@desc) { $d->{IsSVGElement} = '1' ; print STDERR $d->{astNodeName}. " is an SVGElement\n" if($debug); } } # Propagate $hasPut and $hasGet Iter::LocalCompounds( $rootnode, sub { my $classNode = shift; my $className = join( "::", kdocAstUtil::heritage($classNode) ); if ( defined $descendants{$className} ) { my @desc = @{$descendants{$className}}; for my $d (@desc) { my $c = join( "::", kdocAstUtil::heritage($d) ); $hasPut{$c} = '2' if (!$hasPut{$c} && $hasPut{$className}); $hasGet{$c} = '2' if (!$hasGet{$c} && $hasGet{$className}); } } # This code prints out the base classes - useful for KSVG_BASECLASS_GET if ( 0 && defined $descendants{$className} ) { my $baseClass = 1; Iter::Ancestors( $classNode, $rootnode, sub { # called if no ancestors }, undef, sub { # called for each ancestor my $superClassName = join( "::", kdocAstUtil::heritage($_[0]) ); $baseClass = 0 if ( $superClassName ne '' ); # happens with unknown parents; } ); print STDERR "$className is a base class.\n" if ($baseClass); } } ); # Write namespaces print OUT "using namespace KSVG;\n"; print OUT "using namespace KJS;\n\n"; # Write classInfos print OUT "// For all classes with generated data: the ClassInfo\n"; Iter::LocalCompounds( $rootnode, sub { my $classNode = shift; my $className = join( "::", kdocAstUtil::heritage($classNode) ); # We use namespace declartions! my $printName = $className; $printName =~ s/KSVG:://; # Write tagNames if ($hasBridge{$className}) { my $tagName = $printName; $tagName =~ s/SVG//; $tagName =~ s/ElementImpl//; $tagName = lcfirst($tagName); # Special cases, otherwhise they'd be "tRef" / "tSpan" / "sVG" if($printName eq "SVGTRefElementImpl" or $printName eq "SVGTSpanElementImpl" or $printName eq "SVGSVGElementImpl") { $tagName =~ tr/A-Z/a-z/; } while($tagName =~ /[A-Z]/g) { # Special case: color-profile instead of ie. animateColor/animateMotion if($printName eq "SVGColorProfileElementImpl") { $tagName = substr($tagName, 0, pos($tagName) - 1) . "-" . lc($&) . substr($tagName, pos($tagName)); } } # Special cases: gradient & poly aren't element! if($tagName ne "" and $tagName ne "gradient" and $tagName ne "poly") { print OUT "const DOM::DOMString ${printName}::s_tagName = \"$tagName\";\n"; } } # Skip classes without KSVG_GENERATEDDATA if (!$hasGet{$className} && !$skippedClasses{$className}) { $skippedClasses{$className} = '1' ; print STDERR "Skipping $className, no get()\n"; } return if ( defined $skippedClasses{$className} ); my $ok = $hasHashTable{$className}; print STDERR "$className has get() but no hashtable - TODO\n" if (!$ok && $hasGet{$className} eq '1'); print OUT "const ClassInfo ${printName}::s_classInfo = {\"$className\",0,"; if ($ok) { print OUT "\&${printName}::s_hashTable"; } else { print OUT "0"; } print OUT ",0};\n"; #die "problem with $className" unless defined $classinherit{$className}; #print OUT "const short int ${className}::s_inheritanceIndex = $classinherit{$className};\n"; } ); # Generated methods print OUT "\n"; Iter::LocalCompounds( $rootnode, sub { my $classNode = shift; my $className = join( "::", kdocAstUtil::heritage($classNode) ); return if ( defined $skippedClasses{$className} ); # We use namespace declartions! my $printName = $className; $printName =~ s/KSVG:://; my $paramsUsed = 0; print OUT "bool ${printName}::hasProperty(ExecState *p1,const Identifier &p2) const\n"; print OUT "{\n"; if ($hasHashTable{$className}) { print OUT " const HashEntry *e = Lookup::findEntry(\&${printName}::s_hashTable,p2);\n"; print OUT " if(e) return true;\n"; $paramsUsed=1; } # Now look in prototype, if it exists if ( defined $hasFunctions{$className} ) { # We use namespace declartions! my $output = $hasFunctions{$className}; $output =~ s/KSVG:://; print OUT " Object proto = " . $output . "::self(p1);\n"; print OUT " if(proto.hasProperty(p1,p2)) return true;\n"; } # For each direct ancestor... Iter::Ancestors( $classNode, $rootnode, undef, undef, sub { my $superClassName = join( "::", kdocAstUtil::heritage($_[0]) ); # We use namespace declartions! my $printSuperClassName = $superClassName; $printSuperClassName =~ s/KSVG:://; if ( $superClassName ne '' ) { # happens with unknown parents return if ( defined $skippedClasses{$superClassName} ); print OUT " if(${printSuperClassName}::hasProperty(p1,p2)) return true;\n"; $paramsUsed=2; } }, undef ); if ($paramsUsed == 1 && !defined $hasFunctions{$className}){ print OUT " Q_UNUSED(p1);\n"; } if ($paramsUsed == 0){ print OUT " Q_UNUSED(p1); Q_UNUSED(p2);\n"; } print OUT " return false;\n"; print OUT "}\n\n"; my @ancestorsWithGet; Iter::Ancestors( $classNode, $rootnode, undef, undef, sub { my $superClassName = join( "::", kdocAstUtil::heritage($_[0]) ); if ( $superClassName ne '' # happens with unknown parents && ! defined $skippedClasses{$superClassName} ) { if ( $hasGet{$superClassName} ) { push @ancestorsWithGet, $superClassName; } } }, undef ); if ($hasHashTable{$className}) { die unless $hasGet{$className}; if ( $hasGet{$className} eq '1' ) { print OUT "Value ${printName}::get(GET_METHOD_ARGS) const\n"; print OUT "{\n"; if ( defined $hasFunctions{$className} ) { # We use namespace declartions! my $output = $hasFunctions{$className}; $output =~ s/KSVG:://; print OUT " return lookupGet<${output}Func,${printName}>(p1,p2,&s_hashTable,this,p3);\n"; } else { print OUT " return lookupGetValue<${printName}>(p1,p2,&s_hashTable,this,p3);\n"; } print OUT "}\n\n"; if ( defined $hasFunctions{$className} ) { # We use namespace declartions! my $output = $hasFunctions{$className}; $output =~ s/KSVG:://; my $methodName = "${output}Func::cast"; my $const = 'const'; # Special case - we also need that code in toNode() if ($methodName eq 'SVGDOMNodeBridgeProtoFunc::cast') { print OUT "${printName} *$methodName(const ObjectImp *p1) const\n"; $methodName = 'KSVG::toNodeBridge'; print OUT "{\n"; print OUT " return $methodName(p1);\n"; print OUT "}\n\n"; $const = ''; } # Type resolver for the Func class print OUT "${printName} *$methodName(const ObjectImp *p1) $const\n"; print OUT "{\n"; my @toTry; push @toTry, $classNode; if ( defined $descendants{$className} ) { push @toTry, @{$descendants{$className}}; } foreach my $d (@toTry) { my $c = join( "::", kdocAstUtil::heritage($d) ); # We use namespace declartions! my $d = $c; $d =~ s/KSVG:://; print OUT " { const KSVGBridge<$d> *test = dynamic_cast * >(p1);\n"; print OUT " if(test) return test->impl(); }\n"; } print OUT " return 0;\n"; print OUT "}\n\n"; } } } my $methodName = $hasGet{$className} eq '1' ? 'getInParents' : 'get'; print OUT "Value ${printName}::$methodName(GET_METHOD_ARGS) const\n"; print OUT "{\n"; my $paramsUsed = 0; # Now look in prototype, if it exists if ( defined $hasFunctions{$className} ) { # Prototype exists (because the class has functions) # We use namespace declartions! my $output = $hasFunctions{$className}; $output =~ s/KSVG:://; print OUT " Object proto = " . $output . "::self(p1);\n"; print OUT " if(proto.hasProperty(p1,p2)) return proto.get(p1,p2);\n"; ## TODO get() directly $paramsUsed = 1; } foreach my $anc (@ancestorsWithGet) { # We use namespace declartions! my $printAnc = $anc; $printAnc =~ s/KSVG:://; print OUT " if(${printAnc}::hasProperty(p1,p2)) return ${printAnc}::get(p1,p2,p3);\n"; ## TODO get() directly $paramsUsed = 2; } if ($paramsUsed == 0 ){ print OUT " Q_UNUSED(p1); Q_UNUSED(p2); Q_UNUSED(p3);\n"; } elsif ( $paramsUsed == 1 ) { print OUT " Q_UNUSED(p3);\n"; } print OUT " return Undefined();\n"; print OUT "}\n\n"; if ( $hasPut{$className} ) { if ( $hasPut{$className} eq '1' ) { if ($hasHashTable{$className}) { print OUT "bool ${printName}::put(PUT_METHOD_ARGS)\n"; print OUT "{\n"; print OUT " return lookupPut<${printName}>(p1,p2,p3,p4,&s_hashTable,this);\n"; print OUT "}\n\n"; } print OUT "bool ${printName}::putInParents(PUT_METHOD_ARGS)\n"; } else { # forward put print OUT "bool ${printName}::put(PUT_METHOD_ARGS)\n"; } print OUT "{\n"; my $paramsUsed = 0; Iter::Ancestors( $classNode, $rootnode, undef, undef, sub { my $superClassName = join( "::", kdocAstUtil::heritage($_[0]) ); # We use namespace declartions! my $printSuperClassName = $superClassName; $printSuperClassName =~ s/KSVG:://; if ( $superClassName ne '' ) { # happens with unknown parents if ( $hasPut{$superClassName} ) { print OUT " if(${printSuperClassName}::hasProperty(p1,p2)) {\n"; print OUT " ${printSuperClassName}::put(p1,p2,p3,p4);\n"; print OUT " return true;\n"; print OUT " }\n"; $paramsUsed=1; } } }, undef ); if (!$paramsUsed){ print OUT " Q_UNUSED(p1); Q_UNUSED(p2); Q_UNUSED(p3); Q_UNUSED(p4);\n"; } print OUT " return false;\n"; print OUT "}\n\n"; } # Write prototype method print OUT "Object ${printName}::prototype(ExecState *p1) const\n"; print OUT "{\n"; if ( defined $hasFunctions{$className} ) { # We use namespace declartions! my $output = $hasFunctions{$className}; $output =~ s/KSVG:://; # Prototype exists (because the class has functions) print OUT " if(p1) return " . $output . "::self(p1);\n"; } else { # Standard Object prototype print OUT " if(p1) return p1->interpreter()->builtinObjectPrototype();\n"; } print OUT " return Object::dynamicCast(Null());\n"; # hmm print OUT "}\n\n"; # Process classes only with KSVG_BRIDGE if ($hasBridge{$className}) { #print STDERR "Writing bridge() for $className...\n"; # Write bridge method print OUT "ObjectImp *${printName}::bridge(ExecState *p1) const\n"; print OUT "{\n"; if ($hasPut{$className}) { print OUT " return new KSVGRWBridge<${printName}>(p1,const_cast<${printName} *>(this));\n"; } else { print OUT " return new KSVGBridge<${printName}>(p1,const_cast<${printName} *>(this));\n"; } print OUT "}\n\n"; } if ($hasGet{$className}) { # Write cache method print OUT "Value ${printName}::cache(ExecState *p1) const\n"; print OUT "{\n"; if ($hasPut{$className}) { print OUT " return KJS::Value(cacheDOMObject<${printName},KSVGRWBridge<${printName}> >(p1,const_cast<${printName} *>(this)));\n"; } else { print OUT " return KJS::Value(cacheDOMObject<${printName},KSVGBridge<${printName}> >(p1,const_cast<${printName} *>(this)));\n"; } print OUT "}\n\n"; } } ); } 1;