Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/groupoids/doc/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 11.8.2025 mit Größe 177 B image not shown  

Quelle  UniFFICall.cpp   Sprache: unbekannt

 
/* -*- 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 in Prozent
C=87 H=100 G=93

[Verzeichnis aufwärts0.4unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-04-26]