1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsPrintfCString.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "mozilla/uniffi/OwnedRustBuffer.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/UniFFIBinding.h"
#include "mozilla/uniffi/Callbacks.h"
#include "mozilla/Atomics.h"
#include "mozilla/Logging.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
namespace mozilla::uniffi {
extern mozilla::LazyLogModule gUniffiLogger;
// Type we'll be using to store callback handle ref counts
//
// 32-bits should never overflow and ReleaseAcquire is the recommended memory
// ordering for reference counts:
// https://searchfox.org/firefox-main/rev/f26084f2dfc00c1e10377d4433cfea594f7ea8c2/mfbt/Atomics.h#114-119
using HandleRefCount = Atomic<uint32_t, MemoryOrdering::ReleaseAcquire>;
uint64_t CallbackHandleCreate() {
// This allocates an atomic u32 that stores the ref count.
// We cast the address to a `u64`, which will be the handle.
//
// Another approach would be to create a HashMap that maps handles to their
// refcounts. However, that would require a lock and global initialization.
// This works in a similar way, but instead of generating a handle and
// inserting it into the map, we call `new` to allocate a memory location and
// use the address as the handle.
//
// This will safely fit in a JS integer as long as pointers only set the lower
// 53 bits or so. This is okay since the JS code itself assumes only the lower
// 48 bits of the pointer are set:
// https://searchfox.org/firefox-main/rev/20a1fb35a4d5c2f2ea6c865ecebc8e4bee6f86c9/js/public/Value.h#61-66
//
// Finally, we always set the lowest bit on the handle. This allows UniFFI to
// tell if trait interface handles came from JS or Rust.
HandleRefCount* handlePointer = new HandleRefCount(1);
return reinterpret_cast<uint64_t>(handlePointer) | 1;
}
uint32_t CallbackHandleAddRef(uint64_t aHandle) {
HandleRefCount* handlePointer =
reinterpret_cast<HandleRefCount*>(aHandle & ~1);
return ++(*handlePointer);
}
uint32_t CallbackHandleRelease(uint64_t aHandle) {
HandleRefCount* handlePointer =
reinterpret_cast<HandleRefCount*>(aHandle & ~1);
auto refCount = --(*handlePointer);
if (refCount == 0) {
delete handlePointer;
}
return refCount;
}
void AsyncCallbackMethodHandlerBase::ScheduleAsyncCall(
UniquePtr<AsyncCallbackMethodHandlerBase> aHandler,
StaticRefPtr<dom::UniFFICallbackHandler>* aJsHandler) {
nsresult dispatchResult = NS_DispatchToMainThread(NS_NewRunnableFunction(
"UniFFI callback", [handler = std::move(aHandler),
aJsHandler]() MOZ_CAN_RUN_SCRIPT_BOUNDARY mutable {
auto reportError = MakeScopeExit([&handler] {
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> callResult(
dom::RootingCx());
callResult.mCode = dom::UniFFIScaffoldingCallCode::Internal_error;
handler->HandleReturn(callResult, IgnoreErrors());
});
// Take our own reference to the callback handler to ensure that it
// stays alive for the duration of this call
RefPtr<dom::UniFFICallbackHandler> jsHandler = *aJsHandler;
if (!jsHandler) {
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[%s] called, but JS handler not registered",
handler->mUniffiMethodName));
return;
}
JSObject* global = jsHandler->CallbackGlobalOrNull();
if (!global) {
MOZ_LOG(
gUniffiLogger, LogLevel::Error,
("[%s] JS handler has null global", handler->mUniffiMethodName));
return;
}
dom::AutoEntryScript aes(global, handler->mUniffiMethodName);
IgnoredErrorResult error;
RefPtr<dom::Promise> promise =
handler->MakeCall(aes.cx(), jsHandler, error);
if (error.Failed()) {
MOZ_LOG(
gUniffiLogger, LogLevel::Error,
("[%s] Error invoking JS handler", handler->mUniffiMethodName));
return;
}
reportError.release();
if (promise) {
auto promiseHandler = MakeRefPtr<PromiseHandler>(std::move(handler));
promise->AppendNativeHandler(promiseHandler);
}
}));
if (NS_FAILED(dispatchResult)) {
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] Error dispatching UniFFI callback task"));
}
}
MOZ_CAN_RUN_SCRIPT
already_AddRefed<dom::Promise> CallbackFreeHandler::MakeCall(
JSContext* aCx, dom::UniFFICallbackHandler* aJsHandler,
ErrorResult& aError) {
aJsHandler->Destroy(mUniffiHandle.IntoRust(), aError);
// CallbackFreeHandler works like a fire-and-forget callback and returns
// nullptr. There's no Rust code that's awaiting this result.
return nullptr;
}
NS_IMPL_ISUPPORTS0(AsyncCallbackMethodHandlerBase::PromiseHandler);
void AsyncCallbackMethodHandlerBase::PromiseHandler::ResolvedCallback(
JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> callResult(aCx);
if (!callResult.Init(aCx, aValue)) {
JS_ClearPendingException(aCx);
MOZ_LOG(
gUniffiLogger, LogLevel::Error,
("[%s] callback method did not return a UniFFIScaffoldingCallResult",
mHandler->mUniffiMethodName));
callResult.mCode = dom::UniFFIScaffoldingCallCode::Internal_error;
}
mHandler->HandleReturn(callResult, aRv);
}
void AsyncCallbackMethodHandlerBase::PromiseHandler::RejectedCallback(
JSContext* aCx, JS::Handle<JS::Value>, ErrorResult& aRv) {
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> callResult(aCx);
callResult.mCode = dom::UniFFIScaffoldingCallCode::Internal_error;
mHandler->HandleReturn(callResult, aRv);
}
} // namespace mozilla::uniffi
|