| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent c96d76a commit 428e9bc
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -460,6 +460,10 @@ memory. | |||
| 460 | 460 | ||
| 461 | 461 | Keeps the callback strongly referenced by JavaScript. | |
| 462 | 462 | ||
| 463 | + Throws `ERR_INVALID_ARG_VALUE` if the callback function has already been | ||
| 464 | + garbage collected after a previous `library.unrefCallback(pointer)` call, since | ||
| 465 | + a collected function cannot be referenced again. | ||
| 466 | + | ||
| 463 | 467 | ### `library.unrefCallback(pointer)` | |
| 464 | 468 | ||
| 465 | 469 | * `pointer` {bigint} | |
@@ -470,6 +474,9 @@ If the callback function is later garbage collected, subsequent native | |||
| 470 | 474 | invocations become a no-op. Non-void return values are zero-initialized before | |
| 471 | 475 | returning to native code. | |
| 472 | 476 | ||
| 477 | + Throws `ERR_INVALID_ARG_VALUE` if the callback function has already been | ||
| 478 | + garbage collected. | ||
| 479 | + | ||
| 473 | 480 | ## Calling native functions | |
| 474 | 481 | ||
| 475 | 482 | Argument conversion depends on the declared FFI type. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1127,6 +1127,15 @@ void DynamicLibrary::RefCallback(const FunctionCallbackInfo<Value>& args) { | |||
| 1127 | 1127 | return; | |
| 1128 | 1128 | } | |
| 1129 | 1129 | ||
| 1130 | + // The callback function may already have been collected after a previous | ||
| 1131 | + // unrefCallback() call. The persistent handle is empty in that case, and | ||
| 1132 | + // ClearWeak() on an empty handle dereferences a null slot. There is also no | ||
| 1133 | + // function left to make strong again. | ||
| 1134 | + if (existing->second->fn.IsEmpty()) { | ||
| 1135 | + THROW_ERR_INVALID_ARG_VALUE(env, "Callback not found"); | ||
| 1136 | + return; | ||
| 1137 | + } | ||
| 1138 | + | ||
| 1130 | 1139 | existing->second->fn.ClearWeak(); | |
| 1131 | 1140 | } | |
| 1132 | 1141 | ||
@@ -1158,6 +1167,14 @@ void DynamicLibrary::UnrefCallback(const FunctionCallbackInfo<Value>& args) { | |||
| 1158 | 1167 | return; | |
| 1159 | 1168 | } | |
| 1160 | 1169 | ||
| 1170 | + // The callback function may already have been collected by a previous | ||
| 1171 | + // unrefCallback() call. The persistent handle is empty in that case, and | ||
| 1172 | + // SetWeak() on an empty handle dereferences a null slot. | ||
| 1173 | + if (existing->second->fn.IsEmpty()) { | ||
| 1174 | + THROW_ERR_INVALID_ARG_VALUE(env, "Callback not found"); | ||
| 1175 | + return; | ||
| 1176 | + } | ||
| 1177 | + | ||
| 1161 | 1178 | existing->second->fn.SetWeak(); | |
| 1162 | 1179 | } | |
| 1163 | 1180 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,6 +51,37 @@ test('ffi refCallback retains callback function', async (t) => { | |||
| 51 | 51 | lib.unregisterCallback(pointer); | |
| 52 | 52 | }); | |
| 53 | 53 | ||
| 54 | + test('callback ref/unref throw after callback function is collected', async (t) => { | ||
| 55 | + const { lib } = ffi.dlopen(libraryPath, fixtureSymbols); | ||
| 56 | + t.after(() => lib.close()); | ||
| 57 | + | ||
| 58 | + let callback = () => 1; | ||
| 59 | + const ref = new WeakRef(callback); | ||
| 60 | + const pointer = lib.registerCallback( | ||
| 61 | + { arguments: ['i32'], return: 'i32' }, | ||
| 62 | + callback, | ||
| 63 | + ); | ||
| 64 | + | ||
| 65 | + lib.unrefCallback(pointer); | ||
| 66 | + callback = null; | ||
| 67 | + | ||
| 68 | + await gcUntil( | ||
| 69 | + 'callback ref/unref throw after callback function is collected', | ||
| 70 | + () => ref.deref() === undefined, | ||
| 71 | + ); | ||
| 72 | + | ||
| 73 | + t.assert.throws(() => lib.unrefCallback(pointer), { | ||
| 74 | + code: 'ERR_INVALID_ARG_VALUE', | ||
| 75 | + message: /Callback not found/, | ||
| 76 | + }); | ||
| 77 | + t.assert.throws(() => lib.refCallback(pointer), { | ||
| 78 | + code: 'ERR_INVALID_ARG_VALUE', | ||
| 79 | + message: /Callback not found/, | ||
| 80 | + }); | ||
| 81 | + | ||
| 82 | + lib.unregisterCallback(pointer); | ||
| 83 | + }); | ||
| 84 | + | ||
| 54 | 85 | test('callback ref/unref/unregister throw when library is closed', (t) => { | |
| 55 | 86 | const { lib } = ffi.dlopen(libraryPath, fixtureSymbols); | |
| 56 | 87 | const callback = lib.registerCallback(() => {}); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments