-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement JsGetProxyProperties API fixes #950 #4806
Implement JsGetProxyProperties API fixes #950 #4806
Conversation
bin/ch/WScriptJsrt.cpp
Outdated
JsValueRef undefined = JS_INVALID_REFERENCE; | ||
IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undefined)); | ||
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, targetProperty, undefined, true)); | ||
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, handlerProperty, undefined, true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if there is a benefit to doing this here -- the value of return.target will be undefined regardless of whether or not you set it. I personally would say that this method should return { target, handler}
or undefined
rather than {target, handler}
or {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not changed - I think there is potential utility to this - also it enables me to explicitly test the handling of a revoked proxy.
Currently this API does the following:
- if given a non-proxy -> returns undefined
- if given a revoked proxy -> returns { target : undefined, handler : undefined} -> you can explicitly detect a revoked proxy this way as the return value will have two properties not 0
- if given a proxy that has not been revoked returns {target : proxyTarget, handler : proxyHandler}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but then if nothing else, you still dont need to assign undefined to ret.target and ret.handler, they'll be undefined regardless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jackhorton They will, but then Object.keys(ret).length
would then be 0 instead of 2 which seems to be what @rhuanjl was getting at.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it could have {} as the return value for a revoked proxy - just seemed easier to explicitly test it this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The object returned from this helper can have a revoked
property set to true if handler and target are null. We could use that to detect a revoked proxy. Would be easier to test that. Pretty sure real proxy can never have undefined or null handler and target unless it's revoked, though. I'm not really invested in either solution.
lib/Jsrt/Jsrt.cpp
Outdated
VALIDATE_INCOMING_REFERENCE(object, scriptContext); | ||
PARAM_NOT_NULL(isProxy); | ||
|
||
*isProxy = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This initial assignment is probably unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was following the pseudo-policy I've seen of always initialising out values. But in this case considering it's set to a bool on the next line I can see your point - changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rhuanjl The idea I think is that out parameters are always written to regardless of which codepath you take (in languages like C# with native support for out params it’s actually an error not to do so). If, like here, the value is unconditionally set to something then initializing it is redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think SAL/Prefast is supposed to give errors for things like that, but I am not sure if it catches everything.
lib/Jsrt/Jsrt.cpp
Outdated
|
||
Js::JavascriptProxy* proxy = Js::JavascriptProxy::UnsafeFromVar(object); | ||
|
||
if (target != nullptr && handler != nullptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe check target and handler separately in case someone wanted one but not the other?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea - done
@@ -52,6 +52,7 @@ namespace Js | |||
JavascriptProxy(DynamicType * type, ScriptContext * scriptContext, RecyclableObject* target, RecyclableObject* handler); | |||
static BOOL Is(_In_ Var obj); | |||
static BOOL Is(_In_ RecyclableObject* obj); | |||
BOOL IsRevoked(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I personally think all new APIs should be bool
rather than BOOL
. This method can also be marked const, while we are at it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just following what was done in the file, changed now though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t understand the motivation for returning const bool
? The const
in this context seems redundant since bool
is an immutable value type. What am I missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fatcerberus I meant more like bool IsRevoked() const;
, since it can be called on const proxies with no side effects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops fixed (and squashed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also for clarification on the BOOL vs bool, BOOL was the old (microsoft-specific?) version before bool
was part of the C++ standard (and I think its still not even in C, ridiculously). Its main problem is that its typedef'd to int
, which causes silly conversion warnings among other things. Old APIs will remain BOOL indefinitely since changing it would affect almost every file (not to mention some uses of BOOL are for interaction with Win32 APIs that expect it), but I think we have been generally trying to avoid it for anything new unless it needs to interop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C does have bool now, but you have to include stdbool.h for the typedef as it’s normally called _Bool
. I use it in my own code, it was a long overdue addition.
d8e37f4
to
9bd95d3
Compare
bin/ch/WScriptJsrt.cpp
Outdated
bool isProxy = false; | ||
JsValueRef target; | ||
JsValueRef handler; | ||
ChakraRTInterface::JsGetProxyProperties(arguments[1], &isProxy, &target, &handler); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap in IfJsrtErrorSetGo
?
bin/ch/WScriptJsrt.cpp
Outdated
if (target == nullptr) | ||
{ | ||
JsValueRef undefined = JS_INVALID_REFERENCE; | ||
IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undefined)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already called JsGetUndefinedValue
above. Can we fold these two into one call at the top?
bin/ch/WScriptJsrt.cpp
Outdated
IfJsrtErrorSetGo(CreatePropertyIdFromString("handler", &handlerProperty)); | ||
IfJsrtErrorSetGo(ChakraRTInterface::JsCreateObject(&returnValue)); | ||
|
||
if (target == nullptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check to see if target == JS_INVALID_REFERENCE
.
bin/ch/WScriptJsrt.cpp
Outdated
else | ||
{ | ||
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, targetProperty, target, true)); | ||
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, handlerProperty, handler, true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a little bit weird to me to set both of these properties in both sides of the conditional. What about setting target
and handler
to undefined
if they're JS_INVALID_REFERENCE
and having one set of JsSetProperty
which always gets called?
lib/Jsrt/ChakraCore.h
Outdated
/// If object is not a Proxy object the target and handler parameters are not touched. | ||
/// If nullptr is supplied for target or handler the function returns after | ||
/// setting the isProxy value. | ||
/// If the object is a revoked Proxy target and handler are made to point to nullptr. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should set the target and handler to JS_INVALID_REFERENCE
instead of nullptr and have this reflected in the doc here.
|
||
if (!*isProxy) | ||
{ | ||
return JsNoError; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I'm mistaken, _Out_opt_
has the same semantics as _Out_
when it comes to initialization. That means this function should not return without initializing target
and handler
if they are not null. We should just initialize both of them to JS_INVALID_REFERENCE
before the assignment to isProxy
if they're non-null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, _Out_opt
from what I've been able to gather just means the out pointer itself can be nullptr - if it's a non-null pointer then it should indeed be treated the same as an _Out_
parameter.
test/es6/ProxyPropertiesAPI.js
Outdated
assert.isTrue(names.includes("handler")); | ||
|
||
assert.isUndefined(properties.target, "Target of revoked proxy should be undefined."); | ||
assert.isUndefined(properties.handler, "Handler of revoekd proxy should be undefined."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in revoked.
@boingoing Thanks for the review I think I've addressed all of your points. (Done in a separate commit for now to make it easy to compare) |
bin/ch/WScriptJsrt.cpp
Outdated
|
||
if (target == JS_INVALID_REFERENCE) | ||
{ | ||
ChakraRTInterface::JsGetTrueValue(&revoked); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency, can we wrap this in IfJsrtErrorSetGo
?
bin/ch/WScriptJsrt.cpp
Outdated
} | ||
else | ||
{ | ||
ChakraRTInterface::JsGetFalseValue(&revoked); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nitpick about IfJsrtErrorSetGo
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with nit
@@ -52,6 +52,7 @@ namespace Js | |||
JavascriptProxy(DynamicType * type, ScriptContext * scriptContext, RecyclableObject* target, RecyclableObject* handler); | |||
static BOOL Is(_In_ Var obj); | |||
static BOOL Is(_In_ RecyclableObject* obj); | |||
bool IsRevoked() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd move this down with the other non-static, non-virtual methods.
39fa9db
to
4c538d6
Compare
@boingoing Thanks for the review and sorry for the mistakes. I've fixed those additional points, updated to latest and squashed. Please let me know if anything else is needed. |
@dotnet-bot test Windows ci_slow_x64_debug please |
test/es6/ProxyPropertiesAPI.js
Outdated
let properties = WScript.GetProxyProperties(proxy); | ||
|
||
let names = Object.getOwnPropertyNames(properties); | ||
assert.areEqual(names.length, 3, "proxy properties names should have length 2"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
length 2 [](start = 81, length = 8)
shoul have 3 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4c538d6
to
e891f8c
Compare
@rhuanjl Thanks once again for your contribution here! I'll start the internal testing process for this one. |
Merge pull request #4806 from rhuanjl:JsGetProxyProperties Responding to issue #950 This PR: 1. Adds a JavascriptProxy::IsRevoked method to the Javascript proxy class (necessary for the below) 2. Adds a JsGetProxyProperties API to Jsrt which can: a) check if an object is a proxy -> set a provided bool to true/false b) if it is a proxy check if it's revoked c) if it is a revoked proxy set provided target and handler references to nullptr d) if it is a proxy that is not revoked provide references to it's target and handler 3. Tracks the same API through to WScriptJsrt in ch 4. Adds a test that uses the ch implementation (Targeting 1.9 as this will assist with an issue in node-chakracore nodejs/node-chakracore#488 ) **CC:** @jackhorton @kfarnung @dilijev
…fixes #950 Merge pull request #4806 from rhuanjl:JsGetProxyProperties Responding to issue #950 This PR: 1. Adds a JavascriptProxy::IsRevoked method to the Javascript proxy class (necessary for the below) 2. Adds a JsGetProxyProperties API to Jsrt which can: a) check if an object is a proxy -> set a provided bool to true/false b) if it is a proxy check if it's revoked c) if it is a revoked proxy set provided target and handler references to nullptr d) if it is a proxy that is not revoked provide references to it's target and handler 3. Tracks the same API through to WScriptJsrt in ch 4. Adds a test that uses the ch implementation (Targeting 1.9 as this will assist with an issue in node-chakracore nodejs/node-chakracore#488 ) **CC:** @jackhorton @kfarnung @dilijev
@boingoing thanks for getting this merged I hope it's helpful. |
Responding to issue #950
This PR:
a) check if an object is a proxy -> set a provided bool to true/false
b) if it is a proxy check if it's revoked
c) if it is a revoked proxy set provided target and handler references to nullptr
d) if it is a proxy that is not revoked provide references to it's target and handler
(Targeting 1.9 as this will assist with an issue in node-chakracore nodejs/node-chakracore#488 )
CC: @jackhorton @kfarnung @dilijev