| # Copyright (C) 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org> |
| # Copyright (C) 2006 Anders Carlsson <andersca@mac.com> |
| # Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> |
| # Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org> |
| # Copyright (C) 2006 Apple Computer, Inc. |
| # Copyright (C) 2007, 2008, 2009, 2012 Google Inc. |
| # Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au> |
| # 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 constant FileNamePrefix => "V8"; |
| |
| my $codeGenerator; |
| |
| my $outputDir = ""; |
| my $outputHeadersDir = ""; |
| |
| my @headerContent = (); |
| my @implContentHeader = (); |
| my @implFixedHeader = (); |
| my @implContent = (); |
| my @implContentDecls = (); |
| my %implIncludes = (); |
| my %headerIncludes = (); |
| |
| # 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); |
| } |
| |
| 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 = shift; |
| |
| # When we're finished with the one-file-per-class |
| # reorganization, we won't need these special cases. |
| if ($codeGenerator->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("wtf/text/WTFString.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 NeedsCustomOpaqueRootForGC |
| { |
| my $dataNode = shift; |
| return GetGenerateIsReachable($dataNode) || GetCustomIsReachable($dataNode); |
| } |
| |
| sub GetGenerateIsReachable |
| { |
| my $dataNode = shift; |
| return $dataNode->extendedAttributes->{"GenerateIsReachable"} || $dataNode->extendedAttributes->{"V8GenerateIsReachable"} || "" |
| } |
| |
| sub GetCustomIsReachable |
| { |
| my $dataNode = shift; |
| return $dataNode->extendedAttributes->{"CustomIsReachable"} || $dataNode->extendedAttributes->{"V8CustomIsReachable"}; |
| } |
| |
| sub GenerateOpaqueRootForGC |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| if (GetCustomIsReachable($dataNode)) { |
| return; |
| } |
| |
| push(@implContent, <<END); |
| void* V8${interfaceName}::opaqueRootForGC(void* object, v8::Persistent<v8::Object> wrapper) |
| { |
| ASSERT(!wrapper.IsIndependent()); |
| ${interfaceName}* impl = static_cast<${interfaceName}*>(object); |
| END |
| if (GetGenerateIsReachable($dataNode) eq "ImplDocument" || |
| GetGenerateIsReachable($dataNode) eq "ImplElementRoot" || |
| GetGenerateIsReachable($dataNode) eq "ImplOwnerRoot" || |
| GetGenerateIsReachable($dataNode) eq "ImplOwnerNodeRoot" || |
| GetGenerateIsReachable($dataNode) eq "ImplBaseRoot") { |
| |
| $implIncludes{"V8GCController.h"} = 1; |
| |
| my $methodName; |
| $methodName = "document" if (GetGenerateIsReachable($dataNode) eq "ImplDocument"); |
| $methodName = "element" if (GetGenerateIsReachable($dataNode) eq "ImplElementRoot"); |
| $methodName = "owner" if (GetGenerateIsReachable($dataNode) eq "ImplOwnerRoot"); |
| $methodName = "ownerNode" if (GetGenerateIsReachable($dataNode) eq "ImplOwnerNodeRoot"); |
| $methodName = "base" if (GetGenerateIsReachable($dataNode) eq "ImplBaseRoot"); |
| |
| push(@implContent, <<END); |
| if (Node* owner = impl->${methodName}()) |
| return V8GCController::opaqueRootForGC(owner); |
| END |
| } |
| |
| push(@implContent, <<END); |
| return object; |
| } |
| |
| END |
| } |
| |
| 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 $v8InterfaceName = "V8$interfaceName"; |
| |
| # Copy contents of parent classes except the first parent. |
| my @parents; |
| $codeGenerator->AddMethodsConstantsAndAttributesFromParentClasses($dataNode, \@parents, 1); |
| $codeGenerator->LinkOverloadedFunctions($dataNode); |
| |
| # Ensure the IsDOMNodeType function is in sync. |
| die("IsDOMNodeType is out of date with respect to $interfaceName") if IsDOMNodeType($interfaceName) != $codeGenerator->IsSubType($dataNode, "Node"); |
| |
| my $hasDependentLifetime = $dataNode->extendedAttributes->{"V8DependentLifetime"} || $dataNode->extendedAttributes->{"ActiveDOMObject"} |
| || GetGenerateIsReachable($dataNode) || $v8InterfaceName =~ /SVG/; |
| if (!$hasDependentLifetime) { |
| foreach (@{$dataNode->parents}) { |
| my $parent = $_; |
| $headerIncludes{"V8${parent}.h"} = 1; |
| } |
| } |
| |
| # - Add default header template |
| push(@headerContent, GenerateHeaderContentHeader($dataNode)); |
| |
| $headerIncludes{"wtf/text/StringHash.h"} = 1; |
| $headerIncludes{"WrapperTypeInfo.h"} = 1; |
| $headerIncludes{"V8Binding.h"} = 1; |
| $headerIncludes{"V8DOMWrapper.h"} = 1; |
| $headerIncludes{"wtf/HashMap.h"} = 1; |
| $headerIncludes{"v8.h"} = 1; |
| |
| my $headerClassInclude = GetHeaderClassInclude($interfaceName); |
| $headerIncludes{$headerClassInclude} = 1 if $headerClassInclude ne ""; |
| |
| my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($interfaceName); |
| |
| 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<typename PropertyType> class SVGPropertyTearOff;\n") if $svgPropertyType; |
| if ($svgNativeType) { |
| if ($svgNativeType =~ /SVGStaticListPropertyTearOff/) { |
| push(@headerContent, "\ntemplate<typename PropertyType> class SVGStaticListPropertyTearOff;\n"); |
| } else { |
| push(@headerContent, "\ntemplate<typename PropertyType> class SVGListPropertyTearOff;\n"); |
| } |
| } |
| |
| push(@headerContent, "\n"); |
| push(@headerContent, "class FloatRect;\n") if $svgPropertyType && $svgPropertyType eq "FloatRect"; |
| push(@headerContent, "class Dictionary;\n") if $codeGenerator->IsConstructorTemplate($dataNode, "Event"); |
| |
| my $nativeType = GetNativeTypeForConversions($dataNode); |
| if ($dataNode->extendedAttributes->{"NamedConstructor"}) { |
| push(@headerContent, <<END); |
| class V8${nativeType}Constructor { |
| public: |
| static v8::Persistent<v8::FunctionTemplate> GetTemplate(); |
| static WrapperTypeInfo info; |
| }; |
| |
| END |
| } |
| |
| push(@headerContent, "class $v8InterfaceName {\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 = $_; |
| $headerIncludes{"V8${parent}.h"} = 1; |
| push(@headerContent, "${separator}V8${parent}::hasDependentLifetime"); |
| $separator = " || "; |
| } |
| push(@headerContent, ";\n"); |
| } else { |
| push(@headerContent, "false;\n"); |
| } |
| |
| push(@headerContent, <<END); |
| static bool HasInstance(v8::Handle<v8::Value>); |
| static v8::Persistent<v8::FunctionTemplate> GetRawTemplate(); |
| static v8::Persistent<v8::FunctionTemplate> GetTemplate(); |
| static ${nativeType}* toNative(v8::Handle<v8::Object> object) |
| { |
| return reinterpret_cast<${nativeType}*>(object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex)); |
| } |
| static void derefObject(void*); |
| static WrapperTypeInfo info; |
| END |
| |
| if (NeedsCustomOpaqueRootForGC($dataNode)) { |
| push(@headerContent, " static void* opaqueRootForGC(void*, v8::Persistent<v8::Object>);\n"); |
| } |
| |
| if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) { |
| push(@headerContent, " static ActiveDOMObject* toActiveDOMObject(v8::Handle<v8::Object>);\n"); |
| } |
| |
| if ($interfaceName eq "DOMWindow") { |
| push(@headerContent, <<END); |
| static v8::Persistent<v8::ObjectTemplate> GetShadowObjectTemplate(); |
| END |
| } |
| |
| if ($interfaceName eq "HTMLDocument") { |
| push(@headerContent, <<END); |
| static v8::Local<v8::Object> wrapInShadowObject(v8::Local<v8::Object> wrapper, Node* impl); |
| static v8::Handle<v8::Value> getNamedProperty(HTMLDocument* htmlDocument, const AtomicString& key, v8::Handle<v8::Object> creationContext, v8::Isolate*); |
| END |
| } |
| |
| my @enabledPerContextFunctions; |
| 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, <<END); |
| static v8::Handle<v8::Value> ${name}Callback(const v8::Arguments&); |
| END |
| push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; |
| } |
| if ($attrExt->{"V8EnabledPerContext"}) { |
| push(@enabledPerContextFunctions, $function); |
| } |
| } |
| |
| if (IsConstructable($dataNode)) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Value> constructorCallback(const v8::Arguments&); |
| END |
| } |
| |
| my @enabledPerContextAttributes; |
| 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, <<END); |
| static v8::Handle<v8::Value> ${name}AccessorGetter(v8::Local<v8::String> 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, <<END); |
| static void ${name}AccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value>, const v8::AccessorInfo&); |
| END |
| push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; |
| } |
| if ($attrExt->{"V8EnabledPerContext"}) { |
| push(@enabledPerContextAttributes, $attribute); |
| } |
| } |
| |
| GenerateHeaderNamedAndIndexedPropertyAccessors($dataNode); |
| GenerateHeaderCustomCall($dataNode); |
| GenerateHeaderCustomInternalFieldIndices($dataNode); |
| |
| if ($dataNode->extendedAttributes->{"CheckSecurity"}) { |
| push(@headerContent, <<END); |
| static bool namedSecurityCheck(v8::Local<v8::Object> host, v8::Local<v8::Value> key, v8::AccessType, v8::Local<v8::Value> data); |
| static bool indexedSecurityCheck(v8::Local<v8::Object> host, uint32_t index, v8::AccessType, v8::Local<v8::Value> data); |
| END |
| } |
| |
| if (@enabledPerContextAttributes) { |
| push(@headerContent, <<END); |
| static void installPerContextProperties(v8::Handle<v8::Object>, ${nativeType}*); |
| END |
| } else { |
| push(@headerContent, <<END); |
| static void installPerContextProperties(v8::Handle<v8::Object>, ${nativeType}*) { } |
| END |
| } |
| |
| if (@enabledPerContextFunctions) { |
| push(@headerContent, <<END); |
| static void installPerContextPrototypeProperties(v8::Handle<v8::Object>); |
| END |
| } else { |
| push(@headerContent, <<END); |
| static void installPerContextPrototypeProperties(v8::Handle<v8::Object>) { } |
| END |
| } |
| |
| if ($interfaceName eq "HTMLElement") { |
| push(@headerContent, <<END); |
| friend v8::Handle<v8::Object> createV8HTMLWrapper(HTMLElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*); |
| END |
| } elsif ($interfaceName eq "SVGElement") { |
| push(@headerContent, <<END); |
| friend v8::Handle<v8::Object> createV8SVGWrapper(SVGElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*); |
| END |
| } elsif ($interfaceName eq "Element") { |
| push(@headerContent, <<END); |
| // This is a performance optimization hack. See V8Element::wrap. |
| friend v8::Handle<v8::Object> wrap(Node*, v8::Handle<v8::Object> creationContext, v8::Isolate*); |
| END |
| } |
| |
| push(@headerContent, <<END); |
| private: |
| END |
| |
| if (IsConstructable($dataNode) && @{$dataNode->constructors} > 1) { |
| for (my $i = 1; $i <= @{$dataNode->constructors}; $i++) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Value> constructor${i}Callback(const v8::Arguments&); |
| END |
| } |
| } |
| |
| my $noToV8 = $dataNode->extendedAttributes->{"SuppressToJSObject"}; |
| my $noWrap = $dataNode->extendedAttributes->{"V8NoWrapperCache"} || $noToV8; |
| my $createWrapperArgumentType = GetPassRefPtrType($nativeType); |
| if (!$noWrap) { |
| my $createWrapperArgumentType = GetPassRefPtrType($nativeType); |
| push(@headerContent, <<END); |
| friend v8::Handle<v8::Object> wrap(${nativeType}*, v8::Handle<v8::Object> creationContext, v8::Isolate*); |
| static v8::Handle<v8::Object> createWrapper(${createWrapperArgumentType}, v8::Handle<v8::Object> creationContext, v8::Isolate*); |
| END |
| } |
| |
| push(@headerContent, <<END); |
| }; |
| |
| END |
| |
| my $customWrap = !!($dataNode->extendedAttributes->{"CustomToJSObject"} or $dataNode->extendedAttributes->{"V8CustomToJSObject"}); |
| if ($noToV8) { |
| die "Can't suppress toV8 for subclass\n" if @parents; |
| } elsif ($noWrap) { |
| die "Must have custom toV8\n" if !$customWrap; |
| push(@headerContent, <<END); |
| class ${nativeType}; |
| v8::Handle<v8::Value> toV8(${nativeType}*, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* = 0); |
| |
| inline v8::Handle<v8::Value> toV8Fast(${nativeType}* impl, const v8::AccessorInfo& info, Node*) |
| { |
| return toV8(impl, info.Holder(), info.GetIsolate()); |
| } |
| END |
| } else { |
| |
| my $getCachedWrapper = $codeGenerator->IsSubType($dataNode, "Node") ? "V8DOMWrapper::getCachedWrapper(impl)" : "DOMDataStore::current(isolate)->get(impl)"; |
| my $createWrapperCall = $customWrap ? "${v8InterfaceName}::wrap" : "${v8InterfaceName}::createWrapper"; |
| |
| if ($customWrap) { |
| push(@headerContent, <<END); |
| |
| v8::Handle<v8::Object> wrap(${nativeType}* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* = 0); |
| END |
| } else { |
| push(@headerContent, <<END); |
| |
| inline v8::Handle<v8::Object> wrap(${nativeType}* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate = 0) |
| { |
| ASSERT(impl); |
| ASSERT($getCachedWrapper.IsEmpty()); |
| return $createWrapperCall(impl, creationContext, isolate); |
| } |
| END |
| } |
| |
| push(@headerContent, <<END); |
| |
| inline v8::Handle<v8::Object> toV8Object(${nativeType}* impl, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* isolate = 0) |
| { |
| if (UNLIKELY(!impl)) |
| return v8::Handle<v8::Object>(); |
| v8::Handle<v8::Object> wrapper = $getCachedWrapper; |
| if (!wrapper.IsEmpty()) |
| return wrapper; |
| return wrap(impl, creationContext, isolate); |
| } |
| |
| inline v8::Handle<v8::Value> toV8(${nativeType}* impl, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* isolate = 0) |
| { |
| if (UNLIKELY(!impl)) |
| return v8NullWithCheck(isolate); |
| v8::Handle<v8::Value> wrapper = $getCachedWrapper; |
| if (!wrapper.IsEmpty()) |
| return wrapper; |
| return wrap(impl, creationContext, isolate); |
| } |
| END |
| if ($codeGenerator->IsSubType($dataNode, "Node")) { |
| push(@headerContent, <<END); |
| |
| inline v8::Handle<v8::Value> toV8Fast(${nativeType}* impl, const v8::AccessorInfo& info, Node* holder) |
| { |
| if (UNLIKELY(!impl)) |
| return v8::Null(info.GetIsolate()); |
| // What we'd really like to check here is whether we're in the main world or |
| // in an isolated world. The fastest way we know how to do that is to check |
| // whether the holder's inline wrapper is the same wrapper we see in the |
| // v8::AccessorInfo. |
| v8::Handle<v8::Object> wrapper = (holder->wrapper() == info.Holder()) ? impl->wrapper() : V8DOMWrapper::getCachedWrapper(impl); |
| if (!wrapper.IsEmpty()) |
| return wrapper; |
| return wrap(impl, info.Holder(), info.GetIsolate()); |
| } |
| END |
| } |
| } |
| |
| push(@headerContent, <<END); |
| |
| inline v8::Handle<v8::Value> toV8(PassRefPtr< ${nativeType} > impl, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* isolate = 0) |
| { |
| return toV8(impl.get(), creationContext, isolate); |
| } |
| END |
| |
| if ($codeGenerator->IsConstructorTemplate($dataNode, "Event")) { |
| push(@headerContent, "\nbool fill${interfaceName}Init(${interfaceName}Init&, const Dictionary&);\n"); |
| } |
| |
| push(@headerContent, "\n}\n\n"); |
| push(@headerContent, "#endif // $v8InterfaceName" . "_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: Simplify this when all EventTargets are subtypes of EventTarget. |
| if (!$codeGenerator->IsSubType($dataNode, "Node") |
| && ($dataNode->extendedAttributes->{"EventTarget"} |
| || $dataNode->extendedAttributes->{"IsWorkerContext"} |
| || $codeGenerator->IsSubType($dataNode, "AbstractWorker") |
| || $codeGenerator->IsSubType($dataNode, "EventTarget"))) { |
| push(@customInternalFields, "eventListenerCacheIndex"); |
| } |
| |
| return @customInternalFields; |
| } |
| |
| sub GetHeaderClassInclude |
| { |
| my $v8InterfaceName = shift; |
| if ($v8InterfaceName =~ /SVGPathSeg/) { |
| $v8InterfaceName =~ s/Abs|Rel//; |
| } |
| return "wtf/${v8InterfaceName}.h" if $codeGenerator->IsTypedArrayType($v8InterfaceName); |
| return "" if ($codeGenerator->SkipIncludeHeader($v8InterfaceName)); |
| return "${v8InterfaceName}.h"; |
| } |
| |
| sub GenerateHeaderCustomInternalFieldIndices |
| { |
| my $dataNode = shift; |
| my @customInternalFields = GetInternalFields($dataNode); |
| my $customFieldCounter = 0; |
| foreach my $customInternalField (@customInternalFields) { |
| push(@headerContent, <<END); |
| static const int ${customInternalField} = v8DefaultWrapperInternalFieldCount + ${customFieldCounter}; |
| END |
| $customFieldCounter++; |
| } |
| push(@headerContent, <<END); |
| static const int internalFieldCount = v8DefaultWrapperInternalFieldCount + ${customFieldCounter}; |
| END |
| } |
| |
| my %indexerSpecialCases = ( |
| "Storage" => 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, <<END); |
| static v8::Handle<v8::Value> indexedPropertyGetter(uint32_t, const v8::AccessorInfo&); |
| END |
| } |
| |
| if ($isIndexerSpecialCase || $hasCustomIndexedSetter) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Value> indexedPropertySetter(uint32_t, v8::Local<v8::Value>, const v8::AccessorInfo&); |
| END |
| } |
| if ($hasCustomDeleters) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Boolean> indexedPropertyDeleter(uint32_t, const v8::AccessorInfo&); |
| END |
| } |
| if ($hasCustomNamedGetter) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Value> namedPropertyGetter(v8::Local<v8::String>, const v8::AccessorInfo&); |
| END |
| } |
| if ($hasCustomNamedSetter) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Value> namedPropertySetter(v8::Local<v8::String>, v8::Local<v8::Value>, const v8::AccessorInfo&); |
| END |
| } |
| if ($hasCustomDeleters) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Boolean> namedPropertyDeleter(v8::Local<v8::String>, const v8::AccessorInfo&); |
| END |
| } |
| if ($hasCustomEnumerator) { |
| push(@headerContent, <<END); |
| static v8::Handle<v8::Array> namedPropertyEnumerator(const v8::AccessorInfo&); |
| static v8::Handle<v8::Integer> namedPropertyQuery(v8::Local<v8::String>, const v8::AccessorInfo&); |
| END |
| } |
| } |
| |
| sub GenerateHeaderCustomCall |
| { |
| my $dataNode = shift; |
| |
| if ($dataNode->extendedAttributes->{"CustomCall"}) { |
| push(@headerContent, " static v8::Handle<v8::Value> callAsFunctionCallback(const v8::Arguments&);\n"); |
| } |
| if ($dataNode->name eq "Event") { |
| push(@headerContent, " static v8::Handle<v8::Value> dataTransferAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n"); |
| push(@headerContent, " static void valueAccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value>, const v8::AccessorInfo&);\n"); |
| } |
| if ($dataNode->name eq "Location") { |
| push(@headerContent, " static v8::Handle<v8::Value> assignAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n"); |
| push(@headerContent, " static v8::Handle<v8::Value> reloadAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n"); |
| push(@headerContent, " static v8::Handle<v8::Value> replaceAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n"); |
| } |
| } |
| |
| sub IsConstructable |
| { |
| my $dataNode = shift; |
| |
| return $dataNode->extendedAttributes->{"CustomConstructor"} || $dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"Constructor"} || $dataNode->extendedAttributes->{"ConstructorTemplate"}; |
| } |
| |
| sub IsReadonly |
| { |
| my $attribute = shift; |
| my $attrExt = $attribute->signature->extendedAttributes; |
| return ($attribute->type =~ /readonly/ || $attrExt->{"V8ReadOnly"}) && !$attrExt->{"Replaceable"}; |
| } |
| |
| sub GenerateDomainSafeFunctionGetter |
| { |
| my $function = shift; |
| my $interfaceName = shift; |
| |
| my $v8InterfaceName = "V8" . $interfaceName; |
| my $funcName = $function->signature->name; |
| |
| my $signature = "v8::Signature::New(" . $v8InterfaceName . "::GetRawTemplate())"; |
| if ($function->signature->extendedAttributes->{"V8DoNotCheckSignature"}) { |
| $signature = "v8::Local<v8::Signature>()"; |
| } |
| |
| my $callback = GetFunctionTemplateCallbackName($function, $interfaceName); |
| my $newTemplateString = "v8::FunctionTemplate::New($callback, v8Undefined(), $signature)"; |
| |
| AddToImplIncludes("Frame.h"); |
| push(@implContentDecls, <<END); |
| static v8::Handle<v8::Value> ${funcName}AttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) |
| { |
| INC_STATS(\"DOM.$interfaceName.$funcName._get\"); |
| static v8::Persistent<v8::FunctionTemplate> privateTemplate = v8::Persistent<v8::FunctionTemplate>::New($newTemplateString); |
| v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(${v8InterfaceName}::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(); |
| } |
| ${interfaceName}* imp = ${v8InterfaceName}::toNative(holder); |
| if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame(), DoNotReportSecurityError)) { |
| static v8::Persistent<v8::FunctionTemplate> sharedTemplate = v8::Persistent<v8::FunctionTemplate>::New($newTemplateString); |
| return sharedTemplate->GetFunction(); |
| } |
| |
| v8::Local<v8::Value> hiddenValue = info.This()->GetHiddenValue(name); |
| if (!hiddenValue.IsEmpty()) |
| return hiddenValue; |
| |
| return privateTemplate->GetFunction(); |
| } |
| |
| END |
| } |
| |
| sub GenerateDomainSafeFunctionSetter |
| { |
| my $interfaceName = shift; |
| my $v8InterfaceName = "V8" . $interfaceName; |
| |
| push(@implContentDecls, <<END); |
| static void ${interfaceName}DomainSafeFunctionSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) |
| { |
| INC_STATS("DOM.$interfaceName._set"); |
| v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(${v8InterfaceName}::GetTemplate(), info.This()); |
| if (holder.IsEmpty()) |
| return; |
| ${interfaceName}* imp = ${v8InterfaceName}::toNative(holder); |
| if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame())) |
| return; |
| |
| info.This()->SetHiddenValue(name, value); |
| } |
| |
| END |
| } |
| |
| sub GenerateConstructorGetter |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| push(@implContentDecls, <<END); |
| static v8::Handle<v8::Value> ${interfaceName}ConstructorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) |
| { |
| INC_STATS(\"DOM.$interfaceName.constructors._get\"); |
| v8::Handle<v8::Value> data = info.Data(); |
| ASSERT(data->IsExternal()); |
| V8PerContextData* perContextData = V8PerContextData::from(info.Holder()->CreationContext()); |
| if (!perContextData) |
| return v8Undefined(); |
| return perContextData->constructorForType(WrapperTypeInfo::unwrap(data)); |
| } |
| END |
| } |
| |
| sub GenerateFeatureObservation |
| { |
| my $measureAs = shift; |
| |
| if ($measureAs) { |
| AddToImplIncludes("FeatureObserver.h"); |
| return " FeatureObserver::observe(activeDOMWindow(BindingState::instance()), FeatureObserver::${measureAs});\n"; |
| } |
| |
| return ""; |
| } |
| |
| sub GenerateNormalAttrGetter |
| { |
| my $attribute = shift; |
| my $dataNode = shift; |
| |
| my $interfaceName = $dataNode->name; |
| my $attrExt = $attribute->signature->extendedAttributes; |
| my $attrName = $attribute->signature->name; |
| my $attrType = $attribute->signature->type; |
| $codeGenerator->AssertNotSequenceType($attrType); |
| my $nativeType = GetNativeTypeFromSignature($attribute->signature, -1); |
| |
| my $getterStringUsesImp = $interfaceName ne "SVGNumber"; |
| my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName); |
| |
| # Getter |
| my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); |
| push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString; |
| |
| push(@implContentDecls, <<END); |
| static v8::Handle<v8::Value> ${attrName}AttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) |
| { |
| INC_STATS("DOM.$interfaceName.$attrName._get"); |
| END |
| push(@implContentDecls, GenerateFeatureObservation($attrExt->{"V8MeasureAs"})); |
| |
| if ($svgNativeType) { |
| my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName); |
| if ($svgWrappedNativeType =~ /List/) { |
| push(@implContentDecls, <<END); |
| $svgNativeType* imp = V8${interfaceName}::toNative(info.Holder()); |
| END |
| } else { |
| push(@implContentDecls, <<END); |
| $svgNativeType* wrapper = V8${interfaceName}::toNative(info.Holder()); |
| $svgWrappedNativeType& impInstance = wrapper->propertyReference(); |
| END |
| if ($getterStringUsesImp) { |
| push(@implContentDecls, <<END); |
| $svgWrappedNativeType* imp = &impInstance; |
| END |
| } |
| } |
| } elsif ($attrExt->{"V8OnProto"} || $attrExt->{"V8Unforgeable"}) { |
| if ($interfaceName eq "DOMWindow") { |
| push(@implContentDecls, <<END); |
| v8::Handle<v8::Object> holder = info.Holder(); |
| END |
| } else { |
| # perform lookup first |
| push(@implContentDecls, <<END); |
| v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(V8${interfaceName}::GetTemplate(), info.This()); |
| if (holder.IsEmpty()) |
| return v8Undefined(); |
| END |
| } |
| push(@implContentDecls, <<END); |
| ${interfaceName}* imp = V8${interfaceName}::toNative(holder); |
| END |
| } else { |
| my $reflect = $attribute->signature->extendedAttributes->{"Reflect"}; |
| my $url = $attribute->signature->extendedAttributes->{"URL"}; |
| if ($getterStringUsesImp && $reflect && !$url && $codeGenerator->IsSubType($dataNode, "Node") && $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, " Element* imp = V8Element::toNative(info.Holder());\n"); |
| push(@implContentDecls, " return v8ExternalString(imp->getAttribute(${namespace}::${contentAttributeName}Attr), info.GetIsolate());\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, <<END); |
| v8::Handle<v8::String> propertyName = v8::String::NewSymbol("${attrName}"); |
| v8::Handle<v8::Value> value = info.Holder()->GetHiddenValue(propertyName); |
| if (!value.IsEmpty()) |
| return value; |
| END |
| } |
| if (!$attribute->isStatic) { |
| push(@implContentDecls, <<END); |
| ${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder()); |
| END |
| } |
| } |
| |
| # Generate security checks if necessary |
| if ($attribute->signature->extendedAttributes->{"CheckSecurityForNode"}) { |
| push(@implContentDecls, " if (!BindingSecurity::shouldAllowAccessToNode(BindingState::instance(), imp->" . $attribute->signature->name . "()))\n return v8::Handle<v8::Value>(v8::Null(info.GetIsolate()));\n\n"); |
| } |
| |
| my $useExceptions = 1 if @{$attribute->getterExceptions}; |
| if ($useExceptions) { |
| AddToImplIncludes("ExceptionCode.h"); |
| push(@implContentDecls, " ExceptionCode ec = 0;\n"); |
| } |
| |
| my $returnType = $attribute->signature->type; |
| my $getterString; |
| |
| if ($getterStringUsesImp) { |
| my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); |
| push(@arguments, "ec") if $useExceptions; |
| if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { |
| my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; |
| AddToImplIncludes("${implementedBy}.h"); |
| unshift(@arguments, "imp") if !$attribute->isStatic; |
| $functionName = "${implementedBy}::${functionName}"; |
| } elsif ($attribute->isStatic) { |
| $functionName = "${interfaceName}::${functionName}"; |
| } else { |
| $functionName = "imp->${functionName}"; |
| } |
| unshift(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContentDecls, " ", 0, 0)); |
| $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 v8Undefined();\n"); |
| } |
| |
| if ($useExceptions) { |
| if ($nativeType =~ /^V8Parameter/) { |
| push(@implContentDecls, " " . ConvertToV8Parameter($attribute->signature, $nativeType, "v", $getterString) . ";\n"); |
| } else { |
| push(@implContentDecls, " $nativeType v = $getterString;\n"); |
| } |
| push(@implContentDecls, " if (UNLIKELY(ec))\n"); |
| push(@implContentDecls, " return setDOMException(ec, info.GetIsolate());\n"); |
| |
| if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) { |
| push(@implContentDecls, " if (state.hadException())\n"); |
| push(@implContentDecls, " return throwError(state.exception(), info.GetIsolate());\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<SVGAnimatedEnumeration>($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 (!$codeGenerator->IsSubType($dataNode, "Node") && $attrName ne "self" && IsWrapperType($returnType) && (IsReadonly($attribute) || $attribute->signature->extendedAttributes->{"Replaceable"} || $attrName eq "location") |
| && $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"); |
| push(@implContentDecls, " v8::Handle<v8::Value> wrapper = result.get() ? v8::Handle<v8::Value>(DOMDataStore::current(info.GetIsolate())->get(result.get())) : v8Undefined();\n"); |
| push(@implContentDecls, " if (wrapper.IsEmpty()) {\n"); |
| push(@implContentDecls, " wrapper = toV8(result.get(), info.Holder(), info.GetIsolate());\n"); # FIXME: Could use wrap here since the wrapper is empty. |
| push(@implContentDecls, " if (!wrapper.IsEmpty())\n"); |
| 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($interfaceName) or $interfaceName eq "SVGViewSpec") 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.Holder(), info.GetIsolate());\n"); |
| } elsif ($codeGenerator->IsSVGTypeNeedingTearOff($attrType) and not $interfaceName =~ /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 = "&${interfaceName}::update" . $codeGenerator->WK_ucfirst($getter); |
| |
| my $selfIsTearOffType = $codeGenerator->IsSVGTypeNeedingTearOff($interfaceName); |
| if ($selfIsTearOffType) { |
| AddToImplIncludes("SVGStaticPropertyWithParentTearOff.h"); |
| $tearOffType =~ s/SVGPropertyTearOff</SVGStaticPropertyWithParentTearOff<$interfaceName, /; |
| |
| if ($result =~ /matrix/ and $interfaceName eq "SVGTransform") { |
| # SVGTransform offers a matrix() method for internal usage that returns an AffineTransform |
| # and a svgMatrix() method returning a SVGMatrix, used for the bindings. |
| $result =~ s/matrix/svgMatrix/; |
| } |
| |
| push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create(wrapper, $result, $updateMethod)), info.Holder(), info.GetIsolate());\n"); |
| } else { |
| AddToImplIncludes("SVGStaticPropertyTearOff.h"); |
| $tearOffType =~ s/SVGPropertyTearOff</SVGStaticPropertyTearOff<$interfaceName, /; |
| |
| push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create(imp, $result, $updateMethod)), info.Holder(), info.GetIsolate());\n"); |
| } |
| } elsif ($tearOffType =~ /SVGStaticListPropertyTearOff/) { |
| push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create(imp, $result)), info.Holder(), info.GetIsolate());\n"); |
| } elsif ($tearOffType =~ /SVG(Point|PathSeg)List/) { |
| push(@implContentDecls, " return toV8(WTF::getPtr($result), info.Holder(), info.GetIsolate());\n"); |
| } else { |
| push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create($result)), info.Holder(), info.GetIsolate());\n"); |
| } |
| } elsif ($attribute->signature->type eq "MessagePortArray") { |
| AddToImplIncludes("MessagePort.h"); |
| AddToImplIncludes("V8MessagePort.h"); |
| my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name); |
| push(@implContentDecls, <<END); |
| MessagePortArray* ports = imp->${getterFunc}(); |
| if (!ports) |
| return v8::Array::New(0); |
| MessagePortArray portsCopy(*ports); |
| v8::Local<v8::Array> portArray = v8::Array::New(portsCopy.size()); |
| for (size_t i = 0; i < portsCopy.size(); ++i) |
| portArray->Set(v8Integer(i, info.GetIsolate()), toV8(portsCopy[i].get(), info.Holder(), info.GetIsolate())); |
| return portArray; |
| END |
| } elsif ($attribute->signature->type eq "SerializedScriptValue" && $attrExt->{"CachedAttribute"}) { |
| my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name); |
| push(@implContentDecls, <<END); |
| SerializedScriptValue* serialized = imp->${getterFunc}(); |
| value = serialized ? serialized->deserialize() : v8::Handle<v8::Value>(v8::Null(info.GetIsolate())); |
| info.Holder()->SetHiddenValue(propertyName, value); |
| return value; |
| END |
| } elsif (IsDOMNodeType($interfaceName) && IsDOMNodeType($attrType)) { |
| AddToImplIncludes($attrType.".h"); |
| push(@implContentDecls, " return toV8Fast($result, info, imp);\n"); |
| } else { |
| push(@implContentDecls, " return " . NativeToJSValue($attribute->signature, $result, "info.Holder()", "info.GetIsolate()").";\n"); |
| } |
| |
| push(@implContentDecls, "}\n\n"); # end of getter |
| push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; |
| } |
| |
| sub GenerateReplaceableAttrSetter |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| push(@implContentDecls, <<END); |
| static void ${interfaceName}ReplaceableAttrSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) |
| { |
| INC_STATS("DOM.$interfaceName.replaceable._set"); |
| END |
| push(@implContentDecls, GenerateFeatureObservation($dataNode->extendedAttributes->{"V8MeasureAs"})); |
| |
| if ($interfaceName eq "DOMWindow" || $dataNode->extendedAttributes->{"CheckSecurity"}) { |
| AddToImplIncludes("Frame.h"); |
| push(@implContentDecls, <<END); |
| ${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder()); |
| if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame())) |
| return; |
| END |
| } |
| |
| push(@implContentDecls, <<END); |
| info.This()->ForceSet(name, value); |
| } |
| |
| END |
| } |
| |
| sub GenerateNormalAttrSetter |
| { |
| my $attribute = shift; |
| my $dataNode = shift; |
| |
| my $interfaceName = $dataNode->name; |
| 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<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)\n{\n"); |
| push(@implContentDecls, " INC_STATS(\"DOM.$interfaceName.$attrName._set\");\n"); |
| push(@implContentDecls, GenerateFeatureObservation($attribute->signature->extendedAttributes->{"V8MeasureAs"})); |
| |
| # 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 = $attribute->signature->type; |
| if (IsWrapperType($argType)) { |
| push(@implContentDecls, " if (!isUndefinedOrNull(value) && !V8${argType}::HasInstance(value)) {\n"); |
| push(@implContentDecls, " throwTypeError(0, info.GetIsolate());\n"); |
| push(@implContentDecls, " return;\n"); |
| push(@implContentDecls, " }\n"); |
| } |
| } |
| |
| my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName); |
| if ($svgNativeType) { |
| my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName); |
| if ($svgWrappedNativeType =~ /List$/) { |
| push(@implContentDecls, <<END); |
| $svgNativeType* imp = V8${interfaceName}::toNative(info.Holder()); |
| END |
| } else { |
| AddToImplIncludes("ExceptionCode.h"); |
| push(@implContentDecls, " $svgNativeType* wrapper = V8${interfaceName}::toNative(info.Holder());\n"); |
| push(@implContentDecls, " if (wrapper->isReadOnly()) {\n"); |
| push(@implContentDecls, " 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"}) { |
| push(@implContentDecls, <<END); |
| ${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder()); |
| END |
| } else { |
| my $attrType = $attribute->signature->type; |
| my $reflect = $attribute->signature->extendedAttributes->{"Reflect"}; |
| if ($reflect && $codeGenerator->IsSubType($dataNode, "Node") && $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, " Element* imp = V8Element::toNative(info.Holder());\n"); |
| push(@implContentDecls, " AtomicString v = toWebCoreAtomicStringWithNullCheck(value);\n"); |
| push(@implContentDecls, " imp->setAttribute(${namespace}::${contentAttributeName}Attr, v);\n"); |
| push(@implContentDecls, "}\n\n"); |
| push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; |
| return; |
| # Skip the rest of the function! |
| } |
| |
| if (!$attribute->isStatic) { |
| push(@implContentDecls, <<END); |
| ${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder()); |
| END |
| } |
| } |
| |
| my $nativeType = GetNativeTypeFromSignature($attribute->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 = $attribute->signature->type; |
| 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 ($interfaceName eq "SVGNumber") { |
| push(@implContentDecls, " *imp = $result;\n"); |
| } else { |
| if ($attribute->signature->type eq "EventListener") { |
| my $implSetterFunctionName = $codeGenerator->WK_ucfirst($attrName); |
| AddToImplIncludes("V8AbstractEventListener.h"); |
| if (!$codeGenerator->IsSubType($dataNode, "Node")) { |
| 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<V8WorkerContextErrorHandler>(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<V8WindowErrorHandler>(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, $result); |
| push(@arguments, "ec") if $useExceptions; |
| if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) { |
| my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"}; |
| AddToImplIncludes("${implementedBy}.h"); |
| unshift(@arguments, "imp") if !$attribute->isStatic; |
| $functionName = "${implementedBy}::${functionName}"; |
| } elsif ($attribute->isStatic) { |
| $functionName = "${interfaceName}::${functionName}"; |
| } else { |
| $functionName = "imp->${functionName}"; |
| } |
| unshift(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContentDecls, " ", 1, 0)); |
| push(@implContentDecls, " ${functionName}(" . join(", ", @arguments) . ");\n"); |
| } |
| } |
| |
| if ($useExceptions) { |
| push(@implContentDecls, " if (UNLIKELY(ec))\n"); |
| push(@implContentDecls, " setDOMException(ec, info.GetIsolate());\n"); |
| } |
| |
| if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) { |
| push(@implContentDecls, " if (state.hadException())\n"); |
| push(@implContentDecls, " throwError(state.exception(), info.GetIsolate());\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, <<END); |
| info.Holder()->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 GenerateEventListenerCallback |
| { |
| my $interfaceName = 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, <<END); |
| static v8::Handle<v8::Value> ${functionName}EventListenerCallback(const v8::Arguments& args) |
| { |
| INC_STATS("DOM.${interfaceName}.${functionName}EventListener()"); |
| RefPtr<EventListener> listener = V8DOMWrapper::getEventListener(args[1], false, ListenerFind${lookupType}); |
| if (listener) { |
| V8${interfaceName}::toNative(args.Holder())->${functionName}EventListener(toWebCoreAtomicString(args[0]), listener${passRefPtrHandling}, args[2]->BooleanValue()); |
| END |
| if ($requiresHiddenDependency) { |
| push(@implContentDecls, <<END); |
| ${hiddenDependencyAction}HiddenDependency(args.Holder(), args[1], V8${interfaceName}::eventListenerCacheIndex); |
| END |
| } |
| push(@implContentDecls, <<END); |
| } |
| return v8Undefined(); |
| } |
| |
| END |
| } |
| |
| sub GenerateParametersCheckExpression |
| { |
| my $numParameters = shift; |
| my $function = shift; |
| |
| my @andExpression = (); |
| push(@andExpression, "args.Length() == $numParameters"); |
| my $parameterIndex = 0; |
| foreach my $parameter (@{$function->parameters}) { |
| last if $parameterIndex >= $numParameters; |
| my $value = "args[$parameterIndex]"; |
| my $type = $parameter->type; |
| |
| # Only DOMString or wrapper types are checked. |
| # For DOMString with StrictTypeChecking only Null, Undefined and Object |
| # are accepted for compatibility. Otherwise, no restrictions are made to |
| # match the non-overloaded behavior. |
| # FIXME: Implement WebIDL overload resolution algorithm. |
| if ($codeGenerator->IsStringType($type)) { |
| if ($parameter->extendedAttributes->{"StrictTypeChecking"}) { |
| 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 ($codeGenerator->IsArrayType($type) || $codeGenerator->GetSequenceType($type)) { |
| # FIXME: Add proper support for T[], T[]?, sequence<T>. |
| if ($parameter->isNullable) { |
| push(@andExpression, "(${value}->IsNull() || ${value}->IsArray())"); |
| } else { |
| push(@andExpression, "(${value}->IsArray())"); |
| } |
| } elsif (IsWrapperType($type)) { |
| if ($parameter->isNullable) { |
| push(@andExpression, "(${value}->IsNull() || V8${type}::HasInstance($value))"); |
| } else { |
| push(@andExpression, "(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; |
| my $hasVariadic = 0; |
| my $numMandatoryParams = @{$function->parameters}; |
| foreach my $parameter (@{$function->parameters}) { |
| if ($parameter->extendedAttributes->{"Optional"}) { |
| push(@orExpression, GenerateParametersCheckExpression($numParameters, $function)); |
| $numMandatoryParams--; |
| } |
| if ($parameter->isVariadic) { |
| $hasVariadic = 1; |
| last; |
| } |
| $numParameters++; |
| } |
| if (!$hasVariadic) { |
| push(@orExpression, GenerateParametersCheckExpression($numParameters, $function)); |
| } |
| return ($numMandatoryParams, join(" || ", @orExpression)); |
| } |
| |
| sub GenerateOverloadedFunctionCallback |
| { |
| my $function = shift; |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| # 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); |
| my $leastNumMandatoryParams = 255; |
| push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString; |
| push(@implContentDecls, <<END); |
| static v8::Handle<v8::Value> ${name}Callback(const v8::Arguments& args) |
| { |
| INC_STATS(\"DOM.$interfaceName.$name\"); |
| END |
| push(@implContentDecls, GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"})); |
| |
| foreach my $overload (@{$function->{overloads}}) { |
| my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($overload); |
| $leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams); |
| push(@implContentDecls, " if ($parametersCheck)\n"); |
| push(@implContentDecls, " return ${name}$overload->{overloadIndex}Callback(args);\n"); |
| } |
| if ($leastNumMandatoryParams >= 1) { |
| push(@implContentDecls, " if (args.Length() < $leastNumMandatoryParams)\n"); |
| push(@implContentDecls, " return throwNotEnoughArgumentsError(args.GetIsolate());\n"); |
| } |
| push(@implContentDecls, <<END); |
| return throwTypeError(0, args.GetIsolate()); |
| END |
| push(@implContentDecls, "}\n\n"); |
| push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString; |
| } |
| |
| sub GenerateFunctionCallback |
| { |
| my $function = shift; |
| my $dataNode = shift; |
| |
| my $interfaceName = $dataNode->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($interfaceName, !$codeGenerator->IsSubType($dataNode, "Node"), "add"); |
| return; |
| } elsif ($name eq "removeEventListener") { |
| GenerateEventListenerCallback($interfaceName, !$codeGenerator->IsSubType($dataNode, "Node"), "remove"); |
| return; |
| } |
| |
| my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); |
| push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString; |
| push(@implContentDecls, <<END); |
| static v8::Handle<v8::Value> ${name}Callback(const v8::Arguments& args) |
| { |
| INC_STATS(\"DOM.$interfaceName.$name\"); |
| END |
| push(@implContentDecls, GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"})); |
| |
| push(@implContentDecls, GenerateArgumentsCountCheck($function, $dataNode)); |
| |
| if ($name eq "set" and $dataNode->extendedAttributes->{"TypedArray"}) { |
| AddToImplIncludes("V8ArrayBufferViewCustom.h"); |
| push(@implContentDecls, <<END); |
| return setWebGLArrayHelper<$interfaceName, V8${interfaceName}>(args); |
| } |
| |
| END |
| return; |
| } |
| |
| my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($interfaceName); |
| |
| if ($svgNativeType) { |
| my $nativeClassName = GetNativeType($interfaceName); |
| if ($interfaceName =~ /List$/) { |
| push(@implContentDecls, " $nativeClassName imp = V8${interfaceName}::toNative(args.Holder());\n"); |
| } else { |
| AddToImplIncludes("ExceptionCode.h"); |
| push(@implContentDecls, " $nativeClassName wrapper = V8${interfaceName}::toNative(args.Holder());\n"); |
| push(@implContentDecls, " if (wrapper->isReadOnly())\n"); |
| push(@implContentDecls, " return setDOMException(NO_MODIFICATION_ALLOWED_ERR, args.GetIsolate());\n"); |
| my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName); |
| push(@implContentDecls, " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n"); |
| push(@implContentDecls, " $svgWrappedNativeType* imp = &impInstance;\n"); |
| } |
| } elsif (!$function->isStatic) { |
| push(@implContentDecls, <<END); |
| ${interfaceName}* imp = V8${interfaceName}::toNative(args.Holder()); |
| END |
| } |
| |
| # Check domain security if needed |
| if (($dataNode->extendedAttributes->{"CheckSecurity"} |
| || $interfaceName eq "DOMWindow") |
| && !$function->signature->extendedAttributes->{"DoNotCheckSecurity"}) { |
| # We have not find real use cases yet. |
| AddToImplIncludes("Frame.h"); |
| push(@implContentDecls, <<END); |
| if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame())) |
| return v8Undefined(); |
| 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 (!BindingSecurity::shouldAllowAccessToNode(BindingState::instance(), imp->" . $function->signature->name . "(ec)))\n"); |
| push(@implContentDecls, " return v8::Handle<v8::Value>(v8::Null(args.GetIsolate()));\n"); |
| END |
| } |
| |
| my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interfaceName); |
| push(@implContentDecls, $parameterCheckString); |
| |
| # Build the function call string. |
| push(@implContentDecls, GenerateFunctionCallString($function, $paramIndex, " ", $interfaceName, %replacements)); |
| |
| if ($raisesExceptions) { |
| push(@implContentDecls, " }\n"); |
| push(@implContentDecls, " fail:\n"); |
| push(@implContentDecls, " return setDOMException(ec, args.GetIsolate());\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 ? "" : " v8Undefined()") . ";\n"); |
| push(@callWithArgs, "state"); |
| } |
| } |
| if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptExecutionContext")) { |
| push(@$outputArray, $indent . "ScriptExecutionContext* scriptContext = getScriptExecutionContext();\n"); |
| push(@callWithArgs, "scriptContext"); |
| } |
| if ($function and $codeGenerator->ExtendedAttributeContains($callWith, "ScriptArguments")) { |
| push(@$outputArray, $indent . "RefPtr<ScriptArguments> scriptArguments(createScriptArguments(args, " . @{$function->parameters} . "));\n"); |
| push(@callWithArgs, "scriptArguments.release()"); |
| AddToImplIncludes("ScriptArguments.h"); |
| AddToImplIncludes("ScriptCallStackFactory.h"); |
| } |
| return @callWithArgs; |
| } |
| |
| sub GenerateArgumentsCountCheck |
| { |
| my $function = shift; |
| my $dataNode = shift; |
| |
| my $numMandatoryParams = 0; |
| my $allowNonOptional = 1; |
| foreach my $param (@{$function->parameters}) { |
| if ($param->extendedAttributes->{"Optional"} or $param->isVariadic) { |
| $allowNonOptional = 0; |
| } else { |
| die "An argument must not be declared to be optional unless all subsequent arguments to the operation are also optional." if !$allowNonOptional; |
| $numMandatoryParams++; |
| } |
| } |
| |
| my $argumentsCountCheckString = ""; |
| if ($numMandatoryParams >= 1) { |
| $argumentsCountCheckString .= " if (args.Length() < $numMandatoryParams)\n"; |
| $argumentsCountCheckString .= " return throwNotEnoughArgumentsError(args.GetIsolate());\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 $interfaceName = shift; |
| |
| my $parameterCheckString = ""; |
| my $paramIndex = 0; |
| my @paramTransferListNames = (); |
| my %replacements = (); |
| |
| foreach my $parameter (@{$function->parameters}) { |
| TranslateParameter($parameter); |
| my $nativeType = GetNativeTypeFromSignature($parameter, $paramIndex); |
| |
| # Optional arguments with [Optional] should generate an early call with fewer arguments. |
| # Optional arguments with [Optional=...] should not generate the early call. |
| # Optional Dictionary arguments always considered to have default of empty dictionary. |
| my $optional = $parameter->extendedAttributes->{"Optional"}; |
| if ($optional && $optional ne "DefaultIsUndefined" && $optional ne "DefaultIsNullString" && $nativeType ne "Dictionary" && !$parameter->extendedAttributes->{"Callback"}) { |
| $parameterCheckString .= " if (args.Length() <= $paramIndex) {\n"; |
| my $functionCall = GenerateFunctionCallString($function, $paramIndex, " " x 2, $interfaceName, %replacements); |
| $parameterCheckString .= $functionCall; |
| $parameterCheckString .= " }\n"; |
| } |
| |
| my $parameterDefaultPolicy = "DefaultIsUndefined"; |
| if ($optional and $optional eq "DefaultIsNullString") { |
| $parameterDefaultPolicy = "DefaultIsNullString"; |
| } |
| |
| my $parameterName = $parameter->name; |
| if (GetIndexOf($parameterName, @paramTransferListNames) != -1) { |
| $replacements{$parameterName} = "messagePortArray" . ucfirst($parameterName); |
| $paramIndex++; |
| next; |
| } |
| |
| AddToImplIncludes("ExceptionCode.h"); |
| if ($parameter->extendedAttributes->{"Callback"}) { |
| my $v8InterfaceName = "V8" . $parameter->type; |
| AddToImplIncludes("$v8InterfaceName.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 throwTypeError(0, args.GetIsolate());\n"; |
| $parameterCheckString .= " $parameterName = ${v8InterfaceName}::create(args[$paramIndex], getScriptExecutionContext());\n"; |
| $parameterCheckString .= " }\n"; |
| } else { |
| $parameterCheckString .= " if (args.Length() <= $paramIndex || !args[$paramIndex]->IsFunction())\n"; |
| $parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n"; |
| $parameterCheckString .= " RefPtr<" . $parameter->type . "> $parameterName = ${v8InterfaceName}::create(args[$paramIndex], getScriptExecutionContext());\n"; |
| } |
| } elsif ($parameter->extendedAttributes->{"Clamp"}) { |
| my $nativeValue = "${parameterName}NativeValue"; |
| my $paramType = $parameter->type; |
| $parameterCheckString .= " $paramType $parameterName = 0;\n"; |
| $parameterCheckString .= " EXCEPTION_BLOCK(double, $nativeValue, args[$paramIndex]->NumberValue());\n"; |
| $parameterCheckString .= " if (!isnan($nativeValue))\n"; |
| $parameterCheckString .= " $parameterName = clampTo<$paramType>($nativeValue);\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, args.GetIsolate()))\n"; |
| $parameterCheckString .= " return throwTypeError(\"Could not extract transferables\");\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 v8Undefined();\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 ($parameter->isVariadic) { |
| my $nativeElementType = GetNativeType($parameter->type); |
| if ($nativeElementType =~ />$/) { |
| $nativeElementType .= " "; |
| } |
| |
| my $argType = $parameter->type; |
| if (IsWrapperType($argType)) { |
| $parameterCheckString .= " Vector<$nativeElementType> $parameterName;\n"; |
| $parameterCheckString .= " for (int i = $paramIndex; i < args.Length(); ++i) {\n"; |
| $parameterCheckString .= " if (!V8${argType}::HasInstance(args[i]))\n"; |
| $parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n"; |
| $parameterCheckString .= " $parameterName.append(V8${argType}::toNative(v8::Handle<v8::Object>::Cast(args[i])));\n"; |
| $parameterCheckString .= " }\n"; |
| } else { |
| $parameterCheckString .= " EXCEPTION_BLOCK(Vector<$nativeElementType>, $parameterName, toNativeArguments<$nativeElementType>(args, $paramIndex));\n"; |
| } |
| } elsif ($nativeType =~ /^V8Parameter/) { |
| my $value = JSValueToNative($parameter, "MAYBE_MISSING_PARAMETER(args, $paramIndex, $parameterDefaultPolicy)", "args.GetIsolate()"); |
| $parameterCheckString .= " " . ConvertToV8Parameter($parameter, $nativeType, $parameterName, $value) . "\n"; |
| } else { |
| # 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 = $parameter->type; |
| if (IsWrapperType($argType)) { |
| $parameterCheckString .= " if (args.Length() > $paramIndex && !isUndefinedOrNull($argValue) && !V8${argType}::HasInstance($argValue))\n"; |
| $parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n"; |
| } |
| } |
| $parameterCheckString .= " EXCEPTION_BLOCK($nativeType, $parameterName, " . |
| JSValueToNative($parameter, "MAYBE_MISSING_PARAMETER(args, $paramIndex, $parameterDefaultPolicy)", "args.GetIsolate()") . ");\n"; |
| if ($nativeType eq 'Dictionary') { |
| $parameterCheckString .= " if (!$parameterName.isUndefinedOrNull() && !$parameterName.isObject())\n"; |
| $parameterCheckString .= " return throwTypeError(\"Not an object.\", args.GetIsolate());\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 GenerateOverloadedConstructorCallback |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| push(@implContent, <<END); |
| v8::Handle<v8::Value> V8${interfaceName}::constructorCallback(const v8::Arguments& args) |
| { |
| INC_STATS("DOM.${interfaceName}.Constructor"); |
| if (!args.IsConstructCall()) |
| return throwTypeError("DOM object constructor cannot be called as a function."); |
| |
| if (ConstructorMode::current() == ConstructorMode::WrapExistingObject) |
| return args.Holder(); |
| |
| END |
| |
| my $leastNumMandatoryParams = 255; |
| foreach my $constructor (@{$dataNode->constructors}) { |
| my $name = "constructor" . $constructor->{overloadedIndex} . "Callback"; |
| my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($constructor); |
| $leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams); |
| push(@implContent, " if ($parametersCheck)\n"); |
| push(@implContent, " return $name(args);\n"); |
| } |
| if ($leastNumMandatoryParams >= 1) { |
| push(@implContent, " if (args.Length() < $leastNumMandatoryParams)\n"); |
| push(@implContent, " return throwNotEnoughArgumentsError(args.GetIsolate());\n"); |
| } |
| push(@implContent, <<END); |
| return throwTypeError(0, args.GetIsolate()); |
| END |
| push(@implContent, "}\n\n"); |
| } |
| |
| sub GenerateSingleConstructorCallback |
| { |
| my $dataNode = shift; |
| my $function = shift; |
| |
| my $interfaceName = $dataNode->name; |
| my $overloadedIndexString = ""; |
| if ($function->{overloadedIndex} > 0) { |
| $overloadedIndexString .= $function->{overloadedIndex}; |
| } |
| |
| 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 $maybeObserveFeature = GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"}); |
| |
| my @beforeArgumentList; |
| my @afterArgumentList; |
| push(@implContent, <<END); |
| v8::Handle<v8::Value> V8${interfaceName}::constructor${overloadedIndexString}Callback(const v8::Arguments& args) |
| { |
| INC_STATS("DOM.${interfaceName}.Constructor${overloadedIndexString}"); |
| ${maybeObserveFeature} |
| END |
| |
| if ($function->{overloadedIndex} == 0) { |
| push(@implContent, <<END); |
| if (!args.IsConstructCall()) |
| return throwTypeError("DOM object constructor cannot be called as a function."); |
| |
| 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, $interfaceName); |
| push(@implContent, $parameterCheckString); |
| |
| if ($dataNode->extendedAttributes->{"CallWith"} && $dataNode->extendedAttributes->{"CallWith"} eq "ScriptExecutionContext") { |
| push(@beforeArgumentList, "context"); |
| push(@implContent, <<END); |
| |
| ScriptExecutionContext* context = getScriptExecutionContext(); |
| END |
| } |
| |
| 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<${interfaceName}> impl = ${interfaceName}::create(${argumentString});\n"); |
| push(@implContent, " v8::Handle<v8::Object> wrapper = args.Holder();\n"); |
| |
| if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) { |
| push(@implContent, " if (ec)\n"); |
| push(@implContent, " goto fail;\n"); |
| } |
| |
| push(@implContent, <<END); |
| |
| V8DOMWrapper::createDOMWrapper(impl.release(), &info, wrapper, args.GetIsolate()); |
| return wrapper; |
| END |
| |
| if ($raisesExceptions) { |
| push(@implContent, " fail:\n"); |
| push(@implContent, " return setDOMException(ec, args.GetIsolate());\n"); |
| } |
| |
| push(@implContent, "}\n"); |
| push(@implContent, "\n"); |
| } |
| |
| sub GenerateConstructorCallback |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| if (@{$dataNode->constructors} == 1) { |
| GenerateSingleConstructorCallback($dataNode, @{$dataNode->constructors}[0]); |
| } else { |
| foreach my $constructor (@{$dataNode->constructors}) { |
| GenerateSingleConstructorCallback($dataNode, $constructor); |
| } |
| GenerateOverloadedConstructorCallback($dataNode); |
| } |
| } |
| |
| sub GenerateEventConstructorCallback |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| AddToImplIncludes("Dictionary.h"); |
| push(@implContent, <<END); |
| v8::Handle<v8::Value> V8${interfaceName}::constructorCallback(const v8::Arguments& args) |
| { |
| INC_STATS("DOM.${interfaceName}.Constructor"); |
| |
| if (!args.IsConstructCall()) |
| return throwTypeError("DOM object constructor cannot be called as a function."); |
| |
| if (ConstructorMode::current() == ConstructorMode::WrapExistingObject) |
| return args.Holder(); |
| |
| if (args.Length() < 1) |
| return throwNotEnoughArgumentsError(args.GetIsolate()); |
| |
| STRING_TO_V8PARAMETER_EXCEPTION_BLOCK(V8Parameter<>, type, args[0]); |
| ${interfaceName}Init eventInit; |
| if (args.Length() >= 2) { |
| EXCEPTION_BLOCK(Dictionary, options, Dictionary(args[1], args.GetIsolate())); |
| if (!fill${interfaceName}Init(eventInit, options)) |
| return v8Undefined(); |
| } |
| |
| RefPtr<${interfaceName}> event = ${interfaceName}::create(type, eventInit); |
| |
| v8::Handle<v8::Object> wrapper = args.Holder(); |
| V8DOMWrapper::createDOMWrapper(event.release(), &info, wrapper, args.GetIsolate()); |
| return wrapper; |
| } |
| |
| bool fill${interfaceName}Init(${interfaceName}Init& eventInit, const Dictionary& options) |
| { |
| END |
| |
| foreach my $interfaceBase (@{$dataNode->parents}) { |
| push(@implContent, <<END); |
| if (!fill${interfaceBase}Init(eventInit, options)) |
| return false; |
| |
| END |
| } |
| |
| for (my $index = 0; $index < @{$dataNode->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, <<END); |
| return true; |
| } |
| |
| END |
| } |
| |
| sub GenerateTypedArrayConstructorCallback |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| my $viewType = GetTypeNameOfExternalTypedArray($dataNode); |
| my $type = $dataNode->extendedAttributes->{"TypedArray"}; |
| AddToImplIncludes("V8ArrayBufferViewCustom.h"); |
| |
| push(@implContent, <<END); |
| v8::Handle<v8::Value> V8${interfaceName}::constructorCallback(const v8::Arguments& args) |
| { |
| INC_STATS("DOM.${interfaceName}.Contructor"); |
| return constructWebGLArray<$interfaceName, V8${interfaceName}, $type>(args, &info, $viewType); |
| } |
| |
| END |
| } |
| |
| sub GenerateNamedConstructorCallback |
| { |
| my $function = shift; |
| my $dataNode = shift; |
| |
| my $interfaceName = $dataNode->name; |
| 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 $maybeObserveFeature = GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"}); |
| |
| my @beforeArgumentList; |
| my @afterArgumentList; |
| |
| my $toActiveDOMObject = "0"; |
| if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) { |
| $toActiveDOMObject = "V8${interfaceName}::toActiveDOMObject"; |
| } |
| AddToImplIncludes("Frame.h"); |
| |
| push(@implContent, <<END); |
| WrapperTypeInfo V8${interfaceName}Constructor::info = { V8${interfaceName}Constructor::GetTemplate, V8${interfaceName}::derefObject, ${toActiveDOMObject}, 0, V8${interfaceName}::installPerContextPrototypeProperties, 0, WrapperTypeObjectPrototype }; |
| |
| static v8::Handle<v8::Value> V8${interfaceName}ConstructorCallback(const v8::Arguments& args) |
| { |
| INC_STATS("DOM.${interfaceName}.Constructor"); |
| ${maybeObserveFeature} |
| if (!args.IsConstructCall()) |
| return throwTypeError("DOM object constructor cannot be called as a function."); |
| |
| if (ConstructorMode::current() == ConstructorMode::WrapExistingObject) |
| return args.Holder(); |
| |
| Document* document = currentDocument(BindingState::instance()); |
| |
| // Make sure the document is added to the DOM Node map. Otherwise, the ${interfaceName} instance |
| // may end up being the only node in the map and get garbage-collected prematurely. |
| toV8(document, args.Holder(), 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, $interfaceName); |
| 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<${interfaceName}> impl = ${interfaceName}::createForJSConstructor(${argumentString});\n"); |
| push(@implContent, " v8::Handle<v8::Object> wrapper = args.Holder();\n"); |
| |
| if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) { |
| push(@implContent, " if (ec)\n"); |
| push(@implContent, " goto fail;\n"); |
| } |
| |
| push(@implContent, <<END); |
| |
| V8DOMWrapper::createDOMWrapper(impl.release(), &V8${interfaceName}Constructor::info, wrapper, args.GetIsolate()); |
| return wrapper; |
| END |
| |
| if ($raisesExceptions) { |
| push(@implContent, " fail:\n"); |
| push(@implContent, " return setDOMException(ec, args.GetIsolate());\n"); |
| } |
| |
| push(@implContent, "}\n"); |
| |
| push(@implContent, <<END); |
| |
| v8::Persistent<v8::FunctionTemplate> V8${interfaceName}Constructor::GetTemplate() |
| { |
| static v8::Persistent<v8::FunctionTemplate> cachedTemplate; |
| if (!cachedTemplate.IsEmpty()) |
| return cachedTemplate; |
| |
| v8::HandleScope scope; |
| v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8${interfaceName}ConstructorCallback); |
| |
| v8::Local<v8::ObjectTemplate> instance = result->InstanceTemplate(); |
| instance->SetInternalFieldCount(V8${interfaceName}::internalFieldCount); |
| result->SetClassName(v8::String::NewSymbol("${interfaceName}")); |
| result->Inherit(V8${interfaceName}::GetTemplate()); |
| |
| cachedTemplate = v8::Persistent<v8::FunctionTemplate>::New(result); |
| return cachedTemplate; |
| } |
| |
| END |
| } |
| |
| sub GenerateBatchedAttributeData |
| { |
| my $dataNode = shift; |
| my $attributes = shift; |
| my $interfaceName = $dataNode->name; |
| |
| 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 (!IsReadonly($attribute)) { |
| $accessControl .= " | v8::ALL_CAN_WRITE"; |
| } |
| } |
| if ($attrExt->{"V8Unforgeable"}) { |
| $accessControl .= " | v8::PROHIBITS_OVERWRITING"; |
| } |
| $accessControl = "static_cast<v8::AccessControl>(" . $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 = $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 = "${interfaceName}V8Internal::${interfaceName}ReplaceableAttrSetter"; |
| } else { |
| # Default Getter and Setter |
| $getter = "${interfaceName}V8Internal::${attrName}AttrGetter"; |
| $setter = "${interfaceName}V8Internal::${attrName}AttrSetter"; |
| |
| if ($attrExt->{"Replaceable"}) { |
| $setter = "${interfaceName}V8Internal::${interfaceName}ReplaceableAttrSetter"; |
| } |
| |
| # 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"; |
| } |
| } |
| |
| # Read only attributes |
| if (IsReadonly($attribute)) { |
| $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<v8::PropertyAttribute>($propAttr), $on_proto}" . $delimiter . "\n"); |
| } |
| |
| sub IsStandardFunction |
| { |
| my $dataNode = shift; |
| my $function = shift; |
| |
| my $interfaceName = $dataNode->name; |
| my $attrExt = $function->signature->extendedAttributes; |
| return 0 if $attrExt->{"V8Unforgeable"}; |
| return 0 if $function->isStatic; |
| return 0 if $attrExt->{"V8EnabledAtRuntime"}; |
| return 0 if $attrExt->{"V8EnabledPerContext"}; |
| return 0 if RequiresCustomSignature($function); |
| return 0 if $attrExt->{"V8DoNotCheckSignature"}; |
| return 0 if ($attrExt->{"DoNotCheckSecurity"} && ($dataNode->extendedAttributes->{"CheckSecurity"} || $interfaceName eq "DOMWindow")); |
| return 0 if $attrExt->{"NotEnumerable"}; |
| return 0 if $attrExt->{"V8ReadOnly"}; |
| return 1; |
| } |
| |
| sub GenerateNonStandardFunction |
| { |
| my $dataNode = shift; |
| my $function = shift; |
| |
| my $interfaceName = $dataNode->name; |
| 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->{"V8EnabledPerContext"}) { |
| # Only call Set()/SetAccessor() if this method should be enabled |
| my $enable_function = GetContextEnableFunction($function->signature); |
| $conditional = "if (${enable_function}(impl->document()))\n "; |
| } |
| |
| if ($attrExt->{"DoNotCheckSecurity"} && |
| ($dataNode->extendedAttributes->{"CheckSecurity"} || $interfaceName eq "DOMWindow")) { |
| # Functions that are marked DoNotCheckSecurity are always readable but if they are changed |
| # and then accessed on a different domain we do not return the underlying value but instead |
| # return a new copy of the original function. This is achieved by storing the changed value |
| # as hidden property. |
| push(@implContent, <<END); |
| |
| // $commentInfo |
| ${conditional}$template->SetAccessor(v8::String::NewSymbol("$name"), ${interfaceName}V8Internal::${name}AttrGetter, ${interfaceName}V8Internal::${interfaceName}DomainSafeFunctionSetter, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>($property_attributes)); |
| END |
| return; |
| } |
| |
| my $signature = "defaultSignature"; |
| if ($attrExt->{"V8DoNotCheckSignature"} || $function->isStatic) { |
| $signature = "v8::Local<v8::Signature>()"; |
| } |
| |
| 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<v8::PropertyAttribute>($property_attributes)"; |
| } |
| |
| if ($template eq "proto" && $conditional eq "" && $signature eq "defaultSignature" && $property_attributes eq "") { |
| die "This shouldn't happen: Intraface '$interfaceName' $commentInfo\n"; |
| } |
| |
| my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); |
| push(@implContent, "#if ${conditionalString}\n") if $conditionalString; |
| |
| push(@implContent, " ${conditional}$template->Set(v8::String::NewSymbol(\"$name\"), v8::FunctionTemplate::New($callback, v8Undefined(), ${signature})$property_attributes);\n"); |
| |
| push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; |
| } |
| |
| 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: 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; |
| } |
| } |
| |
| my $hasEnumerator = !$isSpecialCase && $codeGenerator->IsSubType($dataNode, "Node"); |
| |
| # FIXME: Find a way to not have to special-case HTMLOptionsCollection. |
| if ($interfaceName eq "HTMLOptionsCollection") { |
| $hasEnumerator = 1; |
| $hasGetter = 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, <<END); |
| setCollectionStringOrUndefinedIndexedGetter<${interfaceName}>(desc); |
| END |
| } else { |
| push(@implContent, <<END); |
| setCollectionStringIndexedGetter<${interfaceName}>(desc); |
| END |
| } |
| } else { |
| push(@implContent, <<END); |
| setCollectionIndexedGetter<${interfaceName}, ${indexerType}>(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 $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"}; |
| |
| 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, <<END); |
| setCollectionNamedGetter<${interfaceName}, ${type}>(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 GenerateImplementation |
| { |
| my $object = shift; |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($dataNode); |
| my $v8InterfaceName = "V8$interfaceName"; |
| my $nativeType = GetNativeTypeForConversions($dataNode); |
| |
| # - Add default header template |
| push(@implFixedHeader, GenerateImplementationContentHeader($dataNode)); |
| |
| AddToImplIncludes("BindingState.h"); |
| AddToImplIncludes("ContextFeatures.h"); |
| AddToImplIncludes("RuntimeEnabledFeatures.h"); |
| AddToImplIncludes("V8Binding.h"); |
| AddToImplIncludes("V8DOMWrapper.h"); |
| |
| AddIncludesForType($interfaceName); |
| |
| my $toActive = $dataNode->extendedAttributes->{"ActiveDOMObject"} ? "${v8InterfaceName}::toActiveDOMObject" : "0"; |
| my $rootForGC = NeedsCustomOpaqueRootForGC($dataNode) ? "${v8InterfaceName}::opaqueRootForGC" : "0"; |
| |
| # Find the super descriptor. |
| my $parentClass = ""; |
| my $parentClassTemplate = ""; |
| foreach (@{$dataNode->parents}) { |
| my $parent = $_; |
| AddToImplIncludes("V8${parent}.h"); |
| $parentClass = "V8" . $parent; |
| $parentClassTemplate = $parentClass . "::GetTemplate()"; |
| last; |
| } |
| push(@implContentDecls, "namespace WebCore {\n\n"); |
| my $parentClassInfo = $parentClass ? "&${parentClass}::info" : "0"; |
| |
| my $WrapperTypePrototype = $dataNode->isException ? "WrapperTypeErrorPrototype" : "WrapperTypeObjectPrototype"; |
| |
| push(@implContentDecls, "WrapperTypeInfo ${v8InterfaceName}::info = { ${v8InterfaceName}::GetTemplate, ${v8InterfaceName}::derefObject, $toActive, $rootForGC, ${v8InterfaceName}::installPerContextPrototypeProperties, $parentClassInfo, $WrapperTypePrototype };\n\n"); |
| push(@implContentDecls, "namespace ${interfaceName}V8Internal {\n\n"); |
| |
| push(@implContentDecls, "template <typename T> void V8_USE(T) { }\n\n"); |
| |
| my $hasConstructors = 0; |
| my $hasReplaceable = 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); |
| } |
| |
| if ($attribute->signature->extendedAttributes->{"Replaceable"}) { |
| $hasReplaceable = 1; |
| } elsif (!$attribute->signature->extendedAttributes->{"CustomSetter"} && |
| !$attribute->signature->extendedAttributes->{"V8CustomSetter"} && |
| !IsReadonly($attribute)) { |
| GenerateNormalAttrSetter($attribute, $dataNode); |
| } |
| } |
| |
| if ($hasConstructors) { |
| GenerateConstructorGetter($dataNode); |
| } |
| |
| if ($hasConstructors || $hasReplaceable) { |
| GenerateReplaceableAttrSetter($dataNode); |
| } |
| |
| if (NeedsCustomOpaqueRootForGC($dataNode)) { |
| GenerateOpaqueRootForGC($dataNode); |
| } |
| |
| if ($dataNode->extendedAttributes->{"TypedArray"}) { |
| my $viewType = GetTypeNameOfExternalTypedArray($dataNode); |
| push(@implContent, <<END); |
| v8::Handle<v8::Object> wrap($interfaceName* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) |
| { |
| ASSERT(impl); |
| v8::Handle<v8::Object> wrapper = V8${interfaceName}::createWrapper(impl, creationContext, isolate); |
| if (!wrapper.IsEmpty()) |
| wrapper->SetIndexedPropertiesToExternalArrayData(impl->baseAddress(), $viewType, impl->length()); |
| return wrapper; |
| } |
| |
| END |
| } |
| |
| my $indexer; |
| my $namedPropertyGetter; |
| my @enabledPerContextFunctions; |
| my @normalFunctions; |
| my $needsDomainSafeFunctionSetter = 0; |
| # Generate methods for functions. |
| foreach my $function (@{$dataNode->functions}) { |
| my $isCustom = $function->signature->extendedAttributes->{"Custom"} || $function->signature->extendedAttributes->{"V8Custom"}; |
| if (!$isCustom) { |
| GenerateFunctionCallback($function, $dataNode); |
| if ($function->{overloadIndex} > 1 && $function->{overloadIndex} == @{$function->{overloads}}) { |
| GenerateOverloadedFunctionCallback($function, $dataNode); |
| } |
| } |
| |
| 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, $interfaceName); |
| $needsDomainSafeFunctionSetter = 1; |
| } |
| } |
| |
| # Separate out functions that are enabled per context so we can process them specially. |
| if ($function->signature->extendedAttributes->{"V8EnabledPerContext"}) { |
| push(@enabledPerContextFunctions, $function); |
| } else { |
| push(@normalFunctions, $function); |
| } |
| } |
| |
| if ($needsDomainSafeFunctionSetter) { |
| GenerateDomainSafeFunctionSetter($interfaceName); |
| } |
| |
| # 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 @enabledAtRuntimeAttributes; |
| my @enabledPerContextAttributes; |
| my @normalAttributes; |
| foreach my $attribute (@$attributes) { |
| |
| if ($interfaceName eq "DOMWindow" && $attribute->signature->extendedAttributes->{"V8Unforgeable"}) { |
| push(@disallowsShadowing, $attribute); |
| } elsif ($attribute->signature->extendedAttributes->{"V8EnabledAtRuntime"}) { |
| push(@enabledAtRuntimeAttributes, $attribute); |
| } elsif ($attribute->signature->extendedAttributes->{"V8EnabledPerContext"}) { |
| push(@enabledPerContextAttributes, $attribute); |
| } else { |
| push(@normalAttributes, $attribute); |
| } |
| } |
| $attributes = \@normalAttributes; |
| # Put the attributes that disallow shadowing on the shadow object. |
| if (@disallowsShadowing) { |
| push(@implContent, "static const V8DOMConfiguration::BatchedAttribute shadowAttrs[] = {\n"); |
| GenerateBatchedAttributeData($dataNode, \@disallowsShadowing); |
| push(@implContent, "};\n\n"); |
| } |
| |
| my $has_attributes = 0; |
| if (@$attributes) { |
| $has_attributes = 1; |
| push(@implContent, "static const V8DOMConfiguration::BatchedAttribute V8${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 (@normalFunctions) { |
| # Only one table entry is needed for overloaded methods: |
| next if $function->{overloadIndex} > 1; |
| # Don't put any nonstandard functions into this table: |
| next if !IsStandardFunction($dataNode, $function); |
| if (!$has_callbacks) { |
| $has_callbacks = 1; |
| push(@implContent, "static const V8DOMConfiguration::BatchedCallback V8${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, <<END); |
| {"$name", $callback}, |
| END |
| push(@implContent, "#endif\n") if $conditionalString; |
| $num_callbacks++; |
| } |
| push(@implContent, "};\n\n") if $has_callbacks; |
| |
| # Setup constants |
| my $has_constants = 0; |
| my @constantsEnabledAtRuntime; |
| if (@{$dataNode->constants}) { |
| $has_constants = 1; |
| push(@implContent, "static const V8DOMConfiguration::BatchedConstant V8${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, <<END); |
| {"${name}", static_cast<signed int>($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->constructors}[0], $dataNode); |
| } elsif ($dataNode->extendedAttributes->{"Constructor"} && !($dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"CustomConstructor"})) { |
| GenerateConstructorCallback($dataNode); |
| } elsif ($codeGenerator->IsConstructorTemplate($dataNode, "Event")) { |
| GenerateEventConstructorCallback($dataNode); |
| } elsif ($codeGenerator->IsConstructorTemplate($dataNode, "TypedArray")) { |
| GenerateTypedArrayConstructorCallback($dataNode); |
| } |
| |
| my $access_check = ""; |
| if ($dataNode->extendedAttributes->{"CheckSecurity"} && !($interfaceName eq "DOMWindow")) { |
| $access_check = "instance->SetAccessCheckCallbacks(V8${interfaceName}::namedSecurityCheck, V8${interfaceName}::indexedSecurityCheck, v8::External::New(&V8${interfaceName}::info));"; |
| } |
| |
| # For the DOMWindow interface, generate the shadow object template |
| # configuration method. |
| if ($interfaceName eq "DOMWindow") { |
| push(@implContent, <<END); |
| static v8::Persistent<v8::ObjectTemplate> ConfigureShadowObjectTemplate(v8::Persistent<v8::ObjectTemplate> templ) |
| { |
| V8DOMConfiguration::batchConfigureAttributes(templ, v8::Handle<v8::ObjectTemplate>(), shadowAttrs, WTF_ARRAY_LENGTH(shadowAttrs)); |
| |
| // Install a security handler with V8. |
| templ->SetAccessCheckCallbacks(V8DOMWindow::namedSecurityCheck, V8DOMWindow::indexedSecurityCheck, v8::External::New(&V8DOMWindow::info)); |
| templ->SetInternalFieldCount(V8DOMWindow::internalFieldCount); |
| return templ; |
| } |
| END |
| } |
| |
| if (!$parentClassTemplate) { |
| $parentClassTemplate = "v8::Persistent<v8::FunctionTemplate>()"; |
| } |
| |
| # Generate the template configuration method |
| push(@implContent, <<END); |
| static v8::Persistent<v8::FunctionTemplate> Configure${v8InterfaceName}Template(v8::Persistent<v8::FunctionTemplate> desc) |
| { |
| desc->ReadOnlyPrototype(); |
| |
| v8::Local<v8::Signature> defaultSignature; |
| END |
| if ($dataNode->extendedAttributes->{"V8EnabledAtRuntime"}) { |
| my $enable_function = GetRuntimeEnableFunctionName($dataNode); |
| push(@implContent, <<END); |
| if (!${enable_function}()) |
| defaultSignature = V8DOMConfiguration::configureTemplate(desc, \"\", $parentClassTemplate, V8${interfaceName}::internalFieldCount, 0, 0, 0, 0); |
| else |
| END |
| } |
| push(@implContent, <<END); |
| defaultSignature = V8DOMConfiguration::configureTemplate(desc, \"${visibleInterfaceName}\", $parentClassTemplate, V8${interfaceName}::internalFieldCount, |
| END |
| # Set up our attributes if we have them |
| if ($has_attributes) { |
| push(@implContent, <<END); |
| V8${interfaceName}Attrs, WTF_ARRAY_LENGTH(V8${interfaceName}Attrs), |
| END |
| } else { |
| push(@implContent, <<END); |
| 0, 0, |
| END |
| } |
| |
| if ($has_callbacks) { |
| push(@implContent, <<END); |
| V8${interfaceName}Callbacks, WTF_ARRAY_LENGTH(V8${interfaceName}Callbacks)); |
| END |
| } else { |
| push(@implContent, <<END); |
| 0, 0); |
| END |
| } |
| |
| AddToImplIncludes("wtf/UnusedParam.h"); |
| push(@implContent, <<END); |
| UNUSED_PARAM(defaultSignature); // In some cases, it will not be used. |
| END |
| |
| if (IsConstructable($dataNode)) { |
| push(@implContent, <<END); |
| desc->SetCallHandler(V8${interfaceName}::constructorCallback); |
| END |
| } |
| |
| if ($access_check or @enabledAtRuntimeAttributes or @normalFunctions or $has_constants) { |
| push(@implContent, <<END); |
| v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate(); |
| v8::Local<v8::ObjectTemplate> 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 (@enabledAtRuntimeAttributes) { |
| 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 V8DOMConfiguration::BatchedAttribute attrData =\\\n"); |
| GenerateSingleBatchedAttribute($interfaceName, $runtime_attr, ";", " "); |
| push(@implContent, <<END); |
| V8DOMConfiguration::configureAttribute(instance, proto, attrData); |
| } |
| END |
| push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; |
| } |
| |
| # Setup the enable-at-runtime constants if we have them |
| foreach my $runtime_const (@constantsEnabledAtRuntime) { |
| my $enable_function = GetRuntimeEnableFunctionName($runtime_const); |
| my $conditionalString = $codeGenerator->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, <<END); |
| static const V8DOMConfiguration::BatchedConstant constData = {"${name}", static_cast<signed int>(${value})}; |
| V8DOMConfiguration::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 (@normalFunctions) { |
| # Only one accessor is needed for overloaded methods: |
| next if $function->{overloadIndex} > 1; |
| |
| $total_functions++; |
| next if IsStandardFunction($dataNode, $function); |
| GenerateNonStandardFunction($dataNode, $function); |
| $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, <<END); |
| V8DOMConfiguration::batchConfigureConstants(desc, proto, V8${interfaceName}Consts, WTF_ARRAY_LENGTH(V8${interfaceName}Consts)); |
| END |
| } |
| |
| # Special cases |
| if ($interfaceName eq "DOMWindow") { |
| push(@implContent, <<END); |
| |
| proto->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::New(&V8DOMWindow::info), false); |
| END |
| } |
| if ($interfaceName eq "HTMLDocument") { |
| push(@implContent, <<END); |
| desc->SetHiddenPrototype(true); |
| END |
| } |
| if ($interfaceName eq "Location") { |
| push(@implContent, <<END); |
| |
| // For security reasons, these functions are on the instance instead |
| // of on the prototype object to ensure that they cannot be overwritten. |
| instance->SetAccessor(v8::String::NewSymbol("reload"), V8Location::reloadAccessorGetter, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly)); |
| instance->SetAccessor(v8::String::NewSymbol("replace"), V8Location::replaceAccessorGetter, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly)); |
| instance->SetAccessor(v8::String::NewSymbol("assign"), V8Location::assignAccessorGetter, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly)); |
| END |
| } |
| |
| push(@implContent, <<END); |
| |
| // Custom toString template |
| desc->Set(v8::String::NewSymbol("toString"), V8PerIsolateData::current()->toStringTemplate()); |
| return desc; |
| } |
| |
| v8::Persistent<v8::FunctionTemplate> ${v8InterfaceName}::GetRawTemplate() |
| { |
| V8PerIsolateData* data = V8PerIsolateData::current(); |
| V8PerIsolateData::TemplateMap::iterator result = data->rawTemplateMap().find(&info); |
| if (result != data->rawTemplateMap().end()) |
| return result->value; |
| |
| v8::HandleScope handleScope; |
| v8::Persistent<v8::FunctionTemplate> templ = createRawTemplate(); |
| data->rawTemplateMap().add(&info, templ); |
| return templ; |
| } |
| |
| v8::Persistent<v8::FunctionTemplate> ${v8InterfaceName}::GetTemplate() |
| { |
| V8PerIsolateData* data = V8PerIsolateData::current(); |
| V8PerIsolateData::TemplateMap::iterator result = data->templateMap().find(&info); |
| if (result != data->templateMap().end()) |
| return result->value; |
| |
| v8::HandleScope handleScope; |
| v8::Persistent<v8::FunctionTemplate> templ = |
| Configure${v8InterfaceName}Template(GetRawTemplate()); |
| data->templateMap().add(&info, templ); |
| return templ; |
| } |
| |
| bool ${v8InterfaceName}::HasInstance(v8::Handle<v8::Value> value) |
| { |
| return GetRawTemplate()->HasInstance(value); |
| } |
| |
| END |
| |
| if (@enabledPerContextAttributes) { |
| push(@implContent, <<END); |
| void ${v8InterfaceName}::installPerContextProperties(v8::Handle<v8::Object> instance, ${nativeType}* impl) |
| { |
| v8::Local<v8::Object> proto = v8::Local<v8::Object>::Cast(instance->GetPrototype()); |
| // When building QtWebkit with V8 this variable is unused when none of the features are enabled. |
| UNUSED_PARAM(proto); |
| END |
| |
| # Setup the enable-by-settings attrs if we have them |
| foreach my $runtimeAttr (@enabledPerContextAttributes) { |
| my $enableFunction = GetContextEnableFunction($runtimeAttr->signature); |
| my $conditionalString = $codeGenerator->GenerateConditionalString($runtimeAttr->signature); |
| push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString; |
| push(@implContent, " if (${enableFunction}(impl->document())) {\n"); |
| push(@implContent, " static const V8DOMConfiguration::BatchedAttribute attrData =\\\n"); |
| GenerateSingleBatchedAttribute($interfaceName, $runtimeAttr, ";", " "); |
| push(@implContent, <<END); |
| V8DOMConfiguration::configureAttribute(instance, proto, attrData); |
| END |
| push(@implContent, " }\n"); |
| push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; |
| } |
| push(@implContent, <<END); |
| } |
| END |
| } |
| |
| if (@enabledPerContextFunctions) { |
| push(@implContent, <<END); |
| void ${v8InterfaceName}::installPerContextPrototypeProperties(v8::Handle<v8::Object> proto) |
| { |
| UNUSED_PARAM(proto); |
| END |
| # Setup the enable-by-settings functions if we have them |
| push(@implContent, <<END); |
| v8::Local<v8::Signature> defaultSignature = v8::Signature::New(GetTemplate()); |
| UNUSED_PARAM(defaultSignature); // In some cases, it will not be used. |
| |
| ScriptExecutionContext* context = toScriptExecutionContext(proto->CreationContext()); |
| END |
| |
| foreach my $runtimeFunc (@enabledPerContextFunctions) { |
| my $enableFunction = GetContextEnableFunction($runtimeFunc->signature); |
| my $conditionalString = $codeGenerator->GenerateConditionalString($runtimeFunc->signature); |
| push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString; |
| push(@implContent, " if (context && context->isDocument() && ${enableFunction}(static_cast<Document*>(context))) {\n"); |
| my $name = $runtimeFunc->signature->name; |
| my $callback = GetFunctionTemplateCallbackName($runtimeFunc, $interfaceName); |
| push(@implContent, <<END); |
| proto->Set(v8::String::NewSymbol("${name}"), v8::FunctionTemplate::New(${callback}, v8Undefined(), defaultSignature)->GetFunction()); |
| END |
| push(@implContent, " }\n"); |
| push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; |
| } |
| |
| push(@implContent, <<END); |
| } |
| 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, <<END); |
| ActiveDOMObject* ${v8InterfaceName}::toActiveDOMObject(v8::Handle<v8::Object> object) |
| { |
| return ${returnValue}; |
| } |
| END |
| } |
| |
| if ($interfaceName eq "DOMWindow") { |
| push(@implContent, <<END); |
| v8::Persistent<v8::ObjectTemplate> V8DOMWindow::GetShadowObjectTemplate() |
| { |
| static v8::Persistent<v8::ObjectTemplate> V8DOMWindowShadowObjectCache; |
| if (V8DOMWindowShadowObjectCache.IsEmpty()) { |
| V8DOMWindowShadowObjectCache = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New()); |
| ConfigureShadowObjectTemplate(V8DOMWindowShadowObjectCache); |
| } |
| return V8DOMWindowShadowObjectCache; |
| } |
| END |
| } |
| |
| GenerateToV8Converters($dataNode, $v8InterfaceName, $nativeType); |
| |
| push(@implContent, <<END); |
| |
| void ${v8InterfaceName}::derefObject(void* object) |
| { |
| static_cast<${nativeType}*>(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{"${v8InterfaceName}.h"}; |
| } |
| |
| sub GenerateHeaderContentHeader |
| { |
| my $dataNode = shift; |
| my $v8InterfaceName = "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 ${v8InterfaceName}" . "_h"); |
| push(@headerContentHeader, "\n#define ${v8InterfaceName}" . "_h\n\n"); |
| return @headerContentHeader; |
| } |
| |
| sub GenerateImplementationContentHeader |
| { |
| my $dataNode = shift; |
| my $v8InterfaceName = "V8" . $dataNode->name; |
| my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode); |
| |
| my @implContentHeader = split("\r", $headerTemplate); |
| |
| push(@implContentHeader, "\n#include \"config.h\"\n"); |
| push(@implContentHeader, "#include \"${v8InterfaceName}.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 $v8InterfaceName = "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 <v8.h>"); |
| push(@unsortedIncludes, "#include <wtf/Forward.h>"); |
| push(@headerContent, join("\n", sort @unsortedIncludes)); |
| |
| push(@headerContent, "\n\nnamespace WebCore {\n\n"); |
| push(@headerContent, "class ScriptExecutionContext;\n\n"); |
| push(@headerContent, "class $v8InterfaceName : public $interfaceName, public ActiveDOMCallback {\n"); |
| |
| push(@headerContent, <<END); |
| public: |
| static PassRefPtr<${v8InterfaceName}> create(v8::Local<v8::Value> value, ScriptExecutionContext* context) |
| { |
| ASSERT(value->IsObject()); |
| ASSERT(context); |
| return adoptRef(new ${v8InterfaceName}(value->ToObject(), context)); |
| } |
| |
| virtual ~${v8InterfaceName}(); |
| |
| 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, <<END); |
| |
| private: |
| ${v8InterfaceName}(v8::Local<v8::Object>, ScriptExecutionContext*); |
| |
| v8::Persistent<v8::Object> m_callback; |
| WorldContextHandle m_worldContext; |
| }; |
| |
| END |
| |
| push(@headerContent, "}\n\n"); |
| push(@headerContent, "#endif // $v8InterfaceName" . "_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 $v8InterfaceName = "V8$interfaceName"; |
| |
| # - Add default header template |
| push(@implFixedHeader, GenerateImplementationContentHeader($dataNode)); |
| |
| AddToImplIncludes("ScriptExecutionContext.h"); |
| AddToImplIncludes("V8Binding.h"); |
| AddToImplIncludes("V8Callback.h"); |
| |
| push(@implContent, "#include <wtf/Assertions.h>\n\n"); |
| push(@implContent, "namespace WebCore {\n\n"); |
| push(@implContent, <<END); |
| ${v8InterfaceName}::${v8InterfaceName}(v8::Local<v8::Object> callback, ScriptExecutionContext* context) |
| : ActiveDOMCallback(context) |
| , m_callback(v8::Persistent<v8::Object>::New(callback)) |
| , m_worldContext(UseCurrentWorld) |
| { |
| } |
| |
| ${v8InterfaceName}::~${v8InterfaceName}() |
| { |
| 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) . " ${v8InterfaceName}::" . $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, <<END); |
| ASSERT(${paramName}); |
| |
| END |
| } |
| } |
| push(@implContent, join(", ", @args)); |
| |
| push(@implContent, ")\n"); |
| push(@implContent, "{\n"); |
| push(@implContent, @argsCheck) if @argsCheck; |
| push(@implContent, " if (!canInvokeCallback())\n"); |
| push(@implContent, " return true;\n\n"); |
| push(@implContent, " v8::HandleScope handleScope;\n\n"); |
| push(@implContent, " v8::Handle<v8::Context> 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<v8::Value> ${paramName}Handle = " . NativeToJSValue($param, $paramName) . ";\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<v8::Value> argv[] = {\n"); |
| push(@implContent, join(",\n", @args)); |
| push(@implContent, "\n };\n\n"); |
| } else { |
| push(@implContent, "\n v8::Handle<v8::Value> *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<v8::Object>::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 BaseInterfaceName |
| { |
| my $dataNode = shift; |
| |
| while (@{$dataNode->parents}) { |
| $dataNode = $codeGenerator->ParseInterface(@{$dataNode->parents}[0], 1); |
| } |
| |
| return $dataNode->name; |
| } |
| |
| sub GenerateToV8Converters |
| { |
| my $dataNode = shift; |
| my $v8InterfaceName = shift; |
| my $nativeType = shift; |
| my $interfaceName = $dataNode->name; |
| |
| if ($dataNode->extendedAttributes->{"V8NoWrapperCache"} || $dataNode->extendedAttributes->{"SuppressToJSObject"}) { |
| return; |
| } |
| |
| my $createWrapperArgumentType = GetPassRefPtrType($nativeType); |
| my $baseType = BaseInterfaceName($dataNode); |
| my $getCachedWrapper = $codeGenerator->IsSubType($dataNode, "Node") ? "V8DOMWrapper::getCachedWrapper(impl.get())" : "DOMDataStore::current(isolate)->get(impl.get())"; |
| |
| push(@implContent, <<END); |
| |
| v8::Handle<v8::Object> ${v8InterfaceName}::createWrapper(${createWrapperArgumentType} impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) |
| { |
| ASSERT(impl.get()); |
| ASSERT($getCachedWrapper.IsEmpty()); |
| END |
| if ($baseType ne $interfaceName) { |
| push(@implContent, <<END); |
| ASSERT(static_cast<void*>(static_cast<${baseType}*>(impl.get())) == static_cast<void*>(impl.get())); |
| END |
| } |
| |
| AddToImplIncludes("Frame.h"); |
| |
| if ($codeGenerator->IsSubType($dataNode, "Document")) { |
| push(@implContent, <<END); |
| if (Frame* frame = impl->frame()) { |
| if (frame->script()->initializeMainWorld()) { |
| // initializeMainWorld may have created a wrapper for the object, retry from the start. |
| v8::Handle<v8::Object> wrapper = V8DOMWrapper::getCachedWrapper(impl.get()); |
| if (!wrapper.IsEmpty()) |
| return wrapper; |
| } |
| } |
| END |
| } |
| |
| push(@implContent, <<END); |
| |
| v8::Handle<v8::Object> wrapper = V8DOMWrapper::instantiateV8Object(creationContext, &info, impl.get()); |
| if (UNLIKELY(wrapper.IsEmpty())) |
| return wrapper; |
| |
| installPerContextProperties(wrapper, impl.get()); |
| v8::Persistent<v8::Object> wrapperHandle = V8DOMWrapper::createDOMWrapper(impl, &info, wrapper, isolate); |
| if (!hasDependentLifetime) |
| wrapperHandle.MarkIndependent(); |
| return wrapper; |
| } |
| END |
| } |
| |
| sub GetNativeTypeForConversions |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| |
| $interfaceName = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName) if $codeGenerator->IsSVGTypeNeedingTearOff($interfaceName); |
| return $interfaceName;; |
| } |
| |
| sub GenerateFunctionCallString() |
| { |
| my $function = shift; |
| my $numberOfParameters = shift; |
| my $indent = shift; |
| my $interfaceName = shift; |
| my %replacements = @_; |
| |
| my $name = $function->signature->name; |
| my $returnType = $function->signature->type; |
| my $nativeReturnType = GetNativeType($returnType, 0); |
| my $result = ""; |
| |
| my $isSVGTearOffType = ($codeGenerator->IsSVGTypeNeedingTearOff($returnType) and not $interfaceName =~ /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 = "${interfaceName}::${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); |
| unshift(@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 $interfaceName =~ /List$/) { |
| push @arguments, "$paramName->propertyReference()"; |
| $result .= $indent . "if (!$paramName)\n"; |
| $result .= $indent . " return setDOMException(WebCore::TYPE_MISMATCH_ERR, args.GetIsolate());\n"; |
| } elsif ($parameter->type eq "SVGMatrix" and $interfaceName 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 ($interfaceName 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(), args.GetIsolate());\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.Holder(), args.GetIsolate());\n"; |
| return $result; |
| } |
| |
| # If the implementing class is a POD type, commit changes |
| if ($codeGenerator->IsSVGTypeNeedingTearOff($interfaceName) and not $interfaceName =~ /List$/) { |
| $result .= $indent . "wrapper->commitChange();\n"; |
| } |
| |
| $return .= ".release()" if ($returnIsRef); |
| $result .= $indent . "return " . NativeToJSValue($function->signature, $return, "args.Holder()", "args.GetIsolate()") . ";\n"; |
| |
| return $result; |
| } |
| |
| sub GetNativeTypeFromSignature |
| { |
| my $signature = shift; |
| my $parameterIndex = shift; |
| |
| my $type = $signature->type; |
| |
| 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 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} >"; |
| } |
| } |
| |
| my $sequenceType = $codeGenerator->GetSequenceType($type); |
| return "Vector<${sequenceType}>" if $sequenceType; |
| |
| 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"; |
| # FIXME: When EventTarget is an interface and not a mixin, fix this so that |
| # EventTarget can be passed as a parameter. |
| return "Node*" if $type eq "EventTarget" and $isParameter; |
| return "double" if $type eq "Date"; |
| return "ScriptValue" if $type eq "DOMObject" or $type eq "any"; |
| return "Dictionary" if $type eq "Dictionary"; |
| |
| return "String" if $type eq "DOMUserData"; # FIXME: Temporary hack? |
| |
| # temporary hack |
| return "RefPtr<NodeFilter>" if $type eq "NodeFilter"; |
| |
| return "RefPtr<SerializedScriptValue>" if $type eq "SerializedScriptValue"; |
| |
| return "RefPtr<IDBKey>" if $type eq "IDBKey"; |
| |
| # necessary as resolvers could be constructed on fly. |
| return "RefPtr<XPathNSResolver>" if $type eq "XPathNSResolver"; |
| |
| return "RefPtr<DOMStringList>" if $type eq "DOMString[]"; |
| return "RefPtr<${type}>" if IsRefPtrType($type) and not $isParameter; |
| |
| return "RefPtr<MediaQueryListListener>" if $type eq "MediaQueryListListener"; |
| |
| # FIXME: Support T[], T[]?, sequence<T> generically |
| return "RefPtr<DOMStringList>" if $type eq "DOMStringList"; |
| |
| # 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 = $signature->type; |
| |
| AddToImplIncludes("ExceptionCode.h") if $type eq "Attr"; |
| return 1 if $type eq "Attr"; |
| return 0; |
| } |
| |
| sub JSValueToNative |
| { |
| my $signature = shift; |
| my $value = shift; |
| my $getIsolate = shift; |
| |
| my $type = $signature->type; |
| |
| return "$value" if $type eq "JSObject"; |
| return "$value->BooleanValue()" if $type eq "boolean"; |
| return "static_cast<$type>($value->NumberValue())" if $type eq "float" or $type eq "double"; |
| |
| return "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<Range::CompareHow>($value->Int32Value())" if $type eq "CompareHow"; |
| return "toWebCoreDate($value)" if $type eq "Date"; |
| return "toDOMStringList($value)" if $type eq "DOMStringList"; |
| # FIXME: Add proper support for T[], T[]? and sequence<T>. |
| return "toDOMStringList($value)" if $type eq "DOMString[]"; |
| |
| 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 "Dictionary($value, $getIsolate)"; |
| } |
| |
| if ($type eq "DOMObject" or $type eq "any") { |
| 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 |
| # FIXME: When EventTarget is an interface and not a mixin, fix this to use |
| # V8EventTarget::HasInstance etc. |
| 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<v8::Object>::Cast($value)) : 0"; |
| } |
| |
| if ($type eq "XPathNSResolver") { |
| return "toXPathNSResolver($value)"; |
| } |
| |
| my $arrayType = $codeGenerator->GetArrayType($type); |
| if ($arrayType) { |
| return "toNativeArray<$arrayType>($value)"; |
| } |
| |
| my $sequenceType = $codeGenerator->GetSequenceType($type); |
| if ($sequenceType) { |
| return "toNativeArray<$sequenceType>($value)"; |
| } |
| |
| AddIncludesForType($type); |
| |
| AddToImplIncludes("V8${type}.h"); |
| return "V8${type}::HasInstance($value) ? V8${type}::toNative(v8::Handle<v8::Object>::Cast($value)) : 0"; |
| } |
| |
| sub GetV8HeaderName |
| { |
| my $type = shift; |
| return "V8Event.h" if $type eq "DOMTimeStamp"; |
| return "EventListener.h" if $type eq "EventListener"; |
| return "SerializedScriptValue.h" if $type eq "SerializedScriptValue"; |
| return "ScriptValue.h" if $type eq "DOMObject" or $type eq "any"; |
| 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<v8::FunctionTemplate> ${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<v8::FunctionTemplate>()"; |
| } else { |
| my $type = $parameter->type; |
| my $sequenceType = $codeGenerator->GetSequenceType($type); |
| if ($sequenceType) { |
| if ($codeGenerator->SkipIncludeHeader($sequenceType)) { |
| $result .= "v8::Handle<v8::FunctionTemplate>()"; |
| next; |
| } |
| AddToImplIncludes("$sequenceType.h"); |
| } else { |
| my $header = GetV8HeaderName($type); |
| AddToImplIncludes($header); |
| } |
| $result .= "V8${type}::GetRawTemplate()"; |
| } |
| } else { |
| $result .= "v8::Handle<v8::FunctionTemplate>()"; |
| } |
| } |
| $result .= " };\n"; |
| $result .= " v8::Handle<v8::Signature> ${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; |
| } |
| |
| |
| my %non_wrapper_types = ( |
| 'CompareHow' => 1, |
| 'DOMObject' => 1, |
| 'DOMString' => 1, |
| 'DOMString[]' => 1, |
| 'DOMTimeStamp' => 1, |
| 'Date' => 1, |
| 'Dictionary' => 1, |
| 'EventListener' => 1, |
| # FIXME: When EventTarget is an interface and not a mixin, fix this so that |
| # EventTarget is treated as a wrapper type. |
| 'EventTarget' => 1, |
| 'IDBKey' => 1, |
| 'JSObject' => 1, |
| 'MediaQueryListListener' => 1, |
| 'NodeFilter' => 1, |
| 'SerializedScriptValue' => 1, |
| 'any' => 1, |
| 'boolean' => 1, |
| 'double' => 1, |
| 'float' => 1, |
| 'int' => 1, |
| 'long long' => 1, |
| 'long' => 1, |
| 'short' => 1, |
| 'unsigned int' => 1, |
| 'unsigned long long' => 1, |
| 'unsigned long' => 1, |
| 'unsigned short' => 1 |
| ); |
| |
| |
| sub IsWrapperType |
| { |
| my $type = shift; |
| return !($non_wrapper_types{$type}); |
| } |
| |
| sub GetTypeNameOfExternalTypedArray |
| { |
| my $dataNode = shift; |
| my $interfaceName = $dataNode->name; |
| my $viewType = $dataNode->extendedAttributes->{"TypedArray"}; |
| return "v8::kExternalByteArray" if $viewType eq "signed char" and $interfaceName eq "Int8Array"; |
| return "v8::kExternalPixelArray" if $viewType eq "unsigned char" and $interfaceName eq "Uint8ClampedArray"; |
| return "v8::kExternalUnsignedByteArray" if $viewType eq "unsigned char" and $interfaceName eq "Uint8Array"; |
| return "v8::kExternalShortArray" if $viewType eq "short" and $interfaceName eq "Int16Array"; |
| return "v8::kExternalUnsignedShortArray" if $viewType eq "unsigned short" and $interfaceName eq "Uint16Array"; |
| return "v8::kExternalIntArray" if $viewType eq "int" and $interfaceName eq "Int32Array"; |
| return "v8::kExternalUnsignedIntArray" if $viewType eq "unsigned int" and $interfaceName eq "Uint32Array"; |
| return "v8::kExternalFloatArray" if $viewType eq "float" and $interfaceName eq "Float32Array"; |
| return "v8::kExternalDoubleArray" if $viewType eq "double" and $interfaceName eq "Float64Array"; |
| |
| die "TypedArray of unknown type is found"; |
| } |
| |
| sub IsDOMNodeType |
| { |
| my $type = shift; |
| |
| return 1 if $type eq 'Attr'; |
| return 1 if $type eq 'CDATASection'; |
| return 1 if $type eq 'CharacterData'; |
| 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 'Entity'; |
| return 1 if $type eq 'EntityReference'; |
| return 1 if $type eq 'HTMLDocument'; |
| return 1 if $type eq 'Node'; |
| return 1 if $type eq 'Notation'; |
| return 1 if $type eq 'ProcessingInstruction'; |
| return 1 if $type eq 'ShadowRoot'; |
| return 1 if $type eq 'SVGDocument'; |
| return 1 if $type eq 'Text'; |
| |
| return 1 if $type =~ /^HTML.*Element$/; |
| return 1 if $type =~ /^SVG.*Element$/; |
| |
| return 1 if $type eq 'TestNode'; |
| |
| return 0; |
| } |
| |
| |
| sub NativeToJSValue |
| { |
| my $signature = shift; |
| my $value = shift; |
| my $getCreationContext = shift; |
| my $getCreationContextArg = $getCreationContext ? ", $getCreationContext" : ""; |
| my $getIsolate = shift; |
| my $getIsolateArg = $getIsolate ? ", $getIsolate" : ""; |
| my $type = $signature->type; |
| |
| return ($getIsolate ? "v8Boolean($value, $getIsolate)" : "v8Boolean($value)") if $type eq "boolean"; |
| return "v8Undefined()" if $type eq "void"; # equivalent to v8Undefined() |
| |
| # 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 "v8UnsignedInteger(std::max(0, " . $value . ")$getIsolateArg)"; |
| } |
| |
| # For all the types where we use 'int' as the representation type, |
| # we use v8Integer() which has a fast small integer conversion check. |
| my $nativeType = GetNativeType($type); |
| return "v8Integer($value$getIsolateArg)" if $nativeType eq "int"; |
| return "v8UnsignedInteger($value$getIsolateArg)" if $nativeType eq "unsigned"; |
| |
| return "v8DateOrNull($value$getIsolateArg)" if $type eq "Date"; |
| # long long and unsigned long long are not representable in ECMAScript. |
| return "v8::Number::New(static_cast<double>($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"; |
| |
| if ($codeGenerator->IsStringType($type)) { |
| my $conv = $signature->extendedAttributes->{"TreatReturnedNullStringAs"}; |
| if (defined $conv) { |
| return "v8StringOrNull($value$getIsolateArg)" if $conv eq "Null"; |
| return "v8StringOrUndefined($value$getIsolateArg)" if $conv eq "Undefined"; |
| |
| die "Unknown value for TreatReturnedNullStringAs extended attribute"; |
| } |
| return "v8String($value$getIsolateArg)"; |
| } |
| |
| my $arrayType = $codeGenerator->GetArrayType($type); |
| if ($arrayType) { |
| if ($type eq "DOMString[]") { |
| AddToImplIncludes("V8DOMStringList.h"); |
| AddToImplIncludes("DOMStringList.h"); |
| } elsif (!$codeGenerator->SkipIncludeHeader($arrayType)) { |
| AddToImplIncludes("V8$arrayType.h"); |
| AddToImplIncludes("$arrayType.h"); |
| } |
| return "v8Array($value$getIsolateArg)"; |
| } |
| |
| my $sequenceType = $codeGenerator->GetSequenceType($type); |
| if ($sequenceType) { |
| if (!$codeGenerator->SkipIncludeHeader($sequenceType)) { |
| AddToImplIncludes("V8$sequenceType.h"); |
| AddToImplIncludes("$sequenceType.h"); |
| } |
| return "v8Array($value$getIsolateArg)"; |
| } |
| |
| AddIncludesForType($type); |
| |
| if (IsDOMNodeType($type)) { |
| return "toV8($value$getCreationContextArg$getIsolateArg)"; |
| } |
| |
| if ($type eq "EventTarget") { |
| return "V8DOMWrapper::convertEventTargetToV8Object($value$getCreationContextArg$getIsolateArg)"; |
| } |
| |
| if ($type eq "EventListener") { |
| AddToImplIncludes("V8AbstractEventListener.h"); |
| return "${value} ? v8::Handle<v8::Value>(static_cast<V8AbstractEventListener*>(${value})->getListenerObject(imp->scriptExecutionContext())) : v8::Handle<v8::Value>(" . ($getIsolate ? "v8::Null($getIsolate)" : "v8::Null()") . ")"; |
| } |
| |
| if ($type eq "SerializedScriptValue") { |
| AddToImplIncludes("$type.h"); |
| return "$value ? $value->deserialize() : v8::Handle<v8::Value>(" . ($getIsolate ? "v8::Null($getIsolate)" : "v8::Null()") . ")"; |
| } |
| |
| AddToImplIncludes("wtf/RefCounted.h"); |
| AddToImplIncludes("wtf/RefPtr.h"); |
| AddToImplIncludes("wtf/GetPtr.h"); |
| |
| return "toV8($value$getCreationContextArg$getIsolateArg)"; |
| } |
| |
| # 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 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") { |
| 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 GetContextEnableFunction |
| { |
| my $signature = shift; |
| |
| # If a parameter is given (e.g. "V8EnabledPerContext=FeatureName") return the {FeatureName}Allowed() method. |
| if ($signature->extendedAttributes->{"V8EnabledPerContext"} && $signature->extendedAttributes->{"V8EnabledPerContext"} ne "VALUE_IS_MISSING") { |
| return "ContextFeatures::" . $codeGenerator->WK_lcfirst($signature->extendedAttributes->{"V8EnabledPerContext"}) . "Enabled"; |
| } |
| |
| # Or it fallbacks to the attribute name if the parameter value is missing. |
| my $attributeName = $signature->name; |
| return "ContextFeatures::" . $codeGenerator->WK_lcfirst($attributeName) . "Enabled"; |
| } |
| |
| sub GetPassRefPtrType |
| { |
| my $v8InterfaceName = shift; |
| |
| my $angleBracketSpace = $v8InterfaceName =~ />$/ ? " " : ""; |
| return "PassRefPtr<${v8InterfaceName}${angleBracketSpace}>"; |
| } |
| |
| 1; |