Skip to content

Commit 6861888

Browse files
committed
[MERGE #4806 @rhuanjl] Implement JsGetProxyProperties API 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
2 parents f58e17f + e891f8c commit 6861888

11 files changed

+231
-0
lines changed

Diff for: bin/ChakraCore/ChakraCore.def

+2
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,5 @@ JsLessThanOrEqual
6464
JsCreateEnhancedFunction
6565

6666
JsSetHostPromiseRejectionTracker
67+
68+
JsGetProxyProperties

Diff for: bin/ch/ChakraRtInterface.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
154154
m_jsApiHooks.pfJsrtCopyString = (JsAPIHooks::JsrtCopyString)GetChakraCoreSymbol(library, "JsCopyString");
155155
m_jsApiHooks.pfJsrtCreatePropertyId = (JsAPIHooks::JsrtCreatePropertyId)GetChakraCoreSymbol(library, "JsCreatePropertyId");
156156
m_jsApiHooks.pfJsrtCreateExternalArrayBuffer = (JsAPIHooks::JsrtCreateExternalArrayBuffer)GetChakraCoreSymbol(library, "JsCreateExternalArrayBuffer");
157+
m_jsApiHooks.pfJsrtGetProxyProperties = (JsAPIHooks::JsrtGetProxyProperties)GetChakraCoreSymbol(library, "JsGetProxyProperties");
157158

158159
m_jsApiHooks.pfJsrtTTDCreateRecordRuntime = (JsAPIHooks::JsrtTTDCreateRecordRuntimePtr)GetChakraCoreSymbol(library, "JsTTDCreateRecordRuntime");
159160
m_jsApiHooks.pfJsrtTTDCreateReplayRuntime = (JsAPIHooks::JsrtTTDCreateReplayRuntimePtr)GetChakraCoreSymbol(library, "JsTTDCreateReplayRuntime");

Diff for: bin/ch/ChakraRtInterface.h

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ struct JsAPIHooks
8686

8787
typedef JsErrorCode(WINAPI *JsrtCreateExternalArrayBuffer)(void *data, unsigned int byteLength, JsFinalizeCallback finalizeCallback, void *callbackState, JsValueRef *result);
8888
typedef JsErrorCode(WINAPI *JsrtCreatePropertyId)(const char *name, size_t length, JsPropertyIdRef *propertyId);
89+
typedef JsErrorCode(WINAPI *JsrtGetProxyProperties)(JsValueRef object, bool* isProxy, JsValueRef* target, JsValueRef* handler);
8990

9091
typedef JsErrorCode(WINAPI *JsrtTTDCreateRecordRuntimePtr)(JsRuntimeAttributes attributes, bool enableDebugging, size_t snapInterval, size_t snapHistoryLength, TTDOpenResourceStreamCallback openResourceStream, JsTTDWriteBytesToStreamCallback writeBytesToStream, JsTTDFlushAndCloseStreamCallback flushAndCloseStream, JsThreadServiceCallback threadService, JsRuntimeHandle *runtime);
9192
typedef JsErrorCode(WINAPI *JsrtTTDCreateReplayRuntimePtr)(JsRuntimeAttributes attributes, const char* infoUri, size_t infoUriCount, bool enableDebugging, TTDOpenResourceStreamCallback openResourceStream, JsTTDReadBytesFromStreamCallback readBytesFromStream, JsTTDFlushAndCloseStreamCallback flushAndCloseStream, JsThreadServiceCallback threadService, JsRuntimeHandle *runtime);
@@ -183,6 +184,7 @@ struct JsAPIHooks
183184
JsrtCopyString pfJsrtCopyString;
184185
JsrtCreatePropertyId pfJsrtCreatePropertyId;
185186
JsrtCreateExternalArrayBuffer pfJsrtCreateExternalArrayBuffer;
187+
JsrtGetProxyProperties pfJsrtGetProxyProperties;
186188

187189
JsrtTTDCreateRecordRuntimePtr pfJsrtTTDCreateRecordRuntime;
188190
JsrtTTDCreateReplayRuntimePtr pfJsrtTTDCreateReplayRuntime;
@@ -412,6 +414,7 @@ class ChakraRTInterface
412414
static JsErrorCode WINAPI JsCreateStringUtf16(const uint16_t *content, size_t length, JsValueRef *value) { return HOOK_JS_API(CreateStringUtf16(content, length, value)); }
413415
static JsErrorCode WINAPI JsCreatePropertyId(const char *name, size_t length, JsPropertyIdRef *propertyId) { return HOOK_JS_API(CreatePropertyId(name, length, propertyId)); }
414416
static JsErrorCode WINAPI JsCreateExternalArrayBuffer(void *data, unsigned int byteLength, JsFinalizeCallback finalizeCallback, void *callbackState, JsValueRef *result) { return HOOK_JS_API(CreateExternalArrayBuffer(data, byteLength, finalizeCallback, callbackState, result)); }
417+
static JsErrorCode WINAPI JsGetProxyProperties(JsValueRef object, bool* isProxy, JsValueRef* target, JsValueRef* handler) { return HOOK_JS_API(GetProxyProperties(object, isProxy, target, handler)); }
415418
};
416419

417420
class AutoRestoreContext

Diff for: bin/ch/WScriptJsrt.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,7 @@ bool WScriptJsrt::Initialize()
859859
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadTextFile", LoadTextFileCallback));
860860
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Flag", FlagCallback));
861861
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "RegisterModuleSource", RegisterModuleSourceCallback));
862+
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "GetProxyProperties", GetProxyPropertiesCallback));
862863

863864
// ToDo Remove
864865
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Edit", EmptyCallback));
@@ -1371,6 +1372,57 @@ JsValueRef __stdcall WScriptJsrt::SleepCallback(JsValueRef callee, bool isConstr
13711372
return returnValue;
13721373
}
13731374

1375+
JsValueRef __stdcall WScriptJsrt::GetProxyPropertiesCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
1376+
{
1377+
HRESULT hr = E_FAIL;
1378+
JsValueRef returnValue = JS_INVALID_REFERENCE;
1379+
JsValueRef undefined = JS_INVALID_REFERENCE;
1380+
JsErrorCode errorCode = JsNoError;
1381+
1382+
IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undefined));
1383+
1384+
returnValue = undefined;
1385+
1386+
if (argumentCount > 1)
1387+
{
1388+
bool isProxy = false;
1389+
JsValueRef target;
1390+
JsValueRef handler;
1391+
IfJsrtErrorSetGo(ChakraRTInterface::JsGetProxyProperties(arguments[1], &isProxy, &target, &handler));
1392+
1393+
if (isProxy)
1394+
{
1395+
JsPropertyIdRef targetProperty;
1396+
JsPropertyIdRef handlerProperty;
1397+
JsPropertyIdRef revokedProperty;
1398+
1399+
IfJsrtErrorSetGo(CreatePropertyIdFromString("target", &targetProperty));
1400+
IfJsrtErrorSetGo(CreatePropertyIdFromString("handler", &handlerProperty));
1401+
IfJsrtErrorSetGo(CreatePropertyIdFromString("revoked", &revokedProperty));
1402+
IfJsrtErrorSetGo(ChakraRTInterface::JsCreateObject(&returnValue));
1403+
1404+
JsValueRef revoked = JS_INVALID_REFERENCE;
1405+
1406+
if (target == JS_INVALID_REFERENCE)
1407+
{
1408+
IfJsrtErrorSetGo(ChakraRTInterface::JsGetTrueValue(&revoked));
1409+
target = undefined;
1410+
handler = undefined;
1411+
}
1412+
else
1413+
{
1414+
IfJsrtErrorSetGo(ChakraRTInterface::JsGetFalseValue(&revoked));
1415+
}
1416+
1417+
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, handlerProperty, handler, true));
1418+
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, targetProperty, target, true));
1419+
IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, revokedProperty, revoked, true));
1420+
}
1421+
}
1422+
Error:
1423+
return returnValue;
1424+
}
1425+
13741426
bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode)
13751427
{
13761428
LPCWSTR errorTypeString = ConvertErrorCodeToMessage(jsErrorCode);

Diff for: bin/ch/WScriptJsrt.h

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class WScriptJsrt
129129
static JsValueRef CALLBACK GetReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
130130
static JsValueRef CALLBACK LeavingCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
131131
static JsValueRef CALLBACK SleepCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
132+
static JsValueRef CALLBACK GetProxyPropertiesCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
132133

133134
static JsErrorCode FetchImportedModuleHelper(JsModuleRecord referencingModule, JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord, LPCSTR refdir = nullptr);
134135

Diff for: lib/Jsrt/ChakraCore.h

+28
Original file line numberDiff line numberDiff line change
@@ -1035,5 +1035,33 @@ CHAKRA_API
10351035
JsSetHostPromiseRejectionTracker(
10361036
_In_ JsHostPromiseRejectionTrackerCallback promiseRejectionTrackerCallback,
10371037
_In_opt_ void *callbackState);
1038+
1039+
/// <summary>
1040+
/// Determines if a provided object is a JavscriptProxy Object and
1041+
/// provides references to a Proxy's target and handler.
1042+
/// </summary>
1043+
/// <remarks>
1044+
/// Requires an active script context.
1045+
/// If object is not a Proxy object the target and handler parameters are not touched.
1046+
/// If nullptr is supplied for target or handler the function returns after
1047+
/// setting the isProxy value.
1048+
/// If the object is a revoked Proxy target and handler are set to JS_INVALID_REFERENCE.
1049+
/// If it is a Proxy object that has not been revoked target and handler are set to the
1050+
/// the object's target and handler.
1051+
/// </remarks>
1052+
/// <param name="object">The object that may be a Proxy.</param>
1053+
/// <param name="isProxy">Pointer to a Boolean - is the object a proxy?</param>
1054+
/// <param name="target">Pointer to a JsValueRef - the object's target.</param>
1055+
/// <param name="handler">Pointer to a JsValueRef - the object's handler.</param>
1056+
/// <returns>
1057+
/// The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
1058+
/// </returns>
1059+
CHAKRA_API
1060+
JsGetProxyProperties(
1061+
_In_ JsValueRef object,
1062+
_Out_ bool* isProxy,
1063+
_Out_opt_ JsValueRef* target,
1064+
_Out_opt_ JsValueRef* handler);
1065+
10381066
#endif // _CHAKRACOREBUILD
10391067
#endif // _CHAKRACORE_H_

Diff for: lib/Jsrt/Jsrt.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -5329,4 +5329,45 @@ CHAKRA_API JsSetHostPromiseRejectionTracker(_In_ JsHostPromiseRejectionTrackerCa
53295329
/*allowInObjectBeforeCollectCallback*/true);
53305330
}
53315331

5332+
CHAKRA_API JsGetProxyProperties (_In_ JsValueRef object, _Out_ bool* isProxy, _Out_opt_ JsValueRef* target, _Out_opt_ JsValueRef* handler)
5333+
{
5334+
return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext * scriptContext) -> JsErrorCode {
5335+
VALIDATE_INCOMING_REFERENCE(object, scriptContext);
5336+
PARAM_NOT_NULL(isProxy);
5337+
5338+
if (target != nullptr)
5339+
{
5340+
*target = JS_INVALID_REFERENCE;
5341+
}
5342+
5343+
if (handler != nullptr)
5344+
{
5345+
*handler = JS_INVALID_REFERENCE;
5346+
}
5347+
5348+
*isProxy = Js::JavascriptProxy::Is(object);
5349+
5350+
if (!*isProxy)
5351+
{
5352+
return JsNoError;
5353+
}
5354+
5355+
Js::JavascriptProxy* proxy = Js::JavascriptProxy::UnsafeFromVar(object);
5356+
bool revoked = proxy->IsRevoked();
5357+
5358+
if (target != nullptr && !revoked)
5359+
{
5360+
*target = static_cast<JsValueRef>(proxy->GetTarget());
5361+
}
5362+
5363+
if (handler != nullptr && !revoked)
5364+
{
5365+
*handler = static_cast<JsValueRef>(proxy->GetHandler());
5366+
}
5367+
5368+
return JsNoError;
5369+
},
5370+
/*allowInObjectBeforeCollectCallback*/true);
5371+
}
5372+
53325373
#endif // _CHAKRACOREBUILD

Diff for: lib/Runtime/Library/JavascriptProxy.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ namespace Js
1616
return JavascriptOperators::GetTypeId(obj) == TypeIds_Proxy;
1717
}
1818

19+
bool JavascriptProxy::IsRevoked() const
20+
{
21+
return (target == nullptr);
22+
}
23+
1924
RecyclableObject* JavascriptProxy::GetTarget()
2025
{
2126
if (target == nullptr)

Diff for: lib/Runtime/Library/JavascriptProxy.h

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ namespace Js
154154
virtual RecyclableObject* ToObject(ScriptContext * requestContext) override;
155155
virtual Var GetTypeOfString(ScriptContext* requestContext) override;
156156

157+
bool IsRevoked() const;
157158
BOOL SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, PropertyId propertyId, Var newValue, ScriptContext* requestContext, BOOL skipPrototypeCheck = FALSE);
158159
BOOL SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, Js::JavascriptString * propertyString, Var newValue, ScriptContext* requestContext);
159160

Diff for: test/es6/ProxyPropertiesAPI.js

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
7+
8+
let tests = [
9+
{
10+
name: "GetProxyProperties: no argugments",
11+
body: function () {
12+
let properties = WScript.GetProxyProperties();
13+
assert.isUndefined(properties, "ProxyProperties of nothing should be undefined.");
14+
}
15+
},
16+
{
17+
name: "GetProxyProperties: non-proxy arguments",
18+
body: function () {
19+
let properties = WScript.GetProxyProperties(undefined);
20+
assert.isUndefined(properties, "ProxyProperties of undefined should be undefined.");
21+
properties = WScript.GetProxyProperties(1);
22+
assert.isUndefined(properties, "ProxyProperties of number should be undefined.");
23+
properties = WScript.GetProxyProperties({});
24+
assert.isUndefined(properties, "ProxyProperties of non-proxy object should be undefined.");
25+
}
26+
},
27+
{
28+
name: "GetProxyProperties: revocable Proxy",
29+
body: function () {
30+
let revocable = Proxy.revocable({someProperty: true, otherProperty: false}, {otherProperty: true, newProperty: 5});
31+
let proxy = revocable.proxy;
32+
let properties = WScript.GetProxyProperties(proxy);
33+
34+
let names = Object.getOwnPropertyNames(properties);
35+
assert.areEqual(names.length, 3, "proxy properties names should have length 3.");
36+
assert.isTrue(names.includes("target"));
37+
assert.isTrue(names.includes("handler"));
38+
assert.isTrue(names.includes("revoked"));
39+
assert.isFalse(properties.revoked, "Revoked bool should be false.");
40+
41+
names = Object.getOwnPropertyNames(properties.target);
42+
assert.areEqual(names.length, 2, "proxy properties target names should have length 2.");
43+
assert.areEqual(properties.target.someProperty, true);
44+
assert.areEqual(properties.target.otherProperty, false);
45+
46+
names = Object.getOwnPropertyNames(properties.handler);
47+
assert.areEqual(names.length, 2, "proxy properties handler names should have length 2.");
48+
assert.areEqual(properties.handler.newProperty, 5);
49+
assert.areEqual(properties.handler.otherProperty, true);
50+
51+
revocable.revoke();
52+
properties = WScript.GetProxyProperties(proxy);
53+
54+
names = Object.getOwnPropertyNames(properties);
55+
assert.areEqual(names.length, 3, "proxy properties names for revokes proxy should have length 3.");
56+
assert.isTrue(names.includes("target"));
57+
assert.isTrue(names.includes("handler"));
58+
assert.isTrue(properties.revoked, "Revoked bool should be true.");
59+
60+
assert.isUndefined(properties.target, "Target of revoked proxy should be undefined.");
61+
assert.isUndefined(properties.handler, "Handler of revoked proxy should be undefined.");
62+
}
63+
},
64+
{
65+
name: "GetProxyProperties: normal Proxy",
66+
body: function () {
67+
let proxy = new Proxy({someProperty: true, otherProperty: false}, {otherProperty: true, newProperty: 5});
68+
let properties = WScript.GetProxyProperties(proxy);
69+
70+
let names = Object.getOwnPropertyNames(properties);
71+
assert.areEqual(names.length, 3, "proxy properties names should have length 3");
72+
assert.isTrue(names.includes("target"));
73+
assert.isTrue(names.includes("handler"));
74+
assert.isTrue(names.includes("revoked"));
75+
assert.isFalse(properties.revoked, "Revoked bool should be false.");
76+
77+
names = Object.getOwnPropertyNames(properties.target);
78+
assert.areEqual(names.length, 2, "proxy properties target names should have length 2");
79+
assert.areEqual(properties.target.someProperty, true);
80+
assert.areEqual(properties.target.otherProperty, false);
81+
82+
names = Object.getOwnPropertyNames(properties.handler);
83+
assert.areEqual(names.length, 2, "proxy properties handler names should have length 2");
84+
assert.areEqual(properties.handler.newProperty, 5);
85+
assert.areEqual(properties.handler.otherProperty, true);
86+
}
87+
}
88+
];
89+
90+
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

Diff for: test/es6/rlexe.xml

+7
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,13 @@
715715
<compile-flags>-args summary -endargs</compile-flags>
716716
</default>
717717
</test>
718+
<test>
719+
<default>
720+
<files>ProxyPropertiesAPI.js</files>
721+
<compile-flags>-args summary -endargs</compile-flags>
722+
<tags>exclude_jshost</tags>
723+
</default>
724+
</test>
718725
<test>
719726
<default>
720727
<files>proxybugs.js</files>

0 commit comments

Comments
 (0)