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