# 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, 2012 Google Inc. # Copyright (C) 2009 Cameron McCormack # Copyright (C) Research In Motion Limited 2010. All rights reserved. # Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) # # 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. # package CodeGeneratorV8; use strict; use Digest::MD5; use constant FileNamePrefix => "V8"; my $codeGenerator; my $module = ""; my $outputDir = ""; my $outputHeadersDir = ""; my @headerContent = (); my @implContentHeader = (); my @implFixedHeader = (); my @implContent = (); my @implContentDecls = (); my %implIncludes = (); my %headerIncludes = (); 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; $outputHeadersDir = shift; bless($reference, $object); return $reference; } # Params: 'domClass' struct sub GenerateInterface { my $object = shift; my $dataNode = shift; my $defines = shift; # Start actual generation if ($dataNode->extendedAttributes->{"Callback"}) { $object->GenerateCallbackHeader($dataNode); $object->GenerateCallbackImplementation($dataNode); } else { $object->GenerateHeader($dataNode); $object->GenerateImplementation($dataNode); } $object->WriteData($dataNode); } # Params: 'idlDocument' struct sub GenerateModule { my $object = shift; my $dataNode = shift; $module = $dataNode->module; } sub AddToImplIncludes { my $header = shift; my $conditional = shift; if (not $conditional) { $implIncludes{$header} = 1; } elsif (not exists($implIncludes{$header})) { $implIncludes{$header} = $conditional; } else { my $oldValue = $implIncludes{$header}; if ($oldValue ne 1) { my %newValue = (); $newValue{$conditional} = 1; foreach my $condition (split(/\|/, $oldValue)) { $newValue{$condition} = 1; } $implIncludes{$header} = join("|", sort keys %newValue); } } } 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 (IsTypedArrayType($type)) { AddToImplIncludes("wtf/${type}.h"); } if (!$codeGenerator->IsPrimitiveType($type) and !$codeGenerator->IsStringType($type) and !$codeGenerator->SkipIncludeHeader($type) and $type ne "Date") { # default, include the same named file AddToImplIncludes(GetV8HeaderName(${type})); if ($type =~ /SVGPathSeg/) { my $joinedName = $type; $joinedName =~ s/Abs|Rel//; AddToImplIncludes("${joinedName}.h"); } } # additional includes (things needed to compile the bindings but not the header) if ($type eq "CanvasRenderingContext2D") { AddToImplIncludes("CanvasGradient.h"); AddToImplIncludes("CanvasPattern.h"); AddToImplIncludes("CanvasStyle.h"); } if ($type eq "CanvasGradient" or $type eq "XPathNSResolver") { AddToImplIncludes("PlatformString.h"); } if ($type eq "CSSStyleSheet" or $type eq "StyleSheet") { AddToImplIncludes("CSSImportRule.h"); } if ($type eq "CSSStyleDeclaration") { AddToImplIncludes("StylePropertySet.h"); } if ($type eq "Plugin" or $type eq "PluginArray" or $type eq "MimeTypeArray") { # So we can get String -> AtomicString conversion for namedItem(). AddToImplIncludes("wtf/text/AtomicString.h"); } } sub GetSVGPropertyTypes { my $implType = shift; my $svgPropertyType; my $svgListPropertyType; my $svgNativeType; return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $implType =~ /SVG/; $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($implType); return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $svgNativeType; # Append space to avoid compilation errors when using PassRefPtr<$svgNativeType> $svgNativeType = "$svgNativeType "; my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($implType); if ($svgNativeType =~ /SVGPropertyTearOff/) { $svgPropertyType = $svgWrappedNativeType; AddToImplIncludes("SVGAnimatedPropertyTearOff.h"); } elsif ($svgNativeType =~ /SVGListPropertyTearOff/ or $svgNativeType =~ /SVGStaticListPropertyTearOff/) { $svgListPropertyType = $svgWrappedNativeType; $headerIncludes{"SVGAnimatedListPropertyTearOff.h"} = 1; $headerIncludes{"SVGStaticListPropertyTearOff.h"} = 1; } elsif ($svgNativeType =~ /SVGTransformListPropertyTearOff/) { $svgListPropertyType = $svgWrappedNativeType; $headerIncludes{"SVGAnimatedListPropertyTearOff.h"} = 1; $headerIncludes{"SVGTransformListPropertyTearOff.h"} = 1; } elsif ($svgNativeType =~ /SVGPathSegListPropertyTearOff/) { $svgListPropertyType = $svgWrappedNativeType; $headerIncludes{"SVGPathSegListPropertyTearOff.h"} = 1; } if ($svgPropertyType) { $svgPropertyType = "SVGPoint" if $svgPropertyType eq "FloatPoint"; } return ($svgPropertyType, $svgListPropertyType, $svgNativeType); } 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, \@allParents, 1); $codeGenerator->LinkOverloadedFunctions($dataNode); my $hasDependentLifetime = $dataNode->extendedAttributes->{"V8DependentLifetime"} || $dataNode->extendedAttributes->{"ActiveDOMObject"} || $className =~ /SVG/; if (!$hasDependentLifetime) { foreach (@{$dataNode->parents}) { my $parent = $codeGenerator->StripModule($_); next if $parent eq "EventTarget"; $headerIncludes{"V8${parent}.h"} = 1; } } # - Add default header template push(@headerContent, GenerateHeaderContentHeader($dataNode)); $headerIncludes{"wtf/text/StringHash.h"} = 1; $headerIncludes{"WrapperTypeInfo.h"} = 1; $headerIncludes{"V8DOMWrapper.h"} = 1; $headerIncludes{"wtf/HashMap.h"} = 1; $headerIncludes{"v8.h"} = 1; my $headerClassInclude = GetHeaderClassInclude($implClassName); $headerIncludes{$headerClassInclude} = 1 if $headerClassInclude ne ""; my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($implClassName); foreach my $headerInclude (sort keys(%headerIncludes)) { if ($headerInclude =~ /wtf|v8\.h/) { push(@headerContent, "#include \<${headerInclude}\>\n"); } else { push(@headerContent, "#include \"${headerInclude}\"\n"); } } push(@headerContent, "\nnamespace WebCore {\n"); push(@headerContent, "\ntemplate class SVGPropertyTearOff;\n") if $svgPropertyType; if ($svgNativeType) { if ($svgNativeType =~ /SVGStaticListPropertyTearOff/) { push(@headerContent, "\ntemplate class SVGStaticListPropertyTearOff;\n"); } else { push(@headerContent, "\ntemplate class SVGListPropertyTearOff;\n"); } } push(@headerContent, "\n"); push(@headerContent, "class FloatRect;\n") if $svgPropertyType && $svgPropertyType eq "FloatRect"; push(@headerContent, "class Dictionary;\n") if IsConstructorTemplate($dataNode, "Event"); my $nativeType = GetNativeTypeForConversions($dataNode, $interfaceName); if ($dataNode->extendedAttributes->{"NamedConstructor"}) { push(@headerContent, < GetTemplate(); static WrapperTypeInfo info; }; END } push(@headerContent, "class $className {\n"); push(@headerContent, "public:\n"); push(@headerContent, " static const bool hasDependentLifetime = "); if ($hasDependentLifetime) { push(@headerContent, "true;\n"); } elsif (@{$dataNode->parents}) { # Even if this type doesn't have the V8DependentLifetime attribute its parents may. # Let the compiler statically determine this for us. my $separator = ""; foreach (@{$dataNode->parents}) { my $parent = $codeGenerator->StripModule($_); next if $parent eq "EventTarget"; $headerIncludes{"V8${parent}.h"} = 1; push(@headerContent, "${separator}V8${parent}::hasDependentLifetime"); $separator = " || "; } push(@headerContent, ";\n"); } else { push(@headerContent, "false;\n"); } my $domMapFunction = GetDomMapFunction($dataNode, $interfaceName); my $forceNewObjectParameter = IsDOMNodeType($interfaceName) ? ", bool forceNewObject = false" : ""; my $forceNewObjectInput = IsDOMNodeType($interfaceName) ? ", bool forceNewObject" : ""; my $forceNewObjectCall = IsDOMNodeType($interfaceName) ? ", forceNewObject" : ""; push(@headerContent, <); static v8::Persistent GetRawTemplate(); static v8::Persistent GetTemplate(); static ${nativeType}* toNative(v8::Handle object) { return reinterpret_cast<${nativeType}*>(object->GetPointerFromInternalField(v8DOMWrapperObjectIndex)); } inline static v8::Handle wrap(${nativeType}*, v8::Isolate* = 0${forceNewObjectParameter}); static void derefObject(void*); static WrapperTypeInfo info; END if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) { push(@headerContent, " static ActiveDOMObject* toActiveDOMObject(v8::Handle);\n"); } if ($implClassName eq "DOMWindow") { push(@headerContent, < GetShadowObjectTemplate(); END } if ($implClassName eq "HTMLDocument") { push(@headerContent, < WrapInShadowObject(v8::Local wrapper, Node* impl); static v8::Handle GetNamedProperty(HTMLDocument* htmlDocument, const AtomicString& key, v8::Isolate*); END } my @enabledAtRuntime; foreach my $function (@{$dataNode->functions}) { my $name = $function->signature->name; my $attrExt = $function->signature->extendedAttributes; if (($attrExt->{"Custom"} || $attrExt->{"V8Custom"}) && !$attrExt->{"ImplementedBy"} && $function->{overloadIndex} == 1) { my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; push(@headerContent, < ${name}Callback(const v8::Arguments&); END push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } if ($attrExt->{"V8EnabledAtRuntime"}) { push(@enabledAtRuntime, $function); } } if (IsConstructable($dataNode)) { push(@headerContent, < constructorCallback(const v8::Arguments&); END } foreach my $attribute (@{$dataNode->attributes}) { my $name = $attribute->signature->name; my $attrExt = $attribute->signature->extendedAttributes; my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); if (($attrExt->{"V8CustomGetter"} || $attrExt->{"CustomGetter"} || $attrExt->{"V8Custom"} || $attrExt->{"Custom"}) && !$attrExt->{"ImplementedBy"}) { push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; push(@headerContent, < ${name}AccessorGetter(v8::Local name, const v8::AccessorInfo&); END push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } if (($attrExt->{"V8CustomSetter"} || $attrExt->{"CustomSetter"} || $attrExt->{"V8Custom"} || $attrExt->{"Custom"}) && !$attrExt->{"ImplementedBy"}) { push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; push(@headerContent, < name, v8::Local, const v8::AccessorInfo&); END push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } if ($attrExt->{"V8EnabledAtRuntime"}) { push(@enabledAtRuntime, $attribute); } } GenerateHeaderNamedAndIndexedPropertyAccessors($dataNode); GenerateHeaderCustomCall($dataNode); GenerateHeaderCustomInternalFieldIndices($dataNode); if ($dataNode->extendedAttributes->{"CheckSecurity"}) { push(@headerContent, < host, v8::Local key, v8::AccessType, v8::Local data); static bool indexedSecurityCheck(v8::Local host, uint32_t index, v8::AccessType, v8::Local data); END } my $wrapSlowArgumentType = GetPassRefPtrType($nativeType); push(@headerContent, < wrapSlow(${wrapSlowArgumentType}, v8::Isolate*); }; END push(@headerContent, < ${className}::wrap(${nativeType}* impl, v8::Isolate* isolate${forceNewObjectInput}) { END push(@headerContent, " if (!forceNewObject) {\n") if IsDOMNodeType($interfaceName); my $getCachedWrapper = IsNodeSubType($dataNode) ? "V8DOMWrapper::getCachedWrapper(impl)" : "${domMapFunction}.get(impl)"; push(@headerContent, < wrapper = $getCachedWrapper; if (!wrapper.IsEmpty()) return wrapper; END push(@headerContent, " }\n") if IsDOMNodeType($interfaceName); push(@headerContent, <extendedAttributes->{"CustomToJSObject"} or $dataNode->extendedAttributes->{"V8CustomToJSObject"})) { push(@headerContent, < toV8(${nativeType}* impl, v8::Isolate* isolate = 0${forceNewObjectParameter}) { if (!impl) return v8::Null(); return ${className}::wrap(impl, isolate${forceNewObjectCall}); } END } elsif ($interfaceName ne 'Node') { push(@headerContent, < toV8(${nativeType}*, v8::Isolate* = 0${forceNewObjectParameter}); END } else { push(@headerContent, < toV8Slow(Node*, v8::Isolate*, bool); inline v8::Handle toV8(Node* impl, v8::Isolate* isolate = 0, bool forceNewObject = false) { if (UNLIKELY(!impl)) return v8::Null(); if (UNLIKELY(forceNewObject)) return toV8Slow(impl, isolate, forceNewObject); v8::Handle wrapper = V8DOMWrapper::getCachedWrapper(impl); if (!wrapper.IsEmpty()) return wrapper; return toV8Slow(impl, isolate, false); } END } push(@headerContent, < toV8(PassRefPtr< ${nativeType} > impl, v8::Isolate* isolate = 0${forceNewObjectParameter}) { return toV8(impl.get(), isolate${forceNewObjectCall}); } END if (IsConstructorTemplate($dataNode, "Event")) { push(@headerContent, "\nbool fill${implClassName}Init(${implClassName}Init&, const Dictionary&);\n"); } push(@headerContent, "\n}\n\n"); push(@headerContent, "#endif // $className" . "_h\n"); my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode); push(@headerContent, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GetInternalFields { my $dataNode = shift; my $name = $dataNode->name; my @customInternalFields = (); # We can't ask whether a parent type has a given extendedAttribute, # so special-case AbstractWorker and WorkerContext to include all sub-types. # Event listeners on DOM nodes are explicitly supported in the GC controller. # FIXME: SVGElementInstance should probably have the EventTarget extended attribute, but doesn't. if (!IsNodeSubType($dataNode) && ($dataNode->extendedAttributes->{"EventTarget"} || $dataNode->extendedAttributes->{"IsWorkerContext"} || IsSubType($dataNode, "AbstractWorker") || $name eq "SVGElementInstance")) { push(@customInternalFields, "eventListenerCacheIndex"); } if ($name eq "DOMWindow") { push(@customInternalFields, "enteredIsolatedWorldIndex"); } return @customInternalFields; } sub GetHeaderClassInclude { my $className = shift; if ($className =~ /SVGPathSeg/) { $className =~ s/Abs|Rel//; } return "wtf/${className}.h" if IsTypedArrayType($className); return "" if ($codeGenerator->SkipIncludeHeader($className)); return "${className}.h"; } sub GenerateHeaderCustomInternalFieldIndices { my $dataNode = shift; my @customInternalFields = GetInternalFields($dataNode); my $customFieldCounter = 0; foreach my $customInternalField (@customInternalFields) { push(@headerContent, < 1, "HTMLAppletElement" => 1, "HTMLEmbedElement" => 1, "HTMLObjectElement" => 1 ); sub GenerateHeaderNamedAndIndexedPropertyAccessors { my $dataNode = shift; my $interfaceName = $dataNode->name; my $hasCustomIndexedGetter = $dataNode->extendedAttributes->{"IndexedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"}; my $hasCustomIndexedSetter = $dataNode->extendedAttributes->{"CustomIndexedSetter"} && !$dataNode->extendedAttributes->{"NumericIndexedGetter"}; my $hasCustomNamedGetter = $dataNode->extendedAttributes->{"NamedGetter"} || $dataNode->extendedAttributes->{"CustomNamedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"}; my $hasCustomNamedSetter = $dataNode->extendedAttributes->{"CustomNamedSetter"}; my $hasCustomDeleters = $dataNode->extendedAttributes->{"CustomDeleteProperty"}; my $hasCustomEnumerator = $dataNode->extendedAttributes->{"CustomEnumerateProperty"}; if ($interfaceName eq "HTMLOptionsCollection") { $interfaceName = "HTMLCollection"; $hasCustomIndexedGetter = 1; $hasCustomNamedGetter = 1; } if ($interfaceName eq "DOMWindow") { $hasCustomDeleters = 0; $hasCustomEnumerator = 0; } if ($interfaceName eq "HTMLAppletElement" || $interfaceName eq "HTMLEmbedElement" || $interfaceName eq "HTMLObjectElement") { $hasCustomNamedGetter = 1; } if ($interfaceName eq "HTMLDocument") { $hasCustomNamedGetter = 0; $hasCustomIndexedGetter = 0; } my $isIndexerSpecialCase = exists $indexerSpecialCases{$interfaceName}; if ($hasCustomIndexedGetter || $isIndexerSpecialCase) { push(@headerContent, < indexedPropertyGetter(uint32_t, const v8::AccessorInfo&); END } if ($isIndexerSpecialCase || $hasCustomIndexedSetter) { push(@headerContent, < indexedPropertySetter(uint32_t, v8::Local, const v8::AccessorInfo&); END } if ($hasCustomDeleters) { push(@headerContent, < indexedPropertyDeleter(uint32_t, const v8::AccessorInfo&); END } if ($hasCustomNamedGetter) { push(@headerContent, < namedPropertyGetter(v8::Local, const v8::AccessorInfo&); END } if ($hasCustomNamedSetter) { push(@headerContent, < namedPropertySetter(v8::Local, v8::Local, const v8::AccessorInfo&); END } if ($hasCustomDeleters) { push(@headerContent, < namedPropertyDeleter(v8::Local, const v8::AccessorInfo&); END } if ($hasCustomEnumerator) { push(@headerContent, < namedPropertyEnumerator(const v8::AccessorInfo&); static v8::Handle namedPropertyQuery(v8::Local, const v8::AccessorInfo&); END } } sub GenerateHeaderCustomCall { my $dataNode = shift; if ($dataNode->extendedAttributes->{"CustomCall"}) { push(@headerContent, " static v8::Handle callAsFunctionCallback(const v8::Arguments&);\n"); } if ($dataNode->name eq "Event") { push(@headerContent, " static v8::Handle dataTransferAccessorGetter(v8::Local name, const v8::AccessorInfo&);\n"); push(@headerContent, " static void valueAccessorSetter(v8::Local name, v8::Local, const v8::AccessorInfo&);\n"); } if ($dataNode->name eq "Location") { push(@headerContent, " static v8::Handle assignAccessorGetter(v8::Local name, const v8::AccessorInfo&);\n"); push(@headerContent, " static v8::Handle reloadAccessorGetter(v8::Local name, const v8::AccessorInfo&);\n"); push(@headerContent, " static v8::Handle replaceAccessorGetter(v8::Local name, const v8::AccessorInfo&);\n"); } } sub GenerateSetDOMException { my $indent = shift; my $getIsolate = shift; my $result = ""; $result .= $indent . "if (UNLIKELY(ec)) {\n"; $result .= $indent . " V8Proxy::setDOMException(ec, $getIsolate);\n"; $result .= $indent . " return v8::Handle();\n"; $result .= $indent . "}\n"; return $result; } sub IsSubType { my $dataNode = shift; my $parentType = shift; return 1 if ($dataNode->name eq $parentType); foreach (@allParents) { my $parent = $codeGenerator->StripModule($_); return 1 if $parent eq $parentType; } return 0; } sub IsNodeSubType { my $dataNode = shift; return IsSubType($dataNode, "Node"); } sub IsVisibleAcrossOrigins { my $dataNode = shift; return $dataNode->extendedAttributes->{"CheckSecurity"} && !($dataNode->name eq "DOMWindow"); } sub IsConstructable { my $dataNode = shift; return $dataNode->extendedAttributes->{"CustomConstructor"} || $dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"Constructor"} || $dataNode->extendedAttributes->{"ConstructorTemplate"}; } sub IsConstructorTemplate { my $dataNode = shift; my $template = shift; return $dataNode->extendedAttributes->{"ConstructorTemplate"} && $dataNode->extendedAttributes->{"ConstructorTemplate"} eq $template; } sub GenerateDomainSafeFunctionGetter { my $function = shift; my $implClassName = shift; my $className = "V8" . $implClassName; my $funcName = $function->signature->name; my $signature = "v8::Signature::New(" . $className . "::GetRawTemplate())"; if ($function->signature->extendedAttributes->{"V8DoNotCheckSignature"}) { $signature = "v8::Local()"; } my $newTemplateString = GenerateNewFunctionTemplate($function, $implClassName, $signature); push(@implContentDecls, < ${funcName}AttrGetter(v8::Local name, const v8::AccessorInfo& info) { INC_STATS(\"DOM.$implClassName.$funcName._get\"); static v8::Persistent privateTemplate = v8::Persistent::New($newTemplateString); v8::Handle holder = V8DOMWrapper::lookupDOMWrapper(${className}::GetTemplate(), info.This()); if (holder.IsEmpty()) { // can only reach here by 'object.__proto__.func', and it should passed // domain security check already return privateTemplate->GetFunction(); } ${implClassName}* imp = ${className}::toNative(holder); if (!V8BindingSecurity::canAccessFrame(V8BindingState::Only(), imp->frame(), false)) { static v8::Persistent sharedTemplate = v8::Persistent::New($newTemplateString); return sharedTemplate->GetFunction(); } return privateTemplate->GetFunction(); } END } sub GenerateConstructorGetter { my $dataNode = shift; my $implClassName = 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->IsExternal() || data->IsNumber()); WrapperTypeInfo* type = WrapperTypeInfo::unwrap(data); END if ($implClassName eq "DOMWindow") { push(@implContentDecls, <extendedAttributes->{"IsWorkerContext"}) { push(@implContentDecls, <();"); } push(@implContentDecls, <signature->extendedAttributes; my $attrName = $attribute->signature->name; my $attrType = GetTypeFromSignature($attribute->signature); my $nativeType = GetNativeTypeFromSignature($attribute->signature, -1); my $getterStringUsesImp = $implClassName ne "SVGNumber"; my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($implClassName); # Getter my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentDecls, < ${attrName}AttrGetter(v8::Local name, const v8::AccessorInfo& info) { INC_STATS(\"DOM.$implClassName.$attrName._get\"); END if ($svgNativeType) { my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($implClassName); if ($svgWrappedNativeType =~ /List/) { push(@implContentDecls, <propertyReference(); END if ($getterStringUsesImp) { push(@implContentDecls, <{"V8OnProto"} || $attrExt->{"V8Unforgeable"}) { if ($interfaceName eq "DOMWindow") { push(@implContentDecls, < holder = info.Holder(); END } else { # perform lookup first push(@implContentDecls, < holder = V8DOMWrapper::lookupDOMWrapper(V8${interfaceName}::GetTemplate(), info.This()); if (holder.IsEmpty()) return v8::Handle(); END } push(@implContentDecls, <signature->extendedAttributes->{"Reflect"}; my $url = $attribute->signature->extendedAttributes->{"URL"}; if ($getterStringUsesImp && $reflect && !$url && IsNodeSubType($dataNode) && $codeGenerator->IsStringType($attrType)) { # Generate super-compact call for regular attribute getter: my $contentAttributeName = $reflect eq "VALUE_IS_MISSING" ? lc $attrName : $reflect; my $namespace = $codeGenerator->NamespaceForAttributeName($interfaceName, $contentAttributeName); AddToImplIncludes("${namespace}.h"); push(@implContentDecls, " return getElementStringAttr(info, ${namespace}::${contentAttributeName}Attr);\n"); push(@implContentDecls, "}\n\n"); push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; return; # Skip the rest of the function! } if ($attribute->signature->type eq "SerializedScriptValue" && $attrExt->{"CachedAttribute"}) { push(@implContentDecls, < propertyName = v8::String::NewSymbol("${attrName}"); v8::Handle value = info.Holder()->GetHiddenValue(propertyName); if (!value.IsEmpty()) return value; END } push(@implContentDecls, <signature->extendedAttributes->{"CheckSecurityForNode"}) { push(@implContentDecls, " if (!V8BindingSecurity::shouldAllowAccessToNode(V8BindingState::Only(), imp->" . $attribute->signature->name . "()))\n return v8::Handle(v8::Null());\n\n"); } my $useExceptions = 1 if @{$attribute->getterExceptions}; if ($useExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContentDecls, " ExceptionCode ec = 0;\n"); } my $returnType = GetTypeFromSignature($attribute->signature); my $getterString; if ($getterStringUsesImp) { my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); push(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContentDecls, " ", 0, 0)); push(@arguments, "ec") if $useExceptions; if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; AddToImplIncludes("${implementedBy}.h"); unshift(@arguments, "imp"); $functionName = "${implementedBy}::${functionName}"; } else { $functionName = "imp->${functionName}"; } $getterString = "${functionName}(" . join(", ", @arguments) . ")"; } else { $getterString = "impInstance"; } my $result; my $wrapper; if ($attribute->signature->type eq "EventListener" && $dataNode->name eq "DOMWindow") { push(@implContentDecls, " if (!imp->document())\n"); push(@implContentDecls, " return v8::Handle();\n"); } if ($useExceptions) { if ($nativeType =~ /^V8Parameter/) { push(@implContentDecls, " " . ConvertToV8Parameter($attribute->signature, $nativeType, "v", $getterString) . ";\n"); } else { push(@implContentDecls, " $nativeType v = $getterString;\n"); } push(@implContentDecls, GenerateSetDOMException(" ", "info.GetIsolate()")); if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) { push(@implContentDecls, " if (state.hadException())\n"); push(@implContentDecls, " return throwError(state.exception());\n"); } $result = "v"; $result .= ".release()" if (IsRefPtrType($returnType)); } else { # Can inline the function call into the return statement to avoid overhead of using a Ref<> temporary $result = $getterString; # Fix amigious conversion problem, by casting to the base type first ($getterString returns a type that inherits from SVGAnimatedEnumeration, not the base class directly). $result = "static_pointer_cast($result)" if $returnType eq "SVGAnimatedEnumeration"; } # Special case for readonly or Replaceable attributes (with a few exceptions). This attempts to ensure that JS wrappers don't get # garbage-collected prematurely when their lifetime is strongly tied to their owner. We accomplish this by inserting a reference to # the newly created wrapper into an internal field of the holder object. if (!IsNodeSubType($dataNode) && $attrName ne "self" && (IsWrapperType($returnType) && ($attribute->type =~ /^readonly/ || $attribute->signature->extendedAttributes->{"Replaceable"}) && $returnType ne "EventTarget" && $returnType ne "SerializedScriptValue" && $returnType ne "DOMWindow" && $returnType ne "MessagePortArray" && $returnType !~ /SVG/ && $returnType !~ /HTML/ && !IsDOMNodeType($returnType))) { my $arrayType = $codeGenerator->GetArrayType($returnType); if ($arrayType) { if (!$codeGenerator->SkipIncludeHeader($arrayType)) { AddToImplIncludes("V8$arrayType.h"); AddToImplIncludes("$arrayType.h"); } push(@implContentDecls, " return v8Array(${getterString}, info.GetIsolate());\n"); push(@implContentDecls, "}\n\n"); return; } AddIncludesForType($returnType); # Check for a wrapper in the wrapper cache. If there is one, we know that a hidden reference has already # been created. If we don't find a wrapper, we create both a wrapper and a hidden reference. push(@implContentDecls, " RefPtr<$returnType> result = ${getterString};\n"); my $domMapFunction = GetDomMapFunction($dataNode, $interfaceName); push(@implContentDecls, " v8::Handle wrapper = result.get() ? ${domMapFunction}.get(result.get()) : v8::Handle();\n"); push(@implContentDecls, " if (wrapper.IsEmpty()) {\n"); push(@implContentDecls, " wrapper = toV8(result.get(), info.GetIsolate());\n"); push(@implContentDecls, " if (!wrapper.IsEmpty())\n"); if ($dataNode->name eq "DOMWindow") { push(@implContentDecls, " V8DOMWrapper::setNamedHiddenWindowReference(imp->frame(), \"${attrName}\", wrapper);\n"); } else { push(@implContentDecls, " V8DOMWrapper::setNamedHiddenReference(info.Holder(), \"${attrName}\", wrapper);\n"); } push(@implContentDecls, " }\n"); push(@implContentDecls, " return wrapper;\n"); push(@implContentDecls, "}\n\n"); push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; return; } if ($codeGenerator->IsSVGAnimatedType($implClassName) and $codeGenerator->IsSVGTypeNeedingTearOff($attrType)) { AddToImplIncludes("V8$attrType.h"); my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($attrType); # Convert from abstract SVGProperty to real type, so the right toJS() method can be invoked. push(@implContentDecls, " return toV8(static_cast<$svgNativeType*>($result), info.GetIsolate());\n"); } elsif ($codeGenerator->IsSVGTypeNeedingTearOff($attrType) and not $implClassName =~ /List$/) { AddToImplIncludes("V8$attrType.h"); AddToImplIncludes("SVGPropertyTearOff.h"); my $tearOffType = $codeGenerator->GetSVGTypeNeedingTearOff($attrType); if ($codeGenerator->IsSVGTypeWithWritablePropertiesNeedingTearOff($attrType) and not defined $attribute->signature->extendedAttributes->{"Immutable"}) { my $getter = $result; $getter =~ s/imp->//; $getter =~ s/\(\)//; my $updateMethod = "&${implClassName}::update" . $codeGenerator->WK_ucfirst($getter); my $selfIsTearOffType = $codeGenerator->IsSVGTypeNeedingTearOff($implClassName); if ($selfIsTearOffType) { AddToImplIncludes("SVGStaticPropertyWithParentTearOff.h"); $tearOffType =~ s/SVGPropertyTearOffsignature->type eq "MessagePortArray") { AddToImplIncludes("V8Array.h"); AddToImplIncludes("MessagePort.h"); my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name); push(@implContentDecls, <${getterFunc}(); if (!ports) return v8::Array::New(0); MessagePortArray portsCopy(*ports); v8::Local portArray = v8::Array::New(portsCopy.size()); for (size_t i = 0; i < portsCopy.size(); ++i) portArray->Set(v8::Integer::New(i), toV8(portsCopy[i].get(), info.GetIsolate())); return portArray; END } else { if ($attribute->signature->type eq "SerializedScriptValue" && $attrExt->{"CachedAttribute"}) { my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name); push(@implContentDecls, <${getterFunc}(); value = serialized ? serialized->deserialize() : v8::Handle(v8::Null()); info.Holder()->SetHiddenValue(propertyName, value); return value; END } else { push(@implContentDecls, " " . ReturnNativeToJSValue($attribute->signature, $result, "info.GetIsolate()").";\n"); } } push(@implContentDecls, "}\n\n"); # end of getter push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GenerateNormalAttrSetter { my $attribute = shift; my $dataNode = shift; my $implClassName = shift; my $interfaceName = shift; AddToImplIncludes("V8BindingMacros.h"); my $attrName = $attribute->signature->name; my $attrExt = $attribute->signature->extendedAttributes; my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentDecls, "static void ${attrName}AttrSetter(v8::Local name, v8::Local value, const v8::AccessorInfo& info)\n{\n"); push(@implContentDecls, " INC_STATS(\"DOM.$implClassName.$attrName._set\");\n"); # If the "StrictTypeChecking" extended attribute is present, and the attribute's type is an # interface type, then if the incoming value does not implement that interface, a TypeError is # thrown rather than silently passing NULL to the C++ code. # Per the Web IDL and ECMAScript specifications, incoming values can always be converted to both # strings and numbers, so do not throw TypeError if the attribute is of these types. if ($attribute->signature->extendedAttributes->{"StrictTypeChecking"}) { my $argType = GetTypeFromSignature($attribute->signature); if (IsWrapperType($argType)) { push(@implContentDecls, " if (!isUndefinedOrNull(value) && !V8${argType}::HasInstance(value)) {\n"); push(@implContentDecls, " V8Proxy::throwTypeError();\n"); push(@implContentDecls, " return;\n"); push(@implContentDecls, " }\n"); } } my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($implClassName); if ($svgNativeType) { my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($implClassName); if ($svgWrappedNativeType =~ /List$/) { push(@implContentDecls, <role() == AnimValRole) {\n"); push(@implContentDecls, " V8Proxy::setDOMException(NO_MODIFICATION_ALLOWED_ERR, info.GetIsolate());\n"); push(@implContentDecls, " return;\n"); push(@implContentDecls, " }\n"); push(@implContentDecls, " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n"); push(@implContentDecls, " $svgWrappedNativeType* imp = &impInstance;\n"); } } elsif ($attrExt->{"V8OnProto"}) { if ($interfaceName eq "DOMWindow") { push(@implContentDecls, < holder = info.Holder(); END } else { # perform lookup first push(@implContentDecls, < holder = V8DOMWrapper::lookupDOMWrapper(V8${interfaceName}::GetTemplate(), info.This()); if (holder.IsEmpty()) return; END } push(@implContentDecls, <signature); my $reflect = $attribute->signature->extendedAttributes->{"Reflect"}; if ($reflect && IsNodeSubType($dataNode) && $codeGenerator->IsStringType($attrType)) { # Generate super-compact call for regular attribute setter: my $contentAttributeName = $reflect eq "VALUE_IS_MISSING" ? lc $attrName : $reflect; my $namespace = $codeGenerator->NamespaceForAttributeName($interfaceName, $contentAttributeName); AddToImplIncludes("${namespace}.h"); push(@implContentDecls, " setElementStringAttr(info, ${namespace}::${contentAttributeName}Attr, value);\n"); push(@implContentDecls, "}\n\n"); push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; return; # Skip the rest of the function! } push(@implContentDecls, <signature, 0); if ($attribute->signature->type eq "EventListener") { if ($dataNode->name eq "DOMWindow") { push(@implContentDecls, " if (!imp->document())\n"); push(@implContentDecls, " return;\n"); } } else { my $value = JSValueToNative($attribute->signature, "value", "info.GetIsolate()"); my $arrayType = $codeGenerator->GetArrayType($nativeType); if ($nativeType =~ /^V8Parameter/) { push(@implContentDecls, " " . ConvertToV8Parameter($attribute->signature, $nativeType, "v", $value, "VOID") . "\n"); } elsif ($arrayType) { push(@implContentDecls, " Vector<$arrayType> v = $value;\n"); } else { push(@implContentDecls, " $nativeType v = $value;\n"); } } my $result = "v"; my $returnType = GetTypeFromSignature($attribute->signature); if (IsRefPtrType($returnType) && !$codeGenerator->GetArrayType($returnType)) { $result = "WTF::getPtr(" . $result . ")"; } my $useExceptions = 1 if @{$attribute->setterExceptions}; if ($useExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContentDecls, " ExceptionCode ec = 0;\n"); } if ($implClassName eq "SVGNumber") { push(@implContentDecls, " *imp = $result;\n"); } else { if ($attribute->signature->type eq "EventListener") { my $implSetterFunctionName = $codeGenerator->WK_ucfirst($attrName); AddToImplIncludes("V8AbstractEventListener.h"); if (!IsNodeSubType($dataNode)) { push(@implContentDecls, " transferHiddenDependency(info.Holder(), imp->$attrName(), value, V8${interfaceName}::eventListenerCacheIndex);\n"); } if ($interfaceName eq "WorkerContext" and $attribute->signature->name eq "onerror") { AddToImplIncludes("V8EventListenerList.h"); AddToImplIncludes("V8WorkerContextErrorHandler.h"); push(@implContentDecls, " imp->set$implSetterFunctionName(V8EventListenerList::findOrCreateWrapper(value, true)"); } elsif ($interfaceName eq "DOMWindow" and $attribute->signature->name eq "onerror") { AddToImplIncludes("V8EventListenerList.h"); AddToImplIncludes("V8WindowErrorHandler.h"); push(@implContentDecls, " imp->set$implSetterFunctionName(V8EventListenerList::findOrCreateWrapper(value, true)"); } else { push(@implContentDecls, " imp->set$implSetterFunctionName(V8DOMWrapper::getEventListener(value, true, ListenerFindOrCreate)"); } push(@implContentDecls, ", ec") if $useExceptions; push(@implContentDecls, ");\n"); } else { my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute); push(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContentDecls, " ", 1, 0)); push(@arguments, $result); push(@arguments, "ec") if $useExceptions; if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; AddToImplIncludes("${implementedBy}.h"); unshift(@arguments, "imp"); $functionName = "${implementedBy}::${functionName}"; } else { $functionName = "imp->${functionName}"; } push(@implContentDecls, " ${functionName}(" . join(", ", @arguments) . ");\n"); } } if ($useExceptions) { push(@implContentDecls, " if (UNLIKELY(ec))\n"); push(@implContentDecls, " V8Proxy::setDOMException(ec, info.GetIsolate());\n"); } if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) { push(@implContentDecls, " if (state.hadException())\n"); push(@implContentDecls, " throwError(state.exception());\n"); } if ($svgNativeType) { if ($useExceptions) { push(@implContentDecls, " if (!ec)\n"); push(@implContentDecls, " wrapper->commitChange();\n"); } else { push(@implContentDecls, " wrapper->commitChange();\n"); } } if ($attribute->signature->type eq "SerializedScriptValue" && $attribute->signature->extendedAttributes->{"CachedAttribute"}) { push(@implContentDecls, <DeleteHiddenValue(v8::String::NewSymbol("${attrName}")); // Invalidate the cached value. END } push(@implContentDecls, " return;\n"); push(@implContentDecls, "}\n\n"); # end of setter push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GetFunctionTemplateCallbackName { my $function = shift; my $interfaceName = shift; 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!" } return "V8${interfaceName}::${name}Callback"; } else { return "${interfaceName}V8Internal::${name}Callback"; } } sub GenerateNewFunctionTemplate { my $function = shift; my $interfaceName = shift; my $signature = shift; my $callback = GetFunctionTemplateCallbackName($function, $interfaceName); return "v8::FunctionTemplate::New($callback, v8::Handle(), $signature)"; } sub GenerateEventListenerCallback { my $implClassName = shift; my $requiresHiddenDependency = shift; my $functionName = shift; my $lookupType = ($functionName eq "add") ? "OrCreate" : "Only"; my $passRefPtrHandling = ($functionName eq "add") ? "" : ".get()"; my $hiddenDependencyAction = ($functionName eq "add") ? "create" : "remove"; push(@implContentDecls, < ${functionName}EventListenerCallback(const v8::Arguments& args) { INC_STATS("DOM.${implClassName}.${functionName}EventListener()"); RefPtr listener = V8DOMWrapper::getEventListener(args[1], false, ListenerFind${lookupType}); if (listener) { V8${implClassName}::toNative(args.Holder())->${functionName}EventListener(v8ValueToAtomicWebCoreString(args[0]), listener${passRefPtrHandling}, args[2]->BooleanValue()); END if ($requiresHiddenDependency) { push(@implContentDecls, <parameters}) { last if $parameterIndex >= $numParameters; my $value = "args[$parameterIndex]"; my $type = GetTypeFromSignature($parameter); # Only DOMString or wrapper types are checked. # For DOMString, Null, Undefined and any Object are accepted too, as # these are acceptable values for a DOMString argument (any Object can # be converted to a string via .toString). if ($codeGenerator->IsStringType($type)) { push(@andExpression, "(${value}->IsNull() || ${value}->IsUndefined() || ${value}->IsString() || ${value}->IsObject())"); } elsif ($parameter->extendedAttributes->{"Callback"}) { # For Callbacks only checks if the value is null or object. push(@andExpression, "(${value}->IsNull() || ${value}->IsFunction())"); } elsif (IsArrayType($type)) { # FIXME: Add proper support for T[], T[]?, sequence. push(@andExpression, "(${value}->IsNull() || ${value}->IsArray())"); } elsif (IsWrapperType($type)) { push(@andExpression, "(${value}->IsNull() || V8${type}::HasInstance($value))"); } $parameterIndex++; } my $res = join(" && ", @andExpression); $res = "($res)" if @andExpression > 1; return $res; } sub GenerateFunctionParametersCheck { my $function = shift; my @orExpression = (); my $numParameters = 0; foreach my $parameter (@{$function->parameters}) { if ($parameter->extendedAttributes->{"Optional"}) { push(@orExpression, GenerateParametersCheckExpression($numParameters, $function)); } $numParameters++; } push(@orExpression, GenerateParametersCheckExpression($numParameters, $function)); return join(" || ", @orExpression); } sub GenerateOverloadedFunctionCallback { my $function = shift; my $dataNode = shift; my $implClassName = shift; # Generate code for choosing the correct overload to call. Overloads are # chosen based on the total number of arguments passed and the type of # values passed in non-primitive argument slots. When more than a single # overload is applicable, precedence is given according to the order of # declaration in the IDL. my $name = $function->signature->name; my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentDecls, < ${name}Callback(const v8::Arguments& args) { INC_STATS(\"DOM.$implClassName.$name\"); END foreach my $overload (@{$function->{overloads}}) { my $parametersCheck = GenerateFunctionParametersCheck($overload); push(@implContentDecls, " if ($parametersCheck)\n"); push(@implContentDecls, " return ${name}$overload->{overloadIndex}Callback(args);\n"); } push(@implContentDecls, <name; my $name = $function->signature->name; if (@{$function->{overloads}} > 1) { # Append a number to an overloaded method's name to make it unique: $name = $name . $function->{overloadIndex}; } # Adding and removing event listeners are not standard callback behavior, # but they are extremely consistent across the various classes that take event listeners, # so we can generate them as a "special case". if ($name eq "addEventListener") { GenerateEventListenerCallback($implClassName, !IsNodeSubType($dataNode), "add"); return; } elsif ($name eq "removeEventListener") { GenerateEventListenerCallback($implClassName, !IsNodeSubType($dataNode), "remove"); return; } my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentDecls, < ${name}Callback(const v8::Arguments& args) { INC_STATS(\"DOM.$implClassName.$name\"); END push(@implContentDecls, GenerateArgumentsCountCheck($function, $dataNode)); my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($implClassName); if ($svgNativeType) { my $nativeClassName = GetNativeType($implClassName); if ($implClassName =~ /List$/) { push(@implContentDecls, " $nativeClassName imp = V8${implClassName}::toNative(args.Holder());\n"); } else { AddToImplIncludes("ExceptionCode.h"); push(@implContentDecls, " $nativeClassName wrapper = V8${implClassName}::toNative(args.Holder());\n"); push(@implContentDecls, " if (wrapper->role() == AnimValRole) {\n"); push(@implContentDecls, " V8Proxy::setDOMException(NO_MODIFICATION_ALLOWED_ERR, args.GetIsolate());\n"); push(@implContentDecls, " return v8::Handle();\n"); push(@implContentDecls, " }\n"); my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($implClassName); push(@implContentDecls, " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n"); push(@implContentDecls, " $svgWrappedNativeType* imp = &impInstance;\n"); } } elsif (!$function->isStatic) { push(@implContentDecls, <extendedAttributes->{"CheckSecurity"} || $interfaceName eq "DOMWindow") && !$function->signature->extendedAttributes->{"DoNotCheckSecurity"}) { # We have not find real use cases yet. push(@implContentDecls, <frame(), true)) return v8::Handle(); END } my $raisesExceptions = @{$function->raisesExceptions}; if (!$raisesExceptions) { foreach my $parameter (@{$function->parameters}) { if ((!$parameter->extendedAttributes->{"Callback"} and TypeCanFailConversion($parameter)) or $parameter->extendedAttributes->{"IsIndex"}) { $raisesExceptions = 1; } } } if ($raisesExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContentDecls, " ExceptionCode ec = 0;\n"); push(@implContentDecls, " {\n"); # The brace here is needed to prevent the ensuing 'goto fail's from jumping past constructors # of objects (like Strings) declared later, causing compile errors. The block scope ends # right before the label 'fail:'. } if ($function->signature->extendedAttributes->{"CheckSecurityForNode"}) { push(@implContentDecls, " if (!V8BindingSecurity::shouldAllowAccessToNode(V8BindingState::Only(), imp->" . $function->signature->name . "(ec)))\n"); push(@implContentDecls, " return v8::Handle(v8::Null());\n"); END } my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $implClassName); push(@implContentDecls, $parameterCheckString); # Build the function call string. push(@implContentDecls, GenerateFunctionCallString($function, $paramIndex, " ", $implClassName, %replacements)); if ($raisesExceptions) { push(@implContentDecls, " }\n"); push(@implContentDecls, " fail:\n"); push(@implContentDecls, " V8Proxy::setDOMException(ec, args.GetIsolate());\n"); push(@implContentDecls, " return v8::Handle();\n"); } push(@implContentDecls, "}\n\n"); push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GenerateCallWith { my $callWith = shift; return () unless $callWith; my $outputArray = shift; my $indent = shift; my $returnVoid = shift; my $emptyContext = shift; my $function = shift; my @callWithArgs; if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState")) { if ($emptyContext) { push(@$outputArray, $indent . "EmptyScriptState state;\n"); push(@callWithArgs, "&state"); } else { push(@$outputArray, $indent . "ScriptState* state = ScriptState::current();\n"); push(@$outputArray, $indent . "if (!state)\n"); push(@$outputArray, $indent . " return" . ($returnVoid ? "" : " v8::Undefined()") . ";\n"); push(@callWithArgs, "state"); } } if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptExecutionContext")) { push(@$outputArray, $indent . "ScriptExecutionContext* scriptContext = getScriptExecutionContext();\n"); push(@$outputArray, $indent . "if (!scriptContext)\n"); push(@$outputArray, $indent . " return" . ($returnVoid ? "" : " v8::Undefined()") . ";\n"); push(@callWithArgs, "scriptContext"); } if ($function and $codeGenerator->ExtendedAttributeContains($callWith, "ScriptArguments")) { push(@$outputArray, $indent . "RefPtr scriptArguments(createScriptArguments(args, " . @{$function->parameters} . "));\n"); push(@callWithArgs, "scriptArguments"); AddToImplIncludes("ScriptArguments.h"); } if ($codeGenerator->ExtendedAttributeContains($callWith, "CallStack")) { push(@$outputArray, $indent . "RefPtr callStack(createScriptCallStackForInspector());\n"); push(@$outputArray, $indent . "if (!callStack)\n"); push(@$outputArray, $indent . " return v8::Undefined();\n"); push(@callWithArgs, "callStack"); AddToImplIncludes("ScriptCallStack.h"); AddToImplIncludes("ScriptCallStackFactory.h"); } return @callWithArgs; } sub GenerateArgumentsCountCheck { my $function = shift; my $dataNode = shift; my $numMandatoryParams = 0; my $optionalSeen = 0; foreach my $param (@{$function->parameters}) { if ($param->extendedAttributes->{"Optional"}) { $optionalSeen = 1; } else { die "An argument must not be declared to be optional unless all subsequent arguments to the operation are also optional." if $optionalSeen; $numMandatoryParams++; } } my $argumentsCountCheckString = ""; if ($numMandatoryParams >= 1) { $argumentsCountCheckString .= " if (args.Length() < $numMandatoryParams)\n"; $argumentsCountCheckString .= " return V8Proxy::throwNotEnoughArgumentsError();\n"; } return $argumentsCountCheckString; } sub GetIndexOf { my $paramName = shift; my @paramList = @_; my $index = 0; foreach my $param (@paramList) { if ($paramName eq $param) { return $index; } $index++; } return -1; } sub GenerateParametersCheck { my $function = shift; my $implClassName = shift; my $parameterCheckString = ""; my $paramIndex = 0; my @paramTransferListNames = (); my %replacements = (); foreach my $parameter (@{$function->parameters}) { TranslateParameter($parameter); my $parameterName = $parameter->name; # Optional arguments with [Optional] should generate an early call with fewer arguments. # Optional arguments with [Optional=...] should not generate the early call. my $optional = $parameter->extendedAttributes->{"Optional"}; if ($optional && $optional ne "DefaultIsUndefined" && $optional ne "DefaultIsNullString" && !$parameter->extendedAttributes->{"Callback"}) { $parameterCheckString .= " if (args.Length() <= $paramIndex) {\n"; my $functionCall = GenerateFunctionCallString($function, $paramIndex, " " x 2, $implClassName, %replacements); $parameterCheckString .= $functionCall; $parameterCheckString .= " }\n"; } my $parameterDefaultPolicy = "DefaultIsUndefined"; if ($optional and $optional eq "DefaultIsNullString") { $parameterDefaultPolicy = "DefaultIsNullString"; } if (GetIndexOf($parameterName, @paramTransferListNames) != -1) { $replacements{$parameterName} = "messagePortArray" . ucfirst($parameterName); $paramIndex++; next; } AddToImplIncludes("ExceptionCode.h"); my $nativeType = GetNativeTypeFromSignature($parameter, $paramIndex); if ($parameter->extendedAttributes->{"Callback"}) { my $className = GetCallbackClassName($parameter->type); AddToImplIncludes("$className.h"); if ($optional) { $parameterCheckString .= " RefPtr<" . $parameter->type . "> $parameterName;\n"; $parameterCheckString .= " if (args.Length() > $paramIndex && !args[$paramIndex]->IsNull() && !args[$paramIndex]->IsUndefined()) {\n"; $parameterCheckString .= " if (!args[$paramIndex]->IsFunction())\n"; $parameterCheckString .= " return throwError(TYPE_MISMATCH_ERR);\n"; $parameterCheckString .= " $parameterName = ${className}::create(args[$paramIndex], getScriptExecutionContext());\n"; $parameterCheckString .= " }\n"; } else { $parameterCheckString .= " if (args.Length() <= $paramIndex || !args[$paramIndex]->IsFunction())\n"; $parameterCheckString .= " return throwError(TYPE_MISMATCH_ERR);\n"; $parameterCheckString .= " RefPtr<" . $parameter->type . "> $parameterName = ${className}::create(args[$paramIndex], getScriptExecutionContext());\n"; } } elsif ($parameter->type eq "SerializedScriptValue") { AddToImplIncludes("SerializedScriptValue.h"); my $useTransferList = 0; my $transferListName = ""; my $TransferListName = ""; if ($parameter->extendedAttributes->{"TransferList"}) { $transferListName = $parameter->extendedAttributes->{"TransferList"}; push(@paramTransferListNames, $transferListName); my @allParameterNames = (); foreach my $parameter (@{$function->parameters}) { push(@allParameterNames, $parameter->name); } my $transferListIndex = GetIndexOf($transferListName, @allParameterNames); if ($transferListIndex == -1) { die "IDL error: TransferList refers to a nonexistent argument"; } AddToImplIncludes("wtf/ArrayBuffer.h"); AddToImplIncludes("MessagePort.h"); $TransferListName = ucfirst($transferListName); $parameterCheckString .= " MessagePortArray messagePortArray$TransferListName;\n"; $parameterCheckString .= " ArrayBufferArray arrayBufferArray$TransferListName;\n"; $parameterCheckString .= " if (args.Length() > $transferListIndex) {\n"; $parameterCheckString .= " if (!extractTransferables(args[$transferListIndex], messagePortArray$TransferListName, arrayBufferArray$TransferListName))\n"; $parameterCheckString .= " return throwError(\"Could not extract transferables\", V8Proxy::TypeError);\n"; $parameterCheckString .= " }\n"; $useTransferList = 1; } $parameterCheckString .= " bool ${parameterName}DidThrow = false;\n"; if (!$useTransferList) { $parameterCheckString .= " $nativeType $parameterName = SerializedScriptValue::create(args[$paramIndex], 0, 0, ${parameterName}DidThrow, args.GetIsolate());\n"; } else { $parameterCheckString .= " $nativeType $parameterName = SerializedScriptValue::create(args[$paramIndex], &messagePortArray$TransferListName, &arrayBufferArray$TransferListName, ${parameterName}DidThrow, args.GetIsolate());\n"; } $parameterCheckString .= " if (${parameterName}DidThrow)\n"; $parameterCheckString .= " return v8::Undefined();\n"; } elsif (TypeCanFailConversion($parameter)) { $parameterCheckString .= " $nativeType $parameterName = " . JSValueToNative($parameter, "args[$paramIndex]", "args.GetIsolate()") . ";\n"; $parameterCheckString .= " if (UNLIKELY(!$parameterName)) {\n"; $parameterCheckString .= " ec = TYPE_MISMATCH_ERR;\n"; $parameterCheckString .= " goto fail;\n"; $parameterCheckString .= " }\n"; } elsif ($nativeType =~ /^V8Parameter/) { my $value = JSValueToNative($parameter, "MAYBE_MISSING_PARAMETER(args, $paramIndex, $parameterDefaultPolicy)", "args.GetIsolate()"); $parameterCheckString .= " " . ConvertToV8Parameter($parameter, $nativeType, $parameterName, $value) . "\n"; } else { AddToImplIncludes("V8BindingMacros.h"); # If the "StrictTypeChecking" extended attribute is present, and the argument's type is an # interface type, then if the incoming value does not implement that interface, a TypeError # is thrown rather than silently passing NULL to the C++ code. # Per the Web IDL and ECMAScript specifications, incoming values can always be converted # to both strings and numbers, so do not throw TypeError if the argument is of these # types. if ($function->signature->extendedAttributes->{"StrictTypeChecking"}) { my $argValue = "args[$paramIndex]"; my $argType = GetTypeFromSignature($parameter); if (IsWrapperType($argType)) { $parameterCheckString .= " if (args.Length() > $paramIndex && !isUndefinedOrNull($argValue) && !V8${argType}::HasInstance($argValue)) {\n"; $parameterCheckString .= " V8Proxy::throwTypeError();\n"; $parameterCheckString .= " return notHandledByInterceptor();\n"; $parameterCheckString .= " }\n"; } } $parameterCheckString .= " EXCEPTION_BLOCK($nativeType, $parameterName, " . JSValueToNative($parameter, "MAYBE_MISSING_PARAMETER(args, $paramIndex, $parameterDefaultPolicy)", "args.GetIsolate()") . ");\n"; if ($nativeType eq 'Dictionary') { $parameterCheckString .= " if (args.Length() > $paramIndex && !$parameterName.isUndefinedOrNull() && !$parameterName.isObject()) {\n"; if (@{$function->raisesExceptions}) { $parameterCheckString .= " ec = TYPE_MISMATCH_ERR;\n"; $parameterCheckString .= " V8Proxy::setDOMException(ec, args.GetIsolate());\n"; } $parameterCheckString .= " return throwError(\"Not an object.\", V8Proxy::TypeError);\n"; $parameterCheckString .= " }\n"; } } if ($parameter->extendedAttributes->{"IsIndex"}) { $parameterCheckString .= " if (UNLIKELY($parameterName < 0)) {\n"; $parameterCheckString .= " ec = INDEX_SIZE_ERR;\n"; $parameterCheckString .= " goto fail;\n"; $parameterCheckString .= " }\n"; } $paramIndex++; } return ($parameterCheckString, $paramIndex, %replacements); } sub GenerateConstructorCallback { my $function = shift; my $dataNode = shift; my $implClassName = shift; my $raisesExceptions = @{$function->raisesExceptions}; if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) { $raisesExceptions = 1; } if (!$raisesExceptions) { foreach my $parameter (@{$function->parameters}) { if ((!$parameter->extendedAttributes->{"Callback"} and TypeCanFailConversion($parameter)) or $parameter->extendedAttributes->{"IsIndex"}) { $raisesExceptions = 1; } } } my @beforeArgumentList; my @afterArgumentList; push(@implContent, < V8${implClassName}::constructorCallback(const v8::Arguments& args) { INC_STATS("DOM.${implClassName}.Constructor"); if (!args.IsConstructCall()) return throwError("DOM object constructor cannot be called as a function.", V8Proxy::TypeError); if (ConstructorMode::current() == ConstructorMode::WrapExistingObject) return args.Holder(); END push(@implContent, GenerateArgumentsCountCheck($function, $dataNode)); if ($raisesExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContent, "\n"); push(@implContent, " ExceptionCode ec = 0;\n"); } # FIXME: Currently [Constructor(...)] does not yet support [Optional] arguments. # It just supports [Optional=DefaultIsUndefined] or [Optional=DefaultIsNullString]. my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $implClassName); push(@implContent, $parameterCheckString); if ($dataNode->extendedAttributes->{"CallWith"} && $dataNode->extendedAttributes->{"CallWith"} eq "ScriptExecutionContext") { push(@beforeArgumentList, "context"); push(@implContent, <extendedAttributes->{"ConstructorRaisesException"}) { push(@afterArgumentList, "ec"); } my @argumentList; my $index = 0; foreach my $parameter (@{$function->parameters}) { last if $index eq $paramIndex; if ($replacements{$parameter->name}) { push(@argumentList, $replacements{$parameter->name}); } else { push(@argumentList, $parameter->name); } $index++; } my $argumentString = join(", ", @beforeArgumentList, @argumentList, @afterArgumentList); push(@implContent, "\n"); push(@implContent, " RefPtr<${implClassName}> impl = ${implClassName}::create(${argumentString});\n"); push(@implContent, " v8::Handle wrapper = args.Holder();\n"); if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) { push(@implContent, " if (ec)\n"); push(@implContent, " goto fail;\n"); } my $DOMObject = GetDomMapName($dataNode, $implClassName); push(@implContent, <::New(wrapper), args.GetIsolate()); return args.Holder(); END if ($raisesExceptions) { push(@implContent, " fail:\n"); push(@implContent, " return throwError(ec);\n"); } push(@implContent, "}\n"); push(@implContent, "\n"); } sub GenerateEventConstructorCallback { my $dataNode = shift; my $implClassName = shift; AddToImplIncludes("Dictionary.h"); AddToImplIncludes("V8BindingMacros.h"); push(@implContent, < V8${implClassName}::constructorCallback(const v8::Arguments& args) { INC_STATS("DOM.${implClassName}.Constructor"); if (!args.IsConstructCall()) return throwError("DOM object constructor cannot be called as a function.", V8Proxy::TypeError); if (ConstructorMode::current() == ConstructorMode::WrapExistingObject) return args.Holder(); if (args.Length() < 1) return V8Proxy::throwNotEnoughArgumentsError(); STRING_TO_V8PARAMETER_EXCEPTION_BLOCK(V8Parameter<>, type, args[0]); ${implClassName}Init eventInit; if (args.Length() >= 2) { EXCEPTION_BLOCK(Dictionary, options, args[1]); if (!fill${implClassName}Init(eventInit, options)) return v8::Undefined(); } RefPtr<${implClassName}> event = ${implClassName}::create(type, eventInit); V8DOMWrapper::setDOMWrapper(args.Holder(), &info, event.get()); V8DOMWrapper::setJSWrapperForDOMObject(event.release(), v8::Persistent::New(args.Holder()), args.GetIsolate()); return args.Holder(); } bool fill${implClassName}Init(${implClassName}Init& eventInit, const Dictionary& options) { END foreach my $interfaceBase (@{$dataNode->parents}) { push(@implContent, <attributes}; $index++) { my $attribute = @{$dataNode->attributes}[$index]; if ($attribute->signature->extendedAttributes->{"InitializedByEventConstructor"}) { my $attributeName = $attribute->signature->name; push(@implContent, " options.get(\"$attributeName\", eventInit.$attributeName);\n"); } } push(@implContent, <raisesExceptions}; if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) { $raisesExceptions = 1; } if (!$raisesExceptions) { foreach my $parameter (@{$function->parameters}) { if ((!$parameter->extendedAttributes->{"Callback"} and TypeCanFailConversion($parameter)) or $parameter->extendedAttributes->{"IsIndex"}) { $raisesExceptions = 1; } } } my @beforeArgumentList; my @afterArgumentList; if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) { push(@implContent, < V8${implClassName}ConstructorCallback(const v8::Arguments& args) { INC_STATS("DOM.${implClassName}.Constructor"); if (!args.IsConstructCall()) return throwError("DOM object constructor cannot be called as a function.", V8Proxy::TypeError); if (ConstructorMode::current() == ConstructorMode::WrapExistingObject) return args.Holder(); Frame* frame = V8Proxy::retrieveFrameForCurrentContext(); if (!frame) return throwError("${implClassName} constructor associated frame is unavailable", V8Proxy::ReferenceError); Document* document = frame->document(); // Make sure the document is added to the DOM Node map. Otherwise, the ${implClassName} instance // may end up being the only node in the map and get garbage-collected prematurely. toV8(document, args.GetIsolate()); END push(@implContent, GenerateArgumentsCountCheck($function, $dataNode)); if ($raisesExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContent, "\n"); push(@implContent, " ExceptionCode ec = 0;\n"); } my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $implClassName); push(@implContent, $parameterCheckString); push(@beforeArgumentList, "document"); if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) { push(@afterArgumentList, "ec"); } my @argumentList; my $index = 0; foreach my $parameter (@{$function->parameters}) { last if $index eq $paramIndex; if ($replacements{$parameter->name}) { push(@argumentList, $replacements{$parameter->name}); } else { push(@argumentList, $parameter->name); } $index++; } my $argumentString = join(", ", @beforeArgumentList, @argumentList, @afterArgumentList); push(@implContent, "\n"); push(@implContent, " RefPtr<${implClassName}> impl = ${implClassName}::createForJSConstructor(${argumentString});\n"); push(@implContent, " v8::Handle wrapper = args.Holder();\n"); if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) { push(@implContent, " if (ec)\n"); push(@implContent, " goto fail;\n"); } my $DOMObject = GetDomMapName($dataNode, $implClassName); push(@implContent, <::New(wrapper), args.GetIsolate()); return args.Holder(); END if ($raisesExceptions) { push(@implContent, " fail:\n"); push(@implContent, " return throwError(ec);\n"); } push(@implContent, "}\n"); push(@implContent, < V8${implClassName}Constructor::GetTemplate() { static v8::Persistent cachedTemplate; if (!cachedTemplate.IsEmpty()) return cachedTemplate; v8::HandleScope scope; v8::Local result = v8::FunctionTemplate::New(V8${implClassName}ConstructorCallback); v8::Local instance = result->InstanceTemplate(); instance->SetInternalFieldCount(V8${implClassName}::internalFieldCount); result->SetClassName(v8::String::New("${implClassName}")); result->Inherit(V8${implClassName}::GetTemplate()); cachedTemplate = v8::Persistent::New(result); return cachedTemplate; } END } sub GenerateBatchedAttributeData { my $dataNode = shift; my $interfaceName = $dataNode->name; my $attributes = shift; foreach my $attribute (@$attributes) { my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); push(@implContent, "#if ${conditionalString}\n") if $conditionalString; GenerateSingleBatchedAttribute($interfaceName, $attribute, ",", ""); push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; } } sub GenerateSingleBatchedAttribute { my $interfaceName = shift; my $attribute = shift; my $delimiter = shift; my $indent = shift; my $attrName = $attribute->signature->name; my $attrExt = $attribute->signature->extendedAttributes; my $accessControl = "v8::DEFAULT"; if ($attrExt->{"DoNotCheckSecurityOnGetter"}) { $accessControl = "v8::ALL_CAN_READ"; } elsif ($attrExt->{"DoNotCheckSecurityOnSetter"}) { $accessControl = "v8::ALL_CAN_WRITE"; } elsif ($attrExt->{"DoNotCheckSecurity"}) { $accessControl = "v8::ALL_CAN_READ"; if (!($attribute->type =~ /^readonly/) && !($attrExt->{"V8ReadOnly"})) { $accessControl .= " | v8::ALL_CAN_WRITE"; } } if ($attrExt->{"V8Unforgeable"}) { $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 "VALUE_IS_MISSING") { # use the naming convension, interface + (capitalize) attr name $customAccessor = $interfaceName . "::" . $attrName; } my $getter; my $setter; my $propAttr = "v8::None"; my $hasCustomSetter = 0; # Check attributes. if ($attrExt->{"NotEnumerable"}) { $propAttr .= " | v8::DontEnum"; } if ($attrExt->{"V8Unforgeable"}) { $propAttr .= " | v8::DontDelete"; } my $on_proto = "0 /* on instance */"; my $data = "0 /* no data */"; # Constructor if ($attribute->signature->type =~ /Constructor$/) { my $constructorType = $codeGenerator->StripModule($attribute->signature->type); $constructorType =~ s/Constructor$//; # $constructorType ~= /Constructor$/ indicates that it is NamedConstructor. # We do not generate the header file for NamedConstructor of class XXXX, # since we generate the NamedConstructor declaration into the header file of class XXXX. if ($constructorType !~ /Constructor$/ || $attribute->signature->extendedAttributes->{"V8CustomConstructor"} || $attribute->signature->extendedAttributes->{"CustomConstructor"}) { AddToImplIncludes("V8${constructorType}.h", $attribute->signature->extendedAttributes->{"Conditional"}); } if ($customAccessor) { $getter = "V8${customAccessor}AccessorGetter"; } else { $data = "&V8${constructorType}::info"; $getter = "${interfaceName}V8Internal::${interfaceName}ConstructorGetter"; } $setter = "0"; $propAttr = "v8::ReadOnly"; } else { # Default Getter and Setter $getter = "${interfaceName}V8Internal::${attrName}AttrGetter"; $setter = "${interfaceName}V8Internal::${attrName}AttrSetter"; # Custom Setter if ($attrExt->{"CustomSetter"} || $attrExt->{"V8CustomSetter"} || $attrExt->{"Custom"} || $attrExt->{"V8Custom"}) { $hasCustomSetter = 1; $setter = "V8${customAccessor}AccessorSetter"; } # Custom Getter if ($attrExt->{"CustomGetter"} || $attrExt->{"V8CustomGetter"} || $attrExt->{"Custom"} || $attrExt->{"V8Custom"}) { $getter = "V8${customAccessor}AccessorGetter"; } } # Replaceable if ($attrExt->{"Replaceable"} && !$hasCustomSetter) { $setter = "0"; # Handle the special case of window.top being marked as Replaceable. # FIXME: Investigate whether we could treat window.top as replaceable # and allow shadowing without it being a security hole. if (!($interfaceName eq "DOMWindow" and $attrName eq "top")) { $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, $indent . " \/\/ $commentInfo\n"); push(@implContent, $indent . " {\"$attrName\", $getter, $setter, $data, $accessControl, static_cast($propAttr), $on_proto}" . $delimiter . "\n"); } sub GenerateImplementationIndexer { my $dataNode = shift; my $indexer = shift; my $interfaceName = $dataNode->name; # FIXME: Figure out what NumericIndexedGetter is really supposed to do. Right now, it's only set on WebGL-related files. my $hasCustomSetter = $dataNode->extendedAttributes->{"CustomIndexedSetter"} && !$dataNode->extendedAttributes->{"NumericIndexedGetter"}; my $hasGetter = $dataNode->extendedAttributes->{"IndexedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"}; # FIXME: Find a way to not have to special-case HTMLOptionsCollection. if ($interfaceName eq "HTMLOptionsCollection") { $hasGetter = 1; } # FIXME: Investigate and remove this nastinesss. In V8, named property handling and indexer handling are apparently decoupled, # which means that object[X] where X is a number doesn't reach named property indexer. So we need to provide # simplistic, mirrored indexer handling in addition to named property handling. my $isSpecialCase = exists $indexerSpecialCases{$interfaceName}; if ($isSpecialCase) { $hasGetter = 1; if ($dataNode->extendedAttributes->{"CustomNamedSetter"}) { $hasCustomSetter = 1; } } if (!$hasGetter) { return; } AddToImplIncludes("V8Collection.h"); if (!$indexer) { $indexer = $codeGenerator->FindSuperMethod($dataNode, "item"); } my $indexerType = $indexer ? $indexer->type : 0; # FIXME: Remove this once toV8 helper methods are implemented (see https://bugs.webkit.org/show_bug.cgi?id=32563). if ($interfaceName eq "WebKitCSSKeyframesRule") { $indexerType = "WebKitCSSKeyframeRule"; } if ($indexerType && !$hasCustomSetter) { if ($indexerType eq "DOMString") { my $conversion = $indexer->extendedAttributes->{"TreatReturnedNullStringAs"}; if ($conversion && $conversion eq "Null") { push(@implContent, <(desc); END } else { push(@implContent, <(desc); END } } else { push(@implContent, <(desc); END # Include the header for this indexer type, because setCollectionIndexedGetter() requires toV8() for this type. AddToImplIncludes("V8${indexerType}.h"); } return; } my $hasDeleter = $dataNode->extendedAttributes->{"CustomDeleteProperty"}; my $hasEnumerator = !$isSpecialCase && IsNodeSubType($dataNode); my $setOn = "Instance"; # V8 has access-check callback API (see ObjectTemplate::SetAccessCheckCallbacks) and it's used on DOMWindow # instead of deleters or enumerators. In addition, the getter should be set on prototype template, to # get implementation straight out of the DOMWindow prototype regardless of what prototype is actually set # on the object. if ($interfaceName eq "DOMWindow") { $setOn = "Prototype"; $hasDeleter = 0; } push(@implContent, " desc->${setOn}Template()->SetIndexedPropertyHandler(V8${interfaceName}::indexedPropertyGetter"); push(@implContent, $hasCustomSetter ? ", V8${interfaceName}::indexedPropertySetter" : ", 0"); push(@implContent, ", 0"); # IndexedPropertyQuery -- not being used at the moment. push(@implContent, $hasDeleter ? ", V8${interfaceName}::indexedPropertyDeleter" : ", 0"); push(@implContent, ", nodeCollectionIndexedPropertyEnumerator<${interfaceName}>") if $hasEnumerator; push(@implContent, ");\n"); } sub GenerateImplementationNamedPropertyGetter { my $dataNode = shift; my $namedPropertyGetter = shift; my $interfaceName = $dataNode->name; my $hasCustomNamedGetter = $dataNode->extendedAttributes->{"CustomNamedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"}; # FIXME: Remove hard-coded HTMLOptionsCollection reference by changing HTMLOptionsCollection to not inherit # from HTMLCollection per W3C spec (http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/html.html#HTMLOptionsCollection). if ($interfaceName eq "HTMLOptionsCollection") { $interfaceName = "HTMLCollection"; $hasCustomNamedGetter = 1; } if ($interfaceName eq "HTMLAppletElement" || $interfaceName eq "HTMLEmbedElement" || $interfaceName eq "HTMLObjectElement") { $hasCustomNamedGetter = 1; } if ($interfaceName eq "HTMLDocument") { $hasCustomNamedGetter = 0; } my $hasGetter = $dataNode->extendedAttributes->{"NamedGetter"} || $hasCustomNamedGetter; if (!$hasGetter) { return; } if (!$namedPropertyGetter) { $namedPropertyGetter = $codeGenerator->FindSuperMethod($dataNode, "namedItem"); } if ($namedPropertyGetter && $namedPropertyGetter->type ne "Node" && !$namedPropertyGetter->extendedAttributes->{"Custom"} && !$hasCustomNamedGetter) { AddToImplIncludes("V8Collection.h"); my $type = $namedPropertyGetter->type; push(@implContent, <(desc); END return; } my $hasCustomNamedSetter = $dataNode->extendedAttributes->{"CustomNamedSetter"}; my $hasDeleter = $dataNode->extendedAttributes->{"CustomDeleteProperty"}; my $hasEnumerator = $dataNode->extendedAttributes->{"CustomEnumerateProperty"}; my $setOn = "Instance"; # V8 has access-check callback API (see ObjectTemplate::SetAccessCheckCallbacks) and it's used on DOMWindow # instead of deleters or enumerators. In addition, the getter should be set on prototype template, to # get implementation straight out of the DOMWindow prototype regardless of what prototype is actually set # on the object. if ($interfaceName eq "DOMWindow") { $setOn = "Prototype"; $hasDeleter = 0; $hasEnumerator = 0; } push(@implContent, " desc->${setOn}Template()->SetNamedPropertyHandler(V8${interfaceName}::namedPropertyGetter, "); push(@implContent, $hasCustomNamedSetter ? "V8${interfaceName}::namedPropertySetter, " : "0, "); # If there is a custom enumerator, there MUST be custom query to properly communicate property attributes. push(@implContent, $hasEnumerator ? "V8${interfaceName}::namedPropertyQuery, " : "0, "); push(@implContent, $hasDeleter ? "V8${interfaceName}::namedPropertyDeleter, " : "0, "); push(@implContent, $hasEnumerator ? "V8${interfaceName}::namedPropertyEnumerator" : "0"); push(@implContent, ");\n"); } sub GenerateImplementationCustomCall { my $dataNode = shift; my $interfaceName = $dataNode->name; my $hasCustomCall = $dataNode->extendedAttributes->{"CustomCall"}; if ($hasCustomCall) { push(@implContent, " desc->InstanceTemplate()->SetCallAsFunctionHandler(V8${interfaceName}::callAsFunctionCallback);\n"); } } sub GenerateImplementationMasqueradesAsUndefined { my $dataNode = shift; if ($dataNode->extendedAttributes->{"MasqueradesAsUndefined"}) { push(@implContent, " desc->InstanceTemplate()->MarkAsUndetectable();\n"); } } sub IsTypedArrayType { my $type = shift; return 1 if (($type eq "ArrayBuffer") or ($type eq "ArrayBufferView")); return 1 if (($type eq "Uint8Array") or ($type eq "Uint8ClampedArray") or ($type eq "Uint16Array") or ($type eq "Uint32Array")); return 1 if (($type eq "Int8Array") or ($type eq "Int16Array") or ($type eq "Int32Array")); return 1 if (($type eq "Float32Array") or ($type eq "Float64Array")); return 0; } sub GenerateImplementation { my $object = shift; my $dataNode = shift; my $interfaceName = $dataNode->name; my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($dataNode); my $className = "V8$interfaceName"; my $implClassName = $interfaceName; # - Add default header template push(@implFixedHeader, GenerateImplementationContentHeader($dataNode)); AddToImplIncludes("RuntimeEnabledFeatures.h"); AddToImplIncludes("V8Proxy.h"); AddToImplIncludes("V8Binding.h"); AddToImplIncludes("V8BindingState.h"); AddToImplIncludes("V8DOMWrapper.h"); AddToImplIncludes("V8IsolatedContext.h"); AddIncludesForType($interfaceName); my $toActive = $dataNode->extendedAttributes->{"ActiveDOMObject"} ? "${className}::toActiveDOMObject" : "0"; # Find the super descriptor. my $parentClass = ""; my $parentClassTemplate = ""; foreach (@{$dataNode->parents}) { my $parent = $codeGenerator->StripModule($_); if ($parent eq "EventTarget") { next; } AddToImplIncludes("V8${parent}.h"); $parentClass = "V8" . $parent; $parentClassTemplate = $parentClass . "::GetTemplate()"; last; } push(@implContentDecls, "namespace WebCore {\n\n"); my $parentClassInfo = $parentClass ? "&${parentClass}::info" : "0"; push(@implContentDecls, "WrapperTypeInfo ${className}::info = { ${className}::GetTemplate, ${className}::derefObject, ${toActive}, ${parentClassInfo} };\n\n"); push(@implContentDecls, "namespace ${interfaceName}V8Internal {\n\n"); push(@implContentDecls, "template void V8_USE(T) { }\n\n"); my $hasConstructors = 0; # Generate property accessors for attributes. for (my $index = 0; $index < @{$dataNode->attributes}; $index++) { my $attribute = @{$dataNode->attributes}[$index]; my $attrType = $attribute->signature->type; # Generate special code for the constructor attributes. if ($attrType =~ /Constructor$/) { if (!($attribute->signature->extendedAttributes->{"CustomGetter"} || $attribute->signature->extendedAttributes->{"V8CustomGetter"})) { $hasConstructors = 1; } next; } if ($attrType eq "EventListener" && $interfaceName eq "DOMWindow") { $attribute->signature->extendedAttributes->{"V8OnProto"} = 1; } if ($attrType eq "SerializedScriptValue") { AddToImplIncludes("SerializedScriptValue.h"); } # 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"}) { next; } # Generate the accessor. if (!($attribute->signature->extendedAttributes->{"CustomGetter"} || $attribute->signature->extendedAttributes->{"V8CustomGetter"})) { GenerateNormalAttrGetter($attribute, $dataNode, $implClassName, $interfaceName); } if (!$attribute->signature->extendedAttributes->{"CustomSetter"} && !$attribute->signature->extendedAttributes->{"V8CustomSetter"} && !$attribute->signature->extendedAttributes->{"Replaceable"} && $attribute->type !~ /^readonly/ && !$attribute->signature->extendedAttributes->{"V8ReadOnly"}) { GenerateNormalAttrSetter($attribute, $dataNode, $implClassName, $interfaceName); } } if ($hasConstructors) { GenerateConstructorGetter($dataNode, $implClassName); } my $indexer; my $namedPropertyGetter; # Generate methods for functions. foreach my $function (@{$dataNode->functions}) { my $isCustom = $function->signature->extendedAttributes->{"Custom"} || $function->signature->extendedAttributes->{"V8Custom"}; if (!$isCustom) { GenerateFunctionCallback($function, $dataNode, $implClassName); if ($function->{overloadIndex} > 1 && $function->{overloadIndex} == @{$function->{overloads}}) { GenerateOverloadedFunctionCallback($function, $dataNode, $implClassName); } } if ($function->signature->name eq "item") { $indexer = $function->signature; } if ($function->signature->name eq "namedItem") { $namedPropertyGetter = $function->signature; } # 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->{"CheckSecurity"} || ($interfaceName eq "DOMWindow")) && $function->signature->extendedAttributes->{"DoNotCheckSecurity"}) { if (!$isCustom || $function->{overloadIndex} == 1) { GenerateDomainSafeFunctionGetter($function, $implClassName); } } } # Attributes my $attributes = $dataNode->attributes; # For the DOMWindow interface we partition the attributes into the # ones that disallows shadowing and the rest. my @disallowsShadowing; # Also separate out attributes that are enabled at runtime so we can process them specially. my @enabledAtRuntime; my @normal; foreach my $attribute (@$attributes) { if ($interfaceName eq "DOMWindow" && $attribute->signature->extendedAttributes->{"V8Unforgeable"}) { push(@disallowsShadowing, $attribute); } elsif ($attribute->signature->extendedAttributes->{"V8EnabledAtRuntime"}) { push(@enabledAtRuntime, $attribute); } else { push(@normal, $attribute); } } $attributes = \@normal; # Put the attributes that disallow shadowing on the shadow object. if (@disallowsShadowing) { push(@implContent, "static const BatchedAttribute shadowAttrs[] = {\n"); GenerateBatchedAttributeData($dataNode, \@disallowsShadowing); push(@implContent, "};\n\n"); } my $has_attributes = 0; if (@$attributes) { $has_attributes = 1; push(@implContent, "static const BatchedAttribute ${interfaceName}Attrs[] = {\n"); GenerateBatchedAttributeData($dataNode, $attributes); push(@implContent, "};\n\n"); } # Setup table of standard callback functions my $num_callbacks = 0; my $has_callbacks = 0; foreach my $function (@{$dataNode->functions}) { # Only one table entry is needed for overloaded methods: next if $function->{overloadIndex} > 1; my $attrExt = $function->signature->extendedAttributes; # Don't put any nonstandard functions into this table: if ($attrExt->{"V8Unforgeable"}) { next; } if ($function->isStatic) { next; } if ($attrExt->{"V8EnabledAtRuntime"} || RequiresCustomSignature($function) || $attrExt->{"V8DoNotCheckSignature"}) { next; } if ($attrExt->{"DoNotCheckSecurity"} && ($dataNode->extendedAttributes->{"CheckSecurity"} || $interfaceName eq "DOMWindow")) { next; } if ($attrExt->{"NotEnumerable"} || $attrExt->{"V8ReadOnly"}) { next; } if (!$has_callbacks) { $has_callbacks = 1; push(@implContent, "static const BatchedCallback ${interfaceName}Callbacks[] = {\n"); } my $name = $function->signature->name; my $callback = GetFunctionTemplateCallbackName($function, $interfaceName); my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@implContent, "#if ${conditionalString}\n") if $conditionalString; push(@implContent, <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; my $attrExt = $constant->extendedAttributes; my $conditional = $attrExt->{"Conditional"}; my $implementedBy = $attrExt->{"ImplementedBy"}; if ($implementedBy) { AddToImplIncludes("${implementedBy}.h"); } if ($attrExt->{"V8EnabledAtRuntime"}) { push(@constantsEnabledAtRuntime, $constant); } else { # 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. if ($conditional) { my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional); push(@implContent, "#if ${conditionalString}\n"); } push(@implContent, <($value)}, END push(@implContent, "#endif\n") if $conditional; } } if ($has_constants) { push(@implContent, "};\n\n"); push(@implContent, $codeGenerator->GenerateCompileTimeCheckForEnumsIfNeeded($dataNode)); } push(@implContentDecls, "} // namespace ${interfaceName}V8Internal\n\n"); if ($dataNode->extendedAttributes->{"NamedConstructor"} && !($dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"CustomConstructor"})) { GenerateNamedConstructorCallback($dataNode->constructor, $dataNode, $interfaceName); } elsif ($dataNode->extendedAttributes->{"Constructor"} && !($dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"CustomConstructor"})) { GenerateConstructorCallback($dataNode->constructor, $dataNode, $interfaceName); } elsif (IsConstructorTemplate($dataNode, "Event")) { GenerateEventConstructorCallback($dataNode, $interfaceName); } my $access_check = ""; if ($dataNode->extendedAttributes->{"CheckSecurity"} && !($interfaceName eq "DOMWindow")) { $access_check = "instance->SetAccessCheckCallbacks(V8${interfaceName}::namedSecurityCheck, V8${interfaceName}::indexedSecurityCheck, v8::External::Wrap(&V8${interfaceName}::info));"; } # 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(), shadowAttrs, WTF_ARRAY_LENGTH(shadowAttrs)); // Install a security handler with V8. templ->SetAccessCheckCallbacks(V8DOMWindow::namedSecurityCheck, V8DOMWindow::indexedSecurityCheck, v8::External::Wrap(&V8DOMWindow::info)); templ->SetInternalFieldCount(V8DOMWindow::internalFieldCount); return templ; } END } if (!$parentClassTemplate) { $parentClassTemplate = "v8::Persistent()"; } # Generate the template configuration method push(@implContent, < Configure${className}Template(v8::Persistent desc) { desc->ReadOnlyPrototype(); v8::Local defaultSignature; END if ($dataNode->extendedAttributes->{"V8EnabledAtRuntime"}) { my $enable_function = GetRuntimeEnableFunctionName($dataNode); push(@implContent, <SetCallHandler(V8${interfaceName}::constructorCallback); END } if ($access_check or @enabledAtRuntime or @{$dataNode->functions} or $has_constants) { push(@implContent, < instance = desc->InstanceTemplate(); v8::Local proto = desc->PrototypeTemplate(); UNUSED_PARAM(instance); // In some cases, it will not be used. UNUSED_PARAM(proto); // In some cases, it will not be used. END } push(@implContent, " $access_check\n"); # Setup the enable-at-runtime attrs if we have them foreach my $runtime_attr (@enabledAtRuntime) { my $enable_function = GetRuntimeEnableFunctionName($runtime_attr->signature); my $conditionalString = $codeGenerator->GenerateConditionalString($runtime_attr->signature); push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString; push(@implContent, " if (${enable_function}()) {\n"); push(@implContent, " static const BatchedAttribute attrData =\\\n"); GenerateSingleBatchedAttribute($interfaceName, $runtime_attr, ";", " "); push(@implContent, <GenerateConditionalString($runtime_const); my $name = $runtime_const->name; my $value = $runtime_const->value; push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString; push(@implContent, " if (${enable_function}()) {\n"); push(@implContent, <(${value})}; batchConfigureConstants(desc, proto, &constData, 1); END push(@implContent, " }\n"); push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; } GenerateImplementationIndexer($dataNode, $indexer); GenerateImplementationNamedPropertyGetter($dataNode, $namedPropertyGetter); GenerateImplementationCustomCall($dataNode); GenerateImplementationMasqueradesAsUndefined($dataNode); # Define our functions with Set() or SetAccessor() my $total_functions = 0; foreach my $function (@{$dataNode->functions}) { # Only one accessor is needed for overloaded methods: next if $function->{overloadIndex} > 1; $total_functions++; my $attrExt = $function->signature->extendedAttributes; my $name = $function->signature->name; my $property_attributes = "v8::DontDelete"; if ($attrExt->{"NotEnumerable"}) { $property_attributes .= " | v8::DontEnum"; } if ($attrExt->{"V8ReadOnly"}) { $property_attributes .= " | v8::ReadOnly"; } my $commentInfo = "Function '$name' (ExtAttr: '" . join(' ', keys(%{$attrExt})) . "')"; my $template = "proto"; if ($attrExt->{"V8Unforgeable"}) { $template = "instance"; } if ($function->isStatic) { $template = "desc"; } my $conditional = ""; if ($attrExt->{"V8EnabledAtRuntime"}) { # Only call Set()/SetAccessor() if this method should be enabled my $enable_function = GetRuntimeEnableFunctionName($function->signature); $conditional = "if (${enable_function}())\n "; } if ($attrExt->{"DoNotCheckSecurity"} && ($dataNode->extendedAttributes->{"CheckSecurity"} || $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}V8Internal::${name}AttrGetter, 0, v8::Handle(), v8::ALL_CAN_READ, static_cast($property_attributes)); END $num_callbacks++; next; } my $signature = "defaultSignature"; if ($attrExt->{"V8DoNotCheckSignature"} || $function->isStatic) { $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 $callback = GetFunctionTemplateCallbackName($function, $interfaceName); if ($property_attributes eq "v8::DontDelete") { $property_attributes = ""; } else { $property_attributes = ", static_cast($property_attributes)"; } if ($template eq "proto" && $conditional eq "" && $signature eq "defaultSignature" && $property_attributes eq "") { # Standard type of callback, already created in the batch, so skip it here. next; } my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@implContent, "#if ${conditionalString}\n") if $conditionalString; push(@implContent, <Set(v8::String::New("$name"), v8::FunctionTemplate::New($callback, v8::Handle(), ${signature})$property_attributes); END push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; $num_callbacks++; } die "Wrong number of callbacks generated for $interfaceName ($num_callbacks, should be $total_functions)" if $num_callbacks != $total_functions; if ($has_constants) { push(@implContent, <SetInternalFieldCount(V8DOMWindow::internalFieldCount); desc->SetHiddenPrototype(true); instance->SetInternalFieldCount(V8DOMWindow::internalFieldCount); // Set access check callbacks, but turned off initially. // When a context is detached from a frame, turn on the access check. // Turning on checks also invalidates inline caches of the object. instance->SetAccessCheckCallbacks(V8DOMWindow::namedSecurityCheck, V8DOMWindow::indexedSecurityCheck, v8::External::Wrap(&V8DOMWindow::info), false); END } if ($interfaceName eq "HTMLDocument") { push(@implContent, <SetHiddenPrototype(true); END } if ($interfaceName eq "Location") { push(@implContent, <SetAccessor(v8::String::New("reload"), V8Location::reloadAccessorGetter, 0, v8::Handle(), v8::ALL_CAN_READ, static_cast(v8::DontDelete | v8::ReadOnly)); instance->SetAccessor(v8::String::New("replace"), V8Location::replaceAccessorGetter, 0, v8::Handle(), v8::ALL_CAN_READ, static_cast(v8::DontDelete | v8::ReadOnly)); instance->SetAccessor(v8::String::New("assign"), V8Location::assignAccessorGetter, 0, v8::Handle(), v8::ALL_CAN_READ, static_cast(v8::DontDelete | v8::ReadOnly)); END } my $nativeType = GetNativeTypeForConversions($dataNode, $interfaceName); push(@implContent, <Set(getToStringName(), getToStringTemplate()); return desc; } v8::Persistent ${className}::GetRawTemplate() { V8BindingPerIsolateData* data = V8BindingPerIsolateData::current(); V8BindingPerIsolateData::TemplateMap::iterator result = data->rawTemplateMap().find(&info); if (result != data->rawTemplateMap().end()) return result->second; v8::HandleScope handleScope; v8::Persistent templ = createRawTemplate(); data->rawTemplateMap().add(&info, templ); return templ; } v8::Persistent ${className}::GetTemplate() { V8BindingPerIsolateData* data = V8BindingPerIsolateData::current(); V8BindingPerIsolateData::TemplateMap::iterator result = data->templateMap().find(&info); if (result != data->templateMap().end()) return result->second; v8::HandleScope handleScope; v8::Persistent templ = Configure${className}Template(GetRawTemplate()); data->templateMap().add(&info, templ); return templ; } bool ${className}::HasInstance(v8::Handle value) { return GetRawTemplate()->HasInstance(value); } END if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) { # MessagePort is handled like an active dom object even though it doesn't inherit # from ActiveDOMObject, so don't try to cast it to ActiveDOMObject. my $returnValue = $interfaceName eq "MessagePort" ? "0" : "toNative(object)"; push(@implContent, < object) { return ${returnValue}; } END } if ($implClassName eq "DOMWindow") { push(@implContent, < V8DOMWindow::GetShadowObjectTemplate() { static v8::Persistent V8DOMWindowShadowObjectCache; if (V8DOMWindowShadowObjectCache.IsEmpty()) { V8DOMWindowShadowObjectCache = v8::Persistent::New(v8::ObjectTemplate::New()); ConfigureShadowObjectTemplate(V8DOMWindowShadowObjectCache); } return V8DOMWindowShadowObjectCache; } END } GenerateToV8Converters($dataNode, $interfaceName, $className, $nativeType); push(@implContent, <(object)->deref(); } } // namespace WebCore END my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode); push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; # We've already added the header for this file in implFixedHeader, so remove # it from implIncludes to ensure we don't #include it twice. delete $implIncludes{"${className}.h"}; } sub GenerateHeaderContentHeader { my $dataNode = shift; my $className = "V8" . $dataNode->name; my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode); my @headerContentHeader = split("\r", $headerTemplate); push(@headerContentHeader, "\n#if ${conditionalString}\n") if $conditionalString; push(@headerContentHeader, "\n#ifndef ${className}" . "_h"); push(@headerContentHeader, "\n#define ${className}" . "_h\n\n"); return @headerContentHeader; } sub GenerateImplementationContentHeader { my $dataNode = shift; my $className = "V8" . $dataNode->name; my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode); my @implContentHeader = split("\r", $headerTemplate); push(@implContentHeader, "\n#include \"config.h\"\n"); push(@implContentHeader, "#include \"${className}.h\"\n\n"); push(@implContentHeader, "#if ${conditionalString}\n\n") if $conditionalString; return @implContentHeader; } sub GenerateCallbackHeader { my $object = shift; my $dataNode = shift; my $interfaceName = $dataNode->name; my $className = "V8$interfaceName"; # - Add default header template push(@headerContent, GenerateHeaderContentHeader($dataNode)); my @unsortedIncludes = (); push(@unsortedIncludes, "#include \"ActiveDOMCallback.h\""); push(@unsortedIncludes, "#include \"$interfaceName.h\""); push(@unsortedIncludes, "#include \"WorldContextHandle.h\""); push(@unsortedIncludes, "#include "); push(@unsortedIncludes, "#include "); push(@headerContent, join("\n", sort @unsortedIncludes)); push(@headerContent, "\n\nnamespace WebCore {\n\n"); push(@headerContent, "class ScriptExecutionContext;\n\n"); push(@headerContent, "class $className : public $interfaceName, public ActiveDOMCallback {\n"); push(@headerContent, < create(v8::Local value, ScriptExecutionContext* context) { ASSERT(value->IsObject()); ASSERT(context); return adoptRef(new ${className}(value->ToObject(), context)); } virtual ~${className}(); END # Functions my $numFunctions = @{$dataNode->functions}; if ($numFunctions > 0) { push(@headerContent, " // Functions\n"); foreach my $function (@{$dataNode->functions}) { my @params = @{$function->parameters}; if (!$function->signature->extendedAttributes->{"Custom"} && !(GetNativeType($function->signature->type) eq "bool")) { push(@headerContent, " COMPILE_ASSERT(false)"); } push(@headerContent, " virtual " . GetNativeTypeForCallbacks($function->signature->type) . " " . $function->signature->name . "("); my @args = (); foreach my $param (@params) { push(@args, GetNativeTypeForCallbacks($param->type) . " " . $param->name); } push(@headerContent, join(", ", @args)); push(@headerContent, ");\n"); } } push(@headerContent, <, ScriptExecutionContext*); v8::Persistent m_callback; WorldContextHandle m_worldContext; }; END push(@headerContent, "}\n\n"); push(@headerContent, "#endif // $className" . "_h\n\n"); my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode); push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } sub GenerateCallbackImplementation { my $object = shift; my $dataNode = shift; my $interfaceName = $dataNode->name; my $className = "V8$interfaceName"; # - Add default header template push(@implFixedHeader, GenerateImplementationContentHeader($dataNode)); AddToImplIncludes("ScriptExecutionContext.h"); AddToImplIncludes("V8Binding.h"); AddToImplIncludes("V8CustomVoidCallback.h"); AddToImplIncludes("V8Proxy.h"); push(@implContent, "#include \n\n"); push(@implContent, "namespace WebCore {\n\n"); push(@implContent, < callback, ScriptExecutionContext* context) : ActiveDOMCallback(context) , m_callback(v8::Persistent::New(callback)) , m_worldContext(UseCurrentWorld) { } ${className}::~${className}() { m_callback.Dispose(); } END # Functions my $numFunctions = @{$dataNode->functions}; if ($numFunctions > 0) { push(@implContent, "// Functions\n"); foreach my $function (@{$dataNode->functions}) { my @params = @{$function->parameters}; if ($function->signature->extendedAttributes->{"Custom"} || !(GetNativeTypeForCallbacks($function->signature->type) eq "bool")) { next; } AddIncludesForType($function->signature->type); push(@implContent, "\n" . GetNativeTypeForCallbacks($function->signature->type) . " ${className}::" . $function->signature->name . "("); my @args = (); my @argsCheck = (); my $thisType = $function->signature->extendedAttributes->{"PassThisToCallback"}; foreach my $param (@params) { my $paramName = $param->name; AddIncludesForType($param->type); push(@args, GetNativeTypeForCallbacks($param->type) . " " . $paramName); if ($thisType and $thisType eq $param->type) { push(@argsCheck, < v8Context = toV8Context(scriptExecutionContext(), m_worldContext);\n"); push(@implContent, " if (v8Context.IsEmpty())\n"); push(@implContent, " return true;\n\n"); push(@implContent, " v8::Context::Scope scope(v8Context);\n\n"); @args = (); foreach my $param (@params) { my $paramName = $param->name; push(@implContent, " v8::Handle ${paramName}Handle = " . NativeToJSValue($param, $paramName, "0") . ";\n"); push(@implContent, " if (${paramName}Handle.IsEmpty()) {\n"); push(@implContent, " if (!isScriptControllerTerminating())\n"); push(@implContent, " CRASH();\n"); push(@implContent, " return true;\n"); push(@implContent, " }\n"); push(@args, " ${paramName}Handle"); } if (scalar(@args) > 0) { push(@implContent, "\n v8::Handle argv[] = {\n"); push(@implContent, join(",\n", @args)); push(@implContent, "\n };\n\n"); } else { push(@implContent, "\n v8::Handle *argv = 0;\n\n"); } push(@implContent, " bool callbackReturnValue = false;\n"); if ($thisType) { foreach my $param (@params) { next if $param->type ne $thisType; my $paramName = $param->name; push(@implContent, " return !invokeCallback(m_callback, v8::Handle::Cast(${paramName}Handle), " . scalar(@params) . ", argv, callbackReturnValue, scriptExecutionContext());\n"); last; } } else { push(@implContent, " return !invokeCallback(m_callback, " . scalar(@params) . ", argv, callbackReturnValue, scriptExecutionContext());\n"); } push(@implContent, "}\n"); } } push(@implContent, "\n} // namespace WebCore\n\n"); my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode); push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; } sub GenerateToV8Converters { my $dataNode = shift; my $interfaceName = shift; my $className = shift; my $nativeType = shift; my $domMapName = GetDomMapName($dataNode, $interfaceName); my $forceNewObjectInput = IsDOMNodeType($interfaceName) ? ", bool forceNewObject" : ""; my $forceNewObjectCall = IsDOMNodeType($interfaceName) ? ", forceNewObject" : ""; my $wrapSlowArgumentType = GetPassRefPtrType($nativeType); push(@implContent, < ${className}::wrapSlow(${wrapSlowArgumentType} impl, v8::Isolate* isolate) { v8::Handle wrapper; END my $proxyInit; if (IsNodeSubType($dataNode)) { $proxyInit = "V8Proxy::retrieve(impl->document()->frame())"; # DocumentType nodes are the only nodes that may have a NULL document. if ($interfaceName eq "DocumentType") { $proxyInit = "impl->document() ? $proxyInit : 0"; } } else { $proxyInit = "0"; } push(@implContent, <windowShell()->context().IsEmpty() && proxy->windowShell()->initContextIfNeeded()) { // initContextIfNeeded may have created a wrapper for the object, retry from the start. return ${className}::wrap(impl.get(), isolate); } END } # FIXME: We need a better way of recovering the correct prototype chain # for every sort of object. For now, we special-case cross-origin visible # objects (i.e., those with CheckSecurity). if (IsVisibleAcrossOrigins($dataNode)) { push(@implContent, <frame()) { proxy = V8Proxy::retrieve(impl->frame()); if (proxy) proxy->windowShell()->initContextIfNeeded(); } END } if (IsNodeSubType($dataNode) || IsVisibleAcrossOrigins($dataNode)) { push(@implContent, < context; if (proxy && !proxy->matchesCurrentContext()) { // For performance, we enter the context only if the currently running context // is different from the context that we are about to enter. context = proxy->context(); if (!context.IsEmpty()) context->Enter(); } END } push(@implContent, <Exit(); END } push(@implContent, < wrapperHandle = v8::Persistent::New(wrapper); if (!hasDependentLifetime) wrapperHandle.MarkIndependent(); END if (IsNodeSubType($dataNode)) { push(@implContent, <extendedAttributes->{"ActiveDOMObject"}); return "DOMNode" if IsNodeSubType($dataNode); return "ActiveDOMObject" if $dataNode->extendedAttributes->{"ActiveDOMObject"}; return "DOMObject"; } sub GetNativeTypeForConversions { my $dataNode = shift; my $type = shift; $type = $codeGenerator->GetSVGTypeNeedingTearOff($type) if $codeGenerator->IsSVGTypeNeedingTearOff($type); return $type; } sub GenerateFunctionCallString() { my $function = shift; my $numberOfParameters = shift; my $indent = shift; my $implClassName = shift; my %replacements = @_; my $name = $function->signature->name; my $returnType = GetTypeFromSignature($function->signature); my $nativeReturnType = GetNativeType($returnType, 0); my $result = ""; my $isSVGTearOffType = ($codeGenerator->IsSVGTypeNeedingTearOff($returnType) and not $implClassName =~ /List$/); $nativeReturnType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($returnType) if $isSVGTearOffType; if ($function->signature->extendedAttributes->{"ImplementedAs"}) { $name = $function->signature->extendedAttributes->{"ImplementedAs"}; } my $index = 0; my $hasScriptState = 0; my @arguments; my $functionName; my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"}; if ($implementedBy) { AddToImplIncludes("${implementedBy}.h"); unshift(@arguments, "imp") if !$function->isStatic; $functionName = "${implementedBy}::${name}"; } elsif ($function->isStatic) { $functionName = "${implClassName}::${name}"; } else { $functionName = "imp->${name}"; } my $callWith = $function->signature->extendedAttributes->{"CallWith"}; my @callWithOutput = (); my @callWithArgs = GenerateCallWith($callWith, \@callWithOutput, $indent, 0, 1, $function); $result .= join("", @callWithOutput); push(@arguments, @callWithArgs); $index += @callWithArgs; $numberOfParameters += @callWithArgs; foreach my $parameter (@{$function->parameters}) { if ($index eq $numberOfParameters) { last; } my $paramName = $parameter->name; my $paramType = $parameter->type; if ($replacements{$paramName}) { push @arguments, $replacements{$paramName}; } elsif ($parameter->type eq "IDBKey" || $parameter->type eq "NodeFilter" || $parameter->type eq "XPathNSResolver") { push @arguments, "$paramName.get()"; } elsif ($codeGenerator->IsSVGTypeNeedingTearOff($parameter->type) and not $implClassName =~ /List$/) { push @arguments, "$paramName->propertyReference()"; $result .= $indent . "if (!$paramName) {\n"; $result .= $indent . " V8Proxy::setDOMException(WebCore::TYPE_MISMATCH_ERR, args.GetIsolate());\n"; $result .= $indent . " return v8::Handle();\n"; $result .= $indent . "}\n"; } elsif ($parameter->type eq "SVGMatrix" and $implClassName eq "SVGTransformList") { push @arguments, "$paramName.get()"; } else { push @arguments, $paramName; } $index++; } if (@{$function->raisesExceptions}) { push @arguments, "ec"; } my $functionString = "$functionName(" . join(", ", @arguments) . ")"; my $return = "result"; my $returnIsRef = IsRefPtrType($returnType); if ($returnType eq "void") { $result .= $indent . "$functionString;\n"; } elsif ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState") or @{$function->raisesExceptions}) { $result .= $indent . $nativeReturnType . " result = $functionString;\n"; } else { # Can inline the function call into the return statement to avoid overhead of using a Ref<> temporary $return = $functionString; $returnIsRef = 0; if ($implClassName eq "SVGTransformList" and IsRefPtrType($returnType)) { $return = "WTF::getPtr(" . $return . ")"; } } if (@{$function->raisesExceptions}) { $result .= $indent . "if (UNLIKELY(ec))\n"; $result .= $indent . " goto fail;\n"; } if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState")) { $result .= $indent . "if (state.hadException())\n"; $result .= $indent . " return throwError(state.exception());\n" } if ($isSVGTearOffType) { AddToImplIncludes("V8$returnType.h"); AddToImplIncludes("SVGPropertyTearOff.h"); my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($returnType); $result .= $indent . "return toV8(WTF::getPtr(${svgNativeType}::create($return)), args.GetIsolate());\n"; return $result; } # If the implementing class is a POD type, commit changes if ($codeGenerator->IsSVGTypeNeedingTearOff($implClassName) and not $implClassName =~ /List$/) { $result .= $indent . "wrapper->commitChange();\n"; } $return .= ".release()" if ($returnIsRef); $result .= $indent . ReturnNativeToJSValue($function->signature, $return, "args.GetIsolate()") . ";\n"; return $result; } sub GetTypeFromSignature { my $signature = shift; return $codeGenerator->StripModule($signature->type); } sub GetNativeTypeFromSignature { my $signature = shift; my $parameterIndex = shift; my $type = GetTypeFromSignature($signature); if ($type eq "unsigned long" and $signature->extendedAttributes->{"IsIndex"}) { # Special-case index arguments because we need to check that they aren't < 0. return "int"; } $type = GetNativeType($type, $parameterIndex >= 0 ? 1 : 0); if ($parameterIndex >= 0 && $type eq "V8Parameter") { # FIXME: This implements [TreatNullAs=NullString] and [TreatUndefinedAs=NullString], # but the Web IDL spec requires [TreatNullAs=EmptyString] and [TreatUndefinedAs=EmptyString]. my $mode = ""; if (($signature->extendedAttributes->{"TreatNullAs"} and $signature->extendedAttributes->{"TreatNullAs"} eq "NullString") and ($signature->extendedAttributes->{"TreatUndefinedAs"} and $signature->extendedAttributes->{"TreatUndefinedAs"} eq "NullString")) { $mode = "WithUndefinedOrNullCheck"; } elsif (($signature->extendedAttributes->{"TreatNullAs"} and $signature->extendedAttributes->{"TreatNullAs"} eq "NullString") or $signature->extendedAttributes->{"Reflect"}) { $mode = "WithNullCheck"; } # FIXME: Add the case for 'elsif ($signature->extendedAttributes->{"TreatUndefinedAs"} and $signature->extendedAttributes->{"TreatUndefinedAs"} eq "NullString"))'. $type .= "<$mode>"; } return $type; } sub IsRefPtrType { my $type = shift; return 0 if $type eq "boolean"; return 0 if $type eq "float"; return 0 if $type eq "int"; return 0 if $type eq "Date"; return 0 if $type eq "DOMString"; return 0 if $type eq "double"; return 0 if $type eq "short"; return 0 if $type eq "long"; return 0 if $type eq "unsigned"; return 0 if $type eq "unsigned long"; return 0 if $type eq "unsigned short"; return 0 if $type eq "float[]"; return 0 if $type eq "double[]"; return 1; } sub GetNativeType { my $type = shift; my $isParameter = shift; my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($type); if ($svgNativeType) { if ($svgNativeType =~ /List$/) { return "${svgNativeType}*"; } else { return "RefPtr<${svgNativeType} >"; } } if ($type eq "float" or $type eq "double") { return $type; } return "V8Parameter" if ($type eq "DOMString" or $type eq "DOMUserData") and $isParameter; return "int" if $type eq "int"; return "int" if $type eq "short" or $type eq "unsigned short"; return "unsigned" if $type eq "unsigned long"; return "int" if $type eq "long"; return "long long" if $type eq "long 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 "DOMTimeStamp" if $type eq "DOMTimeStamp"; return "unsigned" if $type eq "unsigned int"; return "Node*" if $type eq "EventTarget" and $isParameter; return "double" if $type eq "Date"; return "ScriptValue" if $type eq "DOMObject"; return "Dictionary" if $type eq "Dictionary"; return "String" if $type eq "DOMUserData"; # FIXME: Temporary hack? # temporary hack return "RefPtr" if $type eq "NodeFilter"; return "RefPtr" if $type eq "SerializedScriptValue"; return "RefPtr" if $type eq "IDBKey"; # necessary as resolvers could be constructed on fly. return "RefPtr" if $type eq "XPathNSResolver"; return "RefPtr<${type}>" if IsRefPtrType($type) and not $isParameter; return "RefPtr" if $type eq "MediaQueryListListener"; # FIXME: Support T[], T[]?, sequence generically return "Vector" if $type eq "float[]"; return "Vector" if $type eq "double[]"; return "RefPtr" if $type eq "DOMStringList"; return "RefPtr" if $type eq "DOMString[]"; # Default, assume native type is a pointer with same type name as idl type return "${type}*"; } sub GetNativeTypeForCallbacks { my $type = shift; return "const String&" if $type eq "DOMString"; return "SerializedScriptValue*" if $type eq "SerializedScriptValue"; # Callbacks use raw pointers, so pass isParameter = 1 return GetNativeType($type, 1); } 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 TypeCanFailConversion { my $signature = shift; my $type = GetTypeFromSignature($signature); AddToImplIncludes("ExceptionCode.h") if $type eq "Attr"; return 1 if $type eq "Attr"; return 1 if $type eq "VoidCallback"; return 0; } sub JSValueToNative { my $signature = shift; my $value = shift; my $getIsolate = shift; my $type = GetTypeFromSignature($signature); 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 "toInt32($value)" if $type eq "long" or $type eq "short"; return "toUInt32($value)" if $type eq "unsigned long" or $type eq "unsigned short"; return "toInt64($value)" if $type eq "unsigned long long" or $type eq "long long"; return "static_cast($value->Int32Value())" if $type eq "CompareHow"; return "toWebCoreDate($value)" if $type eq "Date"; return "v8ValueToWebCoreDOMStringList($value)" if $type eq "DOMStringList"; # FIXME: Add proper support for T[], T[]? and sequence. return "v8ValueToWebCoreDOMStringList($value)" if $type eq "DOMString[]"; if ($type eq "float[]") { AddToImplIncludes("wtf/Vector.h"); return "v8NumberArrayToVector($value)"; } if ($type eq "double[]") { AddToImplIncludes("wtf/Vector.h"); return "v8NumberArrayToVector($value)"; } if ($type eq "DOMString" or $type eq "DOMUserData") { return $value; } if ($type eq "SerializedScriptValue") { AddToImplIncludes("SerializedScriptValue.h"); return "SerializedScriptValue::create($value, $getIsolate)"; } if ($type eq "IDBKey") { AddToImplIncludes("IDBBindingUtilities.h"); AddToImplIncludes("IDBKey.h"); return "createIDBKeyFromValue($value)"; } if ($type eq "Dictionary") { AddToImplIncludes("Dictionary.h"); return $value; } if ($type eq "DOMObject") { AddToImplIncludes("ScriptValue.h"); return "ScriptValue($value)"; } if ($type eq "NodeFilter") { return "V8DOMWrapper::wrapNativeNodeFilter($value)"; } if ($type eq "MediaQueryListListener") { AddToImplIncludes("MediaQueryListListener.h"); return "MediaQueryListListener::create(" . $value . ")"; } # Default, assume autogenerated type conversion routines if ($type eq "EventTarget") { AddToImplIncludes("V8Node.h"); # EventTarget is not in DOM hierarchy, but all Nodes are EventTarget. return "V8Node::HasInstance($value) ? V8Node::toNative(v8::Handle::Cast($value)) : 0"; } if ($type eq "XPathNSResolver") { return "V8DOMWrapper::getXPathNSResolver($value)"; } my $arrayType = $codeGenerator->GetArrayType($type); if ($arrayType) { return "toNativeArray<$arrayType>($value)"; } AddIncludesForType($type); if (IsDOMNodeType($type)) { AddToImplIncludes("V8${type}.h"); # Perform type checks on the parameter, if it is expected Node type, # return NULL. return "V8${type}::HasInstance($value) ? V8${type}::toNative(v8::Handle::Cast($value)) : 0"; } else { AddToImplIncludes("V8$type.h"); # Perform type checks on the parameter, if it is expected Node type, # return NULL. return "V8${type}::HasInstance($value) ? V8${type}::toNative(v8::Handle::Cast($value)) : 0"; } } sub GetV8HeaderName { my $type = shift; return "V8Event.h" if $type eq "DOMTimeStamp"; return "EventListener.h" if $type eq "EventListener"; return "EventTarget.h" if $type eq "EventTarget"; return "SerializedScriptValue.h" if $type eq "SerializedScriptValue"; return "ScriptValue.h" if $type eq "DOMObject"; return "V8DOMStringList.h" if $type eq "DOMString[]"; return "V8${type}.h"; } 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)) { if ($parameter->type eq "XPathNSResolver") { # Special case for XPathNSResolver. All other browsers accepts a callable, # so, even though it's against IDL, accept objects here. $result .= "v8::Handle()"; } else { my $type = $parameter->type; my $arrayType = $codeGenerator->GetArrayType($type); if ($arrayType) { AddToImplIncludes("$arrayType.h"); } else { my $header = GetV8HeaderName($type); AddToImplIncludes($header); } $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; } # No signature needed for overloaded function if (@{$function->{overloads}} > 1) { return 0; } if ($function->isStatic) { return 0; } # Type checking is performed in the generated code if ($function->signature->extendedAttributes->{"StrictTypeChecking"}) { return 0; } foreach my $parameter (@{$function->parameters}) { my $optional = $parameter->extendedAttributes->{"Optional"}; if (($optional && $optional ne "DefaultIsUndefined" && $optional ne "DefaultIsNullString") || $parameter->extendedAttributes->{"Callback"}) { return 0; } } foreach my $parameter (@{$function->parameters}) { if (IsWrapperType($parameter->type)) { return 1; } } return 0; } # FIXME: Sort this array. my %non_wrapper_types = ( 'float' => 1, 'double' => 1, 'int' => 1, 'unsigned int' => 1, 'short' => 1, 'unsigned short' => 1, 'long' => 1, 'unsigned long' => 1, 'boolean' => 1, 'long long' => 1, 'unsigned long long' => 1, 'float[]' => 1, 'double[]' => 1, 'DOMString' => 1, 'CompareHow' => 1, 'SerializedScriptValue' => 1, 'DOMTimeStamp' => 1, 'JSObject' => 1, 'DOMObject' => 1, 'EventTarget' => 1, 'NodeFilter' => 1, 'EventListener' => 1, 'IDBKey' => 1, 'Dictionary' => 1, 'Date' => 1, 'MediaQueryListListener' => 1 ); sub IsWrapperType { my $type = $codeGenerator->StripModule(shift); return !($non_wrapper_types{$type}); } sub IsArrayType { my $type = $codeGenerator->StripModule(shift); # FIXME: Add proper support for T[], T[]?, sequence. return $type =~ m/\[\]$/; } 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 'HTMLUnknownElement'; 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 NativeToJSValue { my $signature = shift; my $value = shift; my $getIsolate = shift; my $type = GetTypeFromSignature($signature); return "v8Boolean($value)" if $type eq "boolean"; return "v8::Handle()" if $type eq "void"; # equivalent to v8::Undefined() # HTML5 says that unsigned reflected attributes should be in the range # [0, 2^31). When a value isn't in this range, a default value (or 0) # should be returned instead. if ($signature->extendedAttributes->{"Reflect"} and ($type eq "unsigned long" or $type eq "unsigned short")) { $value =~ s/getUnsignedIntegralAttribute/getIntegralAttribute/g; return "v8::Integer::NewFromUnsigned(std::max(0, " . $value . "))"; } # For all the types where we use 'int' as the representation type, # we use Integer::New which has a fast Smi conversion check. my $nativeType = GetNativeType($type); return "v8::Integer::New($value)" if $nativeType eq "int"; return "v8::Integer::NewFromUnsigned($value)" if $nativeType eq "unsigned"; return "v8DateOrNull($value)" if $type eq "Date"; # long long and unsigned long long are not representable in ECMAScript. return "v8::Number::New(static_cast($value))" if $type eq "long long" or $type eq "unsigned long long" or $type eq "DOMTimeStamp"; return "v8::Number::New($value)" if $codeGenerator->IsPrimitiveType($type); return "$value.v8Value()" if $nativeType eq "ScriptValue"; return "v8NumberArray($value)" if $type eq "float[]"; return "v8NumberArray($value)" if $type eq "double[]"; if ($codeGenerator->IsStringType($type)) { my $conv = $signature->extendedAttributes->{"TreatReturnedNullStringAs"}; if (defined $conv) { return "v8StringOrNull($value, $getIsolate)" if $conv eq "Null"; return "v8StringOrUndefined($value, $getIsolate)" if $conv eq "Undefined"; return "v8StringOrFalse($value, $getIsolate)" if $conv eq "False"; die "Unknown value for TreatReturnedNullStringAs extended attribute"; } return "v8String($value, $getIsolate)"; } my $arrayType = $codeGenerator->GetArrayType($type); if ($arrayType) { if (!$codeGenerator->SkipIncludeHeader($arrayType)) { AddToImplIncludes("V8$arrayType.h"); AddToImplIncludes("$arrayType.h"); } return "v8Array($value, $getIsolate)"; } AddIncludesForType($type); # special case for non-DOM node interfaces if (IsDOMNodeType($type)) { return "toV8(${value}" . ($signature->extendedAttributes->{"ReturnNewObject"} ? ", $getIsolate, true)" : ")"); } if ($type eq "EventTarget") { return "V8DOMWrapper::convertEventTargetToV8Object($value)"; } if ($type eq "EventListener") { AddToImplIncludes("V8AbstractEventListener.h"); return "${value} ? v8::Handle(static_cast(${value})->getListenerObject(imp->scriptExecutionContext())) : v8::Handle(v8::Null())"; } if ($type eq "SerializedScriptValue") { AddToImplIncludes("$type.h"); return "$value ? $value->deserialize() : v8::Handle(v8::Null())"; } AddToImplIncludes("wtf/RefCounted.h"); AddToImplIncludes("wtf/RefPtr.h"); AddToImplIncludes("wtf/GetPtr.h"); return "toV8($value, $getIsolate)"; } sub ReturnNativeToJSValue { return "return " . NativeToJSValue(@_); } # Internal helper sub WriteData { my $object = shift; my $dataNode = shift; my $name = $dataNode->name; my $prefix = FileNamePrefix; my $headerFileName = "$outputHeadersDir/$prefix$name.h"; my $implFileName = "$outputDir/$prefix$name.cpp"; # Update a .cpp file if the contents are changed. my $contents = join "", @implContentHeader, @implFixedHeader; my @includes = (); my %implIncludeConditions = (); foreach my $include (keys %implIncludes) { my $condition = $implIncludes{$include}; my $checkType = $include; $checkType =~ s/\.h//; next if $codeGenerator->IsSVGAnimatedType($checkType); if ($include =~ /wtf/) { $include = "\<$include\>"; } else { $include = "\"$include\""; } if ($condition eq 1) { push @includes, $include; } else { push @{$implIncludeConditions{$condition}}, $include; } } foreach my $include (sort @includes) { $contents .= "#include $include\n"; } foreach my $condition (sort keys %implIncludeConditions) { $contents .= "\n#if " . $codeGenerator->GenerateConditionalStringFromAttributeValue($condition) . "\n"; foreach my $include (sort @{$implIncludeConditions{$condition}}) { $contents .= "#include $include\n"; } $contents .= "#endif\n"; } $contents .= "\n"; $contents .= join "", @implContentDecls, @implContent; $codeGenerator->UpdateFile($implFileName, $contents); %implIncludes = (); @implFixedHeader = (); @implContentDecls = (); @implContent = (); # Update a .h file if the contents are changed. $contents = join "", @headerContent; $codeGenerator->UpdateFile($headerFileName, $contents); @headerContent = (); } sub GetCallbackClassName { my $interfaceName = shift; return "V8CustomVoidCallback" if $interfaceName eq "VoidCallback"; return "V8$interfaceName"; } sub ConvertToV8Parameter { my $signature = shift; my $nativeType = shift; my $variableName = shift; my $value = shift; my $suffix = shift; die "Wrong native type passed: $nativeType" unless $nativeType =~ /^V8Parameter/; if ($signature->type eq "DOMString") { AddToImplIncludes("V8BindingMacros.h"); my $macro = "STRING_TO_V8PARAMETER_EXCEPTION_BLOCK"; $macro .= "_$suffix" if $suffix; return "$macro($nativeType, $variableName, $value);" } else { # Don't know how to properly check for conversion exceptions when $parameter->type is "DOMUserData" return "$nativeType $variableName($value, true);"; } } # Returns the RuntimeEnabledFeatures function name that is hooked up to check if a method/attribute is enabled. sub GetRuntimeEnableFunctionName { my $signature = shift; # If a parameter is given (e.g. "V8EnabledAtRuntime=FeatureName") return the RuntimeEnabledFeatures::{FeatureName}Enabled() method. return "RuntimeEnabledFeatures::" . $codeGenerator->WK_lcfirst($signature->extendedAttributes->{"V8EnabledAtRuntime"}) . "Enabled" if ($signature->extendedAttributes->{"V8EnabledAtRuntime"} && $signature->extendedAttributes->{"V8EnabledAtRuntime"} ne "VALUE_IS_MISSING"); # Otherwise return a function named RuntimeEnabledFeatures::{methodName}Enabled(). return "RuntimeEnabledFeatures::" . $codeGenerator->WK_lcfirst($signature->name) . "Enabled"; } sub GetPassRefPtrType { my $className = shift; my $angleBracketSpace = $className =~ />$/ ? " " : ""; return "PassRefPtr<${className}${angleBracketSpace}>"; } sub DebugPrint { my $output = shift; print $output; print "\n"; } 1;