Home | History | Annotate | Download | only in Bindings
      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                 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 
    291 JSValueRef ${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 }
    299 EOF
    300 
    301             unless ($attribute->type =~ /^readonly/) {
    302                 push(@contents, <<EOF);
    303 
    304 bool ${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 
    310 EOF
    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 }
    319 EOF
    320             }
    321         }
    322     }
    323 
    324     push(@contents, <<EOF);
    325 
    326 } // namespace WTR
    327 
    328 EOF
    329 
    330     unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes));
    331     unshift(@contents, @contentsPrefix);
    332 
    333     return { name => $filename, contents => \@contents };
    334 }
    335 
    336 sub _getterName
    337 {
    338     my ($self, $attribute) = @_;
    339 
    340     my $signature = $attribute->signature;
    341     my $name = $signature->name;
    342 
    343     return $name;
    344 }
    345 
    346 sub _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 
    360 sub _implementationClassName
    361 {
    362     my ($idlType) = @_;
    363 
    364     return $idlType;
    365 }
    366 
    367 sub _parentClassName
    368 {
    369     my ($interface) = @_;
    370 
    371     my $parentInterface = _parentInterface($interface);
    372     return $parentInterface ? _className($parentInterface) : "JSWrapper";
    373 }
    374 
    375 sub _parentClassRefGetterExpression
    376 {
    377     my ($self, $interface) = @_;
    378 
    379     my $parentInterface = _parentInterface($interface);
    380     return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0";
    381 }
    382 
    383 sub _parentInterface
    384 {
    385     my ($interface) = @_;
    386     return $interface->parents->[0];
    387 }
    388 
    389 sub _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 
    402 sub _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 
    415 sub _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 
    439 sub _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 
    453 sub _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 
    464 sub _setterName
    465 {
    466     my ($self, $attribute) = @_;
    467 
    468     my $name = $attribute->signature->name;
    469 
    470     return "set" . $$self{codeGenerator}->WK_ucfirst($name);
    471 }
    472 
    473 sub _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 
    488 sub _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;
    496 const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
    497 {
    498 EOF
    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 }
    510 EOF
    511 }
    512 
    513 sub _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 
    534 1;
    535