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