WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
205654
Make _callAsyncFunction:withArguments: work with promises
https://bugs.webkit.org/show_bug.cgi?id=205654
Summary
Make _callAsyncFunction:withArguments: work with promises
Brady Eidson
Reported
2019-12-30 21:56:54 PST
Make _callAsyncFunction:withArguments: work with promises
Attachments
Patch
(13.35 KB, patch)
2020-01-06 14:30 PST
,
Brady Eidson
no flags
Details
Formatted Diff
Diff
Patch
(9.30 KB, patch)
2020-01-06 16:03 PST
,
Brady Eidson
no flags
Details
Formatted Diff
Diff
Patch
(9.35 KB, patch)
2020-01-07 16:07 PST
,
Brady Eidson
no flags
Details
Formatted Diff
Diff
Patch
(9.31 KB, patch)
2020-01-07 22:38 PST
,
Brady Eidson
no flags
Details
Formatted Diff
Diff
Patch
(9.39 KB, patch)
2020-01-08 11:21 PST
,
Brady Eidson
no flags
Details
Formatted Diff
Diff
Show Obsolete
(4)
View All
Add attachment
proposed patch, testcase, etc.
Brady Eidson
Comment 1
2020-01-06 14:30:25 PST
Created
attachment 386888
[details]
Patch
Saam Barati
Comment 2
2020-01-06 14:57:20 PST
Comment on
attachment 386888
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=386888&action=review
r=me
> Source/WTF/wtf/SharedFunction.h:36 > +class SharedFunction<Out(In...)> : public RefCounted<SharedFunction<Out(In...)>> {
I think instead of this we can just use SharedTask
> Source/WebCore/bindings/js/ScriptController.cpp:715 > + auto thenIdentifier = world.vm().propertyNames->builtinNames().thenPrivateName();
this should be: "world.vm().propertyNames->then". A test that would catch this is using your own "thenable"
> Source/WebCore/bindings/js/ScriptController.cpp:719 > + if (!thenFunction.isFunction(world.vm())) {
The right check here is: CallData ignoredCallData; if (thenFunction.isObject && asObject(thenFunction)->methodTable(vm)->getCallData(asObject(thenFunction), ignoredCallData) != CallType::None) I think a test for a callable which does not pass the isFunction() query would be something like this with a Proxy as the "thenFunction": let p = new Proxy(function() { print("this should be called"); }, { }); let thenable = {then: p};
> Source/WebCore/bindings/js/ScriptController.cpp:740 > + auto finalizeCount = makeUniqueWithoutFastMallocCheck<unsigned>(0);
nit: can you change makeUnique to just check if T is a primitive?
> Source/WebCore/bindings/js/ScriptController.cpp:747 > + if (heap) {
these branches are not needed.
> Source/WebCore/bindings/js/ScriptController.cpp:754 > + if (heap) {
ditto.
> Source/WebCore/bindings/js/ScriptController.cpp:756 > + finalizeGuard.get()();
instead of getting the heap pointer here, you can just do: world.vm().heap
Brady Eidson
Comment 3
2020-01-06 16:03:03 PST
Created
attachment 386907
[details]
Patch
Brady Eidson
Comment 4
2020-01-07 08:51:03 PST
The problem with these test runs is spamming to the console from the AsyncFunction.Promise test affecting later runs. Damn. Fixing.
Brady Eidson
Comment 5
2020-01-07 16:07:05 PST
Created
attachment 387049
[details]
Patch
Brady Eidson
Comment 6
2020-01-07 16:07:36 PST
(In reply to Brady Eidson from
comment #4
)
> The problem with these test runs is spamming to the console from the > AsyncFunction.Promise test affecting later runs. > > Damn. > > Fixing.
Nope that wasn't it. Accidentally applying the new async behavior to everybody. Fixed in most recent upload.
Brady Eidson
Comment 7
2020-01-07 21:58:39 PST
That fixed the collateral damage to other API tests but revealed that the original AsyncFunction tests are now busted. I can't quite figure out why, other than verify locally before this patch they work, and with it applied they don't. It's a problem with serialization. Before my patch, a JSC::JSValue representing "undefined" successfully serializes. Same for "null" With my patch, those exact same JSValues do not serialize.
Brady Eidson
Comment 8
2020-01-07 22:04:08 PST
For the "null" and "undefined" jsvalues, SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception) succeeds before my patch It fails after my patch because it has an exception.
Brady Eidson
Comment 9
2020-01-07 22:12:09 PST
Here's the body of SerializedScriptValue::create. With my patch, an exception occurs in here. Without my patch, it doesn't. RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception) { JSGlobalObject* lexicalGlobalObject = toJS(originContext); VM& vm = lexicalGlobalObject->vm(); JSLockHolder locker(vm); auto scope = DECLARE_CATCH_SCOPE(vm); JSValue value = toJS(lexicalGlobalObject, apiValue); ------> Exception happens in the above line auto serializedValue = SerializedScriptValue::create(*lexicalGlobalObject, value); if (UNLIKELY(scope.exception())) { if (exception) *exception = toRef(lexicalGlobalObject, scope.exception()->value()); scope.clearException(); return nullptr; } ASSERT(serializedValue); return serializedValue; }
Brady Eidson
Comment 10
2020-01-07 22:18:39 PST
(In reply to Brady Eidson from
comment #9
)
> Here's the body of SerializedScriptValue::create. > > With my patch, an exception occurs in here. > Without my patch, it doesn't. > > RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef > originContext, JSValueRef apiValue, JSValueRef* exception) > { > JSGlobalObject* lexicalGlobalObject = toJS(originContext); > VM& vm = lexicalGlobalObject->vm(); > JSLockHolder locker(vm); > auto scope = DECLARE_CATCH_SCOPE(vm); > > JSValue value = toJS(lexicalGlobalObject, apiValue); > > ------> Exception happens in the above line > > auto serializedValue = > SerializedScriptValue::create(*lexicalGlobalObject, value); > if (UNLIKELY(scope.exception())) { > if (exception) > *exception = toRef(lexicalGlobalObject, > scope.exception()->value()); > scope.clearException(); > return nullptr; > } > ASSERT(serializedValue); > return serializedValue; > }
Nope, I was wrong - the exception has already occurred the moment you make the DECLARE_CATCH_SCOPE. This must be because of my patch's attempt to pull the "then" property from the result.
Brady Eidson
Comment 11
2020-01-07 22:24:56 PST
(In reply to Brady Eidson from
comment #10
)
> (In reply to Brady Eidson from
comment #9
> > Nope, I was wrong - the exception has already occurred the moment you make > the DECLARE_CATCH_SCOPE. > > This must be because of my patch's attempt to pull the "then" property from > the result.
Confirmed. By smattering exception scope checks throughout ScriptController::executeAsynchronousUserAgentScriptInWorld I found it occurs on this line: auto thenFunction = result.value().get(&globalObject, thenIdentifier);
Brady Eidson
Comment 12
2020-01-07 22:26:04 PST
(In reply to Brady Eidson from
comment #11
)
> (In reply to Brady Eidson from
comment #10
) > > (In reply to Brady Eidson from
comment #9
> > > > Nope, I was wrong - the exception has already occurred the moment you make > > the DECLARE_CATCH_SCOPE. > > > > This must be because of my patch's attempt to pull the "then" property from > > the result. > > Confirmed. By smattering exception scope checks throughout > ScriptController::executeAsynchronousUserAgentScriptInWorld I found it > occurs on this line: > > auto thenFunction = result.value().get(&globalObject, thenIdentifier);
It appears doing that on a JSNull or JSUndefined throws an exception. Will try my own fix and make sure Saam takes another look.
Brady Eidson
Comment 13
2020-01-07 22:38:09 PST
Created
attachment 387079
[details]
Patch
Brady Eidson
Comment 14
2020-01-07 22:39:12 PST
(In reply to Brady Eidson from
comment #13
)
> Created
attachment 387079
[details]
> Patch
Basically added an "isNullOrUndefined()" check on the value before we try to extract "then" from it.
Saam Barati
Comment 15
2020-01-07 22:53:54 PST
Comment on
attachment 387079
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=387079&action=review
> Source/WebCore/bindings/js/ScriptController.cpp:718 > + auto thenFunction = result.value().get(&globalObject, thenIdentifier);
I think the appropriate check is just to check for exceptions after this (and maybe omit the isUndefinedOrNull, since that'll just fall out from this). (Even if result.value() isn't null or undefined, this "get" may run arbitrary JS code, and can throw. For. example, when it's a getter). I think you want to declare a catch scope, and return early (and maybe reject?) if an exception is thrown. The only other place where an exception might get thrown is your JS function call at the bottom. However, since that's the final thing in this function, you can call "release" on your exception scope before doing the JS call.
Brady Eidson
Comment 16
2020-01-07 23:12:15 PST
(In reply to Saam Barati from
comment #15
)
> Comment on
attachment 387079
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=387079&action=review
> > > Source/WebCore/bindings/js/ScriptController.cpp:718 > > + auto thenFunction = result.value().get(&globalObject, thenIdentifier); > > I think the appropriate check is just to check for exceptions after this > (and maybe omit the isUndefinedOrNull, since that'll just fall out from > this). > (Even if result.value() isn't null or undefined, this "get" may run > arbitrary JS code, and can throw. For. example, when it's a getter).
Okay, but...
> I think you want to declare a catch scope, and return early (and maybe > reject?) if an exception is thrown.
No, because it's 100% valid to return null or undefined and pass them back to the UI process. So if looking up "then" on a real object can run arbitrary JS code and throw an exception, we definitely should pass that back to the UI process as an error. But looking up "then" on null or undefined should silently fail, and we need to pass back null or undefined to the UI process. I'm guessing that it's impossible to detect *why* an exception was thrown in the get() call, and therefore I need to prequalify the value beforehand, like in this patch? Or is there something else I'm missing?
Brady Eidson
Comment 17
2020-01-08 07:29:40 PST
Note: The api-ios failures here are not due to this patch.
Brady Eidson
Comment 18
2020-01-08 11:12:25 PST
Pow-wow'ed with Keith - Should do an isObject check before doing the get (This is what some of the related ES spec algorithms do)
Brady Eidson
Comment 19
2020-01-08 11:21:48 PST
Created
attachment 387116
[details]
Patch
Saam Barati
Comment 20
2020-01-08 11:41:02 PST
(In reply to Brady Eidson from
comment #16
)
> (In reply to Saam Barati from
comment #15
) > > Comment on
attachment 387079
[details]
> > Patch > > > > View in context: > >
https://bugs.webkit.org/attachment.cgi?id=387079&action=review
> > > > > Source/WebCore/bindings/js/ScriptController.cpp:718 > > > + auto thenFunction = result.value().get(&globalObject, thenIdentifier); > > > > I think the appropriate check is just to check for exceptions after this > > (and maybe omit the isUndefinedOrNull, since that'll just fall out from > > this). > > (Even if result.value() isn't null or undefined, this "get" may run > > arbitrary JS code, and can throw. For. example, when it's a getter). > > Okay, but... > > > I think you want to declare a catch scope, and return early (and maybe > > reject?) if an exception is thrown. > > No, because it's 100% valid to return null or undefined and pass them back > to the UI process.
I see. I didn't realize that. What Keith suggested to you seems reasonable to me too.
> > So if looking up "then" on a real object can run arbitrary JS code and throw > an exception, we definitely should pass that back to the UI process as an > error. > > But looking up "then" on null or undefined should silently fail, and we need > to pass back null or undefined to the UI process. > > I'm guessing that it's impossible to detect *why* an exception was thrown in > the get() call, and therefore I need to prequalify the value beforehand, > like in this patch? > > Or is there something else I'm missing?
Saam Barati
Comment 21
2020-01-08 11:41:41 PST
Comment on
attachment 387116
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=387116&action=review
> Source/WebCore/bindings/js/ScriptController.cpp:718 > + auto thenFunction = result.value().get(&globalObject, thenIdentifier);
I think you still want an exception check after this.
Brady Eidson
Comment 22
2020-01-08 12:27:19 PST
(In reply to Saam Barati from
comment #21
)
> Comment on
attachment 387116
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=387116&action=review
> > > Source/WebCore/bindings/js/ScriptController.cpp:718 > > + auto thenFunction = result.value().get(&globalObject, thenIdentifier); > > I think you still want an exception check after this.
As mentioned, the SerializedScriptValue creator handles this. It would be nicer to catch the native exception, and I'll do it in a followup
WebKit Commit Bot
Comment 23
2020-01-08 13:47:25 PST
Comment on
attachment 387116
[details]
Patch Clearing flags on attachment: 387116 Committed
r254222
: <
https://trac.webkit.org/changeset/254222
>
WebKit Commit Bot
Comment 24
2020-01-08 13:47:27 PST
All reviewed patches have been landed. Closing bug.
Radar WebKit Bug Importer
Comment 25
2020-01-08 13:48:16 PST
<
rdar://problem/58420346
>
Truitt Savell
Comment 26
2020-01-09 09:01:02 PST
It looks like the changes in
https://trac.webkit.org/changeset/254222/webkit
introduced a Timing out API test TestWebKitAPI.AsyncFunction.Promise
https://results.webkit.org/?suite=api-tests&test=TestWebKitAPI.AsyncFunction.Promise
tracked in
https://bugs.webkit.org/show_bug.cgi?id=206012
Brady Eidson
Comment 27
2020-01-13 13:26:42 PST
(In reply to Truitt Savell from
comment #26
)
> It looks like the changes in
https://trac.webkit.org/changeset/254222/webkit
> > introduced a Timing out API test TestWebKitAPI.AsyncFunction.Promise > >
https://results.webkit.org/?suite=api-tests&test=TestWebKitAPI.AsyncFunction
. > Promise > > tracked in
https://bugs.webkit.org/show_bug.cgi?id=206012
Yet Another Case™ of EWS not agreeing with the bots. Will take a look in that other bug.
Jonathan Bedard
Comment 28
2020-01-17 09:28:44 PST
(In reply to Brady Eidson from
comment #27
)
> (In reply to Truitt Savell from
comment #26
) > > It looks like the changes in
https://trac.webkit.org/changeset/254222/webkit
> > > > introduced a Timing out API test TestWebKitAPI.AsyncFunction.Promise > > > >
https://results.webkit.org/?suite=api-tests&test=TestWebKitAPI.AsyncFunction
. > > Promise > > > > tracked in
https://bugs.webkit.org/show_bug.cgi?id=206012
> > Yet Another Case™ of EWS not agreeing with the bots. > > Will take a look in that other bug.
It looks like it's only on Debug builds, EWS tests Release.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug