Bug 222739 - Cache cross-origin methods / accessors of Window and Location per lexical global object
Summary: Cache cross-origin methods / accessors of Window and Location per lexical glo...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: DOM (show other bugs)
Version: WebKit Nightly Build
Hardware: All All
: P2 Trivial
Assignee: Alexey Shvayka
URL:
Keywords: InRadar
Depends on: 223154
Blocks:
  Show dependency treegraph
 
Reported: 2021-03-04 07:50 PST by Alexey Shvayka
Modified: 2021-03-23 01:46 PDT (History)
20 users (show)

See Also:


Attachments
Patch (49.68 KB, patch)
2021-03-04 07:54 PST, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (50.61 KB, patch)
2021-03-04 14:19 PST, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (54.82 KB, patch)
2021-03-06 01:55 PST, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (54.80 KB, patch)
2021-03-06 05:57 PST, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (54.91 KB, patch)
2021-03-11 08:45 PST, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (54.93 KB, patch)
2021-03-11 16:12 PST, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (50.60 KB, patch)
2021-03-14 13:43 PDT, Alexey Shvayka
no flags Details | Formatted Diff | Diff
Patch (55.14 KB, patch)
2021-03-16 07:27 PDT, Alexey Shvayka
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Alexey Shvayka 2021-03-04 07:50:21 PST
Cache cross-origin methods / accessors of Window and Location per lexical global object
Comment 1 Alexey Shvayka 2021-03-04 07:54:17 PST
Created attachment 422227 [details]
Patch
Comment 2 EWS Watchlist 2021-03-04 07:55:28 PST
This patch modifies the imported WPT tests. Please ensure that any changes on the tests (not coming from a WPT import) are exported to WPT. Please see https://trac.webkit.org/wiki/WPTExportProcess
Comment 3 Alexey Shvayka 2021-03-04 14:19:47 PST
Created attachment 422286 [details]
Patch

Fix & simplify CrossOriginMapKey, and include missing test file.
Comment 4 Darin Adler 2021-03-04 15:47:08 PST
Comment on attachment 422286 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=422286&action=review

> Source/WebCore/ChangeLog:18
> +        because generated JSLocation can't be easily extended.

Is this a long term decision to never fix for location, or just a short term decision to not do it at this time?

> Source/WebCore/bindings/js/JSDOMGlobalObject.cpp:314
> +    JSFunction* jsFunction = m_crossOriginFunctionMap.get(key);
> +    if (!jsFunction) {
> +        jsFunction = JSFunction::create(lexicalGlobalObject->vm(), lexicalGlobalObject, length, propertyName.publicName(), nativeFunction);
> +        m_crossOriginFunctionMap.set(key, jsFunction);
> +    }
> +    return jsFunction;

This is the less-efficient, double hashing, idiom. The more efficient idiom uses either add or ensure. The ensure version is something like this:

    return m_crossOriginFunctionMap.ensure(key, [&] {
        return JSFunction::create(lexicalGlobalObject->vm(), lexicalGlobalObject, length, propertyName.publicName(), nativeFunction);
    }).iterator->value;

I am not sure why WeakGCMap implements set, but neither add nor ensure, so you’d need to deal with that first.

It also seems that WeakGCMap’s set function has a peculiar combination of a non-reference type (I would have expected an rvalue reference as in HashMap itself) but use of WTFMove.

> Source/WebCore/bindings/js/JSDOMGlobalObject.cpp:319
> +    CrossOriginMapKey key = std::make_pair(lexicalGlobalObject, getter ? reinterpret_cast<void*>(getter) : reinterpret_cast<void*>(setter));

I think for clarity we should add this assertion:

    ASSERT(getter || setter);

> Source/WebCore/bindings/js/JSDOMGlobalObject.cpp:328
> +    GetterSetter* getterSetter = m_crossOriginGetterSetterMap.get(key);
> +    if (!getterSetter) {
> +        auto& vm = lexicalGlobalObject->vm();
> +        getterSetter = GetterSetter::create(vm, lexicalGlobalObject,
> +            getter ? JSCustomGetterFunction::create(vm, lexicalGlobalObject, propertyName, getter) : nullptr,
> +            setter ? JSCustomSetterFunction::create(vm, lexicalGlobalObject, propertyName, setter) : nullptr);
> +        m_crossOriginGetterSetterMap.set(key, getterSetter);
> +    }
> +    return getterSetter;

Same as above:

    m_crossOriginFunctionMap.ensure(key, [&] {
        auto& vm = lexicalGlobalObject->vm();
        return GetterSetter::create(vm, lexicalGlobalObject,
            getter ? JSCustomGetterFunction::create(vm, lexicalGlobalObject, propertyName, getter) : nullptr,
            JSCustomSetterFunction::create(vm, lexicalGlobalObject, propertyName, setter) : nullptr);;
    }).iterator->value;

> Source/WebCore/bindings/js/JSDOMGlobalObject.h:88
> +    JSC::JSFunction* getCrossOriginFunction(JSC::JSGlobalObject*, JSC::PropertyName, JSC::NativeFunction, unsigned length);
> +    JSC::GetterSetter* getCrossOriginGetterSetter(JSC::JSGlobalObject*, JSC::PropertyName, JSC::GetValueFunc, JSC::PutValueFunc);

WebKit coding style discourages the use of the word "get" in the names of functions like these.

> Source/WebCore/bindings/js/JSDOMWindowCustom.cpp:206
> +            && slot.isValue() && isHostFunction(slot.getValue(lexicalGlobalObject, propertyName), JSDOMWindow::info()->staticPropHashTable->entry(propertyName)->function());

Rationale for looking this up at runtime instead of link time? Just so we can avoid the need for ForwardDeclareInHeader?
Comment 5 Alexey Shvayka 2021-03-06 01:55:03 PST
Created attachment 422478 [details]
Patch

Add WeakGCMap::ensure(), rename methods, and reword ChangeLog.
Comment 6 Alexey Shvayka 2021-03-06 05:57:10 PST
Created attachment 422486 [details]
Patch

Simplify "showModalDialog" lookup a bit.
Comment 7 Radar WebKit Bug Importer 2021-03-11 07:51:19 PST
<rdar://problem/75315814>
Comment 8 Alexey Shvayka 2021-03-11 08:45:31 PST
Created attachment 422937 [details]
Patch

Attempt to fix Windows build.
Comment 9 Alexey Shvayka 2021-03-11 09:08:17 PST
(In reply to Darin Adler from comment #4)
> Comment on attachment 422286 [details]
> Patch

Thank you for very valuable feedback, Darin! 

> Is this a long term decision to never fix for location, or just a short term
> decision to not do it at this time?

The patch does fix Location; reworded ChangeLog to clarify.
It is a long term decision to have cache maps that handle both Window and Location only in one place (on JSDOMGlobalObject).

> This is the less-efficient, double hashing, idiom. The more efficient idiom
> uses either add or ensure. The ensure version is something like this:
> 
>     return m_crossOriginFunctionMap.ensure(key, [&] {
>         return JSFunction::create(lexicalGlobalObject->vm(),
> lexicalGlobalObject, length, propertyName.publicName(), nativeFunction);
>     }).iterator->value;

Wow, this is a great suggestion!
Apart from avoiding double hashing, it's also a nice cleanup.
Applied the similar change to helper methods of JSObject::getOwnPropertyDescriptor().

> I think for clarity we should add this assertion:
> 
>     ASSERT(getter || setter);

Added.

> > Source/WebCore/bindings/js/JSDOMGlobalObject.h:88
> > +    JSC::JSFunction* getCrossOriginFunction(JSC::JSGlobalObject*, JSC::PropertyName, JSC::NativeFunction, unsigned length);
> > +    JSC::GetterSetter* getCrossOriginGetterSetter(JSC::JSGlobalObject*, JSC::PropertyName, JSC::GetValueFunc, JSC::PutValueFunc);
> 
> WebKit coding style discourages the use of the word "get" in the names of
> functions like these.

Renamed to "createCrossOrigin*" in JSDOMGlobalObject.h and similar methods in JSObject.cpp to drop the "get".

> Rationale for looking this up at runtime instead of link time? Just so we
> can avoid the need for ForwardDeclareInHeader?

Yes, only that; reworded ChangeLog to make it clear.
Please note that only "showModalDialog" is affected, which is already removed from Chrome and Firefox.
Comment 10 Alexey Shvayka 2021-03-11 16:12:52 PST
Created attachment 422986 [details]
Patch

Proper Windows build fix.
Comment 11 Alexey Shvayka 2021-03-13 00:40:19 PST
Comment on attachment 422986 [details]
Patch

(In reply to Alexey Shvayka from comment #10)
> Created attachment 422986 [details]
> Patch
> 
> Proper Windows build fix.

That did the trick. Judging by other Windows builds, the failures are pre-existing, but were not confirmed as such because rebuild without patch has failed.

I am putting r+ on my own patch so the WPT export bot would (hopefully) approve my PR.
The bot doesn't consider the patch reviewed by Darin as it's now obsolete.
Comment 12 Alexey Shvayka 2021-03-13 01:02:47 PST
Committed r274379 (235246@main): <https://commits.webkit.org/235246@main>
Comment 13 WebKit Commit Bot 2021-03-13 18:46:20 PST
Re-opened since this is blocked by bug 223154
Comment 15 Alexey Shvayka 2021-03-14 13:43:54 PDT
Created attachment 423132 [details]
Patch

Revert WeakGCMap::ensure() change.
Comment 16 Alexey Shvayka 2021-03-14 15:05:59 PDT
(In reply to Yusuke Suzuki from comment #14)
> @Alexey Hi! I temporarily reverted this patch since it looks like we are
> getting some crashes after this patch.

Hey! I can confirm these tests are consistently crashing for me locally too. Not sure how the patch passed EWS.

I've tracked down the crashes to WeakGCMap::ensure() returning nullptr for (weakly referenced object) value after it was GCed but before WeakGCMap's entries were pruned.

HashMap nicely handles empty and deleted keys, but not values, so I've brought back double hashing approach that not only checks if the key exists, but also that the value is non-null.

In future, we can optimize jsDOMWindowGetOwnPropertySlotRestrictedAccess() to return a cacheable (by raw C++ pointer) slot.
Comment 17 Darin Adler 2021-03-14 16:05:31 PDT
(In reply to Alexey Shvayka from comment #16)
> HashMap nicely handles empty and deleted keys, but not values, so I've
> brought back double hashing approach that not only checks if the key exists,
> but also that the value is non-null.

Do we have to bring back double hashing? Can we just add a null check?
Comment 18 Alexey Shvayka 2021-03-16 07:27:24 PDT
Created attachment 423327 [details]
Patch
Comment 19 Alexey Shvayka 2021-03-16 07:29:54 PDT
(In reply to Darin Adler from comment #17)
> Do we have to bring back double hashing? Can we just add a null check?

My bad, we don't: instead, I've decorated HashMap::ensure() to perform null check for existing entries.
If GCed weak object is encountered, HashMap is updated using AddResult's iterator, thus completely avoiding double hashing.
ensureValue() method name to better reflect its behavior / return value: no more `}).iterator->value.get()` needed. Neat!
Comment 20 EWS 2021-03-16 16:01:04 PDT
Committed r274528: <https://commits.webkit.org/r274528>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 423327 [details].