Skip to content

Commit 50e23bd

Browse files
committed
[MERGE #5710 @rhuanjl] Do not skip FromPropertyDescriptor in Proxy DefineOwnProperty
Merge pull request #5710 from rhuanjl:defineProperty Fix bug in JavascriptProxy::DefineOwnPropertyDescriptor 1. Previously the FromPropertyDescriptor call was skipped if the descriptor came from ToPropertyDescriptor BUT in the case of a descriptor object with a getter this could result in it being called twice 2. Update comments with spec text in this function as they were out of date 3. Add test case for point 1 Fixes: #5680
2 parents 842077d + 2aa6c9d commit 50e23bd

File tree

3 files changed

+35
-25
lines changed

3 files changed

+35
-25
lines changed

lib/Runtime/Language/JavascriptOperators.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -8643,7 +8643,10 @@ using namespace Js;
86438643
{
86448644
JavascriptOperators::InitProperty(object, PropertyIds::value, descriptor.GetValue());
86458645
}
8646-
JavascriptOperators::InitProperty(object, PropertyIds::writable, JavascriptBoolean::ToVar(descriptor.IsWritable(),scriptContext));
8646+
if (descriptor.WritableSpecified())
8647+
{
8648+
JavascriptOperators::InitProperty(object, PropertyIds::writable, JavascriptBoolean::ToVar(descriptor.IsWritable(), scriptContext));
8649+
}
86478650
}
86488651
else if (descriptor.IsAccessorDescriptor())
86498652
{

lib/Runtime/Library/JavascriptProxy.cpp

+16-24
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,7 @@ namespace Js
16991699

17001700
BOOL JavascriptProxy::DefineOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propId, const PropertyDescriptor& descriptor, bool throwOnError, ScriptContext* requestContext)
17011701
{
1702+
// #sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc
17021703
PROBE_STACK(requestContext, Js::Constants::MinStackDefault);
17031704

17041705
// Reject implicit call
@@ -1712,10 +1713,11 @@ namespace Js
17121713
JavascriptProxy* proxy = JavascriptProxy::FromVar(obj);
17131714

17141715
//1. Assert: IsPropertyKey(P) is true.
1715-
//2. Let handler be the value of the[[ProxyHandler]] internal slot of O.
1716+
//2. Let handler be O.[[ProxyHandler]].
17161717
RecyclableObject *handlerObj = proxy->MarshalHandler(requestContext);
17171718

17181719
//3. If handler is null, then throw a TypeError exception.
1720+
//4. Assert: Type(handler) is Object.
17191721
if (handlerObj == nullptr)
17201722
{
17211723
// the proxy has been revoked; TypeError.
@@ -1724,13 +1726,12 @@ namespace Js
17241726
JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnRevokedProxy, _u("definePropertyDescriptor"));
17251727
}
17261728

1727-
//4. Let target be the value of the[[ProxyTarget]] internal slot of O.
1729+
//5. Let target be O.[[ProxyTarget]].
17281730
RecyclableObject *targetObj = proxy->MarshalTarget(requestContext);
17291731

1730-
//5. Let trap be the result of GetMethod(handler, "defineProperty").
1731-
//6. ReturnIfAbrupt(trap).
1732+
//6. Let trap be ? GetMethod(handler, "defineProperty").
17321733
//7. If trap is undefined, then
1733-
//a.Return the result of calling the[[DefineOwnProperty]] internal method of target with arguments P and Desc.
1734+
//a. Return ? target.[[DefineOwnProperty]](P, Desc).
17341735
JavascriptFunction* defineOwnPropertyMethod = proxy->GetMethodHelper(PropertyIds::defineProperty, requestContext);
17351736

17361737
Assert(!requestContext->IsHeapEnumInProgress());
@@ -1740,18 +1741,10 @@ namespace Js
17401741
}
17411742

17421743
//8. Let descObj be FromPropertyDescriptor(Desc).
1743-
//9. NOTE If Desc was originally generated from an object using ToPropertyDescriptor, then descObj will be that original object.
1744-
//10. Let trapResult be the result of calling the[[Call]] internal method of trap with handler as the this value and a new List containing target, P, and descObj.
1745-
//11. Let booleanTrapResult be ToBoolean(trapResult).
1746-
//12. ReturnIfAbrupt(booleanTrapResult).
1747-
//13. If booleanTrapResult is false, then return false.
1748-
//14. Let targetDesc be the result of calling the[[GetOwnProperty]] internal method of target with argument P.
1749-
//15. ReturnIfAbrupt(targetDesc).
1750-
Var descVar = descriptor.GetOriginal();
1751-
if (descVar == nullptr)
1752-
{
1753-
descVar = JavascriptOperators::FromPropertyDescriptor(descriptor, requestContext);
1754-
}
1744+
//9. Let booleanTrapResult be ToBoolean(? Call(trap, handler, << target, P, descObj >> )).
1745+
//10. If booleanTrapResult is false, then return false.
1746+
//11. Let targetDesc be ? target.[[GetOwnProperty]](P).
1747+
Var descVar = JavascriptOperators::FromPropertyDescriptor(descriptor, requestContext);
17551748

17561749
Var propertyName = GetName(requestContext, propId);
17571750

@@ -1766,18 +1759,17 @@ namespace Js
17661759
return defineResult;
17671760
}
17681761

1769-
//16. Let extensibleTarget be the result of IsExtensible(target).
1770-
//17. ReturnIfAbrupt(extensibleTarget).
1771-
//18. If Desc has a[[Configurable]] field and if Desc.[[Configurable]] is false, then
1762+
//12. Let extensibleTarget be ? IsExtensible(target).
1763+
//13. If Desc has a[[Configurable]] field and if Desc.[[Configurable]] is false, then
17721764
// a.Let settingConfigFalse be true.
1773-
//19. Else let settingConfigFalse be false.
1774-
//20. If targetDesc is undefined, then
1765+
//14. Else let settingConfigFalse be false.
1766+
//15. If targetDesc is undefined, then
17751767
// a.If extensibleTarget is false, then throw a TypeError exception.
17761768
// b.If settingConfigFalse is true, then throw a TypeError exception.
1777-
//21. Else targetDesc is not undefined,
1769+
//16. Else targetDesc is not undefined,
17781770
// a.If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, then throw a TypeError exception.
17791771
// b.If settingConfigFalse is true and targetDesc.[[Configurable]] is true, then throw a TypeError exception.
1780-
//22. Return true.
1772+
//17. Return true.
17811773
PropertyDescriptor targetDescriptor;
17821774
BOOL hasProperty = JavascriptOperators::GetOwnPropertyDescriptor(targetObj, propId, requestContext, &targetDescriptor);
17831775
BOOL isExtensible = targetObj->IsExtensible();

test/es6/proxybugs.js

+15
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,21 @@ var tests = [
495495
});
496496
assert.throws(()=> { Object.keys(proxy);}, TypeError, "proxy's ownKeys is returning duplicate keys", "Proxy's ownKeys trap returned duplicate keys");
497497
}
498+
},
499+
{
500+
name : "Proxy with DefineOwnProperty trap should not get descriptor properties twice",
501+
body() {
502+
const desc = { };
503+
let counter = 0;
504+
let handler = {
505+
defineProperty : function (oTarget, sKey, oDesc) {
506+
return Reflect.defineProperty(oTarget, sKey, oDesc);
507+
}
508+
};
509+
Object.defineProperty(desc, "writable", { get: function () { ++counter; return true; }});
510+
Object.defineProperty(new Proxy({}, handler), "test", desc);
511+
assert.areEqual(1, counter, "Writable property on descriptor should only be checked once");
512+
}
498513
}
499514
];
500515

0 commit comments

Comments
 (0)