# Copyright (C) 2005, 2006 Nikolas Zimmermann # Copyright (C) 2006 Anders Carlsson # Copyright (C) 2006 Samuel Weinig # Copyright (C) 2006 Alexey Proskuryakov # Copyright (C) 2006 Apple Computer, Inc. # Copyright (C) 2007, 2008, 2009 Google Inc. # # This file is part of the KDE project # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # aint with this library; see the file COPYING.LIB. If not, write to # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # package CodeGeneratorV8; use File::stat; my $module = ""; my $outputDir = ""; my @headerContent = (); my @implContentHeader = (); my @implFixedHeader = (); my @implContent = (); my @implContentDecls = (); my %implIncludes = (); my @allParents = (); # Default .h template my $headerTemplate = << "EOF"; /* This file is part of the WebKit open source project. This file has been generated by generate-bindings.pl. DO NOT MODIFY! This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ EOF # Default constructor sub new { my $object = shift; my $reference = { }; $codeGenerator = shift; $outputDir = shift; bless($reference, $object); return $reference; } sub finish { my $object = shift; # Commit changes! $object->WriteData(); } sub leftShift($$) { my ($value, $distance) = @_; return (($value << $distance) & 0xFFFFFFFF); } # Uppercase the first letter, while respecting WebKit style guidelines. # E.g., xmlEncoding becomes XMLEncoding, but xmlllang becomes Xmllang. sub WK_ucfirst { my $param = shift; my $ret = ucfirst($param); $ret =~ s/Xml/XML/ if $ret =~ /^Xml[^a-z]/; return $ret; } # Lowercase the first letter while respecting WebKit style guidelines. # URL becomes url, but SetURL becomes setURL. sub WK_lcfirst { my $param = shift; my $ret = lcfirst($param); $ret =~ s/uRL/url/; return $ret; } # Workaround for V8 bindings difference where RGBColor is not a POD type. sub IsPodType { my $type = shift; return 0 if $type eq "RGBColor"; return $codeGenerator->IsPodType($type); } # Params: 'domClass' struct sub GenerateInterface { my $object = shift; my $dataNode = shift; my $defines = shift; # Start actual generation $object->GenerateHeader($dataNode); $object->GenerateImplementation($dataNode); my $name = $dataNode->name; # Open files for writing my $headerFileName = "$outputDir/V8$name.h"; my $implFileName = "$outputDir/V8$name.cpp"; open($IMPL, ">$implFileName") || die "Couldn't open file $implFileName"; open($HEADER, ">$headerFileName") || die "Couldn't open file $headerFileName"; } # Params: 'idlDocument' struct sub GenerateModule { my $object = shift; my $dataNode = shift; $module = $dataNode->module; } sub GetLegacyHeaderIncludes { my $legacyParent = shift; die "Don't know what headers to include for module $module"; } sub AvoidInclusionOfType { my $type = shift; # Special case: SVGRect.h / SVGPoint.h / SVGNumber.h / SVGMatrix.h do not exist. return 1 if $type eq "SVGRect" or $type eq "SVGPoint" or $type eq "SVGNumber" or $type eq "SVGMatrix"; return 0; } sub UsesManualToJSImplementation { my $type = shift; return 1 if $type eq "SVGPathSeg"; return 0; } sub AddIncludesForType { my $type = $codeGenerator->StripModule(shift); # When we're finished with the one-file-per-class # reorganization, we won't need these special cases. if ($codeGenerator->IsPrimitiveType($type) or AvoidInclusionOfType($type)) { } elsif ($type =~ /SVGPathSeg/) { $joinedName = $type; $joinedName =~ s/Abs|Rel//; $implIncludes{"${joinedName}.h"} = 1; } else { # default, include the same named file $implIncludes{GetImplementationFileName(${type})} = 1; } # additional includes (things needed to compile the bindings but not the header) if ($type eq "CanvasRenderingContext2D") { $implIncludes{"CanvasGradient.h"} = 1; $implIncludes{"CanvasPattern.h"} = 1; $implIncludes{"CanvasStyle.h"} = 1; } if ($type eq "CanvasGradient" or $type eq "XPathNSResolver") { $implIncludes{"PlatformString.h"} = 1; } if ($type eq "CSSStyleDeclaration") { $implIncludes{"CSSMutableStyleDeclaration.h"} = 1; } if ($type eq "Plugin" or $type eq "PluginArray" or $type eq "MimeTypeArray") { # So we can get String -> AtomicString conversion for namedItem(). $implIncludes{"AtomicString.h"} = 1; } } sub AddIncludesForSVGAnimatedType { my $type = shift; $type =~ s/SVGAnimated//; if ($type eq "Point" or $type eq "Rect") { $implIncludes{"Float$type.h"} = 1; } elsif ($type eq "String") { $implIncludes{"PlatformString.h"} = 1; } $implIncludes{"SVGAnimatedTemplate.h"} = 1; } sub AddClassForwardIfNeeded { my $implClassName = shift; # SVGAnimatedLength/Number/etc.. are typedefs to SVGAnimtatedTemplate, so don't use class forwards for them! push(@headerContent, "class $implClassName;\n\n") unless $codeGenerator->IsSVGAnimatedType($implClassName); } sub GetImplementationFileName { my $iface = shift; return "HTMLCollection.h" if $iface eq "UndetectableHTMLCollection"; return "Event.h" if $iface eq "DOMTimeStamp"; return "NamedAttrMap.h" if $iface eq "NamedNodeMap"; return "NameNodeList.h" if $iface eq "NodeList"; return "XMLHttpRequest.h" if $iface eq "XMLHttpRequest"; return "${iface}.h"; } sub GenerateHeader { my $object = shift; my $dataNode = shift; my $interfaceName = $dataNode->name; my $className = "V8$interfaceName"; my $implClassName = $interfaceName; # Copy contents of parent classes except the first parent or if it is # EventTarget. $codeGenerator->AddMethodsConstantsAndAttributesFromParentClasses($dataNode); my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"}; my $conditional = $dataNode->extendedAttributes->{"Conditional"}; # - Add default header template @headerContent = split("\r", $headerTemplate); my $conditionalString; if ($conditional) { $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; push(@headerContent, "\n#if ${conditionalString}\n\n"); } push(@headerContent, "\n#ifndef $className" . "_H"); push(@headerContent, "\n#define $className" . "_H\n\n"); # Get correct pass/store types respecting PODType flag my $podType = $dataNode->extendedAttributes->{"PODType"}; my $passType = $podType ? "JSSVGPODTypeWrapper<$podType>*" : "$implClassName*"; push(@headerContent, "#include \"$podType.h\"\n") if $podType and ($podType ne "double" and $podType ne "float" and $podType ne "RGBA32"); push(@headerContent, "#include \n"); push(@headerContent, "#include \n"); push(@headerContent, "#include \"StringHash.h\"\n"); push(@headerContent, "\nnamespace WebCore {\n\n"); push(@headerContent, "class V8ClassIndex;\n"); push(@headerContent, "\nclass $className {\n"); push(@headerContent, < value); static v8::Persistent GetRawTemplate(); END if ($implClassName eq "DOMWindow") { push(@headerContent, < GetShadowObjectTemplate(); END } push(@headerContent, < GetTemplate(); friend class V8ClassIndex; }; END push(@headerContent, "}\n\n"); push(@headerContent, "#endif // $className" . "_H\n"); push(@headerContent, "#endif // ${conditionalString}\n\n") if $conditional; } sub GenerateSetDOMException { my $indent = shift; my $result = ""; $result .= $indent . "if (ec) {\n"; $result .= $indent . " V8Proxy::setDOMException(ec);\n"; $result .= $indent . " return v8::Handle();\n"; $result .= $indent . "}\n"; return $result; } sub IsNodeSubType { my $dataNode = shift; return 1 if ($dataNode->name eq "Node"); foreach (@allParents) { my $parent = $codeGenerator->StripModule($_); return 1 if $parent eq "Node"; } return 0; } sub HolderToNative { my $dataNode = shift; my $implClassName = shift; my $classIndex = shift; if (IsNodeSubType($dataNode)) { push(@implContentDecls, <(holder); END } else { push(@implContentDecls, <(V8ClassIndex::$classIndex, holder); END } } sub GenerateDomainSafeFunctionGetter { my $function = shift; my $dataNode = shift; my $classIndex = shift; my $implClassName = shift; my $className = "V8" . $dataNode->name; my $funcName = $function->signature->name; my $signature = "v8::Signature::New(" . $className . "::GetRawTemplate())"; if ($function->signature->extendedAttributes->{"V8DoNotCheckSignature"}) { $signature = "v8::Local()"; } my $newTemplateString = GenerateNewFunctionTemplate($function, $dataNode, $signature); $implIncludes{"V8Proxy.h"} = 1; push(@implContentDecls, < ${funcName}AttrGetter(v8::Local name, const v8::AccessorInfo& info) { INC_STATS(\"DOM.$implClassName.$funcName._get\"); static v8::Persistent private_template = v8::Persistent::New($newTemplateString); v8::Handle holder = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); if (holder.IsEmpty()) { // can only reach here by 'object.__proto__.func', and it should passed // domain security check already return private_template->GetFunction(); } END HolderToNative($dataNode, $implClassName, $classIndex); push(@implContentDecls, <frame(), false)) { static v8::Persistent shared_template = v8::Persistent::New($newTemplateString); return shared_template->GetFunction(); } else { return private_template->GetFunction(); } } END } sub GenerateConstructorGetter { my $implClassName = shift; my $classIndex = shift; push(@implContentDecls, < ${implClassName}ConstructorGetter(v8::Local name, const v8::AccessorInfo& info) { INC_STATS(\"DOM.$implClassName.constructors._get\"); v8::Handle data = info.Data(); ASSERT(data->IsNumber()); V8ClassIndex::V8WrapperType type = V8ClassIndex::FromInt(data->Int32Value()); END if ($classIndex eq "DOMWINDOW") { push(@implContentDecls, <(V8ClassIndex::DOMWINDOW, info.Holder()); Frame* frame = window->frame(); if (frame) { // Get the proxy corresponding to the DOMWindow if possible to // make sure that the constructor function is constructed in the // context of the DOMWindow and not in the context of the caller. return V8Proxy::retrieve(frame)->getConstructor(type); } END } if ($classIndex eq "WORKERCONTEXT") { $implIncludes{"WorkerContextExecutionProxy.h"} = 1; push(@implContentDecls, <GetConstructor(type); END } else { push(@implContentDecls, " return v8::Undefined();"); } push(@implContentDecls, <signature->extendedAttributes; my $attrName = $attribute->signature->name; $implIncludes{"V8Proxy.h"} = 1; my $attrType = $codeGenerator->StripModule($attribute->signature->type); my $attrIsPodType = IsPodType($attrType); my $nativeType = GetNativeTypeFromSignature($attribute->signature, 0); my $isPodType = IsPodType($implClassName); my $skipContext = 0; if ($isPodType) { $implClassName = GetNativeType($implClassName); $implIncludes{"V8SVGPODTypeWrapper.h"} = 1; } # Special case: SVGZoomEvent's attributes are all read-only if ($implClassName eq "SVGZoomEvent") { $attrIsPodType = 0; $skipContext = 1; } # Special case: SVGSVGEelement::viewport is read-only if (($implClassName eq "SVGSVGElement") and ($attrName eq "viewport")) { $attrIsPodType = 0; $skipContext = 1; } # Special case for SVGColor if (($implClassName eq "SVGColor") and ($attrName eq "rgbColor")) { $attrIsPodType = 0; } my $getterStringUsesImp = $implClassName ne "double"; # Getter push(@implContentDecls, < ${attrName}AttrGetter(v8::Local name, const v8::AccessorInfo& info) { INC_STATS(\"DOM.$implClassName.$attrName._get\"); END if ($isPodType) { push(@implContentDecls, <* imp_wrapper = V8DOMWrapper::convertToNativeObject >(V8ClassIndex::$classIndex, info.Holder()); $implClassName imp_instance = *imp_wrapper; END if ($getterStringUsesImp) { push(@implContentDecls, <{"v8OnProto"} || $attrExt->{"V8DisallowShadowing"}) { # perform lookup first push(@implContentDecls, < holder = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); if (holder.IsEmpty()) return v8::Undefined(); END HolderToNative($dataNode, $implClassName, $classIndex); } else { push(@implContentDecls, < holder = info.Holder(); END HolderToNative($dataNode, $implClassName, $classIndex); } # Generate security checks if necessary if ($attribute->signature->extendedAttributes->{"CheckNodeSecurity"}) { push(@implContentDecls, " if (!V8Proxy::checkNodeSecurity(imp->$attrName())) return v8::Undefined();\n\n"); } elsif ($attribute->signature->extendedAttributes->{"CheckFrameSecurity"}) { push(@implContentDecls, " if (!V8Proxy::checkNodeSecurity(imp->contentDocument())) return v8::Undefined();\n\n"); } my $useExceptions = 1 if @{$attribute->getterExceptions} and !($isPodType); if ($useExceptions) { $implIncludes{"ExceptionCode.h"} = 1; push(@implContentDecls, " ExceptionCode ec = 0;\n"); } if ($attribute->signature->extendedAttributes->{"v8referenceattr"}) { $attrName = $attribute->signature->extendedAttributes->{"v8referenceattr"}; } my $getterFunc = WK_lcfirst($attrName); $getterFunc .= "Animated" if $codeGenerator->IsSVGAnimatedType($attribute->signature->type); my $returnType = $codeGenerator->StripModule($attribute->signature->type); my $getterString; if ($getterStringUsesImp) { $getterString = "imp->$getterFunc("; $getterString .= "ec" if $useExceptions; $getterString .= ")"; if (IsRefPtrType($returnType)) { $implIncludes{"wtf/GetPtr.h"} = 1; $getterString = "WTF::getPtr(" . $getterString . ")"; } if ($nativeType eq "int" and $attribute->signature->extendedAttributes->{"ConvertFromString"}) { $getterString .= ".toInt()"; } } else { $getterString = "imp_instance"; } if ($nativeType eq "String") { $getterString = "toString($getterString)"; } my $result; my $wrapper; if ($attrIsPodType) { $implIncludes{"V8SVGPODTypeWrapper.h"} = 1; my $getter = $getterString; $getter =~ s/imp->//; $getter =~ s/\(\)//; my $setter = "set" . WK_ucfirst($getter); my $implClassIsAnimatedType = $codeGenerator->IsSVGAnimatedType($implClassName); if (not $implClassIsAnimatedType and $codeGenerator->IsPodTypeWithWriteableProperties($attrType) and not defined $attribute->signature->extendedAttributes->{"Immutable"}) { if (IsPodType($implClassName)) { $wrapper = "new V8SVGStaticPODTypeWrapperWithPODTypeParent<$nativeType, $implClassName>($getterString, imp_wrapper)"; } else { $wrapper = "new V8SVGStaticPODTypeWrapperWithParent<$nativeType, $implClassName>(imp, &${implClassName}::$getter, &${implClassName}::$setter)"; } } else { if ($implClassIsAnimatedType) { $wrapper = "V8SVGDynamicPODTypeWrapperCache<$nativeType, $implClassName>::lookupOrCreateWrapper(imp, &${implClassName}::$getter, &${implClassName}::$setter)"; } else { $wrapper = GenerateSVGStaticPodTypeWrapper($returnType, $getterString); } } push(@implContentDecls, " void* wrapper = $wrapper;\n"); } elsif ($nativeType ne "RGBColor") { push(@implContentDecls, " $nativeType v = "); push(@implContentDecls, "$getterString;\n"); if ($useExceptions) { push(@implContentDecls, GenerateSetDOMException(" ")); } $result = "v"; if (IsRefPtrType($returnType)) { $result = "WTF::getPtr(" . $result . ")"; } } else { # Special case: RGBColor is noncopyable $result = $getterString; } if (IsSVGTypeNeedingContextParameter($attrType) && !$skipContext) { my $resultObject = $result; if ($attrIsPodType) { $resultObject = "wrapper"; } push(@implContentDecls, GenerateSVGContextAssignment($implClassName, $resultObject, " ")); } if ($attrIsPodType) { my $classIndex = uc($attrType); push(@implContentDecls, " return V8DOMWrapper::convertToV8Object(V8ClassIndex::$classIndex, wrapper);\n"); } else { push(@implContentDecls, " " . ReturnNativeToJSValue($attribute->signature, $result, " ").";\n"); } push(@implContentDecls, " }\n\n"); # end of getter } sub GenerateReplaceableAttrSetter { my $implClassName = shift; $implIncludes{"V8Proxy.h"} = 1; push(@implContentDecls, " static void ${attrName}AttrSetter(v8::Local name," . " v8::Local value, const v8::AccessorInfo& info) {\n"); push(@implContentDecls, " INC_STATS(\"DOM.$implClassName.$attrName._set\");\n"); push(@implContentDecls, " v8::Local ${attrName}_string = v8::String::New(\"${attrName}\");\n"); push(@implContentDecls, " info.Holder()->Delete(${attrName}_string);\n"); push(@implContentDecls, " info.This()->Set(${attrName}_string, value);\n"); push(@implContentDecls, " }\n\n"); } sub GenerateNormalAttrSetter { my $attribute = shift; my $dataNode = shift; my $classIndex = shift; my $implClassName = shift; my $attrExt = $attribute->signature->extendedAttributes; $implIncludes{"V8Proxy.h"} = 1; push(@implContentDecls, " static void ${attrName}AttrSetter(v8::Local name," . " v8::Local value, const v8::AccessorInfo& info) {\n"); push(@implContentDecls, " INC_STATS(\"DOM.$implClassName.$attrName._set\");\n"); my $isPodType = IsPodType($implClassName); if ($isPodType) { $implClassName = GetNativeType($implClassName); $implIncludes{"V8SVGPODTypeWrapper.h"} = 1; push(@implContentDecls, " V8SVGPODTypeWrapper<$implClassName>* wrapper = V8DOMWrapper::convertToNativeObject >(V8ClassIndex::$classIndex, info.Holder());\n"); push(@implContentDecls, " $implClassName imp_instance = *wrapper;\n"); push(@implContentDecls, " $implClassName* imp = &imp_instance;\n"); } elsif ($attrExt->{"v8OnProto"}) { # perform lookup first push(@implContentDecls, < holder = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::$classIndex, info.This()); if (holder.IsEmpty()) return v8::Undefined(); END HolderToNative($dataNode, $implClassName, $classIndex); } else { push(@implContentDecls, < holder = info.Holder(); END HolderToNative($dataNode, $implClassName, $classIndex); } my $nativeType = GetNativeTypeFromSignature($attribute->signature, 0); push(@implContentDecls, " $nativeType v = " . JSValueToNative($attribute->signature, "value") . ";\n"); my $result = ""; if ($nativeType eq "int" and $attribute->signature->extendedAttributes->{"ConvertFromString"}) { $result .= "WebCore::String::number("; } $result .= "v"; if ($nativeType eq "int" and $attribute->signature->extendedAttributes->{"ConvertFromString"}) { $result .= ")"; } my $returnType = $codeGenerator->StripModule($attribute->signature->type); if (IsRefPtrType($returnType)) { $result = "WTF::getPtr(" . $result . ")"; } my $useExceptions = 1 if @{$attribute->setterExceptions} and !($isPodType); if ($useExceptions) { $implIncludes{"ExceptionCode.h"} = 1; push(@implContentDecls, " ExceptionCode ec = 0;\n"); } if ($implClassName eq "double") { push(@implContentDecls, " *imp = $result;\n"); } else { push(@implContentDecls, " imp->set" . WK_ucfirst($attrName) . "(" . $result); push(@implContentDecls, ", ec") if $useExceptions; push(@implContentDecls, ");\n"); } if ($useExceptions) { push(@implContentDecls, " V8Proxy::setDOMException(ec);\n"); } if ($isPodType) { push(@implContentDecls, " wrapper->commitChange(*imp, V8Proxy::svgContext(wrapper));\n"); } elsif (IsSVGTypeNeedingContextParameter($implClassName)) { $implIncludes{"SVGElement.h"} = 1; my $currentObject = "imp"; if ($isPodType) { $currentObject = "wrapper"; } push(@implContentDecls, " if (SVGElement* context = V8Proxy::svgContext($currentObject)) {\n"); push(@implContentDecls, " context->svgAttributeChanged(imp->associatedAttributeName());\n"); push(@implContentDecls, " }\n"); } push(@implContentDecls, " return;\n"); push(@implContentDecls, " }\n\n"); # end of setter } sub GenerateNewFunctionTemplate { $function = shift; $dataNode = shift; $signature = shift; my $interfaceName = $dataNode->name; my $name = $function->signature->name; if ($function->signature->extendedAttributes->{"Custom"} || $function->signature->extendedAttributes->{"V8Custom"}) { if ($function->signature->extendedAttributes->{"Custom"} && $function->signature->extendedAttributes->{"V8Custom"}) { die "Custom and V8Custom should be mutually exclusive!" } my $customFunc = $function->signature->extendedAttributes->{"Custom"} || $function->signature->extendedAttributes->{"V8Custom"}; if ($customFunc eq 1) { $customFunc = $interfaceName . WK_ucfirst($name); } return "v8::FunctionTemplate::New(V8Custom::v8${customFunc}Callback, v8::Handle(), $signature)"; } else { return "v8::FunctionTemplate::New(${interfaceName}Internal::${name}Callback, v8::Handle(), $signature)"; } } sub GenerateFunctionCallback { my $function = shift; my $dataNode = shift; my $classIndex = shift; my $implClassName = shift; my $interfaceName = $dataNode->name; my $name = $function->signature->name; push(@implContentDecls, " static v8::Handle ${name}Callback(const v8::Arguments& args) {\n" . " INC_STATS(\"DOM.$implClassName.$name\");\n"); my $numParameters = @{$function->parameters}; if ($function->signature->extendedAttributes->{"RequiresAllArguments"}) { push(@implContentDecls, " if (args.Length() < $numParameters) return v8::Undefined();\n"); } if (IsPodType($implClassName)) { my $nativeClassName = GetNativeType($implClassName); push(@implContentDecls, " V8SVGPODTypeWrapper<$nativeClassName>* imp_wrapper = V8DOMWrapper::convertToNativeObject >(V8ClassIndex::$classIndex, args.Holder());\n"); push(@implContentDecls, " $nativeClassName imp_instance = *imp_wrapper;\n"); push(@implContentDecls, " $nativeClassName* imp = &imp_instance;\n"); } else { push(@implContentDecls, < holder = args.Holder(); END HolderToNative($dataNode, $implClassName, $classIndex); } # Check domain security if needed if (($dataNode->extendedAttributes->{"CheckDomainSecurity"} || $interfaceName eq "DOMWindow") && !$function->signature->extendedAttributes->{"DoNotCheckDomainSecurity"}) { # We have not find real use cases yet. push(@implContentDecls, " if (!V8Proxy::canAccessFrame(imp->frame(), true)) {\n". " return v8::Undefined();\n" . " }\n"); } if (@{$function->raisesExceptions}) { $implIncludes{"ExceptionCode.h"} = 1; push(@implContentDecls, " ExceptionCode ec = 0;\n"); } if ($function->signature->extendedAttributes->{"CustomArgumentHandling"}) { push(@implContentDecls, " ScriptCallStack callStack(args, $numParameters);\n"); $implIncludes{"ScriptCallStack.h"} = 1; } my $paramIndex = 0; foreach my $parameter (@{$function->parameters}) { TranslateParameter($parameter); my $parameterName = $parameter->name; if ($parameter->extendedAttributes->{"Optional"}) { # Generate early call if there are not enough parameters. push(@implContentDecls, " if (args.Length() <= $paramIndex) {\n"); my $functionCall = GenerateFunctionCallString($function, $paramIndex, " " x 2, $implClassName); push(@implContentDecls, $functionCall); push(@implContentDecls, " }\n"); } if (BasicTypeCanFailConversion($parameter)) { push(@implContentDecls, " bool ${parameterName}Ok;\n"); } push(@implContentDecls, " " . GetNativeTypeFromSignature($parameter, 1) . " $parameterName = "); push(@implContentDecls, JSValueToNative($parameter, "args[$paramIndex]", BasicTypeCanFailConversion($parameter) ? "${parameterName}Ok" : undef) . ";\n"); if (TypeCanFailConversion($parameter)) { $implIncludes{"ExceptionCode.h"} = 1; push(@implContentDecls, " if (!$parameterName" . (BasicTypeCanFailConversion($parameter) ? "Ok" : "") . ") {\n" . " V8Proxy::setDOMException(TYPE_MISMATCH_ERR);\n" . " return v8::Handle();\n" . " }\n"); } if ($parameter->extendedAttributes->{"IsIndex"}) { $implIncludes{"ExceptionCode.h"} = 1; push(@implContentDecls, " if ($parameterName < 0) {\n" . " V8Proxy::setDOMException(INDEX_SIZE_ERR);\n" . " return v8::Handle();\n" . " }\n"); } $paramIndex++; } # Build the function call string. my $callString = GenerateFunctionCallString($function, $paramIndex, " ", $implClassName); push(@implContentDecls, "$callString"); push(@implContentDecls, " }\n\n"); } sub GenerateBatchedAttributeData { my $interfaceName = shift; my $attributes = shift; foreach my $attribute (@$attributes) { my $attrName = $attribute->signature->name; my $attrExt = $attribute->signature->extendedAttributes; my $accessControl = "v8::DEFAULT"; if ($attrExt->{"DoNotCheckDomainSecurityOnGet"}) { $accessControl = "v8::ALL_CAN_READ"; } elsif ($attrExt->{"DoNotCheckDomainSecurityOnSet"}) { $accessControl = "v8::ALL_CAN_WRITE"; } elsif ($attrExt->{"DoNotCheckDomainSecurity"}) { $accessControl = "v8::ALL_CAN_READ"; if (!($attribute->type =~ /^readonly/) && !($attrExt->{"V8ReadOnly"})) { $accessControl .= "|v8::ALL_CAN_WRITE"; } } if ($attrExt->{"V8DisallowShadowing"}) { $accessControl .= "|v8::PROHIBITS_OVERWRITING"; } $accessControl = "static_cast(" . $accessControl . ")"; my $customAccessor = $attrExt->{"Custom"} || $attrExt->{"CustomSetter"} || $attrExt->{"CustomGetter"} || $attrExt->{"V8Custom"} || $attrExt->{"V8CustomSetter"} || $attrExt->{"V8CustomGetter"} || ""; if ($customAccessor eq 1) { # use the naming convension, interface + (capitalize) attr name $customAccessor = $interfaceName . WK_ucfirst($attrName); } my $getter; my $setter; my $propAttr = "v8::None"; my $hasCustomSetter = 0; # Check attributes. if ($attrExt->{"DontEnum"}) { $propAttr .= "|v8::DontEnum"; } if ($attrExt->{"V8DisallowShadowing"}) { $propAttr .= "|v8::DontDelete"; } my $on_proto = "0 /* on instance */"; my $data = "V8ClassIndex::INVALID_CLASS_INDEX /* no data */"; # Constructor if ($attribute->signature->type =~ /Constructor$/) { my $constructorType = $codeGenerator->StripModule($attribute->signature->type); $constructorType =~ s/Constructor$//; my $constructorIndex = uc($constructorType); $data = "V8ClassIndex::${constructorIndex}"; $getter = "${interfaceName}Internal::${interfaceName}ConstructorGetter"; $setter = "0"; $propAttr = "v8::ReadOnly"; # EventListeners } elsif ($attribute->signature->type eq "EventListener") { if ($interfaceName eq "DOMWindow") { $getter = "V8Custom::v8DOMWindowEventHandlerAccessorGetter"; $setter = "V8Custom::v8DOMWindowEventHandlerAccessorSetter"; } elsif ($interfaceName eq "Element" || $interfaceName eq "Document" || $interfaceName eq "HTMLBodyElement" || $interfaceName eq "SVGElementInstance" || $interfaceName eq "HTMLFrameSetElement") { $getter = "V8Custom::v8ElementEventHandlerAccessorGetter"; $setter = "V8Custom::v8ElementEventHandlerAccessorSetter"; } else { $getter = "V8Custom::v8${customAccessor}AccessorGetter"; if ($interfaceName eq "WorkerContext" and $attrName eq "self") { $setter = "0"; $propAttr = "v8::ReadOnly"; } else { $setter = "V8Custom::v8${customAccessor}AccessorSetter"; } } # Custom Getter and Setter } elsif ($attrExt->{"Custom"} || $attrExt->{"V8Custom"}) { $getter = "V8Custom::v8${customAccessor}AccessorGetter"; if ($interfaceName eq "WorkerContext" and $attrName eq "self") { $setter = "0"; $propAttr = "v8::ReadOnly"; } else { $hasCustomSetter = 1; $setter = "V8Custom::v8${customAccessor}AccessorSetter"; } # Custom Setter } elsif ($attrExt->{"CustomSetter"} || $attrExt->{"V8CustomSetter"}) { $hasCustomSetter = 1; $getter = "${interfaceName}Internal::${attrName}AttrGetter"; $setter = "V8Custom::v8${customAccessor}AccessorSetter"; # Custom Getter } elsif ($attrExt->{"CustomGetter"}) { $getter = "V8Custom::v8${customAccessor}AccessorGetter"; $setter = "${interfaceName}Internal::${attrName}AttrSetter"; # Replaceable } elsif ($attrExt->{"Replaceable"}) { # Replaceable accessor is put on instance template with ReadOnly attribute. $getter = "${interfaceName}Internal::${attrName}AttrGetter"; $setter = "0"; # Mark to avoid duplicate v8::ReadOnly flags in output. $hasCustomSetter = 1; # Handle the special case of window.top being marked upstream as Replaceable. # FIXME: Investigate why [Replaceable] is not marked as ReadOnly # upstream and reach parity. if (!($interfaceName eq "DOMWindow" and $attrName eq "top")) { $propAttr .= "|v8::ReadOnly"; } # Normal } else { $getter = "${interfaceName}Internal::${attrName}AttrGetter"; $setter = "${interfaceName}Internal::${attrName}AttrSetter"; } if ($attrExt->{"Replaceable"} && !$hasCustomSetter) { $setter = "0"; $propAttr .= "|v8::ReadOnly"; } # Read only attributes if ($attribute->type =~ /^readonly/ || $attrExt->{"V8ReadOnly"}) { $setter = "0"; } # An accessor can be installed on the proto if ($attrExt->{"v8OnProto"}) { $on_proto = "1 /* on proto */"; } my $commentInfo = "Attribute '$attrName' (Type: '" . $attribute->type . "' ExtAttr: '" . join(' ', keys(%{$attrExt})) . "')"; push(@implContent, <($propAttr), $on_proto }, END } } sub GenerateImplementation { my $object = shift; my $dataNode = shift; my $interfaceName = $dataNode->name; my $className = "V8$interfaceName"; my $implClassName = $interfaceName; my $classIndex = uc($codeGenerator->StripModule($interfaceName)); my $hasLegacyParent = $dataNode->extendedAttributes->{"LegacyParent"}; my $conditional = $dataNode->extendedAttributes->{"Conditional"}; @allParents = $codeGenerator->FindParentsRecursively($dataNode); # - Add default header template @implContentHeader = split("\r", $headerTemplate); push(@implFixedHeader, "#include \"config.h\"\n" . "#include \"V8Proxy.h\"\n" . "#include \"V8Binding.h\"\n\n" . "#undef LOG\n\n"); my $conditionalString; if ($conditional) { $conditionalString = "ENABLE(" . join(") && ENABLE(", split(/&/, $conditional)) . ")"; push(@implFixedHeader, "\n#if ${conditionalString}\n\n"); } if ($className =~ /^V8SVGAnimated/) { AddIncludesForSVGAnimatedType($interfaceName); } $implIncludes{"${className}.h"} = 1; AddIncludesForType($interfaceName); $implIncludes{"V8Proxy.h"} = 1; push(@implContentDecls, "namespace WebCore {\n"); push(@implContentDecls, "namespace ${interfaceName}Internal {\n\n"); push(@implContentDecls, "template void V8_USE(T) { }\n\n"); my $hasConstructors = 0; # Generate property accessors for attributes. for ($index = 0; $index < @{$dataNode->attributes}; $index++) { $attribute = @{$dataNode->attributes}[$index]; $attrName = $attribute->signature->name; $attrType = $attribute->signature->type; # Generate special code for the constructor attributes. if ($attrType =~ /Constructor$/) { $hasConstructors = 1; next; } # Make EventListeners always custom. # FIXME: make the perl code capable of generating the # event setters/getters. For now, WebKit has started removing the # [Custom] attribute, so just automatically insert it to avoid forking # other files. This should be okay because we can't generate stubs # for any event getter/setters anyway. if ($attrType eq "EventListener") { $attribute->signature->extendedAttributes->{"Custom"} = 1; $implIncludes{"V8CustomBinding.h"} = 1; next; } # Do not generate accessor if this is a custom attribute. The # call will be forwarded to a hand-written accessor # implementation. if ($attribute->signature->extendedAttributes->{"Custom"} || $attribute->signature->extendedAttributes->{"V8Custom"}) { $implIncludes{"V8CustomBinding.h"} = 1; next; } # Generate the accessor. if ($attribute->signature->extendedAttributes->{"CustomGetter"}) { $implIncludes{"V8CustomBinding.h"} = 1; } else { GenerateNormalAttrGetter($attribute, $dataNode, $classIndex, $implClassName); } if ($attribute->signature->extendedAttributes->{"CustomSetter"} || $attribute->signature->extendedAttributes->{"V8CustomSetter"}) { $implIncludes{"V8CustomBinding.h"} = 1; } elsif ($attribute->signature->extendedAttributes->{"Replaceable"}) { $dataNode->extendedAttributes->{"ExtendsDOMGlobalObject"} || die "Replaceable attribute can only be used in interface that defines ExtendsDOMGlobalObject attribute!"; # GenerateReplaceableAttrSetter($implClassName); } elsif ($attribute->type !~ /^readonly/ && !$attribute->signature->extendedAttributes->{"V8ReadOnly"}) { GenerateNormalAttrSetter($attribute, $dataNode, $classIndex, $implClassName); } } if ($hasConstructors) { GenerateConstructorGetter($implClassName, $classIndex); } # Generate methods for functions. foreach my $function (@{$dataNode->functions}) { # hack for addEventListener/RemoveEventListener # FIXME: avoid naming conflict if ($function->signature->extendedAttributes->{"Custom"} || $function->signature->extendedAttributes->{"V8Custom"}) { $implIncludes{"V8CustomBinding.h"} = 1; } else { GenerateFunctionCallback($function, $dataNode, $classIndex, $implClassName); } # If the function does not need domain security check, we need to # generate an access getter that returns different function objects # for different calling context. if (($dataNode->extendedAttributes->{"CheckDomainSecurity"} || ($interfaceName eq "DOMWindow")) && $function->signature->extendedAttributes->{"DoNotCheckDomainSecurity"}) { GenerateDomainSafeFunctionGetter($function, $dataNode, $classIndex, $implClassName); } } # Attributes my $attributes = $dataNode->attributes; # For the DOMWindow interface we partition the attributes into the # ones that disallows shadowing and the rest. my @disallows_shadowing; my @normal; if ($interfaceName eq "DOMWindow") { foreach my $attribute (@$attributes) { if ($attribute->signature->extendedAttributes->{"V8DisallowShadowing"}) { push(@disallows_shadowing, $attribute); } else { push(@normal, $attribute); } } # Put the attributes that disallow shadowing on the shadow object. $attributes = \@normal; push(@implContent, "static const BatchedAttribute shadow_attrs[] = {\n"); GenerateBatchedAttributeData($interfaceName, \@disallows_shadowing); push(@implContent, "};\n"); } my $has_attributes = 0; if (@$attributes) { $has_attributes = 1; push(@implContent, "static const BatchedAttribute ${interfaceName}_attrs[] = {\n"); GenerateBatchedAttributeData($interfaceName, $attributes); push(@implContent, "};\n"); } # Setup constants my $has_constants = 0; if (@{$dataNode->constants}) { $has_constants = 1; push(@implContent, "static const BatchedConstant ${interfaceName}_consts[] = {\n"); } foreach my $constant (@{$dataNode->constants}) { my $name = $constant->name; my $value = $constant->value; # FIXME: we need the static_cast here only because of one constant, NodeFilter.idl # defines "const unsigned long SHOW_ALL = 0xFFFFFFFF". It would be better if we # handled this here, and converted it to a -1 constant in the c++ output. push(@implContent, <($value) }, END } if ($has_constants) { push(@implContent, "};\n"); } push(@implContentDecls, "} // namespace ${interfaceName}Internal\n\n"); my $access_check = "/* no access check */"; if ($dataNode->extendedAttributes->{"CheckDomainSecurity"} && !($interfaceName eq "DOMWindow")) { $access_check = "instance->SetAccessCheckCallbacks(V8Custom::v8${interfaceName}NamedSecurityCheck, V8Custom::v8${interfaceName}IndexedSecurityCheck, v8::Integer::New(V8ClassIndex::ToInt(V8ClassIndex::${classIndex})));"; } # For the DOMWindow interface, generate the shadow object template # configuration method. if ($implClassName eq "DOMWindow") { push(@implContent, < ConfigureShadowObjectTemplate(v8::Persistent templ) { batchConfigureAttributes(templ, v8::Handle(), shadow_attrs, sizeof(shadow_attrs)/sizeof(*shadow_attrs)); return templ; } END } # Generate the template configuration method push(@implContent, < Configure${className}Template(v8::Persistent desc) { v8::Local instance = desc->InstanceTemplate(); instance->SetInternalFieldCount(2); v8::Local default_signature = v8::Signature::New(desc); v8::Local proto = desc->PrototypeTemplate(); $access_check END # Set up our attributes if we have them if ($has_attributes) { push(@implContent, <functions}) { my $attrExt = $function->signature->extendedAttributes; my $name = $function->signature->name; my $property_attributes = "v8::DontDelete"; if ($attrExt->{"DontEnum"}) { $property_attributes .= "|v8::DontEnum"; } if ($attrExt->{"V8ReadOnly"}) { $property_attributes .= "|v8::ReadOnly"; } my $commentInfo = "Function '$name' (ExtAttr: '" . join(' ', keys(%{$attrExt})) . "')"; my $template = "proto"; if ($attrExt->{"V8OnInstance"}) { $template = "instance"; } if ($attrExt->{"DoNotCheckDomainSecurity"} && ($dataNode->extendedAttributes->{"CheckDomainSecurity"} || $interfaceName eq "DOMWindow")) { # Mark the accessor as ReadOnly and set it on the proto object so # it can be shadowed. This is really a hack to make it work. # There are several sceneria to call into the accessor: # 1) from the same domain: "window.open": # the accessor finds the DOM wrapper in the proto chain; # 2) from the same domain: "window.__proto__.open": # the accessor will NOT find a DOM wrapper in the prototype chain # 3) from another domain: "window.open": # the access find the DOM wrapper in the prototype chain # "window.__proto__.open" from another domain will fail when # accessing '__proto__' # # The solution is very hacky and fragile, it really needs to be replaced # by a better solution. $property_attributes .= "|v8::ReadOnly"; push(@implContent, <SetAccessor( v8::String::New("$name"), ${interfaceName}Internal::${name}AttrGetter, 0, v8::Handle(), v8::ALL_CAN_READ, static_cast($property_attributes)); END next; } my $signature = "default_signature"; if ($attrExt->{"V8DoNotCheckSignature"}){ $signature = "v8::Local()"; } if (RequiresCustomSignature($function)) { $signature = "${name}_signature"; push(@implContent, "\n // Custom Signature '$name'\n", CreateCustomSignature($function)); } # Normal function call is a template my $templateFunction = GenerateNewFunctionTemplate($function, $dataNode, $signature); push(@implContent, <Set( v8::String::New("$name"), $templateFunction, static_cast($property_attributes)); END } # set the super descriptor foreach (@{$dataNode->parents}) { my $parent = $codeGenerator->StripModule($_); if ($parent eq "EventTarget") { next; } $implIncludes{"V8${parent}.h"} = 1; my $parentClassIndex = uc($codeGenerator->StripModule($parent)); push(@implContent, " desc->Inherit(V8DOMWrapper::getTemplate(V8ClassIndex::${parentClassIndex}));\n"); last; } # Set the class name. This is used when printing objects. push(@implContent, " desc->SetClassName(v8::String::New(\"" . GetClassName(${interfaceName}) . "\"));\n"); if ($has_constants) { push(@implContent, < ${className}::GetRawTemplate() { static v8::Persistent ${className}_raw_cache_; if (${className}_raw_cache_.IsEmpty()) { v8::HandleScope scope; v8::Local result = v8::FunctionTemplate::New(V8Proxy::checkNewLegal); ${className}_raw_cache_ = v8::Persistent::New(result); } return ${className}_raw_cache_; } v8::Persistent ${className}::GetTemplate() { static v8::Persistent ${className}_cache_; if (${className}_cache_.IsEmpty()) ${className}_cache_ = Configure${className}Template(GetRawTemplate()); return ${className}_cache_; } bool ${className}::HasInstance(v8::Handle value) { return GetRawTemplate()->HasInstance(value); } END if ($implClassName eq "DOMWindow") { push(@implContent, < V8DOMWindow::GetShadowObjectTemplate() { static v8::Persistent V8DOMWindowShadowObject_cache_; if (V8DOMWindowShadowObject_cache_.IsEmpty()) { V8DOMWindowShadowObject_cache_ = v8::Persistent::New(v8::ObjectTemplate::New()); ConfigureShadowObjectTemplate(V8DOMWindowShadowObject_cache_); } return V8DOMWindowShadowObject_cache_; } END } push(@implContent, <signature->name; my $isPodType = IsPodType($implClassName); my $returnType = $codeGenerator->StripModule($function->signature->type); my $returnsPodType = IsPodType($returnType); my $nativeReturnType = GetNativeType($returnType, 0); my $result = ""; # Special case: SVG matrix transform methods should not mutate # the matrix but return a copy my $copyFirst = 0; if ($implClassName eq "SVGMatrix" && $function->signature->type eq "SVGMatrix") { $copyFirst = 1; } if ($function->signature->extendedAttributes->{"v8implname"}) { $name = $function->signature->extendedAttributes->{"v8implname"}; } if ($function->signature->extendedAttributes->{"ImplementationFunction"}) { $name = $function->signature->extendedAttributes->{"ImplementationFunction"}; } my $functionString = "imp->${name}("; if ($copyFirst) { $functionString = "result.${name}("; } my $returnsListItemPodType = 0; # SVG lists functions that return POD types require special handling if (IsSVGListTypeNeedingSpecialHandling($implClassName) && IsSVGListMethod($name) && $returnsPodType) { $returnsListItemPodType = 1; $result .= $indent . "SVGList > >* listImp = imp;\n"; $functionString = "listImp->${name}("; } my $first = 1; my $index = 0; my $nodeToReturn = 0; foreach my $parameter (@{$function->parameters}) { if ($index eq $numberOfParameters) { last; } if ($first) { $first = 0; } else { $functionString .= ", "; } my $paramName = $parameter->name; my $paramType = $parameter->type; # This is a bit of a hack... we need to convert parameters to methods on SVG lists # of POD types which are items in the list to appropriate SVGList<> instances if ($returnsListItemPodType && $paramType . "List" eq $implClassName) { $paramName = "SVGPODListItem<" . GetNativeType($paramType, 1) . ">::copy($paramName)"; } if ($parameter->type eq "NodeFilter") { $functionString .= "$paramName.get()"; } else { $functionString .= $paramName; } if ($parameter->extendedAttributes->{"Return"}) { $nodeToReturn = $parameter->name; } $index++; } if ($function->signature->extendedAttributes->{"CustomArgumentHandling"}) { $functionString .= ", " if not $first; $functionString .= "&callStack"; if ($first) { $first = 0; } } if (@{$function->raisesExceptions}) { $functionString .= ", " if not $first; $functionString .= "ec"; } $functionString .= ")"; if ((IsRefPtrType($returnType) || $returnsListItemPodType) && !$nodeToReturn) { # We don't use getPtr when $nodeToReturn because that situation is # special-cased below to return a bool. $implIncludes{"wtf/GetPtr.h"} = 1; $functionString = "WTF::getPtr(" . $functionString . ")"; } if ($nodeToReturn) { # Special case for insertBefore, replaceChild, removeChild and # appendChild functions from Node. $result .= $indent . "bool success = $functionString;\n"; if (@{$function->raisesExceptions}) { $result .= GenerateSetDOMException($indent); } $result .= $indent . "if (success)\n"; $result .= $indent . " " . "return V8DOMWrapper::convertNodeToV8Object($nodeToReturn);\n"; $result .= $indent . "return v8::Null();\n"; return $result; } elsif ($returnType eq "void") { $result .= $indent . "$functionString;\n"; } elsif ($copyFirst) { $result .= $indent . GetNativeType($returnType, 0) . " result = *imp;\n" . $indent . "$functionString;\n"; } elsif ($returnsListItemPodType) { $result .= $indent . "RefPtr > result = $functionString;\n"; } else { $result .= $indent . $nativeReturnType . " result = $functionString;\n"; } if (@{$function->raisesExceptions}) { $result .= GenerateSetDOMException($indent); } my $return = "result"; if (IsRefPtrType($returnType) || $returnsListItemPodType) { $implIncludes{"wtf/GetPtr.h"} = 1; $return = "WTF::getPtr(" . $return . ")"; } # If the return type is a POD type, separate out the wrapper generation if ($returnsListItemPodType) { $result .= $indent . "V8SVGPODTypeWrapper<" . $nativeReturnType . ">* wrapper = new "; $result .= "V8SVGPODTypeWrapperCreatorForList<" . $nativeReturnType . ">($return, imp->associatedAttributeName());\n"; $return = "wrapper"; } elsif ($returnsPodType) { $result .= $indent . "V8SVGPODTypeWrapper<" . $nativeReturnType . ">* wrapper = "; $result .= GenerateSVGStaticPodTypeWrapper($returnType, $return) . ";\n"; $return = "wrapper"; } my $generatedSVGContextRetrieval = 0; # If the return type needs an SVG context, output it if (IsSVGTypeNeedingContextParameter($returnType)) { $result .= GenerateSVGContextAssignment($implClassName, $return, $indent); $generatedSVGContextRetrieval = 1; } if (IsSVGTypeNeedingContextParameter($implClassName) && $implClassName =~ /List$/ && IsSVGListMutator($name)) { if (!$generatedSVGContextRetrieval) { $result .= GenerateSVGContextRetrieval($implClassName, $indent); $generatedSVGContextRetrieval = 1; } $result .= $indent . "context->svgAttributeChanged(imp->associatedAttributeName());\n"; $implIncludes{"SVGElement.h"} = 1; } # If the implementing class is a POD type, commit changes if ($isPodType) { if (!$generatedSVGContextRetrieval) { $result .= GenerateSVGContextRetrieval($implClassName, $indent); $generatedSVGContextRetrieval = 1; } $result .= $indent . "imp_wrapper->commitChange(imp_instance, context);\n"; } if ($returnsPodType) { my $classIndex = uc($returnType); $result .= $indent . "return V8DOMWrapper::convertToV8Object(V8ClassIndex::$classIndex, wrapper);\n"; } else { $result .= $indent . ReturnNativeToJSValue($function->signature, $return, $indent) . ";\n"; } return $result; } # Get the class name used for printing javascript DOM-object wrappers. sub GetClassName { my $type = shift; return "HTMLCollection" if $type eq "UndetectableHTMLCollection"; return $type; } sub GetNativeTypeFromSignature { my $signature = shift; my $isParameter = shift; my $type = $codeGenerator->StripModule($signature->type); return GetNativeType($type, $isParameter); } sub IsRefPtrType { my $type = shift; return 1 if $type eq "Attr"; return 1 if $type eq "CanvasGradient"; return 1 if $type eq "ClientRect"; return 1 if $type eq "ClientRectList"; return 1 if $type eq "CDATASection"; return 1 if $type eq "Comment"; return 1 if $type eq "CSSRule"; return 1 if $type eq "CSSStyleRule"; return 1 if $type eq "CSSCharsetRule"; return 1 if $type eq "CSSImportRule"; return 1 if $type eq "CSSMediaRule"; return 1 if $type eq "CSSFontFaceRule"; return 1 if $type eq "CSSPageRule"; return 1 if $type eq "CSSPrimitiveValue"; return 1 if $type eq "CSSStyleSheet"; return 1 if $type eq "CSSStyleDeclaration"; return 1 if $type eq "CSSValue"; return 1 if $type eq "CSSRuleList"; return 1 if $type eq "Database"; return 1 if $type eq "Document"; return 1 if $type eq "DocumentFragment"; return 1 if $type eq "DocumentType"; return 1 if $type eq "Element"; return 1 if $type eq "EntityReference"; return 1 if $type eq "Event"; return 1 if $type eq "FileList"; return 1 if $type eq "HTMLCollection"; return 1 if $type eq "HTMLDocument"; return 1 if $type eq "HTMLElement"; return 1 if $type eq "HTMLOptionsCollection"; return 1 if $type eq "ImageData"; return 1 if $type eq "MediaError"; return 1 if $type eq "MimeType"; return 1 if $type eq "Node"; return 1 if $type eq "NodeList"; return 1 if $type eq "NodeFilter"; return 1 if $type eq "NodeIterator"; return 1 if $type eq "NSResolver"; return 1 if $type eq "Plugin"; return 1 if $type eq "ProcessingInstruction"; return 1 if $type eq "Range"; return 1 if $type eq "Text"; return 1 if $type eq "TextMetrics"; return 1 if $type eq "TimeRanges"; return 1 if $type eq "TreeWalker"; return 1 if $type eq "WebKitCSSMatrix"; return 1 if $type eq "WebKitPoint"; return 1 if $type eq "XPathExpression"; return 1 if $type eq "XPathNSResolver"; return 1 if $type eq "XPathResult"; return 1 if $type eq "SVGAngle"; return 1 if $type eq "SVGElementInstance"; return 1 if $type eq "SVGElementInstanceList"; return 1 if $type =~ /^SVGPathSeg/; return 1 if $type =~ /^SVGAnimated/; return 0; } sub IsVideoClassName { my $class = shift; return 1 if $class eq "V8HTMLAudioElement"; return 1 if $class eq "V8HTMLMediaElement"; return 1 if $class eq "V8HTMLSourceElement"; return 1 if $class eq "V8HTMLVideoElement"; return 1 if $class eq "V8MediaError"; return 1 if $class eq "V8TimeRanges"; return 0; } sub IsWorkerClassName { my $class = shift; return 1 if $class eq "V8Worker"; return 1 if $class eq "V8WorkerContext"; return 1 if $class eq "V8WorkerLocation"; return 1 if $class eq "V8WorkerNavigator"; return 0; } sub GetNativeType { my $type = shift; my $isParameter = shift; if ($type eq "float" or $type eq "AtomicString" or $type eq "double") { return $type; } return "int" if $type eq "int"; return "int" if $type eq "short" or $type eq "unsigned short"; return "int" if $type eq "long" or $type eq "unsigned long"; return "unsigned long long" if $type eq "unsigned long long"; return "bool" if $type eq "boolean"; return "String" if $type eq "DOMString"; return "Range::CompareHow" if $type eq "CompareHow"; return "FloatRect" if $type eq "SVGRect"; return "FloatPoint" if $type eq "SVGPoint"; return "TransformationMatrix" if $type eq "SVGMatrix"; return "SVGTransform" if $type eq "SVGTransform"; return "SVGLength" if $type eq "SVGLength"; return "double" if $type eq "SVGNumber"; return "SVGPaint::SVGPaintType" if $type eq "SVGPaintType"; return "DOMTimeStamp" if $type eq "DOMTimeStamp"; return "unsigned" if $type eq "unsigned int"; return "unsigned" if $type eq "RGBColor"; return "Node*" if $type eq "EventTarget" and $isParameter; return "String" if $type eq "DOMUserData"; # FIXME: Temporary hack? # temporary hack return "RefPtr" if $type eq "NodeFilter"; return "RefPtr<${type}>" if IsRefPtrType($type) and not $isParameter; # Default, assume native type is a pointer with same type name as idl type return "${type}*"; } my %typeCanFailConversion = ( "AtomicString" => 0, "Attr" => 1, "CompareHow" => 0, "DataGridColumn" => 0, "DOMString" => 0, "DOMWindow" => 0, "DocumentType" => 0, "Element" => 0, "Event" => 0, "EventListener" => 0, "EventTarget" => 0, "HTMLElement" => 0, "HTMLOptionElement" => 0, "Node" => 0, "NodeFilter" => 0, "MessagePort" => 0, "NSResolver" => 0, "Range" => 0, "SQLResultSet" => 0, "Storage" => 0, "SVGAngle" => 0, "SVGElement" => 0, "SVGLength" => 1, "SVGMatrix" => 1, "SVGNumber" => 0, "SVGPaintType" => 0, "SVGPathSeg" => 0, "SVGPoint" => 1, "SVGRect" => 1, "SVGTransform" => 1, "VoidCallback" => 1, "WebKitCSSMatrix" => 0, "WebKitPoint" => 0, "XPathEvaluator" => 0, "XPathNSResolver" => 0, "XPathResult" => 0, "boolean" => 0, "double" => 0, "float" => 0, "long" => 0, "unsigned long" => 0, "unsigned short" => 0, ); sub TranslateParameter { my $signature = shift; # The IDL uses some pseudo-types which don't really exist. if ($signature->type eq "TimeoutHandler") { $signature->type("DOMString"); } } sub BasicTypeCanFailConversion { my $signature = shift; my $type = $codeGenerator->StripModule($signature->type); return 1 if $type eq "SVGLength"; return 1 if $type eq "SVGMatrix"; return 1 if $type eq "SVGPoint"; return 1 if $type eq "SVGRect"; return 1 if $type eq "SVGTransform"; return 0; } sub TypeCanFailConversion { my $signature = shift; my $type = $codeGenerator->StripModule($signature->type); $implIncludes{"ExceptionCode.h"} = 1 if $type eq "Attr"; return $typeCanFailConversion{$type} if exists $typeCanFailConversion{$type}; die "Don't know whether a JS value can fail conversion to type $type."; } sub JSValueToNative { my $signature = shift; my $value = shift; my $okParam = shift; my $maybeOkParam = $okParam ? ", ${okParam}" : ""; my $type = $codeGenerator->StripModule($signature->type); return "$value" if $type eq "JSObject"; return "$value->BooleanValue()" if $type eq "boolean"; return "static_cast<$type>($value->NumberValue())" if $type eq "float" or $type eq "double"; return "$value->NumberValue()" if $type eq "SVGNumber"; return "toInt32($value${maybeOkParam})" if $type eq "unsigned long" or $type eq "unsigned short" or $type eq "long"; return "static_cast($value->Int32Value())" if $type eq "CompareHow"; return "static_cast($value->ToInt32()->Int32Value())" if $type eq "SVGPaintType"; return "toWebCoreString($value)" if $type eq "AtomicString" or $type eq "DOMUserData"; if ($type eq "DOMString") { return "toWebCoreStringWithNullCheck($value)" if $signature->extendedAttributes->{"ConvertNullToNullString"}; return "toWebCoreStringWithNullOrUndefinedCheck($value)" if $signature->extendedAttributes->{"ConvertUndefinedOrNullToNullString"}; return "toWebCoreString($value)"; } if ($type eq "NodeFilter") { return "V8DOMWrapper::wrapNativeNodeFilter($value)"; } if ($type eq "SVGRect") { $implIncludes{"FloatRect.h"} = 1; } if ($type eq "SVGPoint") { $implIncludes{"FloatPoint.h"} = 1; } # Default, assume autogenerated type conversion routines $implIncludes{"V8Proxy.h"} = 1; if ($type eq "EventTarget") { $implIncludes{"V8Node.h"} = 1; # EventTarget is not in DOM hierarchy, but all Nodes are EventTarget. return "V8Node::HasInstance($value) ? V8DOMWrapper::convertDOMWrapperToNode($value) : 0"; } AddIncludesForType($type); # $implIncludes{"$type.h"} = 1 unless AvoidInclusionOfType($type); if (IsDOMNodeType($type)) { $implIncludes{"V8${type}.h"} = 1; # Perform type checks on the parameter, if it is expected Node type, # return NULL. return "V8${type}::HasInstance($value) ? V8DOMWrapper::convertDOMWrapperToNode<${type}>($value) : 0"; } else { # TODO: Temporary to avoid Window name conflict. my $classIndex = uc($type); my $implClassName = ${type}; $implIncludes{"V8$type.h"} = 1; if (IsPodType($type)) { my $nativeType = GetNativeType($type); $implIncludes{"V8SVGPODTypeWrapper.h"} = 1; return "V8SVGPODTypeUtil::toSVGPODType<${nativeType}>(V8ClassIndex::${classIndex}, $value${maybeOkParam})" } $implIncludes{"V8${type}.h"} = 1; # Perform type checks on the parameter, if it is expected Node type, # return NULL. return "V8${type}::HasInstance($value) ? V8DOMWrapper::convertToNativeObject<${implClassName}>(V8ClassIndex::${classIndex}, $value) : 0"; } } sub GetV8HeaderName { my $type = shift; return "V8" . GetImplementationFileName($type); } sub CreateCustomSignature { my $function = shift; my $count = @{$function->parameters}; my $name = $function->signature->name; my $result = " const int ${name}_argc = ${count};\n" . " v8::Handle ${name}_argv[${name}_argc] = { "; my $first = 1; foreach my $parameter (@{$function->parameters}) { if ($first) { $first = 0; } else { $result .= ", "; } if (IsWrapperType($parameter->type)) { my $type = $parameter->type; my $header = GetV8HeaderName($type); $implIncludes{$header} = 1; $result .= "V8${type}::GetRawTemplate()"; } else { $result .= "v8::Handle()"; } } $result .= " };\n"; $result .= " v8::Handle ${name}_signature = v8::Signature::New(desc, ${name}_argc, ${name}_argv);\n"; return $result; } sub RequiresCustomSignature { my $function = shift; # No signature needed for Custom function if ($function->signature->extendedAttributes->{"Custom"} || $function->signature->extendedAttributes->{"V8Custom"}) { return 0; } foreach my $parameter (@{$function->parameters}) { if (IsWrapperType($parameter->type)) { return 1; } } return 0; } my %non_wrapper_types = ( 'float' => 1, 'AtomicString' => 1, 'double' => 1, 'short' => 1, 'unsigned short' => 1, 'long' => 1, 'unsigned long' => 1, 'boolean' => 1, 'DOMString' => 1, 'CompareHow' => 1, 'SVGRect' => 1, 'SVGPoint' => 1, 'SVGMatrix' => 1, 'SVGTransform' => 1, 'SVGLength' => 1, 'SVGNumber' => 1, 'SVGPaintType' => 1, 'DOMTimeStamp' => 1, 'JSObject' => 1, 'EventTarget' => 1, 'NodeFilter' => 1, 'EventListener' => 1 ); sub IsWrapperType { my $type = $codeGenerator->StripModule(shift); return !($non_wrapper_types{$type}); } sub IsDOMNodeType { my $type = shift; return 1 if $type eq 'Attr'; return 1 if $type eq 'CDATASection'; return 1 if $type eq 'Comment'; return 1 if $type eq 'Document'; return 1 if $type eq 'DocumentFragment'; return 1 if $type eq 'DocumentType'; return 1 if $type eq 'Element'; return 1 if $type eq 'EntityReference'; return 1 if $type eq 'HTMLCanvasElement'; return 1 if $type eq 'HTMLDocument'; return 1 if $type eq 'HTMLElement'; return 1 if $type eq 'HTMLFormElement'; return 1 if $type eq 'HTMLTableCaptionElement'; return 1 if $type eq 'HTMLTableSectionElement'; return 1 if $type eq 'Node'; return 1 if $type eq 'ProcessingInstruction'; return 1 if $type eq 'SVGElement'; return 1 if $type eq 'SVGDocument'; return 1 if $type eq 'SVGSVGElement'; return 1 if $type eq 'SVGUseElement'; return 1 if $type eq 'Text'; return 0; } sub ReturnNativeToJSValue { my $signature = shift; my $value = shift; my $indent = shift; my $type = $codeGenerator->StripModule($signature->type); my $className= "V8$type"; return "return v8::Date::New(static_cast($value))" if $type eq "DOMTimeStamp"; return "return $value ? v8::True() : v8::False()" if $type eq "boolean"; return "return v8::Undefined()" if $type eq "void"; # For all the types where we use 'int' as the representation type, # we use Integer::New which has a fast Smi conversion check. return "return v8::Integer::New($value)" if GetNativeType($type) eq "int"; return "return v8::Number::New($value)" if $codeGenerator->IsPrimitiveType($type) or $type eq "SVGPaintType"; if ($codeGenerator->IsStringType($type)) { my $conv = $signature->extendedAttributes->{"ConvertNullStringTo"}; if (defined $conv) { return "return v8StringOrNull($value)" if $conv eq "Null"; return "return v8StringOrUndefined($value)" if $conv eq "Undefined"; return "return v8StringOrFalse($value)" if $conv eq "False"; die "Unknown value for ConvertNullStringTo extended attribute"; } return "return v8String($value)"; } # V8 specific. my $implClassName = $type; AddIncludesForType($type); # $implIncludes{GetImplementationFileName($type)} = 1 unless AvoidInclusionOfType($type); # special case for non-DOM node interfaces if (IsDOMNodeType($type)) { return "return V8DOMWrapper::convertNodeToV8Object($value)"; } if ($type eq "EventTarget" or $type eq "SVGElementInstance") { return "return V8DOMWrapper::convertEventTargetToV8Object($value)"; } if ($type eq "Event") { return "return V8DOMWrapper::convertEventToV8Object($value)"; } if ($type eq "EventListener") { return "return V8DOMWrapper::convertEventListenerToV8Object($value)"; } if ($type eq "RGBColor") { my $construct = "RefPtr rgbcolor = RGBColor::create($value);\n"; my $convert = "V8DOMWrapper::convertToV8Object(V8ClassIndex::RGBCOLOR, WTF::getPtr(rgbcolor))"; return $construct . $indent . "return " . $convert; } if ($type eq "WorkerContext" or $type eq "WorkerLocation" or $type eq "WorkerNavigator") { $implIncludes{"WorkerContextExecutionProxy.h"} = 1; my $classIndex = uc($type); return "return WorkerContextExecutionProxy::ToV8Object(V8ClassIndex::$classIndex, $value)"; } else { $implIncludes{"wtf/RefCounted.h"} = 1; $implIncludes{"wtf/RefPtr.h"} = 1; my $classIndex = uc($type); if (IsPodType($type)) { $value = GenerateSVGStaticPodTypeWrapper($type, $value); } return "return V8DOMWrapper::convertToV8Object(V8ClassIndex::$classIndex, $value)"; } } sub GenerateSVGStaticPodTypeWrapper { my $type = shift; my $value = shift; $implIncludes{"V8$type.h"}=1; $implIncludes{"V8SVGPODTypeWrapper.h"} = 1; my $nativeType = GetNativeType($type); return "new V8SVGStaticPODTypeWrapper<$nativeType>($value)"; } # Internal helper sub WriteData { if (defined($IMPL)) { # Write content to file. print $IMPL @implContentHeader; print $IMPL @implFixedHeader; foreach my $implInclude (sort keys(%implIncludes)) { my $checkType = $implInclude; $checkType =~ s/\.h//; print $IMPL "#include \"$implInclude\"\n" unless $codeGenerator->IsSVGAnimatedType($checkType); } print $IMPL "\n"; print $IMPL @implContentDecls; print $IMPL @implContent; close($IMPL); undef($IMPL); %implIncludes = (); @implFixedHeader = (); @implHeaderContent = (); @implContentDecls = (); @implContent = (); } if (defined($HEADER)) { # Write content to file. print $HEADER @headerContent; close($HEADER); undef($HEADER); @headerContent = (); } } sub IsSVGTypeNeedingContextParameter { my $implClassName = shift; if ($implClassName =~ /SVG/ and not $implClassName =~ /Element/) { return 1 unless $implClassName =~ /SVGPaint/ or $implClassName =~ /SVGColor/ or $implClassName =~ /SVGDocument/; } return 0; } sub GenerateSVGContextAssignment { my $srcType = shift; my $value = shift; my $indent = shift; $result = GenerateSVGContextRetrieval($srcType, $indent); $result .= $indent . "V8Proxy::setSVGContext($value, context);\n"; return $result; } sub GenerateSVGContextRetrieval { my $srcType = shift; my $indent = shift; my $srcIsPodType = IsPodType($srcType); my $srcObject = "imp"; if ($srcIsPodType) { $srcObject = "imp_wrapper"; } my $contextDecl; if (IsSVGTypeNeedingContextParameter($srcType)) { $contextDecl = "V8Proxy::svgContext($srcObject)"; } else { $contextDecl = $srcObject; } return $indent . "SVGElement* context = $contextDecl;\n"; } sub IsSVGListMutator { my $functionName = shift; return 1 if $functionName eq "clear"; return 1 if $functionName eq "initialize"; return 1 if $functionName eq "insertItemBefore"; return 1 if $functionName eq "replaceItem"; return 1 if $functionName eq "removeItem"; return 1 if $functionName eq "appendItem"; return 0; } sub IsSVGListMethod { my $functionName = shift; return 1 if $functionName eq "getFirst"; return 1 if $functionName eq "getLast"; return 1 if $functionName eq "getItem"; return IsSVGListMutator($functionName); } sub IsSVGListTypeNeedingSpecialHandling { my $className = shift; return 1 if $className eq "SVGPointList"; return 1 if $className eq "SVGTransformList"; return 0; } sub DebugPrint { my $output = shift; print $output; print "\n"; }