summaryrefslogtreecommitdiff
path: root/Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm')
-rw-r--r--Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm577
1 files changed, 577 insertions, 0 deletions
diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm b/Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm
new file mode 100644
index 000000000..98d37dab9
--- /dev/null
+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/CodeGeneratorTestRunner.pm
@@ -0,0 +1,577 @@
+# Copyright (C) 2010 Apple Inc. All rights reserved.
+# Copyright (C) 2012 Samsung Electronics
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+use strict;
+use warnings;
+use File::Spec;
+
+package CodeGeneratorTestRunner;
+
+use Carp qw<longmess>;
+use Data::Dumper;
+
+sub assert
+{
+ my $message = shift;
+
+ my $mess = longmess();
+ print Dumper($mess);
+
+ die $message;
+}
+
+sub new
+{
+ my ($class, $codeGenerator, $writeDependencies, $verbose, $idlFilePath) = @_;
+
+ my $reference = {
+ codeGenerator => $codeGenerator,
+ idlFilePath => $idlFilePath,
+ };
+
+ bless($reference, $class);
+ return $reference;
+}
+
+sub GenerateInterface
+{
+}
+
+sub WriteData
+{
+ my ($self, $interface, $outputDir) = @_;
+
+ foreach my $file ($self->_generateHeaderFile($interface), $self->_generateImplementationFile($interface)) {
+ $$self{codeGenerator}->UpdateFile(File::Spec->catfile($outputDir, $$file{name}), join("", @{$$file{contents}}));
+ }
+}
+
+sub _className
+{
+ my ($type) = @_;
+
+ return "JS" . _implementationClassName($type);
+}
+
+sub _classRefGetter
+{
+ my ($self, $type) = @_;
+
+ return $$self{codeGenerator}->WK_lcfirst(_implementationClassName($type)) . "Class";
+}
+
+sub _parseLicenseBlock
+{
+ my ($fileHandle) = @_;
+
+ my ($copyright, $readCount, $buffer, $currentCharacter, $previousCharacter);
+ my $startSentinel = "/*";
+ my $lengthOfStartSentinel = length($startSentinel);
+ $readCount = read($fileHandle, $buffer, $lengthOfStartSentinel);
+ return "" if ($readCount < $lengthOfStartSentinel || $buffer ne $startSentinel);
+ $copyright = $buffer;
+
+ while ($readCount = read($fileHandle, $currentCharacter, 1)) {
+ $copyright .= $currentCharacter;
+ return $copyright if $currentCharacter eq "/" && $previousCharacter eq "*";
+ $previousCharacter = $currentCharacter;
+ }
+
+ return "";
+}
+
+sub _parseLicenseBlockFromFile
+{
+ my ($path) = @_;
+ open my $fileHandle, "<", $path or die "Failed to open $path for reading: $!";
+ my $licenseBlock = _parseLicenseBlock($fileHandle);
+ close($fileHandle);
+ return $licenseBlock;
+}
+
+sub _defaultLicenseBlock
+{
+ return <<EOF;
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+EOF
+}
+
+sub _licenseBlock
+{
+ my ($self) = @_;
+ return $self->{licenseBlock} if $self->{licenseBlock};
+
+ my $licenseBlock = _parseLicenseBlockFromFile($self->{idlFilePath}) || _defaultLicenseBlock();
+ $self->{licenseBlock} = $licenseBlock;
+ return $licenseBlock;
+}
+
+sub _generateHeaderFile
+{
+ my ($self, $interface) = @_;
+
+ my @contents = ();
+
+ my $type = $interface->type;
+ my $className = _className($type);
+ my $implementationClassName = _implementationClassName($type);
+ my $filename = $className . ".h";
+
+ push(@contents, $self->_licenseBlock());
+
+ my $parentClassName = _parentClassName($interface);
+
+ push(@contents, <<EOF);
+
+#ifndef ${className}_h
+#define ${className}_h
+
+#include "${parentClassName}.h"
+EOF
+ push(@contents, <<EOF);
+
+namespace WTR {
+
+class ${implementationClassName};
+
+class ${className} : public ${parentClassName} {
+public:
+ static JSClassRef @{[$self->_classRefGetter($type)]}();
+
+private:
+ static const JSStaticFunction* staticFunctions();
+ static const JSStaticValue* staticValues();
+EOF
+
+ if (my @functions = @{$interface->functions}) {
+ push(@contents, "\n // Functions\n\n");
+ foreach my $function (@functions) {
+ push(@contents, " static JSValueRef @{[$function->name]}(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*);\n");
+ }
+ }
+
+ if (my @attributes = @{$interface->attributes}) {
+ push(@contents, "\n // Attributes\n\n");
+ foreach my $attribute (@attributes) {
+ push(@contents, " static JSValueRef @{[$self->_getterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*);\n");
+ push(@contents, " static bool @{[$self->_setterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*);\n") unless $attribute->isReadOnly;
+ }
+ }
+
+ push(@contents, <<EOF);
+};
+
+${implementationClassName}* to${implementationClassName}(JSContextRef, JSValueRef);
+
+} // namespace WTR
+
+#endif // ${className}_h
+EOF
+
+ return { name => $filename, contents => \@contents };
+}
+
+sub _generateImplementationFile
+{
+ my ($self, $interface) = @_;
+
+ my @contentsPrefix = ();
+ my %contentsIncludes = ();
+ my @contents = ();
+
+ my $type = $interface->type;
+ my $className = _className($type);
+ my $implementationClassName = _implementationClassName($type);
+ my $filename = $className . ".cpp";
+
+ push(@contentsPrefix, $self->_licenseBlock());
+
+ my $classRefGetter = $self->_classRefGetter($type);
+ my $parentClassName = _parentClassName($interface);
+
+ $contentsIncludes{"${className}.h"} = 1;
+ $contentsIncludes{"${implementationClassName}.h"} = 1;
+
+ push(@contentsPrefix, <<EOF);
+
+EOF
+
+ push(@contents, <<EOF);
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <wtf/GetPtr.h>
+
+namespace WTR {
+
+${implementationClassName}* to${implementationClassName}(JSContextRef context, JSValueRef value)
+{
+ if (!context || !value || !${className}::${classRefGetter}() || !JSValueIsObjectOfClass(context, value, ${className}::${classRefGetter}()))
+ return 0;
+ return static_cast<${implementationClassName}*>(JSWrapper::unwrap(context, value));
+}
+
+JSClassRef ${className}::${classRefGetter}()
+{
+ static JSClassRef jsClass;
+ if (!jsClass) {
+ JSClassDefinition definition = kJSClassDefinitionEmpty;
+ definition.className = "@{[$type->name]}";
+ definition.parentClass = @{[$self->_parentClassRefGetterExpression($interface)]};
+ definition.staticValues = staticValues();
+ definition.staticFunctions = staticFunctions();
+EOF
+
+ push(@contents, " definition.initialize = initialize;\n") unless _parentInterface($interface);
+ push(@contents, " definition.finalize = finalize;\n") unless _parentInterface($interface);
+
+ push(@contents, <<EOF);
+ jsClass = JSClassCreate(&definition);
+ }
+ return jsClass;
+}
+
+EOF
+
+ push(@contents, $self->_staticFunctionsGetterImplementation($interface), "\n");
+ push(@contents, $self->_staticValuesGetterImplementation($interface));
+
+ if (my @functions = @{$interface->functions}) {
+ push(@contents, "\n// Functions\n");
+
+ foreach my $function (@functions) {
+ push(@contents, <<EOF);
+
+JSValueRef ${className}::@{[$function->name]}(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ ${implementationClassName}* impl = to${implementationClassName}(context, thisObject);
+ if (!impl)
+ return JSValueMakeUndefined(context);
+
+EOF
+ my $functionCall;
+ if ($function->extendedAttributes->{"CustomArgumentHandling"}) {
+ $functionCall = "impl->" . $function->name . "(context, argumentCount, arguments, exception)";
+ } else {
+ my @arguments = ();
+ my @specifiedArguments = @{$function->arguments};
+
+ $self->_includeHeaders(\%contentsIncludes, $function->type);
+
+ if ($function->extendedAttributes->{"PassContext"}) {
+ push(@arguments, "context");
+ }
+
+ foreach my $i (0..$#specifiedArguments) {
+ my $argument = $specifiedArguments[$i];
+
+ $self->_includeHeaders(\%contentsIncludes, $type);
+
+ push(@contents, " " . $self->_platformTypeVariableDeclaration($argument->type, $argument->name, "arguments[$i]", "argumentCount > $i") . "\n");
+
+ push(@arguments, $self->_argumentExpression($argument));
+ }
+
+ $functionCall = "impl->" . $function->name . "(" . join(", ", @arguments) . ")";
+ }
+
+ push(@contents, " ${functionCall};\n\n") if $function->type->name eq "void";
+ push(@contents, " return " . $self->_returnExpression($function->type, $functionCall) . ";\n}\n");
+ }
+ }
+
+ if (my @attributes = @{$interface->attributes}) {
+ push(@contents, "\n// Attributes\n");
+ foreach my $attribute (@attributes) {
+ $self->_includeHeaders(\%contentsIncludes, $attribute->type);
+
+ my $getterName = $self->_getterName($attribute);
+ my $getterExpression = "impl->${getterName}()";
+
+ push(@contents, <<EOF);
+
+JSValueRef ${className}::${getterName}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef* exception)
+{
+ ${implementationClassName}* impl = to${implementationClassName}(context, object);
+ if (!impl)
+ return JSValueMakeUndefined(context);
+
+ return @{[$self->_returnExpression($attribute->type, $getterExpression)]};
+}
+EOF
+
+ unless ($attribute->isReadOnly) {
+ push(@contents, <<EOF);
+
+bool ${className}::@{[$self->_setterName($attribute)]}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef value, JSValueRef* exception)
+{
+ ${implementationClassName}* impl = to${implementationClassName}(context, object);
+ if (!impl)
+ return false;
+
+EOF
+
+ my $platformValue = $self->_platformTypeConstructor($attribute->type, "value");
+
+ push(@contents, <<EOF);
+ impl->@{[$self->_setterName($attribute)]}(${platformValue});
+
+ return true;
+}
+EOF
+ }
+ }
+ }
+
+ push(@contents, <<EOF);
+
+} // namespace WTR
+
+EOF
+
+ unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes));
+ unshift(@contents, "#include \"config.h\"\n");
+ unshift(@contents, @contentsPrefix);
+
+ return { name => $filename, contents => \@contents };
+}
+
+sub _getterName
+{
+ my ($self, $attribute) = @_;
+
+ return $attribute->name;
+}
+
+sub _includeHeaders
+{
+ my ($self, $headers, $type) = @_;
+
+ return unless defined $type;
+ return if $type->name eq "boolean";
+ return if $type->name eq "object";
+ return if $$self{codeGenerator}->IsNonPointerType($type);
+ return if $$self{codeGenerator}->IsStringType($type);
+
+ $$headers{_className($type) . ".h"} = 1;
+ $$headers{_implementationClassName($type) . ".h"} = 1;
+}
+
+sub _implementationClassName
+{
+ my ($type) = @_;
+
+ return $type->name;
+}
+
+sub _parentClassName
+{
+ my ($interface) = @_;
+
+ my $parentInterface = _parentInterface($interface);
+ return $parentInterface ? _className($parentInterface) : "JSWrapper";
+}
+
+sub _parentClassRefGetterExpression
+{
+ my ($self, $interface) = @_;
+
+ my $parentInterface = _parentInterface($interface);
+ return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0";
+}
+
+sub _parentInterface
+{
+ my ($interface) = @_;
+ return $interface->parentType;
+}
+
+sub _platformType
+{
+ my ($self, $type) = @_;
+
+ return undef unless defined $type;
+
+ return "bool" if $type->name eq "boolean";
+ return "JSValueRef" if $type->name eq "object";
+ return "JSRetainPtr<JSStringRef>" if $$self{codeGenerator}->IsStringType($type);
+ return "double" if $$self{codeGenerator}->IsNonPointerType($type);
+ return _implementationClassName($type);
+}
+
+sub _platformTypeConstructor
+{
+ my ($self, $type, $argumentName) = @_;
+
+ return "JSValueToNullableBoolean(context, $argumentName)" if $type->name eq "boolean" && $type->isNullable;
+ return "JSValueToBoolean(context, $argumentName)" if $type->name eq "boolean";
+ return "$argumentName" if $type->name eq "object";
+ return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($type);
+ return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($type);
+ return "to" . _implementationClassName($type) . "(context, $argumentName)";
+}
+
+sub _platformTypeVariableDeclaration
+{
+ my ($self, $type, $variableName, $argumentName, $condition) = @_;
+
+ my $platformType = $self->_platformType($type);
+ my $constructor = $self->_platformTypeConstructor($type, $argumentName);
+
+ my %nonPointerTypes = (
+ "bool" => 1,
+ "double" => 1,
+ "JSRetainPtr<JSStringRef>" => 1,
+ "JSValueRef" => 1,
+ );
+
+ my $nullValue = "0";
+ if ($platformType eq "JSValueRef") {
+ $nullValue = "JSValueMakeUndefined(context)";
+ } elsif (defined $nonPointerTypes{$platformType} && $platformType ne "double") {
+ $nullValue = "$platformType()";
+ }
+
+ $platformType .= "*" unless defined $nonPointerTypes{$platformType};
+
+ return "$platformType $variableName = $condition && $constructor;" if $condition && $platformType eq "bool";
+ return "$platformType $variableName = $condition ? $constructor : $nullValue;" if $condition;
+ return "$platformType $variableName = $constructor;";
+}
+
+sub _returnExpression
+{
+ my ($self, $returnType, $expression) = @_;
+
+ return "JSValueMakeUndefined(context)" if $returnType->name eq "void";
+ return "JSValueMakeBooleanOrNull(context, ${expression})" if $returnType->name eq "boolean" && $returnType->isNullable;
+ return "JSValueMakeBoolean(context, ${expression})" if $returnType->name eq "boolean";
+ return "${expression}" if $returnType->name eq "object";
+ return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnType);
+ return "JSValueMakeStringOrNull(context, ${expression}.get())" if $$self{codeGenerator}->IsStringType($returnType);
+ return "toJS(context, WTF::getPtr(${expression}))";
+}
+
+sub _argumentExpression
+{
+ my ($self, $argument) = @_;
+
+ my $type = $argument->type;
+ my $name = $argument->name;
+
+ return "${name}.get()" if $$self{codeGenerator}->IsStringType($type);
+ return $name;
+}
+
+sub _setterName
+{
+ my ($self, $attribute) = @_;
+
+ my $name = $attribute->name;
+
+ return "set" . $$self{codeGenerator}->WK_ucfirst($name);
+}
+
+sub _staticFunctionsGetterImplementation
+{
+ my ($self, $interface) = @_;
+
+ my $mapFunction = sub {
+ my $name = $_->name;
+ my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly);
+ push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
+
+ return "{ \"$name\", $name, " . join(" | ", @attributes) . " }";
+ };
+
+ return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->functions);
+}
+
+sub _staticFunctionsOrValuesGetterImplementation
+{
+ my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $functionsOrAttributes) = @_;
+
+ my $className = _className($interface->type);
+ my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue);
+
+ my $result = <<EOF;
+const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
+{
+EOF
+
+ my @initializers = map(&$mapFunction, @{$functionsOrAttributes});
+ return $result . " return 0;\n}\n" unless @initializers;
+
+ $result .= <<EOF
+ static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = {
+ @{[join(",\n ", @initializers)]},
+ ${arrayTerminator}
+ };
+ return ${functionOrValue}s;
+}
+EOF
+}
+
+sub _staticValuesGetterImplementation
+{
+ my ($self, $interface) = @_;
+
+ my $mapFunction = sub {
+ return if $_->extendedAttributes->{"NoImplementation"};
+
+ my $attributeName = $_->name;
+ my $getterName = $self->_getterName($_);
+ my $setterName = $_->isReadOnly ? "0" : $self->_setterName($_);
+ my @attributes = qw(kJSPropertyAttributeDontDelete);
+ push(@attributes, "kJSPropertyAttributeReadOnly") if $_->isReadOnly;
+ push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
+
+ return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }";
+ };
+
+ return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $interface->attributes);
+}
+
+1;