1 # Copyright (C) 2016 Apple Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
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.
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.
28 package CodeGeneratorDumpRenderTree;
32 my ($class, $codeGenerator, $writeDependencies, $verbose, $idlFilePath) = @_;
35 codeGenerator => $codeGenerator,
36 idlFilePath => $idlFilePath,
39 bless($reference, $class);
49 my ($self, $interface, $outputDir) = @_;
51 foreach my $file ($self->_generateHeaderFile($interface), $self->_generateImplementationFile($interface)) {
52 $$self{codeGenerator}->UpdateFile(File::Spec->catfile($outputDir, $$file{name}), join("", @{$$file{contents}}));
60 return "JS" . _implementationClassName($type);
65 my ($self, $type) = @_;
67 return $$self{codeGenerator}->WK_lcfirst(_implementationClassName($type)) . "Class";
70 sub _parseLicenseBlock
72 my ($fileHandle) = @_;
74 my ($copyright, $readCount, $buffer, $currentCharacter, $previousCharacter);
75 my $startSentinel = "/*";
76 my $lengthOfStartSentinel = length($startSentinel);
77 $readCount = read($fileHandle, $buffer, $lengthOfStartSentinel);
78 return "" if ($readCount < $lengthOfStartSentinel || $buffer ne $startSentinel);
81 while ($readCount = read($fileHandle, $currentCharacter, 1)) {
82 $copyright .= $currentCharacter;
83 return $copyright if $currentCharacter eq "/" && $previousCharacter eq "*";
84 $previousCharacter = $currentCharacter;
90 sub _parseLicenseBlockFromFile
93 open my $fileHandle, "<", $path or die "Failed to open $path for reading: $!";
94 my $licenseBlock = _parseLicenseBlock($fileHandle);
99 sub _defaultLicenseBlock
103 * Copyright (C) 2016 Apple Inc. All rights reserved.
105 * Redistribution and use in source and binary forms, with or without
106 * modification, are permitted provided that the following conditions
108 * 1. Redistributions of source code must retain the above copyright
109 * notice, this list of conditions and the following disclaimer.
110 * 2. Redistributions in binary form must reproduce the above copyright
111 * notice, this list of conditions and the following disclaimer in the
112 * documentation and/or other materials provided with the distribution.
114 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
115 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
116 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
117 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
118 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
119 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
120 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
121 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
122 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
123 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
124 * THE POSSIBILITY OF SUCH DAMAGE.
132 return $self->{licenseBlock} if $self->{licenseBlock};
134 my $licenseBlock = _parseLicenseBlockFromFile($self->{idlFilePath}) || _defaultLicenseBlock();
135 $self->{licenseBlock} = $licenseBlock;
136 return $licenseBlock;
139 sub _generateHeaderFile
141 my ($self, $interface) = @_;
145 my $type = $interface->type;
146 my $className = _className($type);
147 my $implementationClassName = _implementationClassName($type);
148 my $filename = $className . ".h";
150 push(@contents, $self->_licenseBlock());
152 my $parentClassName = _parentClassName($interface);
154 push(@contents, <<EOF);
156 #ifndef ${className}_h
157 #define ${className}_h
159 #include "${parentClassName}.h"
161 push(@contents, <<EOF);
165 class ${implementationClassName};
167 class ${className} : public ${parentClassName} {
169 static JSClassRef @{[$self->_classRefGetter($type)]}();
172 static const JSStaticFunction* staticFunctions();
173 static const JSStaticValue* staticValues();
176 if (my @functions = @{$interface->functions}) {
177 push(@contents, "\n // Functions\n\n");
178 foreach my $function (@functions) {
179 push(@contents, " static JSValueRef @{[$function->name]}(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef*);\n");
183 if (my @attributes = @{$interface->attributes}) {
184 push(@contents, "\n // Attributes\n\n");
185 foreach my $attribute (@attributes) {
186 push(@contents, " static JSValueRef @{[$self->_getterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef*);\n");
187 push(@contents, " static bool @{[$self->_setterName($attribute)]}(JSContextRef, JSObjectRef, JSStringRef, JSValueRef, JSValueRef*);\n") unless $attribute->isReadOnly;
191 push(@contents, <<EOF);
194 ${implementationClassName}* to${implementationClassName}(JSContextRef, JSValueRef);
198 #endif // ${className}_h
201 return { name => $filename, contents => \@contents };
204 sub _generateImplementationFile
206 my ($self, $interface) = @_;
208 my @contentsPrefix = ();
209 my %contentsIncludes = ();
212 my $type = $interface->type;
213 my $className = _className($type);
214 my $implementationClassName = _implementationClassName($type);
215 my $filename = $className . ".cpp";
217 push(@contentsPrefix, $self->_licenseBlock());
219 my $classRefGetter = $self->_classRefGetter($type);
220 my $parentClassName = _parentClassName($interface);
222 $contentsIncludes{"${className}.h"} = 1;
223 $contentsIncludes{"${implementationClassName}.h"} = 1;
225 push(@contentsPrefix, <<EOF);
229 push(@contents, <<EOF);
230 #include <JavaScriptCore/JSRetainPtr.h>
231 #include <wtf/GetPtr.h>
235 ${implementationClassName}* to${implementationClassName}(JSContextRef context, JSValueRef value)
237 if (!context || !value || !${className}::${classRefGetter}() || !JSValueIsObjectOfClass(context, value, ${className}::${classRefGetter}()))
239 return static_cast<${implementationClassName}*>(JSWrapper::unwrap(context, value));
242 JSClassRef ${className}::${classRefGetter}()
244 static JSClassRef jsClass;
246 JSClassDefinition definition = kJSClassDefinitionEmpty;
247 definition.className = "@{[$type->name]}";
248 definition.parentClass = @{[$self->_parentClassRefGetterExpression($interface)]};
249 definition.staticValues = staticValues();
250 definition.staticFunctions = staticFunctions();
253 push(@contents, " definition.initialize = initialize;\n") unless _parentInterface($interface);
254 push(@contents, " definition.finalize = finalize;\n") unless _parentInterface($interface);
256 push(@contents, <<EOF);
257 jsClass = JSClassCreate(&definition);
264 push(@contents, $self->_staticFunctionsGetterImplementation($interface), "\n");
265 push(@contents, $self->_staticValuesGetterImplementation($interface));
267 if (my @functions = @{$interface->functions}) {
268 push(@contents, "\n// Functions\n");
270 foreach my $function (@functions) {
271 push(@contents, <<EOF);
273 JSValueRef ${className}::@{[$function->name]}(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
275 ${implementationClassName}* impl = to${implementationClassName}(context, thisObject);
277 return JSValueMakeUndefined(context);
281 if ($function->extendedAttributes->{"CustomArgumentHandling"}) {
282 $functionCall = "impl->" . $function->name . "(context, argumentCount, arguments, exception)";
285 my @specifiedArguments = @{$function->arguments};
287 $self->_includeHeaders(\%contentsIncludes, $function->type);
289 if ($function->extendedAttributes->{"PassContext"}) {
290 push(@arguments, "context");
293 foreach my $i (0..$#specifiedArguments) {
294 my $argument = $specifiedArguments[$i];
296 $self->_includeHeaders(\%contentsIncludes, $type);
298 push(@contents, " " . $self->_platformTypeVariableDeclaration($argument->type, $argument->name, "arguments[$i]", "argumentCount > $i") . "\n");
300 push(@arguments, $self->_argumentExpression($argument));
303 $functionCall = "impl->" . $function->name . "(" . join(", ", @arguments) . ")";
306 push(@contents, " ${functionCall};\n\n") if $function->type->name eq "void";
307 push(@contents, " return " . $self->_returnExpression($function->type, $functionCall) . ";\n}\n");
311 if (my @attributes = @{$interface->attributes}) {
312 push(@contents, "\n// Attributes\n");
313 foreach my $attribute (@attributes) {
314 $self->_includeHeaders(\%contentsIncludes, $attribute->type);
316 my $getterName = $self->_getterName($attribute);
317 my $getterExpression = "impl->${getterName}()";
319 push(@contents, <<EOF);
321 JSValueRef ${className}::${getterName}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef* exception)
323 ${implementationClassName}* impl = to${implementationClassName}(context, object);
325 return JSValueMakeUndefined(context);
327 return @{[$self->_returnExpression($attribute->type, $getterExpression)]};
331 unless ($attribute->isReadOnly) {
332 push(@contents, <<EOF);
334 bool ${className}::@{[$self->_setterName($attribute)]}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef value, JSValueRef* exception)
336 ${implementationClassName}* impl = to${implementationClassName}(context, object);
342 my $platformValue = $self->_platformTypeConstructor($attribute->type, "value");
344 push(@contents, <<EOF);
345 impl->@{[$self->_setterName($attribute)]}(${platformValue});
354 push(@contents, <<EOF);
360 unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes));
361 unshift(@contents, "#include \"config.h\"\n");
362 unshift(@contents, @contentsPrefix);
364 return { name => $filename, contents => \@contents };
369 my ($self, $attribute) = @_;
371 return $attribute->name;
376 my ($self, $headers, $type) = @_;
378 return unless defined $type;
379 return if $type->name eq "boolean";
380 return if $type->name eq "object";
381 return if $$self{codeGenerator}->IsNonPointerType($type);
382 return if $$self{codeGenerator}->IsStringType($type);
384 $$headers{_className($type) . ".h"} = 1;
385 $$headers{_implementationClassName($type) . ".h"} = 1;
388 sub _implementationClassName
397 my ($interface) = @_;
399 my $parentInterface = _parentInterface($interface);
400 return $parentInterface ? _className($parentInterface) : "JSWrapper";
403 sub _parentClassRefGetterExpression
405 my ($self, $interface) = @_;
407 my $parentInterface = _parentInterface($interface);
408 return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0";
413 my ($interface) = @_;
414 return $interface->parentType;
419 my ($self, $type) = @_;
421 return undef unless defined $type;
423 return "bool" if $type->name eq "boolean";
424 return "JSValueRef" if $type->name eq "object";
425 return "JSRetainPtr<JSStringRef>" if $$self{codeGenerator}->IsStringType($type);
426 return "double" if $$self{codeGenerator}->IsNonPointerType($type);
427 return _implementationClassName($type);
430 sub _platformTypeConstructor
432 my ($self, $type, $argumentName) = @_;
434 return "JSValueToNullableBoolean(context, $argumentName)" if $type->name eq "boolean" && $type->isNullable;
435 return "JSValueToBoolean(context, $argumentName)" if $type->name eq "boolean";
436 return "$argumentName" if $type->name eq "object";
437 return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($type);
438 return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($type);
439 return "to" . _implementationClassName($type) . "(context, $argumentName)";
442 sub _platformTypeVariableDeclaration
444 my ($self, $type, $variableName, $argumentName, $condition) = @_;
446 my $platformType = $self->_platformType($type);
447 my $constructor = $self->_platformTypeConstructor($type, $argumentName);
449 my %nonPointerTypes = (
452 "JSRetainPtr<JSStringRef>" => 1,
457 if ($platformType eq "JSValueRef") {
458 $nullValue = "JSValueMakeUndefined(context)";
459 } elsif (defined $nonPointerTypes{$platformType} && $platformType ne "double") {
460 $nullValue = "$platformType()";
463 $platformType .= "*" unless defined $nonPointerTypes{$platformType};
465 return "$platformType $variableName = $condition && $constructor;" if $condition && $platformType eq "bool";
466 return "$platformType $variableName = $condition ? $constructor : $nullValue;" if $condition;
467 return "$platformType $variableName = $constructor;";
470 sub _returnExpression
472 my ($self, $returnType, $expression) = @_;
474 return "JSValueMakeUndefined(context)" if $returnType->name eq "void";
475 return "JSValueMakeBooleanOrNull(context, ${expression})" if $returnType->name eq "boolean" && $returnType->isNullable;
476 return "JSValueMakeBoolean(context, ${expression})" if $returnType->name eq "boolean";
477 return "${expression}" if $returnType->name eq "object";
478 return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnType);
479 return "JSValueMakeStringOrNull(context, ${expression}.get())" if $$self{codeGenerator}->IsStringType($returnType);
480 return "toJS(context, WTF::getPtr(${expression}))";
483 sub _argumentExpression
485 my ($self, $argument) = @_;
487 my $type = $argument->type;
488 my $name = $argument->name;
490 return "${name}.get()" if $$self{codeGenerator}->IsStringType($type);
496 my ($self, $attribute) = @_;
498 my $name = $attribute->name;
500 return "set" . $$self{codeGenerator}->WK_ucfirst($name);
503 sub _staticFunctionsGetterImplementation
505 my ($self, $interface) = @_;
507 my $mapFunction = sub {
509 my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly);
510 push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
512 return "{ \"$name\", $name, " . join(" | ", @attributes) . " }";
515 return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->functions);
518 sub _staticFunctionsOrValuesGetterImplementation
520 my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $functionsOrAttributes) = @_;
522 my $className = _className($interface->type);
523 my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue);
526 const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s()
530 my @initializers = map(&$mapFunction, @{$functionsOrAttributes});
531 return $result . " return 0;\n}\n" unless @initializers;
534 static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = {
535 @{[join(",\n ", @initializers)]},
538 return ${functionOrValue}s;
543 sub _staticValuesGetterImplementation
545 my ($self, $interface) = @_;
547 my $mapFunction = sub {
548 return if $_->extendedAttributes->{"NoImplementation"};
550 my $attributeName = $_->name;
551 my $getterName = $self->_getterName($_);
552 my $setterName = $_->isReadOnly ? "0" : $self->_setterName($_);
553 my @attributes = qw(kJSPropertyAttributeDontDelete);
554 push(@attributes, "kJSPropertyAttributeReadOnly") if $_->isReadOnly;
555 push(@attributes, "kJSPropertyAttributeDontEnum") if $_->extendedAttributes->{"DontEnum"};
557 return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }";
560 return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $interface->attributes);