Changeset 185739 in webkit
- Timestamp:
- Jun 19, 2015, 12:33:35 AM (11 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 6 edited
-
ChangeLog (modified) (1 diff)
-
bindings/js/JSDOMPromise.cpp (modified) (2 diffs)
-
bindings/js/JSDOMPromise.h (modified) (1 diff)
-
bindings/scripts/CodeGeneratorJS.pm (modified) (5 diffs)
-
bindings/scripts/test/JS/JSTestObj.cpp (modified) (4 diffs)
-
bindings/scripts/test/TestObj.idl (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r185733 r185739 1 2015-06-19 Youenn Fablet <youenn.fablet@crf.canon.fr> 2 3 Bindings generator should generate code to catch exception and reject promises for Promise-based APIs 4 https://bugs.webkit.org/show_bug.cgi?id=146060 5 6 Reviewed by Darin Adler. 7 8 The binding generator splits the function that binds JS to the DOM class implementation in two for functions returning promise. 9 The first function, called from JS, is responsible of casting this to the expected JSXXX class. 10 If casting fails, an exception is raised. Otherwise, it calls the second function. 11 After calling the second function, it checks whether an exception is raised, in which case it returns a rejected promise. 12 The second function is responsible of argument conversion and calling the DOM class function. 13 14 Covered by expectations and AudioContext promise still working. 15 A test case is added for a promise returning function taking a typed argument as input (if argument value cannot be typed, the promise is rejected). 16 A second test case is a promise-returning function that can raise an exception. In that case the DOMException is used as rejection value. 17 18 As can be seen from generated code, this generalized code adds a mandatory check (is there an exception?) at the end of the function. 19 This check is done even in cases we know there will be no exception. 20 This may be covered by another patch if this optimization is thought useful enough. 21 22 * bindings/js/JSDOMPromise.cpp: 23 (WebCore::rejectPromiseWithExceptionIfAny): Utility method for the binding code. 24 (WebCore::callPromiseFunction): Ditto. 25 * bindings/js/JSDOMPromise.h: 26 * bindings/scripts/CodeGeneratorJS.pm: 27 (GenerateImplementation): 28 (GenerateFunctionCastedThis): Extracted from GenerateImplementationFunctionCall to reuse it in case of promise-returning functions. 29 (GenerateImplementationFunctionCall): 30 (GenerateCallbackImplementation): Deleted. 31 * bindings/scripts/test/JS/JSTestObj.cpp: 32 (WebCore::jsTestObjPrototypeFunctionTestPromiseFunction): 33 (WebCore::jsTestObjPrototypeFunctionTestPromiseFunctionPromise): 34 (WebCore::jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgument): 35 (WebCore::jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgumentPromise): 36 (WebCore::jsTestObjPrototypeFunctionTestPromiseFunctionWithException): 37 (WebCore::jsTestObjPrototypeFunctionTestPromiseFunctionWithExceptionPromise): 38 * bindings/scripts/test/TestObj.idl: 39 1 40 2015-06-18 Jeremy Jones <jeremyj@apple.com> 2 41 -
trunk/Source/WebCore/bindings/js/JSDOMPromise.cpp
r185407 r185739 30 30 31 31 #include "ExceptionCode.h" 32 #include <runtime/Exception.h> 32 33 33 34 using namespace JSC; … … 63 64 } 64 65 66 void rejectPromiseWithExceptionIfAny(JSC::ExecState& state, JSDOMGlobalObject& globalObject, JSPromiseDeferred& promiseDeferred) 67 { 68 if (!state.hadException()) 69 return; 70 71 JSValue error = state.exception()->value(); 72 state.clearException(); 73 74 DeferredWrapper(&state, &globalObject, &promiseDeferred).reject(error); 75 } 76 65 77 } 66 78 -
trunk/Source/WebCore/bindings/js/JSDOMPromise.h
r185407 r185739 58 58 }; 59 59 60 void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&); 61 62 template<class JSClassName> 63 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state, JSClassName& jsObject, JSC::EncodedJSValue promiseFunction(JSC::ExecState*, JSClassName*, JSC::JSPromiseDeferred*)) 64 { 65 JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, jsObject.globalObject()); 66 promiseFunction(&state, &jsObject, promiseDeferred); 67 68 rejectPromiseWithExceptionIfAny(state, *jsObject.globalObject(), *promiseDeferred); 69 ASSERT(!state.hadException()); 70 return promiseDeferred->promise(); 71 } 72 60 73 template <typename Value, typename Error> 61 74 class DOMPromise { -
trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
r185493 r185739 2781 2781 my $functionImplementationName = $function->signature->extendedAttributes->{"ImplementedAs"} || $codeGenerator->WK_lcfirst($function->signature->name); 2782 2782 2783 push(@implContent, "EncodedJSValue JSC_HOST_CALL ${functionName}(ExecState* exec)\n"); 2783 if (IsReturningPromise($function) && !$isCustom) { 2784 AddToImplIncludes("JSDOMPromise.h"); 2785 2786 push(@implContent, "static inline EncodedJSValue ${functionName}Promise(ExecState*, " . $className . "*, JSPromiseDeferred*);\n"); 2787 push(@implContent, "EncodedJSValue JSC_HOST_CALL ${functionName}(ExecState* exec)\n"); 2788 push(@implContent, "{\n"); 2789 2790 GenerateFunctionCastedThis($interface, $interfaceName, $className, $function); 2791 push(@implContent, " return JSValue::encode(callPromiseFunction(*exec, *castedThis, ${functionName}Promise));\n"); 2792 2793 push(@implContent, "}\n"); 2794 push(@implContent, "\nstatic inline EncodedJSValue ${functionName}Promise(ExecState* exec, " . $className . "* castedThis, JSPromiseDeferred* promiseDeferred)\n"); 2795 } 2796 else { 2797 push(@implContent, "EncodedJSValue JSC_HOST_CALL ${functionName}(ExecState* exec)\n"); 2798 } 2799 2784 2800 push(@implContent, "{\n"); 2785 2801 … … 2800 2816 } 2801 2817 } else { 2802 if ($interface->extendedAttributes->{"CustomProxyToJSObject"}) { 2803 push(@implContent, " $className* castedThis = to${className}(exec->thisValue().toThis(exec, NotStrictMode));\n"); 2804 push(@implContent, " if (UNLIKELY(!castedThis))\n"); 2805 push(@implContent, " return throwVMTypeError(exec);\n"); 2806 } elsif ($interface->extendedAttributes->{"WorkerGlobalScope"}) { 2807 push(@implContent, " $className* castedThis = to${className}(exec->thisValue().toThis(exec, NotStrictMode));\n"); 2808 push(@implContent, " if (UNLIKELY(!castedThis))\n"); 2809 push(@implContent, " return throwVMTypeError(exec);\n"); 2810 } else { 2811 push(@implContent, " JSValue thisValue = exec->thisValue();\n"); 2812 push(@implContent, " $className* castedThis = " . GetCastingHelperForThisObject($interface) . "(thisValue);\n"); 2813 my $domFunctionName = $function->signature->name; 2814 push(@implContent, " if (UNLIKELY(!castedThis))\n"); 2815 push(@implContent, " return throwThisTypeError(*exec, \"$interfaceName\", \"$domFunctionName\");\n"); 2816 } 2817 2818 push(@implContent, " ASSERT_GC_OBJECT_INHERITS(castedThis, ${className}::info());\n"); 2818 GenerateFunctionCastedThis($interface, $interfaceName, $className, $function) if not (IsReturningPromise($function) && !$isCustom); 2819 2819 2820 2820 if ($interface->extendedAttributes->{"CheckSecurity"} and … … 3110 3110 my $conditionalString = $codeGenerator->GenerateConditionalString($interface); 3111 3111 push(@implContent, "\n#endif // ${conditionalString}\n") if $conditionalString; 3112 } 3113 3114 sub GenerateFunctionCastedThis 3115 { 3116 my $interface = shift; 3117 my $interfaceName = shift; 3118 my $className = shift; 3119 my $function = shift; 3120 if ($interface->extendedAttributes->{"CustomProxyToJSObject"}) { 3121 push(@implContent, " $className* castedThis = to${className}(exec->thisValue().toThis(exec, NotStrictMode));\n"); 3122 push(@implContent, " if (UNLIKELY(!castedThis))\n"); 3123 push(@implContent, " return throwVMTypeError(exec);\n"); 3124 } elsif ($interface->extendedAttributes->{"WorkerGlobalScope"}) { 3125 push(@implContent, " $className* castedThis = to${className}(exec->thisValue().toThis(exec, NotStrictMode));\n"); 3126 push(@implContent, " if (UNLIKELY(!castedThis))\n"); 3127 push(@implContent, " return throwVMTypeError(exec);\n"); 3128 } else { 3129 push(@implContent, " JSValue thisValue = exec->thisValue();\n"); 3130 push(@implContent, " $className* castedThis = " . GetCastingHelperForThisObject($interface) . "(thisValue);\n"); 3131 my $domFunctionName = $function->signature->name; 3132 push(@implContent, " if (UNLIKELY(!castedThis))\n"); 3133 push(@implContent, " return throwThisTypeError(*exec, \"$interfaceName\", \"$domFunctionName\");\n"); 3134 } 3135 3136 push(@implContent, " ASSERT_GC_OBJECT_INHERITS(castedThis, ${className}::info());\n"); 3112 3137 } 3113 3138 … … 3596 3621 my $raisesException = $function->signature->extendedAttributes->{"RaisesException"}; 3597 3622 3598 if ($function->signature->type eq "void" ) {3623 if ($function->signature->type eq "void" || IsReturningPromise($function)) { 3599 3624 if ($nondeterministic) { 3600 3625 AddToImplIncludes("<replay/InputCursor.h>", "WEB_REPLAY"); … … 3657 3682 push(@implContent, $indent . "result = " . NativeToJSValue($function->signature, 1, $interfaceName, $functionString, $thisObject) . ";\n"); 3658 3683 push(@implContent, "#endif\n"); 3659 } elsif (IsReturningPromise($function)) {3660 AddToImplIncludes("JSDOMPromise.h");3661 3662 push(@implContent, $indent . "JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, castedThis->globalObject());\n");3663 push(@implContent, $indent . $functionString . ";\n");3664 push(@implContent, $indent . "JSValue result = promiseDeferred->promise();\n");3665 3666 3684 } else { 3667 3685 push(@implContent, $indent . "JSValue result = " . NativeToJSValue($function->signature, 1, $interfaceName, $functionString, $thisObject) . ";\n"); 3668 3686 } 3669 # FIXME: In case of IsReturningPromise($function), the function should not throw. Exception should be used to reject the promise callback.3670 3687 push(@implContent, "\n" . $indent . "setDOMException(exec, ec);\n") if $raisesException; 3671 3688 -
trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp
r185493 r185739 157 157 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionAny(JSC::ExecState*); 158 158 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunction(JSC::ExecState*); 159 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgument(JSC::ExecState*); 160 JSC::EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunctionWithException(JSC::ExecState*); 159 161 160 162 // Attributes … … 652 654 { "any", JSC::Function, NoIntrinsic, (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionAny), (intptr_t) (2) }, 653 655 { "testPromiseFunction", JSC::Function, NoIntrinsic, (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionTestPromiseFunction), (intptr_t) (0) }, 656 { "testPromiseFunctionWithFloatArgument", JSC::Function, NoIntrinsic, (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgument), (intptr_t) (1) }, 657 { "testPromiseFunctionWithException", JSC::Function, NoIntrinsic, (intptr_t)static_cast<NativeFunction>(jsTestObjPrototypeFunctionTestPromiseFunctionWithException), (intptr_t) (0) }, 654 658 }; 655 659 … … 4427 4431 } 4428 4432 4433 static inline EncodedJSValue jsTestObjPrototypeFunctionTestPromiseFunctionPromise(ExecState*, JSTestObj*, JSPromiseDeferred*); 4429 4434 EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunction(ExecState* exec) 4430 4435 { … … 4434 4439 return throwThisTypeError(*exec, "TestObj", "testPromiseFunction"); 4435 4440 ASSERT_GC_OBJECT_INHERITS(castedThis, JSTestObj::info()); 4436 auto& impl = castedThis->impl(); 4437 JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, castedThis->globalObject()); 4441 return JSValue::encode(callPromiseFunction(*exec, *castedThis, jsTestObjPrototypeFunctionTestPromiseFunctionPromise)); 4442 } 4443 4444 static inline EncodedJSValue jsTestObjPrototypeFunctionTestPromiseFunctionPromise(ExecState* exec, JSTestObj* castedThis, JSPromiseDeferred* promiseDeferred) 4445 { 4446 auto& impl = castedThis->impl(); 4438 4447 impl.testPromiseFunction(DeferredWrapper(exec, castedThis->globalObject(), promiseDeferred)); 4439 JSValue result = promiseDeferred->promise(); 4440 return JSValue::encode(result); 4448 return JSValue::encode(jsUndefined()); 4449 } 4450 4451 static inline EncodedJSValue jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgumentPromise(ExecState*, JSTestObj*, JSPromiseDeferred*); 4452 EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgument(ExecState* exec) 4453 { 4454 JSValue thisValue = exec->thisValue(); 4455 JSTestObj* castedThis = jsDynamicCast<JSTestObj*>(thisValue); 4456 if (UNLIKELY(!castedThis)) 4457 return throwThisTypeError(*exec, "TestObj", "testPromiseFunctionWithFloatArgument"); 4458 ASSERT_GC_OBJECT_INHERITS(castedThis, JSTestObj::info()); 4459 return JSValue::encode(callPromiseFunction(*exec, *castedThis, jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgumentPromise)); 4460 } 4461 4462 static inline EncodedJSValue jsTestObjPrototypeFunctionTestPromiseFunctionWithFloatArgumentPromise(ExecState* exec, JSTestObj* castedThis, JSPromiseDeferred* promiseDeferred) 4463 { 4464 auto& impl = castedThis->impl(); 4465 if (UNLIKELY(exec->argumentCount() < 1)) 4466 return throwVMError(exec, createNotEnoughArgumentsError(exec)); 4467 float a = exec->argument(0).toFloat(exec); 4468 if (UNLIKELY(exec->hadException())) 4469 return JSValue::encode(jsUndefined()); 4470 if (!std::isfinite(a)) { 4471 setDOMException(exec, TypeError); 4472 return JSValue::encode(jsUndefined()); 4473 } 4474 impl.testPromiseFunctionWithFloatArgument(a, DeferredWrapper(exec, castedThis->globalObject(), promiseDeferred)); 4475 return JSValue::encode(jsUndefined()); 4476 } 4477 4478 static inline EncodedJSValue jsTestObjPrototypeFunctionTestPromiseFunctionWithExceptionPromise(ExecState*, JSTestObj*, JSPromiseDeferred*); 4479 EncodedJSValue JSC_HOST_CALL jsTestObjPrototypeFunctionTestPromiseFunctionWithException(ExecState* exec) 4480 { 4481 JSValue thisValue = exec->thisValue(); 4482 JSTestObj* castedThis = jsDynamicCast<JSTestObj*>(thisValue); 4483 if (UNLIKELY(!castedThis)) 4484 return throwThisTypeError(*exec, "TestObj", "testPromiseFunctionWithException"); 4485 ASSERT_GC_OBJECT_INHERITS(castedThis, JSTestObj::info()); 4486 return JSValue::encode(callPromiseFunction(*exec, *castedThis, jsTestObjPrototypeFunctionTestPromiseFunctionWithExceptionPromise)); 4487 } 4488 4489 static inline EncodedJSValue jsTestObjPrototypeFunctionTestPromiseFunctionWithExceptionPromise(ExecState* exec, JSTestObj* castedThis, JSPromiseDeferred* promiseDeferred) 4490 { 4491 auto& impl = castedThis->impl(); 4492 ExceptionCode ec = 0; 4493 impl.testPromiseFunctionWithException(DeferredWrapper(exec, castedThis->globalObject(), promiseDeferred), ec); 4494 setDOMException(exec, ec); 4495 return JSValue::encode(jsUndefined()); 4441 4496 } 4442 4497 -
trunk/Source/WebCore/bindings/scripts/test/TestObj.idl
r185493 r185739 277 277 // Promise function 278 278 Promise testPromiseFunction(); 279 Promise testPromiseFunctionWithFloatArgument(float a); 280 [RaisesException] Promise testPromiseFunctionWithException(); 279 281 }; 280 282
Note:
See TracChangeset
for help on using the changeset viewer.