diff --git a/bin/NativeTests/JsRTApiTest.cpp b/bin/NativeTests/JsRTApiTest.cpp index 3a40a675b5a..c093f3afbdc 100644 --- a/bin/NativeTests/JsRTApiTest.cpp +++ b/bin/NativeTests/JsRTApiTest.cpp @@ -102,6 +102,37 @@ namespace JsRTApiTest JsRTApiTest::RunWithAttributes(JsRTApiTest::ReferenceCountingTest); } + void WeakReferenceTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) + { + JsValueRef valueRef = JS_INVALID_REFERENCE; + REQUIRE(JsCreateString("test", strlen("test"), &valueRef) == JsNoError); + + JsWeakRef weakRef = JS_INVALID_REFERENCE; + REQUIRE(JsCreateWeakReference(valueRef, &weakRef) == JsNoError); + + // JsGetWeakReferenceValue should return the original value reference. + JsValueRef valueRefFromWeakRef = JS_INVALID_REFERENCE; + CHECK(JsGetWeakReferenceValue(weakRef, &valueRefFromWeakRef) == JsNoError); + CHECK(valueRefFromWeakRef != JS_INVALID_REFERENCE); + CHECK(valueRefFromWeakRef == valueRef); + + // Clear the references on the stack, so that the value will be GC'd. + valueRef = JS_INVALID_REFERENCE; + valueRefFromWeakRef = JS_INVALID_REFERENCE; + + CHECK(JsCollectGarbage(runtime) == JsNoError); + + // JsGetWeakReferenceValue should return an invalid reference after the value was GC'd. + JsValueRef valueRefAfterGC = JS_INVALID_REFERENCE; + CHECK(JsGetWeakReferenceValue(weakRef, &valueRefAfterGC) == JsNoError); + CHECK(valueRefAfterGC == JS_INVALID_REFERENCE); + } + + TEST_CASE("ApiTest_WeakReferenceTest", "[ApiTest]") + { + JsRTApiTest::RunWithAttributes(JsRTApiTest::WeakReferenceTest); + } + void ObjectsAndPropertiesTest1(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) { JsValueRef object = JS_INVALID_REFERENCE; diff --git a/lib/Jsrt/ChakraCore.h b/lib/Jsrt/ChakraCore.h index d999f287ab3..695bff57e2d 100644 --- a/lib/Jsrt/ChakraCore.h +++ b/lib/Jsrt/ChakraCore.h @@ -508,9 +508,47 @@ CHAKRA_API /// The code JsNoError if the operation succeeded, a failure code otherwise. /// CHAKRA_API -JsCreatePromise( - _Out_ JsValueRef *promise, - _Out_ JsValueRef *resolveFunction, - _Out_ JsValueRef *rejectFunction); + JsCreatePromise( + _Out_ JsValueRef *promise, + _Out_ JsValueRef *resolveFunction, + _Out_ JsValueRef *rejectFunction); + +/// +/// A weak reference to a JavaScript value. +/// +/// +/// A value with only weak references is available for garbage-collection. A strong reference +/// to the value (JsValueRef) may be obtained from a weak reference if the value happens +/// to still be available. +/// +typedef JsRef JsWeakRef; + +/// +/// Creates a weak reference to a value. +/// +/// The value to be referenced. +/// Weak reference to the value. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsCreateWeakReference( + _In_ JsValueRef value, + _Out_ JsWeakRef* weakRef); + +/// +/// Gets a strong reference to the value referred to by a weak reference. +/// +/// A weak reference. +/// Reference to the value, or JS_INVALID_REFERENCE if the value is +/// no longer available. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsGetWeakReferenceValue( + _In_ JsWeakRef weakRef, + _Out_ JsValueRef* value); + #endif // CHAKRACOREBUILD_ #endif // _CHAKRACORE_H_ diff --git a/lib/Jsrt/Jsrt.cpp b/lib/Jsrt/Jsrt.cpp index 0e2a24da758..f9f202e637d 100644 --- a/lib/Jsrt/Jsrt.cpp +++ b/lib/Jsrt/Jsrt.cpp @@ -761,7 +761,7 @@ CHAKRA_API JsSetCurrentContext(_In_ JsContextRef newContext) } else { - if(oldScriptContext->IsTTDRecordModeEnabled()) + if(oldScriptContext->IsTTDRecordModeEnabled()) { //already know newScriptContext != oldScriptContext so don't check again if(oldScriptContext->ShouldPerformRecordAction()) @@ -3904,7 +3904,7 @@ CHAKRA_API JsTTDPreExecuteSnapShotInterval(_In_ JsRuntimeHandle runtimeHandle, _ return inflateStatus; } - //If we are in the "active" segment set the continue breakpoint + //If we are in the "active" segment set the continue breakpoint if((moveMode & JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment) == JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment) { GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode @@ -3944,7 +3944,7 @@ CHAKRA_API JsTTDPreExecuteSnapShotInterval(_In_ JsRuntimeHandle runtimeHandle, _ elog->PopMode(TTD::TTDMode::DebuggerLogBreakpoints); elog->PopMode(TTD::TTDMode::DebuggerSuppressBreakpoints); - //If we are in the "active" segment un-set the continue breakpoint + //If we are in the "active" segment un-set the continue breakpoint if((moveMode & JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment) == JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment) { GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode @@ -4036,7 +4036,7 @@ CHAKRA_API JsTTDReplayExecution(_Inout_ JsTTDMoveMode* moveMode, _Out_ int64_t* return JsNoError; }); - if(bpstatus != JsNoError) + if(bpstatus != JsNoError) { return bpstatus; } @@ -4545,4 +4545,49 @@ CHAKRA_API JsCreatePromise(_Out_ JsValueRef *promise, _Out_ JsValueRef *resolve, return JsNoError; }); } + +CHAKRA_API JsCreateWeakReference( + _In_ JsValueRef value, + _Out_ JsWeakRef* weakRef) +{ + VALIDATE_JSREF(value); + PARAM_NOT_NULL(weakRef); + *weakRef = nullptr; + + return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { + ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread(); + if (threadContext == nullptr) + { + return JsErrorNoCurrentContext; + } + + Recycler* recycler = threadContext->GetRecycler(); + if (recycler->IsInObjectBeforeCollectCallback()) + { + return JsErrorInObjectBeforeCollectCallback; + } + + recycler->FindOrCreateWeakReferenceHandle( + reinterpret_cast(value), + reinterpret_cast**>(weakRef)); + return JsNoError; + }); +} + +CHAKRA_API JsGetWeakReferenceValue( + _In_ JsWeakRef weakRef, + _Out_ JsValueRef* value) +{ + VALIDATE_JSREF(weakRef); + PARAM_NOT_NULL(value); + *value = JS_INVALID_REFERENCE; + + return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { + Memory::RecyclerWeakReference* recyclerWeakReference = + reinterpret_cast*>(weakRef); + *value = reinterpret_cast(recyclerWeakReference->Get()); + return JsNoError; + }); +} + #endif // CHAKRACOREBUILD_ diff --git a/lib/Jsrt/JsrtCommonExports.inc b/lib/Jsrt/JsrtCommonExports.inc index a1828185c14..6de2f6c2747 100644 --- a/lib/Jsrt/JsrtCommonExports.inc +++ b/lib/Jsrt/JsrtCommonExports.inc @@ -116,4 +116,6 @@ JsCreatePropertyId JsCopyPropertyId JsCreatePromise + JsCreateWeakReference + JsGetWeakReferenceValue #endif