Changeset 228725 in webkit


Ignore:
Timestamp:
Feb 19, 2018 7:45:03 PM (6 years ago)
Author:
sbarati@apple.com
Message:

Don't use JSFunction's allocation profile when getting the prototype can be effectful
https://bugs.webkit.org/show_bug.cgi?id=182942
<rdar://problem/37584764>

Reviewed by Mark Lam.

JSTests:

  • stress/get-prototype-create-this-effectful.js: Added.

Source/JavaScriptCore:

Prior to this patch, the create_this implementation assumed that anything
that is a JSFunction can use the object allocation profile and go down the
fast path to allocate the |this| object. Implied by this approach is that
accessing the 'prototype' property of the incoming function is not an
effectful operation. This is inherent to the ObjectAllocationProfile
data structure: it caches the prototype field. However, getting the
'prototype' property might be an effectful operation, e.g, it could
be a getter. Many variants of functions in JS have the 'prototype' property
as non-configurable. However, some functions, like bound functions, do not
have the 'prototype' field with these attributes.

This patch adds the notion of 'canUseAllocationProfile' to JSFunction
and threads it through so that we only go down the fast path and use
the allocation profile when the prototype property is non-configurable.

  • bytecompiler/NodesCodegen.cpp:

(JSC::ClassExprNode::emitBytecode):

  • dfg/DFGOperations.cpp:
  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/JSFunction.cpp:

(JSC::JSFunction::prototypeForConstruction):
(JSC::JSFunction::allocateAndInitializeRareData):
(JSC::JSFunction::initializeRareData):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::canUseAllocationProfileNonInline):

  • runtime/JSFunction.h:

(JSC::JSFunction::ensureRareDataAndAllocationProfile):

  • runtime/JSFunctionInlines.h:

(JSC::JSFunction::canUseAllocationProfile):

Location:
trunk
Files:
1 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r228565 r228725  
     12018-02-19  Saam Barati  <sbarati@apple.com>
     2
     3        Don't use JSFunction's allocation profile when getting the prototype can be effectful
     4        https://bugs.webkit.org/show_bug.cgi?id=182942
     5        <rdar://problem/37584764>
     6
     7        Reviewed by Mark Lam.
     8
     9        * stress/get-prototype-create-this-effectful.js: Added.
     10
    1112018-02-16  Saam Barati  <sbarati@apple.com>
    212
  • trunk/Source/JavaScriptCore/ChangeLog

    r228720 r228725  
     12018-02-19  Saam Barati  <sbarati@apple.com>
     2
     3        Don't use JSFunction's allocation profile when getting the prototype can be effectful
     4        https://bugs.webkit.org/show_bug.cgi?id=182942
     5        <rdar://problem/37584764>
     6
     7        Reviewed by Mark Lam.
     8
     9        Prior to this patch, the create_this implementation assumed that anything
     10        that is a JSFunction can use the object allocation profile and go down the
     11        fast path to allocate the |this| object. Implied by this approach is that
     12        accessing the 'prototype' property of the incoming function is not an
     13        effectful operation. This is inherent to the ObjectAllocationProfile
     14        data structure: it caches the prototype field. However, getting the
     15        'prototype' property might be an effectful operation, e.g, it could
     16        be a getter. Many variants of functions in JS have the 'prototype' property
     17        as non-configurable. However, some functions, like bound functions, do not
     18        have the 'prototype' field with these attributes.
     19       
     20        This patch adds the notion of 'canUseAllocationProfile' to JSFunction
     21        and threads it through so that we only go down the fast path and use
     22        the allocation profile when the prototype property is non-configurable.
     23
     24        * bytecompiler/NodesCodegen.cpp:
     25        (JSC::ClassExprNode::emitBytecode):
     26        * dfg/DFGOperations.cpp:
     27        * runtime/CommonSlowPaths.cpp:
     28        (JSC::SLOW_PATH_DECL):
     29        * runtime/JSFunction.cpp:
     30        (JSC::JSFunction::prototypeForConstruction):
     31        (JSC::JSFunction::allocateAndInitializeRareData):
     32        (JSC::JSFunction::initializeRareData):
     33        (JSC::JSFunction::getOwnPropertySlot):
     34        (JSC::JSFunction::canUseAllocationProfileNonInline):
     35        * runtime/JSFunction.h:
     36        (JSC::JSFunction::ensureRareDataAndAllocationProfile):
     37        * runtime/JSFunctionInlines.h:
     38        (JSC::JSFunction::canUseAllocationProfile):
     39
    1402018-02-19  Saam Barati  <sbarati@apple.com>
    241
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r226650 r228725  
    38983898    bool needsHomeObject = false;
    38993899
    3900     // FIXME: Make the prototype non-configurable & non-writable.
    39013900    if (m_constructorExpression) {
    39023901        ASSERT(m_constructorExpression->isFuncExprNode());
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r227723 r228725  
    247247    NativeCallFrameTracer tracer(&vm, exec);
    248248    auto scope = DECLARE_THROW_SCOPE(vm);
    249     if (constructor->type() == JSFunctionType) {
    250         auto rareData = jsCast<JSFunction*>(constructor)->rareData(exec, inlineCapacity);
     249    if (constructor->type() == JSFunctionType && jsCast<JSFunction*>(constructor)->canUseAllocationProfile()) {
     250        auto rareData = jsCast<JSFunction*>(constructor)->ensureRareDataAndAllocationProfile(exec, inlineCapacity);
    251251        RETURN_IF_EXCEPTION(scope, nullptr);
    252252        Structure* structure = rareData->objectAllocationProfile()->structure();
  • trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp

    r226436 r228725  
    223223    JSObject* result;
    224224    JSObject* constructorAsObject = asObject(GET(bytecode.callee()).jsValue());
    225     if (constructorAsObject->type() == JSFunctionType) {
     225    if (constructorAsObject->type() == JSFunctionType && jsCast<JSFunction*>(constructorAsObject)->canUseAllocationProfile()) {
    226226        JSFunction* constructor = jsCast<JSFunction*>(constructorAsObject);
    227227        WriteBarrier<JSCell>& cachedCallee = bytecode.cachedCallee();
     
    232232
    233233        size_t inlineCapacity = bytecode.inlineCapacity();
    234         Structure* structure = constructor->rareData(exec, inlineCapacity)->objectAllocationProfile()->structure();
     234        Structure* structure = constructor->ensureRareDataAndAllocationProfile(exec, inlineCapacity)->objectAllocationProfile()->structure();
    235235        result = constructEmptyObject(exec, structure);
    236236        if (structure->hasPolyProto()) {
  • trunk/Source/JavaScriptCore/runtime/JSFunction.cpp

    r228500 r228725  
    140140JSObject* JSFunction::prototypeForConstruction(VM& vm, ExecState* exec)
    141141{
     142    // This code assumes getting the prototype is not effectful. That's only
     143    // true when we can use the allocation profile.
     144    ASSERT(canUseAllocationProfile());
    142145    auto scope = DECLARE_CATCH_SCOPE(vm);
    143146    JSValue prototype = get(exec, vm.propertyNames->prototype);
    144     ASSERT_UNUSED(scope, !scope.exception());
     147    scope.releaseAssertNoException();
    145148    if (prototype.isObject())
    146149        return asObject(prototype);
     
    152155{
    153156    ASSERT(!m_rareData);
     157    ASSERT(canUseAllocationProfile());
    154158    VM& vm = exec->vm();
    155159    JSObject* prototype = prototypeForConstruction(vm, exec);
     
    168172{
    169173    ASSERT(!!m_rareData);
     174    ASSERT(canUseAllocationProfile());
    170175    VM& vm = exec->vm();
    171176    JSObject* prototype = prototypeForConstruction(vm, exec);
     
    375380
    376381    if (propertyName == vm.propertyNames->prototype && thisObject->jsExecutable()->hasPrototypeProperty() && !thisObject->jsExecutable()->isClassConstructorFunction()) {
     382        // NOTE: class constructors define the prototype property in bytecode using
     383        // defineOwnProperty, which ends up calling into this code (see our defineOwnProperty
     384        // implementation below). The bytecode will end up doing the proper definition
     385        // with the property being non-writable/non-configurable. However, we must ignore
     386        // the initial materialization of the property so that the defineOwnProperty call
     387        // from bytecode succeeds. Otherwise, the materialization here would prevent the
     388        // defineOwnProperty from being able to overwrite the property.
    377389        unsigned attributes;
    378390        PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName, attributes);
  • trunk/Source/JavaScriptCore/runtime/JSFunction.h

    r228420 r228725  
    131131    }
    132132
    133     FunctionRareData* rareData(ExecState* exec, unsigned inlineCapacity)
    134     {
    135         if (UNLIKELY(!m_rareData))
    136             return allocateAndInitializeRareData(exec, inlineCapacity);
    137         if (UNLIKELY(!m_rareData->isObjectAllocationProfileInitialized()))
    138             return initializeRareData(exec, inlineCapacity);
    139         return m_rareData.get();
    140     }
     133    FunctionRareData* ensureRareDataAndAllocationProfile(ExecState*, unsigned inlineCapacity);
    141134
    142135    FunctionRareData* rareData()
     
    161154    JSObject* prototypeForConstruction(VM&, ExecState*);
    162155
     156    bool canUseAllocationProfile();
     157    bool canUseAllocationProfileNonInline();
     158
    163159protected:
    164160    JS_EXPORT_PRIVATE JSFunction(VM&, JSGlobalObject*, Structure*);
     
    167163    void finishCreation(VM&, NativeExecutable*, int length, const String& name);
    168164    void finishCreation(VM&);
    169 
    170     FunctionRareData* allocateRareData(VM&);
    171     FunctionRareData* allocateAndInitializeRareData(ExecState*, size_t inlineCapacity);
    172     FunctionRareData* initializeRareData(ExecState*, size_t inlineCapacity);
    173165
    174166    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
     
    192184        return function;
    193185    }
     186
     187    FunctionRareData* allocateRareData(VM&);
     188    FunctionRareData* allocateAndInitializeRareData(ExecState*, size_t inlineCapacity);
     189    FunctionRareData* initializeRareData(ExecState*, size_t inlineCapacity);
    194190
    195191    bool hasReifiedLength() const;
  • trunk/Source/JavaScriptCore/runtime/JSFunctionInlines.h

    r225891 r228725  
    108108}
    109109
     110inline bool JSFunction::canUseAllocationProfile()
     111{
     112    if (isHostFunction())
     113        return false;
     114
     115    // If we don't have a prototype property, we're not guaranteed it's
     116    // non-configurable. For example, user code can define the prototype
     117    // as a getter. JS semantics require that the getter is called every
     118    // time |construct| occurs with this function as new.target.
     119    return jsExecutable()->hasPrototypeProperty();
     120}
     121
     122inline FunctionRareData* JSFunction::ensureRareDataAndAllocationProfile(ExecState* exec, unsigned inlineCapacity)
     123{
     124    ASSERT(canUseAllocationProfile());
     125    if (UNLIKELY(!m_rareData))
     126        return allocateAndInitializeRareData(exec, inlineCapacity);
     127    if (UNLIKELY(!m_rareData->isObjectAllocationProfileInitialized()))
     128        return initializeRareData(exec, inlineCapacity);
     129    return m_rareData.get();
     130}
     131
    110132} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.