Skip to content

Commit ebc94d8

Browse files
committed
[MERGE #2948 @jasongin] Add JSRT weak reference APIs
Merge pull request #2948 from jasongin:weakref - Add a new typedef: `JsWeakRef` - Add two new APIs: `JsCreateWeakReference()`, `JsGetWeakReferenceValue()` - Add a native test for the new APIs The weak reference APIs are required for N-API in Node-ChakraCore. See nodejs/node-chakracore#238
2 parents cd3407b + ca71319 commit ebc94d8

File tree

4 files changed

+124
-8
lines changed

4 files changed

+124
-8
lines changed

bin/NativeTests/JsRTApiTest.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,37 @@ namespace JsRTApiTest
102102
JsRTApiTest::RunWithAttributes(JsRTApiTest::ReferenceCountingTest);
103103
}
104104

105+
void WeakReferenceTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime)
106+
{
107+
JsValueRef valueRef = JS_INVALID_REFERENCE;
108+
REQUIRE(JsCreateString("test", strlen("test"), &valueRef) == JsNoError);
109+
110+
JsWeakRef weakRef = JS_INVALID_REFERENCE;
111+
REQUIRE(JsCreateWeakReference(valueRef, &weakRef) == JsNoError);
112+
113+
// JsGetWeakReferenceValue should return the original value reference.
114+
JsValueRef valueRefFromWeakRef = JS_INVALID_REFERENCE;
115+
CHECK(JsGetWeakReferenceValue(weakRef, &valueRefFromWeakRef) == JsNoError);
116+
CHECK(valueRefFromWeakRef != JS_INVALID_REFERENCE);
117+
CHECK(valueRefFromWeakRef == valueRef);
118+
119+
// Clear the references on the stack, so that the value will be GC'd.
120+
valueRef = JS_INVALID_REFERENCE;
121+
valueRefFromWeakRef = JS_INVALID_REFERENCE;
122+
123+
CHECK(JsCollectGarbage(runtime) == JsNoError);
124+
125+
// JsGetWeakReferenceValue should return an invalid reference after the value was GC'd.
126+
JsValueRef valueRefAfterGC = JS_INVALID_REFERENCE;
127+
CHECK(JsGetWeakReferenceValue(weakRef, &valueRefAfterGC) == JsNoError);
128+
CHECK(valueRefAfterGC == JS_INVALID_REFERENCE);
129+
}
130+
131+
TEST_CASE("ApiTest_WeakReferenceTest", "[ApiTest]")
132+
{
133+
JsRTApiTest::RunWithAttributes(JsRTApiTest::WeakReferenceTest);
134+
}
135+
105136
void ObjectsAndPropertiesTest1(JsRuntimeAttributes attributes, JsRuntimeHandle runtime)
106137
{
107138
JsValueRef object = JS_INVALID_REFERENCE;

lib/Jsrt/ChakraCore.h

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -508,9 +508,47 @@ CHAKRA_API
508508
/// The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
509509
/// </returns>
510510
CHAKRA_API
511-
JsCreatePromise(
512-
_Out_ JsValueRef *promise,
513-
_Out_ JsValueRef *resolveFunction,
514-
_Out_ JsValueRef *rejectFunction);
511+
JsCreatePromise(
512+
_Out_ JsValueRef *promise,
513+
_Out_ JsValueRef *resolveFunction,
514+
_Out_ JsValueRef *rejectFunction);
515+
516+
/// <summary>
517+
/// A weak reference to a JavaScript value.
518+
/// </summary>
519+
/// <remarks>
520+
/// A value with only weak references is available for garbage-collection. A strong reference
521+
/// to the value (<c>JsValueRef</c>) may be obtained from a weak reference if the value happens
522+
/// to still be available.
523+
/// </remarks>
524+
typedef JsRef JsWeakRef;
525+
526+
/// <summary>
527+
/// Creates a weak reference to a value.
528+
/// </summary>
529+
/// <param name="value">The value to be referenced.</param>
530+
/// <param name="weakRef">Weak reference to the value.</param>
531+
/// <returns>
532+
/// The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
533+
/// </returns>
534+
CHAKRA_API
535+
JsCreateWeakReference(
536+
_In_ JsValueRef value,
537+
_Out_ JsWeakRef* weakRef);
538+
539+
/// <summary>
540+
/// Gets a strong reference to the value referred to by a weak reference.
541+
/// </summary>
542+
/// <param name="weakRef">A weak reference.</param>
543+
/// <param name="value">Reference to the value, or <c>JS_INVALID_REFERENCE</c> if the value is
544+
/// no longer available.</param>
545+
/// <returns>
546+
/// The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
547+
/// </returns>
548+
CHAKRA_API
549+
JsGetWeakReferenceValue(
550+
_In_ JsWeakRef weakRef,
551+
_Out_ JsValueRef* value);
552+
515553
#endif // CHAKRACOREBUILD_
516554
#endif // _CHAKRACORE_H_

lib/Jsrt/Jsrt.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ CHAKRA_API JsSetCurrentContext(_In_ JsContextRef newContext)
761761
}
762762
else
763763
{
764-
if(oldScriptContext->IsTTDRecordModeEnabled())
764+
if(oldScriptContext->IsTTDRecordModeEnabled())
765765
{
766766
//already know newScriptContext != oldScriptContext so don't check again
767767
if(oldScriptContext->ShouldPerformRecordAction())
@@ -3904,7 +3904,7 @@ CHAKRA_API JsTTDPreExecuteSnapShotInterval(_In_ JsRuntimeHandle runtimeHandle, _
39043904
return inflateStatus;
39053905
}
39063906

3907-
//If we are in the "active" segment set the continue breakpoint
3907+
//If we are in the "active" segment set the continue breakpoint
39083908
if((moveMode & JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment) == JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment)
39093909
{
39103910
GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode
@@ -3944,7 +3944,7 @@ CHAKRA_API JsTTDPreExecuteSnapShotInterval(_In_ JsRuntimeHandle runtimeHandle, _
39443944
elog->PopMode(TTD::TTDMode::DebuggerLogBreakpoints);
39453945
elog->PopMode(TTD::TTDMode::DebuggerSuppressBreakpoints);
39463946

3947-
//If we are in the "active" segment un-set the continue breakpoint
3947+
//If we are in the "active" segment un-set the continue breakpoint
39483948
if((moveMode & JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment) == JsTTDMoveMode::JsTTDMoveScanIntervalForContinueInActiveBreakpointSegment)
39493949
{
39503950
GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode
@@ -4036,7 +4036,7 @@ CHAKRA_API JsTTDReplayExecution(_Inout_ JsTTDMoveMode* moveMode, _Out_ int64_t*
40364036
return JsNoError;
40374037
});
40384038

4039-
if(bpstatus != JsNoError)
4039+
if(bpstatus != JsNoError)
40404040
{
40414041
return bpstatus;
40424042
}
@@ -4545,4 +4545,49 @@ CHAKRA_API JsCreatePromise(_Out_ JsValueRef *promise, _Out_ JsValueRef *resolve,
45454545
return JsNoError;
45464546
});
45474547
}
4548+
4549+
CHAKRA_API JsCreateWeakReference(
4550+
_In_ JsValueRef value,
4551+
_Out_ JsWeakRef* weakRef)
4552+
{
4553+
VALIDATE_JSREF(value);
4554+
PARAM_NOT_NULL(weakRef);
4555+
*weakRef = nullptr;
4556+
4557+
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
4558+
ThreadContext* threadContext = ThreadContext::GetContextForCurrentThread();
4559+
if (threadContext == nullptr)
4560+
{
4561+
return JsErrorNoCurrentContext;
4562+
}
4563+
4564+
Recycler* recycler = threadContext->GetRecycler();
4565+
if (recycler->IsInObjectBeforeCollectCallback())
4566+
{
4567+
return JsErrorInObjectBeforeCollectCallback;
4568+
}
4569+
4570+
recycler->FindOrCreateWeakReferenceHandle<char>(
4571+
reinterpret_cast<char*>(value),
4572+
reinterpret_cast<Memory::RecyclerWeakReference<char>**>(weakRef));
4573+
return JsNoError;
4574+
});
4575+
}
4576+
4577+
CHAKRA_API JsGetWeakReferenceValue(
4578+
_In_ JsWeakRef weakRef,
4579+
_Out_ JsValueRef* value)
4580+
{
4581+
VALIDATE_JSREF(weakRef);
4582+
PARAM_NOT_NULL(value);
4583+
*value = JS_INVALID_REFERENCE;
4584+
4585+
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
4586+
Memory::RecyclerWeakReference<char>* recyclerWeakReference =
4587+
reinterpret_cast<Memory::RecyclerWeakReference<char>*>(weakRef);
4588+
*value = reinterpret_cast<JsValueRef>(recyclerWeakReference->Get());
4589+
return JsNoError;
4590+
});
4591+
}
4592+
45484593
#endif // CHAKRACOREBUILD_

lib/Jsrt/JsrtCommonExports.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,6 @@
116116
JsCreatePropertyId
117117
JsCopyPropertyId
118118
JsCreatePromise
119+
JsCreateWeakReference
120+
JsGetWeakReferenceValue
119121
#endif

0 commit comments

Comments
 (0)