diff --git a/bin/ChakraCore/ChakraCore.def b/bin/ChakraCore/ChakraCore.def
index 3010e888d9b..33062845b2a 100644
--- a/bin/ChakraCore/ChakraCore.def
+++ b/bin/ChakraCore/ChakraCore.def
@@ -64,3 +64,5 @@ JsLessThanOrEqual
JsCreateEnhancedFunction
JsSetHostPromiseRejectionTracker
+
+JsGetProxyProperties
diff --git a/bin/ch/ChakraRtInterface.cpp b/bin/ch/ChakraRtInterface.cpp
index 503d6b8c025..ee6133310d2 100644
--- a/bin/ch/ChakraRtInterface.cpp
+++ b/bin/ch/ChakraRtInterface.cpp
@@ -154,6 +154,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
m_jsApiHooks.pfJsrtCopyString = (JsAPIHooks::JsrtCopyString)GetChakraCoreSymbol(library, "JsCopyString");
m_jsApiHooks.pfJsrtCreatePropertyId = (JsAPIHooks::JsrtCreatePropertyId)GetChakraCoreSymbol(library, "JsCreatePropertyId");
m_jsApiHooks.pfJsrtCreateExternalArrayBuffer = (JsAPIHooks::JsrtCreateExternalArrayBuffer)GetChakraCoreSymbol(library, "JsCreateExternalArrayBuffer");
+ m_jsApiHooks.pfJsrtGetProxyProperties = (JsAPIHooks::JsrtGetProxyProperties)GetChakraCoreSymbol(library, "JsGetProxyProperties");
m_jsApiHooks.pfJsrtTTDCreateRecordRuntime = (JsAPIHooks::JsrtTTDCreateRecordRuntimePtr)GetChakraCoreSymbol(library, "JsTTDCreateRecordRuntime");
m_jsApiHooks.pfJsrtTTDCreateReplayRuntime = (JsAPIHooks::JsrtTTDCreateReplayRuntimePtr)GetChakraCoreSymbol(library, "JsTTDCreateReplayRuntime");
diff --git a/bin/ch/ChakraRtInterface.h b/bin/ch/ChakraRtInterface.h
index 5bd00024930..f530c5edb40 100644
--- a/bin/ch/ChakraRtInterface.h
+++ b/bin/ch/ChakraRtInterface.h
@@ -86,6 +86,7 @@ struct JsAPIHooks
typedef JsErrorCode(WINAPI *JsrtCreateExternalArrayBuffer)(void *data, unsigned int byteLength, JsFinalizeCallback finalizeCallback, void *callbackState, JsValueRef *result);
typedef JsErrorCode(WINAPI *JsrtCreatePropertyId)(const char *name, size_t length, JsPropertyIdRef *propertyId);
+ typedef JsErrorCode(WINAPI *JsrtGetProxyProperties)(JsValueRef object, bool* isProxy, JsValueRef* target, JsValueRef* handler);
typedef JsErrorCode(WINAPI *JsrtTTDCreateRecordRuntimePtr)(JsRuntimeAttributes attributes, bool enableDebugging, size_t snapInterval, size_t snapHistoryLength, TTDOpenResourceStreamCallback openResourceStream, JsTTDWriteBytesToStreamCallback writeBytesToStream, JsTTDFlushAndCloseStreamCallback flushAndCloseStream, JsThreadServiceCallback threadService, JsRuntimeHandle *runtime);
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
JsrtCopyString pfJsrtCopyString;
JsrtCreatePropertyId pfJsrtCreatePropertyId;
JsrtCreateExternalArrayBuffer pfJsrtCreateExternalArrayBuffer;
+ JsrtGetProxyProperties pfJsrtGetProxyProperties;
JsrtTTDCreateRecordRuntimePtr pfJsrtTTDCreateRecordRuntime;
JsrtTTDCreateReplayRuntimePtr pfJsrtTTDCreateReplayRuntime;
@@ -412,6 +414,7 @@ class ChakraRTInterface
static JsErrorCode WINAPI JsCreateStringUtf16(const uint16_t *content, size_t length, JsValueRef *value) { return HOOK_JS_API(CreateStringUtf16(content, length, value)); }
static JsErrorCode WINAPI JsCreatePropertyId(const char *name, size_t length, JsPropertyIdRef *propertyId) { return HOOK_JS_API(CreatePropertyId(name, length, propertyId)); }
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)); }
+ static JsErrorCode WINAPI JsGetProxyProperties(JsValueRef object, bool* isProxy, JsValueRef* target, JsValueRef* handler) { return HOOK_JS_API(GetProxyProperties(object, isProxy, target, handler)); }
};
class AutoRestoreContext
diff --git a/bin/ch/WScriptJsrt.cpp b/bin/ch/WScriptJsrt.cpp
index 9b56f621611..83641502fac 100644
--- a/bin/ch/WScriptJsrt.cpp
+++ b/bin/ch/WScriptJsrt.cpp
@@ -859,6 +859,7 @@ bool WScriptJsrt::Initialize()
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadTextFile", LoadTextFileCallback));
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Flag", FlagCallback));
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "RegisterModuleSource", RegisterModuleSourceCallback));
+ IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "GetProxyProperties", GetProxyPropertiesCallback));
// ToDo Remove
IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Edit", EmptyCallback));
@@ -1371,6 +1372,57 @@ JsValueRef __stdcall WScriptJsrt::SleepCallback(JsValueRef callee, bool isConstr
return returnValue;
}
+JsValueRef __stdcall WScriptJsrt::GetProxyPropertiesCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
+{
+ HRESULT hr = E_FAIL;
+ JsValueRef returnValue = JS_INVALID_REFERENCE;
+ JsValueRef undefined = JS_INVALID_REFERENCE;
+ JsErrorCode errorCode = JsNoError;
+
+ IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undefined));
+
+ returnValue = undefined;
+
+ if (argumentCount > 1)
+ {
+ bool isProxy = false;
+ JsValueRef target;
+ JsValueRef handler;
+ IfJsrtErrorSetGo(ChakraRTInterface::JsGetProxyProperties(arguments[1], &isProxy, &target, &handler));
+
+ if (isProxy)
+ {
+ JsPropertyIdRef targetProperty;
+ JsPropertyIdRef handlerProperty;
+ JsPropertyIdRef revokedProperty;
+
+ IfJsrtErrorSetGo(CreatePropertyIdFromString("target", &targetProperty));
+ IfJsrtErrorSetGo(CreatePropertyIdFromString("handler", &handlerProperty));
+ IfJsrtErrorSetGo(CreatePropertyIdFromString("revoked", &revokedProperty));
+ IfJsrtErrorSetGo(ChakraRTInterface::JsCreateObject(&returnValue));
+
+ JsValueRef revoked = JS_INVALID_REFERENCE;
+
+ if (target == JS_INVALID_REFERENCE)
+ {
+ IfJsrtErrorSetGo(ChakraRTInterface::JsGetTrueValue(&revoked));
+ target = undefined;
+ handler = undefined;
+ }
+ else
+ {
+ IfJsrtErrorSetGo(ChakraRTInterface::JsGetFalseValue(&revoked));
+ }
+
+ IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, handlerProperty, handler, true));
+ IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, targetProperty, target, true));
+ IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, revokedProperty, revoked, true));
+ }
+ }
+Error:
+ return returnValue;
+}
+
bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode)
{
LPCWSTR errorTypeString = ConvertErrorCodeToMessage(jsErrorCode);
diff --git a/bin/ch/WScriptJsrt.h b/bin/ch/WScriptJsrt.h
index 1946d9dd923..9ab3fc38c25 100644
--- a/bin/ch/WScriptJsrt.h
+++ b/bin/ch/WScriptJsrt.h
@@ -129,6 +129,7 @@ class WScriptJsrt
static JsValueRef CALLBACK GetReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
static JsValueRef CALLBACK LeavingCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
static JsValueRef CALLBACK SleepCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+ static JsValueRef CALLBACK GetProxyPropertiesCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
static JsErrorCode FetchImportedModuleHelper(JsModuleRecord referencingModule, JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord, LPCSTR refdir = nullptr);
diff --git a/lib/Jsrt/ChakraCore.h b/lib/Jsrt/ChakraCore.h
index e789eae9f0f..fd265063297 100644
--- a/lib/Jsrt/ChakraCore.h
+++ b/lib/Jsrt/ChakraCore.h
@@ -1035,5 +1035,33 @@ CHAKRA_API
JsSetHostPromiseRejectionTracker(
_In_ JsHostPromiseRejectionTrackerCallback promiseRejectionTrackerCallback,
_In_opt_ void *callbackState);
+
+///
+/// Determines if a provided object is a JavscriptProxy Object and
+/// provides references to a Proxy's target and handler.
+///
+///
+/// Requires an active script context.
+/// 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 set to JS_INVALID_REFERENCE.
+/// If it is a Proxy object that has not been revoked target and handler are set to the
+/// the object's target and handler.
+///
+/// The object that may be a Proxy.
+/// Pointer to a Boolean - is the object a proxy?
+/// Pointer to a JsValueRef - the object's target.
+/// Pointer to a JsValueRef - the object's handler.
+///
+/// The code JsNoError if the operation succeeded, a failure code otherwise.
+///
+CHAKRA_API
+ JsGetProxyProperties(
+ _In_ JsValueRef object,
+ _Out_ bool* isProxy,
+ _Out_opt_ JsValueRef* target,
+ _Out_opt_ JsValueRef* handler);
+
#endif // _CHAKRACOREBUILD
#endif // _CHAKRACORE_H_
diff --git a/lib/Jsrt/Jsrt.cpp b/lib/Jsrt/Jsrt.cpp
index 00d9150c38e..b94ea75f17d 100644
--- a/lib/Jsrt/Jsrt.cpp
+++ b/lib/Jsrt/Jsrt.cpp
@@ -5329,4 +5329,45 @@ CHAKRA_API JsSetHostPromiseRejectionTracker(_In_ JsHostPromiseRejectionTrackerCa
/*allowInObjectBeforeCollectCallback*/true);
}
+CHAKRA_API JsGetProxyProperties (_In_ JsValueRef object, _Out_ bool* isProxy, _Out_opt_ JsValueRef* target, _Out_opt_ JsValueRef* handler)
+{
+ return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext * scriptContext) -> JsErrorCode {
+ VALIDATE_INCOMING_REFERENCE(object, scriptContext);
+ PARAM_NOT_NULL(isProxy);
+
+ if (target != nullptr)
+ {
+ *target = JS_INVALID_REFERENCE;
+ }
+
+ if (handler != nullptr)
+ {
+ *handler = JS_INVALID_REFERENCE;
+ }
+
+ *isProxy = Js::JavascriptProxy::Is(object);
+
+ if (!*isProxy)
+ {
+ return JsNoError;
+ }
+
+ Js::JavascriptProxy* proxy = Js::JavascriptProxy::UnsafeFromVar(object);
+ bool revoked = proxy->IsRevoked();
+
+ if (target != nullptr && !revoked)
+ {
+ *target = static_cast(proxy->GetTarget());
+ }
+
+ if (handler != nullptr && !revoked)
+ {
+ *handler = static_cast(proxy->GetHandler());
+ }
+
+ return JsNoError;
+ },
+ /*allowInObjectBeforeCollectCallback*/true);
+}
+
#endif // _CHAKRACOREBUILD
diff --git a/lib/Runtime/Library/JavascriptProxy.cpp b/lib/Runtime/Library/JavascriptProxy.cpp
index 9e5965b473b..83e0528b8c6 100644
--- a/lib/Runtime/Library/JavascriptProxy.cpp
+++ b/lib/Runtime/Library/JavascriptProxy.cpp
@@ -16,6 +16,11 @@ namespace Js
return JavascriptOperators::GetTypeId(obj) == TypeIds_Proxy;
}
+ bool JavascriptProxy::IsRevoked() const
+ {
+ return (target == nullptr);
+ }
+
RecyclableObject* JavascriptProxy::GetTarget()
{
if (target == nullptr)
diff --git a/lib/Runtime/Library/JavascriptProxy.h b/lib/Runtime/Library/JavascriptProxy.h
index 75e4ecfc84f..6472abf5862 100644
--- a/lib/Runtime/Library/JavascriptProxy.h
+++ b/lib/Runtime/Library/JavascriptProxy.h
@@ -154,6 +154,7 @@ namespace Js
virtual RecyclableObject* ToObject(ScriptContext * requestContext) override;
virtual Var GetTypeOfString(ScriptContext* requestContext) override;
+ bool IsRevoked() const;
BOOL SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, PropertyId propertyId, Var newValue, ScriptContext* requestContext, BOOL skipPrototypeCheck = FALSE);
BOOL SetPropertyTrap(Var receiver, SetPropertyTrapKind setPropertyTrapKind, Js::JavascriptString * propertyString, Var newValue, ScriptContext* requestContext);
diff --git a/test/es6/ProxyPropertiesAPI.js b/test/es6/ProxyPropertiesAPI.js
new file mode 100644
index 00000000000..13b4377869a
--- /dev/null
+++ b/test/es6/ProxyPropertiesAPI.js
@@ -0,0 +1,90 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+let tests = [
+ {
+ name: "GetProxyProperties: no argugments",
+ body: function () {
+ let properties = WScript.GetProxyProperties();
+ assert.isUndefined(properties, "ProxyProperties of nothing should be undefined.");
+ }
+ },
+ {
+ name: "GetProxyProperties: non-proxy arguments",
+ body: function () {
+ let properties = WScript.GetProxyProperties(undefined);
+ assert.isUndefined(properties, "ProxyProperties of undefined should be undefined.");
+ properties = WScript.GetProxyProperties(1);
+ assert.isUndefined(properties, "ProxyProperties of number should be undefined.");
+ properties = WScript.GetProxyProperties({});
+ assert.isUndefined(properties, "ProxyProperties of non-proxy object should be undefined.");
+ }
+ },
+ {
+ name: "GetProxyProperties: revocable Proxy",
+ body: function () {
+ let revocable = Proxy.revocable({someProperty: true, otherProperty: false}, {otherProperty: true, newProperty: 5});
+ let proxy = revocable.proxy;
+ let properties = WScript.GetProxyProperties(proxy);
+
+ let names = Object.getOwnPropertyNames(properties);
+ assert.areEqual(names.length, 3, "proxy properties names should have length 3.");
+ assert.isTrue(names.includes("target"));
+ assert.isTrue(names.includes("handler"));
+ assert.isTrue(names.includes("revoked"));
+ assert.isFalse(properties.revoked, "Revoked bool should be false.");
+
+ names = Object.getOwnPropertyNames(properties.target);
+ assert.areEqual(names.length, 2, "proxy properties target names should have length 2.");
+ assert.areEqual(properties.target.someProperty, true);
+ assert.areEqual(properties.target.otherProperty, false);
+
+ names = Object.getOwnPropertyNames(properties.handler);
+ assert.areEqual(names.length, 2, "proxy properties handler names should have length 2.");
+ assert.areEqual(properties.handler.newProperty, 5);
+ assert.areEqual(properties.handler.otherProperty, true);
+
+ revocable.revoke();
+ properties = WScript.GetProxyProperties(proxy);
+
+ names = Object.getOwnPropertyNames(properties);
+ assert.areEqual(names.length, 3, "proxy properties names for revokes proxy should have length 3.");
+ assert.isTrue(names.includes("target"));
+ assert.isTrue(names.includes("handler"));
+ assert.isTrue(properties.revoked, "Revoked bool should be true.");
+
+ assert.isUndefined(properties.target, "Target of revoked proxy should be undefined.");
+ assert.isUndefined(properties.handler, "Handler of revoked proxy should be undefined.");
+ }
+ },
+ {
+ name: "GetProxyProperties: normal Proxy",
+ body: function () {
+ let proxy = new Proxy({someProperty: true, otherProperty: false}, {otherProperty: true, newProperty: 5});
+ let properties = WScript.GetProxyProperties(proxy);
+
+ let names = Object.getOwnPropertyNames(properties);
+ assert.areEqual(names.length, 3, "proxy properties names should have length 3");
+ assert.isTrue(names.includes("target"));
+ assert.isTrue(names.includes("handler"));
+ assert.isTrue(names.includes("revoked"));
+ assert.isFalse(properties.revoked, "Revoked bool should be false.");
+
+ names = Object.getOwnPropertyNames(properties.target);
+ assert.areEqual(names.length, 2, "proxy properties target names should have length 2");
+ assert.areEqual(properties.target.someProperty, true);
+ assert.areEqual(properties.target.otherProperty, false);
+
+ names = Object.getOwnPropertyNames(properties.handler);
+ assert.areEqual(names.length, 2, "proxy properties handler names should have length 2");
+ assert.areEqual(properties.handler.newProperty, 5);
+ assert.areEqual(properties.handler.otherProperty, true);
+ }
+ }
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
\ No newline at end of file
diff --git a/test/es6/rlexe.xml b/test/es6/rlexe.xml
index 406c76c648a..c3f4b44e820 100644
--- a/test/es6/rlexe.xml
+++ b/test/es6/rlexe.xml
@@ -715,6 +715,13 @@
-args summary -endargs
+
+
+ ProxyPropertiesAPI.js
+ -args summary -endargs
+ exclude_jshost
+
+
proxybugs.js