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