[GTK][a11y] Signal state-changed:selected is not emitted for listbox elements when...
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / Bindings / CodeGeneratorTestRunner.pm
1 # Copyright (C) 2010 Apple Inc. All rights reserved.
2 # Copyright (C) 2012 Samsung Electronics
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions
6 # are met:
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.
12 #
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.
24
25 use strict;
26 use warnings;
27 use File::Spec;
28
29 package CodeGeneratorTestRunner;
30
31 use Carp qw<longmess>;
32 use Data::Dumper;
33
34 sub assert
35 {
36     my $message = shift;
37     
38     my $mess = longmess();
39     print Dumper($mess);
40
41     die $message;
42 }
43
44 sub new
45 {
46     my ($class, $codeGenerator, $writeDependencies, $verbose, $idlFilePath) = @_;
47
48     my $reference = {
49         codeGenerator => $codeGenerator,
50         idlFilePath => $idlFilePath,
51     };
52
53     bless($reference, $class);
54     return $reference;
55 }
56
57 sub GenerateInterface
58 {
59 }
60
61 sub WriteData
62 {
63     my ($self, $interface, $outputDir) = @_;
64
65     foreach my $file ($self->_generateHeaderFile($interface), $self->_generateImplementationFile($interface)) {
66         $$self{codeGenerator}->UpdateFile(File::Spec->catfile($outputDir, $$file{name}), join("", @{$$file{contents}}));
67     }
68 }
69
70 sub _className
71 {
72     my ($type) = @_;
73
74     return "JS" . _implementationClassName($type);
75 }
76
77 sub _classRefGetter
78 {
79     my ($self, $type) = @_;
80
81     return $$self{codeGenerator}->WK_lcfirst(_implementationClassName($type)) . "Class";
82 }
83
84 sub _constantGetterFunctionName
85 {
86     my ($constantName) = @_;
87     return "get".$constantName;
88 }
89
90 sub _parseLicenseBlock
91 {
92     my ($fileHandle) = @_;
93
94     my ($copyright, $readCount, $buffer, $currentCharacter, $previousCharacter);
95     my $startSentinel = "/*";
96     my $lengthOfStartSentinel = length($startSentinel);
97     $readCount = read($fileHandle, $buffer, $lengthOfStartSentinel);
98     return "" if ($readCount < $lengthOfStartSentinel || $buffer ne $startSentinel);
99     $copyright = $buffer;
100
101     while ($readCount = read($fileHandle, $currentCharacter, 1)) {
102         $copyright .= $currentCharacter;
103         return $copyright if $currentCharacter eq "/" && $previousCharacter eq "*";
104         $previousCharacter = $currentCharacter;
105     }
106
107     return "";
108 }
109
110 sub _parseLicenseBlockFromFile
111 {
112     my ($path) = @_;
113     open my $fileHandle, "<", $path or die "Failed to open $path for reading: $!";
114     my $licenseBlock = _parseLicenseBlock($fileHandle);
115     close($fileHandle);
116     return $licenseBlock;
117 }
118
119 sub _defaultLicenseBlock
120 {
121     return <<EOF;
122 /*
123  * Copyright (C) 2010 Apple Inc. All rights reserved.
124  *
125  * Redistribution and use in source and binary forms, with or without
126  * modification, are permitted provided that the following conditions
127  * are met:
128  * 1. Redistributions of source code must retain the above copyright
129  *    notice, this list of conditions and the following disclaimer.
130  * 2. Redistributions in binary form must reproduce the above copyright
131  *    notice, this list of conditions and the following disclaimer in the
132  *    documentation and/or other materials provided with the distribution.
133  *
134  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
135  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
136  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
137  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
138  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
139  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
140  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
141  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
142  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
143  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
144  * THE POSSIBILITY OF SUCH DAMAGE.
145  */
146 EOF
147 }
148
149 sub _licenseBlock
150 {
151     my ($self) = @_;
152     return $self->{licenseBlock} if $self->{licenseBlock};
153
154     my $licenseBlock = _parseLicenseBlockFromFile($self->{idlFilePath}) || _defaultLicenseBlock();
155     $self->{licenseBlock} = $licenseBlock;
156     return $licenseBlock;
157 }
158
159 sub _generateHeaderFile
160 {
161     my ($self, $interface) = @_;
162
163     my @contents = ();
164
165     my $type = $interface->type;
166     my $className = _className($type);
167     my $implementationClassName = _implementationClassName($type);
168     my $filename = $className . ".h";
169
170     push(@contents, $self->_licenseBlock());
171
172     my $parentClassName = _parentClassName($interface);
173
174     push(@contents, <<EOF);
175
176 #ifndef ${className}_h
177 #define ${className}_h
178
179 #include "${parentClassName}.h"
180 EOF
181     push(@contents, <<EOF);
182
183 namespace WTR {
184
185 class ${implementationClassName};
186
187 class ${className} : public ${parentClassName} {
188 public:
189     static JSClassRef @{[$self->_classRefGetter($type)]}();
190
191 private:
192     static const JSStaticFunction* staticFunctions();
193     static const JSStaticValue* staticValues();
194 EOF
195
196     if (my @operations = @{$interface->operations}) {
197         push(@contents, "\n    // Functions\n\n");
198         foreach my $operation (@operations) {
199             push(@contents, "    static JSValueRef @{[$operation->name]}(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*);\n");
200         }
201     }
202
203     if (my @attributes = @{$interface->attributes}) {
204         push(@contents, "\n    // Attributes\n\n");
205         foreach my $attribute (@attributes) {
206             push(@contents, "    static JSValueRef @{[$self->_getterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*);\n");
207             push(@contents, "    static bool @{[$self->_setterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*);\n") unless $attribute->isReadOnly;
208         }
209     }
210
211     push(@contents, <<EOF);
212 };
213     
214 ${implementationClassName}* to${implementationClassName}(JSContextRef, JSValueRef);
215
216 } // namespace WTR
217
218 #endif // ${className}_h
219 EOF
220
221     return { name => $filename, contents => \@contents };
222 }
223
224 sub _generateImplementationFile
225 {
226     my ($self, $interface) = @_;
227
228     my @contentsPrefix = ();
229     my %contentsIncludes = ();
230     my @contents = ();
231
232     my $type = $interface->type;
233     my $className = _className($type);
234     my $implementationClassName = _implementationClassName($type);
235     my $filename = $className . ".cpp";
236
237     push(@contentsPrefix, $self->_licenseBlock());
238
239     my $classRefGetter = $self->_classRefGetter($type);
240     my $parentClassName = _parentClassName($interface);
241
242     $contentsIncludes{"${className}.h"} = 1;
243     $contentsIncludes{"${implementationClassName}.h"} = 1;
244
245     push(@contentsPrefix, <<EOF);
246
247 EOF
248
249     push(@contents, <<EOF);
250 #include <JavaScriptCore/JSRetainPtr.h>
251 #include <wtf/GetPtr.h>
252 #include <wtf/MathExtras.h>
253
254 namespace WTR {
255
256 ${implementationClassName}* to${implementationClassName}(JSContextRef context, JSValueRef value)
257 {
258     if (!context || !value || !${className}::${classRefGetter}() || !JSValueIsObjectOfClass(context, value, ${className}::${classRefGetter}()))
259         return 0;
260     return static_cast<${implementationClassName}*>(JSWrapper::unwrap(context, value));
261 }
262
263 JSClassRef ${className}::${classRefGetter}()
264 {
265     static const JSClassRef jsClass = [] {
266         JSClassDefinition definition = kJSClassDefinitionEmpty;
267         definition.className = "@{[$type->name]}";
268         definition.parentClass = @{[$self->_parentClassRefGetterExpression($interface)]};
269         definition.staticValues = staticValues();
270         definition.staticFunctions = staticFunctions();
271 EOF
272
273     push(@contents, "        definition.initialize = initialize;\n") unless _parentInterface($interface);
274     push(@contents, "        definition.finalize = finalize;\n") unless _parentInterface($interface);
275
276     push(@contents, <<EOF);
277         return JSClassCreate(&definition);
278     }();
279     return jsClass;
280 }
281
282 EOF
283
284     if (my @constants = @{$interface->constants}) {
285         push(@contents, "\n// Constants\n");
286
287         foreach my $constant (@constants) {
288             $self->_includeHeaders(\%contentsIncludes, $constant->type);
289
290             my $getterName = _constantGetterFunctionName($self->_getterName($constant));
291             my $getterExpression = "impl->${getterName}()";
292             my $value = $constant->value;
293
294             push(@contents, <<EOF);
295 static JSValueRef $getterName(JSContextRef context, JSObjectRef, JSStringRef, JSValueRef*)
296 {
297     return JSValueMakeNumber(context, $value);
298 }
299
300 EOF
301         }
302
303     }
304
305     push(@contents, $self->_staticFunctionsGetterImplementation($interface), "\n");
306     push(@contents, $self->_staticValuesGetterImplementation($interface));
307
308     if (my @operations = @{$interface->operations}) {
309         push(@contents, "\n// Functions\n");
310
311         foreach my $operation (@operations) {
312             push(@contents, <<EOF);
313
314 JSValueRef ${className}::@{[$operation->name]}(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
315 {
316     ${implementationClassName}* impl = to${implementationClassName}(context, thisObject);
317     if (!impl)
318         return JSValueMakeUndefined(context);
319
320 EOF
321             my $functionCall;
322             if ($operation->extendedAttributes->{"CustomArgumentHandling"}) {
323                 $functionCall = "impl->" . $operation->name . "(context, argumentCount, arguments, exception)";
324             } else {
325                 my @arguments = ();
326                 my @specifiedArguments = @{$operation->arguments};
327
328                 $self->_includeHeaders(\%contentsIncludes, $operation->type);
329
330                 if ($operation->extendedAttributes->{"PassContext"}) {
331                     push(@arguments, "context");
332                 }
333
334                 foreach my $i (0..$#specifiedArguments) {
335                     my $argument = $specifiedArguments[$i];
336
337                     $self->_includeHeaders(\%contentsIncludes, $type);
338
339                     push(@contents, "    " . $self->_platformTypeVariableDeclaration($argument->type, $argument->name, "arguments[$i]", "argumentCount > $i") . "\n");
340                     
341                     push(@arguments, $self->_argumentExpression($argument));
342                 }
343
344                 $functionCall = "impl->" . $operation->name . "(" . join(", ", @arguments) . ")";
345             }
346             
347             push(@contents, "    ${functionCall};\n\n") if $operation->type->name eq "undefined";
348             push(@contents, "    return " . $self->_returnExpression($operation->type, $functionCall) . ";\n}\n");
349         }
350     }
351
352     if (my @attributes = @{$interface->attributes}) {
353         push(@contents, "\n// Attributes\n");
354         foreach my $attribute (@attributes) {
355             $self->_includeHeaders(\%contentsIncludes, $attribute->type);
356
357             my $getterName = $self->_getterName($attribute);
358             my $getterExpression = "impl->${getterName}()";
359
360             push(@contents, <<EOF);
361
362 JSValueRef ${className}::${getterName}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef* exception)
363 {
364     ${implementationClassName}* impl = to${implementationClassName}(context, object);
365     if (!impl)
366         return JSValueMakeUndefined(context);
367
368     return @{[$self->_returnExpression($attribute->type, $getterExpression)]};
369 }
370 EOF
371
372             unless ($attribute->isReadOnly) {
373                 push(@contents, <<EOF);
374
375 bool ${className}::@{[$self->_setterName($attribute)]}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef value, JSValueRef* exception)
376 {
377     ${implementationClassName}* impl = to${implementationClassName}(context, object);
378     if (!impl)
379         return false;
380
381 EOF
382
383                 my $platformValue = $self->_platformTypeConstructor($attribute->type, "value");
384
385                 push(@contents, <<EOF);
386     impl->@{[$self->_setterName($attribute)]}(${platformValue});
387
388     return true;
389 }
390 EOF
391             }
392         }
393     }
394
395     push(@contents, <<EOF);
396
397 } // namespace WTR
398
399 EOF
400
401     unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes));
402     my $conditionalString = $$self{codeGenerator}->GenerateConditionalString($interface);
403     unshift(@contents, "\n#if ${conditionalString}\n\n") if $conditionalString;
404
405     unshift(@contents, "#include \"config.h\"\n");
406     unshift(@contents, @contentsPrefix);
407
408     push(@contents, "\n#endif // ${conditionalString}\n") if $conditionalString;
409
410     return { name => $filename, contents => \@contents };
411 }
412
413 sub _getterName
414 {
415     my ($self, $attribute) = @_;
416
417     return $attribute->name;
418 }
419
420 sub _includeHeaders
421 {
422     my ($self, $headers, $type) = @_;
423
424     return unless defined $type;
425     return if $type->name eq "boolean";
426     return if $type->name eq "object";
427     return if $$self{codeGenerator}->IsPrimitiveType($type);
428     return if $$self{codeGenerator}->IsStringType($type);
429
430     $$headers{_className($type) . ".h"} = 1;
431     $$headers{_implementationClassName($type) . ".h"} = 1;
432 }
433
434 sub _implementationClassName
435 {
436     my ($type) = @_;
437
438     return $type->name;
439 }
440
441 sub _parentClassName
442 {
443     my ($interface) = @_;
444
445     my $parentInterface = _parentInterface($interface);
446     return $parentInterface ? _className($parentInterface) : "JSWrapper";
447 }
448
449 sub _parentClassRefGetterExpression
450 {
451     my ($self, $interface) = @_;
452
453     my $parentInterface = _parentInterface($interface);
454     return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0";
455 }
456
457 sub _parentInterface
458 {
459     my ($interface) = @_;
460     return $interface->parentType;
461 }
462
463 sub _nativeNumericType
464 {
465     my ($self, $type) = @_;
466     my %numericTypeHash = (
467         "byte" => "int8_t",
468         "long long" => "int64_t",
469         "long" => "int32_t",
470         "octet" => "uint8_t",
471         "short" => "int16_t",
472         "unsigned long long" => "uint64_t",
473         "unsigned long" => "uint32_t",
474         "unsigned short" => "uint16_t",
475         "float" => "float",
476         "unrestricted float" => "float",
477         "double" => "double",
478         "unrestricted double" => "double",
479     );
480     my $result = $numericTypeHash{$type->name};
481     return ($result) ? $result : "double";
482 }
483
484 sub _platformType
485 {
486     my ($self, $type) = @_;
487
488     return undef unless defined $type;
489
490     return "bool" if $type->name eq "boolean";
491     return "JSValueRef" if $type->name eq "object";
492     return "JSRetainPtr<JSStringRef>" if $$self{codeGenerator}->IsStringType($type);
493     return $self->_nativeNumericType($type) if $$self{codeGenerator}->IsPrimitiveType($type);
494     return _implementationClassName($type);
495 }
496
497 sub _platformTypeConstructor
498 {
499     my ($self, $type, $argumentName) = @_;
500
501     return "toOptionalBool(context, $argumentName)" if $type->name eq "boolean" && $type->isNullable;
502     return "JSValueToBoolean(context, $argumentName)" if $type->name eq "boolean";
503     return "$argumentName" if $type->name eq "object";
504     return "createJSString(context, $argumentName)" if $$self{codeGenerator}->IsStringType($type);
505     return "toOptionalDouble(context, $argumentName)" if $$self{codeGenerator}->IsPrimitiveType($type) && $type->isNullable;
506     if ($$self{codeGenerator}->IsPrimitiveType($type)) {
507         my $convertToDouble = "JSValueToNumber(context, $argumentName, nullptr)";
508         my $nativeNumericType = $self->_nativeNumericType($type);
509         return "clampTo<$nativeNumericType>($convertToDouble)" if $nativeNumericType ne "double";
510         return $convertToDouble;
511     }
512     return "to" . _implementationClassName($type) . "(context, $argumentName)";
513 }
514
515 sub _platformTypeVariableDeclaration
516 {
517     my ($self, $type, $variableName, $argumentName, $condition) = @_;
518
519     my $platformType = $self->_platformType($type);
520     my $constructor = $self->_platformTypeConstructor($type, $argumentName);
521
522     my %nonPointerTypes = (
523         "bool" => 1,
524         "JSRetainPtr<JSStringRef>" => 1,
525         "JSValueRef" => 1,
526     );
527
528     my $nullValue = "nullptr";
529     if ($platformType eq "JSValueRef") {
530         $nullValue = "JSValueMakeUndefined(context)";
531     } elsif (defined $nonPointerTypes{$platformType} || $$self{codeGenerator}->IsNumericType($type)) {
532         $nullValue = $type->isNullable ? "std::nullopt" : "$platformType()";
533     }
534
535     return "bool $variableName = $condition && $constructor;" if $condition && $platformType eq "bool";
536     return "auto $variableName = $condition ? $constructor : $nullValue;" if $condition;
537     return "auto $variableName = $constructor;";
538 }
539
540 sub _returnExpression
541 {
542     my ($self, $returnType, $expression) = @_;
543
544     return "JSValueMakeUndefined(context)" if $returnType->name eq "undefined";
545     return "makeValue(context, ${expression})" if $returnType->name eq "boolean" && $returnType->isNullable;
546     return "JSValueMakeBoolean(context, ${expression})" if $returnType->name eq "boolean";
547     return "${expression}" if $returnType->name eq "object";
548     return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsPrimitiveType($returnType);
549     return "makeValue(context, ${expression}.get())" if $$self{codeGenerator}->IsStringType($returnType);
550     return "toJS(context, WTF::getPtr(${expression}))";
551 }
552
553 sub _argumentExpression
554 {
555     my ($self, $argument) = @_;
556
557     my $type = $argument->type;
558     my $name = $argument->name;
559
560     return "${name}.get()" if $$self{codeGenerator}->IsStringType($type);
561     return $name;
562 }
563
564 sub _setterName
565 {
566     my ($self, $attribute) = @_;
567
568     my $name = $attribute->name;
569
570     return "set" . $$self{codeGenerator}->WK_ucfirst($name);
571 }
572
573 sub _staticFunctionsGetterImplementation
574 {
575     my ($self, $interface) = @_;
576
577     my $mapFunction = sub {
578         my $name = $_->name;
579         my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly);
580         push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
581
582         return  "{ \"$name\", $name, " . join(" | ", @attributes) . " }";
583     };
584
585     return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->operations);
586 }
587
588 sub _staticFunctionsOrValuesGetterImplementation
589 {
590     my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $operationsOrAttributes) = @_;
591
592     my $className = _className($interface->type);
593     my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue);
594
595     my $result = <<EOF;
596 const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
597 {
598 EOF
599
600     my @initializers = map(&$mapFunction, @{$operationsOrAttributes});
601     return $result . "    return 0;\n}\n" unless @initializers;
602
603     $result .= <<EOF
604     static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = {
605         @{[join(",\n        ", @initializers)]},
606         ${arrayTerminator}
607     };
608     return ${functionOrValue}s;
609 }
610 EOF
611 }
612
613 sub _staticValuesGetterImplementation
614 {
615     my ($self, $interface) = @_;
616
617     my $mapFunction = sub {
618         return if $_->extendedAttributes->{"NoImplementation"};
619
620         my $isReadOnly = 1;
621         my $getterName;
622
623         if (ref($_) eq "IDLAttribute") {
624             $isReadOnly = $_->isReadOnly;
625             $getterName = $self->_getterName($_);
626         } elsif (ref($_) eq "IDLConstant") {
627             $getterName = _constantGetterFunctionName($self->_getterName($_));
628         }
629
630         my $attributeName = $_->name;
631
632         my $setterName = $isReadOnly ? "0" : $self->_setterName($_);
633         my @attributes = qw(kJSPropertyAttributeDontDelete);
634         push(@attributes, "kJSPropertyAttributeReadOnly") if $isReadOnly;
635         push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
636
637         return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }";
638     };
639
640     my $attributesAndConstants = [];
641     push (@$attributesAndConstants, @{ $interface->attributes });
642     if ($interface->constants) {
643         push (@$attributesAndConstants, @{ $interface->constants });
644     }
645
646     return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $attributesAndConstants);
647 }
648
649 1;