1 # Copyright (C) 2010 Apple Inc. All rights reserved.
2 # Copyright (C) 2012 Samsung Electronics
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions
7 # 1. Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # 2. Redistributions in binary form must reproduce the above copyright
10 # notice, this list of conditions and the following disclaimer in the
11 # documentation and/or other materials provided with the distribution.
13 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 # THE POSSIBILITY OF SUCH DAMAGE.
29 package CodeGeneratorTestRunner;
31 use Carp qw<longmess>;
38 my $mess = longmess();
46 my ($class, $codeGenerator, $writeDependencies, $verbose, $idlFilePath) = @_;
49 codeGenerator => $codeGenerator,
50 idlFilePath => $idlFilePath,
53 bless($reference, $class);
63 my ($self, $interface, $outputDir) = @_;
65 foreach my $file ($self->_generateHeaderFile($interface), $self->_generateImplementationFile($interface)) {
66 $$self{codeGenerator}->UpdateFile(File::Spec->catfile($outputDir, $$file{name}), join("", @{$$file{contents}}));
74 return "JS" . _implementationClassName($type);
79 my ($self, $type) = @_;
81 return $$self{codeGenerator}->WK_lcfirst(_implementationClassName($type)) . "Class";
84 sub _parseLicenseBlock
86 my ($fileHandle) = @_;
88 my ($copyright, $readCount, $buffer, $currentCharacter, $previousCharacter);
89 my $startSentinel = "/*";
90 my $lengthOfStartSentinel = length($startSentinel);
91 $readCount = read($fileHandle, $buffer, $lengthOfStartSentinel);
92 return "" if ($readCount < $lengthOfStartSentinel || $buffer ne $startSentinel);
95 while ($readCount = read($fileHandle, $currentCharacter, 1)) {
96 $copyright .= $currentCharacter;
97 return $copyright if $currentCharacter eq "/" && $previousCharacter eq "*";
98 $previousCharacter = $currentCharacter;
104 sub _parseLicenseBlockFromFile
107 open my $fileHandle, "<", $path or die "Failed to open $path for reading: $!";
108 my $licenseBlock = _parseLicenseBlock($fileHandle);
110 return $licenseBlock;
113 sub _defaultLicenseBlock
117 * Copyright (C) 2010 Apple Inc. All rights reserved.
119 * Redistribution and use in source and binary forms, with or without
120 * modification, are permitted provided that the following conditions
122 * 1. Redistributions of source code must retain the above copyright
123 * notice, this list of conditions and the following disclaimer.
124 * 2. Redistributions in binary form must reproduce the above copyright
125 * notice, this list of conditions and the following disclaimer in the
126 * documentation and/or other materials provided with the distribution.
128 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
129 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
130 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
131 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
132 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
133 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
134 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
135 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
136 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
137 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
138 * THE POSSIBILITY OF SUCH DAMAGE.
146 return $self->{licenseBlock} if $self->{licenseBlock};
148 my $licenseBlock = _parseLicenseBlockFromFile($self->{idlFilePath}) || _defaultLicenseBlock();
149 $self->{licenseBlock} = $licenseBlock;
150 return $licenseBlock;
153 sub _generateHeaderFile
155 my ($self, $interface) = @_;
159 my $type = $interface->type;
160 my $className = _className($type);
161 my $implementationClassName = _implementationClassName($type);
162 my $filename = $className . ".h";
164 push(@contents, $self->_licenseBlock());
166 my $parentClassName = _parentClassName($interface);
168 push(@contents, <<EOF);
170 #ifndef ${className}_h
171 #define ${className}_h
173 #include "${parentClassName}.h"
175 push(@contents, <<EOF);
179 class ${implementationClassName};
181 class ${className} : public ${parentClassName} {
183 static JSClassRef @{[$self->_classRefGetter($type)]}();
186 static const JSStaticFunction* staticFunctions();
187 static const JSStaticValue* staticValues();
190 if (my @functions = @{$interface->functions}) {
191 push(@contents, "\n // Functions\n\n");
192 foreach my $function (@functions) {
193 push(@contents, " static JSValueRef @{[$function->name]}(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*);\n");
197 if (my @attributes = @{$interface->attributes}) {
198 push(@contents, "\n // Attributes\n\n");
199 foreach my $attribute (@attributes) {
200 push(@contents, " static JSValueRef @{[$self->_getterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*);\n");
201 push(@contents, " static bool @{[$self->_setterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*);\n") unless $attribute->isReadOnly;
205 push(@contents, <<EOF);
208 ${implementationClassName}* to${implementationClassName}(JSContextRef, JSValueRef);
212 #endif // ${className}_h
215 return { name => $filename, contents => \@contents };
218 sub _generateImplementationFile
220 my ($self, $interface) = @_;
222 my @contentsPrefix = ();
223 my %contentsIncludes = ();
226 my $type = $interface->type;
227 my $className = _className($type);
228 my $implementationClassName = _implementationClassName($type);
229 my $filename = $className . ".cpp";
231 push(@contentsPrefix, $self->_licenseBlock());
233 my $classRefGetter = $self->_classRefGetter($type);
234 my $parentClassName = _parentClassName($interface);
236 $contentsIncludes{"${className}.h"} = 1;
237 $contentsIncludes{"${implementationClassName}.h"} = 1;
239 push(@contentsPrefix, <<EOF);
243 push(@contents, <<EOF);
244 #include <JavaScriptCore/JSRetainPtr.h>
245 #include <wtf/GetPtr.h>
249 ${implementationClassName}* to${implementationClassName}(JSContextRef context, JSValueRef value)
251 if (!context || !value || !${className}::${classRefGetter}() || !JSValueIsObjectOfClass(context, value, ${className}::${classRefGetter}()))
253 return static_cast<${implementationClassName}*>(JSWrapper::unwrap(context, value));
256 JSClassRef ${className}::${classRefGetter}()
258 static JSClassRef jsClass;
260 JSClassDefinition definition = kJSClassDefinitionEmpty;
261 definition.className = "@{[$type->name]}";
262 definition.parentClass = @{[$self->_parentClassRefGetterExpression($interface)]};
263 definition.staticValues = staticValues();
264 definition.staticFunctions = staticFunctions();
267 push(@contents, " definition.initialize = initialize;\n") unless _parentInterface($interface);
268 push(@contents, " definition.finalize = finalize;\n") unless _parentInterface($interface);
270 push(@contents, <<EOF);
271 jsClass = JSClassCreate(&definition);
278 push(@contents, $self->_staticFunctionsGetterImplementation($interface), "\n");
279 push(@contents, $self->_staticValuesGetterImplementation($interface));
281 if (my @functions = @{$interface->functions}) {
282 push(@contents, "\n// Functions\n");
284 foreach my $function (@functions) {
285 push(@contents, <<EOF);
287 JSValueRef ${className}::@{[$function->name]}(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
289 ${implementationClassName}* impl = to${implementationClassName}(context, thisObject);
291 return JSValueMakeUndefined(context);
295 if ($function->extendedAttributes->{"CustomArgumentHandling"}) {
296 $functionCall = "impl->" . $function->name . "(context, argumentCount, arguments, exception)";
299 my @specifiedArguments = @{$function->arguments};
301 $self->_includeHeaders(\%contentsIncludes, $function->type);
303 if ($function->extendedAttributes->{"PassContext"}) {
304 push(@arguments, "context");
307 foreach my $i (0..$#specifiedArguments) {
308 my $argument = $specifiedArguments[$i];
310 $self->_includeHeaders(\%contentsIncludes, $type);
312 push(@contents, " " . $self->_platformTypeVariableDeclaration($argument->type, $argument->name, "arguments[$i]", "argumentCount > $i") . "\n");
314 push(@arguments, $self->_argumentExpression($argument));
317 $functionCall = "impl->" . $function->name . "(" . join(", ", @arguments) . ")";
320 push(@contents, " ${functionCall};\n\n") if $function->type->name eq "void";
321 push(@contents, " return " . $self->_returnExpression($function->type, $functionCall) . ";\n}\n");
325 if (my @attributes = @{$interface->attributes}) {
326 push(@contents, "\n// Attributes\n");
327 foreach my $attribute (@attributes) {
328 $self->_includeHeaders(\%contentsIncludes, $attribute->type);
330 my $getterName = $self->_getterName($attribute);
331 my $getterExpression = "impl->${getterName}()";
333 push(@contents, <<EOF);
335 JSValueRef ${className}::${getterName}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef* exception)
337 ${implementationClassName}* impl = to${implementationClassName}(context, object);
339 return JSValueMakeUndefined(context);
341 return @{[$self->_returnExpression($attribute->type, $getterExpression)]};
345 unless ($attribute->isReadOnly) {
346 push(@contents, <<EOF);
348 bool ${className}::@{[$self->_setterName($attribute)]}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef value, JSValueRef* exception)
350 ${implementationClassName}* impl = to${implementationClassName}(context, object);
356 my $platformValue = $self->_platformTypeConstructor($attribute->type, "value");
358 push(@contents, <<EOF);
359 impl->@{[$self->_setterName($attribute)]}(${platformValue});
368 push(@contents, <<EOF);
374 unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes));
375 unshift(@contents, "#include \"config.h\"\n");
376 unshift(@contents, @contentsPrefix);
378 return { name => $filename, contents => \@contents };
383 my ($self, $attribute) = @_;
385 return $attribute->name;
390 my ($self, $headers, $type) = @_;
392 return unless defined $type;
393 return if $type->name eq "boolean";
394 return if $type->name eq "object";
395 return if $$self{codeGenerator}->IsNonPointerType($type);
396 return if $$self{codeGenerator}->IsStringType($type);
398 $$headers{_className($type) . ".h"} = 1;
399 $$headers{_implementationClassName($type) . ".h"} = 1;
402 sub _implementationClassName
411 my ($interface) = @_;
413 my $parentInterface = _parentInterface($interface);
414 return $parentInterface ? _className($parentInterface) : "JSWrapper";
417 sub _parentClassRefGetterExpression
419 my ($self, $interface) = @_;
421 my $parentInterface = _parentInterface($interface);
422 return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0";
427 my ($interface) = @_;
428 return $interface->parentType;
433 my ($self, $type) = @_;
435 return undef unless defined $type;
437 return "bool" if $type->name eq "boolean";
438 return "JSValueRef" if $type->name eq "object";
439 return "JSRetainPtr<JSStringRef>" if $$self{codeGenerator}->IsStringType($type);
440 return "double" if $$self{codeGenerator}->IsNonPointerType($type);
441 return _implementationClassName($type);
444 sub _platformTypeConstructor
446 my ($self, $type, $argumentName) = @_;
448 return "JSValueToNullableBoolean(context, $argumentName)" if $type->name eq "boolean" && $type->isNullable;
449 return "JSValueToBoolean(context, $argumentName)" if $type->name eq "boolean";
450 return "$argumentName" if $type->name eq "object";
451 return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($type);
452 return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($type);
453 return "to" . _implementationClassName($type) . "(context, $argumentName)";
456 sub _platformTypeVariableDeclaration
458 my ($self, $type, $variableName, $argumentName, $condition) = @_;
460 my $platformType = $self->_platformType($type);
461 my $constructor = $self->_platformTypeConstructor($type, $argumentName);
463 my %nonPointerTypes = (
466 "JSRetainPtr<JSStringRef>" => 1,
471 if ($platformType eq "JSValueRef") {
472 $nullValue = "JSValueMakeUndefined(context)";
473 } elsif (defined $nonPointerTypes{$platformType} && $platformType ne "double") {
474 $nullValue = "$platformType()";
477 $platformType .= "*" unless defined $nonPointerTypes{$platformType};
479 return "$platformType $variableName = $condition && $constructor;" if $condition && $platformType eq "bool";
480 return "$platformType $variableName = $condition ? $constructor : $nullValue;" if $condition;
481 return "$platformType $variableName = $constructor;";
484 sub _returnExpression
486 my ($self, $returnType, $expression) = @_;
488 return "JSValueMakeUndefined(context)" if $returnType->name eq "void";
489 return "JSValueMakeBooleanOrNull(context, ${expression})" if $returnType->name eq "boolean" && $returnType->isNullable;
490 return "JSValueMakeBoolean(context, ${expression})" if $returnType->name eq "boolean";
491 return "${expression}" if $returnType->name eq "object";
492 return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnType);
493 return "JSValueMakeStringOrNull(context, ${expression}.get())" if $$self{codeGenerator}->IsStringType($returnType);
494 return "toJS(context, WTF::getPtr(${expression}))";
497 sub _argumentExpression
499 my ($self, $argument) = @_;
501 my $type = $argument->type;
502 my $name = $argument->name;
504 return "${name}.get()" if $$self{codeGenerator}->IsStringType($type);
510 my ($self, $attribute) = @_;
512 my $name = $attribute->name;
514 return "set" . $$self{codeGenerator}->WK_ucfirst($name);
517 sub _staticFunctionsGetterImplementation
519 my ($self, $interface) = @_;
521 my $mapFunction = sub {
523 my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly);
524 push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
526 return "{ \"$name\", $name, " . join(" | ", @attributes) . " }";
529 return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->functions);
532 sub _staticFunctionsOrValuesGetterImplementation
534 my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $functionsOrAttributes) = @_;
536 my $className = _className($interface->type);
537 my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue);
540 const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
544 my @initializers = map(&$mapFunction, @{$functionsOrAttributes});
545 return $result . " return 0;\n}\n" unless @initializers;
548 static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = {
549 @{[join(",\n ", @initializers)]},
552 return ${functionOrValue}s;
557 sub _staticValuesGetterImplementation
559 my ($self, $interface) = @_;
561 my $mapFunction = sub {
562 return if $_->extendedAttributes->{"NoImplementation"};
564 my $attributeName = $_->name;
565 my $getterName = $self->_getterName($_);
566 my $setterName = $_->isReadOnly ? "0" : $self->_setterName($_);
567 my @attributes = qw(kJSPropertyAttributeDontDelete);
568 push(@attributes, "kJSPropertyAttributeReadOnly") if $_->isReadOnly;
569 push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
571 return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }";
574 return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $interface->attributes);