File: Callbacks.cpp

package info (click to toggle)
firefox 149.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,767,760 kB
  • sloc: cpp: 7,416,064; javascript: 6,752,859; ansic: 3,774,850; python: 1,250,473; xml: 641,578; asm: 439,191; java: 186,617; sh: 56,634; makefile: 18,856; objc: 13,092; perl: 12,763; pascal: 5,960; yacc: 4,583; cs: 3,846; lex: 1,720; ruby: 1,002; php: 436; lisp: 258; awk: 105; sql: 66; sed: 53; csh: 10; exp: 6
file content (156 lines) | stat: -rw-r--r-- 6,191 bytes parent folder | download
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