|
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 EOF |
|
249 my @parameters = (); |
|
250 my @specifiedParameters = @{$function->parameters}; |
|
251 |
|
252 push(@contents, "\n") if scalar @specifiedParameters; |
|
253 |
|
254 $self->_includeHeaders(\%contentsIncludes, $function->signature->type, $function->signature); |
|
255 |
|
256 foreach my $i (0..$#specifiedParameters) { |
|
257 my $parameter = $specifiedParameters[$i]; |
|
258 |
|
259 $self->_includeHeaders(\%contentsIncludes, $idlType, $parameter); |
|
260 |
|
261 push(@contents, " " . $self->_platformTypeVariableDeclaration($parameter, $parameter->name, "arguments[$i]", "argumentCount > $i") . "\n"); |
|
262 |
|
263 push(@parameters, $self->_paramterExpression($parameter)); |
|
264 } |
|
265 |
|
266 my $isVoidReturn = $function->signature->type eq "void"; |
|
267 my $functionName = "impl->" . $function->signature->name; |
|
268 my $functionCall = $functionName . "(" . join(", ", @parameters) . ")"; |
|
269 |
|
270 push(@contents, "\n") unless scalar @specifiedParameters == 1; |
|
271 push(@contents, " ${functionCall};\n\n") if $isVoidReturn; |
|
272 push(@contents, " return " . $self->_returnExpression($function->signature, $functionCall) . ";\n}\n"); |
|
273 } |
|
274 } |
|
275 |
|
276 if (my @attributes = @{$interface->attributes}) { |
|
277 push(@contents, "\n// Attributes\n"); |
|
278 foreach my $attribute (@attributes) { |
|
279 $self->_includeHeaders(\%contentsIncludes, $attribute->signature->type, $attribute->signature); |
|
280 |
|
281 my $getterName = $self->_getterName($attribute); |
|
282 my $getterExpression = "impl->${getterName}()"; |
|
283 |
|
284 push(@contents, <<EOF); |
|
285 |
|
286 JSValueRef ${className}::${getterName}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef* exception) |
|
287 { |
|
288 ${implementationClassName}* impl = to${implementationClassName}(context, object); |
|
289 if (!impl) |
|
290 return JSValueMakeUndefined(context); |
|
291 |
|
292 return @{[$self->_returnExpression($attribute->signature, $getterExpression)]}; |
|
293 } |
|
294 EOF |
|
295 |
|
296 unless ($attribute->type =~ /^readonly/) { |
|
297 push(@contents, <<EOF); |
|
298 |
|
299 bool ${className}::@{[$self->_setterName($attribute)]}(JSContextRef context, JSObjectRef object, JSStringRef, JSValueRef value, JSValueRef* exception) |
|
300 { |
|
301 ${implementationClassName}* impl = to${implementationClassName}(context, object); |
|
302 if (!impl) |
|
303 return false; |
|
304 |
|
305 EOF |
|
306 |
|
307 my $platformValue = $self->_platformTypeConstructor($attribute->signature, "value"); |
|
308 |
|
309 push(@contents, <<EOF); |
|
310 impl->@{[$self->_setterName($attribute)]}(${platformValue}); |
|
311 |
|
312 return true; |
|
313 } |
|
314 EOF |
|
315 } |
|
316 } |
|
317 } |
|
318 |
|
319 push(@contents, <<EOF); |
|
320 |
|
321 } // namespace WTR |
|
322 |
|
323 EOF |
|
324 |
|
325 unshift(@contents, map { "#include \"$_\"\n" } sort keys(%contentsIncludes)); |
|
326 unshift(@contents, @contentsPrefix); |
|
327 |
|
328 return { name => $filename, contents => \@contents }; |
|
329 } |
|
330 |
|
331 sub _getterName |
|
332 { |
|
333 my ($self, $attribute) = @_; |
|
334 |
|
335 my $signature = $attribute->signature; |
|
336 my $name = $signature->name; |
|
337 |
|
338 return $name; |
|
339 } |
|
340 |
|
341 sub _includeHeaders |
|
342 { |
|
343 my ($self, $headers, $idlType, $signature) = @_; |
|
344 |
|
345 return unless defined $idlType; |
|
346 return if $idlType eq "boolean" or $$self{codeGenerator}->IsNonPointerType($idlType); |
|
347 |
|
348 $$headers{_className($idlType) . ".h"} = 1; |
|
349 $$headers{_implementationClassName($idlType) . ".h"} = 1; |
|
350 } |
|
351 |
|
352 sub _implementationClassName |
|
353 { |
|
354 my ($idlType) = @_; |
|
355 |
|
356 return $idlType; |
|
357 } |
|
358 |
|
359 sub _parentClassName |
|
360 { |
|
361 my ($interface) = @_; |
|
362 |
|
363 my $parentInterface = _parentInterface($interface); |
|
364 return $parentInterface ? _className($parentInterface) : "JSWrapper"; |
|
365 } |
|
366 |
|
367 sub _parentClassRefGetterExpression |
|
368 { |
|
369 my ($self, $interface) = @_; |
|
370 |
|
371 my $parentInterface = _parentInterface($interface); |
|
372 return $parentInterface ? $self->_classRefGetter($parentInterface) . "()" : "0"; |
|
373 } |
|
374 |
|
375 sub _parentInterface |
|
376 { |
|
377 my ($interface) = @_; |
|
378 return $interface->parents->[0]; |
|
379 } |
|
380 |
|
381 sub _platformType |
|
382 { |
|
383 my ($self, $idlType, $signature) = @_; |
|
384 |
|
385 return undef unless defined $idlType; |
|
386 |
|
387 return "bool" if $idlType eq "boolean"; |
|
388 return "JSRetainPtr<JSStringRef>" if $$self{codeGenerator}->IsStringType($idlType); |
|
389 return "double" if $$self{codeGenerator}->IsNonPointerType($idlType); |
|
390 return _implementationClassName($idlType); |
|
391 } |
|
392 |
|
393 sub _platformTypeConstructor |
|
394 { |
|
395 my ($self, $signature, $argumentName) = @_; |
|
396 |
|
397 my $idlType = $signature->type; |
|
398 |
|
399 return "JSRetainPtr<JSStringRef>(Adopt, JSValueToStringCopy(context, $argumentName, 0))" if $$self{codeGenerator}->IsStringType($idlType); |
|
400 return "JSValueToBoolean(context, $argumentName)" if $idlType eq "boolean"; |
|
401 return "JSValueToNumber(context, $argumentName, 0)" if $$self{codeGenerator}->IsNonPointerType($idlType); |
|
402 return "to" . _implementationClassName($idlType) . "(context, $argumentName)"; |
|
403 } |
|
404 |
|
405 sub _platformTypeVariableDeclaration |
|
406 { |
|
407 my ($self, $signature, $variableName, $argumentName, $condition) = @_; |
|
408 |
|
409 my $platformType = $self->_platformType($signature->type, $signature); |
|
410 my $constructor = $self->_platformTypeConstructor($signature, $argumentName); |
|
411 |
|
412 my %nonPointerTypes = ( |
|
413 "bool" => 1, |
|
414 "double" => 1, |
|
415 "JSRetainPtr<JSStringRef>" => 1, |
|
416 ); |
|
417 |
|
418 my $nullValue = "0"; |
|
419 $nullValue = "$platformType()" if defined $nonPointerTypes{$platformType} && $platformType ne "double"; |
|
420 |
|
421 $platformType .= "*" unless defined $nonPointerTypes{$platformType}; |
|
422 |
|
423 return "$platformType $variableName = $condition && $constructor;" if $condition && $platformType eq "bool"; |
|
424 return "$platformType $variableName = $condition ? $constructor : $nullValue;" if $condition; |
|
425 return "$platformType $variableName = $constructor;"; |
|
426 } |
|
427 |
|
428 sub _returnExpression |
|
429 { |
|
430 my ($self, $signature, $expression) = @_; |
|
431 |
|
432 my $convertNullStringAttribute = $signature->extendedAttributes->{"ConvertNullStringTo"}; |
|
433 my $nullOrEmptyString = "NullStringAsEmptyString"; |
|
434 $nullOrEmptyString = "NullStringAsNull" if defined $convertNullStringAttribute && $convertNullStringAttribute eq "Null"; |
|
435 |
|
436 my $returnIDLType = $signature->type; |
|
437 |
|
438 return "JSValueMakeUndefined(context)" if $returnIDLType eq "void"; |
|
439 return "JSValueMakeBoolean(context, ${expression})" if $returnIDLType eq "boolean"; |
|
440 return "JSValueMakeNumber(context, ${expression})" if $$self{codeGenerator}->IsNonPointerType($returnIDLType); |
|
441 return "toJS(context, WTF::getPtr(${expression}))"; |
|
442 } |
|
443 |
|
444 sub _paramterExpression |
|
445 { |
|
446 my ($self, $parameter) = @_; |
|
447 |
|
448 my $idlType = $parameter->type; |
|
449 my $name = $parameter->name; |
|
450 |
|
451 return "${name}.get()" if $$self{codeGenerator}->IsStringType($idlType); |
|
452 return $name; |
|
453 } |
|
454 |
|
455 sub _setterName |
|
456 { |
|
457 my ($self, $attribute) = @_; |
|
458 |
|
459 my $name = $attribute->signature->name; |
|
460 |
|
461 return "set" . $$self{codeGenerator}->WK_ucfirst($name); |
|
462 } |
|
463 |
|
464 sub _staticFunctionsGetterImplementation |
|
465 { |
|
466 my ($self, $interface) = @_; |
|
467 |
|
468 my $mapFunction = sub { |
|
469 my $name = $_->signature->name; |
|
470 my @attributes = qw(kJSPropertyAttributeDontDelete kJSPropertyAttributeReadOnly); |
|
471 push(@attributes, "kJSPropertyAttributeDontEnum") if $_->signature->extendedAttributes->{"DontEnum"}; |
|
472 |
|
473 return "{ \"$name\", $name, " . join(" | ", @attributes) . " }"; |
|
474 }; |
|
475 |
|
476 return $self->_staticFunctionsOrValuesGetterImplementation($interface, "function", "{ 0, 0, 0 }", $mapFunction, $interface->functions); |
|
477 } |
|
478 |
|
479 sub _staticFunctionsOrValuesGetterImplementation |
|
480 { |
|
481 my ($self, $interface, $functionOrValue, $arrayTerminator, $mapFunction, $functionsOrAttributes) = @_; |
|
482 |
|
483 my $className = _className($interface->name); |
|
484 my $uppercaseFunctionOrValue = $$self{codeGenerator}->WK_ucfirst($functionOrValue); |
|
485 |
|
486 my $result = <<EOF; |
|
487 const JSStatic${uppercaseFunctionOrValue}* ${className}::static${uppercaseFunctionOrValue}s() |
|
488 { |
|
489 EOF |
|
490 |
|
491 my @initializers = map(&$mapFunction, @{$functionsOrAttributes}); |
|
492 return $result . " return 0;\n}\n" unless @initializers; |
|
493 |
|
494 $result .= <<EOF |
|
495 static const JSStatic${uppercaseFunctionOrValue} ${functionOrValue}s[] = { |
|
496 @{[join(",\n ", @initializers)]}, |
|
497 ${arrayTerminator} |
|
498 }; |
|
499 return ${functionOrValue}s; |
|
500 } |
|
501 EOF |
|
502 } |
|
503 |
|
504 sub _staticValuesGetterImplementation |
|
505 { |
|
506 my ($self, $interface) = @_; |
|
507 |
|
508 my $mapFunction = sub { |
|
509 return if $_->signature->extendedAttributes->{"NoImplementation"}; |
|
510 |
|
511 my $attributeName = $_->signature->name; |
|
512 my $attributeIsReadonly = $_->type =~ /^readonly/; |
|
513 my $getterName = $self->_getterName($_); |
|
514 my $setterName = $attributeIsReadonly ? "0" : $self->_setterName($_); |
|
515 my @attributes = qw(kJSPropertyAttributeDontDelete); |
|
516 push(@attributes, "kJSPropertyAttributeReadOnly") if $attributeIsReadonly; |
|
517 push(@attributes, "kJSPropertyAttributeDontEnum") if $_->signature->extendedAttributes->{"DontEnum"}; |
|
518 |
|
519 return "{ \"$attributeName\", $getterName, $setterName, " . join(" | ", @attributes) . " }"; |
|
520 }; |
|
521 |
|
522 return $self->_staticFunctionsOrValuesGetterImplementation($interface, "value", "{ 0, 0, 0, 0 }", $mapFunction, $interface->attributes); |
|
523 } |
|
524 |
|
525 1; |