blob: 0dcefb17483de120efbb3906e4273a8c29e888c4 [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)
# Copyright (C) 2012 Ericsson AB. All rights reserved.
# Copyright (C) 2013 Samsung Electronics. All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this library; see the file COPYING.LIB. If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
package Block;
# Sample code:
# my $outer = new Block("Free Name 1", "namespace Foo {", "} // namespace Foo");
# $outer->add(" void foo() {}");
# my $inner = new Block("Free Name 2", "namespace Bar {", "} // namespace Bar");
# $inner->add(" void bar() {}");
# $outer->add($inner);
# print $outer->toString();
#
# Output code:
# namespace Foo {
# void foo() {}
# namespace Bar {
# void bar() {}
# } // namespace Bar
# } // namespace Foo
sub new
{
my $package = shift;
my $name = shift || "Anonymous block";
my $header = shift || "";
my $footer = shift || "";
my $object = {
"name" => $name,
"header" => [$header],
"footer" => [$footer],
"contents" => [],
};
bless $object, $package;
return $object;
}
sub addHeader
{
my $object = shift;
my $header = shift || "";
push(@{$object->{header}}, $header);
}
sub addFooter
{
my $object = shift;
my $footer = shift || "";
push(@{$object->{footer}}, $footer);
}
sub add
{
my $object = shift;
my $content = shift || "";
push(@{$object->{contents}}, $content);
}
sub toString
{
my $object = shift;
my $header = join "", @{$object->{header}};
my $footer = join "", @{$object->{footer}};
my $code = "";
$code .= "/* BEGIN " . $object->{name} . " */\n" if $verbose;
$code .= $header . "\n" if $header;
for my $content (@{$object->{contents}}) {
if (ref($content) eq "Block") {
$code .= $content->toString();
} else {
$code .= $content;
}
}
$code .= $footer . "\n" if $footer;
$code .= "/* END " . $object->{name} . " */\n" if $verbose;
return $code;
}
package code_generator_v8;
use strict;
use Cwd;
use File::Basename;
use File::Find;
use File::Spec;
my $idlDocument;
my $idlDirectories;
my $preprocessor;
my $verbose;
my $interfaceIdlFiles;
my $writeFileOnlyIfChanged;
my $sourceRoot;
# Cache of IDL file pathnames.
my $idlFiles;
my $cachedInterfaces = {};
my %implIncludes = ();
my %headerIncludes = ();
# Header code structure:
# Root ... Copyright, include duplication check
# Conditional ... #if FEATURE ... #endif (to be removed soon)
# Includes
# NameSpaceWebCore
# Class
# ClassPublic
# ClassPrivate
my %header;
# Implementation code structure:
# Root ... Copyright
# Conditional ... #if FEATURE ... #endif (to be removed soon)
# Includes
# NameSpaceWebCore
# NameSpaceInternal ... namespace ${implClassName}V8Internal in case of non-callback
my %implementation;
# Promise is not yet in the Web IDL spec but is going to be speced
# as primitive types in the future.
# Since V8 dosn't provide Promise primitive object currently,
# primitiveTypeHash doesn't contain Promise.
my %primitiveTypeHash = ("Date" => 1,
"DOMString" => 1,
"DOMTimeStamp" => 1, # typedef unsigned long long
"boolean" => 1,
"void" => 1,
"byte" => 1,
"octet" => 1,
"short" => 1,
"long" => 1,
"long long" => 1,
"unsigned short" => 1,
"unsigned long" => 1,
"unsigned long long" => 1,
"float" => 1,
"double" => 1,
);
my %nonWrapperTypes = ("CompareHow" => 1,
"Dictionary" => 1,
"EventListener" => 1,
"EventHandler" => 1,
"MediaQueryListListener" => 1,
"NodeFilter" => 1,
"SerializedScriptValue" => 1,
"any" => 1,
);
my %typedArrayHash = ("ArrayBuffer" => [],
"ArrayBufferView" => [],
"Uint8Array" => ["unsigned char", "v8::kExternalUnsignedByteArray"],
"Uint8ClampedArray" => ["unsigned char", "v8::kExternalPixelArray"],
"Uint16Array" => ["unsigned short", "v8::kExternalUnsignedShortArray"],
"Uint32Array" => ["unsigned int", "v8::kExternalUnsignedIntArray"],
"Int8Array" => ["signed char", "v8::kExternalByteArray"],
"Int16Array" => ["short", "v8::kExternalShortArray"],
"Int32Array" => ["int", "v8::kExternalIntArray"],
"Float32Array" => ["float", "v8::kExternalFloatArray"],
"Float64Array" => ["double", "v8::kExternalDoubleArray"],
);
my %callbackFunctionTypeHash = ();
my %enumTypeHash = ();
my %svgAttributesInHTMLHash = ("class" => 1, "id" => 1, "onabort" => 1, "onclick" => 1,
"onerror" => 1, "onload" => 1, "onmousedown" => 1,
"onmouseenter" => 1, "onmouseleave" => 1,
"onmousemove" => 1, "onmouseout" => 1, "onmouseover" => 1,
"onmouseup" => 1, "onresize" => 1, "onscroll" => 1,
"onunload" => 1);
my %svgTypeNeedingTearOff = (
"SVGAngle" => "SVGPropertyTearOff<SVGAngle>",
"SVGLength" => "SVGPropertyTearOff<SVGLength>",
"SVGLengthList" => "SVGListPropertyTearOff<SVGLengthList>",
"SVGMatrix" => "SVGPropertyTearOff<SVGMatrix>",
"SVGNumber" => "SVGPropertyTearOff<SVGNumber>",
"SVGNumberList" => "SVGListPropertyTearOff<SVGNumberList>",
"SVGPathSegList" => "SVGPathSegListPropertyTearOff",
"SVGPoint" => "SVGPropertyTearOff<SVGPoint>",
"SVGPointList" => "SVGListPropertyTearOff<SVGPointList>",
"SVGPreserveAspectRatio" => "SVGPropertyTearOff<SVGPreserveAspectRatio>",
"SVGRect" => "SVGPropertyTearOff<SVGRect>",
"SVGStringList" => "SVGStaticListPropertyTearOff<SVGStringList>",
"SVGTransform" => "SVGPropertyTearOff<SVGTransform>",
"SVGTransformList" => "SVGTransformListPropertyTearOff"
);
my %svgTypeWithWritablePropertiesNeedingTearOff = (
"SVGPoint" => 1,
"SVGMatrix" => 1
);
# Default .h template
my $headerTemplate = <<EOF;
/*
This file is part of the Blink open source project.
This file has been auto-generated by CodeGeneratorV8.pm. 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
sub new
{
my $object = shift;
my $reference = { };
$idlDocument = shift;
$idlDirectories = shift;
$preprocessor = shift;
$verbose = shift;
$interfaceIdlFiles = shift;
$writeFileOnlyIfChanged = shift;
$sourceRoot = dirname(dirname(dirname(Cwd::abs_path($0))));
bless($reference, $object);
return $reference;
}
sub IDLFileForInterface
{
my $interfaceName = shift;
unless ($idlFiles) {
my @directories = map { $_ = "$sourceRoot/$_" if -d "$sourceRoot/$_"; $_ } @$idlDirectories;
push(@directories, ".");
$idlFiles = { };
foreach my $idlFile (@$interfaceIdlFiles) {
$idlFiles->{fileparse(basename($idlFile), ".idl")} = $idlFile;
}
my $wanted = sub {
$idlFiles->{$1} = $File::Find::name if /^([A-Z].*)\.idl$/;
$File::Find::prune = 1 if /^\../;
};
find($wanted, @directories);
}
return $idlFiles->{$interfaceName};
}
sub ParseInterface
{
my $interfaceName = shift;
if (exists $cachedInterfaces->{$interfaceName}) {
return $cachedInterfaces->{$interfaceName};
}
# Step #1: Find the IDL file associated with 'interface'
my $filename = IDLFileForInterface($interfaceName)
or die("Could NOT find IDL file for interface \"$interfaceName\" $!\n");
print " | |> Parsing parent IDL \"$filename\" for interface \"$interfaceName\"\n" if $verbose;
# Step #2: Parse the found IDL file (in quiet mode).
my $parser = idl_parser->new(1);
my $document = $parser->Parse($filename, $preprocessor);
foreach my $interface (@{$document->interfaces}) {
if ($interface->name eq $interfaceName or $interface->isPartial) {
$cachedInterfaces->{$interfaceName} = $interface;
return $interface;
}
}
die("Could NOT find interface definition for $interfaceName in $filename");
}
sub GenerateInterface
{
my $object = shift;
my $interface = shift;
%callbackFunctionTypeHash = map { $_->name => $_ } @{$idlDocument->callbackFunctions};
%enumTypeHash = map { $_->name => $_->values } @{$idlDocument->enumerations};
my $v8ClassName = GetV8ClassName($interface);
my $defineName = $v8ClassName . "_h";
my $internalNamespace = GetImplName($interface) . "V8Internal";
my $conditionalString = GenerateConditionalString($interface);
my $conditionalIf = "";
my $conditionalEndif = "";
if ($conditionalString) {
$conditionalIf = "#if ${conditionalString}";
$conditionalEndif = "#endif // ${conditionalString}";
}
$header{root} = new Block("ROOT", "", "");
# FIXME: newlines should be generated by Block::toString().
$header{conditional} = new Block("Conditional", "$conditionalIf", $conditionalEndif ? "$conditionalEndif\n" : "");
$header{includes} = new Block("Includes", "", "");
$header{nameSpaceWebCore} = new Block("Namespace WebCore", "\nnamespace WebCore {\n", "}\n");
$header{class} = new Block("Class definition", "", "");
$header{classPublic} = new Block("Class public:", "public:", "");
$header{classPrivate} = new Block("Class private:", "private:", "");
$header{root}->add($header{conditional});
$header{conditional}->add($header{includes});
$header{conditional}->add($header{nameSpaceWebCore});
$header{nameSpaceWebCore}->add($header{class});
$header{class}->add($header{classPublic});
$header{class}->add($header{classPrivate});
# - Add default header template
$header{root}->addHeader($headerTemplate . "\n");
$header{root}->addHeader("#ifndef $defineName\n#define $defineName\n");
$header{root}->addFooter("#endif // $defineName");
$implementation{root} = new Block("ROOT", "", "");
$conditionalEndif = "\n$conditionalEndif" if !$interface->isCallback and $conditionalEndif;
$implementation{conditional} = new Block("Conditional", $conditionalIf, $conditionalEndif);
$implementation{includes} = new Block("Includes", "", "");
# FIXME: newlines should be generated by Block::toString().
my $nameSpaceWebCoreBegin = "namespace WebCore {\n";
my $nameSpaceWebCoreEnd = "} // namespace WebCore";
$nameSpaceWebCoreBegin = "$nameSpaceWebCoreBegin\n" if !$interface->isCallback;
$nameSpaceWebCoreEnd = "\n$nameSpaceWebCoreEnd\n" if $interface->isCallback;
$implementation{nameSpaceWebCore} = new Block("Namespace WebCore", $nameSpaceWebCoreBegin, $nameSpaceWebCoreEnd);
$implementation{nameSpaceInternal} = new Block("Internal namespace", "namespace $internalNamespace {\n", "} // namespace $internalNamespace\n");
$implementation{root}->add($implementation{conditional});
$implementation{conditional}->add($implementation{includes});
$implementation{conditional}->add($implementation{nameSpaceWebCore});
if (!$interface->isCallback) {
$implementation{nameSpaceWebCore}->add($implementation{nameSpaceInternal});
}
# - Add default header template
$implementation{root}->addHeader($headerTemplate);
$implementation{root}->addHeader("\n#include \"config.h\"");
$implementation{includes}->add("#include \"${v8ClassName}.h\"\n\n");
# Start actual generation
if ($interface->isCallback) {
$object->GenerateCallbackHeader($interface);
$object->GenerateCallbackImplementation($interface);
} else {
$object->GenerateHeader($interface);
$object->GenerateImplementation($interface);
}
}
sub AddToImplIncludes
{
my $header = shift;
$implIncludes{$header} = 1;
}
sub AddToHeaderIncludes
{
my @includes = @_;
for my $include (@includes) {
$headerIncludes{$include} = 1;
}
}
sub AddIncludesForType
{
my $type = shift;
return if IsPrimitiveType($type) or IsEnumType($type);
# Default includes
if ($type eq "SerializedScriptValue") {
AddToImplIncludes("bindings/v8/SerializedScriptValue.h");
} elsif ($type eq "any" || IsCallbackFunctionType($type)) {
AddToImplIncludes("bindings/v8/ScriptValue.h");
} elsif ($type eq "Promise") {
AddToImplIncludes("bindings/v8/ScriptPromise.h");
} elsif ($type eq "EventHandler") {
AddToImplIncludes("bindings/v8/V8AbstractEventListener.h");
AddToImplIncludes("bindings/v8/V8EventListenerList.h");
} elsif (IsTypedArrayType($type)) {
AddToImplIncludes("bindings/v8/custom/V8${type}Custom.h");
} elsif (my $arrayType = GetArrayType($type)) {
AddIncludesForType($arrayType);
} else {
AddToImplIncludes("V8${type}.h");
}
}
sub HeaderFilesForInterface
{
my $interfaceName = shift;
my $implClassName = shift;
my @includes = ();
if (IsPrimitiveType($interfaceName) or IsEnumType($interfaceName) or IsCallbackFunctionType($interfaceName)) {
# Not interface type, no header files
} elsif (IsTypedArrayType($interfaceName)) {
push(@includes, "wtf/${interfaceName}.h");
} else {
my $idlFilename = Cwd::abs_path(IDLFileForInterface($interfaceName)) or die("Could NOT find IDL file for interface \"$interfaceName\" $!\n");
my $idlRelPath = File::Spec->abs2rel($idlFilename, $sourceRoot);
push(@includes, dirname($idlRelPath) . "/" . $implClassName . ".h");
}
return @includes;
}
sub NeedsOpaqueRootForGC
{
my $interface = shift;
return $interface->extendedAttributes->{"GenerateIsReachable"} || $interface->extendedAttributes->{"CustomIsReachable"};
}
sub GenerateOpaqueRootForGC
{
my $interface = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
if ($interface->extendedAttributes->{"CustomIsReachable"}) {
return;
}
my $code = <<END;
void* ${v8ClassName}::opaqueRootForGC(void* object, v8::Isolate* isolate)
{
${implClassName}* impl = fromInternalPointer(object);
END
my $isReachableMethod = $interface->extendedAttributes->{"GenerateIsReachable"};
if ($isReachableMethod) {
AddToImplIncludes("bindings/v8/V8GCController.h");
AddToImplIncludes("core/dom/Element.h");
$code .= <<END;
if (Node* owner = impl->${isReachableMethod}())
return V8GCController::opaqueRootForGC(owner, isolate);
END
}
$code .= <<END;
return object;
}
END
$implementation{nameSpaceWebCore}->add($code);
}
sub GetSVGPropertyTypes
{
my $implType = shift;
my $svgPropertyType;
my $svgListPropertyType;
my $svgNativeType;
return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $implType =~ /SVG/;
$svgNativeType = GetSVGTypeNeedingTearOff($implType);
return ($svgPropertyType, $svgListPropertyType, $svgNativeType) if not $svgNativeType;
# Append space to avoid compilation errors when using PassRefPtr<$svgNativeType>
$svgNativeType = "$svgNativeType ";
my $svgWrappedNativeType = GetSVGWrappedTypeNeedingTearOff($implType);
if ($svgNativeType =~ /SVGPropertyTearOff/) {
$svgPropertyType = $svgWrappedNativeType;
AddToHeaderIncludes("core/svg/properties/SVGAnimatedPropertyTearOff.h");
} elsif ($svgNativeType =~ /SVGListPropertyTearOff/ or $svgNativeType =~ /SVGStaticListPropertyTearOff/ or $svgNativeType =~ /SVGTransformListPropertyTearOff/) {
$svgListPropertyType = $svgWrappedNativeType;
AddToHeaderIncludes("core/svg/properties/SVGAnimatedListPropertyTearOff.h");
} elsif ($svgNativeType =~ /SVGPathSegListPropertyTearOff/) {
$svgListPropertyType = $svgWrappedNativeType;
AddToHeaderIncludes("core/svg/properties/SVGPathSegListPropertyTearOff.h");
}
return ($svgPropertyType, $svgListPropertyType, $svgNativeType);
}
sub GetIndexedGetterFunction
{
my $interface = shift;
# FIXME: Expose indexed getter of CSSMixFunctionValue by removing this special case
# because CSSValueList(which is parent of CSSMixFunctionValue) has indexed property getter.
if ($interface->name eq "CSSMixFunctionValue") {
return 0;
}
return GetSpecialAccessorFunctionForType($interface, "getter", "unsigned long", 1);
}
sub GetIndexedSetterFunction
{
my $interface = shift;
return GetSpecialAccessorFunctionForType($interface, "setter", "unsigned long", 2);
}
sub GetIndexedDeleterFunction
{
my $interface = shift;
return GetSpecialAccessorFunctionForType($interface, "deleter", "unsigned long", 1);
}
sub GetNamedGetterFunction
{
my $interface = shift;
return GetSpecialAccessorFunctionForType($interface, "getter", "DOMString", 1);
}
sub GetNamedSetterFunction
{
my $interface = shift;
return GetSpecialAccessorFunctionForType($interface, "setter", "DOMString", 2);
}
sub GetNamedDeleterFunction
{
my $interface = shift;
return GetSpecialAccessorFunctionForType($interface, "deleter", "DOMString", 1);
}
sub GetSpecialAccessorFunctionForType
{
my $interface = shift;
my $special = shift;
my $firstParameterType = shift;
my $numberOfParameters = shift;
foreach my $function (@{$interface->functions}) {
my $specials = $function->specials;
my $specialExists = grep { $_ eq $special } @$specials;
my $parameters = $function->parameters;
if ($specialExists and scalar(@$parameters) == $numberOfParameters and $parameters->[0]->type eq $firstParameterType) {
return $function;
}
}
return 0;
}
sub GetV8StringResourceMode
{
my $extendedAttributes = shift;
# Blink uses the non-standard identifier NullString instead of Web IDL
# standard EmptyString, in [TreatNullAs=NullString] and [TreatUndefinedAs=NullString],
# and does not support [TreatUndefinedAs=Null] or [TreatUndefinedAs=Missing]
# https://sites.google.com/a/chromium.org/dev/blink/webidl/blink-idl-extended-attributes#TOC-TreatNullAs-a-p-TreatUndefinedAs-a-p-
my $mode = "";
if (($extendedAttributes->{"TreatNullAs"} and $extendedAttributes->{"TreatNullAs"} eq "NullString") and ($extendedAttributes->{"TreatUndefinedAs"} and $extendedAttributes->{"TreatUndefinedAs"} eq "NullString")) {
$mode = "WithUndefinedOrNullCheck";
} elsif ($extendedAttributes->{"TreatNullAs"} and $extendedAttributes->{"TreatNullAs"} eq "NullString") {
$mode = "WithNullCheck";
}
return $mode;
}
sub GenerateHeader
{
my $object = shift;
my $interface = shift;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
LinkOverloadedFunctions($interface);
# Ensure the IsDOMNodeType function is in sync.
die("IsDOMNodeType is out of date with respect to $interfaceName") if IsDOMNodeType($interfaceName) != InheritsInterface($interface, "Node");
my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($interfaceName);
my $parentInterface = $interface->parent;
AddToHeaderIncludes("V8${parentInterface}.h") if $parentInterface;
AddToHeaderIncludes("bindings/v8/WrapperTypeInfo.h");
AddToHeaderIncludes("bindings/v8/V8Binding.h");
AddToHeaderIncludes("bindings/v8/V8DOMWrapper.h");
AddToHeaderIncludes(HeaderFilesForInterface($interfaceName, $implClassName));
foreach my $headerInclude (sort keys(%headerIncludes)) {
$header{includes}->add("#include \"${headerInclude}\"\n");
}
$header{nameSpaceWebCore}->addHeader("\ntemplate<typename PropertyType> class SVGPropertyTearOff;\n") if $svgPropertyType;
if ($svgNativeType) {
if ($svgNativeType =~ /SVGStaticListPropertyTearOff/) {
$header{nameSpaceWebCore}->addHeader("\ntemplate<typename PropertyType> class SVGStaticListPropertyTearOff;\n");
} else {
$header{nameSpaceWebCore}->addHeader("\ntemplate<typename PropertyType> class SVGListPropertyTearOff;\n");
}
}
$header{nameSpaceWebCore}->addHeader("\nclass Dictionary;") if IsConstructorTemplate($interface, "Event");
my $nativeType = GetNativeTypeForConversions($interface);
if ($interface->extendedAttributes->{"NamedConstructor"}) {
$header{nameSpaceWebCore}->addHeader(<<END);
class V8${nativeType}Constructor {
public:
static v8::Handle<v8::FunctionTemplate> GetTemplate(v8::Isolate*, WrapperWorldType);
static const WrapperTypeInfo wrapperTypeInfo;
};
END
}
$header{class}->addHeader("class $v8ClassName {");
$header{class}->addFooter("};");
$header{classPublic}->add(<<END);
static bool HasInstance(v8::Handle<v8::Value>, v8::Isolate*, WrapperWorldType);
static bool HasInstanceInAnyWorld(v8::Handle<v8::Value>, v8::Isolate*);
static v8::Handle<v8::FunctionTemplate> GetTemplate(v8::Isolate*, WrapperWorldType);
static ${nativeType}* toNative(v8::Handle<v8::Object> object)
{
return fromInternalPointer(object->GetAlignedPointerFromInternalField(v8DOMWrapperObjectIndex));
}
static void derefObject(void*);
static const WrapperTypeInfo wrapperTypeInfo;
END
if (NeedsOpaqueRootForGC($interface)) {
$header{classPublic}->add(" static void* opaqueRootForGC(void*, v8::Isolate*);\n");
}
if (InheritsExtendedAttribute($interface, "ActiveDOMObject")) {
$header{classPublic}->add(" static ActiveDOMObject* toActiveDOMObject(v8::Handle<v8::Object>);\n");
}
if (InheritsInterface($interface, "EventTarget")) {
$header{classPublic}->add(" static EventTarget* toEventTarget(v8::Handle<v8::Object>);\n");
}
if ($interfaceName eq "Window") {
$header{classPublic}->add(<<END);
static v8::Handle<v8::ObjectTemplate> GetShadowObjectTemplate(v8::Isolate*, WrapperWorldType);
END
}
my @perContextEnabledFunctions;
foreach my $function (@{$interface->functions}) {
my $name = $function->name;
next if $name eq "";
my $attrExt = $function->extendedAttributes;
if (HasCustomMethod($attrExt) && $function->{overloadIndex} == 1) {
my $conditionalString = GenerateConditionalString($function);
$header{classPublic}->add("#if ${conditionalString}\n") if $conditionalString;
$header{classPublic}->add(<<END);
static void ${name}MethodCustom(const v8::FunctionCallbackInfo<v8::Value>&);
END
$header{classPublic}->add("#endif // ${conditionalString}\n") if $conditionalString;
}
if ($attrExt->{"PerContextEnabled"}) {
push(@perContextEnabledFunctions, $function);
}
}
if (IsConstructable($interface)) {
$header{classPublic}->add(" static void constructorCallback(const v8::FunctionCallbackInfo<v8::Value>&);\n");
END
}
if (HasCustomConstructor($interface)) {
$header{classPublic}->add(" static void constructorCustom(const v8::FunctionCallbackInfo<v8::Value>&);\n");
}
my @perContextEnabledAttributes;
foreach my $attribute (@{$interface->attributes}) {
my $name = $attribute->name;
my $attrExt = $attribute->extendedAttributes;
my $conditionalString = GenerateConditionalString($attribute);
if (HasCustomGetter($attrExt) && !$attrExt->{"ImplementedBy"}) {
$header{classPublic}->add("#if ${conditionalString}\n") if $conditionalString;
$header{classPublic}->add(<<END);
static void ${name}AttributeGetterCustom(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>&);
END
$header{classPublic}->add("#endif // ${conditionalString}\n") if $conditionalString;
}
if (HasCustomSetter($attribute) && !$attrExt->{"ImplementedBy"}) {
$header{classPublic}->add("#if ${conditionalString}\n") if $conditionalString;
$header{classPublic}->add(<<END);
static void ${name}AttributeSetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<void>&);
END
$header{classPublic}->add("#endif // ${conditionalString}\n") if $conditionalString;
}
if ($attrExt->{"PerContextEnabled"}) {
push(@perContextEnabledAttributes, $attribute);
}
}
GenerateHeaderNamedAndIndexedPropertyAccessors($interface);
GenerateHeaderLegacyCall($interface);
GenerateHeaderCustomInternalFieldIndices($interface);
my $toWrappedType;
my $fromWrappedType;
if ($interface->parent) {
my $v8ParentClassName = "V8" . $interface->parent;
$toWrappedType = "${v8ParentClassName}::toInternalPointer(impl)";
$fromWrappedType = "static_cast<${nativeType}*>(${v8ParentClassName}::fromInternalPointer(object))";
} else {
$toWrappedType = "impl";
$fromWrappedType = "static_cast<${nativeType}*>(object)";
}
$header{classPublic}->add(<<END);
static inline void* toInternalPointer(${nativeType}* impl)
{
return $toWrappedType;
}
static inline ${nativeType}* fromInternalPointer(void* object)
{
return $fromWrappedType;
}
END
if ($interface->name eq "Window") {
$header{classPublic}->add(<<END);
static bool namedSecurityCheckCustom(v8::Local<v8::Object> host, v8::Local<v8::Value> key, v8::AccessType, v8::Local<v8::Value> data);
static bool indexedSecurityCheckCustom(v8::Local<v8::Object> host, uint32_t index, v8::AccessType, v8::Local<v8::Value> data);
END
}
if (@perContextEnabledAttributes) {
$header{classPublic}->add(<<END);
static void installPerContextEnabledProperties(v8::Handle<v8::Object>, ${nativeType}*, v8::Isolate*);
END
} else {
$header{classPublic}->add(<<END);
static void installPerContextEnabledProperties(v8::Handle<v8::Object>, ${nativeType}*, v8::Isolate*) { }
END
}
if (@perContextEnabledFunctions) {
$header{classPublic}->add(<<END);
static void installPerContextEnabledPrototypeProperties(v8::Handle<v8::Object>, v8::Isolate*);
END
} else {
$header{classPublic}->add(<<END);
static void installPerContextEnabledPrototypeProperties(v8::Handle<v8::Object>, v8::Isolate*) { }
END
}
if ($interfaceName eq "HTMLElement") {
$header{classPublic}->add(<<END);
friend v8::Handle<v8::Object> createV8HTMLWrapper(HTMLElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
friend v8::Handle<v8::Object> createV8HTMLDirectWrapper(HTMLElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
} elsif ($interfaceName eq "SVGElement") {
$header{classPublic}->add(<<END);
friend v8::Handle<v8::Object> createV8SVGWrapper(SVGElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
friend v8::Handle<v8::Object> createV8SVGDirectWrapper(SVGElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
friend v8::Handle<v8::Object> createV8SVGFallbackWrapper(SVGElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
} elsif ($interfaceName eq "HTMLUnknownElement") {
$header{classPublic}->add(<<END);
friend v8::Handle<v8::Object> createV8HTMLFallbackWrapper(HTMLUnknownElement*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
} elsif ($interfaceName eq "Element") {
$header{classPublic}->add(<<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
}
$header{classPublic}->add("\n"); # blank line to separate classPrivate
my $noToV8 = $interface->extendedAttributes->{"DoNotGenerateToV8"};
my $noWrap = $interface->extendedAttributes->{"DoNotGenerateWrap"} || $noToV8;
if (!$noWrap) {
my $createWrapperArgumentType = GetPassRefPtrType($nativeType);
$header{classPrivate}->add(<<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
}
$header{nameSpaceWebCore}->add(<<END);
template<>
class WrapperTypeTraits<${nativeType} > {
public:
static const WrapperTypeInfo* wrapperTypeInfo() { return &${v8ClassName}::wrapperTypeInfo; }
};
END
my $customWrap = NeedsSpecialWrap($interface);
if ($noToV8) {
die "Can't suppress toV8 for subclass\n" if $interface->parent;
} elsif ($noWrap) {
die "Must have custom toV8\n" if !$customWrap;
$header{nameSpaceWebCore}->add(<<END);
class ${nativeType};
v8::Handle<v8::Value> toV8(${nativeType}*, v8::Handle<v8::Object> creationContext, v8::Isolate*);
template<class CallbackInfo>
inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ${nativeType}* impl)
{
v8SetReturnValue(callbackInfo, toV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
}
template<class CallbackInfo>
inline void v8SetReturnValueForMainWorld(const CallbackInfo& callbackInfo, ${nativeType}* impl)
{
v8SetReturnValue(callbackInfo, toV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
}
template<class CallbackInfo, class Wrappable>
inline void v8SetReturnValueFast(const CallbackInfo& callbackInfo, ${nativeType}* impl, Wrappable*)
{
v8SetReturnValue(callbackInfo, toV8(impl, callbackInfo.Holder(), callbackInfo.GetIsolate()));
}
END
} else {
my $createWrapperCall = $customWrap ? "${v8ClassName}::wrap" : "${v8ClassName}::createWrapper";
if ($customWrap) {
$header{nameSpaceWebCore}->add(<<END);
v8::Handle<v8::Object> wrap(${nativeType}* impl, v8::Handle<v8::Object> creationContext, v8::Isolate*);
END
} else {
$header{nameSpaceWebCore}->add(<<END);
inline v8::Handle<v8::Object> wrap(${nativeType}* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
{
ASSERT(impl);
ASSERT(!DOMDataStore::containsWrapper<${v8ClassName}>(impl, isolate));
return $createWrapperCall(impl, creationContext, isolate);
}
END
}
$header{nameSpaceWebCore}->add(<<END);
inline v8::Handle<v8::Value> toV8(${nativeType}* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
{
if (UNLIKELY(!impl))
return v8NullWithCheck(isolate);
v8::Handle<v8::Value> wrapper = DOMDataStore::getWrapper<${v8ClassName}>(impl, isolate);
if (!wrapper.IsEmpty())
return wrapper;
return wrap(impl, creationContext, isolate);
}
template<typename CallbackInfo>
inline void v8SetReturnValue(const CallbackInfo& callbackInfo, ${nativeType}* impl)
{
if (UNLIKELY(!impl)) {
v8SetReturnValueNull(callbackInfo);
return;
}
if (DOMDataStore::setReturnValueFromWrapper<${v8ClassName}>(callbackInfo.GetReturnValue(), impl))
return;
v8::Handle<v8::Object> wrapper = wrap(impl, callbackInfo.Holder(), callbackInfo.GetIsolate());
v8SetReturnValue(callbackInfo, wrapper);
}
template<typename CallbackInfo>
inline void v8SetReturnValueForMainWorld(const CallbackInfo& callbackInfo, ${nativeType}* impl)
{
ASSERT(worldType(callbackInfo.GetIsolate()) == MainWorld);
if (UNLIKELY(!impl)) {
v8SetReturnValueNull(callbackInfo);
return;
}
if (DOMDataStore::setReturnValueFromWrapperForMainWorld<${v8ClassName}>(callbackInfo.GetReturnValue(), impl))
return;
v8::Handle<v8::Value> wrapper = wrap(impl, callbackInfo.Holder(), callbackInfo.GetIsolate());
v8SetReturnValue(callbackInfo, wrapper);
}
template<class CallbackInfo, class Wrappable>
inline void v8SetReturnValueFast(const CallbackInfo& callbackInfo, ${nativeType}* impl, Wrappable* wrappable)
{
if (UNLIKELY(!impl)) {
v8SetReturnValueNull(callbackInfo);
return;
}
if (DOMDataStore::setReturnValueFromWrapperFast<${v8ClassName}>(callbackInfo.GetReturnValue(), impl, callbackInfo.Holder(), wrappable))
return;
v8::Handle<v8::Object> wrapper = wrap(impl, callbackInfo.Holder(), callbackInfo.GetIsolate());
v8SetReturnValue(callbackInfo, wrapper);
}
END
}
$header{nameSpaceWebCore}->add(<<END);
inline v8::Handle<v8::Value> toV8(PassRefPtr<${nativeType} > impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
{
return toV8(impl.get(), creationContext, isolate);
}
template<class CallbackInfo>
inline void v8SetReturnValue(const CallbackInfo& callbackInfo, PassRefPtr<${nativeType} > impl)
{
v8SetReturnValue(callbackInfo, impl.get());
}
template<class CallbackInfo>
inline void v8SetReturnValueForMainWorld(const CallbackInfo& callbackInfo, PassRefPtr<${nativeType} > impl)
{
v8SetReturnValueForMainWorld(callbackInfo, impl.get());
}
template<class CallbackInfo, class Wrappable>
inline void v8SetReturnValueFast(const CallbackInfo& callbackInfo, PassRefPtr<${nativeType} > impl, Wrappable* wrappable)
{
v8SetReturnValueFast(callbackInfo, impl.get(), wrappable);
}
END
if (IsConstructorTemplate($interface, "Event")) {
$header{nameSpaceWebCore}->add("bool fill${implClassName}Init(${implClassName}Init&, const Dictionary&);\n\n");
}
}
sub GetInternalFields
{
my $interface = shift;
my @customInternalFields = ();
# Event listeners on DOM nodes are explicitly supported in the GC controller.
if (!InheritsInterface($interface, "Node") &&
InheritsInterface($interface, "EventTarget")) {
push(@customInternalFields, "eventListenerCacheIndex");
}
return @customInternalFields;
}
sub GenerateHeaderCustomInternalFieldIndices
{
my $interface = shift;
my @customInternalFields = GetInternalFields($interface);
my $customFieldCounter = 0;
foreach my $customInternalField (@customInternalFields) {
$header{classPublic}->add(<<END);
static const int ${customInternalField} = v8DefaultWrapperInternalFieldCount + ${customFieldCounter};
END
$customFieldCounter++;
}
$header{classPublic}->add(<<END);
static const int internalFieldCount = v8DefaultWrapperInternalFieldCount + ${customFieldCounter};
END
}
sub GenerateHeaderNamedAndIndexedPropertyAccessors
{
my $interface = shift;
my $indexedGetterFunction = GetIndexedGetterFunction($interface);
my $hasCustomIndexedGetter = $indexedGetterFunction && $indexedGetterFunction->extendedAttributes->{"Custom"};
my $indexedSetterFunction = GetIndexedSetterFunction($interface);
my $hasCustomIndexedSetter = $indexedSetterFunction && $indexedSetterFunction->extendedAttributes->{"Custom"};
my $indexedDeleterFunction = GetIndexedDeleterFunction($interface);
my $hasCustomIndexedDeleters = $indexedDeleterFunction && $indexedDeleterFunction->extendedAttributes->{"Custom"};
my $namedGetterFunction = GetNamedGetterFunction($interface);
my $hasCustomNamedGetter = $namedGetterFunction && $namedGetterFunction->extendedAttributes->{"Custom"};
my $namedSetterFunction = GetNamedSetterFunction($interface);
my $hasCustomNamedSetter = $namedSetterFunction && $namedSetterFunction->extendedAttributes->{"Custom"};
my $namedDeleterFunction = GetNamedDeleterFunction($interface);
my $hasCustomNamedDeleter = $namedDeleterFunction && $namedDeleterFunction->extendedAttributes->{"Custom"};
my $namedEnumeratorFunction = $namedGetterFunction && !$namedGetterFunction->extendedAttributes->{"NotEnumerable"};
my $hasCustomNamedEnumerator = $namedGetterFunction && $namedGetterFunction->extendedAttributes->{"CustomEnumerateProperty"};
if ($hasCustomIndexedGetter) {
$header{classPublic}->add(" static void indexedPropertyGetterCustom(uint32_t, const v8::PropertyCallbackInfo<v8::Value>&);\n");
}
if ($hasCustomIndexedSetter) {
$header{classPublic}->add(" static void indexedPropertySetterCustom(uint32_t, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);\n");
}
if ($hasCustomIndexedDeleters) {
$header{classPublic}->add(" static void indexedPropertyDeleterCustom(uint32_t, const v8::PropertyCallbackInfo<v8::Boolean>&);\n");
}
if ($hasCustomNamedGetter) {
$header{classPublic}->add(" static void namedPropertyGetterCustom(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>&);\n");
}
if ($hasCustomNamedSetter) {
$header{classPublic}->add(" static void namedPropertySetterCustom(v8::Local<v8::String>, v8::Local<v8::Value>, const v8::PropertyCallbackInfo<v8::Value>&);\n");
}
if ($hasCustomNamedDeleter) {
$header{classPublic}->add(" static void namedPropertyDeleterCustom(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Boolean>&);\n");
}
if ($hasCustomNamedEnumerator) {
$header{classPublic}->add(" static void namedPropertyEnumeratorCustom(const v8::PropertyCallbackInfo<v8::Array>&);\n");
$header{classPublic}->add(" static void namedPropertyQueryCustom(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Integer>&);\n");
}
}
sub GenerateHeaderLegacyCall
{
my $interface = shift;
if ($interface->extendedAttributes->{"CustomLegacyCall"}) {
$header{classPublic}->add(" static void legacyCallCustom(const v8::FunctionCallbackInfo<v8::Value>&);\n");
}
}
sub HasActivityLogging
{
my $forMainWorldSuffix = shift;
my $attrExt = shift;
my $access = shift;
if (!$attrExt->{"ActivityLogging"}) {
return 0;
}
my $logAllAccess = ($attrExt->{"ActivityLogging"} =~ /^Access/);
my $logGetter = ($attrExt->{"ActivityLogging"} =~ /^Getter/);
my $logSetter = ($attrExt->{"ActivityLogging"} =~ /^Setter/);
my $logOnlyIsolatedWorlds = ($attrExt->{"ActivityLogging"} =~ /ForIsolatedWorlds$/);
if ($logOnlyIsolatedWorlds && $forMainWorldSuffix eq "ForMainWorld") {
return 0;
}
return $logAllAccess || ($logGetter && $access eq "Getter") || ($logSetter && $access eq "Setter");
}
sub IsConstructable
{
my $interface = shift;
return $interface->extendedAttributes->{"CustomConstructor"} || $interface->extendedAttributes->{"Constructor"} || $interface->extendedAttributes->{"ConstructorTemplate"};
}
sub HasCustomConstructor
{
my $interface = shift;
return $interface->extendedAttributes->{"CustomConstructor"};
}
sub HasCustomGetter
{
my $attrExt = shift;
return $attrExt->{"Custom"} || $attrExt->{"CustomGetter"};
}
sub HasCustomSetter
{
my $attribute = shift;
my $attrExt = $attribute->extendedAttributes;
return ($attrExt->{"Custom"} || $attrExt->{"CustomSetter"}) && !IsReadonly($attribute);
}
sub HasCustomMethod
{
my $attrExt = shift;
return $attrExt->{"Custom"};
}
sub IsReadonly
{
my $attribute = shift;
my $attrExt = $attribute->extendedAttributes;
return $attribute->isReadOnly && !$attrExt->{"Replaceable"} && !$attrExt->{"PutForwards"};
}
sub GetV8ClassName
{
my $interface = shift;
return "V8" . $interface->name;
}
sub GetImplName
{
my $interfaceOrAttributeOrFunction = shift;
return $interfaceOrAttributeOrFunction->extendedAttributes->{"ImplementedAs"} || $interfaceOrAttributeOrFunction->name;
}
sub GetImplNameFromImplementedBy
{
my $implementedBy = shift;
my $interface = ParseInterface($implementedBy);
return $interface->extendedAttributes->{"ImplementedAs"} || $implementedBy;
}
sub GenerateDomainSafeFunctionGetter
{
my $function = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $funcName = $function->name;
my $functionLength = GetFunctionLength($function);
my $signature = "v8::Signature::New(V8PerIsolateData::from(info.GetIsolate())->rawTemplate(&" . $v8ClassName . "::wrapperTypeInfo, currentWorldType))";
if ($function->extendedAttributes->{"DoNotCheckSignature"}) {
$signature = "v8::Local<v8::Signature>()";
}
my $newTemplateParams = "${implClassName}V8Internal::${funcName}MethodCallback${forMainWorldSuffix}, v8Undefined(), $signature";
AddToImplIncludes("bindings/v8/BindingSecurity.h");
$implementation{nameSpaceInternal}->add(<<END);
static void ${funcName}AttributeGetter${forMainWorldSuffix}(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
{
// This is only for getting a unique pointer which we can pass to privateTemplate.
static int privateTemplateUniqueKey;
WrapperWorldType currentWorldType = worldType(info.GetIsolate());
V8PerIsolateData* data = V8PerIsolateData::from(info.GetIsolate());
v8::Handle<v8::FunctionTemplate> privateTemplate = data->privateTemplate(currentWorldType, &privateTemplateUniqueKey, $newTemplateParams, $functionLength);
v8::Handle<v8::Object> holder = info.This()->FindInstanceInPrototypeChain(${v8ClassName}::GetTemplate(info.GetIsolate(), currentWorldType));
if (holder.IsEmpty()) {
// can only reach here by 'object.__proto__.func', and it should passed
// domain security check already
v8SetReturnValue(info, privateTemplate->GetFunction());
return;
}
${implClassName}* imp = ${v8ClassName}::toNative(holder);
if (!BindingSecurity::shouldAllowAccessToFrame(imp->frame(), DoNotReportSecurityError)) {
static int sharedTemplateUniqueKey;
v8::Handle<v8::FunctionTemplate> sharedTemplate = data->privateTemplate(currentWorldType, &sharedTemplateUniqueKey, $newTemplateParams, $functionLength);
v8SetReturnValue(info, sharedTemplate->GetFunction());
return;
}
v8::Local<v8::Value> hiddenValue = info.This()->GetHiddenValue(name);
if (!hiddenValue.IsEmpty()) {
v8SetReturnValue(info, hiddenValue);
return;
}
v8SetReturnValue(info, privateTemplate->GetFunction());
}
END
$implementation{nameSpaceInternal}->add(<<END);
static void ${funcName}AttributeGetterCallback${forMainWorldSuffix}(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
{
TRACE_EVENT_SET_SAMPLING_STATE("Blink", "DOMGetter");
${implClassName}V8Internal::${funcName}AttributeGetter${forMainWorldSuffix}(name, info);
TRACE_EVENT_SET_SAMPLING_STATE("V8", "Execution");
}
END
}
sub GenerateDomainSafeFunctionSetter
{
my $interface = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
AddToImplIncludes("bindings/v8/BindingSecurity.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
$implementation{nameSpaceInternal}->add(<<END);
static void ${implClassName}DomainSafeFunctionSetter(v8::Local<v8::String> name, v8::Local<v8::Value> jsValue, const v8::PropertyCallbackInfo<void>& info)
{
v8::Handle<v8::Object> holder = info.This()->FindInstanceInPrototypeChain(${v8ClassName}::GetTemplate(info.GetIsolate(), worldType(info.GetIsolate())));
if (holder.IsEmpty())
return;
${implClassName}* imp = ${v8ClassName}::toNative(holder);
ExceptionState es(info.GetIsolate());
if (!BindingSecurity::shouldAllowAccessToFrame(imp->frame(), es)) {
es.throwIfNeeded();
return;
}
info.This()->SetHiddenValue(name, jsValue);
}
END
}
sub GenerateConstructorGetter
{
my $interface = shift;
my $implClassName = GetImplName($interface);
$implementation{nameSpaceInternal}->add(<<END);
static void ${implClassName}ConstructorGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
{
v8::Handle<v8::Value> data = info.Data();
ASSERT(data->IsExternal());
V8PerContextData* perContextData = V8PerContextData::from(info.Holder()->CreationContext());
if (!perContextData)
return;
v8SetReturnValue(info, perContextData->constructorForType(WrapperTypeInfo::unwrap(data)));
}
END
}
sub GenerateFeatureObservation
{
my $measureAs = shift;
if ($measureAs) {
AddToImplIncludes("core/page/UseCounter.h");
return " UseCounter::count(activeDOMWindow(), UseCounter::${measureAs});\n";
}
return "";
}
sub GenerateDeprecationNotification
{
my $deprecateAs = shift;
if ($deprecateAs) {
AddToImplIncludes("core/page/UseCounter.h");
return " UseCounter::countDeprecation(activeExecutionContext(), UseCounter::${deprecateAs});\n";
}
return "";
}
sub GenerateActivityLogging
{
my $accessType = shift;
my $interface = shift;
my $propertyName = shift;
my $interfaceName = $interface->name;
AddToImplIncludes("bindings/v8/V8DOMActivityLogger.h");
my $code = "";
if ($accessType eq "Method") {
$code .= <<END;
V8PerContextData* contextData = V8PerContextData::from(args.GetIsolate()->GetCurrentContext());
if (contextData && contextData->activityLogger()) {
Vector<v8::Handle<v8::Value> > loggerArgs = toVectorOfArguments(args);
contextData->activityLogger()->log("${interfaceName}.${propertyName}", args.Length(), loggerArgs.data(), "${accessType}");
}
END
} elsif ($accessType eq "Setter") {
$code .= <<END;
V8PerContextData* contextData = V8PerContextData::from(info.GetIsolate()->GetCurrentContext());
if (contextData && contextData->activityLogger()) {
v8::Handle<v8::Value> loggerArg[] = { jsValue };
contextData->activityLogger()->log("${interfaceName}.${propertyName}", 1, &loggerArg[0], "${accessType}");
}
END
} elsif ($accessType eq "Getter") {
$code .= <<END;
V8PerContextData* contextData = V8PerContextData::from(info.GetIsolate()->GetCurrentContext());
if (contextData && contextData->activityLogger())
contextData->activityLogger()->log("${interfaceName}.${propertyName}", 0, 0, "${accessType}");
END
} else {
die "Unrecognized activity logging access type";
}
return $code;
}
sub GenerateNormalAttributeGetterCallback
{
my $attribute = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $attrExt = $attribute->extendedAttributes;
my $attrName = $attribute->name;
my $conditionalString = GenerateConditionalString($attribute);
my $code = "";
$code .= "#if ${conditionalString}\n" if $conditionalString;
$code .= "static void ${attrName}AttributeGetterCallback${forMainWorldSuffix}(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)\n";
$code .= "{\n";
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"Blink\", \"DOMGetter\");\n";
$code .= GenerateFeatureObservation($attrExt->{"MeasureAs"});
$code .= GenerateDeprecationNotification($attrExt->{"DeprecateAs"});
if (HasActivityLogging($forMainWorldSuffix, $attrExt, "Getter")) {
$code .= GenerateActivityLogging("Getter", $interface, "${attrName}");
}
if (HasCustomGetter($attrExt)) {
$code .= " ${v8ClassName}::${attrName}AttributeGetterCustom(name, info);\n";
} else {
$code .= " ${implClassName}V8Internal::${attrName}AttributeGetter${forMainWorldSuffix}(name, info);\n";
}
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"V8\", \"Execution\");\n";
$code .= "}\n";
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= "\n";
$implementation{nameSpaceInternal}->add($code);
}
sub GetCachedAttribute
{
my $attribute = shift;
my $attrExt = $attribute->extendedAttributes;
if (($attribute->type eq "any" || $attribute->type eq "SerializedScriptValue") && $attrExt->{"CachedAttribute"}) {
return $attrExt->{"CachedAttribute"};
}
return "";
}
sub GenerateNormalAttributeGetter
{
my $attribute = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $attrExt = $attribute->extendedAttributes;
my $attrName = $attribute->name;
my $attrType = $attribute->type;
my $attrCached = GetCachedAttribute($attribute);
if (HasCustomGetter($attrExt)) {
return;
}
AssertNotSequenceType($attrType);
my $nativeType = GetNativeType($attribute->type, $attribute->extendedAttributes, "");
my $svgNativeType = GetSVGTypeNeedingTearOff($interfaceName);
my $conditionalString = GenerateConditionalString($attribute);
my $code = "";
$code .= "#if ${conditionalString}\n" if $conditionalString;
$code .= <<END;
static void ${attrName}AttributeGetter${forMainWorldSuffix}(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
{
END
if ($svgNativeType) {
my $svgWrappedNativeType = GetSVGWrappedTypeNeedingTearOff($interfaceName);
if ($svgWrappedNativeType =~ /List/) {
$code .= <<END;
$svgNativeType* imp = ${v8ClassName}::toNative(info.Holder());
END
} else {
$code .= <<END;
$svgNativeType* wrapper = ${v8ClassName}::toNative(info.Holder());
$svgWrappedNativeType& impInstance = wrapper->propertyReference();
$svgWrappedNativeType* imp = &impInstance;
END
}
} elsif ($attrExt->{"OnProto"} || $attrExt->{"Unforgeable"}) {
if ($interfaceName eq "Window") {
$code .= <<END;
v8::Handle<v8::Object> holder = info.Holder();
END
} else {
# perform lookup first
$code .= <<END;
v8::Handle<v8::Object> holder = info.This()->FindInstanceInPrototypeChain(${v8ClassName}::GetTemplate(info.GetIsolate(), worldType(info.GetIsolate())));
if (holder.IsEmpty())
return;
END
}
$code .= <<END;
${implClassName}* imp = ${v8ClassName}::toNative(holder);
END
} else {
my $reflect = $attribute->extendedAttributes->{"Reflect"};
my $url = $attribute->extendedAttributes->{"URL"};
if ($reflect && !$url && InheritsInterface($interface, "Node") && $attrType eq "DOMString") {
# Generate super-compact call for regular attribute getter:
my ($functionName, @arguments) = GetterExpression($interfaceName, $attribute);
$code .= " Element* imp = V8Element::toNative(info.Holder());\n";
$code .= " v8SetReturnValueString(info, imp->${functionName}(" . join(", ", @arguments) . "), info.GetIsolate());\n";
$code .= "}\n";
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= "\n";
$implementation{nameSpaceInternal}->add($code);
return;
# Skip the rest of the function!
}
my $imp = 0;
if ($attrCached) {
$imp = 1;
$code .= <<END;
v8::Handle<v8::String> propertyName = v8::String::NewSymbol("${attrName}");
${implClassName}* imp = ${v8ClassName}::toNative(info.Holder());
if (!imp->$attrCached()) {
v8::Handle<v8::Value> jsValue = info.Holder()->GetHiddenValue(propertyName);
if (!jsValue.IsEmpty()) {
v8SetReturnValue(info, jsValue);
return;
}
}
END
}
if (!$attribute->isStatic && !$imp) {
$code .= <<END;
${implClassName}* imp = ${v8ClassName}::toNative(info.Holder());
END
}
}
my $useExceptions = 1 if $attribute->extendedAttributes->{"GetterRaisesException"} || $attribute->extendedAttributes->{"RaisesException"};
if ($useExceptions || $attribute->extendedAttributes->{"CheckSecurityForNode"}) {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
$code .= " ExceptionState es(info.GetIsolate());\n";
}
# Generate security checks if necessary
if ($attribute->extendedAttributes->{"CheckSecurityForNode"}) {
AddToImplIncludes("bindings/v8/BindingSecurity.h");
$code .= " if (!BindingSecurity::shouldAllowAccessToNode(imp->" . GetImplName($attribute) . "(), es)) {\n";
$code .= " v8SetReturnValueNull(info);\n";
$code .= " es.throwIfNeeded();\n";
$code .= " return;\n";
$code .= " }\n";
}
my $isNullable = $attribute->isNullable;
if ($isNullable) {
$code .= " bool isNull = false;\n";
}
my $returnType = $attribute->type;
AddIncludesForType($returnType);
my $getterString;
my ($functionName, @arguments) = GetterExpression($interfaceName, $attribute);
push(@arguments, "isNull") if $isNullable;
push(@arguments, "es") if $useExceptions;
if ($attribute->extendedAttributes->{"ImplementedBy"}) {
my $implementedBy = $attribute->extendedAttributes->{"ImplementedBy"};
my $implementedByImplName = GetImplNameFromImplementedBy($implementedBy);
AddToImplIncludes(HeaderFilesForInterface($implementedBy, $implementedByImplName));
unshift(@arguments, "imp") if !$attribute->isStatic;
$functionName = "${implementedByImplName}::${functionName}";
} elsif ($attribute->isStatic) {
$functionName = "${implClassName}::${functionName}";
} else {
$functionName = "imp->${functionName}";
}
my ($arg, $subCode) = GenerateCallWith($attribute->extendedAttributes->{"CallWith"}, " ", 0);
$code .= $subCode;
unshift(@arguments, @$arg);
$getterString = "${functionName}(" . join(", ", @arguments) . ")";
my $expression;
if ($attribute->type eq "EventHandler" && $interface->name eq "Window") {
$code .= " if (!imp->document())\n";
$code .= " return;\n";
}
if ($useExceptions || $isNullable) {
if ($nativeType =~ /^V8StringResource/) {
$code .= " " . ConvertToV8StringResource($attribute, $nativeType, "cppValue", $getterString) . ";\n";
} else {
$code .= " $nativeType jsValue = $getterString;\n";
}
if ($isNullable) {
$code .= " if (isNull) {\n";
$code .= " v8SetReturnValueNull(info);\n";
$code .= " return;\n";
$code .= " }\n";
}
if ($useExceptions) {
if ($useExceptions) {
$code .= " if (UNLIKELY(es.throwIfNeeded()))\n";
$code .= " return;\n";
}
if (ExtendedAttributeContains($attribute->extendedAttributes->{"CallWith"}, "ScriptState")) {
$code .= " if (state.hadException()) {\n";
$code .= " throwError(state.exception(), info.GetIsolate());\n";
$code .= " return;\n";
$code .= " }\n";
}
}
$expression = "jsValue";
$expression .= ".release()" if (IsRefPtrType($returnType));
} else {
# Can inline the function call into the return statement to avoid overhead of using a Ref<> temporary
$expression = $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).
$expression = "static_pointer_cast<SVGAnimatedEnumeration>($expression)" if $returnType eq "SVGAnimatedEnumeration";
}
if (ShouldKeepAttributeAlive($interface, $attribute, $returnType)) {
my $arrayType = GetArrayType($returnType);
if ($arrayType) {
$code .= " v8SetReturnValue(info, v8Array(${getterString}, info.GetIsolate()));\n";
$code .= "}\n\n";
$implementation{nameSpaceInternal}->add($code);
return;
}
AddToImplIncludes("bindings/v8/V8HiddenPropertyName.h");
# 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.
my $nativeReturnType = GetNativeType($returnType);
my $v8ReturnType = "V8" . $returnType;
$code .= " $nativeReturnType result = ${getterString};\n";
if ($forMainWorldSuffix) {
$code .= " if (result && DOMDataStore::setReturnValueFromWrapper${forMainWorldSuffix}<${v8ReturnType}>(info.GetReturnValue(), result.get()))\n";
} else {
$code .= " if (result && DOMDataStore::setReturnValueFromWrapper<${v8ReturnType}>(info.GetReturnValue(), result.get()))\n";
}
$code .= " return;\n";
$code .= " v8::Handle<v8::Value> wrapper = toV8(result.get(), info.Holder(), info.GetIsolate());\n";
$code .= " if (!wrapper.IsEmpty()) {\n";
$code .= " V8HiddenPropertyName::setNamedHiddenReference(info.Holder(), \"${attrName}\", wrapper);\n";
$code .= " v8SetReturnValue(info, wrapper);\n";
$code .= " }\n";
$code .= "}\n";
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= "\n";
$implementation{nameSpaceInternal}->add($code);
return;
}
if ((IsSVGAnimatedType($interfaceName) or $interfaceName eq "SVGViewSpec") and IsSVGTypeNeedingTearOff($attrType)) {
AddToImplIncludes("V8$attrType.h");
my $svgNativeType = GetSVGTypeNeedingTearOff($attrType);
# Convert from abstract SVGProperty to real type, so the right toJS() method can be invoked.
if ($forMainWorldSuffix eq "ForMainWorld") {
$code .= " v8SetReturnValueForMainWorld(info, static_cast<$svgNativeType*>($expression));\n";
} else {
$code .= " v8SetReturnValueFast(info, static_cast<$svgNativeType*>($expression), imp);\n";
}
} elsif (IsSVGTypeNeedingTearOff($attrType) and not $interfaceName =~ /List$/) {
AddToImplIncludes("V8$attrType.h");
AddToImplIncludes("core/svg/properties/SVGPropertyTearOff.h");
my $tearOffType = GetSVGTypeNeedingTearOff($attrType);
my $wrappedValue;
if (IsSVGTypeWithWritablePropertiesNeedingTearOff($attrType) and not defined $attribute->extendedAttributes->{"Immutable"}) {
my $getter = $expression;
$getter =~ s/imp->//;
$getter =~ s/\(\)//;
my $updateMethod = "&${implClassName}::update" . FirstLetterToUpperCase($getter);
my $selfIsTearOffType = IsSVGTypeNeedingTearOff($interfaceName);
if ($selfIsTearOffType) {
AddToImplIncludes("core/svg/properties/SVGMatrixTearOff.h");
# FIXME: Don't create a new one everytime we access the matrix property. This means, e.g, === won't work.
$wrappedValue = "WTF::getPtr(SVGMatrixTearOff::create(wrapper, $expression))";
} else {
AddToImplIncludes("core/svg/properties/SVGStaticPropertyTearOff.h");
$tearOffType =~ s/SVGPropertyTearOff</SVGStaticPropertyTearOff<$implClassName, /;
$wrappedValue = "WTF::getPtr(${tearOffType}::create(imp, $expression, $updateMethod))";
}
} elsif ($tearOffType =~ /SVGStaticListPropertyTearOff/) {
$wrappedValue = "WTF::getPtr(${tearOffType}::create(imp, $expression))";
} elsif ($tearOffType =~ /SVG(Point|PathSeg)List/) {
$wrappedValue = "WTF::getPtr($expression)";
} else {
$wrappedValue = "WTF::getPtr(${tearOffType}::create($expression))";
}
if ($forMainWorldSuffix eq "ForMainWorld") {
$code .= " v8SetReturnValueForMainWorld(info, $wrappedValue);\n";
} else {
$code .= " v8SetReturnValueFast(info, $wrappedValue, imp);\n";
}
} elsif ($attrCached) {
if ($attribute->type eq "SerializedScriptValue") {
$code .= <<END;
RefPtr<SerializedScriptValue> serialized = $getterString;
ScriptValue jsValue = serialized ? serialized->deserialize() : v8::Handle<v8::Value>(v8::Null(info.GetIsolate()));
info.Holder()->SetHiddenValue(propertyName, jsValue);
v8SetReturnValue(info, jsValue);
END
} else {
$code .= <<END;
ScriptValue jsValue = $getterString;
info.Holder()->SetHiddenValue(propertyName, jsValue.v8Value());
v8SetReturnValue(info, jsValue.v8Value());
END
}
} elsif ($attribute->type eq "EventHandler") {
AddToImplIncludes("bindings/v8/V8AbstractEventListener.h");
# FIXME: Pass the main world ID for main-world-only getters.
my ($functionName, @arguments) = GetterExpression($interfaceName, $attribute);
my $implementedBy = $attribute->extendedAttributes->{"ImplementedBy"};
if ($implementedBy) {
my $implementedByImplName = GetImplNameFromImplementedBy($implementedBy);
$functionName = "${implementedByImplName}::${functionName}";
push(@arguments, "imp");
} else {
$functionName = "imp->${functionName}";
}
push(@arguments, "isolatedWorldForIsolate(info.GetIsolate())");
$code .= " EventListener* jsValue = ${functionName}(" . join(", ", @arguments) . ");\n";
$code .= " v8SetReturnValue(info, jsValue ? v8::Handle<v8::Value>(V8AbstractEventListener::cast(jsValue)->getListenerObject(imp->executionContext())) : v8::Handle<v8::Value>(v8::Null(info.GetIsolate())));\n";
} else {
my $nativeValue = NativeToJSValue($attribute->type, $attribute->extendedAttributes, $expression, " ", "", "info.GetIsolate()", "info", "imp", $forMainWorldSuffix, "return");
$code .= "${nativeValue}\n";
}
$code .= "}\n"; # end of getter
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= "\n";
$implementation{nameSpaceInternal}->add($code);
}
sub ShouldKeepAttributeAlive
{
my ($interface, $attribute, $returnType) = @_;
my $attrName = $attribute->name;
return 1 if $attribute->extendedAttributes->{"KeepAttributeAliveForGC"};
# For readonly attributes (including Replaceable), as a general rule, for
# performance reasons we keep the attribute wrapper alive while the owner
# wrapper is alive, because the attribute never changes.
return 0 if !IsWrapperType($returnType);
return 0 if !IsReadonly($attribute) && !$attribute->extendedAttributes->{"Replaceable"};
# However, there are a couple of exceptions.
# Node lifetime is managed by object grouping.
return 0 if InheritsInterface($interface, "Node");
return 0 if IsDOMNodeType($returnType);
# To avoid adding a reference to itself.
# FIXME: Introduce [DoNotKeepAttributeAliveForGC] and remove this hack
# depending on the attribute name.
return 0 if $attrName eq "self";
# FIXME: Remove these hard-coded hacks.
return 0 if $returnType eq "EventTarget";
return 0 if $returnType eq "SerializedScriptValue";
return 0 if $returnType eq "Window";
return 0 if $returnType =~ /SVG/;
return 0 if $returnType =~ /HTML/;
return 1;
}
sub GenerateReplaceableAttributeSetterCallback
{
my $interface = shift;
my $implClassName = GetImplName($interface);
my $code = "";
$code .= "static void ${implClassName}ReplaceableAttributeSetterCallback(v8::Local<v8::String> name, v8::Local<v8::Value> jsValue, const v8::PropertyCallbackInfo<void>& info)\n";
$code .= "{\n";
$code .= GenerateFeatureObservation($interface->extendedAttributes->{"MeasureAs"});
$code .= GenerateDeprecationNotification($interface->extendedAttributes->{"DeprecateAs"});
$code .= GenerateCustomElementInvocationScopeIfNeeded($interface->extendedAttributes);
if (HasActivityLogging("", $interface->extendedAttributes, "Setter")) {
die "IDL error: ActivityLog attribute cannot exist on a ReplacableAttributeSetterCallback";
}
$code .= " ${implClassName}V8Internal::${implClassName}ReplaceableAttributeSetter(name, jsValue, info);\n";
$code .= "}\n\n";
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateReplaceableAttributeSetter
{
my $interface = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $code = "";
$code .= <<END;
static void ${implClassName}ReplaceableAttributeSetter(v8::Local<v8::String> name, v8::Local<v8::Value> jsValue, const v8::PropertyCallbackInfo<void>& info)
{
END
if ($interface->extendedAttributes->{"CheckSecurity"}) {
AddToImplIncludes("bindings/v8/BindingSecurity.h");
$code .= <<END;
${implClassName}* imp = ${v8ClassName}::toNative(info.Holder());
ExceptionState es(info.GetIsolate());
if (!BindingSecurity::shouldAllowAccessToFrame(imp->frame(), es)) {
es.throwIfNeeded();
return;
}
END
}
$code .= <<END;
info.This()->ForceSet(name, jsValue);
}
END
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateCustomElementInvocationScopeIfNeeded
{
my $code = "";
my $ext = shift;
if ($ext->{"CustomElementCallbacks"} or $ext->{"Reflect"}) {
AddToImplIncludes("core/dom/custom/CustomElementCallbackDispatcher.h");
$code .= <<END;
CustomElementCallbackDispatcher::CallbackDeliveryScope deliveryScope;
END
}
return $code;
}
sub GenerateNormalAttributeSetterCallback
{
my $attribute = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $attrExt = $attribute->extendedAttributes;
my $attrName = $attribute->name;
my $conditionalString = GenerateConditionalString($attribute);
my $code = "";
$code .= "#if ${conditionalString}\n" if $conditionalString;
$code .= "static void ${attrName}AttributeSetterCallback${forMainWorldSuffix}(v8::Local<v8::String> name, v8::Local<v8::Value> jsValue, const v8::PropertyCallbackInfo<void>& info)\n";
$code .= "{\n";
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"Blink\", \"DOMSetter\");\n";
$code .= GenerateFeatureObservation($attrExt->{"MeasureAs"});
$code .= GenerateDeprecationNotification($attrExt->{"DeprecateAs"});
if (HasActivityLogging($forMainWorldSuffix, $attrExt, "Setter")) {
$code .= GenerateActivityLogging("Setter", $interface, "${attrName}");
}
$code .= GenerateCustomElementInvocationScopeIfNeeded($attrExt);
if (HasCustomSetter($attribute)) {
$code .= " ${v8ClassName}::${attrName}AttributeSetterCustom(name, jsValue, info);\n";
} else {
$code .= " ${implClassName}V8Internal::${attrName}AttributeSetter${forMainWorldSuffix}(name, jsValue, info);\n";
}
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"V8\", \"Execution\");\n";
$code .= "}\n";
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= "\n";
$implementation{nameSpaceInternal}->add($code);
}
sub FindAttributeWithName
{
my $interface = shift;
my $attrName = shift;
foreach my $attribute (@{$interface->attributes}) {
if ($attribute->name eq $attrName) {
return $attribute;
}
}
}
sub GenerateNormalAttributeSetter
{
my $attribute = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $attrName = $attribute->name;
my $attrExt = $attribute->extendedAttributes;
my $attrType = $attribute->type;
my $attrCached = GetCachedAttribute($attribute);
if (HasCustomSetter($attribute)) {
return;
}
my $conditionalString = GenerateConditionalString($attribute);
my $code = "";
$code .= "#if ${conditionalString}\n" if $conditionalString;
$code .= "static void ${attrName}AttributeSetter${forMainWorldSuffix}(v8::Local<v8::String> name, v8::Local<v8::Value> jsValue, const v8::PropertyCallbackInfo<void>& info)\n";
$code .= "{\n";
# If the "StrictTypeChecking" extended attribute is present, and the attribute's type is an
# interface type, then if the incoming value does not implement that interface, a TypeError is
# thrown rather than silently passing NULL to the C++ code.
# Per the Web IDL and ECMAScript specifications, incoming values can always be converted to both
# strings and numbers, so do not throw TypeError if the attribute is of these types.
if ($attribute->extendedAttributes->{"StrictTypeChecking"}) {
my $argType = $attribute->type;
if (IsWrapperType($argType)) {
$code .= " if (!isUndefinedOrNull(jsValue) && !V8${argType}::HasInstance(jsValue, info.GetIsolate(), worldType(info.GetIsolate()))) {\n";
$code .= " throwTypeError(ExceptionMessages::failedToSet(\"${attrName}\", \"${interfaceName}\", \"The provided value is not of type '${argType}'.\"), info.GetIsolate());\n";
$code .= " return;\n";
$code .= " }\n";
}
}
my $svgNativeType = GetSVGTypeNeedingTearOff($interfaceName);
if ($svgNativeType) {
my $svgWrappedNativeType = GetSVGWrappedTypeNeedingTearOff($interfaceName);
if ($svgWrappedNativeType =~ /List$/) {
$code .= <<END;
$svgNativeType* imp = ${v8ClassName}::toNative(info.Holder());
END
} else {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
$code .= " $svgNativeType* wrapper = ${v8ClassName}::toNative(info.Holder());\n";
$code .= " if (wrapper->isReadOnly()) {\n";
$code .= " setDOMException(NoModificationAllowedError, ExceptionMessages::failedToSet(\"${attrName}\", \"${interfaceName}\", \"The attribute is read-only.\"), info.GetIsolate());\n";
$code .= " return;\n";
$code .= " }\n";
$code .= " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n";
$code .= " $svgWrappedNativeType* imp = &impInstance;\n";
}
} elsif ($attrExt->{"OnProto"}) {
$code .= <<END;
${implClassName}* imp = ${v8ClassName}::toNative(info.Holder());
END
} elsif($attrExt->{"PutForwards"}) {
my $destinationAttrName = $attrExt->{"PutForwards"};
my $destinationInterface = ParseInterface($attrType);
die "[PutForwards=x] value must be a wrapper type" unless $destinationInterface;
my $destinationAttribute = FindAttributeWithName($destinationInterface, $destinationAttrName);
die "[PutForwards=x] could not find $destinationAttrName in interface $attrType" unless $destinationAttribute;
$code .= <<END;
${implClassName}* proxyImp = ${v8ClassName}::toNative(info.Holder());
${attrType}* imp = proxyImp->${attrName}();
if (!imp)
return;
END
# Override attribute and fall through to forward setter call.
$attribute = $destinationAttribute;
$attrName = $attribute->name;
$attrType = $attribute->type;
$attrExt = $attribute->extendedAttributes;
} else {
my $reflect = $attribute->extendedAttributes->{"Reflect"};
if ($reflect && InheritsInterface($interface, "Node") && $attrType eq "DOMString") {
# Generate super-compact call for regular attribute setter:
my $contentAttributeName = $reflect eq "VALUE_IS_MISSING" ? lc $attrName : $reflect;
my $namespace = NamespaceForAttributeName($interfaceName, $contentAttributeName);
my $mode = GetV8StringResourceMode($attribute->extendedAttributes);
AddToImplIncludes("${namespace}.h");
$code .= " Element* imp = V8Element::toNative(info.Holder());\n";
$code .= " V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<$mode>, stringResource, jsValue);\n";
# Attr (not Attribute) used in content attributes
$code .= " imp->setAttribute(${namespace}::${contentAttributeName}Attr, stringResource);\n";
$code .= "}\n";
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= "\n";
$implementation{nameSpaceInternal}->add($code);
return;
# Skip the rest of the function!
}
if (!$attribute->isStatic) {
$code .= <<END;
${implClassName}* imp = ${v8ClassName}::toNative(info.Holder());
END
}
}
my $nativeType = GetNativeType($attribute->type, $attribute->extendedAttributes, "parameter");
if ($attribute->type eq "EventHandler") {
if ($interface->name eq "Window") {
$code .= " if (!imp->document())\n";
$code .= " return;\n";
}
} else {
my $asSetterValue = 0;
$code .= JSValueToNativeStatement($attribute->type, $attribute->extendedAttributes, $asSetterValue, "jsValue", "cppValue", " ", "info.GetIsolate()");
}
if (IsEnumType($attrType)) {
# setter ignores invalid enumeration values
my @enumValues = ValidEnumValues($attrType);
my @validEqualities = ();
foreach my $enumValue (@enumValues) {
push(@validEqualities, "string == \"$enumValue\"");
}
my $enumValidationExpression = join(" || ", @validEqualities);
$code .= <<END;
String string = cppValue;
if (!($enumValidationExpression))
return;
END
}
my $expression = "cppValue";
my $returnType = $attribute->type;
if (IsRefPtrType($returnType) && !GetArrayType($returnType)) {
$expression = "WTF::getPtr(" . $expression . ")";
}
$code .= GenerateCustomElementInvocationScopeIfNeeded($attribute->extendedAttributes);
my $returnSvgNativeType = GetSVGTypeNeedingTearOff($returnType);
if ($returnSvgNativeType) {
$code .= <<END;
if (!$expression) {
throwUninformativeAndGenericTypeError(info.GetIsolate());
return;
}
END
$expression = $expression . "->propertyReference()";
}
my $useExceptions = 1 if $attribute->extendedAttributes->{"SetterRaisesException"} || $attribute->extendedAttributes->{"RaisesException"};
if ($useExceptions) {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
$code .= " ExceptionState es(info.GetIsolate());\n";
}
if ($attribute->type eq "EventHandler") {
my $implementedBy = $attribute->extendedAttributes->{"ImplementedBy"};
my $implementedByImplName;
if ($implementedBy) {
$implementedByImplName = GetImplNameFromImplementedBy($implementedBy);
}
if (!InheritsInterface($interface, "Node")) {
my $attrImplName = GetImplName($attribute);
my @arguments;
if ($implementedBy) {
$attrImplName = "${implementedByImplName}::${attrImplName}";
push(@arguments, "imp");
} else {
$attrImplName = "imp->${attrImplName}";
}
push(@arguments, "isolatedWorldForIsolate(info.GetIsolate())");
$code .= " transferHiddenDependency(info.Holder(), ${attrImplName}(" . join(", ", @arguments) . "), jsValue, ${v8ClassName}::eventListenerCacheIndex, info.GetIsolate());\n";
}
my ($functionName, @arguments) = SetterExpression($interfaceName, $attribute);
if ($implementedBy) {
$functionName = "${implementedByImplName}::${functionName}";
push(@arguments, "imp");
} else {
$functionName = "imp->${functionName}";
}
if (($interfaceName eq "Window" or $interfaceName eq "WorkerGlobalScope") and $attribute->name eq "onerror") {
AddToImplIncludes("bindings/v8/V8ErrorHandler.h");
push(@arguments, "V8EventListenerList::findOrCreateWrapper<V8ErrorHandler>(jsValue, true, info.GetIsolate())");
} else {
push(@arguments, "V8EventListenerList::getEventListener(jsValue, true, ListenerFindOrCreate)");
}
push(@arguments, "isolatedWorldForIsolate(info.GetIsolate())");
$code .= " ${functionName}(" . join(", ", @arguments) . ");\n";
} else {
my ($functionName, @arguments) = SetterExpression($interfaceName, $attribute);
push(@arguments, $expression);
push(@arguments, "es") if $useExceptions;
if ($attribute->extendedAttributes->{"ImplementedBy"}) {
my $implementedBy = $attribute->extendedAttributes->{"ImplementedBy"};
my $implementedByImplName = GetImplNameFromImplementedBy($implementedBy);
AddToImplIncludes(HeaderFilesForInterface($implementedBy, $implementedByImplName));
unshift(@arguments, "imp") if !$attribute->isStatic;
$functionName = "${implementedByImplName}::${functionName}";
} elsif ($attribute->isStatic) {
$functionName = "${implClassName}::${functionName}";
} else {
$functionName = "imp->${functionName}";
}
my ($arg, $subCode) = GenerateCallWith($attribute->extendedAttributes->{"SetterCallWith"} || $attribute->extendedAttributes->{"CallWith"}, " ", 1);
$code .= $subCode;
unshift(@arguments, @$arg);
$code .= " ${functionName}(" . join(", ", @arguments) . ");\n";
}
if ($useExceptions) {
$code .= " es.throwIfNeeded();\n";
}
if (ExtendedAttributeContains($attribute->extendedAttributes->{"CallWith"}, "ScriptState")) {
$code .= " if (state.hadException())\n";
$code .= " throwError(state.exception(), info.GetIsolate());\n";
}
if ($svgNativeType) {
if ($useExceptions) {
$code .= " if (!es.hadException())\n";
$code .= " wrapper->commitChange();\n";
} else {
$code .= " wrapper->commitChange();\n";
}
}
if ($attrCached) {
$code .= <<END;
info.Holder()->DeleteHiddenValue(v8::String::NewSymbol("${attrName}")); // Invalidate the cached value.
END
}
$code .= "}\n"; # end of setter
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= "\n";
$implementation{nameSpaceInternal}->add($code);
}
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 ($type eq "DOMString") {
if ($parameter->extendedAttributes->{"StrictTypeChecking"}) {
push(@andExpression, "(${value}->IsNull() || ${value}->IsUndefined() || ${value}->IsString() || ${value}->IsObject())");
}
} elsif (IsCallbackInterface($parameter->type)) {
# For Callbacks only checks if the value is null or object.
push(@andExpression, "(${value}->IsNull() || ${value}->IsFunction())");
} elsif (GetArrayOrSequenceType($type)) {
if ($parameter->isNullable) {
push(@andExpression, "(${value}->IsNull() || ${value}->IsArray())");
} else {
push(@andExpression, "(${value}->IsArray())");
}
} elsif (IsWrapperType($type)) {
if ($parameter->isNullable) {
push(@andExpression, "(${value}->IsNull() || V8${type}::HasInstance($value, args.GetIsolate(), worldType(args.GetIsolate())))");
} else {
push(@andExpression, "(V8${type}::HasInstance($value, args.GetIsolate(), worldType(args.GetIsolate())))");
}
}
$parameterIndex++;
}
my $res = join(" && ", @andExpression);
$res = "($res)" if @andExpression > 1;
return $res;
}
# As per Web IDL specification, the length of a function Object is
# its number of mandatory parameters.
sub GetFunctionLength
{
my $function = shift;
my $numMandatoryParams = 0;
foreach my $parameter (@{$function->parameters}) {
# Abort as soon as we find the first optional parameter as no mandatory
# parameter can follow an optional one.
last if $parameter->isOptional;
$numMandatoryParams++;
}
return $numMandatoryParams;
}
sub GenerateFunctionParametersCheck
{
my $function = shift;
my @orExpression = ();
my $numParameters = 0;
my $hasVariadic = 0;
my $numMandatoryParams = @{$function->parameters};
foreach my $parameter (@{$function->parameters}) {
if ($parameter->isOptional) {
push(@orExpression, GenerateParametersCheckExpression($numParameters, $function));
$numMandatoryParams--;
}
if ($parameter->isVariadic) {
$hasVariadic = 1;
last;
}
$numParameters++;
}
if (!$hasVariadic) {
push(@orExpression, GenerateParametersCheckExpression($numParameters, $function));
}
return ($numMandatoryParams, join(" || ", @orExpression));
}
sub GenerateOverloadedFunction
{
my $function = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
# Generate code for choosing the correct overload to call. Overloads are
# chosen based on the total number of arguments passed and the type of
# values passed in non-primitive argument slots. When more than a single
# overload is applicable, precedence is given according to the order of
# declaration in the IDL.
my $name = $function->name;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $conditionalString = GenerateConditionalString($function);
my $leastNumMandatoryParams = 255;
my $code = "";
$code .= "#if ${conditionalString}\n\n" if $conditionalString;
$code .= <<END;
static void ${name}Method${forMainWorldSuffix}(const v8::FunctionCallbackInfo<v8::Value>& args)
{
END
$code .= GenerateFeatureObservation($function->extendedAttributes->{"MeasureAs"});
$code .= GenerateDeprecationNotification($function->extendedAttributes->{"DeprecateAs"});
foreach my $overload (@{$function->{overloads}}) {
my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($overload);
$leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams);
$code .= " if ($parametersCheck) {\n";
my $overloadedIndexString = $overload->{overloadIndex};
$code .= " ${name}${overloadedIndexString}Method${forMainWorldSuffix}(args);\n";
$code .= " return;\n";
$code .= " }\n";
}
if ($leastNumMandatoryParams >= 1) {
$code .= " if (UNLIKELY(args.Length() < $leastNumMandatoryParams)) {\n";
$code .= " throwTypeError(ExceptionMessages::failedToExecute(\"$name\", \"$interfaceName\", ExceptionMessages::notEnoughArguments($leastNumMandatoryParams, args.Length())), args.GetIsolate());\n";
$code .= " return;\n";
$code .= " }\n";
}
$code .= <<END;
throwUninformativeAndGenericTypeError(args.GetIsolate());
END
$code .= "}\n\n";
$code .= "#endif // ${conditionalString}\n\n" if $conditionalString;
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateFunctionCallback
{
my $function = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $name = $function->name;
if ($name eq "") {
return;
}
my $conditionalString = GenerateConditionalString($function);
my $code = "";
$code .= "#if ${conditionalString}\n\n" if $conditionalString;
$code .= <<END;
static void ${name}MethodCallback${forMainWorldSuffix}(const v8::FunctionCallbackInfo<v8::Value>& args)
{
END
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"Blink\", \"DOMMethod\");\n";
$code .= GenerateFeatureObservation($function->extendedAttributes->{"MeasureAs"});
$code .= GenerateDeprecationNotification($function->extendedAttributes->{"DeprecateAs"});
if (HasActivityLogging($forMainWorldSuffix, $function->extendedAttributes, "Access")) {
$code .= GenerateActivityLogging("Method", $interface, "${name}");
}
if (HasCustomMethod($function->extendedAttributes)) {
$code .= " ${v8ClassName}::${name}MethodCustom(args);\n";
} else {
$code .= " ${implClassName}V8Internal::${name}Method${forMainWorldSuffix}(args);\n";
}
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"V8\", \"Execution\");\n";
$code .= "}\n\n";
$code .= "#endif // ${conditionalString}\n\n" if $conditionalString;
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateFunction
{
my $function = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $name = $function->name;
my $implName = GetImplName($function);
my $funcExt = $function->extendedAttributes;
if (HasCustomMethod($funcExt) || $name eq "") {
return;
}
if (@{$function->{overloads}} > 1) {
# Append a number to an overloaded method's name to make it unique:
$name = $name . $function->{overloadIndex};
}
my $conditionalString = GenerateConditionalString($function);
my $code = "";
$code .= "#if ${conditionalString}\n\n" if $conditionalString;
$code .= "static void ${name}Method${forMainWorldSuffix}(const v8::FunctionCallbackInfo<v8::Value>& args)\n";
$code .= "{\n";
if ($name eq "addEventListener" || $name eq "removeEventListener") {
my $lookupType = ($name eq "addEventListener") ? "OrCreate" : "Only";
my $passRefPtrHandling = ($name eq "addEventListener") ? "" : ".get()";
my $hiddenDependencyAction = ($name eq "addEventListener") ? "create" : "remove";
AddToImplIncludes("bindings/v8/BindingSecurity.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
AddToImplIncludes("bindings/v8/V8EventListenerList.h");
AddToImplIncludes("core/frame/DOMWindow.h");
$code .= <<END;
EventTarget* impl = ${v8ClassName}::toNative(args.Holder());
if (DOMWindow* window = impl->toDOMWindow()) {
ExceptionState es(args.GetIsolate());
if (!BindingSecurity::shouldAllowAccessToFrame(window->frame(), es)) {
es.throwIfNeeded();
return;
}
if (!window->document())
return;
}
RefPtr<EventListener> listener = V8EventListenerList::getEventListener(args[1], false, ListenerFind${lookupType});
if (listener) {
V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<WithNullCheck>, stringResource, args[0]);
impl->${implName}(stringResource, listener${passRefPtrHandling}, args[2]->BooleanValue());
if (!impl->toNode())
${hiddenDependencyAction}HiddenDependency(args.Holder(), args[1], ${v8ClassName}::eventListenerCacheIndex, args.GetIsolate());
}
}
END
$code .= "#endif // ${conditionalString}\n\n" if $conditionalString;
$implementation{nameSpaceInternal}->add($code);
return;
}
$code .= GenerateArgumentsCountCheck($function, $interface);
if ($name eq "set" and IsConstructorTemplate($interface, "TypedArray")) {
AddToImplIncludes("bindings/v8/custom/V8ArrayBufferViewCustom.h");
$code .= <<END;
setWebGLArrayHelper<$implClassName, ${v8ClassName}>(args);
}
END
$implementation{nameSpaceInternal}->add($code);
return;
}
my ($svgPropertyType, $svgListPropertyType, $svgNativeType) = GetSVGPropertyTypes($interfaceName);
if ($svgNativeType) {
my $nativeClassName = GetNativeType($interfaceName);
if ($interfaceName =~ /List$/) {
$code .= " $nativeClassName imp = ${v8ClassName}::toNative(args.Holder());\n";
} else {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
AddToImplIncludes("core/dom/ExceptionCode.h");
$code .= " $nativeClassName wrapper = ${v8ClassName}::toNative(args.Holder());\n";
$code .= " if (wrapper->isReadOnly()) {\n";
$code .= " setDOMException(NoModificationAllowedError, ExceptionMessages::failedToExecute(\"${name}\", \"${interfaceName}\", \"The object is read-only.\"), args.GetIsolate());\n";
$code .= " return;\n";
$code .= " }\n";
my $svgWrappedNativeType = GetSVGWrappedTypeNeedingTearOff($interfaceName);
$code .= " $svgWrappedNativeType& impInstance = wrapper->propertyReference();\n";
$code .= " $svgWrappedNativeType* imp = &impInstance;\n";
}
} elsif (!$function->isStatic) {
$code .= <<END;
${implClassName}* imp = ${v8ClassName}::toNative(args.Holder());
END
}
$code .= GenerateCustomElementInvocationScopeIfNeeded($funcExt);
my $raisesExceptions = $function->extendedAttributes->{"RaisesException"} || ($interface->extendedAttributes->{"CheckSecurity"} && !$function->extendedAttributes->{"DoNotCheckSecurity"});
if ($raisesExceptions) {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
$code .= " ExceptionState es(args.GetIsolate());\n";
}
# Check domain security if needed
if ($interface->extendedAttributes->{"CheckSecurity"} && !$function->extendedAttributes->{"DoNotCheckSecurity"}) {
# We have not find real use cases yet.
AddToImplIncludes("bindings/v8/BindingSecurity.h");
$code .= <<END;
if (!BindingSecurity::shouldAllowAccessToFrame(imp->frame(), es)) {
es.throwIfNeeded();
return;
}
END
}
if ($function->extendedAttributes->{"CheckSecurityForNode"}) {
AddToImplIncludes("bindings/v8/BindingSecurity.h");
$code .= " if (!BindingSecurity::shouldAllowAccessToNode(imp->" . GetImplName($function) . "(es), es)) {\n";
$code .= " v8SetReturnValueNull(args);\n";
$code .= " es.throwIfNeeded();\n";
$code .= " return;\n";
$code .= " }\n";
END
}
my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interface, $forMainWorldSuffix);
$code .= $parameterCheckString;
# Build the function call string.
$code .= GenerateFunctionCallString($function, $paramIndex, " ", $interface, $forMainWorldSuffix, %replacements);
$code .= "}\n\n";
$code .= "#endif // ${conditionalString}\n\n" if $conditionalString;
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateCallWith
{
my $callWith = shift;
return ([], "") unless $callWith;
my $indent = shift;
my $returnVoid = shift;
my $function = shift;
my $code = "";
my @callWithArgs;
if (ExtendedAttributeContains($callWith, "ScriptState")) {
$code .= $indent . "ScriptState* currentState = ScriptState::current();\n";
$code .= $indent . "if (!currentState)\n";
$code .= $indent . " return" . ($returnVoid ? "" : " v8Undefined()") . ";\n";
$code .= $indent . "ScriptState& state = *currentState;\n";
push(@callWithArgs, "&state");
}
if (ExtendedAttributeContains($callWith, "ExecutionContext")) {
$code .= $indent . "ExecutionContext* scriptContext = getExecutionContext();\n";
push(@callWithArgs, "scriptContext");
}
if ($function and ExtendedAttributeContains($callWith, "ScriptArguments")) {
$code .= $indent . "RefPtr<ScriptArguments> scriptArguments(createScriptArguments(args, " . @{$function->parameters} . "));\n";
push(@callWithArgs, "scriptArguments.release()");
AddToImplIncludes("bindings/v8/ScriptCallStackFactory.h");
AddToImplIncludes("core/inspector/ScriptArguments.h");
}
if (ExtendedAttributeContains($callWith, "ActiveWindow")) {
push(@callWithArgs, "activeDOMWindow()");
}
if (ExtendedAttributeContains($callWith, "FirstWindow")) {
push(@callWithArgs, "firstDOMWindow()");
}
return ([@callWithArgs], $code);
}
sub GenerateArgumentsCountCheck
{
my $function = shift;
my $interface = shift;
my $functionName = $function->name;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $numMandatoryParams = 0;
my $allowNonOptional = 1;
foreach my $param (@{$function->parameters}) {
if ($param->isOptional 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 (UNLIKELY(args.Length() < $numMandatoryParams)) {\n";
$argumentsCountCheckString .= " throwTypeError(ExceptionMessages::failedToExecute(\"$functionName\", \"$interfaceName\", ExceptionMessages::notEnoughArguments($numMandatoryParams, args.Length())), args.GetIsolate());\n";
$argumentsCountCheckString .= " return;\n";
$argumentsCountCheckString .= " }\n";
}
return $argumentsCountCheckString;
}
sub GenerateParametersCheck
{
my $function = shift;
my $interface = shift;
my $forMainWorldSuffix = shift;
my $style = shift || "new";
my $functionName = $function->name;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $parameterCheckString = "";
my $paramIndex = 0;
my %replacements = ();
foreach my $parameter (@{$function->parameters}) {
my $humanFriendlyIndex = $paramIndex + 1;
my $nativeType = GetNativeType($parameter->type, $parameter->extendedAttributes, "parameter");
# Optional arguments without [Default=...] 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.
if ($parameter->isOptional && !$parameter->extendedAttributes->{"Default"} && $nativeType ne "Dictionary" && !IsCallbackInterface($parameter->type)) {
$parameterCheckString .= <<END;
if (UNLIKELY(args.Length() <= $paramIndex)) {
END
$parameterCheckString .= GenerateFunctionCallString($function, $paramIndex, " " x 2, $interface, $forMainWorldSuffix, %replacements);
$parameterCheckString .= <<END;
return;
}
END
}
my $parameterName = $parameter->name;
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
if (IsCallbackInterface($parameter->type)) {
my $v8ClassName = "V8" . $parameter->type;
AddToImplIncludes("$v8ClassName.h");
if ($parameter->isOptional) {
$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 .= " throwTypeError(ExceptionMessages::failedToExecute(\"$functionName\", \"$interfaceName\", \"The callback provided as parameter $humanFriendlyIndex is not a function.\"), args.GetIsolate());\n";
$parameterCheckString .= " return;\n";
$parameterCheckString .= " }\n";
$parameterCheckString .= " $parameterName = ${v8ClassName}::create(args[$paramIndex], getExecutionContext());\n";
$parameterCheckString .= " }\n";
} else {
$parameterCheckString .= " if (args.Length() <= $paramIndex || ";
if ($parameter->isNullable) {
$parameterCheckString .= "!(args[$paramIndex]->IsFunction() || args[$paramIndex]->IsNull())";
} else {
$parameterCheckString .= "!args[$paramIndex]->IsFunction()";
}
$parameterCheckString .= ") {\n";
$parameterCheckString .= " throwTypeError(ExceptionMessages::failedToExecute(\"$functionName\", \"$interfaceName\", \"The callback provided as parameter $humanFriendlyIndex is not a function.\"), args.GetIsolate());\n";
$parameterCheckString .= " return;\n";
$parameterCheckString .= " }\n";
$parameterCheckString .= " RefPtr<" . $parameter->type . "> $parameterName = ";
$parameterCheckString .= "args[$paramIndex]->IsNull() ? 0 : " if $parameter->isNullable;
$parameterCheckString .= "${v8ClassName}::create(args[$paramIndex], getExecutionContext());\n";
}
} elsif ($parameter->extendedAttributes->{"Clamp"}) {
my $nativeValue = "${parameterName}NativeValue";
my $paramType = $parameter->type;
$parameterCheckString .= " $paramType $parameterName = 0;\n";
$parameterCheckString .= " V8TRYCATCH_VOID(double, $nativeValue, args[$paramIndex]->NumberValue());\n";
$parameterCheckString .= " if (!std::isnan($nativeValue))\n";
$parameterCheckString .= " $parameterName = clampTo<$paramType>($nativeValue);\n";
} elsif ($parameter->type eq "SerializedScriptValue") {
AddToImplIncludes("bindings/v8/SerializedScriptValue.h");
$parameterCheckString .= " bool ${parameterName}DidThrow = false;\n";
$parameterCheckString .= " $nativeType $parameterName = SerializedScriptValue::create(args[$paramIndex], 0, 0, ${parameterName}DidThrow, args.GetIsolate());\n";
$parameterCheckString .= " if (${parameterName}DidThrow)\n";
$parameterCheckString .= " return;\n";
} elsif ($parameter->isVariadic) {
my $nativeElementType = GetNativeType($parameter->type);
if ($nativeElementType =~ />$/) {
$nativeElementType .= " ";
}
my $argType = $parameter->type;
if (IsWrapperType($argType)) {
$parameterCheckString .= " Vector<$nativeElementType> $parameterName;\n";
$parameterCheckString .= " for (int i = $paramIndex; i < args.Length(); ++i) {\n";
$parameterCheckString .= " if (!V8${argType}::HasInstance(args[i], args.GetIsolate(), worldType(args.GetIsolate()))) {\n";
$parameterCheckString .= " throwTypeError(ExceptionMessages::failedToExecute(\"$functionName\", \"$interfaceName\", \"parameter $humanFriendlyIndex is not of type \'$argType\'.\"), args.GetIsolate());\n";
$parameterCheckString .= " return;\n";
$parameterCheckString .= " }\n";
$parameterCheckString .= " $parameterName.append(V8${argType}::toNative(v8::Handle<v8::Object>::Cast(args[i])));\n";
$parameterCheckString .= " }\n";
} else {
$parameterCheckString .= " V8TRYCATCH_VOID(Vector<$nativeElementType>, $parameterName, toNativeArguments<$nativeElementType>(args, $paramIndex));\n";
}
} elsif ($nativeType =~ /^V8StringResource/) {
my $default = defined $parameter->extendedAttributes->{"Default"} ? $parameter->extendedAttributes->{"Default"} : "";
my $jsValue = $parameter->isOptional && $default eq "NullString" ? "argumentOrNull(args, $paramIndex)" : "args[$paramIndex]";
my $stringResourceParameterName = $parameterName;
my $isNullable = $parameter->isNullable && !IsRefPtrType($parameter->type);
if ($isNullable) {
$parameterCheckString .= " bool ${parameterName}IsNull = $jsValue->IsNull();\n";
$stringResourceParameterName .= "StringResource";
}
$parameterCheckString .= JSValueToNativeStatement($parameter->type, $parameter->extendedAttributes, $humanFriendlyIndex, $jsValue, $stringResourceParameterName, " ", "args.GetIsolate()");
$parameterCheckString .= " String $parameterName = $stringResourceParameterName;\n" if $isNullable;
if (IsEnumType($parameter->type)) {
my @enumValues = ValidEnumValues($parameter->type);
my @validEqualities = ();
foreach my $enumValue (@enumValues) {
push(@validEqualities, "string == \"$enumValue\"");
}
my $enumValidationExpression = join(" || ", @validEqualities);
$parameterCheckString .= " String string = $parameterName;\n";
$parameterCheckString .= " if (!($enumValidationExpression)) {\n";
$parameterCheckString .= " throwTypeError(ExceptionMessages::failedToExecute(\"$functionName\", \"$interfaceName\", \"parameter $humanFriendlyIndex (\'\" + string + \"\') is not a valid enum value.\"), args.GetIsolate());\n";
$parameterCheckString .= " return;\n";
$parameterCheckString .= " }\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->extendedAttributes->{"StrictTypeChecking"}) {
my $argValue = "args[$paramIndex]";
my $argType = $parameter->type;
if (IsWrapperType($argType)) {
$parameterCheckString .= " if (args.Length() > $paramIndex && !isUndefinedOrNull($argValue) && !V8${argType}::HasInstance($argValue, args.GetIsolate(), worldType(args.GetIsolate()))) {\n";
$parameterCheckString .= " throwTypeError(ExceptionMessages::failedToExecute(\"$functionName\", \"$interfaceName\", \"parameter $humanFriendlyIndex is not of type \'$argType\'.\"), args.GetIsolate());\n";
$parameterCheckString .= " return;\n";
$parameterCheckString .= " }\n";
}
}
my $default = defined $parameter->extendedAttributes->{"Default"} ? $parameter->extendedAttributes->{"Default"} : "";
my $jsValue = $parameter->isOptional && $default eq "NullString" ? "argumentOrNull(args, $paramIndex)" : "args[$paramIndex]";
my $isNullable = $parameter->isNullable && !IsRefPtrType($parameter->type);
$parameterCheckString .= " bool ${parameterName}IsNull = $jsValue->IsNull();\n" if $isNullable;
$parameterCheckString .= JSValueToNativeStatement($parameter->type, $parameter->extendedAttributes, $humanFriendlyIndex, $jsValue, $parameterName, " ", "args.GetIsolate()");
if ($nativeType eq 'Dictionary' or $nativeType eq 'ScriptPromise') {
$parameterCheckString .= " if (!$parameterName.isUndefinedOrNull() && !$parameterName.isObject()) {\n";
if ($functionName eq "Constructor") {
$parameterCheckString .= " throwTypeError(ExceptionMessages::failedToConstruct(\"$interfaceName\", \"parameter ${humanFriendlyIndex} ('${parameterName}') is not an object.\"), args.GetIsolate());\n";
} else {
$parameterCheckString .= " throwTypeError(ExceptionMessages::failedToExecute(\"$functionName\", \"$interfaceName\", \"parameter ${humanFriendlyIndex} ('${parameterName}') is not an object.\"), args.GetIsolate());\n";
}
$parameterCheckString .= " return;\n";
$parameterCheckString .= " }\n";
}
}
$paramIndex++;
}
return ($parameterCheckString, $paramIndex, %replacements);
}
sub GenerateOverloadedConstructorCallback
{
my $interface = shift;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $code = "";
$code .= <<END;
static void constructor(const v8::FunctionCallbackInfo<v8::Value>& args)
{
END
my $leastNumMandatoryParams = 255;
foreach my $constructor (@{$interface->constructors}) {
my $name = "constructor" . $constructor->overloadedIndex;
my ($numMandatoryParams, $parametersCheck) = GenerateFunctionParametersCheck($constructor);
$leastNumMandatoryParams = $numMandatoryParams if ($numMandatoryParams < $leastNumMandatoryParams);
$code .= " if ($parametersCheck) {\n";
$code .= " ${implClassName}V8Internal::${name}(args);\n";
$code .= " return;\n";
$code .= " }\n";
}
if ($leastNumMandatoryParams >= 1) {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
$code .= " if (UNLIKELY(args.Length() < $leastNumMandatoryParams)) {\n";
$code .= " throwTypeError(ExceptionMessages::failedToConstruct(\"$interfaceName\", ExceptionMessages::notEnoughArguments($leastNumMandatoryParams, args.Length())), args.GetIsolate());\n";
$code .= " return;\n";
$code .= " }\n";
}
$code .= <<END;
throwUninformativeAndGenericTypeError(args.GetIsolate());
return;
END
$code .= "}\n\n";
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateSingleConstructorCallback
{
my $interface = shift;
my $function = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $overloadedIndexString = "";
if ($function->overloadedIndex > 0) {
$overloadedIndexString .= $function->overloadedIndex;
}
my $raisesExceptions = $function->extendedAttributes->{"RaisesException"};
if ($interface->extendedAttributes->{"ConstructorRaisesException"}) {
$raisesExceptions = 1;
}
my @beforeArgumentList;
my @afterArgumentList;
my $code = "";
$code .= <<END;
static void constructor${overloadedIndexString}(const v8::FunctionCallbackInfo<v8::Value>& args)
{
END
if ($function->overloadedIndex == 0) {
$code .= GenerateArgumentsCountCheck($function, $interface);
}
if ($raisesExceptions) {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
$code .= " ExceptionState es(args.GetIsolate());\n";
}
# FIXME: Currently [Constructor(...)] does not yet support optional arguments without [Default=...]
my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interface, "");
$code .= $parameterCheckString;
if ($interface->extendedAttributes->{"ConstructorCallWith"}) {
if ($interface->extendedAttributes->{"ConstructorCallWith"} eq "ExecutionContext") {
push(@beforeArgumentList, "context");
$code .= "\n";
$code .= " ExecutionContext* context = getExecutionContext();";
} elsif ($interface->extendedAttributes->{"ConstructorCallWith"} eq "Document") {
push(@beforeArgumentList, "document");
$code .= "\n";
$code .= " Document& document = *toDocument(getExecutionContext());";
}
}
if ($interface->extendedAttributes->{"ConstructorRaisesException"}) {
push(@afterArgumentList, "es");
}
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);
$code .= "\n";
$code .= " RefPtr<${implClassName}> impl = ${implClassName}::create(${argumentString});\n";
$code .= " v8::Handle<v8::Object> wrapper = args.Holder();\n";
if ($interface->extendedAttributes->{"ConstructorRaisesException"}) {
$code .= " if (es.throwIfNeeded())\n";
$code .= " return;\n";
}
$code .= <<END;
V8DOMWrapper::associateObjectWithWrapper<${v8ClassName}>(impl.release(), &${v8ClassName}::wrapperTypeInfo, wrapper, args.GetIsolate(), WrapperConfiguration::Dependent);
args.GetReturnValue().Set(wrapper);
}
END
$implementation{nameSpaceInternal}->add($code);
}
# The Web IDL specification states that Interface objects for interfaces MUST have a property named
# "length" that returns the length of the shortest argument list of the entries in the effective
# overload set for constructors. In other words, use the lowest number of mandatory arguments among
# all constructors.
sub GetInterfaceLength
{
my $interface = shift;
my $leastConstructorLength = 0;
if (IsConstructorTemplate($interface, "Event") || IsConstructorTemplate($interface, "TypedArray")) {
$leastConstructorLength = 1;
} elsif ($interface->extendedAttributes->{"Constructor"} || $interface->extendedAttributes->{"CustomConstructor"}) {
my @constructors = @{$interface->constructors};
my @customConstructors = @{$interface->customConstructors};
$leastConstructorLength = 255;
foreach my $constructor (@constructors, @customConstructors) {
my $constructorLength = GetFunctionLength($constructor);
$leastConstructorLength = $constructorLength if ($constructorLength < $leastConstructorLength);
}
}
return $leastConstructorLength;
}
sub GenerateConstructorCallback
{
my $interface = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $code = "";
$code .= "void ${v8ClassName}::constructorCallback(const v8::FunctionCallbackInfo<v8::Value>& args)\n";
$code .= "{\n";
$code .= " TRACE_EVENT_SCOPED_SAMPLING_STATE(\"Blink\", \"DOMConstructor\");\n";
$code .= GenerateFeatureObservation($interface->extendedAttributes->{"MeasureAs"});
$code .= GenerateDeprecationNotification($interface->extendedAttributes->{"DeprecateAs"});
$code .= GenerateConstructorHeader($interface->name);
if (HasCustomConstructor($interface)) {
$code .= " ${v8ClassName}::constructorCustom(args);\n";
} else {
$code .= " ${implClassName}V8Internal::constructor(args);\n";
}
$code .= "}\n\n";
$implementation{nameSpaceWebCore}->add($code);
}
sub GenerateConstructor
{
my $interface = shift;
if (@{$interface->constructors} == 1) {
GenerateSingleConstructorCallback($interface, @{$interface->constructors}[0]);
} else {
foreach my $constructor (@{$interface->constructors}) {
GenerateSingleConstructorCallback($interface, $constructor);
}
GenerateOverloadedConstructorCallback($interface);
}
}
sub GenerateEventConstructor
{
my $interface = shift;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my @anyAttributeNames;
my @serializableAnyAttributeNames;
foreach my $attribute (@{$interface->attributes}) {
if ($attribute->type eq "any") {
push(@anyAttributeNames, $attribute->name);
if (!$attribute->extendedAttributes->{"Unserializable"}) {
push(@serializableAnyAttributeNames, $attribute->name);
}
}
}
AddToImplIncludes("bindings/v8/Dictionary.h");
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
$implementation{nameSpaceInternal}->add(<<END);
static void constructor(const v8::FunctionCallbackInfo<v8::Value>& args)
{
if (args.Length() < 1) {
throwTypeError(ExceptionMessages::failedToConstruct("$interfaceName", "An event name must be provided."), args.GetIsolate());
return;
}
V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(V8StringResource<>, type, args[0]);
END
foreach my $attrName (@anyAttributeNames) {
$implementation{nameSpaceInternal}->add(" v8::Local<v8::Value> ${attrName};\n");
}
$implementation{nameSpaceInternal}->add(<<END);
${implClassName}Init eventInit;
if (args.Length() >= 2) {
V8TRYCATCH_VOID(Dictionary, options, Dictionary(args[1], args.GetIsolate()));
if (!fill${implClassName}Init(eventInit, options))
return;
END
# Store 'any'-typed properties on the wrapper to avoid leaking them between isolated worlds.
foreach my $attrName (@anyAttributeNames) {
$implementation{nameSpaceInternal}->add(<<END);
options.get("${attrName}", ${attrName});
if (!${attrName}.IsEmpty())
args.Holder()->SetHiddenValue(V8HiddenPropertyName::${attrName}(args.GetIsolate()), ${attrName});
END
}
$implementation{nameSpaceInternal}->add(<<END);
}
RefPtr<${implClassName}> event = ${implClassName}::create(type, eventInit);
END
if (@serializableAnyAttributeNames) {
# If we're in an isolated world, create a SerializedScriptValue and store it in the event for
# later cloning if the property is accessed from another world.
# The main world case is handled lazily (in Custom code).
$implementation{nameSpaceInternal}->add(" if (isolatedWorldForIsolate(args.GetIsolate())) {\n");
foreach my $attrName (@serializableAnyAttributeNames) {
my $setter = "setSerialized" . FirstLetterToUpperCase($attrName);
$implementation{nameSpaceInternal}->add(<<END);
if (!${attrName}.IsEmpty())
event->${setter}(SerializedScriptValue::createAndSwallowExceptions(${attrName}, args.GetIsolate()));
END
}
$implementation{nameSpaceInternal}->add(" }\n\n");
}
$implementation{nameSpaceInternal}->add(<<END);
v8::Handle<v8::Object> wrapper = args.Holder();
V8DOMWrapper::associateObjectWithWrapper<${v8ClassName}>(event.release(), &${v8ClassName}::wrapperTypeInfo, wrapper, args.GetIsolate(), WrapperConfiguration::Dependent);
v8SetReturnValue(args, wrapper);
}
END
my $code = "";
$code .= <<END;
bool fill${implClassName}Init(${implClassName}Init& eventInit, const Dictionary& options)
{
END
if ($interface->parent) {
my $interfaceBase = $interface->parent;
$code .= <<END;
if (!fill${interfaceBase}Init(eventInit, options))
return false;
END
}
foreach my $attribute (@{$interface->attributes}) {
if ($attribute->extendedAttributes->{"InitializedByEventConstructor"}) {
if ($attribute->type ne "any") {
my $attributeName = $attribute->name;
my $attributeImplName = GetImplName($attribute);
my $deprecation = $attribute->extendedAttributes->{"DeprecateAs"};
my $dictionaryGetter = "options.get(\"$attributeName\", eventInit.$attributeImplName)";
if ($attribute->extendedAttributes->{"DeprecateAs"}) {
$code .= " if ($dictionaryGetter)\n";
$code .= " " . GenerateDeprecationNotification($attribute->extendedAttributes->{"DeprecateAs"});
} else {
$code .= " $dictionaryGetter;\n";
}
}
}
}
$code .= <<END;
return true;
}
END
$implementation{nameSpaceWebCore}->add($code);
}
sub GenerateNamedConstructor
{
my $function = shift;
my $interface = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $raisesExceptions = $function->extendedAttributes->{"RaisesException"};
if ($interface->extendedAttributes->{"ConstructorRaisesException"}) {
$raisesExceptions = 1;
}
my $maybeObserveFeature = GenerateFeatureObservation($function->extendedAttributes->{"MeasureAs"});
my $maybeDeprecateFeature = GenerateDeprecationNotification($function->extendedAttributes->{"DeprecateAs"});
my @beforeArgumentList;
my @afterArgumentList;
my $toActiveDOMObject = "0";
if (InheritsExtendedAttribute($interface, "ActiveDOMObject")) {
$toActiveDOMObject = "${v8ClassName}::toActiveDOMObject";
}
my $toEventTarget = "0";
if (InheritsInterface($interface, "EventTarget")) {
$toEventTarget = "${v8ClassName}::toEventTarget";
}
$implementation{nameSpaceWebCore}->add(<<END);
const WrapperTypeInfo ${v8ClassName}Constructor::wrapperTypeInfo = { ${v8ClassName}Constructor::GetTemplate, ${v8ClassName}::derefObject, $toActiveDOMObject, $toEventTarget, 0, ${v8ClassName}::installPerContextEnabledPrototypeProperties, 0, WrapperTypeObjectPrototype };
END
my $code = "";
$code .= <<END;
static void ${v8ClassName}ConstructorCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
{
END
$code .= $maybeObserveFeature if $maybeObserveFeature;
$code .= $maybeDeprecateFeature if $maybeDeprecateFeature;
$code .= GenerateConstructorHeader($function->extendedAttributes->{"NamedConstructor"});
AddToImplIncludes("V8Document.h");
$code .= <<END;
Document* document = currentDocument();
ASSERT(document);
// Make sure the document is added to the DOM Node map. Otherwise, the ${implClassName} instance
// may end up being the only node in the map and get garbage-collected prematurely.
toV8(document, args.Holder(), args.GetIsolate());
END
$code .= GenerateArgumentsCountCheck($function, $interface);
if ($raisesExceptions) {
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/ExceptionState.h");
$code .= " ExceptionState es(args.GetIsolate());\n";
}
my ($parameterCheckString, $paramIndex, %replacements) = GenerateParametersCheck($function, $interface);
$code .= $parameterCheckString;
push(@beforeArgumentList, "*document");
if ($interface->extendedAttributes->{"ConstructorRaisesException"}) {
push(@afterArgumentList, "es");
}
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);
$code .= "\n";
$code .= " RefPtr<${implClassName}> impl = ${implClassName}::createForJSConstructor(${argumentString});\n";
$code .= " v8::Handle<v8::Object> wrapper = args.Holder();\n";
if ($interface->extendedAttributes->{"ConstructorRaisesException"}) {
$code .= " if (es.throwIfNeeded())\n";
$code .= " return;\n";
}
$code .= <<END;
V8DOMWrapper::associateObjectWithWrapper<${v8ClassName}>(impl.release(), &${v8ClassName}Constructor::wrapperTypeInfo, wrapper, args.GetIsolate(), WrapperConfiguration::Dependent);
args.GetReturnValue().Set(wrapper);
}
END
$implementation{nameSpaceWebCore}->add($code);
$code = <<END;
v8::Handle<v8::FunctionTemplate> ${v8ClassName}Constructor::GetTemplate(v8::Isolate* isolate, WrapperWorldType currentWorldType)
{
// This is only for getting a unique pointer which we can pass to privateTemplate.
static int privateTemplateUniqueKey;
V8PerIsolateData* data = V8PerIsolateData::from(isolate);
v8::Handle<v8::FunctionTemplate> result = data->privateTemplateIfExists(currentWorldType, &privateTemplateUniqueKey);
if (!result.IsEmpty())
return result;
TRACE_EVENT_SCOPED_SAMPLING_STATE("Blink\", \"BuildDOMTemplate");
v8::HandleScope scope(isolate);
result = v8::FunctionTemplate::New(${v8ClassName}ConstructorCallback);
v8::Local<v8::ObjectTemplate> instance = result->InstanceTemplate();
instance->SetInternalFieldCount(${v8ClassName}::internalFieldCount);
result->SetClassName(v8::String::NewSymbol("${implClassName}"));
result->Inherit(${v8ClassName}::GetTemplate(isolate, currentWorldType));
data->setPrivateTemplate(currentWorldType, &privateTemplateUniqueKey, result);
return scope.Close(result);
}
END
$implementation{nameSpaceWebCore}->add($code);
}
sub GenerateConstructorHeader
{
my $constructorName = shift;
AddToImplIncludes("bindings/v8/ExceptionMessages.h");
AddToImplIncludes("bindings/v8/V8ObjectConstructor.h");
my $content = <<END;
if (!args.IsConstructCall()) {
throwTypeError(ExceptionMessages::failedToConstruct("$constructorName", "Please use the 'new' operator, this DOM object constructor cannot be called as a function."), args.GetIsolate());
return;
}
if (ConstructorMode::current() == ConstructorMode::WrapExistingObject) {
args.GetReturnValue().Set(args.Holder());
return;
}
END
return $content;
}
sub GenerateAttributeConfigurationArray
{
my $interface = shift;
my $attributes = shift;
my $code = "";
foreach my $attribute (@$attributes) {
my $conditionalString = GenerateConditionalString($attribute);
my $subCode = "";
$subCode .= "#if ${conditionalString}\n" if $conditionalString;
$subCode .= GenerateAttributeConfiguration($interface, $attribute, ",", "");
$subCode .= "#endif // ${conditionalString}\n" if $conditionalString;
$code .= $subCode;
}
return $code;
}
sub GenerateAttributeConfigurationParameters
{
my $interface = shift;
my $attribute = shift;
my $attrName = $attribute->name;
my $attrExt = $attribute->extendedAttributes;
my $implClassName = GetImplName($interface);
my @accessControlList;
if ($attrExt->{"DoNotCheckSecurityOnGetter"}) {
push(@accessControlList, "v8::ALL_CAN_READ");
} elsif ($attrExt->{"DoNotCheckSecurityOnSetter"}) {
push(@accessControlList, "v8::ALL_CAN_WRITE");
} elsif ($attrExt->{"DoNotCheckSecurity"}) {
push(@accessControlList, "v8::ALL_CAN_READ");
if (!IsReadonly($attribute)) {
push(@accessControlList, "v8::ALL_CAN_WRITE");
}
}
if ($attrExt->{"Unforgeable"}) {
push(@accessControlList, "v8::PROHIBITS_OVERWRITING");
}
@accessControlList = ("v8::DEFAULT") unless @accessControlList;
my $accessControl = "static_cast<v8::AccessControl>(" . join(" | ", @accessControlList) . ")";
my $customAccessor = HasCustomGetter($attrExt) || HasCustomSetter($attribute) || "";
if ($customAccessor eq "VALUE_IS_MISSING") {
# use the naming convension, interface + (capitalize) attr name
$customAccessor = $implClassName . "::" . $attrName;
}
my $getter;
my $setter;
my $getterForMainWorld;
my $setterForMainWorld;
my $isConstructor = ($attribute->type =~ /Constructor$/);
# Check attributes.
# As per Web IDL specification, constructor properties on the ECMAScript global object should be
# configurable and should not be enumerable.
my @propAttributeList;
if ($attrExt->{"NotEnumerable"} || $isConstructor) {
push(@propAttributeList, "v8::DontEnum");
}
if ($attrExt->{"Unforgeable"} && !$isConstructor) {
push(@propAttributeList, "v8::DontDelete");
}
@propAttributeList = ("v8::None") unless @propAttributeList;
my $propAttribute = join(" | ", @propAttributeList);
my $on_proto = "0 /* on instance */";
my $data = "0"; # no data
# Constructor
if ($isConstructor) {
my $constructorType = $attribute->type;
$constructorType =~ s/Constructor$//;
# For NamedConstructors we do not generate a header file. The code for the NamedConstructor
# gets generated when we generate the code for its interface.
if ($constructorType !~ /Constructor$/) {
AddToImplIncludes("V8${constructorType}.h");
}
$data = "const_cast<WrapperTypeInfo*>(&V8${constructorType}::wrapperTypeInfo)";
$getter = "${implClassName}V8Internal::${implClassName}ConstructorGetter";
$setter = "${implClassName}V8Internal::${implClassName}ReplaceableAttributeSetterCallback";
$getterForMainWorld = "0";
$setterForMainWorld = "0";
} else {
# Default Getter and Setter
$getter = "${implClassName}V8Internal::${attrName}AttributeGetterCallback";
$setter = "${implClassName}V8Internal::${attrName}AttributeSetterCallback";
$getterForMainWorld = "${getter}ForMainWorld";
$setterForMainWorld = "${setter}ForMainWorld";
if (!HasCustomSetter($attribute) && !$attrExt->{"PutForwards"} && $attrExt->{"Replaceable"}) {
$setter = "${implClassName}V8Internal::${implClassName}ReplaceableAttributeSetterCallback";
$setterForMainWorld = "0";
}
}
# Read only attributes
if (IsReadonly($attribute)) {
$setter = "0";
$setterForMainWorld = "0";
}
# An accessor can be installed on the proto
if ($attrExt->{"OnProto"}) {
$on_proto = "1 /* on proto */";
}
if (!$attrExt->{"PerWorldBindings"}) {
$getterForMainWorld = "0";
$setterForMainWorld = "0";
}
return ($attrName, $getter, $setter, $getterForMainWorld, $setterForMainWorld, $data, $accessControl, "static_cast<v8::PropertyAttribute>($propAttribute)", $on_proto);
}
sub GenerateAttributeConfiguration
{
my $interface = shift;
my $attribute = shift;
my $delimiter = shift;
my $indent = shift;
my $code = "";
my ($attrName, $getter, $setter, $getterForMainWorld, $setterForMainWorld, $data, $accessControl, $propAttribute, $on_proto) = GenerateAttributeConfigurationParameters($interface, $attribute);
$code .= $indent . " {\"$attrName\", $getter, $setter, $getterForMainWorld, $setterForMainWorld, $data, $accessControl, $propAttribute, $on_proto}" . $delimiter . "\n";
return $code;
}
sub GenerateStaticAttribute
{
my $interface = shift;
my $attribute = shift;
my $attrExt = $attribute->extendedAttributes;
my $code = "";
my ($attrName, $getter, $setter, $getterForMainWorld, $setterForMainWorld, $data, $accessControl, $propAttribute, $on_proto) = GenerateAttributeConfigurationParameters($interface, $attribute);
die "Static attributes do not support optimized getters or setters for the main world" if $getterForMainWorld || $setterForMainWorld;
my $conditionalString = GenerateConditionalString($attribute);
$code .= "#if ${conditionalString}\n" if $conditionalString;
$code .= " desc->SetNativeDataProperty(v8::String::NewSymbol(\"$attrName\"), $getter, $setter, v8::External::New($data), $propAttribute, v8::Handle<v8::AccessorSignature>(), $accessControl);\n";
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
return $code;
}
sub IsStandardFunction
{
my $interface = shift;
my $function = shift;
my $interfaceName = $interface->name;
my $attrExt = $function->extendedAttributes;
return 0 if $attrExt->{"Unforgeable"};
return 0 if $function->isStatic;
return 0 if $attrExt->{"RuntimeEnabled"};
return 0 if $attrExt->{"PerContextEnabled"};
return 0 if RequiresCustomSignature($function);
return 0 if $attrExt->{"DoNotCheckSignature"};
return 0 if ($attrExt->{"DoNotCheckSecurity"} && ($interface->extendedAttributes->{"CheckSecurity"} || $interfaceName eq "Window"));
return 0 if $attrExt->{"NotEnumerable"};
return 0 if $attrExt->{"ReadOnly"};
return 1;
}
sub GenerateNonStandardFunction
{
my $interface = shift;
my $function = shift;
my $code = "";
my $implClassName = GetImplName($interface);
my $attrExt = $function->extendedAttributes;
my $name = $function->name;
my $property_attributes = "v8::DontDelete";
if ($attrExt->{"NotEnumerable"}) {
$property_attributes .= " | v8::DontEnum";
}
if ($attrExt->{"ReadOnly"}) {
$property_attributes .= " | v8::ReadOnly";
}
my $commentInfo = "Function '$name' (Extended Attributes: '" . join(' ', keys(%{$attrExt})) . "')";
my $template = "proto";
if ($attrExt->{"Unforgeable"}) {
$template = "instance";
}
if ($function->isStatic) {
$template = "desc";
}
my $conditional = "";
if ($attrExt->{"RuntimeEnabled"}) {
# Only call Set()/SetAccessor() if this method should be enabled
my $runtimeEnabledFunction = GetRuntimeEnabledFunctionName($function);
$conditional = "if (${runtimeEnabledFunction}())\n ";
}
if ($attrExt->{"PerContextEnabled"}) {
# Only call Set()/SetAccessor() if this method should be enabled
my $contextEnabledFunction = GetContextEnabledFunctionName($function);
$conditional = "if (${contextEnabledFunction}(impl->document()))\n ";
}
if ($interface->extendedAttributes->{"CheckSecurity"} && $attrExt->{"DoNotCheckSecurity"}) {
my $setter = $attrExt->{"ReadOnly"} ? "0" : "${implClassName}V8Internal::${implClassName}DomainSafeFunctionSetter";
# 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.
$code .= <<END;
// $commentInfo
END
if ($function->extendedAttributes->{"PerWorldBindings"}) {
$code .= " if (currentWorldType == MainWorld) {\n";
$code .= " ${conditional}$template->SetAccessor(v8::String::NewSymbol(\"$name\"), ${implClassName}V8Internal::${name}AttributeGetterCallbackForMainWorld, ${setter}, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>($property_attributes));\n";
$code .= " } else {\n";
$code .= " ${conditional}$template->SetAccessor(v8::String::NewSymbol(\"$name\"), ${implClassName}V8Internal::${name}AttributeGetterCallback, ${setter}, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>($property_attributes));\n";
$code .= " }\n";
} else {
$code .= " ${conditional}$template->SetAccessor(v8::String::NewSymbol(\"$name\"), ${implClassName}V8Internal::${name}AttributeGetterCallback, ${setter}, v8Undefined(), v8::ALL_CAN_READ, static_cast<v8::PropertyAttribute>($property_attributes));\n";
}
return $code;
}
my $signature = "defaultSignature";
if ($attrExt->{"DoNotCheckSignature"} || $function->isStatic) {
$signature = "v8::Local<v8::Signature>()";
}
my $conditionalString = GenerateConditionalString($function);
$code .= "#if ${conditionalString}\n" if $conditionalString;
if (RequiresCustomSignature($function)) {
$signature = "${name}Signature";
$code .= "\n // Custom Signature '$name'\n" . CreateCustomSignature($function);
}
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: Class '$implClassName' $commentInfo\n";
}
my $functionLength = GetFunctionLength($function);
if ($function->extendedAttributes->{"PerWorldBindings"}) {
$code .= " if (currentWorldType == MainWorld) {\n";
$code .= " ${conditional}$template->Set(v8::String::NewSymbol(\"$name\"), v8::FunctionTemplate::New(${implClassName}V8Internal::${name}MethodCallbackForMainWorld, v8Undefined(), ${signature}, $functionLength)$property_attributes);\n";
$code .= " } else {\n";
$code .= " ${conditional}$template->Set(v8::String::NewSymbol(\"$name\"), v8::FunctionTemplate::New(${implClassName}V8Internal::${name}MethodCallback, v8Undefined(), ${signature}, $functionLength)$property_attributes);\n";
$code .= " }\n";
} else {
$code .= " ${conditional}$template->Set(v8::String::NewSymbol(\"$name\"), v8::FunctionTemplate::New(${implClassName}V8Internal::${name}MethodCallback, v8Undefined(), ${signature}, $functionLength)$property_attributes);\n";
}
$code .= "#endif // ${conditionalString}\n" if $conditionalString;
return $code;
}
sub GenerateIsNullExpression
{
my $type = shift;
my $variableName = shift;
if (IsUnionType($type)) {
my $types = $type->unionMemberTypes;
my @expression = ();
for my $i (0 .. scalar(@$types)-1) {
my $unionMemberType = $types->[$i];
my $unionMemberVariable = $variableName . $i;
my $isNull = GenerateIsNullExpression($unionMemberType, $unionMemberVariable);
push @expression, $isNull;
}
return join " && ", @expression;
}
if (IsRefPtrType($type)) {
return "!${variableName}";
} elsif ($type eq "DOMString") {
return "${variableName}.isNull()";
} elsif ($type eq "Promise") {
return "${variableName}.isNull()";
} else {
return "";
}
}
sub GenerateIfElseStatement
{
my $type = shift;
my $outputVariableName = shift;
my $conditions = shift;
my $statements = shift;
my $code = "";
if (@$conditions == 1) {
$code .= " ${type} ${outputVariableName} = " . $statements->[0] . "\n";
} else {
$code .= " ${type} ${outputVariableName};\n";
for my $i (0 .. @$conditions - 1) {
my $token = "else if";
$token = "if" if $i == 0;
$token = "else" if $i == @$conditions - 1;
$code .= " ${token}";
$code .= " (" . $conditions->[$i] . ")" if $conditions->[$i];
$code .= "\n";
$code .= " ${outputVariableName} = " . $statements->[$i] . "\n";
}
}
return $code;
}
sub GenerateImplementationIndexedPropertyAccessors
{
my $interface = shift;
my $interfaceName = $interface->name;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $indexedGetterFunction = GetIndexedGetterFunction($interface);
if ($indexedGetterFunction) {
my $hasCustomIndexedGetter = $indexedGetterFunction->extendedAttributes->{"Custom"};
if (!$hasCustomIndexedGetter) {
GenerateImplementationIndexedPropertyGetter($interface, $indexedGetterFunction);
}
GenerateImplementationIndexedPropertyGetterCallback($interface, $hasCustomIndexedGetter);
}
my $indexedSetterFunction = GetIndexedSetterFunction($interface);
if ($indexedSetterFunction) {
my $hasCustomIndexedSetter = $indexedSetterFunction->extendedAttributes->{"Custom"};
if (!$hasCustomIndexedSetter) {
GenerateImplementationIndexedPropertySetter($interface, $indexedSetterFunction);
}
GenerateImplementationIndexedPropertySetterCallback($interface, $hasCustomIndexedSetter);
}
my $indexedDeleterFunction = GetIndexedDeleterFunction($interface);
if ($indexedDeleterFunction) {
my $hasCustomIndexedDeleter = $indexedDeleterFunction->extendedAttributes->{"Custom"};
if (!$hasCustomIndexedDeleter) {
GenerateImplementationIndexedPropertyDeleter($interface, $indexedDeleterFunction);
}
GenerateImplementationIndexedPropertyDeleterCallback($interface, $hasCustomIndexedDeleter);
}
my $indexedEnumeratorFunction = $indexedGetterFunction;
$indexedEnumeratorFunction = 0 if $indexedGetterFunction && $indexedGetterFunction->extendedAttributes->{"NotEnumerable"};
my $indexedQueryFunction = 0;
# If there is an enumerator, there MUST be a query method to properly communicate property attributes.
my $hasQuery = $indexedQueryFunction || $indexedEnumeratorFunction;
my $setOn = "Instance";
# V8 has access-check callback API (see ObjectTemplate::SetAccessCheckCallbacks) and it's used on Window
# instead of deleters or enumerators. In addition, the getter should be set on prototype template, to
# get implementation straight out of the Window prototype regardless of what prototype is actually set
# on the object.
if ($interfaceName eq "Window") {
$setOn = "Prototype";
}
my $code = "";
if ($indexedGetterFunction || $indexedSetterFunction || $indexedDeleterFunction || $indexedEnumeratorFunction || $hasQuery) {
$code .= " desc->${setOn}Template()->SetIndexedPropertyHandler(${implClassName}V8Internal::indexedPropertyGetterCallback";
$code .= $indexedSetterFunction ? ", ${implClassName}V8Internal::indexedPropertySetterCallback" : ", 0";
$code .= ", 0"; # IndexedPropertyQuery -- not being used at the moment.
$code .= $indexedDeleterFunction ? ", ${implClassName}V8Internal::indexedPropertyDeleterCallback" : ", 0";
$code .= $indexedEnumeratorFunction ? ", indexedPropertyEnumerator<${implClassName}>" : ", 0";
$code .= ");\n";
}
return $code;
}
sub GenerateImplementationIndexedPropertyGetter
{
my $interface = shift;
my $indexedGetterFunction = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $methodName = GetImplName($indexedGetterFunction);
my $returnType = $indexedGetterFunction->type;
my $nativeType = GetNativeType($returnType);
my $nativeValue = "element";
$nativeValue .= ".release()" if (IsRefPtrType($returnType));
my $isNull = GenerateIsNullExpression($returnType, "element");
my $returnJSValueCode = NativeToJSValue($indexedGetterFunction->type, $indexedGetterFunction->extendedAttributes, $nativeValue, " ", "", "info.GetIsolate()", "info", "collection", "", "return");
my $raisesExceptions = $indexedGetterFunction->extendedAttributes->{"RaisesException"};
my $methodCallCode = GenerateMethodCall($returnType, "element", "collection->${methodName}", "index", $raisesExceptions);
my $getterCode = "static void indexedPropertyGetter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info)\n";
$getterCode .= "{\n";
$getterCode .= " ASSERT(V8DOMWrapper::maybeDOMWrapper(info.Holder()));\n";
$getterCode .= " ${implClassName}* collection = ${v8ClassName}::toNative(info.Holder());\n";
if ($raisesExceptions) {
$getterCode .= " ExceptionState es(info.GetIsolate());\n";
}
$getterCode .= $methodCallCode . "\n";
if ($raisesExceptions) {
$getterCode .= " if (es.throwIfNeeded())\n";
$getterCode .= " return;\n";
}
if (IsUnionType($returnType)) {
$getterCode .= "${returnJSValueCode}\n";
$getterCode .= " return;\n";
} else {
$getterCode .= " if (${isNull})\n";
$getterCode .= " return;\n";
$getterCode .= $returnJSValueCode . "\n";
}
$getterCode .= "}\n\n";
$implementation{nameSpaceInternal}->add($getterCode);
}
sub GenerateImplementationIndexedPropertyGetterCallback
{
my $interface = shift;
my $hasCustom = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $code = "static void indexedPropertyGetterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info)\n";
$code .= "{\n";
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"Blink\", \"DOMIndexedProperty\");\n";
if ($hasCustom) {
$code .= " ${v8ClassName}::indexedPropertyGetterCustom(index, info);\n";
} else {
$code .= " ${implClassName}V8Internal::indexedPropertyGetter(index, info);\n";
}
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"V8\", \"Execution\");\n";
$code .= "}\n\n";
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateImplementationIndexedPropertySetterCallback
{
my $interface = shift;
my $hasCustom = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $code = "static void indexedPropertySetterCallback(uint32_t index, v8::Local<v8::Value> jsValue, const v8::PropertyCallbackInfo<v8::Value>& info)\n";
$code .= "{\n";
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"Blink\", \"DOMIndexedProperty\");\n";
if ($hasCustom) {
$code .= " ${v8ClassName}::indexedPropertySetterCustom(index, jsValue, info);\n";
} else {
$code .= " ${implClassName}V8Internal::indexedPropertySetter(index, jsValue, info);\n";
}
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"V8\", \"Execution\");\n";
$code .= "}\n\n";
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateImplementationIndexedPropertyDeleterCallback
{
my $interface = shift;
my $hasCustom = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $code = "static void indexedPropertyDeleterCallback(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info)\n";
$code .= "{\n";
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"Blink\", \"DOMIndexedProperty\");\n";
if ($hasCustom) {
$code .= " ${v8ClassName}::indexedPropertyDeleterCustom(index, info);\n";
} else {
$code .= " ${implClassName}V8Internal::indexedPropertyDeleter(index, info);\n";
}
$code .= " TRACE_EVENT_SET_SAMPLING_STATE(\"V8\", \"Execution\");\n";
$code .= "}\n\n";
$implementation{nameSpaceInternal}->add($code);
}
sub GenerateImplementationIndexedPropertySetter
{
my $interface = shift;
my $indexedSetterFunction = shift;
my $implClassName = GetImplName($interface);
my $v8ClassName = GetV8ClassName($interface);
my $methodName = GetImplName($indexedSetterFunction);
my $type = $indexedSetterFunction->parameters->[1]->type;
my $raisesExceptions = $indexedSetterFunction->extendedAttributes->{"RaisesException"};
my $treatNullAs = $indexedSetterFunction->parameters->[1]->extendedAttributes->{"TreatNullAs"};
my $treatUndefinedAs = $indexedSetterFunction->parameters->[1]->extendedAttributes->{"TreatUndefinedAs"};
my $asSetterValue = 0;
my $code = "static void indexedPropertySetter(uint32_t index, v8::Local<v8::Value> jsValue, const v8::PropertyCallbackInfo<v8::Value>& info)\n";
$code .= "{\n";
$code .= " ${implClassName}* collection = ${v8ClassName}::toNative(info.Holder());\n";
$code .= JSValueToNativeStatement($indexedSetterFunction->parameters->[1]->type, $indexedSetterFunction->extendedAttributes, $asSetterValue, "jsValue", "propertyValue", " ", "info.GetIsolate()");
my $extraArguments = "";
if ($raisesExceptions) {
$code .= " ExceptionState es(info.GetIsolate());\n";
$extraArguments = ", es";