Skip to content
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

Merged
merged 1 commit into from
Mar 13, 2018

Conversation

rhuanjl
Copy link
Collaborator

@rhuanjl rhuanjl commented Mar 10, 2018

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

JsValueRef undefined = JS_INVALID_REFERENCE;
IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undefined));
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, targetProperty, undefined, true));
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, handlerProperty, undefined, true));
Copy link
Contributor

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 {}

Copy link
Collaborator Author

@rhuanjl rhuanjl Mar 11, 2018

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:

  1. if given a non-proxy -> returns undefined
  2. 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
  3. if given a proxy that has not been revoked returns {target : proxyTarget, handler : proxyHandler}

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Collaborator Author

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.

Copy link
Contributor

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.

VALIDATE_INCOMING_REFERENCE(object, scriptContext);
PARAM_NOT_NULL(isProxy);

*isProxy = false;
Copy link
Contributor

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.

Copy link
Collaborator Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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.


Js::JavascriptProxy* proxy = Js::JavascriptProxy::UnsafeFromVar(object);

if (target != nullptr && handler != nullptr)
Copy link
Contributor

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?

Copy link
Collaborator Author

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();
Copy link
Contributor

@jackhorton jackhorton Mar 11, 2018

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.

Copy link
Collaborator Author

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.

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops fixed (and squashed)

Copy link
Contributor

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.

Copy link
Contributor

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.

@rhuanjl rhuanjl force-pushed the JsGetProxyProperties branch 2 times, most recently from d8e37f4 to 9bd95d3 Compare March 11, 2018 18:14
bool isProxy = false;
JsValueRef target;
JsValueRef handler;
ChakraRTInterface::JsGetProxyProperties(arguments[1], &isProxy, &target, &handler);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap in IfJsrtErrorSetGo?

if (target == nullptr)
{
JsValueRef undefined = JS_INVALID_REFERENCE;
IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undefined));
Copy link
Contributor

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?

IfJsrtErrorSetGo(CreatePropertyIdFromString("handler", &handlerProperty));
IfJsrtErrorSetGo(ChakraRTInterface::JsCreateObject(&returnValue));

if (target == nullptr)
Copy link
Contributor

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.

else
{
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, targetProperty, target, true));
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, handlerProperty, handler, true));
Copy link
Contributor

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?

/// 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.
Copy link
Contributor

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;
Copy link
Contributor

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.

Copy link
Contributor

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.

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.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in revoked.

@rhuanjl
Copy link
Collaborator Author

rhuanjl commented Mar 13, 2018

@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)


if (target == JS_INVALID_REFERENCE)
{
ChakraRTInterface::JsGetTrueValue(&revoked);
Copy link
Contributor

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?

}
else
{
ChakraRTInterface::JsGetFalseValue(&revoked);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same nitpick about IfJsrtErrorSetGo.

Copy link
Contributor

@boingoing boingoing left a 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

@boingoing boingoing self-assigned this Mar 13, 2018
Copy link
Contributor

@kfarnung kfarnung left a 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;
Copy link
Contributor

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.

@rhuanjl rhuanjl force-pushed the JsGetProxyProperties branch from 39fa9db to 4c538d6 Compare March 13, 2018 19:29
@rhuanjl
Copy link
Collaborator Author

rhuanjl commented Mar 13, 2018

@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.

@rhuanjl
Copy link
Collaborator Author

rhuanjl commented Mar 13, 2018

@dotnet-bot test Windows ci_slow_x64_debug please

let properties = WScript.GetProxyProperties(proxy);

let names = Object.getOwnPropertyNames(properties);
assert.areEqual(names.length, 3, "proxy properties names should have length 2");
Copy link
Contributor

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 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should*


In reply to: 174286438 [](ancestors = 174286438)

@rhuanjl rhuanjl force-pushed the JsGetProxyProperties branch from 4c538d6 to e891f8c Compare March 13, 2018 21:26
@boingoing
Copy link
Contributor

@rhuanjl Thanks once again for your contribution here! I'll start the internal testing process for this one.

@chakrabot chakrabot merged commit e891f8c into chakra-core:release/1.9 Mar 13, 2018
chakrabot pushed a commit that referenced this pull request Mar 13, 2018
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
@rhuanjl rhuanjl deleted the JsGetProxyProperties branch March 13, 2018 23:11
chakrabot pushed a commit that referenced this pull request Mar 14, 2018
…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
@rhuanjl
Copy link
Collaborator Author

rhuanjl commented Mar 14, 2018

@boingoing thanks for getting this merged I hope it's helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants