• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
24use strict;
25use warnings;
26use File::Spec;
27
28package CodeGeneratorTestRunner;
29
30sub 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
43sub GenerateModule
44{
45}
46
47sub 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
58sub finish
59{
60}
61
62sub _className
63{
64    my ($idlType) = @_;
65
66    return "JS" . _implementationClassName($idlType);
67}
68
69sub _classRefGetter
70{
71    my ($self, $idlType) = @_;
72    return $$self{codeGenerator}->WK_lcfirst(_implementationClassName($idlType)) . "Class";
73}
74
75sub _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 */
106EOF
107}
108
109sub _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"
130EOF
131    push(@contents, <<EOF);
132
133namespace WTR {
134
135class ${implementationClassName};
136
137class ${className} : public ${parentClassName} {
138public:
139    static JSClassRef @{[$self->_classRefGetter($idlType)]}();
140
141private:
142    static const JSStaticFunction* staticFunctions();
143    static const JSStaticValue* staticValues();
144EOF
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
169EOF
170
171    return { name => $filename, contents => \@contents };
172}
173
174sub _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
197EOF
198
199    push(@contents, <<EOF);
200#include <JavaScriptCore/JSRetainPtr.h>
201#include <wtf/GetPtr.h>
202
203namespace 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
212JSClassRef ${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();
221EOF
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
232EOF
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
243JSValueRef ${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
249EOF
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                if ($function->signature->extendedAttributes->{"PassContext"}) {
260                    push(@parameters, "context");
261                }
262
263                foreach my $i (0..$#specifiedParameters) {
264                    my $parameter = $specifiedParameters[$i];
265
266                    $self->_includeHeaders(\%contentsIncludes, $idlType, $parameter);
267
268                    push(@contents, "    " . $self->_platformTypeVariableDeclaration($parameter, $parameter->name, "arguments[$i]", "argumentCount > $i") . "\n");
269
270                    push(@parameters, $self->_parameterExpression($parameter));
271                }
272
273                $functionCall = "impl->" . $function->signature->name . "(" . join(", ", @parameters) . ")";
274            }
275
276            push(@contents, "    ${functionCall};\n\n") if $function->signature->type eq "void";
277            push(@contents, "    return " . $self->_returnExpression($function->signature, $functionCall) . ";\n}\n");
278        }
279    }
280
281    if (my @attributes = @{$interface->attributes}) {
282        push(@contents, "\n// Attributes\n");
283        foreach my $attribute (@attributes) {
284            $self->_includeHeaders(\%contentsIncludes, $attribute->signature->type, $attribute->signature);
285
286            my $getterName = $self->_getterName($attribute);
287            my $getterExpression = "impl->${getterName}()";
288
289            push(@contents, <<EOF);
290
291JSValueRef ${className}::${getterName}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef* exception)
292{
293    ${implementationClassName}* impl = to${implementationClassName}(context, object);
294    if (!impl)
295        return JSValueMakeUndefined(context);
296
297    return @{[$self->_returnExpression($attribute->signature, $getterExpression)]};
298}
299EOF
300
301            unless ($attribute->type =~ /^readonly/) {
302                push(@contents, <<EOF);
303
304bool ${className}::@{[$self->_setterName($attribute)]}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef value, JSValueRef* exception)
305{
306    ${implementationClassName}* impl = to${implementationClassName}(context, object);
307    if (!impl)
308        return false;
309
310EOF
311
312                my $platformValue = $self->_platformTypeConstructor($attribute->signature, "value");
313
314                push(@contents, <<EOF);
315    impl->@{[$self->_setterName($attribute)]}(${platformValue});
316
317    return true;
318}
319EOF
320            }
321        }
322    }
323
324    push(@contents, <<EOF);
325
326} // namespace WTR
327
328EOF
329
330    unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes));
331    unshift(@contents, @contentsPrefix);
332
333    return { name => $filename, contents => \@contents };
334}
335
336sub _getterName
337{
338    my ($self, $attribute) = @_;
339
340    my $signature = $attribute->signature;
341    my $name = $signature->name;
342
343    return $name;
344}
345
346sub _includeHeaders
347{
348    my ($self, $headers, $idlType, $signature) = @_;
349
350    return unless defined $idlType;
351    return if $idlType eq "boolean";
352    return if $idlType eq "object";
353    return if $$self{codeGenerator}->IsNonPointerType($idlType);
354    return if $$self{codeGenerator}->IsStringType($idlType);
355
356    $$headers{_className($idlType) . ".h"} = 1;
357    $$headers{_implementationClassName($idlType) . ".h"} = 1;
358}
359
360sub _implementationClassName
361{
362    my ($idlType) = @_;
363
364    return $idlType;
365}
366
367sub _parentClassName
368{
369    my ($interface) = @_;
370
371    my $parentInterface = _parentInterface($interface);
372    return $parentInterface ? _className($parentInterface) : "JSWrapper";
373}
374
375sub _parentClassRefGetterExpression
376{
377    my ($self, $interface) = @_;
378
379    my $parentInterface = _parentInterface($interface);
380    return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0";
381}
382
383sub _parentInterface
384{
385    my ($interface) = @_;
386    return $interface->parents->[0];
387}
388
389sub _platformType
390{
391    my ($self, $idlType, $signature) = @_;
392
393    return undef unless defined $idlType;
394
395    return "bool" if $idlType eq "boolean";
396    return "JSValueRef" if $idlType eq "object";
397    return "JSRetainPtr<JSStringRef>" if $$self{codeGenerator}->IsStringType($idlType);
398    return "double" if $$self{codeGenerator}->IsNonPointerType($idlType);
399    return _implementationClassName($idlType);
400}
401
402sub _platformTypeConstructor
403{
404    my ($self, $signature, $argumentName) = @_;
405
406    my $idlType = $signature->type;
407
408    return "JSValueToBoolean(context, $argumentName)" if $idlType eq "boolean";
409    return "$argumentName" if $idlType eq "object";
410    return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($idlType);
411    return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($idlType);
412    return "to" . _implementationClassName($idlType) . "(context, $argumentName)";
413}
414
415sub _platformTypeVariableDeclaration
416{
417    my ($self, $signature, $variableName, $argumentName, $condition) = @_;
418
419    my $platformType = $self->_platformType($signature->type, $signature);
420    my $constructor = $self->_platformTypeConstructor($signature, $argumentName);
421
422    my %nonPointerTypes = (
423        "bool" => 1,
424        "double" => 1,
425        "JSRetainPtr<JSStringRef>" => 1,
426        "JSValueRef" => 1,
427    );
428
429    my $nullValue = "0";
430    $nullValue = "$platformType()" if defined $nonPointerTypes{$platformType} && $platformType ne "double";
431
432    $platformType .= "*" unless defined $nonPointerTypes{$platformType};
433
434    return "$platformType $variableName = $condition && $constructor;" if $condition && $platformType eq "bool";
435    return "$platformType $variableName = $condition ? $constructor : $nullValue;" if $condition;
436    return "$platformType $variableName = $constructor;";
437}
438
439sub _returnExpression
440{
441    my ($self, $signature, $expression) = @_;
442
443    my $returnIDLType = $signature->type;
444
445    return "JSValueMakeUndefined(context)" if $returnIDLType eq "void";
446    return "JSValueMakeBoolean(context, ${expression})" if $returnIDLType eq "boolean";
447    return "${expression}" if $returnIDLType eq "object";
448    return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnIDLType);
449    return "JSValueMakeStringOrNull(context, ${expression}.get())" if $$self{codeGenerator}->IsStringType($returnIDLType);
450    return "toJS(context, WTF::getPtr(${expression}))";
451}
452
453sub _parameterExpression
454{
455    my ($self, $parameter) = @_;
456
457    my $idlType = $parameter->type;
458    my $name = $parameter->name;
459
460    return "${name}.get()" if $$self{codeGenerator}->IsStringType($idlType);
461    return $name;
462}
463
464sub _setterName
465{
466    my ($self, $attribute) = @_;
467
468    my $name = $attribute->signature->name;
469
470    return "set" . $$self{codeGenerator}->WK_ucfirst($name);
471}
472
473sub _staticFunctionsGetterImplementation
474{
475    my ($self, $interface) = @_;
476
477    my $mapFunction = sub {
478        my $name = $_->signature->name;
479        my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly);
480        push(@attributes, "kJSPropertyAttributeDontEnum") if $_->signature->extendedAttributes->{"DontEnum"};
481
482        return  "{ \"$name\", $name, " . join(" | ", @attributes) . " }";
483    };
484
485    return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->functions);
486}
487
488sub _staticFunctionsOrValuesGetterImplementation
489{
490    my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $functionsOrAttributes) = @_;
491
492    my $className = _className($interface->name);
493    my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue);
494
495    my $result = <<EOF;
496const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
497{
498EOF
499
500    my @initializers = map(&$mapFunction, @{$functionsOrAttributes});
501    return $result . "    return 0;\n}\n" unless @initializers;
502
503    $result .= <<EOF
504    static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = {
505        @{[join(",\n        ", @initializers)]},
506        ${arrayTerminator}
507    };
508    return ${functionOrValue}s;
509}
510EOF
511}
512
513sub _staticValuesGetterImplementation
514{
515    my ($self, $interface) = @_;
516
517    my $mapFunction = sub {
518        return if $_->signature->extendedAttributes->{"NoImplementation"};
519
520        my $attributeName = $_->signature->name;
521        my $attributeIsReadonly = $_->type =~ /^readonly/;
522        my $getterName = $self->_getterName($_);
523        my $setterName = $attributeIsReadonly ? "0" : $self->_setterName($_);
524        my @attributes = qw(kJSPropertyAttributeDontDelete);
525        push(@attributes, "kJSPropertyAttributeReadOnly") if $attributeIsReadonly;
526        push(@attributes, "kJSPropertyAttributeDontEnum") if $_->signature->extendedAttributes->{"DontEnum"};
527
528        return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }";
529    };
530
531    return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $interface->attributes);
532}
533
5341;
535