diff --git a/lib/VM/JSObject.cpp b/lib/VM/JSObject.cpp index 1a55dc69ee9..b06dbad158f 100644 --- a/lib/VM/JSObject.cpp +++ b/lib/VM/JSObject.cpp @@ -1699,6 +1699,24 @@ CallResult JSObject::putComputedWithReceiver_RJS( if (LLVM_UNLIKELY( desc.flags.internalSetter || receiverHandle->isHostObject() || receiverHandle->isProxyObject())) { + // If putComputed is called on a proxy whose target's prototype + // is an array with a propname of 'length', then internalSetter + // will be true, and the receiver will be a proxy. In that case, + // proxy wins. + if (receiverHandle->isProxyObject()) { + if (*descDefinedRes) { + dpf.setValue = 1; + } else { + dpf = DefinePropertyFlags::getDefaultNewPropertyFlags(); + } + return JSProxy::defineOwnProperty( + receiverHandle, + runtime, + nameValPrimitiveHandle, + dpf, + valueHandle, + opFlags); + } SymbolID id{}; LAZY_TO_IDENTIFIER(runtime, nameValPrimitiveHandle, id); if (desc.flags.internalSetter) { @@ -1709,23 +1727,10 @@ CallResult JSObject::putComputedWithReceiver_RJS( desc.castToNamedPropertyDescriptorRef(), valueHandle, opFlags); - } else if (receiverHandle->isHostObject()) { - return vmcast(receiverHandle.get())->set(id, *valueHandle); } assert( - receiverHandle->isProxyObject() && "descriptor flags are impossible"); - if (*descDefinedRes) { - dpf.setValue = 1; - } else { - dpf = DefinePropertyFlags::getDefaultNewPropertyFlags(); - } - return JSProxy::defineOwnProperty( - receiverHandle, - runtime, - nameValPrimitiveHandle, - dpf, - valueHandle, - opFlags); + receiverHandle->isHostObject() && "descriptor flags are impossible"); + return vmcast(receiverHandle.get())->set(id, *valueHandle); } } diff --git a/test/hermes/proxy.js b/test/hermes/proxy.js index 1f82cdffa15..bc8102f3ed9 100644 --- a/test/hermes/proxy.js +++ b/test/hermes/proxy.js @@ -2125,5 +2125,14 @@ checkDeep({...f})(_ => ({a:1, b:2})) // uses fast array access (this will trip an assert otherwise) new Proxy([], {}).unshift(0); +// If putComputed is called on a proxy whose target's prototype is an +// array with a propname of 'length', then internalSetter will be +// true, and the receiver will be a proxy. In that case, proxy needs +// to win; the behavior may assert or be UB otherwise. +var p = new Proxy(Object.create([]), {}); +// using String() forces putComputed +p[String('length')] = 0x123; +p[0xABC] = 1111; + print('done'); // CHECK-LABEL: done