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