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