blob: 8accb7ad623f80fee0cd8a746289104fb8502990 [file] [log] [blame]
# Copyright (C) 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
# Copyright (C) 2006 Anders Carlsson <andersca@mac.com>
# Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
# Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org>
# Copyright (C) 2006 Apple Computer, Inc.
# Copyright (C) 2007, 2008, 2009, 2012 Google Inc.
# Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
# Copyright (C) Research In Motion Limited 2010. All rights reserved.
# Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this library; see the file COPYING.LIB. If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
package CodeGeneratorV8;
use strict;
use constant FileNamePrefix => "V8";
my $codeGenerator;
my $outputDir = "";
my $outputHeadersDir = "";
my @headerContent = ();
my @implContentHeader = ();
my @implFixedHeader = ();
my @implContent = ();
my @implContentDecls = ();
my %implIncludes = ();
my %headerIncludes = ();
# Default .h template
my $headerTemplate = << "EOF";
/*
This file is part of the WebKit open source project.
This file has been generated by generate-bindings.pl. DO NOT MODIFY!
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
EOF
# Default constructor
sub new
{
my $object = shift;
my $reference = { };
$codeGenerator = shift;
$outputDir = shift;
$outputHeadersDir = shift;
bless($reference, $object);
return $reference;
}
# Params: 'domClass' struct
sub GenerateInterface
{
my $object = shift;
my $dataNode = shift;
my $defines = shift;
# Start actual generation
if ($dataNode->extendedAttributes->{"Callback"}) {
$object->GenerateCallbackHeader($dataNode);
$object->GenerateCallbackImplementation($dataNode);
} else {
$object->GenerateHeader($dataNode);
$object->GenerateImplementation($dataNode);
}
$object->WriteData($dataNode);
}
sub AddToImplIncludes
{
my $header = shift;
my $conditional = shift;
if (not $conditional) {
$implIncludes{$header} = 1;
} elsif (not exists($implIncludes{$header})) {
$implIncludes{$header} = $conditional;
} else {
my $oldValue = $implIncludes{$header};
if ($oldValue ne 1) {
my %newValue = ();
$newValue{$conditional} = 1;
foreach my $condition (split(/\|/, $oldValue)) {
$newValue{$condition} = 1;
}
$implIncludes{$header} = join("|", sort keys %newValue);
}
}
}
sub AddIncludesForType
{
my $type = shift;
# When we're finished with the one-file-per-class
# reorganization, we won't need these special cases.
if ($codeGenerator->IsTypedArrayType($type)) {
AddToImplIncludes("wtf/${type}.h");
}
if (!$codeGenerator->IsPrimitiveType($type) and !$codeGenerator->IsStringType($type) and !$codeGenerator->SkipIncludeHeader($type) and $type ne "Date") {
# default, include the same named file
AddToImplIncludes(GetV8HeaderName(${type}));
if ($type =~ /SVGPathSeg/) {
my $joinedName = $type;
$joinedName =~ s/Abs|Rel//;
AddToImplIncludes("${joinedName}.h");
}
}
# additional includes (things needed to compile the bindings but not the header)
if ($type eq "CanvasRenderingContext2D") {
AddToImplIncludes("CanvasGradient.h");
AddToImplIncludes("CanvasPattern.h");
AddToImplIncludes("CanvasStyle.h");
}
if ($type eq "CanvasGradient" or $type eq "XPathNSResolver") {
AddToImplIncludes("wtf/text/WTFString.h");
}
if ($type eq "CSSStyleSheet" or $type eq "StyleSheet") {
AddToImplIncludes("CSSImportRule.h");
}
if ($type eq "CSSStyleDeclaration") {
AddToImplIncludes("StylePropertySet.h");
}
if ($type eq "Plugin" or $type eq "PluginArray" or $type eq "MimeTypeArray") {
# So we can get String -> AtomicString conversion for namedItem().
AddToImplIncludes("wtf/text/AtomicString.h");
}
}
sub NeedsCustomOpaqueRootForGC
{
my $dataNode = shift;
return GetGenerateIsReachable($dataNode) || GetCustomIsReachable($dataNode);
}
sub GetGenerateIsReachable
{
my $dataNode = shift;
return $dataNode->extendedAttributes->{"GenerateIsReachable"} || $dataNode->extendedAttributes->{"V8GenerateIsReachable"} || ""
}
sub GetCustomIsReachable
{
my $dataNode = shift;
return $dataNode->extendedAttributes->{"CustomIsReachable"} || $dataNode->extendedAttributes->{"V8CustomIsReachable"};
}
sub GenerateOpaqueRootForGC
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
if (GetCustomIsReachable($dataNode)) {
return;
}
push(@implContent, <<END);
void* V8${interfaceName}::opaqueRootForGC(void* object, v8::Persistent<v8::Object> wrapper)
{
ASSERT(!wrapper.IsIndependent());
${interfaceName}* impl = static_cast<${interfaceName}*>(object);
END
if (GetGenerateIsReachable($dataNode) eq "ImplDocument" ||
GetGenerateIsReachable($dataNode) eq "ImplElementRoot" ||
GetGenerateIsReachable($dataNode) eq "ImplOwnerRoot" ||
GetGenerateIsReachable($dataNode) eq "ImplOwnerNodeRoot" ||
GetGenerateIsReachable($dataNode) eq "ImplBaseRoot") {
$implIncludes{"V8GCController.h"} = 1;
my $methodName;
$methodName = "document" if (GetGenerateIsReachable($dataNode) eq "ImplDocument");
$methodName = "element" if (GetGenerateIsReachable($dataNode) eq "ImplElementRoot");
$methodName = "owner" if (GetGenerateIsReachable($dataNode) eq "ImplOwnerRoot");
$methodName = "ownerNode" if (GetGenerateIsReachable($dataNode) eq "ImplOwnerNodeRoot");
$methodName = "base" if (GetGenerateIsReachable($dataNode) eq "ImplBaseRoot");
push(@implContent, <<END);
if (Node* owner = impl->${methodName}())
return V8GCController::opaqueRootForGC(owner);
END
}
push(@implContent, <<END);
return object;
}
END
}
sub GetSVGPropertyTypes
{
my $implType = shift;
my $svgPropertyType;
my $svgListPropertyType;
my $svgNativeType;
return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $implType =~ /SVG/;
$svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($implType);
return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $svgNativeType;
# Append space to avoid compilation errors when using PassRefPtr<$svgNativeType>
$svgNativeType = "$svgNativeType ";
my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($implType);
if ($svgNativeType =~ /SVGPropertyTearOff/) {
$svgPropertyType = $svgWrappedNativeType;
AddToImplIncludes("SVGAnimatedPropertyTearOff.h");
} elsif ($svgNativeType =~ /SVGListPropertyTearOff/ or $svgNativeType =~ /SVGStaticListPropertyTearOff/) {
$svgListPropertyType = $svgWrappedNativeType;
$headerIncludes{"SVGAnimatedListPropertyTearOff.h"} = 1;
$headerIncludes{"SVGStaticListPropertyTearOff.h"} = 1;
} elsif ($svgNativeType =~ /SVGTransformListPropertyTearOff/) {
$svgListPropertyType = $svgWrappedNativeType;
$headerIncludes{"SVGAnimatedListPropertyTearOff.h"} = 1;
$headerIncludes{"SVGTransformListPropertyTearOff.h"} = 1;
} elsif ($svgNativeType =~ /SVGPathSegListPropertyTearOff/) {
$svgListPropertyType = $svgWrappedNativeType;
$headerIncludes{"SVGPathSegListPropertyTearOff.h"} = 1;
}
if ($svgPropertyType) {
$svgPropertyType = "SVGPoint" if $svgPropertyType eq "FloatPoint";
}
return ($svgPropertyType, $svgListPropertyType, $svgNativeType);
}
sub GenerateHeader
{
my $object = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $v8InterfaceName = "V8$interfaceName";
# Copy contents of parent classes except the first parent.
my @parents;
$codeGenerator->AddMethodsConstantsAndAttributesFromParentClasses($dataNode, \@parents, 1);
$codeGenerator->LinkOverloadedFunctions($dataNode);
# Ensure the IsDOMNodeType function is in sync.
die("IsDOMNodeType is out of date with respect to $interfaceName") if IsDOMNodeType($interfaceName) != $codeGenerator->IsSubType($dataNode, "Node");
my $hasDependentLifetime = $dataNode->extendedAttributes->{"V8DependentLifetime"} || $dataNode->extendedAttributes->{"ActiveDOMObject"}
|| GetGenerateIsReachable($dataNode) || $v8InterfaceName =~ /SVG/;
if (!$hasDependentLifetime) {
foreach (@{$dataNode->parents}) {
my $parent = $_;
$headerIncludes{"V8${parent}.h"} = 1;
}
}
# - Add default header template
push(@headerContent, GenerateHeaderContentHeader($dataNode));
$headerIncludes{"wtf/text/StringHash.h"} = 1;
$headerIncludes{"WrapperTypeInfo.h"} = 1;
$headerIncludes{"V8Binding.h"} = 1;
$headerIncludes{"V8DOMWrapper.h"} = 1;
$headerIncludes{"wtf/HashMap.h"} = 1;
$headerIncludes{"v8.h"} = 1;
my $headerClassInclude = GetHeaderClassInclude($interfaceName);
$headerIncludes{$headerClassInclude} = 1 if $headerClassInclude ne "";
my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($interfaceName);
foreach my $headerInclude (sort keys(%headerIncludes)) {
if ($headerInclude =~ /wtf|v8\.h/) {
push(@headerContent, "#include \<${headerInclude}\>\n");
} else {
push(@headerContent, "#include \"${headerInclude}\"\n");
}
}
push(@headerContent, "\nnamespace WebCore {\n");
push(@headerContent, "\ntemplate<typename PropertyType> class SVGPropertyTearOff;\n") if $svgPropertyType;
if ($svgNativeType) {
if ($svgNativeType =~ /SVGStaticListPropertyTearOff/) {
push(@headerContent, "\ntemplate<typename PropertyType> class SVGStaticListPropertyTearOff;\n");
} else {
push(@headerContent, "\ntemplate<typename PropertyType> class SVGListPropertyTearOff;\n");
}
}
push(@headerContent, "\n");
push(@headerContent, "class FloatRect;\n") if $svgPropertyType && $svgPropertyType eq "FloatRect";
push(@headerContent, "class Dictionary;\n") if $codeGenerator->IsConstructorTemplate($dataNode, "Event");
my $nativeType = GetNativeTypeForConversions($dataNode);
if ($dataNode->extendedAttributes->{"NamedConstructor"}) {
push(@headerContent, <<END);
class V8${nativeType}Constructor {
public:
static v8::Persistent<v8::FunctionTemplate> GetTemplate();
static WrapperTypeInfo info;
};
END
}
push(@headerContent, "class $v8InterfaceName {\n");
push(@headerContent, "public:\n");
push(@headerContent, " static const bool hasDependentLifetime = ");
if ($hasDependentLifetime) {
push(@headerContent, "true;\n");
} elsif (@{$dataNode->parents}) {
# Even if this type doesn't have the V8DependentLifetime attribute its parents may.
# Let the compiler statically determine this for us.
my $separator = "";
foreach (@{$dataNode->parents}) {
my $parent = $_;
$headerIncludes{"V8${parent}.h"} = 1;
push(@headerContent, "${separator}V8${parent}::hasDependentLifetime");
$separator = " || ";
}
push(@headerContent, ";\n");
} else {
push(@headerContent, "false;\n");
}
push(@headerContent, <<END);
static bool HasInstance(v8::Handle<v8::Value>);
static v8::Persistent<v8::FunctionTemplate> GetRawTemplate();
static v8::Persistent<v8::FunctionTemplate> GetTemplate();
static ${nativeType}* toNative(v8::Handle<v8::Object> object)
{
return reinterpret_cast<${nativeType}*>(object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex));
}
static void derefObject(void*);
static WrapperTypeInfo info;
END
if (NeedsCustomOpaqueRootForGC($dataNode)) {
push(@headerContent, " static void* opaqueRootForGC(void*, v8::Persistent<v8::Object>);\n");
}
if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) {
push(@headerContent, " static ActiveDOMObject* toActiveDOMObject(v8::Handle<v8::Object>);\n");
}
if ($interfaceName eq "DOMWindow") {
push(@headerContent, <<END);
static v8::Persistent<v8::ObjectTemplate> GetShadowObjectTemplate();
END
}
if ($interfaceName eq "HTMLDocument") {
push(@headerContent, <<END);
static v8::Local<v8::Object> wrapInShadowObject(v8::Local<v8::Object> wrapper, Node* impl);
static v8::Handle<v8::Value> getNamedProperty(HTMLDocument* htmlDocument, const AtomicString& key, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
}
my @enabledPerContextFunctions;
foreach my $function (@{$dataNode->functions}) {
my $name = $function->signature->name;
my $attrExt = $function->signature->extendedAttributes;
if (($attrExt->{"Custom"} || $attrExt->{"V8Custom"}) && !$attrExt->{"ImplementedBy"} && $function->{overloadIndex} == 1) {
my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
push(@headerContent, <<END);
static v8::Handle<v8::Value> ${name}Callback(const v8::Arguments&);
END
push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
if ($attrExt->{"V8EnabledPerContext"}) {
push(@enabledPerContextFunctions, $function);
}
}
if (IsConstructable($dataNode)) {
push(@headerContent, <<END);
static v8::Handle<v8::Value> constructorCallback(const v8::Arguments&);
END
}
my @enabledPerContextAttributes;
foreach my $attribute (@{$dataNode->attributes}) {
my $name = $attribute->signature->name;
my $attrExt = $attribute->signature->extendedAttributes;
my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature);
if (($attrExt->{"V8CustomGetter"} || $attrExt->{"CustomGetter"} ||
$attrExt->{"V8Custom"} || $attrExt->{"Custom"}) &&
!$attrExt->{"ImplementedBy"}) {
push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
push(@headerContent, <<END);
static v8::Handle<v8::Value> ${name}AccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);
END
push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
if (($attrExt->{"V8CustomSetter"} || $attrExt->{"CustomSetter"} ||
$attrExt->{"V8Custom"} || $attrExt->{"Custom"}) &&
!$attrExt->{"ImplementedBy"}) {
push(@headerContent, "#if ${conditionalString}\n") if $conditionalString;
push(@headerContent, <<END);
static void ${name}AccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value>, const v8::AccessorInfo&);
END
push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
if ($attrExt->{"V8EnabledPerContext"}) {
push(@enabledPerContextAttributes, $attribute);
}
}
GenerateHeaderNamedAndIndexedPropertyAccessors($dataNode);
GenerateHeaderCustomCall($dataNode);
GenerateHeaderCustomInternalFieldIndices($dataNode);
if ($dataNode->extendedAttributes->{"CheckSecurity"}) {
push(@headerContent, <<END);
static bool namedSecurityCheck(v8::Local<v8::Object> host, v8::Local<v8::Value> key, v8::AccessType, v8::Local<v8::Value> data);
static bool indexedSecurityCheck(v8::Local<v8::Object> host, uint32_t index, v8::AccessType, v8::Local<v8::Value> data);
END
}
if (@enabledPerContextAttributes) {
push(@headerContent, <<END);
static void installPerContextProperties(v8::Handle<v8::Object>, ${nativeType}*);
END
} else {
push(@headerContent, <<END);
static void installPerContextProperties(v8::Handle<v8::Object>, ${nativeType}*) { }
END
}
if (@enabledPerContextFunctions) {
push(@headerContent, <<END);
static void installPerContextPrototypeProperties(v8::Handle<v8::Object>);
END
} else {
push(@headerContent, <<END);
static void installPerContextPrototypeProperties(v8::Handle<v8::Object>) { }
END
}
if ($interfaceName eq "HTMLElement") {
push(@headerContent, <<END);
friend v8::Handle<v8::Object> createV8HTMLWrapper(HTMLElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
} elsif ($interfaceName eq "SVGElement") {
push(@headerContent, <<END);
friend v8::Handle<v8::Object> createV8SVGWrapper(SVGElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
} elsif ($interfaceName eq "Element") {
push(@headerContent, <<END);
// This is a performance optimization hack. See V8Element::wrap.
friend v8::Handle<v8::Object> wrap(Node*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
}
push(@headerContent, <<END);
private:
END
if (IsConstructable($dataNode) && @{$dataNode->constructors} > 1) {
for (my $i = 1; $i <= @{$dataNode->constructors}; $i++) {
push(@headerContent, <<END);
static v8::Handle<v8::Value> constructor${i}Callback(const v8::Arguments&);
END
}
}
my $noToV8 = $dataNode->extendedAttributes->{"SuppressToJSObject"};
my $noWrap = $dataNode->extendedAttributes->{"V8NoWrapperCache"} || $noToV8;
my $createWrapperArgumentType = GetPassRefPtrType($nativeType);
if (!$noWrap) {
my $createWrapperArgumentType = GetPassRefPtrType($nativeType);
push(@headerContent, <<END);
friend v8::Handle<v8::Object> wrap(${nativeType}*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
static v8::Handle<v8::Object> createWrapper(${createWrapperArgumentType}, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
}
push(@headerContent, <<END);
};
END
my $customWrap = !!($dataNode->extendedAttributes->{"CustomToJSObject"} or $dataNode->extendedAttributes->{"V8CustomToJSObject"});
if ($noToV8) {
die "Can't suppress toV8 for subclass\n" if @parents;
} elsif ($noWrap) {
die "Must have custom toV8\n" if !$customWrap;
push(@headerContent, <<END);
class ${nativeType};
v8::Handle<v8::Value> toV8(${nativeType}*, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* = 0);
inline v8::Handle<v8::Value> toV8Fast(${nativeType}* impl, const v8::AccessorInfo& info, Node*)
{
return toV8(impl, info.Holder(), info.GetIsolate());
}
END
} else {
my $getCachedWrapper = $codeGenerator->IsSubType($dataNode, "Node") ? "V8DOMWrapper::getCachedWrapper(impl)" : "DOMDataStore::current(isolate)->get(impl)";
my $createWrapperCall = $customWrap ? "${v8InterfaceName}::wrap" : "${v8InterfaceName}::createWrapper";
if ($customWrap) {
push(@headerContent, <<END);
v8::Handle<v8::Object> wrap(${nativeType}* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* = 0);
END
} else {
push(@headerContent, <<END);
inline v8::Handle<v8::Object> wrap(${nativeType}* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate = 0)
{
ASSERT(impl);
ASSERT($getCachedWrapper.IsEmpty());
return $createWrapperCall(impl, creationContext, isolate);
}
END
}
push(@headerContent, <<END);
inline v8::Handle<v8::Object> toV8Object(${nativeType}* impl, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* isolate = 0)
{
if (UNLIKELY(!impl))
return v8::Handle<v8::Object>();
v8::Handle<v8::Object> wrapper = $getCachedWrapper;
if (!wrapper.IsEmpty())
return wrapper;
return wrap(impl, creationContext, isolate);
}
inline v8::Handle<v8::Value> toV8(${nativeType}* impl, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* isolate = 0)
{
if (UNLIKELY(!impl))
return v8NullWithCheck(isolate);
v8::Handle<v8::Value> wrapper = $getCachedWrapper;
if (!wrapper.IsEmpty())
return wrapper;
return wrap(impl, creationContext, isolate);
}
END
if ($codeGenerator->IsSubType($dataNode, "Node")) {
push(@headerContent, <<END);
inline v8::Handle<v8::Value> toV8Fast(${nativeType}* impl, const v8::AccessorInfo& info, Node* holder)
{
if (UNLIKELY(!impl))
return v8::Null(info.GetIsolate());
// What we'd really like to check here is whether we're in the main world or
// in an isolated world. The fastest way we know how to do that is to check
// whether the holder's inline wrapper is the same wrapper we see in the
// v8::AccessorInfo.
v8::Handle<v8::Object> wrapper = (holder->wrapper() == info.Holder()) ? impl->wrapper() : V8DOMWrapper::getCachedWrapper(impl);
if (!wrapper.IsEmpty())
return wrapper;
return wrap(impl, info.Holder(), info.GetIsolate());
}
END
}
}
push(@headerContent, <<END);
inline v8::Handle<v8::Value> toV8(PassRefPtr< ${nativeType} > impl, v8::Handle<v8::Object> creationContext = v8::Handle<v8::Object>(), v8::Isolate* isolate = 0)
{
return toV8(impl.get(), creationContext, isolate);
}
END
if ($codeGenerator->IsConstructorTemplate($dataNode, "Event")) {
push(@headerContent, "\nbool fill${interfaceName}Init(${interfaceName}Init&, const Dictionary&);\n");
}
push(@headerContent, "\n}\n\n");
push(@headerContent, "#endif // $v8InterfaceName" . "_h\n");
my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
push(@headerContent, "#endif // ${conditionalString}\n\n") if $conditionalString;
}
sub GetInternalFields
{
my $dataNode = shift;
my $name = $dataNode->name;
my @customInternalFields = ();
# We can't ask whether a parent type has a given extendedAttribute,
# so special-case AbstractWorker and WorkerContext to include all sub-types.
# Event listeners on DOM nodes are explicitly supported in the GC controller.
# FIXME: Simplify this when all EventTargets are subtypes of EventTarget.
if (!$codeGenerator->IsSubType($dataNode, "Node")
&& ($dataNode->extendedAttributes->{"EventTarget"}
|| $dataNode->extendedAttributes->{"IsWorkerContext"}
|| $codeGenerator->IsSubType($dataNode, "AbstractWorker")
|| $codeGenerator->IsSubType($dataNode, "EventTarget"))) {
push(@customInternalFields, "eventListenerCacheIndex");
}
return @customInternalFields;
}
sub GetHeaderClassInclude
{
my $v8InterfaceName = shift;
if ($v8InterfaceName =~ /SVGPathSeg/) {
$v8InterfaceName =~ s/Abs|Rel//;
}
return "wtf/${v8InterfaceName}.h" if $codeGenerator->IsTypedArrayType($v8InterfaceName);
return "" if ($codeGenerator->SkipIncludeHeader($v8InterfaceName));
return "${v8InterfaceName}.h";
}
sub GenerateHeaderCustomInternalFieldIndices
{
my $dataNode = shift;
my @customInternalFields = GetInternalFields($dataNode);
my $customFieldCounter = 0;
foreach my $customInternalField (@customInternalFields) {
push(@headerContent, <<END);
static const int ${customInternalField} = v8DefaultWrapperInternalFieldCount + ${customFieldCounter};
END
$customFieldCounter++;
}
push(@headerContent, <<END);
static const int internalFieldCount = v8DefaultWrapperInternalFieldCount + ${customFieldCounter};
END
}
my %indexerSpecialCases = (
"Storage" => 1,
"HTMLAppletElement" => 1,
"HTMLEmbedElement" => 1,
"HTMLObjectElement" => 1
);
sub GenerateHeaderNamedAndIndexedPropertyAccessors
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $hasCustomIndexedGetter = $dataNode->extendedAttributes->{"IndexedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"};
my $hasCustomIndexedSetter = $dataNode->extendedAttributes->{"CustomIndexedSetter"} && !$dataNode->extendedAttributes->{"NumericIndexedGetter"};
my $hasCustomNamedGetter = $dataNode->extendedAttributes->{"NamedGetter"} || $dataNode->extendedAttributes->{"CustomNamedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"};
my $hasCustomNamedSetter = $dataNode->extendedAttributes->{"CustomNamedSetter"};
my $hasCustomDeleters = $dataNode->extendedAttributes->{"CustomDeleteProperty"};
my $hasCustomEnumerator = $dataNode->extendedAttributes->{"CustomEnumerateProperty"};
if ($interfaceName eq "HTMLOptionsCollection") {
$interfaceName = "HTMLCollection";
$hasCustomIndexedGetter = 1;
$hasCustomNamedGetter = 1;
}
if ($interfaceName eq "DOMWindow") {
$hasCustomDeleters = 0;
$hasCustomEnumerator = 0;
}
if ($interfaceName eq "HTMLAppletElement" || $interfaceName eq "HTMLEmbedElement" || $interfaceName eq "HTMLObjectElement") {
$hasCustomNamedGetter = 1;
}
if ($interfaceName eq "HTMLDocument") {
$hasCustomNamedGetter = 0;
$hasCustomIndexedGetter = 0;
}
my $isIndexerSpecialCase = exists $indexerSpecialCases{$interfaceName};
if ($hasCustomIndexedGetter || $isIndexerSpecialCase) {
push(@headerContent, <<END);
static v8::Handle<v8::Value> indexedPropertyGetter(uint32_t, const v8::AccessorInfo&);
END
}
if ($isIndexerSpecialCase || $hasCustomIndexedSetter) {
push(@headerContent, <<END);
static v8::Handle<v8::Value> indexedPropertySetter(uint32_t, v8::Local<v8::Value>, const v8::AccessorInfo&);
END
}
if ($hasCustomDeleters) {
push(@headerContent, <<END);
static v8::Handle<v8::Boolean> indexedPropertyDeleter(uint32_t, const v8::AccessorInfo&);
END
}
if ($hasCustomNamedGetter) {
push(@headerContent, <<END);
static v8::Handle<v8::Value> namedPropertyGetter(v8::Local<v8::String>, const v8::AccessorInfo&);
END
}
if ($hasCustomNamedSetter) {
push(@headerContent, <<END);
static v8::Handle<v8::Value> namedPropertySetter(v8::Local<v8::String>, v8::Local<v8::Value>, const v8::AccessorInfo&);
END
}
if ($hasCustomDeleters) {
push(@headerContent, <<END);
static v8::Handle<v8::Boolean> namedPropertyDeleter(v8::Local<v8::String>, const v8::AccessorInfo&);
END
}
if ($hasCustomEnumerator) {
push(@headerContent, <<END);
static v8::Handle<v8::Array> namedPropertyEnumerator(const v8::AccessorInfo&);
static v8::Handle<v8::Integer> namedPropertyQuery(v8::Local<v8::String>, const v8::AccessorInfo&);
END
}
}
sub GenerateHeaderCustomCall
{
my $dataNode = shift;
if ($dataNode->extendedAttributes->{"CustomCall"}) {
push(@headerContent, " static v8::Handle<v8::Value> callAsFunctionCallback(const v8::Arguments&);\n");
}
if ($dataNode->name eq "Event") {
push(@headerContent, " static v8::Handle<v8::Value> dataTransferAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n");
push(@headerContent, " static void valueAccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value>, const v8::AccessorInfo&);\n");
}
if ($dataNode->name eq "Location") {
push(@headerContent, " static v8::Handle<v8::Value> assignAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n");
push(@headerContent, " static v8::Handle<v8::Value> reloadAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n");
push(@headerContent, " static v8::Handle<v8::Value> replaceAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo&);\n");
}
}
sub IsConstructable
{
my $dataNode = shift;
return $dataNode->extendedAttributes->{"CustomConstructor"} || $dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"Constructor"} || $dataNode->extendedAttributes->{"ConstructorTemplate"};
}
sub IsReadonly
{
my $attribute = shift;
my $attrExt = $attribute->signature->extendedAttributes;
return ($attribute->type =~ /readonly/ || $attrExt->{"V8ReadOnly"}) && !$attrExt->{"Replaceable"};
}
sub GenerateDomainSafeFunctionGetter
{
my $function = shift;
my $interfaceName = shift;
my $v8InterfaceName = "V8" . $interfaceName;
my $funcName = $function->signature->name;
my $signature = "v8::Signature::New(" . $v8InterfaceName . "::GetRawTemplate())";
if ($function->signature->extendedAttributes->{"V8DoNotCheckSignature"}) {
$signature = "v8::Local<v8::Signature>()";
}
my $callback = GetFunctionTemplateCallbackName($function, $interfaceName);
my $newTemplateString = "v8::FunctionTemplate::New($callback, v8Undefined(), $signature)";
AddToImplIncludes("Frame.h");
push(@implContentDecls, <<END);
static v8::Handle<v8::Value> ${funcName}AttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
{
INC_STATS(\"DOM.$interfaceName.$funcName._get\");
static v8::Persistent<v8::FunctionTemplate> privateTemplate = v8::Persistent<v8::FunctionTemplate>::New($newTemplateString);
v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(${v8InterfaceName}::GetTemplate(), info.This());
if (holder.IsEmpty()) {
// can only reach here by 'object.__proto__.func', and it should passed
// domain security check already
return privateTemplate->GetFunction();
}
${interfaceName}* imp = ${v8InterfaceName}::toNative(holder);
if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame(), DoNotReportSecurityError)) {
static v8::Persistent<v8::FunctionTemplate> sharedTemplate = v8::Persistent<v8::FunctionTemplate>::New($newTemplateString);
return sharedTemplate->GetFunction();
}
v8::Local<v8::Value> hiddenValue = info.This()->GetHiddenValue(name);
if (!hiddenValue.IsEmpty())
return hiddenValue;
return privateTemplate->GetFunction();
}
END
}
sub GenerateDomainSafeFunctionSetter
{
my $interfaceName = shift;
my $v8InterfaceName = "V8" . $interfaceName;
push(@implContentDecls, <<END);
static void ${interfaceName}DomainSafeFunctionSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
{
INC_STATS("DOM.$interfaceName._set");
v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(${v8InterfaceName}::GetTemplate(), info.This());
if (holder.IsEmpty())
return;
${interfaceName}* imp = ${v8InterfaceName}::toNative(holder);
if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame()))
return;
info.This()->SetHiddenValue(name, value);
}
END
}
sub GenerateConstructorGetter
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
push(@implContentDecls, <<END);
static v8::Handle<v8::Value> ${interfaceName}ConstructorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
{
INC_STATS(\"DOM.$interfaceName.constructors._get\");
v8::Handle<v8::Value> data = info.Data();
ASSERT(data->IsExternal());
V8PerContextData* perContextData = V8PerContextData::from(info.Holder()->CreationContext());
if (!perContextData)
return v8Undefined();
return perContextData->constructorForType(WrapperTypeInfo::unwrap(data));
}
END
}
sub GenerateFeatureObservation
{
my $measureAs = shift;
if ($measureAs) {
AddToImplIncludes("FeatureObserver.h");
return " FeatureObserver::observe(activeDOMWindow(BindingState::instance()), FeatureObserver::${measureAs});\n";
}
return "";
}
sub GenerateNormalAttrGetter
{
my $attribute = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $attrExt = $attribute->signature->extendedAttributes;
my $attrName = $attribute->signature->name;
my $attrType = $attribute->signature->type;
$codeGenerator->AssertNotSequenceType($attrType);
my $nativeType = GetNativeTypeFromSignature($attribute->signature, -1);
my $getterStringUsesImp = $interfaceName ne "SVGNumber";
my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName);
# Getter
my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature);
push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString;
push(@implContentDecls, <<END);
static v8::Handle<v8::Value> ${attrName}AttrGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
{
INC_STATS("DOM.$interfaceName.$attrName._get");
END
push(@implContentDecls, GenerateFeatureObservation($attrExt->{"V8MeasureAs"}));
if ($svgNativeType) {
my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName);
if ($svgWrappedNativeType =~ /List/) {
push(@implContentDecls, <<END);
$svgNativeType* imp = V8${interfaceName}::toNative(info.Holder());
END
} else {
push(@implContentDecls, <<END);
$svgNativeType* wrapper = V8${interfaceName}::toNative(info.Holder());
$svgWrappedNativeType& impInstance = wrapper->propertyReference();
END
if ($getterStringUsesImp) {
push(@implContentDecls, <<END);
$svgWrappedNativeType* imp = &impInstance;
END
}
}
} elsif ($attrExt->{"V8OnProto"} || $attrExt->{"V8Unforgeable"}) {
if ($interfaceName eq "DOMWindow") {
push(@implContentDecls, <<END);
v8::Handle<v8::Object> holder = info.Holder();
END
} else {
# perform lookup first
push(@implContentDecls, <<END);
v8::Handle<v8::Object> holder = V8DOMWrapper::lookupDOMWrapper(V8${interfaceName}::GetTemplate(), info.This());
if (holder.IsEmpty())
return v8Undefined();
END
}
push(@implContentDecls, <<END);
${interfaceName}* imp = V8${interfaceName}::toNative(holder);
END
} else {
my $reflect = $attribute->signature->extendedAttributes->{"Reflect"};
my $url = $attribute->signature->extendedAttributes->{"URL"};
if ($getterStringUsesImp && $reflect && !$url && $codeGenerator->IsSubType($dataNode, "Node") && $codeGenerator->IsStringType($attrType)) {
# Generate super-compact call for regular attribute getter:
my $contentAttributeName = $reflect eq "VALUE_IS_MISSING" ? lc $attrName : $reflect;
my $namespace = $codeGenerator->NamespaceForAttributeName($interfaceName, $contentAttributeName);
AddToImplIncludes("${namespace}.h");
push(@implContentDecls, " Element* imp = V8Element::toNative(info.Holder());\n");
push(@implContentDecls, " return v8ExternalString(imp->getAttribute(${namespace}::${contentAttributeName}Attr), info.GetIsolate());\n");
push(@implContentDecls, "}\n\n");
push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString;
return;
# Skip the rest of the function!
}
if ($attribute->signature->type eq "SerializedScriptValue" && $attrExt->{"CachedAttribute"}) {
push(@implContentDecls, <<END);
v8::Handle<v8::String> propertyName = v8::String::NewSymbol("${attrName}");
v8::Handle<v8::Value> value = info.Holder()->GetHiddenValue(propertyName);
if (!value.IsEmpty())
return value;
END
}
if (!$attribute->isStatic) {
push(@implContentDecls, <<END);
${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder());
END
}
}
# Generate security checks if necessary
if ($attribute->signature->extendedAttributes->{"CheckSecurityForNode"}) {
push(@implContentDecls, " if (!BindingSecurity::shouldAllowAccessToNode(BindingState::instance(), imp->" . $attribute->signature->name . "()))\n return v8::Handle<v8::Value>(v8::Null(info.GetIsolate()));\n\n");
}
my $useExceptions = 1 if @{$attribute->getterExceptions};
if ($useExceptions) {
AddToImplIncludes("ExceptionCode.h");
push(@implContentDecls, " ExceptionCode ec = 0;\n");
}
my $returnType = $attribute->signature->type;
my $getterString;
if ($getterStringUsesImp) {
my ($functionName, @arguments) = $codeGenerator->GetterExpression(\%implIncludes, $interfaceName, $attribute);
push(@arguments, "ec") if $useExceptions;
if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) {
my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"};
AddToImplIncludes("${implementedBy}.h");
unshift(@arguments, "imp") if !$attribute->isStatic;
$functionName = "${implementedBy}::${functionName}";
} elsif ($attribute->isStatic) {
$functionName = "${interfaceName}::${functionName}";
} else {
$functionName = "imp->${functionName}";
}
unshift(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContentDecls, " ", 0, 0));
$getterString = "${functionName}(" . join(", ", @arguments) . ")";
} else {
$getterString = "impInstance";
}
my $result;
my $wrapper;
if ($attribute->signature->type eq "EventListener" && $dataNode->name eq "DOMWindow") {
push(@implContentDecls, " if (!imp->document())\n");
push(@implContentDecls, " return v8Undefined();\n");
}
if ($useExceptions) {
if ($nativeType =~ /^V8Parameter/) {
push(@implContentDecls, " " . ConvertToV8Parameter($attribute->signature, $nativeType, "v", $getterString) . ";\n");
} else {
push(@implContentDecls, " $nativeType v = $getterString;\n");
}
push(@implContentDecls, " if (UNLIKELY(ec))\n");
push(@implContentDecls, " return setDOMException(ec, info.GetIsolate());\n");
if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) {
push(@implContentDecls, " if (state.hadException())\n");
push(@implContentDecls, " return throwError(state.exception(), info.GetIsolate());\n");
}
$result = "v";
$result .= ".release()" if (IsRefPtrType($returnType));
} else {
# Can inline the function call into the return statement to avoid overhead of using a Ref<> temporary
$result = $getterString;
# Fix amigious conversion problem, by casting to the base type first ($getterString returns a type that inherits from SVGAnimatedEnumeration, not the base class directly).
$result = "static_pointer_cast<SVGAnimatedEnumeration>($result)" if $returnType eq "SVGAnimatedEnumeration";
}
# Special case for readonly or Replaceable attributes (with a few exceptions). This attempts to ensure that JS wrappers don't get
# garbage-collected prematurely when their lifetime is strongly tied to their owner. We accomplish this by inserting a reference to
# the newly created wrapper into an internal field of the holder object.
if (!$codeGenerator->IsSubType($dataNode, "Node") && $attrName ne "self" && IsWrapperType($returnType) && (IsReadonly($attribute) || $attribute->signature->extendedAttributes->{"Replaceable"} || $attrName eq "location")
&& $returnType ne "EventTarget" && $returnType ne "SerializedScriptValue" && $returnType ne "DOMWindow"
&& $returnType ne "MessagePortArray"
&& $returnType !~ /SVG/ && $returnType !~ /HTML/ && !IsDOMNodeType($returnType)) {
my $arrayType = $codeGenerator->GetArrayType($returnType);
if ($arrayType) {
if (!$codeGenerator->SkipIncludeHeader($arrayType)) {
AddToImplIncludes("V8$arrayType.h");
AddToImplIncludes("$arrayType.h");
}
push(@implContentDecls, " return v8Array(${getterString}, info.GetIsolate());\n");
push(@implContentDecls, "}\n\n");
return;
}
AddIncludesForType($returnType);
# Check for a wrapper in the wrapper cache. If there is one, we know that a hidden reference has already
# been created. If we don't find a wrapper, we create both a wrapper and a hidden reference.
push(@implContentDecls, " RefPtr<$returnType> result = ${getterString};\n");
push(@implContentDecls, " v8::Handle<v8::Value> wrapper = result.get() ? v8::Handle<v8::Value>(DOMDataStore::current(info.GetIsolate())->get(result.get())) : v8Undefined();\n");
push(@implContentDecls, " if (wrapper.IsEmpty()) {\n");
push(@implContentDecls, " wrapper = toV8(result.get(), info.Holder(), info.GetIsolate());\n"); # FIXME: Could use wrap here since the wrapper is empty.
push(@implContentDecls, " if (!wrapper.IsEmpty())\n");
push(@implContentDecls, " V8DOMWrapper::setNamedHiddenReference(info.Holder(), \"${attrName}\", wrapper);\n");
push(@implContentDecls, " }\n");
push(@implContentDecls, " return wrapper;\n");
push(@implContentDecls, "}\n\n");
push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString;
return;
}
if (($codeGenerator->IsSVGAnimatedType($interfaceName) or $interfaceName eq "SVGViewSpec") and $codeGenerator->IsSVGTypeNeedingTearOff($attrType)) {
AddToImplIncludes("V8$attrType.h");
my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($attrType);
# Convert from abstract SVGProperty to real type, so the right toJS() method can be invoked.
push(@implContentDecls, " return toV8(static_cast<$svgNativeType*>($result), info.Holder(), info.GetIsolate());\n");
} elsif ($codeGenerator->IsSVGTypeNeedingTearOff($attrType) and not $interfaceName =~ /List$/) {
AddToImplIncludes("V8$attrType.h");
AddToImplIncludes("SVGPropertyTearOff.h");
my $tearOffType = $codeGenerator->GetSVGTypeNeedingTearOff($attrType);
if ($codeGenerator->IsSVGTypeWithWritablePropertiesNeedingTearOff($attrType) and not defined $attribute->signature->extendedAttributes->{"Immutable"}) {
my $getter = $result;
$getter =~ s/imp->//;
$getter =~ s/\(\)//;
my $updateMethod = "&${interfaceName}::update" . $codeGenerator->WK_ucfirst($getter);
my $selfIsTearOffType = $codeGenerator->IsSVGTypeNeedingTearOff($interfaceName);
if ($selfIsTearOffType) {
AddToImplIncludes("SVGStaticPropertyWithParentTearOff.h");
$tearOffType =~ s/SVGPropertyTearOff</SVGStaticPropertyWithParentTearOff<$interfaceName, /;
if ($result =~ /matrix/ and $interfaceName eq "SVGTransform") {
# SVGTransform offers a matrix() method for internal usage that returns an AffineTransform
# and a svgMatrix() method returning a SVGMatrix, used for the bindings.
$result =~ s/matrix/svgMatrix/;
}
push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create(wrapper, $result, $updateMethod)), info.Holder(), info.GetIsolate());\n");
} else {
AddToImplIncludes("SVGStaticPropertyTearOff.h");
$tearOffType =~ s/SVGPropertyTearOff</SVGStaticPropertyTearOff<$interfaceName, /;
push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create(imp, $result, $updateMethod)), info.Holder(), info.GetIsolate());\n");
}
} elsif ($tearOffType =~ /SVGStaticListPropertyTearOff/) {
push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create(imp, $result)), info.Holder(), info.GetIsolate());\n");
} elsif ($tearOffType =~ /SVG(Point|PathSeg)List/) {
push(@implContentDecls, " return toV8(WTF::getPtr($result), info.Holder(), info.GetIsolate());\n");
} else {
push(@implContentDecls, " return toV8(WTF::getPtr(${tearOffType}::create($result)), info.Holder(), info.GetIsolate());\n");
}
} elsif ($attribute->signature->type eq "MessagePortArray") {
AddToImplIncludes("MessagePort.h");
AddToImplIncludes("V8MessagePort.h");
my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name);
push(@implContentDecls, <<END);
MessagePortArray* ports = imp->${getterFunc}();
if (!ports)
return v8::Array::New(0);
MessagePortArray portsCopy(*ports);
v8::Local<v8::Array> portArray = v8::Array::New(portsCopy.size());
for (size_t i = 0; i < portsCopy.size(); ++i)
portArray->Set(v8Integer(i, info.GetIsolate()), toV8(portsCopy[i].get(), info.Holder(), info.GetIsolate()));
return portArray;
END
} elsif ($attribute->signature->type eq "SerializedScriptValue" && $attrExt->{"CachedAttribute"}) {
my $getterFunc = $codeGenerator->WK_lcfirst($attribute->signature->name);
push(@implContentDecls, <<END);
SerializedScriptValue* serialized = imp->${getterFunc}();
value = serialized ? serialized->deserialize() : v8::Handle<v8::Value>(v8::Null(info.GetIsolate()));
info.Holder()->SetHiddenValue(propertyName, value);
return value;
END
} elsif (IsDOMNodeType($interfaceName) && IsDOMNodeType($attrType)) {
AddToImplIncludes($attrType.".h");
push(@implContentDecls, " return toV8Fast($result, info, imp);\n");
} else {
push(@implContentDecls, " return " . NativeToJSValue($attribute->signature, $result, "info.Holder()", "info.GetIsolate()").";\n");
}
push(@implContentDecls, "}\n\n"); # end of getter
push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString;
}
sub GenerateReplaceableAttrSetter
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
push(@implContentDecls, <<END);
static void ${interfaceName}ReplaceableAttrSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
{
INC_STATS("DOM.$interfaceName.replaceable._set");
END
push(@implContentDecls, GenerateFeatureObservation($dataNode->extendedAttributes->{"V8MeasureAs"}));
if ($interfaceName eq "DOMWindow" || $dataNode->extendedAttributes->{"CheckSecurity"}) {
AddToImplIncludes("Frame.h");
push(@implContentDecls, <<END);
${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder());
if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame()))
return;
END
}
push(@implContentDecls, <<END);
info.This()->ForceSet(name, value);
}
END
}
sub GenerateNormalAttrSetter
{
my $attribute = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $attrName = $attribute->signature->name;
my $attrExt = $attribute->signature->extendedAttributes;
my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature);
push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString;
push(@implContentDecls, "static void ${attrName}AttrSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info)\n{\n");
push(@implContentDecls, " INC_STATS(\"DOM.$interfaceName.$attrName._set\");\n");
push(@implContentDecls, GenerateFeatureObservation($attribute->signature->extendedAttributes->{"V8MeasureAs"}));
# If the "StrictTypeChecking" extended attribute is present, and the attribute's type is an
# interface type, then if the incoming value does not implement that interface, a TypeError is
# thrown rather than silently passing NULL to the C++ code.
# Per the Web IDL and ECMAScript specifications, incoming values can always be converted to both
# strings and numbers, so do not throw TypeError if the attribute is of these types.
if ($attribute->signature->extendedAttributes->{"StrictTypeChecking"}) {
my $argType = $attribute->signature->type;
if (IsWrapperType($argType)) {
push(@implContentDecls, " if (!isUndefinedOrNull(value) && !V8${argType}::HasInstance(value)) {\n");
push(@implContentDecls, " throwTypeError(0, info.GetIsolate());\n");
push(@implContentDecls, " return;\n");
push(@implContentDecls, " }\n");
}
}
my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName);
if ($svgNativeType) {
my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName);
if ($svgWrappedNativeType =~ /List$/) {
push(@implContentDecls, <<END);
$svgNativeType* imp = V8${interfaceName}::toNative(info.Holder());
END
} else {
AddToImplIncludes("ExceptionCode.h");
push(@implContentDecls, " $svgNativeType* wrapper = V8${interfaceName}::toNative(info.Holder());\n");
push(@implContentDecls, " if (wrapper->isReadOnly()) {\n");
push(@implContentDecls, " setDOMException(NO_MODIFICATION_ALLOWED_ERR, info.GetIsolate());\n");
push(@implContentDecls, " return;\n");
push(@implContentDecls, " }\n");
push(@implContentDecls, " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n");
push(@implContentDecls, " $svgWrappedNativeType* imp = &impInstance;\n");
}
} elsif ($attrExt->{"V8OnProto"}) {
push(@implContentDecls, <<END);
${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder());
END
} else {
my $attrType = $attribute->signature->type;
my $reflect = $attribute->signature->extendedAttributes->{"Reflect"};
if ($reflect && $codeGenerator->IsSubType($dataNode, "Node") && $codeGenerator->IsStringType($attrType)) {
# Generate super-compact call for regular attribute setter:
my $contentAttributeName = $reflect eq "VALUE_IS_MISSING" ? lc $attrName : $reflect;
my $namespace = $codeGenerator->NamespaceForAttributeName($interfaceName, $contentAttributeName);
AddToImplIncludes("${namespace}.h");
push(@implContentDecls, " Element* imp = V8Element::toNative(info.Holder());\n");
push(@implContentDecls, " AtomicString v = toWebCoreAtomicStringWithNullCheck(value);\n");
push(@implContentDecls, " imp->setAttribute(${namespace}::${contentAttributeName}Attr, v);\n");
push(@implContentDecls, "}\n\n");
push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString;
return;
# Skip the rest of the function!
}
if (!$attribute->isStatic) {
push(@implContentDecls, <<END);
${interfaceName}* imp = V8${interfaceName}::toNative(info.Holder());
END
}
}
my $nativeType = GetNativeTypeFromSignature($attribute->signature, 0);
if ($attribute->signature->type eq "EventListener") {
if ($dataNode->name eq "DOMWindow") {
push(@implContentDecls, " if (!imp->document())\n");
push(@implContentDecls, " return;\n");
}
} else {
my $value = JSValueToNative($attribute->signature, "value", "info.GetIsolate()");
my $arrayType = $codeGenerator->GetArrayType($nativeType);
if ($nativeType =~ /^V8Parameter/) {
push(@implContentDecls, " " . ConvertToV8Parameter($attribute->signature, $nativeType, "v", $value, "VOID") . "\n");
} elsif ($arrayType) {
push(@implContentDecls, " Vector<$arrayType> v = $value;\n");
} else {
push(@implContentDecls, " $nativeType v = $value;\n");
}
}
my $result = "v";
my $returnType = $attribute->signature->type;
if (IsRefPtrType($returnType) && !$codeGenerator->GetArrayType($returnType)) {
$result = "WTF::getPtr(" . $result . ")";
}
my $useExceptions = 1 if @{$attribute->setterExceptions};
if ($useExceptions) {
AddToImplIncludes("ExceptionCode.h");
push(@implContentDecls, " ExceptionCode ec = 0;\n");
}
if ($interfaceName eq "SVGNumber") {
push(@implContentDecls, " *imp = $result;\n");
} else {
if ($attribute->signature->type eq "EventListener") {
my $implSetterFunctionName = $codeGenerator->WK_ucfirst($attrName);
AddToImplIncludes("V8AbstractEventListener.h");
if (!$codeGenerator->IsSubType($dataNode, "Node")) {
push(@implContentDecls, " transferHiddenDependency(info.Holder(), imp->$attrName(), value, V8${interfaceName}::eventListenerCacheIndex);\n");
}
if ($interfaceName eq "WorkerContext" and $attribute->signature->name eq "onerror") {
AddToImplIncludes("V8EventListenerList.h");
AddToImplIncludes("V8WorkerContextErrorHandler.h");
push(@implContentDecls, " imp->set$implSetterFunctionName(V8EventListenerList::findOrCreateWrapper<V8WorkerContextErrorHandler>(value, true)");
} elsif ($interfaceName eq "DOMWindow" and $attribute->signature->name eq "onerror") {
AddToImplIncludes("V8EventListenerList.h");
AddToImplIncludes("V8WindowErrorHandler.h");
push(@implContentDecls, " imp->set$implSetterFunctionName(V8EventListenerList::findOrCreateWrapper<V8WindowErrorHandler>(value, true)");
} else {
push(@implContentDecls, " imp->set$implSetterFunctionName(V8DOMWrapper::getEventListener(value, true, ListenerFindOrCreate)");
}
push(@implContentDecls, ", ec") if $useExceptions;
push(@implContentDecls, ");\n");
} else {
my ($functionName, @arguments) = $codeGenerator->SetterExpression(\%implIncludes, $interfaceName, $attribute);
push(@arguments, $result);
push(@arguments, "ec") if $useExceptions;
if ($attribute->signature->extendedAttributes->{"ImplementedBy"}) {
my $implementedBy = $attribute->signature->extendedAttributes->{"ImplementedBy"};
AddToImplIncludes("${implementedBy}.h");
unshift(@arguments, "imp") if !$attribute->isStatic;
$functionName = "${implementedBy}::${functionName}";
} elsif ($attribute->isStatic) {
$functionName = "${interfaceName}::${functionName}";
} else {
$functionName = "imp->${functionName}";
}
unshift(@arguments, GenerateCallWith($attribute->signature->extendedAttributes->{"CallWith"}, \@implContentDecls, " ", 1, 0));
push(@implContentDecls, " ${functionName}(" . join(", ", @arguments) . ");\n");
}
}
if ($useExceptions) {
push(@implContentDecls, " if (UNLIKELY(ec))\n");
push(@implContentDecls, " setDOMException(ec, info.GetIsolate());\n");
}
if ($codeGenerator->ExtendedAttributeContains($attribute->signature->extendedAttributes->{"CallWith"}, "ScriptState")) {
push(@implContentDecls, " if (state.hadException())\n");
push(@implContentDecls, " throwError(state.exception(), info.GetIsolate());\n");
}
if ($svgNativeType) {
if ($useExceptions) {
push(@implContentDecls, " if (!ec)\n");
push(@implContentDecls, " wrapper->commitChange();\n");
} else {
push(@implContentDecls, " wrapper->commitChange();\n");
}
}
if ($attribute->signature->type eq "SerializedScriptValue" && $attribute->signature->extendedAttributes->{"CachedAttribute"}) {
push(@implContentDecls, <<END);
info.Holder()->DeleteHiddenValue(v8::String::NewSymbol("${attrName}")); // Invalidate the cached value.
END
}
push(@implContentDecls, " return;\n");
push(@implContentDecls, "}\n\n"); # end of setter
push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString;
}
sub GetFunctionTemplateCallbackName
{
my $function = shift;
my $interfaceName = shift;
my $name = $function->signature->name;
if ($function->signature->extendedAttributes->{"Custom"} ||
$function->signature->extendedAttributes->{"V8Custom"}) {
if ($function->signature->extendedAttributes->{"Custom"} &&
$function->signature->extendedAttributes->{"V8Custom"}) {
die "Custom and V8Custom should be mutually exclusive!"
}
return "V8${interfaceName}::${name}Callback";
} else {
return "${interfaceName}V8Internal::${name}Callback";
}
}
sub GenerateEventListenerCallback
{
my $interfaceName = shift;
my $requiresHiddenDependency = shift;
my $functionName = shift;
my $lookupType = ($functionName eq "add") ? "OrCreate" : "Only";
my $passRefPtrHandling = ($functionName eq "add") ? "" : ".get()";
my $hiddenDependencyAction = ($functionName eq "add") ? "create" : "remove";
push(@implContentDecls, <<END);
static v8::Handle<v8::Value> ${functionName}EventListenerCallback(const v8::Arguments& args)
{
INC_STATS("DOM.${interfaceName}.${functionName}EventListener()");
RefPtr<EventListener> listener = V8DOMWrapper::getEventListener(args[1], false, ListenerFind${lookupType});
if (listener) {
V8${interfaceName}::toNative(args.Holder())->${functionName}EventListener(toWebCoreAtomicString(args[0]), listener${passRefPtrHandling}, args[2]->BooleanValue());
END
if ($requiresHiddenDependency) {
push(@implContentDecls, <<END);
${hiddenDependencyAction}HiddenDependency(args.Holder(), args[1], V8${interfaceName}::eventListenerCacheIndex);
END
}
push(@implContentDecls, <<END);
}
return v8Undefined();
}
END
}
sub GenerateParametersCheckExpression
{
my $numParameters = shift;
my $function = shift;
my @andExpression = ();
push(@andExpression, "args.Length() == $numParameters");
my $parameterIndex = 0;
foreach my $parameter (@{$function->parameters}) {
last if $parameterIndex >= $numParameters;
my $value = "args[$parameterIndex]";
my $type = $parameter->type;
# Only DOMString or wrapper types are checked.
# For DOMString with StrictTypeChecking only Null, Undefined and Object
# are accepted for compatibility. Otherwise, no restrictions are made to
# match the non-overloaded behavior.
# FIXME: Implement WebIDL overload resolution algorithm.
if ($codeGenerator->IsStringType($type)) {
if ($parameter->extendedAttributes->{"StrictTypeChecking"}) {
push(@andExpression, "(${value}->IsNull() || ${value}->IsUndefined() || ${value}->IsString() || ${value}->IsObject())");
}
} elsif ($parameter->extendedAttributes->{"Callback"}) {
# For Callbacks only checks if the value is null or object.
push(@andExpression, "(${value}->IsNull() || ${value}->IsFunction())");
} elsif ($codeGenerator->IsArrayType($type) || $codeGenerator->GetSequenceType($type)) {
# FIXME: Add proper support for T[], T[]?, sequence<T>.
if ($parameter->isNullable) {
push(@andExpression, "(${value}->IsNull() || ${value}->IsArray())");
} else {
push(@andExpression, "(${value}->IsArray())");
}
} elsif (IsWrapperType($type)) {
if ($parameter->isNullable) {
push(@andExpression, "(${value}->IsNull() || V8${type}::HasInstance($value))");
} else {
push(@andExpression, "(V8${type}::HasInstance($value))");
}
}
$parameterIndex++;
}
my $res = join(" && ", @andExpression);
$res = "($res)" if @andExpression > 1;
return $res;
}
sub GenerateFunctionParametersCheck
{
my $function = shift;
my @orExpression = ();
my $numParameters = 0;
my $hasVariadic = 0;
my $numMandatoryParams = @{$function->parameters};
foreach my $parameter (@{$function->parameters}) {
if ($parameter->extendedAttributes->{"Optional"}) {
push(@orExpression, GenerateParametersCheckExpression($numParameters, $function));
$numMandatoryParams--;
}
if ($parameter->isVariadic) {
$hasVariadic = 1;
last;
}
$numParameters++;
}
if (!$hasVariadic) {
push(@orExpression, GenerateParametersCheckExpression($numParameters, $function));
}
return ($numMandatoryParams, join(" || ", @orExpression));
}
sub GenerateOverloadedFunctionCallback
{
my $function = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
# Generate code for choosing the correct overload to call. Overloads are
# chosen based on the total number of arguments passed and the type of
# values passed in non-primitive argument slots. When more than a single
# overload is applicable, precedence is given according to the order of
# declaration in the IDL.
my $name = $function->signature->name;
my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
my $leastNumMandatoryParams = 255;
push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString;
push(@implContentDecls, <<END);
static v8::Handle<v8::Value> ${name}Callback(const v8::Arguments& args)
{
INC_STATS(\"DOM.$interfaceName.$name\");
END
push(@implContentDecls, GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"}));
foreach my $overload (@{$function->{overloads}}) {
my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($overload);
$leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams);
push(@implContentDecls, " if ($parametersCheck)\n");
push(@implContentDecls, " return ${name}$overload->{overloadIndex}Callback(args);\n");
}
if ($leastNumMandatoryParams >= 1) {
push(@implContentDecls, " if (args.Length() < $leastNumMandatoryParams)\n");
push(@implContentDecls, " return throwNotEnoughArgumentsError(args.GetIsolate());\n");
}
push(@implContentDecls, <<END);
return throwTypeError(0, args.GetIsolate());
END
push(@implContentDecls, "}\n\n");
push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString;
}
sub GenerateFunctionCallback
{
my $function = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $name = $function->signature->name;
if (@{$function->{overloads}} > 1) {
# Append a number to an overloaded method's name to make it unique:
$name = $name . $function->{overloadIndex};
}
# Adding and removing event listeners are not standard callback behavior,
# but they are extremely consistent across the various classes that take event listeners,
# so we can generate them as a "special case".
if ($name eq "addEventListener") {
GenerateEventListenerCallback($interfaceName, !$codeGenerator->IsSubType($dataNode, "Node"), "add");
return;
} elsif ($name eq "removeEventListener") {
GenerateEventListenerCallback($interfaceName, !$codeGenerator->IsSubType($dataNode, "Node"), "remove");
return;
}
my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
push(@implContentDecls, "#if ${conditionalString}\n\n") if $conditionalString;
push(@implContentDecls, <<END);
static v8::Handle<v8::Value> ${name}Callback(const v8::Arguments& args)
{
INC_STATS(\"DOM.$interfaceName.$name\");
END
push(@implContentDecls, GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"}));
push(@implContentDecls, GenerateArgumentsCountCheck($function, $dataNode));
if ($name eq "set" and $dataNode->extendedAttributes->{"TypedArray"}) {
AddToImplIncludes("V8ArrayBufferViewCustom.h");
push(@implContentDecls, <<END);
return setWebGLArrayHelper<$interfaceName, V8${interfaceName}>(args);
}
END
return;
}
my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($interfaceName);
if ($svgNativeType) {
my $nativeClassName = GetNativeType($interfaceName);
if ($interfaceName =~ /List$/) {
push(@implContentDecls, " $nativeClassName imp = V8${interfaceName}::toNative(args.Holder());\n");
} else {
AddToImplIncludes("ExceptionCode.h");
push(@implContentDecls, " $nativeClassName wrapper = V8${interfaceName}::toNative(args.Holder());\n");
push(@implContentDecls, " if (wrapper->isReadOnly())\n");
push(@implContentDecls, " return setDOMException(NO_MODIFICATION_ALLOWED_ERR, args.GetIsolate());\n");
my $svgWrappedNativeType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($interfaceName);
push(@implContentDecls, " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n");
push(@implContentDecls, " $svgWrappedNativeType* imp = &impInstance;\n");
}
} elsif (!$function->isStatic) {
push(@implContentDecls, <<END);
${interfaceName}* imp = V8${interfaceName}::toNative(args.Holder());
END
}
# Check domain security if needed
if (($dataNode->extendedAttributes->{"CheckSecurity"}
|| $interfaceName eq "DOMWindow")
&& !$function->signature->extendedAttributes->{"DoNotCheckSecurity"}) {
# We have not find real use cases yet.
AddToImplIncludes("Frame.h");
push(@implContentDecls, <<END);
if (!BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), imp->frame()))
return v8Undefined();
END
}
my $raisesExceptions = @{$function->raisesExceptions};
if (!$raisesExceptions) {
foreach my $parameter (@{$function->parameters}) {
if ((!$parameter->extendedAttributes->{"Callback"} and TypeCanFailConversion($parameter)) or $parameter->extendedAttributes->{"IsIndex"}) {
$raisesExceptions = 1;
}
}
}
if ($raisesExceptions) {
AddToImplIncludes("ExceptionCode.h");
push(@implContentDecls, " ExceptionCode ec = 0;\n");
push(@implContentDecls, " {\n");
# The brace here is needed to prevent the ensuing 'goto fail's from jumping past constructors
# of objects (like Strings) declared later, causing compile errors. The block scope ends
# right before the label 'fail:'.
}
if ($function->signature->extendedAttributes->{"CheckSecurityForNode"}) {
push(@implContentDecls, " if (!BindingSecurity::shouldAllowAccessToNode(BindingState::instance(), imp->" . $function->signature->name . "(ec)))\n");
push(@implContentDecls, " return v8::Handle<v8::Value>(v8::Null(args.GetIsolate()));\n");
END
}
my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interfaceName);
push(@implContentDecls, $parameterCheckString);
# Build the function call string.
push(@implContentDecls, GenerateFunctionCallString($function, $paramIndex, " ", $interfaceName, %replacements));
if ($raisesExceptions) {
push(@implContentDecls, " }\n");
push(@implContentDecls, " fail:\n");
push(@implContentDecls, " return setDOMException(ec, args.GetIsolate());\n");
}
push(@implContentDecls, "}\n\n");
push(@implContentDecls, "#endif // ${conditionalString}\n\n") if $conditionalString;
}
sub GenerateCallWith
{
my $callWith = shift;
return () unless $callWith;
my $outputArray = shift;
my $indent = shift;
my $returnVoid = shift;
my $emptyContext = shift;
my $function = shift;
my @callWithArgs;
if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState")) {
if ($emptyContext) {
push(@$outputArray, $indent . "EmptyScriptState state;\n");
push(@callWithArgs, "&state");
} else {
push(@$outputArray, $indent . "ScriptState* state = ScriptState::current();\n");
push(@$outputArray, $indent . "if (!state)\n");
push(@$outputArray, $indent . " return" . ($returnVoid ? "" : " v8Undefined()") . ";\n");
push(@callWithArgs, "state");
}
}
if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptExecutionContext")) {
push(@$outputArray, $indent . "ScriptExecutionContext* scriptContext = getScriptExecutionContext();\n");
push(@callWithArgs, "scriptContext");
}
if ($function and $codeGenerator->ExtendedAttributeContains($callWith, "ScriptArguments")) {
push(@$outputArray, $indent . "RefPtr<ScriptArguments> scriptArguments(createScriptArguments(args, " . @{$function->parameters} . "));\n");
push(@callWithArgs, "scriptArguments.release()");
AddToImplIncludes("ScriptArguments.h");
AddToImplIncludes("ScriptCallStackFactory.h");
}
return @callWithArgs;
}
sub GenerateArgumentsCountCheck
{
my $function = shift;
my $dataNode = shift;
my $numMandatoryParams = 0;
my $allowNonOptional = 1;
foreach my $param (@{$function->parameters}) {
if ($param->extendedAttributes->{"Optional"} or $param->isVariadic) {
$allowNonOptional = 0;
} else {
die "An argument must not be declared to be optional unless all subsequent arguments to the operation are also optional." if !$allowNonOptional;
$numMandatoryParams++;
}
}
my $argumentsCountCheckString = "";
if ($numMandatoryParams >= 1) {
$argumentsCountCheckString .= " if (args.Length() < $numMandatoryParams)\n";
$argumentsCountCheckString .= " return throwNotEnoughArgumentsError(args.GetIsolate());\n";
}
return $argumentsCountCheckString;
}
sub GetIndexOf
{
my $paramName = shift;
my @paramList = @_;
my $index = 0;
foreach my $param (@paramList) {
if ($paramName eq $param) {
return $index;
}
$index++;
}
return -1;
}
sub GenerateParametersCheck
{
my $function = shift;
my $interfaceName = shift;
my $parameterCheckString = "";
my $paramIndex = 0;
my @paramTransferListNames = ();
my %replacements = ();
foreach my $parameter (@{$function->parameters}) {
TranslateParameter($parameter);
my $nativeType = GetNativeTypeFromSignature($parameter, $paramIndex);
# Optional arguments with [Optional] should generate an early call with fewer arguments.
# Optional arguments with [Optional=...] should not generate the early call.
# Optional Dictionary arguments always considered to have default of empty dictionary.
my $optional = $parameter->extendedAttributes->{"Optional"};
if ($optional && $optional ne "DefaultIsUndefined" && $optional ne "DefaultIsNullString" && $nativeType ne "Dictionary" && !$parameter->extendedAttributes->{"Callback"}) {
$parameterCheckString .= " if (args.Length() <= $paramIndex) {\n";
my $functionCall = GenerateFunctionCallString($function, $paramIndex, " " x 2, $interfaceName, %replacements);
$parameterCheckString .= $functionCall;
$parameterCheckString .= " }\n";
}
my $parameterDefaultPolicy = "DefaultIsUndefined";
if ($optional and $optional eq "DefaultIsNullString") {
$parameterDefaultPolicy = "DefaultIsNullString";
}
my $parameterName = $parameter->name;
if (GetIndexOf($parameterName, @paramTransferListNames) != -1) {
$replacements{$parameterName} = "messagePortArray" . ucfirst($parameterName);
$paramIndex++;
next;
}
AddToImplIncludes("ExceptionCode.h");
if ($parameter->extendedAttributes->{"Callback"}) {
my $v8InterfaceName = "V8" . $parameter->type;
AddToImplIncludes("$v8InterfaceName.h");
if ($optional) {
$parameterCheckString .= " RefPtr<" . $parameter->type . "> $parameterName;\n";
$parameterCheckString .= " if (args.Length() > $paramIndex && !args[$paramIndex]->IsNull() && !args[$paramIndex]->IsUndefined()) {\n";
$parameterCheckString .= " if (!args[$paramIndex]->IsFunction())\n";
$parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n";
$parameterCheckString .= " $parameterName = ${v8InterfaceName}::create(args[$paramIndex], getScriptExecutionContext());\n";
$parameterCheckString .= " }\n";
} else {
$parameterCheckString .= " if (args.Length() <= $paramIndex || !args[$paramIndex]->IsFunction())\n";
$parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n";
$parameterCheckString .= " RefPtr<" . $parameter->type . "> $parameterName = ${v8InterfaceName}::create(args[$paramIndex], getScriptExecutionContext());\n";
}
} elsif ($parameter->extendedAttributes->{"Clamp"}) {
my $nativeValue = "${parameterName}NativeValue";
my $paramType = $parameter->type;
$parameterCheckString .= " $paramType $parameterName = 0;\n";
$parameterCheckString .= " EXCEPTION_BLOCK(double, $nativeValue, args[$paramIndex]->NumberValue());\n";
$parameterCheckString .= " if (!isnan($nativeValue))\n";
$parameterCheckString .= " $parameterName = clampTo<$paramType>($nativeValue);\n";
} elsif ($parameter->type eq "SerializedScriptValue") {
AddToImplIncludes("SerializedScriptValue.h");
my $useTransferList = 0;
my $transferListName = "";
my $TransferListName = "";
if ($parameter->extendedAttributes->{"TransferList"}) {
$transferListName = $parameter->extendedAttributes->{"TransferList"};
push(@paramTransferListNames, $transferListName);
my @allParameterNames = ();
foreach my $parameter (@{$function->parameters}) {
push(@allParameterNames, $parameter->name);
}
my $transferListIndex = GetIndexOf($transferListName, @allParameterNames);
if ($transferListIndex == -1) {
die "IDL error: TransferList refers to a nonexistent argument";
}
AddToImplIncludes("wtf/ArrayBuffer.h");
AddToImplIncludes("MessagePort.h");
$TransferListName = ucfirst($transferListName);
$parameterCheckString .= " MessagePortArray messagePortArray$TransferListName;\n";
$parameterCheckString .= " ArrayBufferArray arrayBufferArray$TransferListName;\n";
$parameterCheckString .= " if (args.Length() > $transferListIndex) {\n";
$parameterCheckString .= " if (!extractTransferables(args[$transferListIndex], messagePortArray$TransferListName, arrayBufferArray$TransferListName, args.GetIsolate()))\n";
$parameterCheckString .= " return throwTypeError(\"Could not extract transferables\");\n";
$parameterCheckString .= " }\n";
$useTransferList = 1;
}
$parameterCheckString .= " bool ${parameterName}DidThrow = false;\n";
if (!$useTransferList) {
$parameterCheckString .= " $nativeType $parameterName = SerializedScriptValue::create(args[$paramIndex], 0, 0, ${parameterName}DidThrow, args.GetIsolate());\n";
} else {
$parameterCheckString .= " $nativeType $parameterName = SerializedScriptValue::create(args[$paramIndex], &messagePortArray$TransferListName, &arrayBufferArray$TransferListName, ${parameterName}DidThrow, args.GetIsolate());\n";
}
$parameterCheckString .= " if (${parameterName}DidThrow)\n";
$parameterCheckString .= " return v8Undefined();\n";
} elsif (TypeCanFailConversion($parameter)) {
$parameterCheckString .= " $nativeType $parameterName = " .
JSValueToNative($parameter, "args[$paramIndex]", "args.GetIsolate()") . ";\n";
$parameterCheckString .= " if (UNLIKELY(!$parameterName)) {\n";
$parameterCheckString .= " ec = TYPE_MISMATCH_ERR;\n";
$parameterCheckString .= " goto fail;\n";
$parameterCheckString .= " }\n";
} elsif ($parameter->isVariadic) {
my $nativeElementType = GetNativeType($parameter->type);
if ($nativeElementType =~ />$/) {
$nativeElementType .= " ";
}
my $argType = $parameter->type;
if (IsWrapperType($argType)) {
$parameterCheckString .= " Vector<$nativeElementType> $parameterName;\n";
$parameterCheckString .= " for (int i = $paramIndex; i < args.Length(); ++i) {\n";
$parameterCheckString .= " if (!V8${argType}::HasInstance(args[i]))\n";
$parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n";
$parameterCheckString .= " $parameterName.append(V8${argType}::toNative(v8::Handle<v8::Object>::Cast(args[i])));\n";
$parameterCheckString .= " }\n";
} else {
$parameterCheckString .= " EXCEPTION_BLOCK(Vector<$nativeElementType>, $parameterName, toNativeArguments<$nativeElementType>(args, $paramIndex));\n";
}
} elsif ($nativeType =~ /^V8Parameter/) {
my $value = JSValueToNative($parameter, "MAYBE_MISSING_PARAMETER(args, $paramIndex, $parameterDefaultPolicy)", "args.GetIsolate()");
$parameterCheckString .= " " . ConvertToV8Parameter($parameter, $nativeType, $parameterName, $value) . "\n";
} else {
# If the "StrictTypeChecking" extended attribute is present, and the argument's type is an
# interface type, then if the incoming value does not implement that interface, a TypeError
# is thrown rather than silently passing NULL to the C++ code.
# Per the Web IDL and ECMAScript specifications, incoming values can always be converted
# to both strings and numbers, so do not throw TypeError if the argument is of these
# types.
if ($function->signature->extendedAttributes->{"StrictTypeChecking"}) {
my $argValue = "args[$paramIndex]";
my $argType = $parameter->type;
if (IsWrapperType($argType)) {
$parameterCheckString .= " if (args.Length() > $paramIndex && !isUndefinedOrNull($argValue) && !V8${argType}::HasInstance($argValue))\n";
$parameterCheckString .= " return throwTypeError(0, args.GetIsolate());\n";
}
}
$parameterCheckString .= " EXCEPTION_BLOCK($nativeType, $parameterName, " .
JSValueToNative($parameter, "MAYBE_MISSING_PARAMETER(args, $paramIndex, $parameterDefaultPolicy)", "args.GetIsolate()") . ");\n";
if ($nativeType eq 'Dictionary') {
$parameterCheckString .= " if (!$parameterName.isUndefinedOrNull() && !$parameterName.isObject())\n";
$parameterCheckString .= " return throwTypeError(\"Not an object.\", args.GetIsolate());\n";
}
}
if ($parameter->extendedAttributes->{"IsIndex"}) {
$parameterCheckString .= " if (UNLIKELY($parameterName < 0)) {\n";
$parameterCheckString .= " ec = INDEX_SIZE_ERR;\n";
$parameterCheckString .= " goto fail;\n";
$parameterCheckString .= " }\n";
}
$paramIndex++;
}
return ($parameterCheckString, $paramIndex, %replacements);
}
sub GenerateOverloadedConstructorCallback
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
push(@implContent, <<END);
v8::Handle<v8::Value> V8${interfaceName}::constructorCallback(const v8::Arguments& args)
{
INC_STATS("DOM.${interfaceName}.Constructor");
if (!args.IsConstructCall())
return throwTypeError("DOM object constructor cannot be called as a function.");
if (ConstructorMode::current() == ConstructorMode::WrapExistingObject)
return args.Holder();
END
my $leastNumMandatoryParams = 255;
foreach my $constructor (@{$dataNode->constructors}) {
my $name = "constructor" . $constructor->{overloadedIndex} . "Callback";
my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($constructor);
$leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams);
push(@implContent, " if ($parametersCheck)\n");
push(@implContent, " return $name(args);\n");
}
if ($leastNumMandatoryParams >= 1) {
push(@implContent, " if (args.Length() < $leastNumMandatoryParams)\n");
push(@implContent, " return throwNotEnoughArgumentsError(args.GetIsolate());\n");
}
push(@implContent, <<END);
return throwTypeError(0, args.GetIsolate());
END
push(@implContent, "}\n\n");
}
sub GenerateSingleConstructorCallback
{
my $dataNode = shift;
my $function = shift;
my $interfaceName = $dataNode->name;
my $overloadedIndexString = "";
if ($function->{overloadedIndex} > 0) {
$overloadedIndexString .= $function->{overloadedIndex};
}
my $raisesExceptions = @{$function->raisesExceptions};
if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) {
$raisesExceptions = 1;
}
if (!$raisesExceptions) {
foreach my $parameter (@{$function->parameters}) {
if ((!$parameter->extendedAttributes->{"Callback"} and TypeCanFailConversion($parameter)) or $parameter->extendedAttributes->{"IsIndex"}) {
$raisesExceptions = 1;
}
}
}
my $maybeObserveFeature = GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"});
my @beforeArgumentList;
my @afterArgumentList;
push(@implContent, <<END);
v8::Handle<v8::Value> V8${interfaceName}::constructor${overloadedIndexString}Callback(const v8::Arguments& args)
{
INC_STATS("DOM.${interfaceName}.Constructor${overloadedIndexString}");
${maybeObserveFeature}
END
if ($function->{overloadedIndex} == 0) {
push(@implContent, <<END);
if (!args.IsConstructCall())
return throwTypeError("DOM object constructor cannot be called as a function.");
if (ConstructorMode::current() == ConstructorMode::WrapExistingObject)
return args.Holder();
END
push(@implContent, GenerateArgumentsCountCheck($function, $dataNode));
}
if ($raisesExceptions) {
AddToImplIncludes("ExceptionCode.h");
push(@implContent, "\n");
push(@implContent, " ExceptionCode ec = 0;\n");
}
# FIXME: Currently [Constructor(...)] does not yet support [Optional] arguments.
# It just supports [Optional=DefaultIsUndefined] or [Optional=DefaultIsNullString].
my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interfaceName);
push(@implContent, $parameterCheckString);
if ($dataNode->extendedAttributes->{"CallWith"} && $dataNode->extendedAttributes->{"CallWith"} eq "ScriptExecutionContext") {
push(@beforeArgumentList, "context");
push(@implContent, <<END);
ScriptExecutionContext* context = getScriptExecutionContext();
END
}
if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) {
push(@afterArgumentList, "ec");
}
my @argumentList;
my $index = 0;
foreach my $parameter (@{$function->parameters}) {
last if $index eq $paramIndex;
if ($replacements{$parameter->name}) {
push(@argumentList, $replacements{$parameter->name});
} else {
push(@argumentList, $parameter->name);
}
$index++;
}
my $argumentString = join(", ", @beforeArgumentList, @argumentList, @afterArgumentList);
push(@implContent, "\n");
push(@implContent, " RefPtr<${interfaceName}> impl = ${interfaceName}::create(${argumentString});\n");
push(@implContent, " v8::Handle<v8::Object> wrapper = args.Holder();\n");
if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) {
push(@implContent, " if (ec)\n");
push(@implContent, " goto fail;\n");
}
push(@implContent, <<END);
V8DOMWrapper::createDOMWrapper(impl.release(), &info, wrapper, args.GetIsolate());
return wrapper;
END
if ($raisesExceptions) {
push(@implContent, " fail:\n");
push(@implContent, " return setDOMException(ec, args.GetIsolate());\n");
}
push(@implContent, "}\n");
push(@implContent, "\n");
}
sub GenerateConstructorCallback
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
if (@{$dataNode->constructors} == 1) {
GenerateSingleConstructorCallback($dataNode, @{$dataNode->constructors}[0]);
} else {
foreach my $constructor (@{$dataNode->constructors}) {
GenerateSingleConstructorCallback($dataNode, $constructor);
}
GenerateOverloadedConstructorCallback($dataNode);
}
}
sub GenerateEventConstructorCallback
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
AddToImplIncludes("Dictionary.h");
push(@implContent, <<END);
v8::Handle<v8::Value> V8${interfaceName}::constructorCallback(const v8::Arguments& args)
{
INC_STATS("DOM.${interfaceName}.Constructor");
if (!args.IsConstructCall())
return throwTypeError("DOM object constructor cannot be called as a function.");
if (ConstructorMode::current() == ConstructorMode::WrapExistingObject)
return args.Holder();
if (args.Length() < 1)
return throwNotEnoughArgumentsError(args.GetIsolate());
STRING_TO_V8PARAMETER_EXCEPTION_BLOCK(V8Parameter<>, type, args[0]);
${interfaceName}Init eventInit;
if (args.Length() >= 2) {
EXCEPTION_BLOCK(Dictionary, options, Dictionary(args[1], args.GetIsolate()));
if (!fill${interfaceName}Init(eventInit, options))
return v8Undefined();
}
RefPtr<${interfaceName}> event = ${interfaceName}::create(type, eventInit);
v8::Handle<v8::Object> wrapper = args.Holder();
V8DOMWrapper::createDOMWrapper(event.release(), &info, wrapper, args.GetIsolate());
return wrapper;
}
bool fill${interfaceName}Init(${interfaceName}Init& eventInit, const Dictionary& options)
{
END
foreach my $interfaceBase (@{$dataNode->parents}) {
push(@implContent, <<END);
if (!fill${interfaceBase}Init(eventInit, options))
return false;
END
}
for (my $index = 0; $index < @{$dataNode->attributes}; $index++) {
my $attribute = @{$dataNode->attributes}[$index];
if ($attribute->signature->extendedAttributes->{"InitializedByEventConstructor"}) {
my $attributeName = $attribute->signature->name;
push(@implContent, " options.get(\"$attributeName\", eventInit.$attributeName);\n");
}
}
push(@implContent, <<END);
return true;
}
END
}
sub GenerateTypedArrayConstructorCallback
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $viewType = GetTypeNameOfExternalTypedArray($dataNode);
my $type = $dataNode->extendedAttributes->{"TypedArray"};
AddToImplIncludes("V8ArrayBufferViewCustom.h");
push(@implContent, <<END);
v8::Handle<v8::Value> V8${interfaceName}::constructorCallback(const v8::Arguments& args)
{
INC_STATS("DOM.${interfaceName}.Contructor");
return constructWebGLArray<$interfaceName, V8${interfaceName}, $type>(args, &info, $viewType);
}
END
}
sub GenerateNamedConstructorCallback
{
my $function = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $raisesExceptions = @{$function->raisesExceptions};
if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) {
$raisesExceptions = 1;
}
if (!$raisesExceptions) {
foreach my $parameter (@{$function->parameters}) {
if ((!$parameter->extendedAttributes->{"Callback"} and TypeCanFailConversion($parameter)) or $parameter->extendedAttributes->{"IsIndex"}) {
$raisesExceptions = 1;
}
}
}
my $maybeObserveFeature = GenerateFeatureObservation($function->signature->extendedAttributes->{"V8MeasureAs"});
my @beforeArgumentList;
my @afterArgumentList;
my $toActiveDOMObject = "0";
if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) {
$toActiveDOMObject = "V8${interfaceName}::toActiveDOMObject";
}
AddToImplIncludes("Frame.h");
push(@implContent, <<END);
WrapperTypeInfo V8${interfaceName}Constructor::info = { V8${interfaceName}Constructor::GetTemplate, V8${interfaceName}::derefObject, ${toActiveDOMObject}, 0, V8${interfaceName}::installPerContextPrototypeProperties, 0, WrapperTypeObjectPrototype };
static v8::Handle<v8::Value> V8${interfaceName}ConstructorCallback(const v8::Arguments& args)
{
INC_STATS("DOM.${interfaceName}.Constructor");
${maybeObserveFeature}
if (!args.IsConstructCall())
return throwTypeError("DOM object constructor cannot be called as a function.");
if (ConstructorMode::current() == ConstructorMode::WrapExistingObject)
return args.Holder();
Document* document = currentDocument(BindingState::instance());
// Make sure the document is added to the DOM Node map. Otherwise, the ${interfaceName} instance
// may end up being the only node in the map and get garbage-collected prematurely.
toV8(document, args.Holder(), args.GetIsolate());
END
push(@implContent, GenerateArgumentsCountCheck($function, $dataNode));
if ($raisesExceptions) {
AddToImplIncludes("ExceptionCode.h");
push(@implContent, "\n");
push(@implContent, " ExceptionCode ec = 0;\n");
}
my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interfaceName);
push(@implContent, $parameterCheckString);
push(@beforeArgumentList, "document");
if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) {
push(@afterArgumentList, "ec");
}
my @argumentList;
my $index = 0;
foreach my $parameter (@{$function->parameters}) {
last if $index eq $paramIndex;
if ($replacements{$parameter->name}) {
push(@argumentList, $replacements{$parameter->name});
} else {
push(@argumentList, $parameter->name);
}
$index++;
}
my $argumentString = join(", ", @beforeArgumentList, @argumentList, @afterArgumentList);
push(@implContent, "\n");
push(@implContent, " RefPtr<${interfaceName}> impl = ${interfaceName}::createForJSConstructor(${argumentString});\n");
push(@implContent, " v8::Handle<v8::Object> wrapper = args.Holder();\n");
if ($dataNode->extendedAttributes->{"ConstructorRaisesException"}) {
push(@implContent, " if (ec)\n");
push(@implContent, " goto fail;\n");
}
push(@implContent, <<END);
V8DOMWrapper::createDOMWrapper(impl.release(), &V8${interfaceName}Constructor::info, wrapper, args.GetIsolate());
return wrapper;
END
if ($raisesExceptions) {
push(@implContent, " fail:\n");
push(@implContent, " return setDOMException(ec, args.GetIsolate());\n");
}
push(@implContent, "}\n");
push(@implContent, <<END);
v8::Persistent<v8::FunctionTemplate> V8${interfaceName}Constructor::GetTemplate()
{
static v8::Persistent<v8::FunctionTemplate> cachedTemplate;
if (!cachedTemplate.IsEmpty())
return cachedTemplate;
v8::HandleScope scope;
v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8${interfaceName}ConstructorCallback);
v8::Local<v8::ObjectTemplate> instance = result->InstanceTemplate();
instance->SetInternalFieldCount(V8${interfaceName}::internalFieldCount);
result->SetClassName(v8::String::NewSymbol("${interfaceName}"));
result->Inherit(V8${interfaceName}::GetTemplate());
cachedTemplate = v8::Persistent<v8::FunctionTemplate>::New(result);
return cachedTemplate;
}
END
}
sub GenerateBatchedAttributeData
{
my $dataNode = shift;
my $attributes = shift;
my $interfaceName = $dataNode->name;
foreach my $attribute (@$attributes) {
my $conditionalString = $codeGenerator->GenerateConditionalString($attribute->signature);
push(@implContent, "#if ${conditionalString}\n") if $conditionalString;
GenerateSingleBatchedAttribute($interfaceName, $attribute, ",", "");
push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
}
sub GenerateSingleBatchedAttribute
{
my $interfaceName = shift;
my $attribute = shift;
my $delimiter = shift;
my $indent = shift;
my $attrName = $attribute->signature->name;
my $attrExt = $attribute->signature->extendedAttributes;
my $accessControl = "v8::DEFAULT";
if ($attrExt->{"DoNotCheckSecurityOnGetter"}) {
$accessControl = "v8::ALL_CAN_READ";
} elsif ($attrExt->{"DoNotCheckSecurityOnSetter"}) {
$accessControl = "v8::ALL_CAN_WRITE";
} elsif ($attrExt->{"DoNotCheckSecurity"}) {
$accessControl = "v8::ALL_CAN_READ";
if (!IsReadonly($attribute)) {
$accessControl .= " | v8::ALL_CAN_WRITE";
}
}
if ($attrExt->{"V8Unforgeable"}) {
$accessControl .= " | v8::PROHIBITS_OVERWRITING";
}
$accessControl = "static_cast<v8::AccessControl>(" . $accessControl . ")";
my $customAccessor =
$attrExt->{"Custom"} ||
$attrExt->{"CustomSetter"} ||
$attrExt->{"CustomGetter"} ||
$attrExt->{"V8Custom"} ||
$attrExt->{"V8CustomSetter"} ||
$attrExt->{"V8CustomGetter"} ||
"";
if ($customAccessor eq "VALUE_IS_MISSING") {
# use the naming convension, interface + (capitalize) attr name
$customAccessor = $interfaceName . "::" . $attrName;
}
my $getter;
my $setter;
my $propAttr = "v8::None";
my $hasCustomSetter = 0;
# Check attributes.
if ($attrExt->{"NotEnumerable"}) {
$propAttr .= " | v8::DontEnum";
}
if ($attrExt->{"V8Unforgeable"}) {
$propAttr .= " | v8::DontDelete";
}
my $on_proto = "0 /* on instance */";
my $data = "0 /* no data */";
# Constructor
if ($attribute->signature->type =~ /Constructor$/) {
my $constructorType = $attribute->signature->type;
$constructorType =~ s/Constructor$//;
# $constructorType ~= /Constructor$/ indicates that it is NamedConstructor.
# We do not generate the header file for NamedConstructor of class XXXX,
# since we generate the NamedConstructor declaration into the header file of class XXXX.
if ($constructorType !~ /Constructor$/ || $attribute->signature->extendedAttributes->{"V8CustomConstructor"} || $attribute->signature->extendedAttributes->{"CustomConstructor"}) {
AddToImplIncludes("V8${constructorType}.h", $attribute->signature->extendedAttributes->{"Conditional"});
}
if ($customAccessor) {
$getter = "V8${customAccessor}AccessorGetter";
} else {
$data = "&V8${constructorType}::info";
$getter = "${interfaceName}V8Internal::${interfaceName}ConstructorGetter";
}
$setter = "${interfaceName}V8Internal::${interfaceName}ReplaceableAttrSetter";
} else {
# Default Getter and Setter
$getter = "${interfaceName}V8Internal::${attrName}AttrGetter";
$setter = "${interfaceName}V8Internal::${attrName}AttrSetter";
if ($attrExt->{"Replaceable"}) {
$setter = "${interfaceName}V8Internal::${interfaceName}ReplaceableAttrSetter";
}
# Custom Setter
if ($attrExt->{"CustomSetter"} || $attrExt->{"V8CustomSetter"} || $attrExt->{"Custom"} || $attrExt->{"V8Custom"}) {
$hasCustomSetter = 1;
$setter = "V8${customAccessor}AccessorSetter";
}
# Custom Getter
if ($attrExt->{"CustomGetter"} || $attrExt->{"V8CustomGetter"} || $attrExt->{"Custom"} || $attrExt->{"V8Custom"}) {
$getter = "V8${customAccessor}AccessorGetter";
}
}
# Read only attributes
if (IsReadonly($attribute)) {
$setter = "0";
}
# An accessor can be installed on the proto
if ($attrExt->{"V8OnProto"}) {
$on_proto = "1 /* on proto */";
}
my $commentInfo = "Attribute '$attrName' (Type: '" . $attribute->type .
"' ExtAttr: '" . join(' ', keys(%{$attrExt})) . "')";
push(@implContent, $indent . " \/\/ $commentInfo\n");
push(@implContent, $indent . " {\"$attrName\", $getter, $setter, $data, $accessControl, static_cast<v8::PropertyAttribute>($propAttr), $on_proto}" . $delimiter . "\n");
}
sub IsStandardFunction
{
my $dataNode = shift;
my $function = shift;
my $interfaceName = $dataNode->name;
my $attrExt = $function->signature->extendedAttributes;
return 0 if $attrExt->{"V8Unforgeable"};
return 0 if $function->isStatic;
return 0 if $attrExt->{"V8EnabledAtRuntime"};
return 0 if $attrExt->{"V8EnabledPerContext"};
return 0 if RequiresCustomSignature($function);
return 0 if $attrExt->{"V8DoNotCheckSignature"};
return 0 if ($attrExt->{"DoNotCheckSecurity"} && ($dataNode->extendedAttributes->{"CheckSecurity"} || $interfaceName eq "DOMWindow"));
return 0 if $attrExt->{"NotEnumerable"};
return 0 if $attrExt->{"V8ReadOnly"};
return 1;
}
sub GenerateNonStandardFunction
{
my $dataNode = shift;
my $function = shift;
my $interfaceName = $dataNode->name;
my $attrExt = $function->signature->extendedAttributes;
my $name = $function->signature->name;
my $property_attributes = "v8::DontDelete";
if ($attrExt->{"NotEnumerable"}) {
$property_attributes .= " | v8::DontEnum";
}
if ($attrExt->{"V8ReadOnly"}) {
$property_attributes .= " | v8::ReadOnly";
}
my $commentInfo = "Function '$name' (ExtAttr: '" . join(' ', keys(%{$attrExt})) . "')";
my $template = "proto";
if ($attrExt->{"V8Unforgeable"}) {
$template = "instance";
}
if ($function->isStatic) {
$template = "desc";
}
my $conditional = "";
if ($attrExt->{"V8EnabledAtRuntime"}) {
# Only call Set()/SetAccessor() if this method should be enabled
my $enable_function = GetRuntimeEnableFunctionName($function->signature);
$conditional = "if (${enable_function}())\n ";
}
if ($attrExt->{"V8EnabledPerContext"}) {
# Only call Set()/SetAccessor() if this method should be enabled
my $enable_function = GetContextEnableFunction($function->signature);
$conditional = "if (${enable_function}(impl->document()))\n ";
}
if ($attrExt->{"DoNotCheckSecurity"} &&
($dataNode->extendedAttributes->{"CheckSecurity"} || $interfaceName eq "DOMWindow")) {
# Functions that are marked DoNotCheckSecurity are always readable but if they are changed
# and then accessed on a different domain we do not return the underlying value but instead
# return a new copy of the original function. This is achieved by storing the changed value
# as hidden property.
push(@implContent, <<END);
// $commentInfo
${conditional}$template->SetAccessor(v8::String::NewSymbol("$name"), ${interfaceName}V8Internal::${name}AttrGetter, ${interfaceName}V8Internal::${interfaceName}DomainSafeFunctionSetter, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>($property_attributes));
END
return;
}
my $signature = "defaultSignature";
if ($attrExt->{"V8DoNotCheckSignature"} || $function->isStatic) {
$signature = "v8::Local<v8::Signature>()";
}
if (RequiresCustomSignature($function)) {
$signature = "${name}Signature";
push(@implContent, "\n // Custom Signature '$name'\n", CreateCustomSignature($function));
}
# Normal function call is a template
my $callback = GetFunctionTemplateCallbackName($function, $interfaceName);
if ($property_attributes eq "v8::DontDelete") {
$property_attributes = "";
} else {
$property_attributes = ", static_cast<v8::PropertyAttribute>($property_attributes)";
}
if ($template eq "proto" && $conditional eq "" && $signature eq "defaultSignature" && $property_attributes eq "") {
die "This shouldn't happen: Intraface '$interfaceName' $commentInfo\n";
}
my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
push(@implContent, "#if ${conditionalString}\n") if $conditionalString;
push(@implContent, " ${conditional}$template->Set(v8::String::NewSymbol(\"$name\"), v8::FunctionTemplate::New($callback, v8Undefined(), ${signature})$property_attributes);\n");
push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
sub GenerateImplementationIndexer
{
my $dataNode = shift;
my $indexer = shift;
my $interfaceName = $dataNode->name;
# FIXME: Figure out what NumericIndexedGetter is really supposed to do. Right now, it's only set on WebGL-related files.
my $hasCustomSetter = $dataNode->extendedAttributes->{"CustomIndexedSetter"} && !$dataNode->extendedAttributes->{"NumericIndexedGetter"};
my $hasGetter = $dataNode->extendedAttributes->{"IndexedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"};
# FIXME: Investigate and remove this nastinesss. In V8, named property handling and indexer handling are apparently decoupled,
# which means that object[X] where X is a number doesn't reach named property indexer. So we need to provide
# simplistic, mirrored indexer handling in addition to named property handling.
my $isSpecialCase = exists $indexerSpecialCases{$interfaceName};
if ($isSpecialCase) {
$hasGetter = 1;
if ($dataNode->extendedAttributes->{"CustomNamedSetter"}) {
$hasCustomSetter = 1;
}
}
my $hasEnumerator = !$isSpecialCase && $codeGenerator->IsSubType($dataNode, "Node");
# FIXME: Find a way to not have to special-case HTMLOptionsCollection.
if ($interfaceName eq "HTMLOptionsCollection") {
$hasEnumerator = 1;
$hasGetter = 1;
}
if (!$hasGetter) {
return;
}
AddToImplIncludes("V8Collection.h");
if (!$indexer) {
$indexer = $codeGenerator->FindSuperMethod($dataNode, "item");
}
my $indexerType = $indexer ? $indexer->type : 0;
# FIXME: Remove this once toV8 helper methods are implemented (see https://bugs.webkit.org/show_bug.cgi?id=32563).
if ($interfaceName eq "WebKitCSSKeyframesRule") {
$indexerType = "WebKitCSSKeyframeRule";
}
if ($indexerType && !$hasCustomSetter) {
if ($indexerType eq "DOMString") {
my $conversion = $indexer->extendedAttributes->{"TreatReturnedNullStringAs"};
if ($conversion && $conversion eq "Null") {
push(@implContent, <<END);
setCollectionStringOrUndefinedIndexedGetter<${interfaceName}>(desc);
END
} else {
push(@implContent, <<END);
setCollectionStringIndexedGetter<${interfaceName}>(desc);
END
}
} else {
push(@implContent, <<END);
setCollectionIndexedGetter<${interfaceName}, ${indexerType}>(desc);
END
# Include the header for this indexer type, because setCollectionIndexedGetter() requires toV8() for this type.
AddToImplIncludes("V8${indexerType}.h");
}
return;
}
my $hasDeleter = $dataNode->extendedAttributes->{"CustomDeleteProperty"};
my $setOn = "Instance";
# V8 has access-check callback API (see ObjectTemplate::SetAccessCheckCallbacks) and it's used on DOMWindow
# instead of deleters or enumerators. In addition, the getter should be set on prototype template, to
# get implementation straight out of the DOMWindow prototype regardless of what prototype is actually set
# on the object.
if ($interfaceName eq "DOMWindow") {
$setOn = "Prototype";
$hasDeleter = 0;
}
push(@implContent, " desc->${setOn}Template()->SetIndexedPropertyHandler(V8${interfaceName}::indexedPropertyGetter");
push(@implContent, $hasCustomSetter ? ", V8${interfaceName}::indexedPropertySetter" : ", 0");
push(@implContent, ", 0"); # IndexedPropertyQuery -- not being used at the moment.
push(@implContent, $hasDeleter ? ", V8${interfaceName}::indexedPropertyDeleter" : ", 0");
push(@implContent, ", nodeCollectionIndexedPropertyEnumerator<${interfaceName}>") if $hasEnumerator;
push(@implContent, ");\n");
}
sub GenerateImplementationNamedPropertyGetter
{
my $dataNode = shift;
my $namedPropertyGetter = shift;
my $interfaceName = $dataNode->name;
my $hasCustomNamedGetter = $dataNode->extendedAttributes->{"CustomNamedGetter"} || $dataNode->extendedAttributes->{"CustomGetOwnPropertySlot"};
if ($interfaceName eq "HTMLAppletElement" || $interfaceName eq "HTMLEmbedElement" || $interfaceName eq "HTMLObjectElement") {
$hasCustomNamedGetter = 1;
}
if ($interfaceName eq "HTMLDocument") {
$hasCustomNamedGetter = 0;
}
my $hasGetter = $dataNode->extendedAttributes->{"NamedGetter"} || $hasCustomNamedGetter;
if (!$hasGetter) {
return;
}
if (!$namedPropertyGetter) {
$namedPropertyGetter = $codeGenerator->FindSuperMethod($dataNode, "namedItem");
}
if ($namedPropertyGetter && $namedPropertyGetter->type ne "Node" && !$namedPropertyGetter->extendedAttributes->{"Custom"} && !$hasCustomNamedGetter) {
AddToImplIncludes("V8Collection.h");
my $type = $namedPropertyGetter->type;
push(@implContent, <<END);
setCollectionNamedGetter<${interfaceName}, ${type}>(desc);
END
return;
}
my $hasCustomNamedSetter = $dataNode->extendedAttributes->{"CustomNamedSetter"};
my $hasDeleter = $dataNode->extendedAttributes->{"CustomDeleteProperty"};
my $hasEnumerator = $dataNode->extendedAttributes->{"CustomEnumerateProperty"};
my $setOn = "Instance";
# V8 has access-check callback API (see ObjectTemplate::SetAccessCheckCallbacks) and it's used on DOMWindow
# instead of deleters or enumerators. In addition, the getter should be set on prototype template, to
# get implementation straight out of the DOMWindow prototype regardless of what prototype is actually set
# on the object.
if ($interfaceName eq "DOMWindow") {
$setOn = "Prototype";
$hasDeleter = 0;
$hasEnumerator = 0;
}
push(@implContent, " desc->${setOn}Template()->SetNamedPropertyHandler(V8${interfaceName}::namedPropertyGetter, ");
push(@implContent, $hasCustomNamedSetter ? "V8${interfaceName}::namedPropertySetter, " : "0, ");
# If there is a custom enumerator, there MUST be custom query to properly communicate property attributes.
push(@implContent, $hasEnumerator ? "V8${interfaceName}::namedPropertyQuery, " : "0, ");
push(@implContent, $hasDeleter ? "V8${interfaceName}::namedPropertyDeleter, " : "0, ");
push(@implContent, $hasEnumerator ? "V8${interfaceName}::namedPropertyEnumerator" : "0");
push(@implContent, ");\n");
}
sub GenerateImplementationCustomCall
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $hasCustomCall = $dataNode->extendedAttributes->{"CustomCall"};
if ($hasCustomCall) {
push(@implContent, " desc->InstanceTemplate()->SetCallAsFunctionHandler(V8${interfaceName}::callAsFunctionCallback);\n");
}
}
sub GenerateImplementationMasqueradesAsUndefined
{
my $dataNode = shift;
if ($dataNode->extendedAttributes->{"MasqueradesAsUndefined"})
{
push(@implContent, " desc->InstanceTemplate()->MarkAsUndetectable();\n");
}
}
sub GenerateImplementation
{
my $object = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $visibleInterfaceName = $codeGenerator->GetVisibleInterfaceName($dataNode);
my $v8InterfaceName = "V8$interfaceName";
my $nativeType = GetNativeTypeForConversions($dataNode);
# - Add default header template
push(@implFixedHeader, GenerateImplementationContentHeader($dataNode));
AddToImplIncludes("BindingState.h");
AddToImplIncludes("ContextFeatures.h");
AddToImplIncludes("RuntimeEnabledFeatures.h");
AddToImplIncludes("V8Binding.h");
AddToImplIncludes("V8DOMWrapper.h");
AddIncludesForType($interfaceName);
my $toActive = $dataNode->extendedAttributes->{"ActiveDOMObject"} ? "${v8InterfaceName}::toActiveDOMObject" : "0";
my $rootForGC = NeedsCustomOpaqueRootForGC($dataNode) ? "${v8InterfaceName}::opaqueRootForGC" : "0";
# Find the super descriptor.
my $parentClass = "";
my $parentClassTemplate = "";
foreach (@{$dataNode->parents}) {
my $parent = $_;
AddToImplIncludes("V8${parent}.h");
$parentClass = "V8" . $parent;
$parentClassTemplate = $parentClass . "::GetTemplate()";
last;
}
push(@implContentDecls, "namespace WebCore {\n\n");
my $parentClassInfo = $parentClass ? "&${parentClass}::info" : "0";
my $WrapperTypePrototype = $dataNode->isException ? "WrapperTypeErrorPrototype" : "WrapperTypeObjectPrototype";
push(@implContentDecls, "WrapperTypeInfo ${v8InterfaceName}::info = { ${v8InterfaceName}::GetTemplate, ${v8InterfaceName}::derefObject, $toActive, $rootForGC, ${v8InterfaceName}::installPerContextPrototypeProperties, $parentClassInfo, $WrapperTypePrototype };\n\n");
push(@implContentDecls, "namespace ${interfaceName}V8Internal {\n\n");
push(@implContentDecls, "template <typename T> void V8_USE(T) { }\n\n");
my $hasConstructors = 0;
my $hasReplaceable = 0;
# Generate property accessors for attributes.
for (my $index = 0; $index < @{$dataNode->attributes}; $index++) {
my $attribute = @{$dataNode->attributes}[$index];
my $attrType = $attribute->signature->type;
# Generate special code for the constructor attributes.
if ($attrType =~ /Constructor$/) {
if (!($attribute->signature->extendedAttributes->{"CustomGetter"} ||
$attribute->signature->extendedAttributes->{"V8CustomGetter"})) {
$hasConstructors = 1;
}
next;
}
if ($attrType eq "EventListener" && $interfaceName eq "DOMWindow") {
$attribute->signature->extendedAttributes->{"V8OnProto"} = 1;
}
if ($attrType eq "SerializedScriptValue") {
AddToImplIncludes("SerializedScriptValue.h");
}
# Do not generate accessor if this is a custom attribute. The
# call will be forwarded to a hand-written accessor
# implementation.
if ($attribute->signature->extendedAttributes->{"Custom"} ||
$attribute->signature->extendedAttributes->{"V8Custom"}) {
next;
}
# Generate the accessor.
if (!($attribute->signature->extendedAttributes->{"CustomGetter"} ||
$attribute->signature->extendedAttributes->{"V8CustomGetter"})) {
GenerateNormalAttrGetter($attribute, $dataNode);
}
if ($attribute->signature->extendedAttributes->{"Replaceable"}) {
$hasReplaceable = 1;
} elsif (!$attribute->signature->extendedAttributes->{"CustomSetter"} &&
!$attribute->signature->extendedAttributes->{"V8CustomSetter"} &&
!IsReadonly($attribute)) {
GenerateNormalAttrSetter($attribute, $dataNode);
}
}
if ($hasConstructors) {
GenerateConstructorGetter($dataNode);
}
if ($hasConstructors || $hasReplaceable) {
GenerateReplaceableAttrSetter($dataNode);
}
if (NeedsCustomOpaqueRootForGC($dataNode)) {
GenerateOpaqueRootForGC($dataNode);
}
if ($dataNode->extendedAttributes->{"TypedArray"}) {
my $viewType = GetTypeNameOfExternalTypedArray($dataNode);
push(@implContent, <<END);
v8::Handle<v8::Object> wrap($interfaceName* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
{
ASSERT(impl);
v8::Handle<v8::Object> wrapper = V8${interfaceName}::createWrapper(impl, creationContext, isolate);
if (!wrapper.IsEmpty())
wrapper->SetIndexedPropertiesToExternalArrayData(impl->baseAddress(), $viewType, impl->length());
return wrapper;
}
END
}
my $indexer;
my $namedPropertyGetter;
my @enabledPerContextFunctions;
my @normalFunctions;
my $needsDomainSafeFunctionSetter = 0;
# Generate methods for functions.
foreach my $function (@{$dataNode->functions}) {
my $isCustom = $function->signature->extendedAttributes->{"Custom"} || $function->signature->extendedAttributes->{"V8Custom"};
if (!$isCustom) {
GenerateFunctionCallback($function, $dataNode);
if ($function->{overloadIndex} > 1 && $function->{overloadIndex} == @{$function->{overloads}}) {
GenerateOverloadedFunctionCallback($function, $dataNode);
}
}
if ($function->signature->name eq "item") {
$indexer = $function->signature;
}
if ($function->signature->name eq "namedItem") {
$namedPropertyGetter = $function->signature;
}
# If the function does not need domain security check, we need to
# generate an access getter that returns different function objects
# for different calling context.
if (($dataNode->extendedAttributes->{"CheckSecurity"} || ($interfaceName eq "DOMWindow")) && $function->signature->extendedAttributes->{"DoNotCheckSecurity"}) {
if (!$isCustom || $function->{overloadIndex} == 1) {
GenerateDomainSafeFunctionGetter($function, $interfaceName);
$needsDomainSafeFunctionSetter = 1;
}
}
# Separate out functions that are enabled per context so we can process them specially.
if ($function->signature->extendedAttributes->{"V8EnabledPerContext"}) {
push(@enabledPerContextFunctions, $function);
} else {
push(@normalFunctions, $function);
}
}
if ($needsDomainSafeFunctionSetter) {
GenerateDomainSafeFunctionSetter($interfaceName);
}
# Attributes
my $attributes = $dataNode->attributes;
# For the DOMWindow interface we partition the attributes into the
# ones that disallows shadowing and the rest.
my @disallowsShadowing;
# Also separate out attributes that are enabled at runtime so we can process them specially.
my @enabledAtRuntimeAttributes;
my @enabledPerContextAttributes;
my @normalAttributes;
foreach my $attribute (@$attributes) {
if ($interfaceName eq "DOMWindow" && $attribute->signature->extendedAttributes->{"V8Unforgeable"}) {
push(@disallowsShadowing, $attribute);
} elsif ($attribute->signature->extendedAttributes->{"V8EnabledAtRuntime"}) {
push(@enabledAtRuntimeAttributes, $attribute);
} elsif ($attribute->signature->extendedAttributes->{"V8EnabledPerContext"}) {
push(@enabledPerContextAttributes, $attribute);
} else {
push(@normalAttributes, $attribute);
}
}
$attributes = \@normalAttributes;
# Put the attributes that disallow shadowing on the shadow object.
if (@disallowsShadowing) {
push(@implContent, "static const V8DOMConfiguration::BatchedAttribute shadowAttrs[] = {\n");
GenerateBatchedAttributeData($dataNode, \@disallowsShadowing);
push(@implContent, "};\n\n");
}
my $has_attributes = 0;
if (@$attributes) {
$has_attributes = 1;
push(@implContent, "static const V8DOMConfiguration::BatchedAttribute V8${interfaceName}Attrs[] = {\n");
GenerateBatchedAttributeData($dataNode, $attributes);
push(@implContent, "};\n\n");
}
# Setup table of standard callback functions
my $num_callbacks = 0;
my $has_callbacks = 0;
foreach my $function (@normalFunctions) {
# Only one table entry is needed for overloaded methods:
next if $function->{overloadIndex} > 1;
# Don't put any nonstandard functions into this table:
next if !IsStandardFunction($dataNode, $function);
if (!$has_callbacks) {
$has_callbacks = 1;
push(@implContent, "static const V8DOMConfiguration::BatchedCallback V8${interfaceName}Callbacks[] = {\n");
}
my $name = $function->signature->name;
my $callback = GetFunctionTemplateCallbackName($function, $interfaceName);
my $conditionalString = $codeGenerator->GenerateConditionalString($function->signature);
push(@implContent, "#if ${conditionalString}\n") if $conditionalString;
push(@implContent, <<END);
{"$name", $callback},
END
push(@implContent, "#endif\n") if $conditionalString;
$num_callbacks++;
}
push(@implContent, "};\n\n") if $has_callbacks;
# Setup constants
my $has_constants = 0;
my @constantsEnabledAtRuntime;
if (@{$dataNode->constants}) {
$has_constants = 1;
push(@implContent, "static const V8DOMConfiguration::BatchedConstant V8${interfaceName}Consts[] = {\n");
}
foreach my $constant (@{$dataNode->constants}) {
my $name = $constant->name;
my $value = $constant->value;
my $attrExt = $constant->extendedAttributes;
my $conditional = $attrExt->{"Conditional"};
my $implementedBy = $attrExt->{"ImplementedBy"};
if ($implementedBy) {
AddToImplIncludes("${implementedBy}.h");
}
if ($attrExt->{"V8EnabledAtRuntime"}) {
push(@constantsEnabledAtRuntime, $constant);
} else {
# FIXME: we need the static_cast here only because of one constant, NodeFilter.idl
# defines "const unsigned long SHOW_ALL = 0xFFFFFFFF". It would be better if we
# handled this here, and converted it to a -1 constant in the c++ output.
if ($conditional) {
my $conditionalString = $codeGenerator->GenerateConditionalStringFromAttributeValue($conditional);
push(@implContent, "#if ${conditionalString}\n");
}
push(@implContent, <<END);
{"${name}", static_cast<signed int>($value)},
END
push(@implContent, "#endif\n") if $conditional;
}
}
if ($has_constants) {
push(@implContent, "};\n\n");
push(@implContent, $codeGenerator->GenerateCompileTimeCheckForEnumsIfNeeded($dataNode));
}
push(@implContentDecls, "} // namespace ${interfaceName}V8Internal\n\n");
if ($dataNode->extendedAttributes->{"NamedConstructor"} && !($dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"CustomConstructor"})) {
GenerateNamedConstructorCallback(@{$dataNode->constructors}[0], $dataNode);
} elsif ($dataNode->extendedAttributes->{"Constructor"} && !($dataNode->extendedAttributes->{"V8CustomConstructor"} || $dataNode->extendedAttributes->{"CustomConstructor"})) {
GenerateConstructorCallback($dataNode);
} elsif ($codeGenerator->IsConstructorTemplate($dataNode, "Event")) {
GenerateEventConstructorCallback($dataNode);
} elsif ($codeGenerator->IsConstructorTemplate($dataNode, "TypedArray")) {
GenerateTypedArrayConstructorCallback($dataNode);
}
my $access_check = "";
if ($dataNode->extendedAttributes->{"CheckSecurity"} && !($interfaceName eq "DOMWindow")) {
$access_check = "instance->SetAccessCheckCallbacks(V8${interfaceName}::namedSecurityCheck, V8${interfaceName}::indexedSecurityCheck, v8::External::New(&V8${interfaceName}::info));";
}
# For the DOMWindow interface, generate the shadow object template
# configuration method.
if ($interfaceName eq "DOMWindow") {
push(@implContent, <<END);
static v8::Persistent<v8::ObjectTemplate> ConfigureShadowObjectTemplate(v8::Persistent<v8::ObjectTemplate> templ)
{
V8DOMConfiguration::batchConfigureAttributes(templ, v8::Handle<v8::ObjectTemplate>(), shadowAttrs, WTF_ARRAY_LENGTH(shadowAttrs));
// Install a security handler with V8.
templ->SetAccessCheckCallbacks(V8DOMWindow::namedSecurityCheck, V8DOMWindow::indexedSecurityCheck, v8::External::New(&V8DOMWindow::info));
templ->SetInternalFieldCount(V8DOMWindow::internalFieldCount);
return templ;
}
END
}
if (!$parentClassTemplate) {
$parentClassTemplate = "v8::Persistent<v8::FunctionTemplate>()";
}
# Generate the template configuration method
push(@implContent, <<END);
static v8::Persistent<v8::FunctionTemplate> Configure${v8InterfaceName}Template(v8::Persistent<v8::FunctionTemplate> desc)
{
desc->ReadOnlyPrototype();
v8::Local<v8::Signature> defaultSignature;
END
if ($dataNode->extendedAttributes->{"V8EnabledAtRuntime"}) {
my $enable_function = GetRuntimeEnableFunctionName($dataNode);
push(@implContent, <<END);
if (!${enable_function}())
defaultSignature = V8DOMConfiguration::configureTemplate(desc, \"\", $parentClassTemplate, V8${interfaceName}::internalFieldCount, 0, 0, 0, 0);
else
END
}
push(@implContent, <<END);
defaultSignature = V8DOMConfiguration::configureTemplate(desc, \"${visibleInterfaceName}\", $parentClassTemplate, V8${interfaceName}::internalFieldCount,
END
# Set up our attributes if we have them
if ($has_attributes) {
push(@implContent, <<END);
V8${interfaceName}Attrs, WTF_ARRAY_LENGTH(V8${interfaceName}Attrs),
END
} else {
push(@implContent, <<END);
0, 0,
END
}
if ($has_callbacks) {
push(@implContent, <<END);
V8${interfaceName}Callbacks, WTF_ARRAY_LENGTH(V8${interfaceName}Callbacks));
END
} else {
push(@implContent, <<END);
0, 0);
END
}
AddToImplIncludes("wtf/UnusedParam.h");
push(@implContent, <<END);
UNUSED_PARAM(defaultSignature); // In some cases, it will not be used.
END
if (IsConstructable($dataNode)) {
push(@implContent, <<END);
desc->SetCallHandler(V8${interfaceName}::constructorCallback);
END
}
if ($access_check or @enabledAtRuntimeAttributes or @normalFunctions or $has_constants) {
push(@implContent, <<END);
v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate();
v8::Local<v8::ObjectTemplate> proto = desc->PrototypeTemplate();
UNUSED_PARAM(instance); // In some cases, it will not be used.
UNUSED_PARAM(proto); // In some cases, it will not be used.
END
}
push(@implContent, " $access_check\n");
# Setup the enable-at-runtime attrs if we have them
foreach my $runtime_attr (@enabledAtRuntimeAttributes) {
my $enable_function = GetRuntimeEnableFunctionName($runtime_attr->signature);
my $conditionalString = $codeGenerator->GenerateConditionalString($runtime_attr->signature);
push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString;
push(@implContent, " if (${enable_function}()) {\n");
push(@implContent, " static const V8DOMConfiguration::BatchedAttribute attrData =\\\n");
GenerateSingleBatchedAttribute($interfaceName, $runtime_attr, ";", " ");
push(@implContent, <<END);
V8DOMConfiguration::configureAttribute(instance, proto, attrData);
}
END
push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
}
# Setup the enable-at-runtime constants if we have them
foreach my $runtime_const (@constantsEnabledAtRuntime) {
my $enable_function = GetRuntimeEnableFunctionName($runtime_const);
my $conditionalString = $codeGenerator->GenerateConditionalString($runtime_const);
my $name = $runtime_const->name;
my $value = $runtime_const->value;
push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString;
push(@implContent, " if (${enable_function}()) {\n");
push(@implContent, <<END);
static const V8DOMConfiguration::BatchedConstant constData = {"${name}", static_cast<signed int>(${value})};
V8DOMConfiguration::batchConfigureConstants(desc, proto, &constData, 1);
END
push(@implContent, " }\n");
push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
}
GenerateImplementationIndexer($dataNode, $indexer);
GenerateImplementationNamedPropertyGetter($dataNode, $namedPropertyGetter);
GenerateImplementationCustomCall($dataNode);
GenerateImplementationMasqueradesAsUndefined($dataNode);
# Define our functions with Set() or SetAccessor()
my $total_functions = 0;
foreach my $function (@normalFunctions) {
# Only one accessor is needed for overloaded methods:
next if $function->{overloadIndex} > 1;
$total_functions++;
next if IsStandardFunction($dataNode, $function);
GenerateNonStandardFunction($dataNode, $function);
$num_callbacks++;
}
die "Wrong number of callbacks generated for $interfaceName ($num_callbacks, should be $total_functions)" if $num_callbacks != $total_functions;
if ($has_constants) {
push(@implContent, <<END);
V8DOMConfiguration::batchConfigureConstants(desc, proto, V8${interfaceName}Consts, WTF_ARRAY_LENGTH(V8${interfaceName}Consts));
END
}
# Special cases
if ($interfaceName eq "DOMWindow") {
push(@implContent, <<END);
proto->SetInternalFieldCount(V8DOMWindow::internalFieldCount);
desc->SetHiddenPrototype(true);
instance->SetInternalFieldCount(V8DOMWindow::internalFieldCount);
// Set access check callbacks, but turned off initially.
// When a context is detached from a frame, turn on the access check.
// Turning on checks also invalidates inline caches of the object.
instance->SetAccessCheckCallbacks(V8DOMWindow::namedSecurityCheck, V8DOMWindow::indexedSecurityCheck, v8::External::New(&V8DOMWindow::info), false);
END
}
if ($interfaceName eq "HTMLDocument") {
push(@implContent, <<END);
desc->SetHiddenPrototype(true);
END
}
if ($interfaceName eq "Location") {
push(@implContent, <<END);
// For security reasons, these functions are on the instance instead
// of on the prototype object to ensure that they cannot be overwritten.
instance->SetAccessor(v8::String::NewSymbol("reload"), V8Location::reloadAccessorGetter, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly));
instance->SetAccessor(v8::String::NewSymbol("replace"), V8Location::replaceAccessorGetter, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly));
instance->SetAccessor(v8::String::NewSymbol("assign"), V8Location::assignAccessorGetter, 0, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly));
END
}
push(@implContent, <<END);
// Custom toString template
desc->Set(v8::String::NewSymbol("toString"), V8PerIsolateData::current()->toStringTemplate());
return desc;
}
v8::Persistent<v8::FunctionTemplate> ${v8InterfaceName}::GetRawTemplate()
{
V8PerIsolateData* data = V8PerIsolateData::current();
V8PerIsolateData::TemplateMap::iterator result = data->rawTemplateMap().find(&info);
if (result != data->rawTemplateMap().end())
return result->value;
v8::HandleScope handleScope;
v8::Persistent<v8::FunctionTemplate> templ = createRawTemplate();
data->rawTemplateMap().add(&info, templ);
return templ;
}
v8::Persistent<v8::FunctionTemplate> ${v8InterfaceName}::GetTemplate()
{
V8PerIsolateData* data = V8PerIsolateData::current();
V8PerIsolateData::TemplateMap::iterator result = data->templateMap().find(&info);
if (result != data->templateMap().end())
return result->value;
v8::HandleScope handleScope;
v8::Persistent<v8::FunctionTemplate> templ =
Configure${v8InterfaceName}Template(GetRawTemplate());
data->templateMap().add(&info, templ);
return templ;
}
bool ${v8InterfaceName}::HasInstance(v8::Handle<v8::Value> value)
{
return GetRawTemplate()->HasInstance(value);
}
END
if (@enabledPerContextAttributes) {
push(@implContent, <<END);
void ${v8InterfaceName}::installPerContextProperties(v8::Handle<v8::Object> instance, ${nativeType}* impl)
{
v8::Local<v8::Object> proto = v8::Local<v8::Object>::Cast(instance->GetPrototype());
// When building QtWebkit with V8 this variable is unused when none of the features are enabled.
UNUSED_PARAM(proto);
END
# Setup the enable-by-settings attrs if we have them
foreach my $runtimeAttr (@enabledPerContextAttributes) {
my $enableFunction = GetContextEnableFunction($runtimeAttr->signature);
my $conditionalString = $codeGenerator->GenerateConditionalString($runtimeAttr->signature);
push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString;
push(@implContent, " if (${enableFunction}(impl->document())) {\n");
push(@implContent, " static const V8DOMConfiguration::BatchedAttribute attrData =\\\n");
GenerateSingleBatchedAttribute($interfaceName, $runtimeAttr, ";", " ");
push(@implContent, <<END);
V8DOMConfiguration::configureAttribute(instance, proto, attrData);
END
push(@implContent, " }\n");
push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
push(@implContent, <<END);
}
END
}
if (@enabledPerContextFunctions) {
push(@implContent, <<END);
void ${v8InterfaceName}::installPerContextPrototypeProperties(v8::Handle<v8::Object> proto)
{
UNUSED_PARAM(proto);
END
# Setup the enable-by-settings functions if we have them
push(@implContent, <<END);
v8::Local<v8::Signature> defaultSignature = v8::Signature::New(GetTemplate());
UNUSED_PARAM(defaultSignature); // In some cases, it will not be used.
ScriptExecutionContext* context = toScriptExecutionContext(proto->CreationContext());
END
foreach my $runtimeFunc (@enabledPerContextFunctions) {
my $enableFunction = GetContextEnableFunction($runtimeFunc->signature);
my $conditionalString = $codeGenerator->GenerateConditionalString($runtimeFunc->signature);
push(@implContent, "\n#if ${conditionalString}\n") if $conditionalString;
push(@implContent, " if (context && context->isDocument() && ${enableFunction}(static_cast<Document*>(context))) {\n");
my $name = $runtimeFunc->signature->name;
my $callback = GetFunctionTemplateCallbackName($runtimeFunc, $interfaceName);
push(@implContent, <<END);
proto->Set(v8::String::NewSymbol("${name}"), v8::FunctionTemplate::New(${callback}, v8Undefined(), defaultSignature)->GetFunction());
END
push(@implContent, " }\n");
push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
push(@implContent, <<END);
}
END
}
if ($dataNode->extendedAttributes->{"ActiveDOMObject"}) {
# MessagePort is handled like an active dom object even though it doesn't inherit
# from ActiveDOMObject, so don't try to cast it to ActiveDOMObject.
my $returnValue = $interfaceName eq "MessagePort" ? "0" : "toNative(object)";
push(@implContent, <<END);
ActiveDOMObject* ${v8InterfaceName}::toActiveDOMObject(v8::Handle<v8::Object> object)
{
return ${returnValue};
}
END
}
if ($interfaceName eq "DOMWindow") {
push(@implContent, <<END);
v8::Persistent<v8::ObjectTemplate> V8DOMWindow::GetShadowObjectTemplate()
{
static v8::Persistent<v8::ObjectTemplate> V8DOMWindowShadowObjectCache;
if (V8DOMWindowShadowObjectCache.IsEmpty()) {
V8DOMWindowShadowObjectCache = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
ConfigureShadowObjectTemplate(V8DOMWindowShadowObjectCache);
}
return V8DOMWindowShadowObjectCache;
}
END
}
GenerateToV8Converters($dataNode, $v8InterfaceName, $nativeType);
push(@implContent, <<END);
void ${v8InterfaceName}::derefObject(void* object)
{
static_cast<${nativeType}*>(object)->deref();
}
} // namespace WebCore
END
my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString;
# We've already added the header for this file in implFixedHeader, so remove
# it from implIncludes to ensure we don't #include it twice.
delete $implIncludes{"${v8InterfaceName}.h"};
}
sub GenerateHeaderContentHeader
{
my $dataNode = shift;
my $v8InterfaceName = "V8" . $dataNode->name;
my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
my @headerContentHeader = split("\r", $headerTemplate);
push(@headerContentHeader, "\n#if ${conditionalString}\n") if $conditionalString;
push(@headerContentHeader, "\n#ifndef ${v8InterfaceName}" . "_h");
push(@headerContentHeader, "\n#define ${v8InterfaceName}" . "_h\n\n");
return @headerContentHeader;
}
sub GenerateImplementationContentHeader
{
my $dataNode = shift;
my $v8InterfaceName = "V8" . $dataNode->name;
my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
my @implContentHeader = split("\r", $headerTemplate);
push(@implContentHeader, "\n#include \"config.h\"\n");
push(@implContentHeader, "#include \"${v8InterfaceName}.h\"\n\n");
push(@implContentHeader, "#if ${conditionalString}\n\n") if $conditionalString;
return @implContentHeader;
}
sub GenerateCallbackHeader
{
my $object = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $v8InterfaceName = "V8$interfaceName";
# - Add default header template
push(@headerContent, GenerateHeaderContentHeader($dataNode));
my @unsortedIncludes = ();
push(@unsortedIncludes, "#include \"ActiveDOMCallback.h\"");
push(@unsortedIncludes, "#include \"$interfaceName.h\"");
push(@unsortedIncludes, "#include \"WorldContextHandle.h\"");
push(@unsortedIncludes, "#include <v8.h>");
push(@unsortedIncludes, "#include <wtf/Forward.h>");
push(@headerContent, join("\n", sort @unsortedIncludes));
push(@headerContent, "\n\nnamespace WebCore {\n\n");
push(@headerContent, "class ScriptExecutionContext;\n\n");
push(@headerContent, "class $v8InterfaceName : public $interfaceName, public ActiveDOMCallback {\n");
push(@headerContent, <<END);
public:
static PassRefPtr<${v8InterfaceName}> create(v8::Local<v8::Value> value, ScriptExecutionContext* context)
{
ASSERT(value->IsObject());
ASSERT(context);
return adoptRef(new ${v8InterfaceName}(value->ToObject(), context));
}
virtual ~${v8InterfaceName}();
END
# Functions
my $numFunctions = @{$dataNode->functions};
if ($numFunctions > 0) {
push(@headerContent, " // Functions\n");
foreach my $function (@{$dataNode->functions}) {
my @params = @{$function->parameters};
if (!$function->signature->extendedAttributes->{"Custom"} &&
!(GetNativeType($function->signature->type) eq "bool")) {
push(@headerContent, " COMPILE_ASSERT(false)");
}
push(@headerContent, " virtual " . GetNativeTypeForCallbacks($function->signature->type) . " " . $function->signature->name . "(");
my @args = ();
foreach my $param (@params) {
push(@args, GetNativeTypeForCallbacks($param->type) . " " . $param->name);
}
push(@headerContent, join(", ", @args));
push(@headerContent, ");\n");
}
}
push(@headerContent, <<END);
private:
${v8InterfaceName}(v8::Local<v8::Object>, ScriptExecutionContext*);
v8::Persistent<v8::Object> m_callback;
WorldContextHandle m_worldContext;
};
END
push(@headerContent, "}\n\n");
push(@headerContent, "#endif // $v8InterfaceName" . "_h\n\n");
my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
push(@headerContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
sub GenerateCallbackImplementation
{
my $object = shift;
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $v8InterfaceName = "V8$interfaceName";
# - Add default header template
push(@implFixedHeader, GenerateImplementationContentHeader($dataNode));
AddToImplIncludes("ScriptExecutionContext.h");
AddToImplIncludes("V8Binding.h");
AddToImplIncludes("V8Callback.h");
push(@implContent, "#include <wtf/Assertions.h>\n\n");
push(@implContent, "namespace WebCore {\n\n");
push(@implContent, <<END);
${v8InterfaceName}::${v8InterfaceName}(v8::Local<v8::Object> callback, ScriptExecutionContext* context)
: ActiveDOMCallback(context)
, m_callback(v8::Persistent<v8::Object>::New(callback))
, m_worldContext(UseCurrentWorld)
{
}
${v8InterfaceName}::~${v8InterfaceName}()
{
m_callback.Dispose();
}
END
# Functions
my $numFunctions = @{$dataNode->functions};
if ($numFunctions > 0) {
push(@implContent, "// Functions\n");
foreach my $function (@{$dataNode->functions}) {
my @params = @{$function->parameters};
if ($function->signature->extendedAttributes->{"Custom"} ||
!(GetNativeTypeForCallbacks($function->signature->type) eq "bool")) {
next;
}
AddIncludesForType($function->signature->type);
push(@implContent, "\n" . GetNativeTypeForCallbacks($function->signature->type) . " ${v8InterfaceName}::" . $function->signature->name . "(");
my @args = ();
my @argsCheck = ();
my $thisType = $function->signature->extendedAttributes->{"PassThisToCallback"};
foreach my $param (@params) {
my $paramName = $param->name;
AddIncludesForType($param->type);
push(@args, GetNativeTypeForCallbacks($param->type) . " " . $paramName);
if ($thisType and $thisType eq $param->type) {
push(@argsCheck, <<END);
ASSERT(${paramName});
END
}
}
push(@implContent, join(", ", @args));
push(@implContent, ")\n");
push(@implContent, "{\n");
push(@implContent, @argsCheck) if @argsCheck;
push(@implContent, " if (!canInvokeCallback())\n");
push(@implContent, " return true;\n\n");
push(@implContent, " v8::HandleScope handleScope;\n\n");
push(@implContent, " v8::Handle<v8::Context> v8Context = toV8Context(scriptExecutionContext(), m_worldContext);\n");
push(@implContent, " if (v8Context.IsEmpty())\n");
push(@implContent, " return true;\n\n");
push(@implContent, " v8::Context::Scope scope(v8Context);\n\n");
@args = ();
foreach my $param (@params) {
my $paramName = $param->name;
push(@implContent, " v8::Handle<v8::Value> ${paramName}Handle = " . NativeToJSValue($param, $paramName) . ";\n");
push(@implContent, " if (${paramName}Handle.IsEmpty()) {\n");
push(@implContent, " if (!isScriptControllerTerminating())\n");
push(@implContent, " CRASH();\n");
push(@implContent, " return true;\n");
push(@implContent, " }\n");
push(@args, " ${paramName}Handle");
}
if (scalar(@args) > 0) {
push(@implContent, "\n v8::Handle<v8::Value> argv[] = {\n");
push(@implContent, join(",\n", @args));
push(@implContent, "\n };\n\n");
} else {
push(@implContent, "\n v8::Handle<v8::Value> *argv = 0;\n\n");
}
push(@implContent, " bool callbackReturnValue = false;\n");
if ($thisType) {
foreach my $param (@params) {
next if $param->type ne $thisType;
my $paramName = $param->name;
push(@implContent, " return !invokeCallback(m_callback, v8::Handle<v8::Object>::Cast(${paramName}Handle), " . scalar(@params) . ", argv, callbackReturnValue, scriptExecutionContext());\n");
last;
}
} else {
push(@implContent, " return !invokeCallback(m_callback, " . scalar(@params) . ", argv, callbackReturnValue, scriptExecutionContext());\n");
}
push(@implContent, "}\n");
}
}
push(@implContent, "\n} // namespace WebCore\n\n");
my $conditionalString = $codeGenerator->GenerateConditionalString($dataNode);
push(@implContent, "#endif // ${conditionalString}\n") if $conditionalString;
}
sub BaseInterfaceName
{
my $dataNode = shift;
while (@{$dataNode->parents}) {
$dataNode = $codeGenerator->ParseInterface(@{$dataNode->parents}[0], 1);
}
return $dataNode->name;
}
sub GenerateToV8Converters
{
my $dataNode = shift;
my $v8InterfaceName = shift;
my $nativeType = shift;
my $interfaceName = $dataNode->name;
if ($dataNode->extendedAttributes->{"V8NoWrapperCache"} || $dataNode->extendedAttributes->{"SuppressToJSObject"}) {
return;
}
my $createWrapperArgumentType = GetPassRefPtrType($nativeType);
my $baseType = BaseInterfaceName($dataNode);
my $getCachedWrapper = $codeGenerator->IsSubType($dataNode, "Node") ? "V8DOMWrapper::getCachedWrapper(impl.get())" : "DOMDataStore::current(isolate)->get(impl.get())";
push(@implContent, <<END);
v8::Handle<v8::Object> ${v8InterfaceName}::createWrapper(${createWrapperArgumentType} impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
{
ASSERT(impl.get());
ASSERT($getCachedWrapper.IsEmpty());
END
if ($baseType ne $interfaceName) {
push(@implContent, <<END);
ASSERT(static_cast<void*>(static_cast<${baseType}*>(impl.get())) == static_cast<void*>(impl.get()));
END
}
AddToImplIncludes("Frame.h");
if ($codeGenerator->IsSubType($dataNode, "Document")) {
push(@implContent, <<END);
if (Frame* frame = impl->frame()) {
if (frame->script()->initializeMainWorld()) {
// initializeMainWorld may have created a wrapper for the object, retry from the start.
v8::Handle<v8::Object> wrapper = V8DOMWrapper::getCachedWrapper(impl.get());
if (!wrapper.IsEmpty())
return wrapper;
}
}
END
}
push(@implContent, <<END);
v8::Handle<v8::Object> wrapper = V8DOMWrapper::instantiateV8Object(creationContext, &info, impl.get());
if (UNLIKELY(wrapper.IsEmpty()))
return wrapper;
installPerContextProperties(wrapper, impl.get());
v8::Persistent<v8::Object> wrapperHandle = V8DOMWrapper::createDOMWrapper(impl, &info, wrapper, isolate);
if (!hasDependentLifetime)
wrapperHandle.MarkIndependent();
return wrapper;
}
END
}
sub GetNativeTypeForConversions
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
$interfaceName = $codeGenerator->GetSVGTypeNeedingTearOff($interfaceName) if $codeGenerator->IsSVGTypeNeedingTearOff($interfaceName);
return $interfaceName;;
}
sub GenerateFunctionCallString()
{
my $function = shift;
my $numberOfParameters = shift;
my $indent = shift;
my $interfaceName = shift;
my %replacements = @_;
my $name = $function->signature->name;
my $returnType = $function->signature->type;
my $nativeReturnType = GetNativeType($returnType, 0);
my $result = "";
my $isSVGTearOffType = ($codeGenerator->IsSVGTypeNeedingTearOff($returnType) and not $interfaceName =~ /List$/);
$nativeReturnType = $codeGenerator->GetSVGWrappedTypeNeedingTearOff($returnType) if $isSVGTearOffType;
if ($function->signature->extendedAttributes->{"ImplementedAs"}) {
$name = $function->signature->extendedAttributes->{"ImplementedAs"};
}
my $index = 0;
my $hasScriptState = 0;
my @arguments;
my $functionName;
my $implementedBy = $function->signature->extendedAttributes->{"ImplementedBy"};
if ($implementedBy) {
AddToImplIncludes("${implementedBy}.h");
unshift(@arguments, "imp") if !$function->isStatic;
$functionName = "${implementedBy}::${name}";
} elsif ($function->isStatic) {
$functionName = "${interfaceName}::${name}";
} else {
$functionName = "imp->${name}";
}
my $callWith = $function->signature->extendedAttributes->{"CallWith"};
my @callWithOutput = ();
my @callWithArgs = GenerateCallWith($callWith, \@callWithOutput, $indent, 0, 1, $function);
$result .= join("", @callWithOutput);
unshift(@arguments, @callWithArgs);
$index += @callWithArgs;
$numberOfParameters += @callWithArgs;
foreach my $parameter (@{$function->parameters}) {
if ($index eq $numberOfParameters) {
last;
}
my $paramName = $parameter->name;
my $paramType = $parameter->type;
if ($replacements{$paramName}) {
push @arguments, $replacements{$paramName};
} elsif ($parameter->type eq "IDBKey" || $parameter->type eq "NodeFilter" || $parameter->type eq "XPathNSResolver") {
push @arguments, "$paramName.get()";
} elsif ($codeGenerator->IsSVGTypeNeedingTearOff($parameter->type) and not $interfaceName =~ /List$/) {
push @arguments, "$paramName->propertyReference()";
$result .= $indent . "if (!$paramName)\n";
$result .= $indent . " return setDOMException(WebCore::TYPE_MISMATCH_ERR, args.GetIsolate());\n";
} elsif ($parameter->type eq "SVGMatrix" and $interfaceName eq "SVGTransformList") {
push @arguments, "$paramName.get()";
} else {
push @arguments, $paramName;
}
$index++;
}
if (@{$function->raisesExceptions}) {
push @arguments, "ec";
}
my $functionString = "$functionName(" . join(", ", @arguments) . ")";
my $return = "result";
my $returnIsRef = IsRefPtrType($returnType);
if ($returnType eq "void") {
$result .= $indent . "$functionString;\n";
} elsif ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState") or @{$function->raisesExceptions}) {
$result .= $indent . $nativeReturnType . " result = $functionString;\n";
} else {
# Can inline the function call into the return statement to avoid overhead of using a Ref<> temporary
$return = $functionString;
$returnIsRef = 0;
if ($interfaceName eq "SVGTransformList" and IsRefPtrType($returnType)) {
$return = "WTF::getPtr(" . $return . ")";
}
}
if (@{$function->raisesExceptions}) {
$result .= $indent . "if (UNLIKELY(ec))\n";
$result .= $indent . " goto fail;\n";
}
if ($codeGenerator->ExtendedAttributeContains($callWith, "ScriptState")) {
$result .= $indent . "if (state.hadException())\n";
$result .= $indent . " return throwError(state.exception(), args.GetIsolate());\n"
}
if ($isSVGTearOffType) {
AddToImplIncludes("V8$returnType.h");
AddToImplIncludes("SVGPropertyTearOff.h");
my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($returnType);
$result .= $indent . "return toV8(WTF::getPtr(${svgNativeType}::create($return)), args.Holder(), args.GetIsolate());\n";
return $result;
}
# If the implementing class is a POD type, commit changes
if ($codeGenerator->IsSVGTypeNeedingTearOff($interfaceName) and not $interfaceName =~ /List$/) {
$result .= $indent . "wrapper->commitChange();\n";
}
$return .= ".release()" if ($returnIsRef);
$result .= $indent . "return " . NativeToJSValue($function->signature, $return, "args.Holder()", "args.GetIsolate()") . ";\n";
return $result;
}
sub GetNativeTypeFromSignature
{
my $signature = shift;
my $parameterIndex = shift;
my $type = $signature->type;
if ($type eq "unsigned long" and $signature->extendedAttributes->{"IsIndex"}) {
# Special-case index arguments because we need to check that they aren't < 0.
return "int";
}
$type = GetNativeType($type, $parameterIndex >= 0 ? 1 : 0);
if ($parameterIndex >= 0 && $type eq "V8Parameter") {
# FIXME: This implements [TreatNullAs=NullString] and [TreatUndefinedAs=NullString],
# but the Web IDL spec requires [TreatNullAs=EmptyString] and [TreatUndefinedAs=EmptyString].
my $mode = "";
if (($signature->extendedAttributes->{"TreatNullAs"} and $signature->extendedAttributes->{"TreatNullAs"} eq "NullString") and ($signature->extendedAttributes->{"TreatUndefinedAs"} and $signature->extendedAttributes->{"TreatUndefinedAs"} eq "NullString")) {
$mode = "WithUndefinedOrNullCheck";
} elsif (($signature->extendedAttributes->{"TreatNullAs"} and $signature->extendedAttributes->{"TreatNullAs"} eq "NullString") or $signature->extendedAttributes->{"Reflect"}) {
$mode = "WithNullCheck";
}
# FIXME: Add the case for 'elsif ($signature->extendedAttributes->{"TreatUndefinedAs"} and $signature->extendedAttributes->{"TreatUndefinedAs"} eq "NullString"))'.
$type .= "<$mode>";
}
return $type;
}
sub IsRefPtrType
{
my $type = shift;
return 0 if $type eq "boolean";
return 0 if $type eq "float";
return 0 if $type eq "int";
return 0 if $type eq "Date";
return 0 if $type eq "DOMString";
return 0 if $type eq "double";
return 0 if $type eq "short";
return 0 if $type eq "long";
return 0 if $type eq "unsigned";
return 0 if $type eq "unsigned long";
return 0 if $type eq "unsigned short";
return 1;
}
sub GetNativeType
{
my $type = shift;
my $isParameter = shift;
my $svgNativeType = $codeGenerator->GetSVGTypeNeedingTearOff($type);
if ($svgNativeType) {
if ($svgNativeType =~ /List$/) {
return "${svgNativeType}*";
} else {
return "RefPtr<${svgNativeType} >";
}
}
my $sequenceType = $codeGenerator->GetSequenceType($type);
return "Vector<${sequenceType}>" if $sequenceType;
if ($type eq "float" or $type eq "double") {
return $type;
}
return "V8Parameter" if ($type eq "DOMString" or $type eq "DOMUserData") and $isParameter;
return "int" if $type eq "int";
return "int" if $type eq "short" or $type eq "unsigned short";
return "unsigned" if $type eq "unsigned long";
return "int" if $type eq "long";
return "long long" if $type eq "long long";
return "unsigned long long" if $type eq "unsigned long long";
return "bool" if $type eq "boolean";
return "String" if $type eq "DOMString";
return "Range::CompareHow" if $type eq "CompareHow";
return "DOMTimeStamp" if $type eq "DOMTimeStamp";
return "unsigned" if $type eq "unsigned int";
# FIXME: When EventTarget is an interface and not a mixin, fix this so that
# EventTarget can be passed as a parameter.
return "Node*" if $type eq "EventTarget" and $isParameter;
return "double" if $type eq "Date";
return "ScriptValue" if $type eq "DOMObject" or $type eq "any";
return "Dictionary" if $type eq "Dictionary";
return "String" if $type eq "DOMUserData"; # FIXME: Temporary hack?
# temporary hack
return "RefPtr<NodeFilter>" if $type eq "NodeFilter";
return "RefPtr<SerializedScriptValue>" if $type eq "SerializedScriptValue";
return "RefPtr<IDBKey>" if $type eq "IDBKey";
# necessary as resolvers could be constructed on fly.
return "RefPtr<XPathNSResolver>" if $type eq "XPathNSResolver";
return "RefPtr<DOMStringList>" if $type eq "DOMString[]";
return "RefPtr<${type}>" if IsRefPtrType($type) and not $isParameter;
return "RefPtr<MediaQueryListListener>" if $type eq "MediaQueryListListener";
# FIXME: Support T[], T[]?, sequence<T> generically
return "RefPtr<DOMStringList>" if $type eq "DOMStringList";
# Default, assume native type is a pointer with same type name as idl type
return "${type}*";
}
sub GetNativeTypeForCallbacks
{
my $type = shift;
return "const String&" if $type eq "DOMString";
return "SerializedScriptValue*" if $type eq "SerializedScriptValue";
# Callbacks use raw pointers, so pass isParameter = 1
return GetNativeType($type, 1);
}
sub TranslateParameter
{
my $signature = shift;
# The IDL uses some pseudo-types which don't really exist.
if ($signature->type eq "TimeoutHandler") {
$signature->type("DOMString");
}
}
sub TypeCanFailConversion
{
my $signature = shift;
my $type = $signature->type;
AddToImplIncludes("ExceptionCode.h") if $type eq "Attr";
return 1 if $type eq "Attr";
return 0;
}
sub JSValueToNative
{
my $signature = shift;
my $value = shift;
my $getIsolate = shift;
my $type = $signature->type;
return "$value" if $type eq "JSObject";
return "$value->BooleanValue()" if $type eq "boolean";
return "static_cast<$type>($value->NumberValue())" if $type eq "float" or $type eq "double";
return "toInt32($value)" if $type eq "long" or $type eq "short";
return "toUInt32($value)" if $type eq "unsigned long" or $type eq "unsigned short";
return "toInt64($value)" if $type eq "unsigned long long" or $type eq "long long";
return "static_cast<Range::CompareHow>($value->Int32Value())" if $type eq "CompareHow";
return "toWebCoreDate($value)" if $type eq "Date";
return "toDOMStringList($value)" if $type eq "DOMStringList";
# FIXME: Add proper support for T[], T[]? and sequence<T>.
return "toDOMStringList($value)" if $type eq "DOMString[]";
if ($type eq "DOMString" or $type eq "DOMUserData") {
return $value;
}
if ($type eq "SerializedScriptValue") {
AddToImplIncludes("SerializedScriptValue.h");
return "SerializedScriptValue::create($value, $getIsolate)";
}
if ($type eq "IDBKey") {
AddToImplIncludes("IDBBindingUtilities.h");
AddToImplIncludes("IDBKey.h");
return "createIDBKeyFromValue($value)";
}
if ($type eq "Dictionary") {
AddToImplIncludes("Dictionary.h");
return "Dictionary($value, $getIsolate)";
}
if ($type eq "DOMObject" or $type eq "any") {
AddToImplIncludes("ScriptValue.h");
return "ScriptValue($value)";
}
if ($type eq "NodeFilter") {
return "V8DOMWrapper::wrapNativeNodeFilter($value)";
}
if ($type eq "MediaQueryListListener") {
AddToImplIncludes("MediaQueryListListener.h");
return "MediaQueryListListener::create(" . $value . ")";
}
# Default, assume autogenerated type conversion routines
# FIXME: When EventTarget is an interface and not a mixin, fix this to use
# V8EventTarget::HasInstance etc.
if ($type eq "EventTarget") {
AddToImplIncludes("V8Node.h");
# EventTarget is not in DOM hierarchy, but all Nodes are EventTarget.
return "V8Node::HasInstance($value) ? V8Node::toNative(v8::Handle<v8::Object>::Cast($value)) : 0";
}
if ($type eq "XPathNSResolver") {
return "toXPathNSResolver($value)";
}
my $arrayType = $codeGenerator->GetArrayType($type);
if ($arrayType) {
return "toNativeArray<$arrayType>($value)";
}
my $sequenceType = $codeGenerator->GetSequenceType($type);
if ($sequenceType) {
return "toNativeArray<$sequenceType>($value)";
}
AddIncludesForType($type);
AddToImplIncludes("V8${type}.h");
return "V8${type}::HasInstance($value) ? V8${type}::toNative(v8::Handle<v8::Object>::Cast($value)) : 0";
}
sub GetV8HeaderName
{
my $type = shift;
return "V8Event.h" if $type eq "DOMTimeStamp";
return "EventListener.h" if $type eq "EventListener";
return "SerializedScriptValue.h" if $type eq "SerializedScriptValue";
return "ScriptValue.h" if $type eq "DOMObject" or $type eq "any";
return "V8DOMStringList.h" if $type eq "DOMString[]";
return "V8${type}.h";
}
sub CreateCustomSignature
{
my $function = shift;
my $count = @{$function->parameters};
my $name = $function->signature->name;
my $result = " const int ${name}Argc = ${count};\n" .
" v8::Handle<v8::FunctionTemplate> ${name}Argv[${name}Argc] = { ";
my $first = 1;
foreach my $parameter (@{$function->parameters}) {
if ($first) { $first = 0; }
else { $result .= ", "; }
if (IsWrapperType($parameter->type)) {
if ($parameter->type eq "XPathNSResolver") {
# Special case for XPathNSResolver. All other browsers accepts a callable,
# so, even though it's against IDL, accept objects here.
$result .= "v8::Handle<v8::FunctionTemplate>()";
} else {
my $type = $parameter->type;
my $sequenceType = $codeGenerator->GetSequenceType($type);
if ($sequenceType) {
if ($codeGenerator->SkipIncludeHeader($sequenceType)) {
$result .= "v8::Handle<v8::FunctionTemplate>()";
next;
}
AddToImplIncludes("$sequenceType.h");
} else {
my $header = GetV8HeaderName($type);
AddToImplIncludes($header);
}
$result .= "V8${type}::GetRawTemplate()";
}
} else {
$result .= "v8::Handle<v8::FunctionTemplate>()";
}
}
$result .= " };\n";
$result .= " v8::Handle<v8::Signature> ${name}Signature = v8::Signature::New(desc, ${name}Argc, ${name}Argv);\n";
return $result;
}
sub RequiresCustomSignature
{
my $function = shift;
# No signature needed for Custom function
if ($function->signature->extendedAttributes->{"Custom"} ||
$function->signature->extendedAttributes->{"V8Custom"}) {
return 0;
}
# No signature needed for overloaded function
if (@{$function->{overloads}} > 1) {
return 0;
}
if ($function->isStatic) {
return 0;
}
# Type checking is performed in the generated code
if ($function->signature->extendedAttributes->{"StrictTypeChecking"}) {
return 0;
}
foreach my $parameter (@{$function->parameters}) {
my $optional = $parameter->extendedAttributes->{"Optional"};
if (($optional && $optional ne "DefaultIsUndefined" && $optional ne "DefaultIsNullString") || $parameter->extendedAttributes->{"Callback"}) {
return 0;
}
}
foreach my $parameter (@{$function->parameters}) {
if (IsWrapperType($parameter->type)) {
return 1;
}
}
return 0;
}
my %non_wrapper_types = (
'CompareHow' => 1,
'DOMObject' => 1,
'DOMString' => 1,
'DOMString[]' => 1,
'DOMTimeStamp' => 1,
'Date' => 1,
'Dictionary' => 1,
'EventListener' => 1,
# FIXME: When EventTarget is an interface and not a mixin, fix this so that
# EventTarget is treated as a wrapper type.
'EventTarget' => 1,
'IDBKey' => 1,
'JSObject' => 1,
'MediaQueryListListener' => 1,
'NodeFilter' => 1,
'SerializedScriptValue' => 1,
'any' => 1,
'boolean' => 1,
'double' => 1,
'float' => 1,
'int' => 1,
'long long' => 1,
'long' => 1,
'short' => 1,
'unsigned int' => 1,
'unsigned long long' => 1,
'unsigned long' => 1,
'unsigned short' => 1
);
sub IsWrapperType
{
my $type = shift;
return !($non_wrapper_types{$type});
}
sub GetTypeNameOfExternalTypedArray
{
my $dataNode = shift;
my $interfaceName = $dataNode->name;
my $viewType = $dataNode->extendedAttributes->{"TypedArray"};
return "v8::kExternalByteArray" if $viewType eq "signed char" and $interfaceName eq "Int8Array";
return "v8::kExternalPixelArray" if $viewType eq "unsigned char" and $interfaceName eq "Uint8ClampedArray";
return "v8::kExternalUnsignedByteArray" if $viewType eq "unsigned char" and $interfaceName eq "Uint8Array";
return "v8::kExternalShortArray" if $viewType eq "short" and $interfaceName eq "Int16Array";
return "v8::kExternalUnsignedShortArray" if $viewType eq "unsigned short" and $interfaceName eq "Uint16Array";
return "v8::kExternalIntArray" if $viewType eq "int" and $interfaceName eq "Int32Array";
return "v8::kExternalUnsignedIntArray" if $viewType eq "unsigned int" and $interfaceName eq "Uint32Array";
return "v8::kExternalFloatArray" if $viewType eq "float" and $interfaceName eq "Float32Array";
return "v8::kExternalDoubleArray" if $viewType eq "double" and $interfaceName eq "Float64Array";
die "TypedArray of unknown type is found";
}
sub IsDOMNodeType
{
my $type = shift;
return 1 if $type eq 'Attr';
return 1 if $type eq 'CDATASection';
return 1 if $type eq 'CharacterData';
return 1 if $type eq 'Comment';
return 1 if $type eq 'Document';
return 1 if $type eq 'DocumentFragment';
return 1 if $type eq 'DocumentType';
return 1 if $type eq 'Element';
return 1 if $type eq 'Entity';
return 1 if $type eq 'EntityReference';
return 1 if $type eq 'HTMLDocument';
return 1 if $type eq 'Node';
return 1 if $type eq 'Notation';
return 1 if $type eq 'ProcessingInstruction';
return 1 if $type eq 'ShadowRoot';
return 1 if $type eq 'SVGDocument';
return 1 if $type eq 'Text';
return 1 if $type =~ /^HTML.*Element$/;
return 1 if $type =~ /^SVG.*Element$/;
return 1 if $type eq 'TestNode';
return 0;
}
sub NativeToJSValue
{
my $signature = shift;
my $value = shift;
my $getCreationContext = shift;
my $getCreationContextArg = $getCreationContext ? ", $getCreationContext" : "";
my $getIsolate = shift;
my $getIsolateArg = $getIsolate ? ", $getIsolate" : "";
my $type = $signature->type;
return ($getIsolate ? "v8Boolean($value, $getIsolate)" : "v8Boolean($value)") if $type eq "boolean";
return "v8Undefined()" if $type eq "void"; # equivalent to v8Undefined()
# HTML5 says that unsigned reflected attributes should be in the range
# [0, 2^31). When a value isn't in this range, a default value (or 0)
# should be returned instead.
if ($signature->extendedAttributes->{"Reflect"} and ($type eq "unsigned long" or $type eq "unsigned short")) {
$value =~ s/getUnsignedIntegralAttribute/getIntegralAttribute/g;
return "v8UnsignedInteger(std::max(0, " . $value . ")$getIsolateArg)";
}
# For all the types where we use 'int' as the representation type,
# we use v8Integer() which has a fast small integer conversion check.
my $nativeType = GetNativeType($type);
return "v8Integer($value$getIsolateArg)" if $nativeType eq "int";
return "v8UnsignedInteger($value$getIsolateArg)" if $nativeType eq "unsigned";
return "v8DateOrNull($value$getIsolateArg)" if $type eq "Date";
# long long and unsigned long long are not representable in ECMAScript.
return "v8::Number::New(static_cast<double>($value))" if $type eq "long long" or $type eq "unsigned long long" or $type eq "DOMTimeStamp";
return "v8::Number::New($value)" if $codeGenerator->IsPrimitiveType($type);
return "$value.v8Value()" if $nativeType eq "ScriptValue";
if ($codeGenerator->IsStringType($type)) {
my $conv = $signature->extendedAttributes->{"TreatReturnedNullStringAs"};
if (defined $conv) {
return "v8StringOrNull($value$getIsolateArg)" if $conv eq "Null";
return "v8StringOrUndefined($value$getIsolateArg)" if $conv eq "Undefined";
die "Unknown value for TreatReturnedNullStringAs extended attribute";
}
return "v8String($value$getIsolateArg)";
}
my $arrayType = $codeGenerator->GetArrayType($type);
if ($arrayType) {
if ($type eq "DOMString[]") {
AddToImplIncludes("V8DOMStringList.h");
AddToImplIncludes("DOMStringList.h");
} elsif (!$codeGenerator->SkipIncludeHeader($arrayType)) {
AddToImplIncludes("V8$arrayType.h");
AddToImplIncludes("$arrayType.h");
}
return "v8Array($value$getIsolateArg)";
}
my $sequenceType = $codeGenerator->GetSequenceType($type);
if ($sequenceType) {
if (!$codeGenerator->SkipIncludeHeader($sequenceType)) {
AddToImplIncludes("V8$sequenceType.h");
AddToImplIncludes("$sequenceType.h");
}
return "v8Array($value$getIsolateArg)";
}
AddIncludesForType($type);
if (IsDOMNodeType($type)) {
return "toV8($value$getCreationContextArg$getIsolateArg)";
}
if ($type eq "EventTarget") {
return "V8DOMWrapper::convertEventTargetToV8Object($value$getCreationContextArg$getIsolateArg)";
}
if ($type eq "EventListener") {
AddToImplIncludes("V8AbstractEventListener.h");
return "${value} ? v8::Handle<v8::Value>(static_cast<V8AbstractEventListener*>(${value})->getListenerObject(imp->scriptExecutionContext())) : v8::Handle<v8::Value>(" . ($getIsolate ? "v8::Null($getIsolate)" : "v8::Null()") . ")";
}
if ($type eq "SerializedScriptValue") {
AddToImplIncludes("$type.h");
return "$value ? $value->deserialize() : v8::Handle<v8::Value>(" . ($getIsolate ? "v8::Null($getIsolate)" : "v8::Null()") . ")";
}
AddToImplIncludes("wtf/RefCounted.h");
AddToImplIncludes("wtf/RefPtr.h");
AddToImplIncludes("wtf/GetPtr.h");
return "toV8($value$getCreationContextArg$getIsolateArg)";
}
# Internal helper
sub WriteData
{
my $object = shift;
my $dataNode = shift;
my $name = $dataNode->name;
my $prefix = FileNamePrefix;
my $headerFileName = "$outputHeadersDir/$prefix$name.h";
my $implFileName = "$outputDir/$prefix$name.cpp";
# Update a .cpp file if the contents are changed.
my $contents = join "", @implContentHeader, @implFixedHeader;
my @includes = ();
my %implIncludeConditions = ();
foreach my $include (keys %implIncludes) {
my $condition = $implIncludes{$include};
my $checkType = $include;
$checkType =~ s/\.h//;
next if $codeGenerator->IsSVGAnimatedType($checkType);
if ($include =~ /wtf/) {
$include = "\<$include\>";
} else {
$include = "\"$include\"";
}
if ($condition eq 1) {
push @includes, $include;
} else {
push @{$implIncludeConditions{$condition}}, $include;
}
}
foreach my $include (sort @includes) {
$contents .= "#include $include\n";
}
foreach my $condition (sort keys %implIncludeConditions) {
$contents .= "\n#if " . $codeGenerator->GenerateConditionalStringFromAttributeValue($condition) . "\n";
foreach my $include (sort @{$implIncludeConditions{$condition}}) {
$contents .= "#include $include\n";
}
$contents .= "#endif\n";
}
$contents .= "\n";
$contents .= join "", @implContentDecls, @implContent;
$codeGenerator->UpdateFile($implFileName, $contents);
%implIncludes = ();
@implFixedHeader = ();
@implContentDecls = ();
@implContent = ();
# Update a .h file if the contents are changed.
$contents = join "", @headerContent;
$codeGenerator->UpdateFile($headerFileName, $contents);
@headerContent = ();
}
sub ConvertToV8Parameter
{
my $signature = shift;
my $nativeType = shift;
my $variableName = shift;
my $value = shift;
my $suffix = shift;
die "Wrong native type passed: $nativeType" unless $nativeType =~ /^V8Parameter/;
if ($signature->type eq "DOMString") {
my $macro = "STRING_TO_V8PARAMETER_EXCEPTION_BLOCK";
$macro .= "_$suffix" if $suffix;
return "$macro($nativeType, $variableName, $value);"
} else {
# Don't know how to properly check for conversion exceptions when $parameter->type is "DOMUserData"
return "$nativeType $variableName($value, true);";
}
}
# Returns the RuntimeEnabledFeatures function name that is hooked up to check if a method/attribute is enabled.
sub GetRuntimeEnableFunctionName
{
my $signature = shift;
# If a parameter is given (e.g. "V8EnabledAtRuntime=FeatureName") return the RuntimeEnabledFeatures::{FeatureName}Enabled() method.
return "RuntimeEnabledFeatures::" . $codeGenerator->WK_lcfirst($signature->extendedAttributes->{"V8EnabledAtRuntime"}) . "Enabled" if ($signature->extendedAttributes->{"V8EnabledAtRuntime"} && $signature->extendedAttributes->{"V8EnabledAtRuntime"} ne "VALUE_IS_MISSING");
# Otherwise return a function named RuntimeEnabledFeatures::{methodName}Enabled().
return "RuntimeEnabledFeatures::" . $codeGenerator->WK_lcfirst($signature->name) . "Enabled";
}
sub GetContextEnableFunction
{
my $signature = shift;
# If a parameter is given (e.g. "V8EnabledPerContext=FeatureName") return the {FeatureName}Allowed() method.
if ($signature->extendedAttributes->{"V8EnabledPerContext"} && $signature->extendedAttributes->{"V8EnabledPerContext"} ne "VALUE_IS_MISSING") {
return "ContextFeatures::" . $codeGenerator->WK_lcfirst($signature->extendedAttributes->{"V8EnabledPerContext"}) . "Enabled";
}
# Or it fallbacks to the attribute name if the parameter value is missing.
my $attributeName = $signature->name;
return "ContextFeatures::" . $codeGenerator->WK_lcfirst($attributeName) . "Enabled";
}
sub GetPassRefPtrType
{
my $v8InterfaceName = shift;
my $angleBracketSpace = $v8InterfaceName =~ />$/ ? " " : "";
return "PassRefPtr<${v8InterfaceName}${angleBracketSpace}>";
}
1;