# Copyright (C) 2005, 2006 Nikolas Zimmermann # Copyright (C) 2006 Anders Carlsson # Copyright (C) 2006 Samuel Weinig # Copyright (C) 2006 Alexey Proskuryakov # Copyright (C) 2006 Apple Computer, Inc. # Copyright (C) 2007, 2008, 2009, 2012 Google Inc. # Copyright (C) 2009 Cameron McCormack # Copyright (C) Research In Motion Limited 2010. All rights reserved. # Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2012 Ericsson AB. All rights reserved. # # 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 @headerContent = (); my @implContentHeader = (); my @implContent = (); my @implContentInternals = (); 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; bless($reference, $object); return $reference; } sub GenerateInterface { my $object = shift; my $interface = shift; # Start actual generation if ($interface->extendedAttributes->{"Callback"}) { $object->GenerateCallbackHeader($interface); $object->GenerateCallbackImplementation($interface); } else { $object->GenerateHeader($interface); $object->GenerateImplementation($interface); } } 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 $interface = shift; return GetGenerateIsReachable($interface) || GetCustomIsReachable($interface); } sub GetGenerateIsReachable { my $interface = shift; return $interface->extendedAttributes->{"GenerateIsReachable"} || $interface->extendedAttributes->{"V8GenerateIsReachable"} || "" } sub GetCustomIsReachable { my $interface = shift; return $interface->extendedAttributes->{"CustomIsReachable"} || $interface->extendedAttributes->{"V8CustomIsReachable"}; } sub GenerateOpaqueRootForGC { my $interface = shift; my $interfaceName = $interface->name; if (GetCustomIsReachable($interface)) { return; } push(@implContent, < wrapper, v8::Isolate* isolate) { ASSERT(!wrapper.IsIndependent(isolate)); ${interfaceName}* impl = static_cast<${interfaceName}*>(object); END if (GetGenerateIsReachable($interface) eq "ImplDocument" || GetGenerateIsReachable($interface) eq "ImplElementRoot" || GetGenerateIsReachable($interface) eq "ImplOwnerRoot" || GetGenerateIsReachable($interface) eq "ImplOwnerNodeRoot") { $implIncludes{"V8GCController.h"} = 1; my $methodName; $methodName = "document" if (GetGenerateIsReachable($interface) eq "ImplDocument"); $methodName = "element" if (GetGenerateIsReachable($interface) eq "ImplElementRoot"); $methodName = "owner" if (GetGenerateIsReachable($interface) eq "ImplOwnerRoot"); $methodName = "ownerNode" if (GetGenerateIsReachable($interface) eq "ImplOwnerNodeRoot"); push(@implContent, <${methodName}()) return V8GCController::opaqueRootForGC(owner, isolate); END } push(@implContent, <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 $interface = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; # Copy contents of parent interfaces except the first parent. my @parents; $codeGenerator->AddMethodsConstantsAndAttributesFromParentInterfaces($interface, \@parents, 1); $codeGenerator->LinkOverloadedFunctions($interface); # Ensure the IsDOMNodeType function is in sync. die("IsDOMNodeType is out of date with respect to $interfaceName") if IsDOMNodeType($interfaceName) != $codeGenerator->InheritsInterface($interface, "Node"); my $hasDependentLifetime = $interface->extendedAttributes->{"V8DependentLifetime"} || $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject") || GetGenerateIsReachable($interface) || $v8InterfaceName =~ /SVG/; if (!$hasDependentLifetime) { foreach (@{$interface->parents}) { my $parent = $_; $headerIncludes{"V8${parent}.h"} = 1; } } # - Add default header template push(@headerContent, GenerateHeaderContentHeader($interface)); $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 class SVGPropertyTearOff;\n") if $svgPropertyType; if ($svgNativeType) { if ($svgNativeType =~ /SVGStaticListPropertyTearOff/) { push(@headerContent, "\ntemplate class SVGStaticListPropertyTearOff;\n"); } else { push(@headerContent, "\ntemplate class SVGListPropertyTearOff;\n"); } } push(@headerContent, "\n"); push(@headerContent, "class FloatRect;\n") if $svgPropertyType && $svgPropertyType eq "FloatRect"; push(@headerContent, "class Dictionary;\n") if $codeGenerator->IsConstructorTemplate($interface, "Event"); my $nativeType = GetNativeTypeForConversions($interface); if ($interface->extendedAttributes->{"NamedConstructor"}) { push(@headerContent, < GetTemplate(v8::Isolate*, WrapperWorldType); 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 (@{$interface->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 (@{$interface->parents}) { my $parent = $_; $headerIncludes{"V8${parent}.h"} = 1; push(@headerContent, "${separator}V8${parent}::hasDependentLifetime"); $separator = " || "; } push(@headerContent, ";\n"); } else { push(@headerContent, "false;\n"); } my $fromFunctionOpening = ""; my $fromFunctionClosing = ""; if ($interface->extendedAttributes->{"V8WrapAsFunction"}) { $fromFunctionOpening = "V8DOMWrapper::fromFunction("; $fromFunctionClosing = ")"; } push(@headerContent, <, v8::Isolate*, WrapperWorldType); static bool HasInstanceInAnyWorld(v8::Handle, v8::Isolate*); static v8::Persistent GetTemplate(v8::Isolate*, WrapperWorldType); static ${nativeType}* toNative(v8::Handle object) { return reinterpret_cast<${nativeType}*>(${fromFunctionOpening}object${fromFunctionClosing}->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex)); } static void derefObject(void*); static WrapperTypeInfo info; END if (NeedsCustomOpaqueRootForGC($interface)) { push(@headerContent, " static void* opaqueRootForGC(void*, v8::Persistent, v8::Isolate*);\n"); } if ($codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject")) { push(@headerContent, " static ActiveDOMObject* toActiveDOMObject(v8::Handle);\n"); } if ($codeGenerator->InheritsExtendedAttribute($interface, "EventTarget")) { push(@headerContent, " static EventTarget* toEventTarget(v8::Handle);\n"); } if ($interfaceName eq "DOMWindow") { push(@headerContent, < GetShadowObjectTemplate(v8::Isolate*, WrapperWorldType); END } if ($interfaceName eq "HTMLDocument") { push(@headerContent, < wrapInShadowObject(v8::Local wrapper, Node* impl, v8::Isolate*); END } my @enabledPerContextFunctions; foreach my $function (@{$interface->functions}) { my $name = $function->signature->name; my $attrExt = $function->signature->extendedAttributes; if (HasCustomMethod($attrExt) && !$attrExt->{"ImplementedBy"} && $function->{overloadIndex} == 1) { my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; push(@headerContent, < ${name}MethodCustom(const v8::Arguments&); END push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } if ($attrExt->{"V8EnabledPerContext"}) { push(@enabledPerContextFunctions, $function); } } if (IsConstructable($interface)) { push(@headerContent, " static v8::Handle constructorCallback(const v8::Arguments&);\n"); END } if (HasCustomConstructor($interface)) { push(@headerContent, " static v8::Handle constructorCustom(const v8::Arguments&);\n"); } my @enabledPerContextAttributes; foreach my $attribute (@{$interface->attributes}) { my $name = $attribute->signature->name; my $attrExt = $attribute->signature->extendedAttributes; my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); if (HasCustomGetter($attrExt) && !$attrExt->{"ImplementedBy"}) { push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; push(@headerContent, < ${name}AttrGetterCustom(v8::Local name, const v8::AccessorInfo&); END push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } if (HasCustomSetter($attrExt) && !$attrExt->{"ImplementedBy"}) { push(@headerContent, "#if ${conditionalString}\n") if $conditionalString; push(@headerContent, < name, v8::Local, const v8::AccessorInfo&); END push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } if ($attrExt->{"V8EnabledPerContext"}) { push(@enabledPerContextAttributes, $attribute); } } GenerateHeaderNamedAndIndexedPropertyAccessors($interface); GenerateHeaderCustomCall($interface); GenerateHeaderCustomInternalFieldIndices($interface); if ($interface->name eq "DOMWindow") { push(@headerContent, < host, v8::Local key, v8::AccessType, v8::Local data); static bool indexedSecurityCheckCustom(v8::Local host, uint32_t index, v8::AccessType, v8::Local data); END } if (@enabledPerContextAttributes) { push(@headerContent, <, ${nativeType}*, v8::Isolate*); END } else { push(@headerContent, <, ${nativeType}*, v8::Isolate*) { } END } if (@enabledPerContextFunctions) { push(@headerContent, <, v8::Isolate*); END } else { push(@headerContent, <, v8::Isolate*) { } END } if ($interfaceName eq "HTMLElement") { push(@headerContent, < createV8HTMLWrapper(HTMLElement*, v8::Handle creationContext, v8::Isolate*); friend v8::Handle createV8HTMLDirectWrapper(HTMLElement*, v8::Handle creationContext, v8::Isolate*); END } elsif ($interfaceName eq "SVGElement") { push(@headerContent, < createV8SVGWrapper(SVGElement*, v8::Handle creationContext, v8::Isolate*); friend v8::Handle createV8SVGDirectWrapper(SVGElement*, v8::Handle creationContext, v8::Isolate*); friend v8::Handle createV8SVGFallbackWrapper(SVGElement*, v8::Handle creationContext, v8::Isolate*); END } elsif ($interfaceName eq "HTMLUnknownElement") { push(@headerContent, < createV8HTMLFallbackWrapper(HTMLUnknownElement*, v8::Handle creationContext, v8::Isolate*); END } elsif ($interfaceName eq "Element") { push(@headerContent, < wrap(Node*, v8::Handle creationContext, v8::Isolate*); END } push(@headerContent, <extendedAttributes->{"SuppressToJSObject"}; my $noWrap = $interface->extendedAttributes->{"V8NoWrapperCache"} || $noToV8; if (!$noWrap) { my $createWrapperArgumentType = GetPassRefPtrType($nativeType); push(@headerContent, < wrap(${nativeType}*, v8::Handle creationContext, v8::Isolate*); static v8::Handle createWrapper(${createWrapperArgumentType}, v8::Handle creationContext, v8::Isolate*); END } push(@headerContent, < class WrapperTypeTraits<${nativeType} > { public: static WrapperTypeInfo* info() { return &${v8InterfaceName}::info; } }; END my $customWrap = !!($interface->extendedAttributes->{"CustomToJSObject"} or $interface->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, < toV8(${nativeType}*, v8::Handle creationContext, v8::Isolate*); v8::Handle toV8ForMainWorld(${nativeType}*, v8::Handle creationContext, v8::Isolate*); template inline v8::Handle toV8Fast(${nativeType}* impl, const HolderContainer& container, Wrappable*) { return toV8(impl, container.Holder(), container.GetIsolate()); } template inline v8::Handle toV8FastForMainWorld(${nativeType}* impl, const HolderContainer& container, Wrappable*) { return toV8ForMainWorld(impl, container.Holder(), container.GetIsolate()); } END } else { my $createWrapperCall = $customWrap ? "${v8InterfaceName}::wrap" : "${v8InterfaceName}::createWrapper"; my $returningWrapper = $interface->extendedAttributes->{"V8WrapAsFunction"} ? "V8DOMWrapper::toFunction(wrapper)" : "wrapper"; my $returningCreatedWrapperOpening = $interface->extendedAttributes->{"V8WrapAsFunction"} ? "V8DOMWrapper::toFunction(" : ""; my $returningCreatedWrapperClosing = $interface->extendedAttributes->{"V8WrapAsFunction"} ? ", \"${interfaceName}\", isolate)" : ""; if ($customWrap) { push(@headerContent, < wrap(${nativeType}* impl, v8::Handle creationContext, v8::Isolate*); END } else { push(@headerContent, < wrap(${nativeType}* impl, v8::Handle creationContext, v8::Isolate* isolate) { ASSERT(impl); ASSERT(DOMDataStore::getWrapper(impl, isolate).IsEmpty()); return ${returningCreatedWrapperOpening}$createWrapperCall(impl, creationContext, isolate)${returningCreatedWrapperClosing}; } END } push(@headerContent, < toV8(${nativeType}* impl, v8::Handle creationContext, v8::Isolate* isolate) { if (UNLIKELY(!impl)) return v8NullWithCheck(isolate); v8::Handle wrapper = DOMDataStore::getWrapper(impl, isolate); if (!wrapper.IsEmpty()) return $returningWrapper; return wrap(impl, creationContext, isolate); } inline v8::Handle toV8ForMainWorld(${nativeType}* impl, v8::Handle creationContext, v8::Isolate* isolate) { ASSERT(worldType(isolate) == MainWorld); if (UNLIKELY(!impl)) return v8NullWithCheck(isolate); v8::Handle wrapper = DOMDataStore::getWrapperForMainWorld(impl); if (!wrapper.IsEmpty()) return $returningWrapper; return wrap(impl, creationContext, isolate); } template inline v8::Handle toV8Fast(${nativeType}* impl, const HolderContainer& container, Wrappable* wrappable) { if (UNLIKELY(!impl)) return v8Null(container.GetIsolate()); v8::Handle wrapper = DOMDataStore::getWrapperFast(impl, container, wrappable); if (!wrapper.IsEmpty()) return $returningWrapper; return wrap(impl, container.Holder(), container.GetIsolate()); } template inline v8::Handle toV8FastForMainWorld(${nativeType}* impl, const HolderContainer& container, Wrappable* wrappable) { ASSERT(worldType(container.GetIsolate()) == MainWorld); if (UNLIKELY(!impl)) return v8Null(container.GetIsolate()); v8::Handle wrapper = DOMDataStore::getWrapperForMainWorld(impl); if (!wrapper.IsEmpty()) return $returningWrapper; return wrap(impl, container.Holder(), container.GetIsolate()); } template inline v8::Handle toV8FastForMainWorld(PassRefPtr< ${nativeType} > impl, const HolderContainer& container, Wrappable* wrappable) { return toV8FastForMainWorld(impl.get(), container, wrappable); } END } push(@headerContent, < inline v8::Handle toV8Fast(PassRefPtr< ${nativeType} > impl, const HolderContainer& container, Wrappable* wrappable) { return toV8Fast(impl.get(), container, wrappable); } inline v8::Handle toV8(PassRefPtr< ${nativeType} > impl, v8::Handle creationContext, v8::Isolate* isolate) { return toV8(impl.get(), creationContext, isolate); } END if ($codeGenerator->IsConstructorTemplate($interface, "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($interface); push(@headerContent, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GetInternalFields { my $interface = shift; my @customInternalFields = (); # Event listeners on DOM nodes are explicitly supported in the GC controller. if (!$codeGenerator->InheritsInterface($interface, "Node") && $codeGenerator->InheritsExtendedAttribute($interface, "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 $interface = shift; my @customInternalFields = GetInternalFields($interface); my $customFieldCounter = 0; foreach my $customInternalField (@customInternalFields) { push(@headerContent, < 1, "HTMLAppletElement" => 1, "HTMLEmbedElement" => 1, "HTMLObjectElement" => 1 ); sub GenerateHeaderNamedAndIndexedPropertyAccessors { my $interface = shift; my $interfaceName = $interface->name; my $hasCustomIndexedGetter = $interface->extendedAttributes->{"IndexedGetter"} || $interface->extendedAttributes->{"CustomGetOwnPropertySlot"}; my $hasCustomIndexedSetter = $interface->extendedAttributes->{"CustomIndexedSetter"} && !$interface->extendedAttributes->{"NumericIndexedGetter"}; my $hasCustomNamedGetter = $interface->extendedAttributes->{"NamedGetter"} || $interface->extendedAttributes->{"CustomNamedGetter"} || $interface->extendedAttributes->{"CustomGetOwnPropertySlot"}; my $hasCustomNamedSetter = $interface->extendedAttributes->{"CustomNamedSetter"}; my $hasCustomDeleters = $interface->extendedAttributes->{"CustomDeleteProperty"}; my $hasCustomEnumerator = $interface->extendedAttributes->{"CustomEnumerateProperty"}; if ($interfaceName eq "HTMLOptionsCollection") { $interfaceName = "HTMLCollection"; $hasCustomIndexedGetter = 1; $hasCustomNamedGetter = 1; } if ($interfaceName eq "DOMWindow") { $hasCustomDeleters = 0; $hasCustomEnumerator = 0; } if ($interfaceName eq "HTMLAppletElement" || $interfaceName eq "HTMLEmbedElement" || $interfaceName eq "HTMLObjectElement") { $hasCustomNamedGetter = 1; } if ($interfaceName eq "HTMLDocument") { $hasCustomNamedGetter = 0; $hasCustomIndexedGetter = 0; } my $isIndexerSpecialCase = exists $indexerSpecialCases{$interfaceName}; if ($hasCustomIndexedGetter || $isIndexerSpecialCase) { push(@headerContent, < indexedPropertyGetter(uint32_t, const v8::AccessorInfo&); END } if ($isIndexerSpecialCase || $hasCustomIndexedSetter) { push(@headerContent, < indexedPropertySetter(uint32_t, v8::Local, const v8::AccessorInfo&); END } if ($hasCustomDeleters) { push(@headerContent, < indexedPropertyDeleter(uint32_t, const v8::AccessorInfo&); END } if ($hasCustomNamedGetter) { push(@headerContent, < namedPropertyGetter(v8::Local, const v8::AccessorInfo&); END } if ($hasCustomNamedSetter) { push(@headerContent, < namedPropertySetter(v8::Local, v8::Local, const v8::AccessorInfo&); END } if ($hasCustomDeleters) { push(@headerContent, < namedPropertyDeleter(v8::Local, const v8::AccessorInfo&); END } if ($hasCustomEnumerator) { push(@headerContent, < namedPropertyEnumerator(const v8::AccessorInfo&); static v8::Handle namedPropertyQuery(v8::Local, const v8::AccessorInfo&); END } } sub GenerateHeaderCustomCall { my $interface = shift; if ($interface->extendedAttributes->{"CustomCall"}) { push(@headerContent, " static v8::Handle callAsFunctionCallback(const v8::Arguments&);\n"); } if ($interface->name eq "Location") { push(@headerContent, " static v8::Handle assignAttrGetterCustom(v8::Local name, const v8::AccessorInfo&);\n"); push(@headerContent, " static v8::Handle reloadAttrGetterCustom(v8::Local name, const v8::AccessorInfo&);\n"); push(@headerContent, " static v8::Handle replaceAttrGetterCustom(v8::Local name, const v8::AccessorInfo&);\n"); } } sub IsConstructable { my $interface = shift; return $interface->extendedAttributes->{"CustomConstructor"} || $interface->extendedAttributes->{"V8CustomConstructor"} || $interface->extendedAttributes->{"Constructor"} || $interface->extendedAttributes->{"ConstructorTemplate"}; } sub HasCustomConstructor { my $interface = shift; return $interface->extendedAttributes->{"CustomConstructor"} || $interface->extendedAttributes->{"V8CustomConstructor"}; } sub HasCustomGetter { my $attrExt = shift; return $attrExt->{"Custom"} || $attrExt->{"V8Custom"} || $attrExt->{"CustomGetter"} || $attrExt->{"V8CustomGetter"}; } sub HasCustomSetter { my $attrExt = shift; return $attrExt->{"Custom"} || $attrExt->{"V8Custom"} || $attrExt->{"CustomSetter"} || $attrExt->{"V8CustomSetter"}; } sub HasCustomMethod { my $attrExt = shift; return $attrExt->{"Custom"} || $attrExt->{"V8Custom"}; } 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(V8PerIsolateData::from(info.GetIsolate())->rawTemplate(&" . $v8InterfaceName . "::info, currentWorldType))"; if ($function->signature->extendedAttributes->{"V8DoNotCheckSignature"}) { $signature = "v8::Local()"; } my $newTemplateParams = "${interfaceName}V8Internal::${funcName}MethodCallback, v8Undefined(), $signature"; AddToImplIncludes("Frame.h"); push(@implContentInternals, < ${funcName}AttrGetter(v8::Local name, const v8::AccessorInfo& info) { // This is only for getting a unique pointer which we can pass to privateTemplate. static const char* privateTemplateUniqueKey = "${funcName}PrivateTemplate"; WrapperWorldType currentWorldType = worldType(info.GetIsolate()); V8PerIsolateData* data = V8PerIsolateData::from(info.GetIsolate()); v8::Persistent privateTemplate = data->privateTemplate(currentWorldType, &privateTemplateUniqueKey, $newTemplateParams); v8::Handle holder = info.This()->FindInstanceInPrototypeChain(${v8InterfaceName}::GetTemplate(info.GetIsolate(), currentWorldType)); 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 const char* sharedTemplateUniqueKey = "${funcName}SharedTemplate"; v8::Persistent sharedTemplate = data->privateTemplate(currentWorldType, &sharedTemplateUniqueKey, $newTemplateParams); return sharedTemplate->GetFunction(); } v8::Local hiddenValue = info.This()->GetHiddenValue(name); if (!hiddenValue.IsEmpty()) return hiddenValue; return privateTemplate->GetFunction(); } static v8::Handle ${funcName}AttrGetterCallback(v8::Local name, const v8::AccessorInfo& info) { return ${interfaceName}V8Internal::${funcName}AttrGetter(name, info); } END } sub GenerateDomainSafeFunctionSetter { my $interfaceName = shift; my $v8InterfaceName = "V8" . $interfaceName; push(@implContentInternals, < name, v8::Local value, const v8::AccessorInfo& info) { v8::Handle holder = info.This()->FindInstanceInPrototypeChain(${v8InterfaceName}::GetTemplate(info.GetIsolate(), worldType(info.GetIsolate()))); 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 $interface = shift; my $interfaceName = $interface->name; push(@implContentInternals, < ${interfaceName}ConstructorGetter(v8::Local name, const v8::AccessorInfo& info) { v8::Handle 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 GenerateNormalAttrGetterCallback { my $attribute = shift; my $interface = shift; my $forMainWorldSuffix = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; my $attrExt = $attribute->signature->extendedAttributes; my $attrName = $attribute->signature->name; my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); push(@implContentInternals, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentInternals, "static v8::Handle ${attrName}AttrGetterCallback${forMainWorldSuffix}(v8::Local name, const v8::AccessorInfo& info)\n"); push(@implContentInternals, "{\n"); push(@implContentInternals, GenerateFeatureObservation($attrExt->{"V8MeasureAs"})); if (HasCustomGetter($attrExt)) { push(@implContentInternals, " return ${v8InterfaceName}::${attrName}AttrGetterCustom(name, info);\n"); } else { push(@implContentInternals, " return ${interfaceName}V8Internal::${attrName}AttrGetter${forMainWorldSuffix}(name, info);\n"); } push(@implContentInternals, "}\n\n"); push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GenerateNormalAttrGetter { my $attribute = shift; my $interface = shift; my $forMainWorldSuffix = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; my $attrExt = $attribute->signature->extendedAttributes; my $attrName = $attribute->signature->name; my $attrType = $attribute->signature->type; if (HasCustomGetter($attrExt)) { return; } $codeGenerator->AssertNotSequenceType($attrType); my $getterStringUsesImp = $interfaceName ne "SVGNumber"; my $nativeType = GetNativeTypeFromSignature($attribute->signature, -1); my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName); my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); push(@implContentInternals, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentInternals, < ${attrName}AttrGetter${forMainWorldSuffix}(v8::Local name, const v8::AccessorInfo& info) { END if ($svgNativeType) { my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName); if ($svgWrappedNativeType =~ /List/) { push(@implContentInternals, <propertyReference(); END if ($getterStringUsesImp) { push(@implContentInternals, <{"V8OnProto"} || $attrExt->{"V8Unforgeable"}) { if ($interfaceName eq "DOMWindow") { push(@implContentInternals, < holder = info.Holder(); END } else { # perform lookup first push(@implContentInternals, < holder = info.This()->FindInstanceInPrototypeChain(${v8InterfaceName}::GetTemplate(info.GetIsolate(), worldType(info.GetIsolate()))); if (holder.IsEmpty()) return v8Undefined(); END } push(@implContentInternals, <signature->extendedAttributes->{"Reflect"}; my $url = $attribute->signature->extendedAttributes->{"URL"}; if ($getterStringUsesImp && $reflect && !$url && $codeGenerator->InheritsInterface($interface, "Node") && $codeGenerator->IsStringType($attrType)) { # Generate super-compact call for regular attribute getter: my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); push(@implContentInternals, " Element* imp = V8Element::toNative(info.Holder());\n"); push(@implContentInternals, " return v8String(imp->${functionName}(" . join(", ", @arguments) . "), info.GetIsolate(), ReturnUnsafeHandle);\n"); push(@implContentInternals, "}\n\n"); push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; return; # Skip the rest of the function! } if ($attribute->signature->type eq "SerializedScriptValue" && $attrExt->{"CachedAttribute"}) { push(@implContentInternals, < propertyName = v8::String::NewSymbol("${attrName}"); v8::Handle value = info.Holder()->GetHiddenValue(propertyName); if (!value.IsEmpty()) return value; END } if (!$attribute->isStatic) { push(@implContentInternals, <signature->extendedAttributes->{"CheckSecurityForNode"}) { push(@implContentInternals, " if (!BindingSecurity::shouldAllowAccessToNode(BindingState::instance(), imp->" . $attribute->signature->name . "()))\n return v8::Handle(v8Null(info.GetIsolate()));\n\n"); } my $useExceptions = 1 if @{$attribute->getterExceptions}; my $isNullable = $attribute->signature->isNullable; if ($useExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContentInternals, " ExceptionCode ec = 0;\n"); } if ($isNullable) { push(@implContentInternals, " bool isNull = false;\n"); } my $returnType = $attribute->signature->type; my $getterString; if ($getterStringUsesImp) { my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute); push(@arguments, "isNull") if $isNullable; 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"}, \@implContentInternals, " ", 0)); $getterString = "${functionName}(" . join(", ", @arguments) . ")"; } else { $getterString = "impInstance"; } my $result; if ($attribute->signature->type eq "EventListener" && $interface->name eq "DOMWindow") { push(@implContentInternals, " if (!imp->document())\n"); push(@implContentInternals, " return v8Undefined();\n"); } if ($useExceptions || $isNullable) { if ($nativeType =~ /^V8StringResource/) { push(@implContentInternals, " " . ConvertToV8StringResource($attribute->signature, $nativeType, "v", $getterString) . ";\n"); } else { push(@implContentInternals, " $nativeType v = $getterString;\n"); } if ($isNullable) { push(@implContentInternals, " if (isNull)\n"); push(@implContentInternals, " return v8Null(info.GetIsolate());\n"); } if ($useExceptions) { push(@implContentInternals, " if (UNLIKELY(ec))\n"); push(@implContentInternals, " return setDOMException(ec, info.GetIsolate());\n"); if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) { push(@implContentInternals, " if (state.hadException())\n"); push(@implContentInternals, " return throwError(state.exception(), info.GetIsolate());\n"); } } $result = "v"; $result .= ".release()" if ($codeGenerator->IsRefPtrType($returnType)); } else { # Can inline the function call into the return statement to avoid overhead of using a Ref<> temporary $result = $getterString; # Fix amigious conversion problem, by casting to the base type first ($getterString returns a type that inherits from SVGAnimatedEnumeration, not the base class directly). $result = "static_pointer_cast($result)" if $returnType eq "SVGAnimatedEnumeration"; } # Special case for readonly or Replaceable attributes (with a few exceptions). This attempts to ensure that JS wrappers don't get # garbage-collected prematurely when their lifetime is strongly tied to their owner. We accomplish this by inserting a reference to # the newly created wrapper into an internal field of the holder object. if ((!$codeGenerator->InheritsInterface($interface, "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)) || $attribute->signature->extendedAttributes->{"V8CacheAttributeForGC"}) { my $arrayType = $codeGenerator->GetArrayType($returnType); if ($arrayType) { if (!$codeGenerator->SkipIncludeHeader($arrayType)) { AddToImplIncludes("V8$arrayType.h"); AddToImplIncludes("$arrayType.h"); } push(@implContentInternals, " return v8Array(${getterString}, info.GetIsolate());\n"); push(@implContentInternals, "}\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(@implContentInternals, " RefPtr<$returnType> result = ${getterString};\n"); push(@implContentInternals, " v8::Handle wrapper = result.get() ? v8::Handle(DOMDataStore::getWrapper(result.get(), info.GetIsolate())) : v8Undefined();\n"); push(@implContentInternals, " if (wrapper.IsEmpty()) {\n"); push(@implContentInternals, " wrapper = toV8(result.get(), info.Holder(), info.GetIsolate());\n"); # FIXME: Could use wrap here since the wrapper is empty. push(@implContentInternals, " if (!wrapper.IsEmpty())\n"); push(@implContentInternals, " V8HiddenPropertyName::setNamedHiddenReference(info.Holder(), \"${attrName}\", wrapper);\n"); push(@implContentInternals, " }\n"); push(@implContentInternals, " return wrapper;\n"); push(@implContentInternals, "}\n\n"); push(@implContentInternals, "#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(@implContentInternals, " return toV8Fast$forMainWorldSuffix(static_cast<$svgNativeType*>($result), info, imp);\n"); } elsif ($codeGenerator->IsSVGTypeNeedingTearOff($attrType) and not $interfaceName =~ /List$/) { AddToImplIncludes("V8$attrType.h"); AddToImplIncludes("SVGPropertyTearOff.h"); my $tearOffType = $codeGenerator->GetSVGTypeNeedingTearOff($attrType); my $wrappedValue; 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/SVGPropertyTearOffsignature->type eq "MessagePortArray") { AddToImplIncludes("MessagePort.h"); AddToImplIncludes("V8MessagePort.h"); my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name); push(@implContentInternals, <${getterFunc}(); if (!ports) return v8::Array::New(0); MessagePortArray portsCopy(*ports); v8::Local portArray = v8::Array::New(portsCopy.size()); for (size_t i = 0; i < portsCopy.size(); ++i) portArray->Set(v8Integer(i, info.GetIsolate()), toV8Fast$forMainWorldSuffix(portsCopy[i].get(), info, imp)); return portArray; END } elsif ($attribute->signature->type eq "SerializedScriptValue" && $attrExt->{"CachedAttribute"}) { my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name); push(@implContentInternals, < serialized = imp->${getterFunc}(); value = serialized ? serialized->deserialize() : v8::Handle(v8Null(info.GetIsolate())); info.Holder()->SetHiddenValue(propertyName, value); return value; END } else { push(@implContentInternals, " return " . NativeToJSValue($attribute->signature, $result, "info.Holder()", "info.GetIsolate()", "info", "imp", "ReturnUnsafeHandle", $forMainWorldSuffix).";\n"); } push(@implContentInternals, "}\n\n"); # end of getter push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GenerateReplaceableAttrSetterCallback { my $interface = shift; my $interfaceName = $interface->name; push(@implContentInternals, "static void ${interfaceName}ReplaceableAttrSetterCallback(v8::Local name, v8::Local value, const v8::AccessorInfo& info)\n"); push(@implContentInternals, "{\n"); push(@implContentInternals, GenerateFeatureObservation($interface->extendedAttributes->{"V8MeasureAs"})); push(@implContentInternals, " return ${interfaceName}V8Internal::${interfaceName}ReplaceableAttrSetter(name, value, info);\n"); push(@implContentInternals, "}\n\n"); } sub GenerateReplaceableAttrSetter { my $interface = shift; my $interfaceName = $interface->name; push(@implContentInternals, < name, v8::Local value, const v8::AccessorInfo& info) { END if ($interface->extendedAttributes->{"CheckSecurity"}) { AddToImplIncludes("Frame.h"); push(@implContentInternals, <frame())) return; END } push(@implContentInternals, <ForceSet(name, value); } END } sub GenerateNormalAttrSetterCallback { my $attribute = shift; my $interface = shift; my $forMainWorldSuffix = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; my $attrExt = $attribute->signature->extendedAttributes; my $attrName = $attribute->signature->name; my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); push(@implContentInternals, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentInternals, "static void ${attrName}AttrSetterCallback${forMainWorldSuffix}(v8::Local name, v8::Local value, const v8::AccessorInfo& info)\n"); push(@implContentInternals, "{\n"); push(@implContentInternals, GenerateFeatureObservation($attrExt->{"V8MeasureAs"})); if (HasCustomSetter($attrExt)) { push(@implContentInternals, " ${v8InterfaceName}::${attrName}AttrSetterCustom(name, value, info);\n"); } else { push(@implContentInternals, " ${interfaceName}V8Internal::${attrName}AttrSetter${forMainWorldSuffix}(name, value, info);\n"); } push(@implContentInternals, "}\n\n"); push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GenerateNormalAttrSetter { my $attribute = shift; my $interface = shift; my $forMainWorldSuffix = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; my $attrName = $attribute->signature->name; my $attrExt = $attribute->signature->extendedAttributes; my $attrType = $attribute->signature->type; if (HasCustomSetter($attrExt)) { return; } my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature); push(@implContentInternals, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentInternals, "static void ${attrName}AttrSetter${forMainWorldSuffix}(v8::Local name, v8::Local value, const v8::AccessorInfo& info)\n"); push(@implContentInternals, "{\n"); # If the "StrictTypeChecking" extended attribute is present, and the attribute's type is an # interface type, then if the incoming value does not implement that interface, a TypeError is # thrown rather than silently passing NULL to the C++ code. # Per the Web IDL and ECMAScript specifications, incoming values can always be converted to both # strings and numbers, so do not throw TypeError if the attribute is of these types. if ($attribute->signature->extendedAttributes->{"StrictTypeChecking"}) { my $argType = $attribute->signature->type; if (IsWrapperType($argType)) { push(@implContentInternals, " if (!isUndefinedOrNull(value) && !V8${argType}::HasInstance(value, info.GetIsolate(), worldType(info.GetIsolate()))) {\n"); push(@implContentInternals, " throwTypeError(0, info.GetIsolate());\n"); push(@implContentInternals, " return;\n"); push(@implContentInternals, " }\n"); } } my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName); if ($svgNativeType) { my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName); if ($svgWrappedNativeType =~ /List$/) { push(@implContentInternals, <isReadOnly()) {\n"); push(@implContentInternals, " setDOMException(NO_MODIFICATION_ALLOWED_ERR, info.GetIsolate());\n"); push(@implContentInternals, " return;\n"); push(@implContentInternals, " }\n"); push(@implContentInternals, " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n"); push(@implContentInternals, " $svgWrappedNativeType* imp = &impInstance;\n"); } } elsif ($attrExt->{"V8OnProto"}) { push(@implContentInternals, <signature->extendedAttributes->{"Reflect"}; if ($reflect && $codeGenerator->InheritsInterface($interface, "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(@implContentInternals, " Element* imp = V8Element::toNative(info.Holder());\n"); push(@implContentInternals, " V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource, stringResource, value);\n"); push(@implContentInternals, " imp->setAttribute(${namespace}::${contentAttributeName}Attr, stringResource);\n"); push(@implContentInternals, "}\n\n"); push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; return; # Skip the rest of the function! } if (!$attribute->isStatic) { push(@implContentInternals, <signature, 0); if ($attribute->signature->type eq "EventListener") { if ($interface->name eq "DOMWindow") { push(@implContentInternals, " if (!imp->document())\n"); push(@implContentInternals, " return;\n"); } } else { my $value = JSValueToNative($attribute->signature, "value", "info.GetIsolate()"); my $arrayType = $codeGenerator->GetArrayType($nativeType); if ($nativeType =~ /^V8StringResource/) { push(@implContentInternals, " " . ConvertToV8StringResource($attribute->signature, $nativeType, "v", $value, "VOID") . "\n"); } elsif ($arrayType) { push(@implContentInternals, " Vector<$arrayType> v = $value;\n"); } elsif ($attribute->signature->extendedAttributes->{"EnforceRange"}) { push(@implContentInternals, " V8TRYCATCH_WITH_TYPECHECK_VOID($nativeType, v, $value, info.GetIsolate());\n"); } else { push(@implContentInternals, " $nativeType v = $value;\n"); } } if ($codeGenerator->IsEnumType($attrType)) { # setter ignores invalid enumeration values my @enumValues = $codeGenerator->ValidEnumValues($attrType); my @validEqualities = (); foreach my $enumValue (@enumValues) { push(@validEqualities, "string == \"$enumValue\""); } my $enumValidationExpression = join(" || ", @validEqualities); push(@implContentInternals, <signature->type; if ($codeGenerator->IsRefPtrType($returnType) && !$codeGenerator->GetArrayType($returnType)) { $result = "WTF::getPtr(" . $result . ")"; } my $useExceptions = 1 if @{$attribute->setterExceptions}; if ($useExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContentInternals, " ExceptionCode ec = 0;\n"); } if ($interfaceName eq "SVGNumber") { push(@implContentInternals, " *imp = $result;\n"); } else { if ($attribute->signature->type eq "EventListener") { my $implSetterFunctionName = $codeGenerator->WK_ucfirst($attrName); AddToImplIncludes("V8AbstractEventListener.h"); if (!$codeGenerator->InheritsInterface($interface, "Node")) { push(@implContentInternals, " transferHiddenDependency(info.Holder(), imp->$attrName(), value, ${v8InterfaceName}::eventListenerCacheIndex, info.GetIsolate());\n"); } AddToImplIncludes("V8EventListenerList.h"); if ($interfaceName eq "WorkerContext" and $attribute->signature->name eq "onerror") { AddToImplIncludes("V8WorkerContextErrorHandler.h"); push(@implContentInternals, " imp->set$implSetterFunctionName(V8EventListenerList::findOrCreateWrapper(value, true)"); } elsif ($interfaceName eq "DOMWindow" and $attribute->signature->name eq "onerror") { AddToImplIncludes("V8WindowErrorHandler.h"); push(@implContentInternals, " imp->set$implSetterFunctionName(V8EventListenerList::findOrCreateWrapper(value, true)"); } else { push(@implContentInternals, " imp->set$implSetterFunctionName(V8EventListenerList::getEventListener(value, true, ListenerFindOrCreate)"); } push(@implContentInternals, ", ec") if $useExceptions; push(@implContentInternals, ");\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"}, \@implContentInternals, " ", 1)); push(@implContentInternals, " ${functionName}(" . join(", ", @arguments) . ");\n"); } } if ($useExceptions) { push(@implContentInternals, " if (UNLIKELY(ec))\n"); push(@implContentInternals, " setDOMException(ec, info.GetIsolate());\n"); } if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) { push(@implContentInternals, " if (state.hadException())\n"); push(@implContentInternals, " throwError(state.exception(), info.GetIsolate());\n"); } if ($svgNativeType) { if ($useExceptions) { push(@implContentInternals, " if (!ec)\n"); push(@implContentInternals, " wrapper->commitChange();\n"); } else { push(@implContentInternals, " wrapper->commitChange();\n"); } } if ($attribute->signature->type eq "SerializedScriptValue" && $attribute->signature->extendedAttributes->{"CachedAttribute"}) { push(@implContentInternals, <DeleteHiddenValue(v8::String::NewSymbol("${attrName}")); // Invalidate the cached value. END } push(@implContentInternals, " return;\n"); push(@implContentInternals, "}\n\n"); # end of setter push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; } 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->GetArrayType($type) || $codeGenerator->GetSequenceType($type)) { 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, args.GetIsolate(), worldType(args.GetIsolate())))"); } else { push(@andExpression, "(V8${type}::HasInstance($value, args.GetIsolate(), worldType(args.GetIsolate())))"); } } $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 GenerateOverloadedFunction { my $function = shift; my $interface = shift; my $interfaceName = $interface->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(@implContentInternals, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentInternals, < ${name}Method(const v8::Arguments& args) { END push(@implContentInternals, GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"})); foreach my $overload (@{$function->{overloads}}) { my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($overload); $leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams); push(@implContentInternals, " if ($parametersCheck)\n"); my $overloadedIndexString = $overload->{overloadIndex}; push(@implContentInternals, " return ${name}${overloadedIndexString}Method(args);\n"); } if ($leastNumMandatoryParams >= 1) { push(@implContentInternals, " if (args.Length() < $leastNumMandatoryParams)\n"); push(@implContentInternals, " return throwNotEnoughArgumentsError(args.GetIsolate());\n"); } push(@implContentInternals, <name; my $v8InterfaceName = "V8$interfaceName"; my $name = $function->signature->name; my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@implContentInternals, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentInternals, < ${name}MethodCallback(const v8::Arguments& args) { END push(@implContentInternals, GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"})); if (HasCustomMethod($function->signature->extendedAttributes)) { push(@implContentInternals, " return ${v8InterfaceName}::${name}MethodCustom(args);\n"); } else { push(@implContentInternals, " return ${interfaceName}V8Internal::${name}Method(args);\n"); } push(@implContentInternals, "}\n\n"); push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GenerateFunction { my $function = shift; my $interface = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; my $name = $function->signature->name; my $funcExt = $function->signature->extendedAttributes; if (HasCustomMethod($funcExt)) { return; } if (@{$function->{overloads}} > 1) { # Append a number to an overloaded method's name to make it unique: $name = $name . $function->{overloadIndex}; } my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@implContentInternals, "#if ${conditionalString}\n\n") if $conditionalString; push(@implContentInternals, "static v8::Handle ${name}Method(const v8::Arguments& args)\n"); push(@implContentInternals, "{\n"); if ($name eq "addEventListener" || $name eq "removeEventListener") { my $lookupType = ($name eq "addEventListener") ? "OrCreate" : "Only"; my $passRefPtrHandling = ($name eq "addEventListener") ? "" : ".get()"; my $hiddenDependencyAction = ($name eq "addEventListener") ? "create" : "remove"; AddToImplIncludes("V8EventListenerList.h"); push(@implContentInternals, < listener = V8EventListenerList::getEventListener(args[1], false, ListenerFind${lookupType}); if (listener) { V8TRYCATCH_FOR_V8STRINGRESOURCE(V8StringResource, stringResource, args[0]); V8${interfaceName}::toNative(args.Holder())->${name}(stringResource, listener${passRefPtrHandling}, args[2]->BooleanValue()); END if (!$codeGenerator->InheritsInterface($interface, "Node")) { push(@implContentInternals, <extendedAttributes->{"TypedArray"}) { AddToImplIncludes("V8ArrayBufferViewCustom.h"); push(@implContentInternals, <(args); } END return; } my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($interfaceName); if ($svgNativeType) { my $nativeClassName = GetNativeType($interfaceName); if ($interfaceName =~ /List$/) { push(@implContentInternals, " $nativeClassName imp = ${v8InterfaceName}::toNative(args.Holder());\n"); } else { AddToImplIncludes("ExceptionCode.h"); push(@implContentInternals, " $nativeClassName wrapper = ${v8InterfaceName}::toNative(args.Holder());\n"); push(@implContentInternals, " if (wrapper->isReadOnly())\n"); push(@implContentInternals, " return setDOMException(NO_MODIFICATION_ALLOWED_ERR, args.GetIsolate());\n"); my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName); push(@implContentInternals, " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n"); push(@implContentInternals, " $svgWrappedNativeType* imp = &impInstance;\n"); } } elsif (!$function->isStatic) { push(@implContentInternals, <extendedAttributes->{"CheckSecurity"} && !$function->signature->extendedAttributes->{"DoNotCheckSecurity"}) { # We have not find real use cases yet. AddToImplIncludes("Frame.h"); push(@implContentInternals, <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(@implContentInternals, " ExceptionCode ec = 0;\n"); push(@implContentInternals, " {\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(@implContentInternals, " if (!BindingSecurity::shouldAllowAccessToNode(BindingState::instance(), imp->" . $function->signature->name . "(ec)))\n"); push(@implContentInternals, " return v8::Handle(v8Null(args.GetIsolate()));\n"); END } my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interfaceName); push(@implContentInternals, $parameterCheckString); # Build the function call string. push(@implContentInternals, GenerateFunctionCallString($function, $paramIndex, " ", $interfaceName, %replacements)); if ($raisesExceptions) { push(@implContentInternals, " }\n"); push(@implContentInternals, " fail:\n"); push(@implContentInternals, " return setDOMException(ec, args.GetIsolate());\n"); } push(@implContentInternals, "}\n\n"); push(@implContentInternals, "#endif // ${conditionalString}\n\n") if $conditionalString; } sub GenerateCallWith { my $callWith = shift; return () unless $callWith; my $outputArray = shift; my $indent = shift; my $returnVoid = shift; my $function = shift; my @callWithArgs; if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState")) { push(@$outputArray, $indent . "ScriptState* currentState = ScriptState::current();\n"); push(@$outputArray, $indent . "if (!currentState)\n"); push(@$outputArray, $indent . " return" . ($returnVoid ? "" : " v8Undefined()") . ";\n"); push(@$outputArray, $indent . "ScriptState& state = *currentState;\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(createScriptArguments(args, " . @{$function->parameters} . "));\n"); push(@callWithArgs, "scriptArguments.release()"); AddToImplIncludes("ScriptArguments.h"); AddToImplIncludes("ScriptCallStackFactory.h"); } return @callWithArgs; } sub GenerateArgumentsCountCheck { my $function = shift; my $interface = 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}) { 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 .= " V8TRYCATCH(double, $nativeValue, args[$paramIndex]->NumberValue());\n"; $parameterCheckString .= " if (!std::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\", args.GetIsolate());\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], args.GetIsolate(), worldType(args.GetIsolate())))\n"; $parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n"; $parameterCheckString .= " $parameterName.append(V8${argType}::toNative(v8::Handle::Cast(args[i])));\n"; $parameterCheckString .= " }\n"; } else { $parameterCheckString .= " V8TRYCATCH(Vector<$nativeElementType>, $parameterName, toNativeArguments<$nativeElementType>(args, $paramIndex));\n"; } } elsif ($nativeType =~ /^V8StringResource/) { my $value = JSValueToNative($parameter, $optional && $optional eq "DefaultIsNullString" ? "argumentOrNull(args, $paramIndex)" : "args[$paramIndex]", "args.GetIsolate()"); $parameterCheckString .= " " . ConvertToV8StringResource($parameter, $nativeType, $parameterName, $value) . "\n"; if ($codeGenerator->IsEnumType($parameter->type)) { my @enumValues = $codeGenerator->ValidEnumValues($parameter->type); my @validEqualities = (); foreach my $enumValue (@enumValues) { push(@validEqualities, "string == \"$enumValue\""); } my $enumValidationExpression = join(" || ", @validEqualities); $parameterCheckString .= " String string = $parameterName;\n"; $parameterCheckString .= " if (!($enumValidationExpression))\n"; $parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\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, args.GetIsolate(), worldType(args.GetIsolate())))\n"; $parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n"; } } my $value = JSValueToNative($parameter, $optional && $optional eq "DefaultIsNullString" ? "argumentOrNull(args, $paramIndex)" : "args[$paramIndex]", "args.GetIsolate()"); if ($parameter->extendedAttributes->{"EnforceRange"}) { $parameterCheckString .= " V8TRYCATCH_WITH_TYPECHECK($nativeType, $parameterName, $value, args.GetIsolate());\n"; } else { $parameterCheckString .= " V8TRYCATCH($nativeType, $parameterName, $value);\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 $interface = shift; my $interfaceName = $interface->name; push(@implContentInternals, < constructor(const v8::Arguments& args) { END my $leastNumMandatoryParams = 255; foreach my $constructor (@{$interface->constructors}) { my $name = "constructor" . $constructor->{overloadedIndex}; my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($constructor); $leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams); push(@implContentInternals, " if ($parametersCheck)\n"); push(@implContentInternals, " return ${interfaceName}V8Internal::${name}(args);\n"); } if ($leastNumMandatoryParams >= 1) { push(@implContentInternals, " if (args.Length() < $leastNumMandatoryParams)\n"); push(@implContentInternals, " return throwNotEnoughArgumentsError(args.GetIsolate());\n"); } push(@implContentInternals, <name; my $overloadedIndexString = ""; if ($function->{overloadedIndex} > 0) { $overloadedIndexString .= $function->{overloadedIndex}; } my $raisesExceptions = @{$function->raisesExceptions}; if ($interface->extendedAttributes->{"ConstructorRaisesException"}) { $raisesExceptions = 1; } if (!$raisesExceptions) { foreach my $parameter (@{$function->parameters}) { if ((!$parameter->extendedAttributes->{"Callback"} and TypeCanFailConversion($parameter)) or $parameter->extendedAttributes->{"IsIndex"}) { $raisesExceptions = 1; } } } my @beforeArgumentList; my @afterArgumentList; push(@implContentInternals, < constructor${overloadedIndexString}(const v8::Arguments& args) { END if ($function->{overloadedIndex} == 0) { push(@implContentInternals, GenerateArgumentsCountCheck($function, $interface)); } if ($raisesExceptions) { AddToImplIncludes("ExceptionCode.h"); push(@implContentInternals, "\n"); push(@implContentInternals, " 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(@implContentInternals, $parameterCheckString); if ($interface->extendedAttributes->{"CallWith"} && $interface->extendedAttributes->{"CallWith"} eq "ScriptExecutionContext") { push(@beforeArgumentList, "context"); push(@implContentInternals, <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(@implContentInternals, "\n"); push(@implContentInternals, " RefPtr<${interfaceName}> impl = ${interfaceName}::create(${argumentString});\n"); push(@implContentInternals, " v8::Handle wrapper = args.Holder();\n"); if ($interface->extendedAttributes->{"ConstructorRaisesException"}) { push(@implContentInternals, " if (ec)\n"); push(@implContentInternals, " goto fail;\n"); } push(@implContentInternals, <name; push(@implContent, "v8::Handle V8${interfaceName}::constructorCallback(const v8::Arguments& args)\n"); push(@implContent, "{\n"); push(@implContent, GenerateFeatureObservation($interface->extendedAttributes->{"V8MeasureAs"})); push(@implContent, GenerateConstructorHeader()); if (HasCustomConstructor($interface)) { push(@implContent, " return V8${interfaceName}::constructorCustom(args);\n"); } else { push(@implContent, " return ${interfaceName}V8Internal::constructor(args);\n"); } push(@implContent, "}\n\n"); } sub GenerateConstructor { my $interface = shift; my $interfaceName = $interface->name; if (@{$interface->constructors} == 1) { GenerateSingleConstructorCallback($interface, @{$interface->constructors}[0]); } else { foreach my $constructor (@{$interface->constructors}) { GenerateSingleConstructorCallback($interface, $constructor); } GenerateOverloadedConstructorCallback($interface); } } sub GenerateEventConstructor { my $interface = shift; my $interfaceName = $interface->name; AddToImplIncludes("Dictionary.h"); push(@implContentInternals, < constructor(const v8::Arguments& args) { END push(@implContentInternals, <, type, args[0]); ${interfaceName}Init eventInit; if (args.Length() >= 2) { V8TRYCATCH(Dictionary, options, Dictionary(args[1], args.GetIsolate())); if (!fill${interfaceName}Init(eventInit, options)) return v8Undefined(); } RefPtr<${interfaceName}> event = ${interfaceName}::create(type, eventInit); v8::Handle wrapper = args.Holder(); V8DOMWrapper::associateObjectWithWrapper(event.release(), &V8${interfaceName}::info, wrapper, args.GetIsolate(), WrapperConfiguration::Dependent); return wrapper; } END push(@implContent, <parents}) { push(@implContent, <attributes}; $index++) { my $attribute = @{$interface->attributes}[$index]; if ($attribute->signature->extendedAttributes->{"InitializedByEventConstructor"}) { my $attributeName = $attribute->signature->name; push(@implContent, " options.get(\"$attributeName\", eventInit.$attributeName);\n"); } } push(@implContent, <name; my $viewType = GetTypeNameOfExternalTypedArray($interface); my $type = $interface->extendedAttributes->{"TypedArray"}; AddToImplIncludes("V8ArrayBufferViewCustom.h"); push(@implContentInternals, < constructor(const v8::Arguments& args) { return constructWebGLArray<$interfaceName, V8${interfaceName}, $type>(args, &V8${interfaceName}::info, $viewType); } END } sub GenerateNamedConstructor { my $function = shift; my $interface = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; my $raisesExceptions = @{$function->raisesExceptions}; if ($interface->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 ($codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject")) { $toActiveDOMObject = "${v8InterfaceName}::toActiveDOMObject"; } my $toEventTarget = "0"; if ($codeGenerator->InheritsExtendedAttribute($interface, "EventTarget")) { $toEventTarget = "${v8InterfaceName}::toEventTarget"; } AddToImplIncludes("Frame.h"); push(@implContent, < ${v8InterfaceName}ConstructorCallback(const v8::Arguments& args) { ${maybeObserveFeature} END push(@implContent, GenerateConstructorHeader()); AddToImplIncludes("V8Document.h"); push(@implContent, <extendedAttributes->{"ConstructorRaisesException"}) { push(@afterArgumentList, "ec"); } my @argumentList; my $index = 0; foreach my $parameter (@{$function->parameters}) { last if $index eq $paramIndex; if ($replacements{$parameter->name}) { push(@argumentList, $replacements{$parameter->name}); } else { push(@argumentList, $parameter->name); } $index++; } my $argumentString = join(", ", @beforeArgumentList, @argumentList, @afterArgumentList); push(@implContent, "\n"); push(@implContent, " RefPtr<${interfaceName}> impl = ${interfaceName}::createForJSConstructor(${argumentString});\n"); push(@implContent, " v8::Handle wrapper = args.Holder();\n"); if ($interface->extendedAttributes->{"ConstructorRaisesException"}) { push(@implContent, " if (ec)\n"); push(@implContent, " goto fail;\n"); } push(@implContent, < ${v8InterfaceName}Constructor::GetTemplate(v8::Isolate* isolate, WrapperWorldType currentWorldType) { static v8::Persistent cachedTemplate; if (!cachedTemplate.IsEmpty()) return cachedTemplate; v8::HandleScope scope; v8::Local result = v8::FunctionTemplate::New(${v8InterfaceName}ConstructorCallback); v8::Local instance = result->InstanceTemplate(); instance->SetInternalFieldCount(${v8InterfaceName}::internalFieldCount); result->SetClassName(v8::String::NewSymbol("${interfaceName}")); result->Inherit(${v8InterfaceName}::GetTemplate(isolate, currentWorldType)); cachedTemplate = v8::Persistent::New(isolate, result); return cachedTemplate; } END } sub GenerateConstructorHeader { my $content = <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(" . $accessControl . ")"; my $customAccessor = HasCustomGetter($attrExt) || HasCustomSetter($attrExt) || ""; if ($customAccessor eq "VALUE_IS_MISSING") { # use the naming convension, interface + (capitalize) attr name $customAccessor = $interfaceName . "::" . $attrName; } my $getter; my $setter; my $getterForMainWorld; my $setterForMainWorld; my $propAttr = "v8::None"; # 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"}); } $data = "&V8${constructorType}::info"; $getter = "${interfaceName}V8Internal::${interfaceName}ConstructorGetter"; $setter = "${interfaceName}V8Internal::${interfaceName}ReplaceableAttrSetterCallback"; $getterForMainWorld = "0"; $setterForMainWorld = "0"; } else { # Default Getter and Setter $getter = "${interfaceName}V8Internal::${attrName}AttrGetterCallback"; $setter = "${interfaceName}V8Internal::${attrName}AttrSetterCallback"; $getterForMainWorld = "${getter}ForMainWorld"; $setterForMainWorld = "${setter}ForMainWorld"; if (!HasCustomSetter($attrExt) && $attrExt->{"Replaceable"}) { $setter = "${interfaceName}V8Internal::${interfaceName}ReplaceableAttrSetterCallback"; $setterForMainWorld = "0"; } } # Read only attributes if (IsReadonly($attribute)) { $setter = "0"; $setterForMainWorld = "0"; } # An accessor can be installed on the proto if ($attrExt->{"V8OnProto"}) { $on_proto = "1 /* on proto */"; } if (!$attrExt->{"V8PerWorldBindings"}) { $getterForMainWorld = "0"; $setterForMainWorld = "0"; } my $commentInfo = "Attribute '$attrName' (Type: '" . $attribute->type . "' ExtAttr: '" . join(' ', keys(%{$attrExt})) . "')"; push(@implContent, $indent . " \/\/ $commentInfo\n"); push(@implContent, $indent . " {\"$attrName\", $getter, $setter, $getterForMainWorld, $setterForMainWorld, $data, $accessControl, static_cast($propAttr), $on_proto}" . $delimiter . "\n"); } sub IsStandardFunction { my $interface = shift; my $function = shift; my $interfaceName = $interface->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"} && ($interface->extendedAttributes->{"CheckSecurity"} || $interfaceName eq "DOMWindow")); return 0 if $attrExt->{"NotEnumerable"}; return 0 if $attrExt->{"V8ReadOnly"}; return 1; } sub GenerateNonStandardFunction { my $interface = shift; my $function = shift; my $interfaceName = $interface->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 ($interface->extendedAttributes->{"CheckSecurity"} && $attrExt->{"DoNotCheckSecurity"}) { # 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, <SetAccessor(v8::String::NewSymbol("$name"), ${interfaceName}V8Internal::${name}AttrGetterCallback, ${interfaceName}V8Internal::${interfaceName}DomainSafeFunctionSetter, v8Undefined(), v8::ALL_CAN_READ, static_cast($property_attributes)); END return; } my $signature = "defaultSignature"; if ($attrExt->{"V8DoNotCheckSignature"} || $function->isStatic) { $signature = "v8::Local()"; } if (RequiresCustomSignature($function)) { $signature = "${name}Signature"; push(@implContent, "\n // Custom Signature '$name'\n", CreateCustomSignature($function)); } if ($property_attributes eq "v8::DontDelete") { $property_attributes = ""; } else { $property_attributes = ", static_cast($property_attributes)"; } if ($template eq "proto" && $conditional eq "" && $signature eq "defaultSignature" && $property_attributes eq "") { 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(${interfaceName}V8Internal::${name}MethodCallback, v8Undefined(), ${signature})$property_attributes);\n"); push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; } sub GenerateImplementationIndexer { my $interface = shift; my $indexer = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; # FIXME: Figure out what NumericIndexedGetter is really supposed to do. Right now, it's only set on WebGL-related files. my $hasCustomSetter = $interface->extendedAttributes->{"CustomIndexedSetter"} && !$interface->extendedAttributes->{"NumericIndexedGetter"}; my $hasGetter = $interface->extendedAttributes->{"IndexedGetter"} || $interface->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 ($interface->extendedAttributes->{"CustomNamedSetter"}) { $hasCustomSetter = 1; } } my $hasEnumerator = !$isSpecialCase && $codeGenerator->InheritsInterface($interface, "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($interface, "item"); } my $indexerType = $indexer ? $indexer->type : 0; # FIXME: Remove this once toV8 helper methods are implemented (see https://bugs.webkit.org/show_bug.cgi?id=32563). if ($interfaceName eq "WebKitCSSKeyframesRule") { $indexerType = "WebKitCSSKeyframeRule"; } if ($indexerType && !$hasCustomSetter) { if ($indexerType eq "DOMString") { my $conversion = $indexer->extendedAttributes->{"TreatReturnedNullStringAs"}; if ($conversion && $conversion eq "Null") { push(@implContent, <(desc); END } else { push(@implContent, <(desc); END } } else { push(@implContent, <(desc); END # Include the header for this indexer type, because setCollectionIndexedGetter() requires toV8() for this type. AddToImplIncludes("V8${indexerType}.h"); } return; } my $hasDeleter = $interface->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(${v8InterfaceName}::indexedPropertyGetter"); push(@implContent, $hasCustomSetter ? ", ${v8InterfaceName}::indexedPropertySetter" : ", 0"); push(@implContent, ", 0"); # IndexedPropertyQuery -- not being used at the moment. push(@implContent, $hasDeleter ? ", ${v8InterfaceName}::indexedPropertyDeleter" : ", 0"); push(@implContent, ", nodeCollectionIndexedPropertyEnumerator<${interfaceName}>") if $hasEnumerator; push(@implContent, ");\n"); } sub GenerateImplementationNamedPropertyGetter { my $interface = shift; my $namedPropertyGetter = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; my $hasCustomNamedGetter = $interface->extendedAttributes->{"CustomNamedGetter"} || $interface->extendedAttributes->{"CustomGetOwnPropertySlot"}; if ($interfaceName eq "HTMLAppletElement" || $interfaceName eq "HTMLEmbedElement" || $interfaceName eq "HTMLObjectElement") { $hasCustomNamedGetter = 1; } if ($interfaceName eq "HTMLDocument") { $hasCustomNamedGetter = 0; } my $hasGetter = $interface->extendedAttributes->{"NamedGetter"} || $hasCustomNamedGetter; if (!$hasGetter) { return; } if (!$namedPropertyGetter) { $namedPropertyGetter = $codeGenerator->FindSuperMethod($interface, "namedItem"); } if ($namedPropertyGetter && $namedPropertyGetter->type ne "Node" && !$namedPropertyGetter->extendedAttributes->{"Custom"} && !$hasCustomNamedGetter) { AddToImplIncludes("V8Collection.h"); my $type = $namedPropertyGetter->type; push(@implContent, <(desc); END return; } my $hasCustomNamedSetter = $interface->extendedAttributes->{"CustomNamedSetter"}; my $hasDeleter = $interface->extendedAttributes->{"CustomDeleteProperty"}; my $hasEnumerator = $interface->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; } if ($interfaceName eq "HTMLPropertiesCollection") { push(@implContent, " desc->${setOn}Template()->SetNamedPropertyHandler(V8HTMLCollection::namedPropertyGetter, "); } else { push(@implContent, " desc->${setOn}Template()->SetNamedPropertyHandler(${v8InterfaceName}::namedPropertyGetter, "); } push(@implContent, $hasCustomNamedSetter ? "${v8InterfaceName}::namedPropertySetter, " : "0, "); # If there is a custom enumerator, there MUST be custom query to properly communicate property attributes. push(@implContent, $hasEnumerator ? "${v8InterfaceName}::namedPropertyQuery, " : "0, "); push(@implContent, $hasDeleter ? "${v8InterfaceName}::namedPropertyDeleter, " : "0, "); push(@implContent, $hasEnumerator ? "${v8InterfaceName}::namedPropertyEnumerator" : "0"); push(@implContent, ");\n"); } sub GenerateImplementationCustomCall { my $interface = shift; my $interfaceName = $interface->name; if ($interface->extendedAttributes->{"CustomCall"}) { push(@implContent, " desc->InstanceTemplate()->SetCallAsFunctionHandler(V8${interfaceName}::callAsFunctionCallback);\n"); } } sub GenerateImplementationMasqueradesAsUndefined { my $interface = shift; if ($interface->extendedAttributes->{"MasqueradesAsUndefined"}) { push(@implContent, " desc->InstanceTemplate()->MarkAsUndetectable();\n"); } } sub GenerateImplementation { my $object = shift; my $interface = shift; my $interfaceName = $interface->name; my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($interface); my $v8InterfaceName = "V8$interfaceName"; my $nativeType = GetNativeTypeForConversions($interface); my $vtableNameGnu = GetGnuVTableNameForInterface($interface); my $vtableRefGnu = GetGnuVTableRefForInterface($interface); my $vtableRefWin = GetWinVTableRefForInterface($interface); # - Add default header template push(@implContentHeader, GenerateImplementationContentHeader($interface)); AddToImplIncludes("BindingState.h"); AddToImplIncludes("ContextFeatures.h"); AddToImplIncludes("RuntimeEnabledFeatures.h"); AddToImplIncludes("V8Binding.h"); AddToImplIncludes("V8DOMWrapper.h"); AddIncludesForType($interfaceName); my $toActiveDOMObject = $codeGenerator->InheritsExtendedAttribute($interface, "ActiveDOMObject") ? "${v8InterfaceName}::toActiveDOMObject" : "0"; my $toEventTarget = $codeGenerator->InheritsExtendedAttribute($interface, "EventTarget") ? "${v8InterfaceName}::toEventTarget" : "0"; my $rootForGC = NeedsCustomOpaqueRootForGC($interface) ? "${v8InterfaceName}::opaqueRootForGC" : "0"; # Find the super descriptor. my $parentClass = ""; my $parentClassTemplate = ""; foreach (@{$interface->parents}) { my $parent = $_; AddToImplIncludes("V8${parent}.h"); $parentClass = "V8" . $parent; $parentClassTemplate = $parentClass . "::GetTemplate(isolate, currentWorldType)"; last; } push(@implContentInternals, <(object)); #if defined(OS_WIN) void* expectedVTablePointer = reinterpret_cast(${vtableRefWin}); #else void* expectedVTablePointer = ${vtableRefGnu}; #endif if (actualVTablePointer != expectedVTablePointer) CRASH(); } #endif // ENABLE(BINDING_INTEGRITY) END my $parentClassInfo = $parentClass ? "&${parentClass}::info" : "0"; my $WrapperTypePrototype = $interface->isException ? "WrapperTypeErrorPrototype" : "WrapperTypeObjectPrototype"; push(@implContentInternals, "WrapperTypeInfo ${v8InterfaceName}::info = { ${v8InterfaceName}::GetTemplate, ${v8InterfaceName}::derefObject, $toActiveDOMObject, $toEventTarget, $rootForGC, ${v8InterfaceName}::installPerContextPrototypeProperties, $parentClassInfo, $WrapperTypePrototype };\n\n"); push(@implContentInternals, "namespace ${interfaceName}V8Internal {\n\n"); push(@implContentInternals, "template void V8_USE(T) { }\n\n"); my $hasConstructors = 0; my $hasReplaceable = 0; # Generate property accessors for attributes. for (my $index = 0; $index < @{$interface->attributes}; $index++) { my $attribute = @{$interface->attributes}[$index]; my $attrType = $attribute->signature->type; my $attrExt = $attribute->signature->extendedAttributes; # Generate special code for the constructor attributes. if ($attrType =~ /Constructor$/) { if (!HasCustomGetter($attrExt)) { $hasConstructors = 1; } next; } if ($attrType eq "EventListener" && $interfaceName eq "DOMWindow") { $attrExt->{"V8OnProto"} = 1; } if ($attrType eq "SerializedScriptValue") { AddToImplIncludes("SerializedScriptValue.h"); } GenerateNormalAttrGetter($attribute, $interface, ""); GenerateNormalAttrGetterCallback($attribute, $interface, ""); if ($attrExt->{"V8PerWorldBindings"}) { GenerateNormalAttrGetter($attribute, $interface, "ForMainWorld"); GenerateNormalAttrGetterCallback($attribute, $interface, "ForMainWorld"); } if (!HasCustomSetter($attrExt) && $attrExt->{"Replaceable"}) { $hasReplaceable = 1; } elsif (!IsReadonly($attribute)) { GenerateNormalAttrSetter($attribute, $interface, ""); GenerateNormalAttrSetterCallback($attribute, $interface, ""); if ($attrExt->{"V8PerWorldBindings"}) { GenerateNormalAttrSetter($attribute, $interface, "ForMainWorld"); GenerateNormalAttrSetterCallback($attribute, $interface, "ForMainWorld"); } } } if ($hasConstructors) { GenerateConstructorGetter($interface); } if ($hasConstructors || $hasReplaceable) { GenerateReplaceableAttrSetter($interface); GenerateReplaceableAttrSetterCallback($interface); } if (NeedsCustomOpaqueRootForGC($interface)) { GenerateOpaqueRootForGC($interface); } if ($interface->extendedAttributes->{"CheckSecurity"} && $interface->name ne "DOMWindow") { GenerateSecurityCheckFunctions($interface); } if ($interface->extendedAttributes->{"TypedArray"}) { my $viewType = GetTypeNameOfExternalTypedArray($interface); push(@implContent, < wrap($interfaceName* impl, v8::Handle creationContext, v8::Isolate* isolate) { ASSERT(impl); v8::Handle wrapper = ${v8InterfaceName}::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 (@{$interface->functions}) { GenerateFunction($function, $interface); if ($function->{overloadIndex} == @{$function->{overloads}}) { if ($function->{overloadIndex} > 1) { GenerateOverloadedFunction($function, $interface); } GenerateFunctionCallback($function, $interface); } if ($function->signature->name eq "item") { $indexer = $function->signature; } if ($interfaceName eq "HTMLPropertiesCollection") { if ($function->signature->name eq "propertyNodeList") { $namedPropertyGetter = $function->signature; } } elsif ($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 ($interface->extendedAttributes->{"CheckSecurity"} && $function->signature->extendedAttributes->{"DoNotCheckSecurity"}) { if (!HasCustomMethod($function->signature->extendedAttributes) || $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 = $interface->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($interface, \@disallowsShadowing); push(@implContent, "};\n\n"); } my $has_attributes = 0; if (@$attributes) { $has_attributes = 1; push(@implContent, "static const V8DOMConfiguration::BatchedAttribute ${v8InterfaceName}Attrs[] = {\n"); GenerateBatchedAttributeData($interface, $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($interface, $function); if (!$has_callbacks) { $has_callbacks = 1; push(@implContent, "static const V8DOMConfiguration::BatchedMethod ${v8InterfaceName}Methods[] = {\n"); } my $name = $function->signature->name; my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature); push(@implContent, "#if ${conditionalString}\n") if $conditionalString; push(@implContent, <constants}) { $has_constants = 1; push(@implContent, "static const V8DOMConfiguration::BatchedConstant ${v8InterfaceName}Consts[] = {\n"); } foreach my $constant (@{$interface->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 { if ($conditional) { my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional); push(@implContent, "#if ${conditionalString}\n"); } # If the value we're dealing with is a hex number, preprocess it into a signed integer # here, rather than running static_cast in the generated code. if (substr($value, 0, 2) eq "0x") { $value = unpack('i', pack('I', hex($value))); } push(@implContent, <GenerateCompileTimeCheckForEnumsIfNeeded($interface)); } if (!HasCustomConstructor($interface)) { if ($interface->extendedAttributes->{"NamedConstructor"}) { GenerateNamedConstructor(@{$interface->constructors}[0], $interface); } elsif ($interface->extendedAttributes->{"Constructor"}) { GenerateConstructor($interface); } elsif ($codeGenerator->IsConstructorTemplate($interface, "Event")) { GenerateEventConstructor($interface); } elsif ($codeGenerator->IsConstructorTemplate($interface, "TypedArray")) { GenerateTypedArrayConstructor($interface); } } if (IsConstructable($interface)) { GenerateConstructorCallback($interface); } push(@implContentInternals, "} // namespace ${interfaceName}V8Internal\n\n"); my $access_check = ""; if ($interface->extendedAttributes->{"CheckSecurity"} && $interfaceName ne "DOMWindow") { $access_check = "instance->SetAccessCheckCallbacks(${interfaceName}V8Internal::namedSecurityCheck, ${interfaceName}V8Internal::indexedSecurityCheck, v8::External::New(&${v8InterfaceName}::info));"; } # For the DOMWindow interface, generate the shadow object template # configuration method. if ($interfaceName eq "DOMWindow") { push(@implContent, < ConfigureShadowObjectTemplate(v8::Persistent templ, v8::Isolate* isolate, WrapperWorldType currentWorldType) { V8DOMConfiguration::batchConfigureAttributes(templ, v8::Handle(), shadowAttrs, WTF_ARRAY_LENGTH(shadowAttrs), isolate, currentWorldType); // Install a security handler with V8. templ->SetAccessCheckCallbacks(V8DOMWindow::namedSecurityCheckCustom, V8DOMWindow::indexedSecurityCheckCustom, v8::External::New(&V8DOMWindow::info)); templ->SetInternalFieldCount(V8DOMWindow::internalFieldCount); return templ; } END } if (!$parentClassTemplate) { $parentClassTemplate = "v8::Persistent()"; } # Generate the template configuration method push(@implContent, < Configure${v8InterfaceName}Template(v8::Persistent desc, v8::Isolate* isolate, WrapperWorldType currentWorldType) { desc->ReadOnlyPrototype(); v8::Local defaultSignature; END if ($interface->extendedAttributes->{"V8EnabledAtRuntime"}) { my $enable_function = GetRuntimeEnableFunctionName($interface); push(@implContent, <GenerateConstructorConditionalString($interface); push(@implContent, "#if $conditionalString\n") if $conditionalString; push(@implContent, " desc->SetCallHandler(${v8InterfaceName}::constructorCallback);\n"); push(@implContent, "#endif // $conditionalString\n") if $conditionalString; } if ($access_check or @enabledAtRuntimeAttributes or @normalFunctions or $has_constants) { push(@implContent, < instance = desc->InstanceTemplate(); v8::Local proto = desc->PrototypeTemplate(); UNUSED_PARAM(instance); // In some cases, it will not be used. UNUSED_PARAM(proto); // In some cases, it will not be used. END } push(@implContent, " $access_check\n"); # Setup the enable-at-runtime attrs if we have them foreach my $runtime_attr (@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, <GenerateConditionalString($runtime_const); my $name = $runtime_const->name; my $value = $runtime_const->value; push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString; push(@implContent, " if (${enable_function}()) {\n"); push(@implContent, <(${value})}; V8DOMConfiguration::batchConfigureConstants(desc, proto, &constData, 1, isolate); END push(@implContent, " }\n"); push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; } GenerateImplementationIndexer($interface, $indexer); GenerateImplementationNamedPropertyGetter($interface, $namedPropertyGetter); GenerateImplementationCustomCall($interface); GenerateImplementationMasqueradesAsUndefined($interface); # 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($interface, $function); GenerateNonStandardFunction($interface, $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, <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::namedSecurityCheckCustom, V8DOMWindow::indexedSecurityCheckCustom, v8::External::New(&V8DOMWindow::info), false); END } if ($interfaceName eq "HTMLDocument" or $interfaceName eq "DedicatedWorkerContext" or $interfaceName eq "SharedWorkerContext") { push(@implContent, <SetHiddenPrototype(true); END } if ($interfaceName eq "Location") { push(@implContent, <SetAccessor(v8::String::NewSymbol("reload"), V8Location::reloadAttrGetterCustom, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast(v8::DontDelete | v8::ReadOnly)); instance->SetAccessor(v8::String::NewSymbol("replace"), V8Location::replaceAttrGetterCustom, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast(v8::DontDelete | v8::ReadOnly)); instance->SetAccessor(v8::String::NewSymbol("assign"), V8Location::assignAttrGetterCustom, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast(v8::DontDelete | v8::ReadOnly)); END } push(@implContent, <Set(v8::String::NewSymbol("toString"), V8PerIsolateData::current()->toStringTemplate()); return desc; } v8::Persistent ${v8InterfaceName}::GetTemplate(v8::Isolate* isolate, WrapperWorldType currentWorldType) { V8PerIsolateData* data = V8PerIsolateData::from(isolate); V8PerIsolateData::TemplateMap::iterator result = data->templateMap(currentWorldType).find(&info); if (result != data->templateMap(currentWorldType).end()) return result->value; v8::HandleScope handleScope; v8::Persistent templ = Configure${v8InterfaceName}Template(data->rawTemplate(&info, currentWorldType), isolate, currentWorldType); data->templateMap(currentWorldType).add(&info, templ); return templ; } bool ${v8InterfaceName}::HasInstance(v8::Handle value, v8::Isolate* isolate, WrapperWorldType currentWorldType) { return V8PerIsolateData::from(isolate)->hasInstance(&info, value, currentWorldType); } bool ${v8InterfaceName}::HasInstanceInAnyWorld(v8::Handle value, v8::Isolate* isolate) { return V8PerIsolateData::from(isolate)->hasInstance(&info, value, MainWorld) || V8PerIsolateData::from(isolate)->hasInstance(&info, value, IsolatedWorld) || V8PerIsolateData::from(isolate)->hasInstance(&info, value, WorkerWorld); } END if (@enabledPerContextAttributes) { push(@implContent, < instance, ${nativeType}* impl, v8::Isolate* isolate) { v8::Local proto = v8::Local::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, < proto, v8::Isolate* isolate) { UNUSED_PARAM(proto); END # Setup the enable-by-settings functions if we have them push(@implContent, < defaultSignature = v8::Signature::New(GetTemplate(isolate, worldType(isolate))); 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}(toDocument(context))) {\n"); my $name = $runtimeFunc->signature->name; push(@implContent, <Set(v8::String::NewSymbol("${name}"), v8::FunctionTemplate::New(${interfaceName}V8Internal::${name}MethodCallback, v8Undefined(), defaultSignature)->GetFunction()); END push(@implContent, " }\n"); push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; } push(@implContent, <InheritsExtendedAttribute($interface, "ActiveDOMObject")) { # MessagePort is handled like an active dom object even though it doesn't inherit # from ActiveDOMObject, so don't try to cast it to ActiveDOMObject. my $returnValue = $interfaceName eq "MessagePort" ? "0" : "toNative(object)"; push(@implContent, < object) { return $returnValue; } END } if ($codeGenerator->InheritsExtendedAttribute($interface, "EventTarget")) { push(@implContent, < object) { return toNative(object); } END } if ($interfaceName eq "DOMWindow") { push(@implContent, < V8DOMWindow::GetShadowObjectTemplate(v8::Isolate* isolate, WrapperWorldType currentWorldType) { if (currentWorldType == MainWorld) { static v8::Persistent V8DOMWindowShadowObjectCacheForMainWorld; if (V8DOMWindowShadowObjectCacheForMainWorld.IsEmpty()) { V8DOMWindowShadowObjectCacheForMainWorld = v8::Persistent::New(isolate, v8::ObjectTemplate::New()); ConfigureShadowObjectTemplate(V8DOMWindowShadowObjectCacheForMainWorld, isolate, currentWorldType); } return V8DOMWindowShadowObjectCacheForMainWorld; } else { static v8::Persistent V8DOMWindowShadowObjectCacheForNonMainWorld; if (V8DOMWindowShadowObjectCacheForNonMainWorld.IsEmpty()) { V8DOMWindowShadowObjectCacheForNonMainWorld = v8::Persistent::New(isolate, v8::ObjectTemplate::New()); ConfigureShadowObjectTemplate(V8DOMWindowShadowObjectCacheForNonMainWorld, isolate, currentWorldType); } return V8DOMWindowShadowObjectCacheForNonMainWorld; } } END } GenerateToV8Converters($interface, $v8InterfaceName, $nativeType); push(@implContent, <(object)->deref(); } } // namespace WebCore END my $conditionalString = $codeGenerator->GenerateConditionalString($interface); push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; # We've already added the header for this file in implContentHeader, so remove # it from implIncludes to ensure we don't #include it twice. delete $implIncludes{"${v8InterfaceName}.h"}; } sub GenerateHeaderContentHeader { my $interface = shift; my $v8InterfaceName = "V8" . $interface->name; my $conditionalString = $codeGenerator->GenerateConditionalString($interface); my @headerContentHeader = split("\r", $headerTemplate); push(@headerContentHeader, "\n#ifndef ${v8InterfaceName}" . "_h\n"); push(@headerContentHeader, "#define ${v8InterfaceName}" . "_h\n\n"); push(@headerContentHeader, "#if ${conditionalString}\n") if $conditionalString; return @headerContentHeader; } sub GenerateImplementationContentHeader { my $interface = shift; my $v8InterfaceName = "V8" . $interface->name; my $conditionalString = $codeGenerator->GenerateConditionalString($interface); my @implContentHeader = split("\r", $headerTemplate); push(@implContentHeader, "\n#include \"config.h\"\n"); push(@implContentHeader, "#if ${conditionalString}\n") if $conditionalString; push(@implContentHeader, "#include \"${v8InterfaceName}.h\"\n\n"); return @implContentHeader; } sub GenerateCallbackHeader { my $object = shift; my $interface = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; # - Add default header template push(@headerContent, GenerateHeaderContentHeader($interface)); my @unsortedIncludes = (); push(@unsortedIncludes, "#include \"ActiveDOMCallback.h\""); push(@unsortedIncludes, "#include \"$interfaceName.h\""); push(@unsortedIncludes, "#include \"ScopedPersistent.h\""); push(@unsortedIncludes, "#include \"WorldContextHandle.h\""); push(@unsortedIncludes, "#include "); push(@unsortedIncludes, "#include "); push(@headerContent, join("\n", sort @unsortedIncludes)); push(@headerContent, "\n\nnamespace WebCore {\n\n"); push(@headerContent, "class ScriptExecutionContext;\n\n"); push(@headerContent, "class $v8InterfaceName : public $interfaceName, public ActiveDOMCallback {\n"); push(@headerContent, < create(v8::Handle value, ScriptExecutionContext* context) { ASSERT(value->IsObject()); ASSERT(context); return adoptRef(new ${v8InterfaceName}(v8::Handle::Cast(value), context)); } virtual ~${v8InterfaceName}(); END # Functions my $numFunctions = @{$interface->functions}; if ($numFunctions > 0) { push(@headerContent, " // Functions\n"); foreach my $function (@{$interface->functions}) { my @params = @{$function->parameters}; if (!$function->signature->extendedAttributes->{"Custom"} && !(GetNativeType($function->signature->type) eq "bool")) { push(@headerContent, " COMPILE_ASSERT(false)"); } push(@headerContent, " virtual " . GetNativeTypeForCallbacks($function->signature->type) . " " . $function->signature->name . "("); my @args = (); foreach my $param (@params) { push(@args, GetNativeTypeForCallbacks($param->type) . " " . $param->name); } push(@headerContent, join(", ", @args)); push(@headerContent, ");\n"); } } push(@headerContent, <, ScriptExecutionContext*); ScopedPersistent m_callback; WorldContextHandle m_worldContext; }; END push(@headerContent, "}\n\n"); push(@headerContent, "#endif // $v8InterfaceName" . "_h\n\n"); my $conditionalString = $codeGenerator->GenerateConditionalString($interface); push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString; } sub GenerateCallbackImplementation { my $object = shift; my $interface = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; # - Add default header template push(@implContentHeader, GenerateImplementationContentHeader($interface)); AddToImplIncludes("ScriptExecutionContext.h"); AddToImplIncludes("V8Binding.h"); AddToImplIncludes("V8Callback.h"); push(@implContent, "#include \n\n"); push(@implContent, "namespace WebCore {\n\n"); push(@implContent, < callback, ScriptExecutionContext* context) : ActiveDOMCallback(context) , m_callback(callback) , m_worldContext(UseCurrentWorld) { } ${v8InterfaceName}::~${v8InterfaceName}() { } END # Functions my $numFunctions = @{$interface->functions}; if ($numFunctions > 0) { push(@implContent, "// Functions\n"); foreach my $function (@{$interface->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, < v8Context = toV8Context(scriptExecutionContext(), m_worldContext);\n"); push(@implContent, " if (v8Context.IsEmpty())\n"); push(@implContent, " return true;\n\n"); push(@implContent, " v8::Context::Scope scope(v8Context);\n\n"); @args = (); foreach my $param (@params) { my $paramName = $param->name; push(@implContent, " v8::Handle ${paramName}Handle = " . NativeToJSValue($param, $paramName, "v8::Handle()", "v8Context->GetIsolate()", "") . ";\n"); push(@implContent, " if (${paramName}Handle.IsEmpty()) {\n"); push(@implContent, " if (!isScriptControllerTerminating())\n"); push(@implContent, " CRASH();\n"); push(@implContent, " return true;\n"); push(@implContent, " }\n"); push(@args, " ${paramName}Handle"); } if (scalar(@args) > 0) { push(@implContent, "\n v8::Handle argv[] = {\n"); push(@implContent, join(",\n", @args)); push(@implContent, "\n };\n\n"); } else { push(@implContent, "\n v8::Handle *argv = 0;\n\n"); } push(@implContent, " bool callbackReturnValue = false;\n"); if ($thisType) { foreach my $param (@params) { next if $param->type ne $thisType; my $paramName = $param->name; push(@implContent, " return !invokeCallback(m_callback.get(), v8::Handle::Cast(${paramName}Handle), " . scalar(@params) . ", argv, callbackReturnValue, scriptExecutionContext());\n"); last; } } else { push(@implContent, " return !invokeCallback(m_callback.get(), " . scalar(@params) . ", argv, callbackReturnValue, scriptExecutionContext());\n"); } push(@implContent, "}\n"); } } push(@implContent, "\n} // namespace WebCore\n\n"); my $conditionalString = $codeGenerator->GenerateConditionalString($interface); push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString; } sub BaseInterfaceName { my $interface = shift; while (@{$interface->parents}) { $interface = $codeGenerator->ParseInterface(@{$interface->parents}[0], 1); } return $interface->name; } sub GenerateToV8Converters { my $interface = shift; my $v8InterfaceName = shift; my $nativeType = shift; my $interfaceName = $interface->name; if ($interface->extendedAttributes->{"V8NoWrapperCache"} || $interface->extendedAttributes->{"SuppressToJSObject"}) { return; } AddToImplIncludes("Frame.h"); my $createWrapperArgumentType = GetPassRefPtrType($nativeType); my $baseType = BaseInterfaceName($interface); push(@implContent, < ${v8InterfaceName}::createWrapper(${createWrapperArgumentType} impl, v8::Handle creationContext, v8::Isolate* isolate) { ASSERT(impl.get()); ASSERT(DOMDataStore::getWrapper(impl.get(), isolate).IsEmpty()); END my $vtableNameGnu = GetGnuVTableNameForInterface($interface); push(@implContent, <(static_cast<${baseType}*>(impl.get())) == static_cast(impl.get())); END if ($codeGenerator->InheritsInterface($interface, "Document")) { push(@implContent, <frame()) { if (frame->script()->initializeMainWorld()) { // initializeMainWorld may have created a wrapper for the object, retry from the start. v8::Handle wrapper = DOMDataStore::getWrapper(impl.get(), isolate); if (!wrapper.IsEmpty()) return wrapper; } } END } push(@implContent, < wrapper = V8DOMWrapper::createWrapper(creationContext, &info, impl.get(), isolate); if (UNLIKELY(wrapper.IsEmpty())) return wrapper; installPerContextProperties(wrapper, impl.get(), isolate); V8DOMWrapper::associateObjectWithWrapper(impl, &info, wrapper, isolate, hasDependentLifetime ? WrapperConfiguration::Dependent : WrapperConfiguration::Independent); return wrapper; } END } sub GenerateSecurityCheckFunctions { my $interface = shift; my $interfaceName = $interface->name; my $v8InterfaceName = "V8$interfaceName"; push(@implContentInternals, < host, uint32_t index, v8::AccessType type, v8::Local) { $interfaceName* imp = ${v8InterfaceName}::toNative(host); return BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame(), DoNotReportSecurityError); } bool namedSecurityCheck(v8::Local host, v8::Local key, v8::AccessType type, v8::Local) { $interfaceName* imp = ${v8InterfaceName}::toNative(host); return BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame(), DoNotReportSecurityError); } END } sub GetNativeTypeForConversions { my $interface = shift; my $interfaceName = $interface->name; $interfaceName = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName) if $codeGenerator->IsSVGTypeNeedingTearOff($interfaceName); return $interfaceName; } # See http://refspecs.linux-foundation.org/cxxabi-1.83.html. sub GetGnuVTableRefForInterface { my $interface = shift; my $vtableName = GetGnuVTableNameForInterface($interface); if (!$vtableName) { return "0"; } my $typename = GetNativeTypeForConversions($interface); my $offset = GetGnuVTableOffsetForType($typename); return "&" . $vtableName . "[" . $offset . "]"; } sub GetGnuVTableNameForInterface { my $interface = shift; my $typename = GetNativeTypeForConversions($interface); my $templatePosition = index($typename, "<"); return "" if $templatePosition != -1; return "" if GetImplementationLacksVTableForInterface($interface); return "" if GetV8SkipVTableValidationForInterface($interface); return "_ZTV" . GetGnuMangledNameForInterface($interface); } sub GetGnuMangledNameForInterface { my $interface = shift; my $typename = GetNativeTypeForConversions($interface); my $templatePosition = index($typename, "<"); if ($templatePosition != -1) { return ""; } my $mangledType = length($typename) . $typename; my $namespace = GetNamespaceForInterface($interface); my $mangledNamespace = "N" . length($namespace) . $namespace; return $mangledNamespace . $mangledType . "E"; } sub GetGnuVTableOffsetForType { my $typename = shift; if ($typename eq "SVGAElement" || $typename eq "SVGCircleElement" || $typename eq "SVGClipPathElement" || $typename eq "SVGDefsElement" || $typename eq "SVGEllipseElement" || $typename eq "SVGForeignObjectElement" || $typename eq "SVGGElement" || $typename eq "SVGImageElement" || $typename eq "SVGLineElement" || $typename eq "SVGPathElement" || $typename eq "SVGPolyElement" || $typename eq "SVGPolygonElement" || $typename eq "SVGPolylineElement" || $typename eq "SVGRectElement" || $typename eq "SVGSVGElement" || $typename eq "SVGStyledLocatableElement" || $typename eq "SVGStyledTransformableElement" || $typename eq "SVGSwitchElement" || $typename eq "SVGTextElement" || $typename eq "SVGTransformable" || $typename eq "SVGUseElement") { return "3"; } return "2"; } # See http://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B_Name_Mangling. sub GetWinVTableRefForInterface { my $interface = shift; my $vtableName = GetWinVTableNameForInterface($interface); return 0 if !$vtableName; return "__identifier(\"" . $vtableName . "\")"; } sub GetWinVTableNameForInterface { my $interface = shift; my $typename = GetNativeTypeForConversions($interface); my $templatePosition = index($typename, "<"); return "" if $templatePosition != -1; return "" if GetImplementationLacksVTableForInterface($interface); return "" if GetV8SkipVTableValidationForInterface($interface); return "??_7" . GetWinMangledNameForInterface($interface) . "6B@"; } sub GetWinMangledNameForInterface { my $interface = shift; my $typename = GetNativeTypeForConversions($interface); my $namespace = GetNamespaceForInterface($interface); return $typename . "@" . $namespace . "@@"; } sub GetNamespaceForInterface { my $interface = shift; return $interface->extendedAttributes->{"ImplementationNamespace"} || "WebCore"; } sub GetImplementationLacksVTableForInterface { my $interface = shift; return $interface->extendedAttributes->{"ImplementationLacksVTable"}; } sub GetV8SkipVTableValidationForInterface { my $interface = shift; return $interface->extendedAttributes->{"V8SkipVTableValidation"}; } 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 @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, $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 "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 = $codeGenerator->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 $codeGenerator->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 . " v8::Local exception = state.exception();\n"; $result .= $indent . " state.clearException();\n"; $result .= $indent . " return throwError(exception, args.GetIsolate());\n"; $result .= $indent . "}\n"; } if ($isSVGTearOffType) { AddToImplIncludes("V8$returnType.h"); AddToImplIncludes("SVGPropertyTearOff.h"); my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($returnType); # FIXME: Update for all ScriptWrappables. if (IsDOMNodeType($interfaceName)) { $result .= $indent . "return toV8Fast(WTF::getPtr(${svgNativeType}::create($return)), args, imp);\n"; } else { $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); my $nativeValue; # FIXME: Update for all ScriptWrappables. if (IsDOMNodeType($interfaceName)) { $nativeValue = NativeToJSValue($function->signature, $return, "args.Holder()", "args.GetIsolate()", "args", "imp", "ReturnUnsafeHandle", ""); } else { $nativeValue = NativeToJSValue($function->signature, $return, "args.Holder()", "args.GetIsolate()", 0, 0, "ReturnUnsafeHandle", ""); } $result .= $indent . "return " . $nativeValue . ";\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 "V8StringResource") { # 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 GetNativeType { my $type = shift; my $isParameter = shift; my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($type); if ($svgNativeType) { if ($svgNativeType =~ /List$/) { return "${svgNativeType}*"; } else { return "RefPtr<${svgNativeType} >"; } } return "float" if $type eq "float"; return "double" if $type eq "double"; return "int" if $type eq "long" or $type eq "int" or $type eq "short" or $type eq "unsigned short"; return "unsigned" if $type eq "unsigned long" or $type eq "unsigned int"; 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 "V8StringResource" if ($type eq "DOMString" or $codeGenerator->IsEnumType($type)) and $isParameter; return "String" if $type eq "DOMString" or $codeGenerator->IsEnumType($type); return "Range::CompareHow" if $type eq "CompareHow"; return "DOMTimeStamp" if $type eq "DOMTimeStamp"; return "double" if $type eq "Date"; return "ScriptValue" if $type eq "any"; return "Dictionary" if $type eq "Dictionary"; return "RefPtr" if $type eq "DOMStringList"; return "RefPtr" if $type eq "MediaQueryListListener"; return "RefPtr" if $type eq "NodeFilter"; return "RefPtr" if $type eq "SerializedScriptValue"; return "RefPtr" if $type eq "XPathNSResolver"; return "RefPtr<${type}>" if $codeGenerator->IsRefPtrType($type) and not $isParameter; my $arrayType = $codeGenerator->GetArrayType($type); my $sequenceType = $codeGenerator->GetSequenceType($type); my $arrayOrSequenceType = $arrayType || $sequenceType; if ($arrayOrSequenceType) { my $nativeType = GetNativeType($arrayOrSequenceType); $nativeType .= " " if ($nativeType =~ />$/); return "Vector<${nativeType}>"; } # 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 "PassRefPtr" if $type eq "SerializedScriptValue"; # Callbacks use raw pointers, so pass isParameter = 1 return GetNativeType($type, 1); } 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; my $intConversion = $signature->extendedAttributes->{"EnforceRange"} ? "EnforceRange" : "NormalConversion"; 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"; if ($intConversion ne "NormalConversion") { return "toInt32($value, $intConversion, ok)" if $type eq "long" or $type eq "short"; return "toUInt32($value, $intConversion, ok)" if $type eq "unsigned long" or $type eq "unsigned short"; return "toInt64($value, $intConversion, ok)" if $type eq "long long"; return "toUInt64($value, $intConversion, ok)" if $type eq "unsigned long long"; } else { 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 "long long"; return "toUInt64($value)" if $type eq "unsigned long long"; } return "static_cast($value->Int32Value())" if $type eq "CompareHow"; return "toWebCoreDate($value)" if $type eq "Date"; return "toDOMStringList($value, $getIsolate)" if $type eq "DOMStringList"; if ($type eq "DOMString") { return $value; } if ($codeGenerator->IsEnumType($type)) { return $value; } if ($type eq "SerializedScriptValue") { AddToImplIncludes("SerializedScriptValue.h"); return "SerializedScriptValue::create($value, $getIsolate)"; } if ($type eq "Dictionary") { AddToImplIncludes("Dictionary.h"); return "Dictionary($value, $getIsolate)"; } if ($type eq "any") { AddToImplIncludes("ScriptValue.h"); return "ScriptValue($value)"; } if ($type eq "NodeFilter") { return "toNodeFilter($value)"; } if ($type eq "MediaQueryListListener") { AddToImplIncludes("MediaQueryListListener.h"); return "MediaQueryListListener::create(" . $value . ")"; } if ($type eq "EventTarget") { return "V8DOMWrapper::isDOMWrapper($value) ? toWrapperTypeInfo(v8::Handle::Cast($value))->toEventTarget(v8::Handle::Cast($value)) : 0"; } if ($type eq "XPathNSResolver") { return "toXPathNSResolver($value, $getIsolate)"; } my $arrayType = $codeGenerator->GetArrayType($type); my $sequenceType = $codeGenerator->GetSequenceType($type); my $arrayOrSequenceType = $arrayType || $sequenceType; if ($arrayOrSequenceType) { if ($codeGenerator->IsRefPtrType($arrayOrSequenceType)) { AddToImplIncludes("V8${arrayOrSequenceType}.h"); return "(toRefPtrNativeArray<${arrayOrSequenceType}, V8${arrayOrSequenceType}>($value, $getIsolate))"; } return "toNativeArray<" . GetNativeType($arrayOrSequenceType) . ">($value)"; } AddIncludesForType($type); AddToImplIncludes("V8${type}.h"); return "V8${type}::HasInstance($value, $getIsolate, worldType($getIsolate)) ? V8${type}::toNative(v8::Handle::Cast($value)) : 0"; } sub GetV8HeaderName { my $type = shift; return "V8Event.h" if $type eq "DOMTimeStamp"; return "EventListener.h" if $type eq "EventListener"; return "SerializedScriptValue.h" if $type eq "SerializedScriptValue"; return "ScriptValue.h" if $type eq "any"; return "V8${type}.h"; } sub CreateCustomSignature { my $function = shift; my $count = @{$function->parameters}; my $name = $function->signature->name; my $result = " const int ${name}Argc = ${count};\n" . " v8::Handle ${name}Argv[${name}Argc] = { "; my $first = 1; foreach my $parameter (@{$function->parameters}) { if ($first) { $first = 0; } else { $result .= ", "; } if (IsWrapperType($parameter->type)) { if ($parameter->type eq "XPathNSResolver") { # Special case for XPathNSResolver. All other browsers accepts a callable, # so, even though it's against IDL, accept objects here. $result .= "v8::Handle()"; } else { my $type = $parameter->type; my $arrayType = $codeGenerator->GetArrayType($type); my $sequenceType = $codeGenerator->GetSequenceType($type); my $arrayOrSequenceType = $arrayType || $sequenceType; if ($arrayOrSequenceType) { if ($arrayType eq "DOMString") { AddToImplIncludes("V8DOMStringList.h"); AddToImplIncludes("DOMStringList.h"); } elsif ($codeGenerator->IsRefPtrType($arrayOrSequenceType)) { AddToImplIncludes(GetV8HeaderName($arrayOrSequenceType)); AddToImplIncludes("${arrayOrSequenceType}.h"); } else { $result .= "v8::Handle()"; next; } } else { AddToImplIncludes(GetV8HeaderName($type)); } $result .= "V8PerIsolateData::from(isolate)->rawTemplate(&V8${type}::info, currentWorldType)"; } } else { $result .= "v8::Handle()"; } } $result .= " };\n"; $result .= " v8::Handle ${name}Signature = v8::Signature::New(desc, ${name}Argc, ${name}Argv);\n"; return $result; } sub RequiresCustomSignature { my $function = shift; # No signature needed for Custom function if (HasCustomMethod($function->signature->extendedAttributes)) { 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, '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, '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; # FIXME: Should this return false for Sequence and Array types? return !($non_wrapper_types{$type}); } sub GetTypeNameOfExternalTypedArray { my $interface = shift; my $interfaceName = $interface->name; my $viewType = $interface->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 $getIsolate = shift; die "An Isolate is mandatory for native value => JS value conversion." unless $getIsolate; my $getHolderContainer = shift; my $getHolderContainerArg = $getHolderContainer ? ", $getHolderContainer" : ""; my $getScriptWrappable = shift; my $getScriptWrappableArg = $getScriptWrappable ? ", $getScriptWrappable" : ""; my $returnHandleType = shift; my $returnHandleTypeArg = $returnHandleType ? ", $returnHandleType" : ""; my $forMainWorldSuffix = shift; my $type = $signature->type; return "v8Boolean($value, $getIsolate)" 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 . "), $getIsolate)"; } # 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, $getIsolate)" if $nativeType eq "int"; return "v8UnsignedInteger($value, $getIsolate)" if $nativeType eq "unsigned"; return "v8DateOrNull($value, $getIsolate)" if $type eq "Date"; # long long and unsigned long long are not representable in ECMAScript. return "v8::Number::New(static_cast($value))" if $type eq "long long" or $type eq "unsigned long long" or $type eq "DOMTimeStamp"; return "v8::Number::New($value)" if $codeGenerator->IsPrimitiveType($type); return "$value.v8Value()" if $nativeType eq "ScriptValue"; if ($codeGenerator->IsStringType($type) or $codeGenerator->IsEnumType($type)) { my $conv = $signature->extendedAttributes->{"TreatReturnedNullStringAs"}; if (defined $conv) { return "v8StringOrNull($value, $getIsolate$returnHandleTypeArg)" if $conv eq "Null"; return "v8StringOrUndefined($value, $getIsolate$returnHandleTypeArg)" if $conv eq "Undefined"; die "Unknown value for TreatReturnedNullStringAs extended attribute"; } return "v8String($value, $getIsolate$returnHandleTypeArg)"; } my $arrayType = $codeGenerator->GetArrayType($type); my $sequenceType = $codeGenerator->GetSequenceType($type); my $arrayOrSequenceType = $arrayType || $sequenceType; if ($arrayOrSequenceType) { if ($arrayType eq "DOMString") { AddToImplIncludes("V8DOMStringList.h"); AddToImplIncludes("DOMStringList.h"); } elsif ($codeGenerator->IsRefPtrType($arrayOrSequenceType)) { AddToImplIncludes(GetV8HeaderName($arrayOrSequenceType)); AddToImplIncludes("${arrayOrSequenceType}.h"); } return "v8Array($value, $getIsolate)"; } AddIncludesForType($type); if (IsDOMNodeType($type) || $type eq "EventTarget") { if ($getScriptWrappable) { return "toV8Fast${forMainWorldSuffix}($value$getHolderContainerArg$getScriptWrappableArg)"; } return "toV8($value, $getCreationContext, $getIsolate)"; } if ($type eq "EventListener") { AddToImplIncludes("V8AbstractEventListener.h"); return "${value} ? v8::Handle(static_cast(${value})->getListenerObject(imp->scriptExecutionContext())) : v8::Handle(v8Null($getIsolate))"; } if ($type eq "SerializedScriptValue") { AddToImplIncludes("$type.h"); return "$value ? $value->deserialize() : v8::Handle(v8Null($getIsolate))"; } AddToImplIncludes("wtf/RefCounted.h"); AddToImplIncludes("wtf/RefPtr.h"); AddToImplIncludes("wtf/GetPtr.h"); if ($getScriptWrappable) { return "toV8Fast$forMainWorldSuffix($value$getHolderContainerArg$getScriptWrappableArg)"; } return "toV8($value, $getCreationContext, $getIsolate)"; } sub WriteData { my $object = shift; my $interface = shift; my $outputDir = shift; my $outputHeadersDir = shift; my $name = $interface->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; 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 "", @implContentInternals, @implContent; $codeGenerator->UpdateFile($implFileName, $contents); %implIncludes = (); @implContentHeader = (); @implContentInternals = (); @implContent = (); # Update a .h file if the contents are changed. $contents = join "", @headerContent; $codeGenerator->UpdateFile($headerFileName, $contents); @headerContent = (); } sub ConvertToV8StringResource { my $signature = shift; my $nativeType = shift; my $variableName = shift; my $value = shift; my $suffix = shift; die "Wrong native type passed: $nativeType" unless $nativeType =~ /^V8StringResource/; if ($signature->type eq "DOMString" or $codeGenerator->IsEnumType($signature->type)) { my $macro = "V8TRYCATCH_FOR_V8STRINGRESOURCE"; $macro .= "_$suffix" if $suffix; return "$macro($nativeType, $variableName, $value);" } else { 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. return "ContextFeatures::" . $codeGenerator->WK_lcfirst($signature->name) . "Enabled"; } sub GetPassRefPtrType { my $v8InterfaceName = shift; my $angleBracketSpace = $v8InterfaceName =~ />$/ ? " " : ""; return "PassRefPtr<${v8InterfaceName}${angleBracketSpace}>"; } 1;