Ignore:
Timestamp:
Feb 25, 2016, 2:58:23 PM (9 years ago)
Author:
[email protected]
Message:

[ES6] Implement Proxy.Set
https://bugs.webkit.org/show_bug.cgi?id=154511

Reviewed by Filip Pizlo.

This patch is mostly an implementation of
Proxy.Set with respect to section 9.5.9
of the ECMAScript spec.
https://tc39.github.io/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver

This patch also changes JSObject::putInline and JSObject::putByIndex
to be aware that a Proxy in the prototype chain will intercept
property accesses.

  • runtime/JSObject.cpp:

(JSC::JSObject::putInlineSlow):
(JSC::JSObject::attemptToInterceptPutByIndexOnHoleForPrototype):

  • runtime/JSObject.h:
  • runtime/JSObjectInlines.h:

(JSC::JSObject::canPerformFastPutInline):
(JSC::JSObject::putInline):

  • runtime/JSType.h:
  • runtime/ProxyObject.cpp:

(JSC::ProxyObject::getOwnPropertySlotByIndex):
(JSC::ProxyObject::performPut):
(JSC::ProxyObject::put):
(JSC::ProxyObject::putByIndexCommon):
(JSC::ProxyObject::putByIndex):
(JSC::performProxyCall):
(JSC::ProxyObject::getCallData):
(JSC::performProxyConstruct):
(JSC::ProxyObject::deletePropertyByIndex):
(JSC::ProxyObject::visitChildren):

  • runtime/ProxyObject.h:

(JSC::ProxyObject::create):
(JSC::ProxyObject::createStructure):
(JSC::ProxyObject::target):
(JSC::ProxyObject::handler):

  • tests/es6.yaml:
  • tests/stress/proxy-set.js: Added.

(assert):
(throw.new.Error.let.handler.set 45):
(throw.new.Error):
(let.target.set x):
(let.target.get x):
(set let):

Location:
trunk/Source/JavaScriptCore/runtime
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/JSObject.cpp

    r196999 r197136  
    422422                }
    423423            }
     424        }
     425        if (obj->type() == ProxyObjectType) {
     426            ProxyObject* proxy = jsCast<ProxyObject*>(obj);
     427            proxy->ProxyObject::put(proxy, exec, propertyName, value, slot);
     428            return;
    424429        }
    425430        JSValue prototype = obj->prototype();
     
    19171922            }
    19181923        }
     1924
     1925        if (current->type() == ProxyObjectType) {
     1926            ProxyObject* proxy = jsCast<ProxyObject*>(current);
     1927            proxy->putByIndexCommon(exec, thisValue, i, value, shouldThrow);
     1928            return true;
     1929        }
    19191930       
    19201931        JSValue prototypeValue = current->prototype();
  • trunk/Source/JavaScriptCore/runtime/JSObject.h

    r196849 r197136  
    856856    template<PutMode>
    857857    bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&);
     858    bool canPerformFastPutInline(ExecState* exec, VM&, PropertyName);
    858859
    859860    JS_EXPORT_PRIVATE NEVER_INLINE void putInlineSlow(ExecState*, PropertyName, JSValue, PutPropertySlot&);
  • trunk/Source/JavaScriptCore/runtime/JSObjectInlines.h

    r194175 r197136  
    3131namespace JSC {
    3232
     33ALWAYS_INLINE bool JSObject::canPerformFastPutInline(ExecState* exec, VM& vm, PropertyName propertyName)
     34{
     35    if (UNLIKELY(propertyName == exec->propertyNames().underscoreProto))
     36        return false;
     37
     38    // Check if there are any setters or getters in the prototype chain
     39    JSValue prototype;
     40    JSObject* obj = this;
     41    while (true) {
     42        if (obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || obj->type() == ProxyObjectType)
     43            return false;
     44
     45        prototype = obj->prototype();
     46        if (prototype.isNull())
     47            return true;
     48
     49        obj = asObject(prototype);
     50    }
     51
     52    ASSERT_NOT_REACHED();
     53}
     54
    3355// ECMA 8.6.2.2
    3456ALWAYS_INLINE void JSObject::putInline(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
     
    4567        return;
    4668    }
    47    
    48     // Check if there are any setters or getters in the prototype chain
    49     JSValue prototype;
    50     if (propertyName != exec->propertyNames().underscoreProto) {
    51         for (JSObject* obj = thisObject; !obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
    52             prototype = obj->prototype();
    53             if (prototype.isNull()) {
    54                 ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName));
    55                 if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot)
    56                     && slot.isStrictMode())
    57                     throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
    58                 return;
    59             }
    60         }
    61     }
    6269
    63     thisObject->putInlineSlow(exec, propertyName, value, slot);
     70    if (thisObject->canPerformFastPutInline(exec, vm, propertyName)) {
     71        ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName));
     72        if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot)
     73            && slot.isStrictMode())
     74            throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
     75    } else
     76        thisObject->putInlineSlow(exec, propertyName, value, slot);
    6477}
    6578
  • trunk/Source/JavaScriptCore/runtime/JSType.h

    r189279 r197136  
    7979    ClosureObjectType,
    8080
    81     LastJSCObjectType = ClosureObjectType,
     81    ProxyObjectType,
     82
     83    LastJSCObjectType = ProxyObjectType,
    8284};
    8385
  • trunk/Source/JavaScriptCore/runtime/ProxyObject.cpp

    r197042 r197136  
    301301}
    302302
     303template <typename PerformDefaultPutFunction>
     304void ProxyObject::performPut(ExecState* exec, JSValue putValue, JSValue thisValue, PropertyName propertyName, PerformDefaultPutFunction performDefaultPutFunction)
     305{
     306    VM& vm = exec->vm();
     307    JSValue handlerValue = this->handler();
     308    if (handlerValue.isNull()) {
     309        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
     310        return;
     311    }
     312
     313    JSObject* handler = jsCast<JSObject*>(handlerValue);
     314    CallData callData;
     315    CallType callType;
     316    JSValue setMethod = handler->getMethod(exec, callData, callType, vm.propertyNames->set, ASCIILiteral("'set' property of a Proxy's handler should be callable."));
     317    if (exec->hadException())
     318        return;
     319    JSObject* target = this->target();
     320    if (setMethod.isUndefined()) {
     321        performDefaultPutFunction();
     322        return;
     323    }
     324
     325    MarkedArgumentBuffer arguments;
     326    arguments.append(target);
     327    arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
     328    arguments.append(putValue);
     329    arguments.append(thisValue);
     330    JSValue trapResult = call(exec, setMethod, callType, callData, handler, arguments);
     331    if (exec->hadException())
     332        return;
     333    bool trapResultAsBool = trapResult.toBoolean(exec);
     334    if (exec->hadException())
     335        return;
     336    if (!trapResultAsBool)
     337        return;
     338
     339    PropertyDescriptor descriptor;
     340    if (target->getOwnPropertyDescriptor(exec, propertyName, descriptor)) {
     341        if (descriptor.isDataDescriptor() && !descriptor.configurable() && !descriptor.writable()) {
     342            if (!sameValue(exec, descriptor.value(), putValue)) {
     343                throwVMTypeError(exec, ASCIILiteral("Proxy handler's 'set' on a non-configurable and non-writable property on 'target' should either return false or be the same value already on the 'target'."));
     344                return;
     345            }
     346        } else if (descriptor.isAccessorDescriptor() && !descriptor.configurable() && descriptor.setter().isUndefined()) {
     347            throwVMTypeError(exec, ASCIILiteral("Proxy handler's 'set' method on a non-configurable accessor property without a setter should return false."));
     348            return;
     349        }
     350    }
     351}
     352
     353void ProxyObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
     354{
     355    VM& vm = exec->vm();
     356    ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
     357    auto performDefaultPut = [&] () {
     358        JSObject* target = jsCast<JSObject*>(thisObject->target());
     359        target->methodTable(vm)->put(target, exec, propertyName, value, slot);
     360    };
     361    thisObject->performPut(exec, value, slot.thisValue(), propertyName, performDefaultPut);
     362}
     363
     364void ProxyObject::putByIndexCommon(ExecState* exec, JSValue thisValue, unsigned propertyName, JSValue putValue, bool shouldThrow)
     365{
     366    VM& vm = exec->vm();
     367    Identifier ident = Identifier::from(exec, propertyName);
     368    if (exec->hadException())
     369        return;
     370    auto performDefaultPut = [&] () {
     371        JSObject* target = this->target();
     372        bool isStrictMode = shouldThrow;
     373        PutPropertySlot slot(thisValue, isStrictMode); // We must preserve the "this" target of the putByIndex.
     374        target->methodTable(vm)->put(target, exec, ident.impl(), putValue, slot);
     375    };
     376    performPut(exec, putValue, thisValue, ident.impl(), performDefaultPut);
     377}
     378
     379void ProxyObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
     380{
     381    ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
     382    thisObject->putByIndexCommon(exec, thisObject, propertyName, value, shouldThrow);
     383}
     384
    303385static EncodedJSValue JSC_HOST_CALL performProxyCall(ExecState* exec)
    304386{
     
    346428}
    347429
    348 void ProxyObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
    349 {
    350     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
    351     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
    352     Base::visitChildren(thisObject, visitor);
    353 
    354     visitor.append(&thisObject->m_target);
    355     visitor.append(&thisObject->m_handler);
    356 }
    357 
    358430static EncodedJSValue JSC_HOST_CALL performProxyConstruct(ExecState* exec)
    359431{
     
    477549}
    478550
     551void ProxyObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     552{
     553    ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
     554    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     555    Base::visitChildren(thisObject, visitor);
     556
     557    visitor.append(&thisObject->m_target);
     558    visitor.append(&thisObject->m_handler);
     559}
     560
    479561} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/ProxyObject.h

    r197042 r197136  
    3636    typedef JSNonFinalObject Base;
    3737
    38     const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | TypeOfShouldCallGetCallData;
     38    const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | TypeOfShouldCallGetCallData | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero;
    3939
    4040    static ProxyObject* create(ExecState* exec, Structure* structure, JSValue target, JSValue handler)
     
    4848    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
    4949    {
    50         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
     50        return Structure::create(vm, globalObject, prototype, TypeInfo(ProxyObjectType, StructureFlags), info(), NonArray | MayHaveIndexedAccessors);
    5151    }
    5252
     
    5555    JSObject* target() { return m_target.get(); }
    5656    JSValue handler() { return m_handler.get(); }
     57
     58    static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
     59    static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
     60    void putByIndexCommon(ExecState*, JSValue thisValue, unsigned propertyName, JSValue putValue, bool shouldThrow);
    5761
    5862private:
     
    7377    template <typename DefaultDeleteFunction>
    7478    bool performDelete(ExecState*, PropertyName, DefaultDeleteFunction);
     79    template <typename PerformDefaultPutFunction>
     80    void performPut(ExecState*, JSValue putValue, JSValue thisValue, PropertyName, PerformDefaultPutFunction);
    7581
    7682    WriteBarrier<JSObject> m_target;
Note: See TracChangeset for help on using the changeset viewer.