Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/components/uniffi-js/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 10 kB image not shown  

Quelle  UniFFICall.cpp   Sprache: C

 
/* -*- 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 "nsThreadUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/UniFFICall.h"

namespace mozilla::uniffi {
extern mozilla::LazyLogModule gUniffiLogger;

using dom::GlobalObject;
using dom::RootedDictionary;
using dom::Sequence;
using dom::UniFFIScaffoldingCallResult;
using dom::UniFFIScaffoldingValue;

void UniffiSyncCallHandler::CallSync(
    UniquePtr<UniffiSyncCallHandler> aHandler, const GlobalObject& aGlobal,
    const Sequence<UniFFIScaffoldingValue>& aArgs,
    RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue,
    ErrorResult& aError) {
  MOZ_ASSERT(NS_IsMainThread());
  aHandler->PrepareRustArgs(aArgs, aError);
  if (aError.Failed()) {
    return;
  }
  RustCallStatus callStatus{};
  aHandler->MakeRustCall(&callStatus);
  aHandler->mUniffiCallStatusCode = callStatus.code;
  if (callStatus.error_buf.data) {
    aHandler->mUniffiCallStatusErrorBuf = OwnedRustBuffer(callStatus.error_buf);
  }
  aHandler->ExtractCallResult(aGlobal.Context(), aReturnValue, aError);
}

already_AddRefed<dom::Promise> UniffiSyncCallHandler::CallAsyncWrapper(
    UniquePtr<UniffiSyncCallHandler> aHandler, const dom::GlobalObject& aGlobal,
    const dom::Sequence<dom::UniFFIScaffoldingValue>& aArgs,
    ErrorResult& aError) {
  MOZ_ASSERT(NS_IsMainThread());
  aHandler->PrepareRustArgs(aArgs, aError);
  if (aError.Failed()) {
    return nullptr;
  }

  // Create the promise that we return to JS
  nsCOMPtr<nsIGlobalObject> xpcomGlobal =
      do_QueryInterface(aGlobal.GetAsSupports());
  RefPtr<dom::Promise> returnPromise =
      dom::Promise::Create(xpcomGlobal, aError);
  if (aError.Failed()) {
    return nullptr;
  }

  // Create a second promise that gets resolved by a background task that
  // calls the scaffolding function
  RefPtr taskPromise =
      new MozPromise<UniquePtr<UniffiSyncCallHandler>, nsresult, true>::Private(
          __func__);

  nsresult dispatchResult = NS_DispatchBackgroundTask(
      NS_NewRunnableFunction(
          __func__,
          [handler = std::move(aHandler), taskPromise]() mutable {
            RustCallStatus callStatus{};
            handler->MakeRustCall(&callStatus);
            handler->mUniffiCallStatusCode = callStatus.code;
            if (callStatus.error_buf.data) {
              handler->mUniffiCallStatusErrorBuf =
                  OwnedRustBuffer(callStatus.error_buf);
            }
            taskPromise->Resolve(std::move(handler), __func__);
          }),
      NS_DISPATCH_EVENT_MAY_BLOCK);
  if (NS_FAILED(dispatchResult)) {
    taskPromise->Reject(dispatchResult, __func__);
  }

  // When the background task promise completes, resolve the JS promise
  taskPromise->Then(
      GetCurrentSerialEventTarget(), __func__,
      [xpcomGlobal, returnPromise](
          typename MozPromise<UniquePtr<UniffiSyncCallHandler>, nsresult,
                              true>::ResolveOrRejectValue&& aResult) {
        if (!aResult.IsResolve()) {
          returnPromise->MaybeRejectWithUnknownError(__func__);
          return;
        }
        auto handler = std::move(aResult.ResolveValue());
        dom::AutoEntryScript aes(xpcomGlobal,
                                 "UniffiSyncCallHandler::CallAsyncWrapper");
        dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> returnValue(
            aes.cx());

        ErrorResult error;
        handler->ExtractCallResult(aes.cx(), returnValue, error);
        error.WouldReportJSException();
        if (error.Failed()) {
          returnPromise->MaybeReject(std::move(error));
        } else {
          returnPromise->MaybeResolve(returnValue);
        }
      });

  // Return the JS promise, using forget() to convert it to already_AddRefed
  return returnPromise.forget();
}

void UniffiCallHandlerBase::ExtractCallResult(
    JSContext* aCx,
    dom::RootedDictionary<dom::UniFFIScaffoldingCallResult>& aDest,
    ErrorResult& aError) {
  switch (mUniffiCallStatusCode) {
    case RUST_CALL_SUCCESS: {
      aDest.mCode = dom::UniFFIScaffoldingCallCode::Success;
      ExtractSuccessfulCallResult(aCx, aDest.mData, aError);
      break;
    }

    case RUST_CALL_ERROR: {
      // Rust Err() value.  Populate data with the `RustBuffer` containing the
      // error
      aDest.mCode = dom::UniFFIScaffoldingCallCode::Error;

      JS::Rooted<JSObject*> obj(aCx);
      mUniffiCallStatusErrorBuf.IntoArrayBuffer(aCx, &obj, aError);
      if (aError.Failed()) {
        break;
      }
      aDest.mData.Construct().SetAsArrayBuffer().Init(obj);
      break;
    }

    default: {
      // This indicates a RustError, which should rarely happen in practice.
      // The normal case is a Rust panic, but FF sets panic=abort.
      aDest.mCode = dom::UniFFIScaffoldingCallCode::Internal_error;
      if (mUniffiCallStatusErrorBuf.IsValid()) {
        JS::Rooted<JSObject*> obj(aCx);
        mUniffiCallStatusErrorBuf.IntoArrayBuffer(aCx, &obj, aError);
        if (aError.Failed()) {
          break;
        }
        aDest.mData.Construct().SetAsArrayBuffer().Init(obj);
      }

      break;
    }
  }
}

already_AddRefed<dom::Promise> UniffiAsyncCallHandler::CallAsync(
    UniquePtr<UniffiAsyncCallHandler> aHandler,
    const dom::GlobalObject& aGlobal,
    const dom::Sequence<dom::UniFFIScaffoldingValue>& aArgs,
    ErrorResult& aError) {
  MOZ_ASSERT(NS_IsMainThread());
  // Async calls return a Future rather than doing any work.  This means we can
  // make the call right now on the JS main thread without fear of blocking it.
  aHandler->PrepareArgsAndMakeRustCall(aArgs, aError);
  if (aError.Failed()) {
    return nullptr;
  }

  // Create the promise that the handler will resolve
  nsCOMPtr<nsIGlobalObject> global(do_QueryInterface(aGlobal.GetAsSupports()));
  aHandler->mPromise = dom::Promise::Create(global, aError);
  // Also get a copy to return to JS
  RefPtr<dom::Promise> returnPromise(aHandler->mPromise);

  if (aError.Failed()) {
    aError.ThrowUnknownError("[UniFFI] dom::Promise::Create failed"_ns);
    return nullptr;
  }

  // Schedule a poll for the future in a background thread.
  nsresult dispatchResult = NS_DispatchBackgroundTask(NS_NewRunnableFunction(
      __func__, [handler = std::move(aHandler)]() mutable {
        UniffiAsyncCallHandler::Poll(std::move(handler));
      }));
  if (NS_FAILED(dispatchResult)) {
    aError.ThrowUnknownError(
        "[UniFFI] UniffiAsyncCallHandler::CallAsync - Error scheduling background task"_ns);
    return nullptr;
  }

  // Return a copy of the JS promise, using forget() to convert it to
  // already_AddRefed
  return returnPromise.forget();
}

// Callback function for async calls
//
// This is passed to Rust when we poll the future alongside a 64-bit handle that
// represents the callback data.  For uniffi-bindgen-gecko-js, the handle is a
// `UniffiAsyncCallHandler*` casted to an int.
//
// Rust calls this when either the future is ready or when it's time to poll it
// again.
void UniffiAsyncCallHandler::FutureCallback(uint64_t aCallHandlerHandle,
                                            int8_t aCode) {
  // Recreate the UniquePtr we previously released
  UniquePtr<UniffiAsyncCallHandler> handler(
      reinterpret_cast<UniffiAsyncCallHandler*>(
          static_cast<uintptr_t>(aCallHandlerHandle)));

  switch (aCode) {
    case UNIFFI_FUTURE_READY: {
      // `Future::poll` returned a `Ready` value on the Rust side.
      nsresult dispatchResult = NS_DispatchToMainThread(NS_NewRunnableFunction(
          __func__, [handler = std::move(handler)]() mutable {
            UniffiAsyncCallHandler::Finish(std::move(handler));
          }));
      if (NS_FAILED(dispatchResult)) {
        MOZ_LOG(gUniffiLogger, LogLevel::Error,
                ("[UniFFI] NS_DispatchToMainThread failed in "
                 "UniffiAsyncCallHandler::FutureCallback"));
      }
      break;
    }

    case UNIFFI_FUTURE_MAYBE_READY: {
      // The Rust waker was invoked after `poll` returned a `Pending` value.
      // Poll the future again soon in a background task.
      nsresult dispatchResult = NS_DispatchBackgroundTask(
          NS_NewRunnableFunction(__func__,
                                 [handler = std::move(handler)]() mutable {
                                   UniffiAsyncCallHandler::Poll(
                                       std::move(handler));
                                 }),
          NS_DISPATCH_NORMAL);
      if (NS_FAILED(dispatchResult)) {
        MOZ_LOG(gUniffiLogger, LogLevel::Error,
                ("[UniFFI] NS_DispatchBackgroundTask failed in "
                 "UniffiAsyncCallHandler::FutureCallback"));
      }
      break;
    }

    default: {
      // Invalid poll code, this should never happen, but if it does log an
      // error and reject the promise.
      MOZ_LOG(gUniffiLogger, LogLevel::Error,
              ("[UniFFI] Invalid poll code in "
               "UniffiAsyncCallHandler::FutureCallback %d",
               aCode));
      handler->mPromise->MaybeRejectWithUndefined();
      break;
    }
  };
}

void UniffiAsyncCallHandler::Poll(UniquePtr<UniffiAsyncCallHandler> aHandler) {
  auto futureHandle = aHandler->mFutureHandle;
  auto pollFn = aHandler->mPollFn;
  // Release the UniquePtr into a raw pointer and convert it to a handle
  // so that we can pass it as a handle to the UniFFI code.  It gets converted
  // back in `UniffiAsyncCallHandler::FutureCallback()`, which the Rust code
  // guarentees will be called if the future makes progress.
  uint64_t selfHandle =
      static_cast<uint64_t>(reinterpret_cast<uintptr_t>(aHandler.release()));
  pollFn(futureHandle, UniffiAsyncCallHandler::FutureCallback, selfHandle);
}

// Complete the Rust future, extract the call result and resolve/reject the JS
// promise
void UniffiAsyncCallHandler::Finish(
    UniquePtr<UniffiAsyncCallHandler> aHandler) {
  RefPtr<dom::Promise> promise = aHandler->mPromise;
  if (!promise) {
    return;
  }
  dom::AutoEntryScript aes(promise->GetGlobalObject(),
                           "UniffiAsyncCallHandler::Finish");
  dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> returnValue(aes.cx());
  ErrorResult error;

  RustCallStatus callStatus{};
  aHandler->CallCompleteFn(&callStatus);
  aHandler->mUniffiCallStatusCode = callStatus.code;
  if (callStatus.error_buf.data) {
    aHandler->mUniffiCallStatusErrorBuf = OwnedRustBuffer(callStatus.error_buf);
  }
  aHandler->ExtractCallResult(aes.cx(), returnValue, error);
  error.WouldReportJSException();
  if (error.Failed()) {
    aHandler->mPromise->MaybeReject(std::move(error));
  } else {
    aHandler->mPromise->MaybeResolve(returnValue);
  }
}

UniffiAsyncCallHandler::~UniffiAsyncCallHandler() { mFreeFn(mFutureHandle); }

}  // namespace mozilla::uniffi

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.31 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.