5f1c59e3c2943f153bd51bebff16cc5cab6988df
[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}";
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 "JSValueToBoolean(context, $argumentName)" if $type eq "boolean";
435     return "$argumentName" if $type->name eq "object";
436     return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($type);
437     return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($type);
438     return "to" . _implementationClassName($type) . "(context, $argumentName)";
439 }
440
441 sub _platformTypeVariableDeclaration
442 {
443     my ($self, $type, $variableName, $argumentName, $condition) = @_;
444
445     my $platformType = $self->_platformType($type);
446     my $constructor = $self->_platformTypeConstructor($type, $argumentName);
447
448     my %nonPointerTypes = (
449         "bool" => 1,
450         "double" => 1,
451         "JSRetainPtr<JSStringRef>" => 1,
452         "JSValueRef" => 1,
453     );
454
455     my $nullValue = "0";
456     if ($platformType eq "JSValueRef") {
457         $nullValue = "JSValueMakeUndefined(context)";
458     } elsif (defined $nonPointerTypes{$platformType} && $platformType ne "double") {
459         $nullValue = "$platformType()";
460     }
461
462     $platformType .= "*" unless defined $nonPointerTypes{$platformType};
463
464     return "$platformType $variableName = $condition && $constructor;" if $condition && $platformType eq "bool";
465     return "$platformType $variableName = $condition ? $constructor : $nullValue;" if $condition;
466     return "$platformType $variableName = $constructor;";
467 }
468
469 sub _returnExpression
470 {
471     my ($self, $returnType, $expression) = @_;
472
473     return "JSValueMakeUndefined(context)" if $returnType->name eq "void";
474     return "JSValueMakeBoolean(context, ${expression})" if $returnType->name eq "boolean";
475     return "${expression}" if $returnType->name eq "object";
476     return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnType);
477     return "JSValueMakeStringOrNull(context, ${expression}.get())" if $$self{codeGenerator}->IsStringType($returnType);
478     return "toJS(context, WTF::getPtr(${expression}))";
479 }
480
481 sub _argumentExpression
482 {
483     my ($self, $argument) = @_;
484
485     my $type = $argument->type;
486     my $name = $argument->name;
487
488     return "${name}.get()" if $$self{codeGenerator}->IsStringType($type);
489     return $name;
490 }
491
492 sub _setterName
493 {
494     my ($self, $attribute) = @_;
495
496     my $name = $attribute->name;
497
498     return "set" . $$self{codeGenerator}->WK_ucfirst($name);
499 }
500
501 sub _staticFunctionsGetterImplementation
502 {
503     my ($self, $interface) = @_;
504
505     my $mapFunction = sub {
506         my $name = $_->name;
507         my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly);
508         push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
509
510         return  "{ \"$name\", $name, " . join(" | ", @attributes) . " }";
511     };
512
513     return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->functions);
514 }
515
516 sub _staticFunctionsOrValuesGetterImplementation
517 {
518     my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $functionsOrAttributes) = @_;
519
520     my $className = _className($interface->type);
521     my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue);
522
523     my $result = <<EOF;
524 const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
525 {
526 EOF
527
528     my @initializers = map(&$mapFunction, @{$functionsOrAttributes});
529     return $result . "    return 0;\n}\n" unless @initializers;
530
531     $result .= <<EOF
532     static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = {
533         @{[join(",\n        ", @initializers)]},
534         ${arrayTerminator}
535     };
536     return ${functionOrValue}s;
537 }
538 EOF
539 }
540
541 sub _staticValuesGetterImplementation
542 {
543     my ($self, $interface) = @_;
544
545     my $mapFunction = sub {
546         return if $_->extendedAttributes->{"NoImplementation"};
547
548         my $attributeName = $_->name;
549         my $getterName = $self->_getterName($_);
550         my $setterName = $_->isReadOnly ? "0" : $self->_setterName($_);
551         my @attributes = qw(kJSPropertyAttributeDontDelete);
552         push(@attributes, "kJSPropertyAttributeReadOnly") if $_->isReadOnly;
553         push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
554
555         return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }";
556     };
557
558     return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $interface->attributes);
559 }
560
561 1;