Changeset 234022 in webkit


Ignore:
Timestamp:
Jul 19, 2018 9:47:51 PM (6 years ago)
Author:
sbarati@apple.com
Message:

Conservatively make Object.assign's fast path do a two phase protocol of loading everything then storing everything to try to prevent a crash
https://bugs.webkit.org/show_bug.cgi?id=187836
<rdar://problem/42409527>

Reviewed by Mark Lam.

We have crash reports that we're crashing on source->getDirect in Object.assign's
fast path. Mark investigated this and determined we end up with a nullptr for
butterfly. This is curious, because source's Structure indicated that it has
out of line properties. My leading hypothesis for this at the moment is a bit
handwavy, but it's essentially:

  • We end up firing a watchpoint when assigning to the target (this can happen

if a watchpoint was set up for storing to that particular field)

  • When we fire that watchpoint, we end up doing some kind work on the source,

perhaps causing it to flattenDictionaryStructure. Therefore, we end up
mutating source.

I'm not super convinced this is what we're running into, but just by reading
the code, I think it needs to be something similar to this. Seeing if this change
fixes the crasher will give us good data to determine if something like this is
happening or if the bug is something else entirely.

  • runtime/ObjectConstructor.cpp:

(JSC::objectConstructorAssign):

Location:
trunk/Source/JavaScriptCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r233999 r234022  
     12018-07-19  Saam Barati  <sbarati@apple.com>
     2
     3        Conservatively make Object.assign's fast path do a two phase protocol of loading everything then storing everything to try to prevent a crash
     4        https://bugs.webkit.org/show_bug.cgi?id=187836
     5        <rdar://problem/42409527>
     6
     7        Reviewed by Mark Lam.
     8
     9        We have crash reports that we're crashing on source->getDirect in Object.assign's
     10        fast path. Mark investigated this and determined we end up with a nullptr for
     11        butterfly. This is curious, because source's Structure indicated that it has
     12        out of line properties. My leading hypothesis for this at the moment is a bit
     13        handwavy, but it's essentially:
     14        - We end up firing a watchpoint when assigning to the target (this can happen
     15        if a watchpoint was set up for storing to that particular field)
     16        - When we fire that watchpoint, we end up doing some kind work on the source,
     17        perhaps causing it to flattenDictionaryStructure. Therefore, we end up
     18        mutating source.
     19       
     20        I'm not super convinced this is what we're running into, but just by reading
     21        the code, I think it needs to be something similar to this. Seeing if this change
     22        fixes the crasher will give us good data to determine if something like this is
     23        happening or if the bug is something else entirely.
     24
     25        * runtime/ObjectConstructor.cpp:
     26        (JSC::objectConstructorAssign):
     27
    1282018-07-19  Commit Queue  <commit-queue@webkit.org>
    229
  • trunk/Source/JavaScriptCore/runtime/ObjectConstructor.cpp

    r233122 r234022  
    319319            }
    320320
    321             auto canPerformFastPropertyEnumerationForObjectAssign = [] (Structure* structure) {
    322                 if (structure->typeInfo().overridesGetOwnPropertySlot())
    323                     return false;
    324                 if (structure->typeInfo().overridesGetPropertyNames())
    325                     return false;
    326                 // FIXME: Indexed properties can be handled.
    327                 // https://bugs.webkit.org/show_bug.cgi?id=185358
    328                 if (hasIndexedProperties(structure->indexingType()))
    329                     return false;
    330                 if (structure->hasGetterSetterProperties())
    331                     return false;
    332                 if (structure->isUncacheableDictionary())
    333                     return false;
    334                 // Cannot perform fast [[Put]] to |target| if the property names of the |source| contain "__proto__".
    335                 if (structure->hasUnderscoreProtoPropertyExcludingOriginalProto())
    336                     return false;
    337                 return true;
    338             };
    339 
    340             Structure* structure = source->structure(vm);
    341             if (canPerformFastPropertyEnumerationForObjectAssign(structure)) {
    342                 // |source| Structure does not have any getters. And target can perform fast put.
    343                 // So enumerating properties and putting properties are non observable.
    344                 structure->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool {
    345                     if (entry.attributes & PropertyAttribute::DontEnum)
     321            bool canDoFastPath;
     322            Vector<RefPtr<UniquedStringImpl>, 8> properties;
     323            MarkedArgumentBuffer values;
     324            {
     325                auto canPerformFastPropertyEnumerationForObjectAssign = [] (Structure* structure) {
     326                    if (structure->typeInfo().overridesGetOwnPropertySlot())
     327                        return false;
     328                    if (structure->typeInfo().overridesGetPropertyNames())
     329                        return false;
     330                    // FIXME: Indexed properties can be handled.
     331                    // https://bugs.webkit.org/show_bug.cgi?id=185358
     332                    if (hasIndexedProperties(structure->indexingType()))
     333                        return false;
     334                    if (structure->hasGetterSetterProperties())
     335                        return false;
     336                    if (structure->isUncacheableDictionary())
     337                        return false;
     338                    // Cannot perform fast [[Put]] to |target| if the property names of the |source| contain "__proto__".
     339                    if (structure->hasUnderscoreProtoPropertyExcludingOriginalProto())
     340                        return false;
     341                    return true;
     342                };
     343
     344                Structure* structure = source->structure(vm);
     345                canDoFastPath = canPerformFastPropertyEnumerationForObjectAssign(structure);
     346                if (canDoFastPath) {
     347                    // |source| Structure does not have any getters. And target can perform fast put.
     348                    // So enumerating properties and putting properties are non observable.
     349
     350                    // FIXME: It doesn't seem like we should have to do this in two phases, but
     351                    // we're running into crashes where it appears that source is transitioning
     352                    // under us, and even ends up in a state where it has a null butterfly. My
     353                    // leading hypothesis here is that we fire some value replacement watchpoint
     354                    // that ends up transitioning the structure underneath us.
     355                    // https://bugs.webkit.org/show_bug.cgi?id=187837
     356
     357                    structure->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool {
     358                        if (entry.attributes & PropertyAttribute::DontEnum)
     359                            return true;
     360
     361                        PropertyName propertyName(entry.key);
     362                        if (propertyName.isPrivateName())
     363                            return true;
     364                       
     365                        properties.append(entry.key);
     366                        values.appendWithCrashOnOverflow(source->getDirect(entry.offset));
     367
    346368                        return true;
    347 
    348                     PropertyName propertyName(entry.key);
    349                     if (propertyName.isPrivateName())
    350                         return true;
    351 
     369                    });
     370                }
     371            }
     372
     373            if (canDoFastPath) {
     374                for (size_t i = 0; i < properties.size(); ++i) {
    352375                    // FIXME: We could put properties in a batching manner to accelerate Object.assign more.
    353376                    // https://bugs.webkit.org/show_bug.cgi?id=185358
    354377                    PutPropertySlot putPropertySlot(target, true);
    355                     target->putOwnDataProperty(vm, propertyName, source->getDirect(entry.offset), putPropertySlot);
    356                     return true;
    357                 });
     378                    target->putOwnDataProperty(vm, properties[i].get(), values.at(i), putPropertySlot);
     379                }
     380
    358381                continue;
    359382            }
Note: See TracChangeset for help on using the changeset viewer.