/* 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/. */

#define __STDC_LIMIT_MACROS
#include <stdint.h>

#include "js-config.h"

#ifdef JS_DEBUG
// A hack for MFBT. Guard objects need this to work.
#  define DEBUG 1
#endif

#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/Proxy.h"
#include "js/CharacterEncoding.h"
#include "js/Class.h"
#include "js/MemoryMetrics.h"
#include "js/Principals.h"
#include "js/StructuredClone.h"
#include "js/Wrapper.h"
#include "assert.h"

struct ProxyTraps {
  bool (*enter)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                js::BaseProxyHandler::Action action, bool* bp);

  bool (*getOwnPropertyDescriptor)(
      JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
      JS::MutableHandle<JS::PropertyDescriptor> desc);
  bool (*defineProperty)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                         JS::Handle<JS::PropertyDescriptor> desc,
                         JS::ObjectOpResult& result);
  bool (*ownPropertyKeys)(JSContext* cx, JS::HandleObject proxy,
                          JS::MutableHandleIdVector props);
  bool (*delete_)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                  JS::ObjectOpResult& result);

  bool (*enumerate)(JSContext* cx, JS::HandleObject proxy,
                    JS::MutableHandleIdVector props);

  bool (*getPrototypeIfOrdinary)(JSContext* cx, JS::HandleObject proxy,
                                 bool* isOrdinary,
                                 JS::MutableHandleObject protop);
  // getPrototype
  // setPrototype
  // setImmutablePrototype

  bool (*preventExtensions)(JSContext* cx, JS::HandleObject proxy,
                            JS::ObjectOpResult& result);

  bool (*isExtensible)(JSContext* cx, JS::HandleObject proxy, bool* succeeded);

  bool (*has)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
  bool (*get)(JSContext* cx, JS::HandleObject proxy, JS::HandleValue receiver,
              JS::HandleId id, JS::MutableHandleValue vp);
  bool (*set)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
              JS::HandleValue v, JS::HandleValue receiver,
              JS::ObjectOpResult& result);

  bool (*call)(JSContext* cx, JS::HandleObject proxy, const JS::CallArgs& args);
  bool (*construct)(JSContext* cx, JS::HandleObject proxy,
                    const JS::CallArgs& args);

  bool (*hasOwn)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                 bool* bp);
  bool (*getOwnEnumerablePropertyKeys)(JSContext* cx, JS::HandleObject proxy,
                                       JS::MutableHandleIdVector props);
  bool (*nativeCall)(JSContext* cx, JS::IsAcceptableThis test,
                     JS::NativeImpl impl, JS::CallArgs args);
  bool (*hasInstance)(JSContext* cx, JS::HandleObject proxy,
                      JS::MutableHandleValue v, bool* bp);
  bool (*objectClassIs)(JS::HandleObject obj, js::ESClass classValue,
                        JSContext* cx);
  const char* (*className)(JSContext* cx, JS::HandleObject proxy);
  JSString* (*fun_toString)(JSContext* cx, JS::HandleObject proxy,
                            bool isToString);
  // bool (*regexp_toShared)(JSContext *cx, JS::HandleObject proxy, RegExpGuard
  // *g);
  bool (*boxedValue_unbox)(JSContext* cx, JS::HandleObject proxy,
                           JS::MutableHandleValue vp);
  bool (*defaultValue)(JSContext* cx, JS::HandleObject obj, JSType hint,
                       JS::MutableHandleValue vp);
  void (*trace)(JSTracer* trc, JSObject* proxy);
  void (*finalize)(JSFreeOp* fop, JSObject* proxy);
  size_t (*objectMoved)(JSObject* proxy, JSObject* old);

  bool (*isCallable)(JSObject* obj);
  bool (*isConstructor)(JSObject* obj);

  // getElements

  // isScripted
};

static int HandlerFamily;

#define DEFER_TO_TRAP_OR_BASE_CLASS(_base)                                    \
                                                                              \
  /* Standard internal methods. */                                            \
  virtual bool enumerate(JSContext* cx, JS::HandleObject proxy,               \
                         JS::MutableHandleIdVector props) const override {    \
    return mTraps.enumerate ? mTraps.enumerate(cx, proxy, props)              \
                            : _base::enumerate(cx, proxy, props);             \
  }                                                                           \
                                                                              \
  virtual bool has(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,    \
                   bool* bp) const override {                                 \
    return mTraps.has ? mTraps.has(cx, proxy, id, bp)                         \
                      : _base::has(cx, proxy, id, bp);                        \
  }                                                                           \
                                                                              \
  virtual bool get(JSContext* cx, JS::HandleObject proxy,                     \
                   JS::HandleValue receiver, JS::HandleId id,                 \
                   JS::MutableHandleValue vp) const override {                \
    return mTraps.get ? mTraps.get(cx, proxy, receiver, id, vp)               \
                      : _base::get(cx, proxy, receiver, id, vp);              \
  }                                                                           \
                                                                              \
  virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,    \
                   JS::HandleValue v, JS::HandleValue receiver,               \
                   JS::ObjectOpResult& result) const override {               \
    return mTraps.set ? mTraps.set(cx, proxy, id, v, receiver, result)        \
                      : _base::set(cx, proxy, id, v, receiver, result);       \
  }                                                                           \
                                                                              \
  virtual bool call(JSContext* cx, JS::HandleObject proxy,                    \
                    const JS::CallArgs& args) const override {                \
    return mTraps.call ? mTraps.call(cx, proxy, args)                         \
                       : _base::call(cx, proxy, args);                        \
  }                                                                           \
                                                                              \
  virtual bool construct(JSContext* cx, JS::HandleObject proxy,               \
                         const JS::CallArgs& args) const override {           \
    return mTraps.construct ? mTraps.construct(cx, proxy, args)               \
                            : _base::construct(cx, proxy, args);              \
  }                                                                           \
                                                                              \
  /* Spidermonkey extensions. */                                              \
  virtual bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, \
                      bool* bp) const override {                              \
    return mTraps.hasOwn ? mTraps.hasOwn(cx, proxy, id, bp)                   \
                         : _base::hasOwn(cx, proxy, id, bp);                  \
  }                                                                           \
                                                                              \
  virtual bool getOwnEnumerablePropertyKeys(                                  \
      JSContext* cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) \
      const override {                                                        \
    return mTraps.getOwnEnumerablePropertyKeys                                \
               ? mTraps.getOwnEnumerablePropertyKeys(cx, proxy, props)        \
               : _base::getOwnEnumerablePropertyKeys(cx, proxy, props);       \
  }                                                                           \
                                                                              \
  virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test,           \
                          JS::NativeImpl impl, const JS::CallArgs& args)      \
      const override {                                                        \
    return mTraps.nativeCall ? mTraps.nativeCall(cx, test, impl, args)        \
                             : _base::nativeCall(cx, test, impl, args);       \
  }                                                                           \
                                                                              \
  virtual bool hasInstance(JSContext* cx, JS::HandleObject proxy,             \
                           JS::MutableHandleValue v, bool* bp)                \
      const override {                                                        \
    return mTraps.hasInstance ? mTraps.hasInstance(cx, proxy, v, bp)          \
                              : _base::hasInstance(cx, proxy, v, bp);         \
  }                                                                           \
                                                                              \
  virtual const char* className(JSContext* cx, JS::HandleObject proxy)        \
      const override {                                                        \
    return mTraps.className ? mTraps.className(cx, proxy)                     \
                            : _base::className(cx, proxy);                    \
  }                                                                           \
                                                                              \
  virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy,       \
                                 bool isToString) const override {            \
    return mTraps.fun_toString ? mTraps.fun_toString(cx, proxy, isToString)   \
                               : _base::fun_toString(cx, proxy, isToString);  \
  }                                                                           \
                                                                              \
  virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy,        \
                                JS::MutableHandleValue vp) const override {   \
    return mTraps.boxedValue_unbox ? mTraps.boxedValue_unbox(cx, proxy, vp)   \
                                   : _base::boxedValue_unbox(cx, proxy, vp);  \
  }                                                                           \
                                                                              \
  virtual void trace(JSTracer* trc, JSObject* proxy) const override {         \
    mTraps.trace ? mTraps.trace(trc, proxy) : _base::trace(trc, proxy);       \
  }                                                                           \
                                                                              \
  virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override {      \
    mTraps.finalize ? mTraps.finalize(fop, proxy)                             \
                    : _base::finalize(fop, proxy);                            \
  }                                                                           \
                                                                              \
  virtual size_t objectMoved(JSObject* proxy, JSObject* old) const override { \
    return mTraps.objectMoved ? mTraps.objectMoved(proxy, old)                \
                              : _base::objectMoved(proxy, old);               \
  }                                                                           \
                                                                              \
  virtual bool isCallable(JSObject* obj) const override {                     \
    return mTraps.isCallable ? mTraps.isCallable(obj)                         \
                             : _base::isCallable(obj);                        \
  }                                                                           \
                                                                              \
  virtual bool isConstructor(JSObject* obj) const override {                  \
    return mTraps.isConstructor ? mTraps.isConstructor(obj)                   \
                                : _base::isConstructor(obj);                  \
  }

class WrapperProxyHandler : public js::Wrapper {
  ProxyTraps mTraps;

 public:
  WrapperProxyHandler(const ProxyTraps& aTraps)
      : js::Wrapper(0), mTraps(aTraps) {}

  virtual bool finalizeInBackground(const JS::Value& priv) const override {
    return false;
  }

  DEFER_TO_TRAP_OR_BASE_CLASS(js::Wrapper)

  virtual bool getOwnPropertyDescriptor(
      JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
      JS::MutableHandle<JS::PropertyDescriptor> desc) const override {
    return mTraps.getOwnPropertyDescriptor
               ? mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc)
               : js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
  }

  virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy,
                              JS::HandleId id,
                              JS::Handle<JS::PropertyDescriptor> desc,
                              JS::ObjectOpResult& result) const override {
    return mTraps.defineProperty
               ? mTraps.defineProperty(cx, proxy, id, desc, result)
               : js::Wrapper::defineProperty(cx, proxy, id, desc, result);
  }

  virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy,
                               JS::MutableHandleIdVector props) const override {
    return mTraps.ownPropertyKeys
               ? mTraps.ownPropertyKeys(cx, proxy, props)
               : js::Wrapper::ownPropertyKeys(cx, proxy, props);
  }

  virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                       JS::ObjectOpResult& result) const override {
    return mTraps.delete_ ? mTraps.delete_(cx, proxy, id, result)
                          : js::Wrapper::delete_(cx, proxy, id, result);
  }

  virtual bool preventExtensions(JSContext* cx, JS::HandleObject proxy,
                                 JS::ObjectOpResult& result) const override {
    return mTraps.preventExtensions
               ? mTraps.preventExtensions(cx, proxy, result)
               : js::Wrapper::preventExtensions(cx, proxy, result);
  }

  virtual bool isExtensible(JSContext* cx, JS::HandleObject proxy,
                            bool* succeeded) const override {
    return mTraps.isExtensible
               ? mTraps.isExtensible(cx, proxy, succeeded)
               : js::Wrapper::isExtensible(cx, proxy, succeeded);
  }
};

class RustJSPrincipal : public JSPrincipals {
  const void* origin;  // box with origin in it
  void (*destroyCallback)(JSPrincipals* principal);
  bool (*writeCallback)(JSContext* cx, JSStructuredCloneWriter* writer);

 public:
  RustJSPrincipal(const void* origin, void (*destroy)(JSPrincipals* principal),
                  bool (*write)(JSContext* cx, JSStructuredCloneWriter* writer))
      : JSPrincipals() {
    this->origin = origin;
    this->destroyCallback = destroy;
    this->writeCallback = write;
  }

  virtual const void* getOrigin() { return origin; }

  virtual void destroy() {
    if (this->destroyCallback) this->destroyCallback(this);
  }

  bool isSystemOrAddonPrincipal() { return false; }

  bool write(JSContext* cx, JSStructuredCloneWriter* writer) {
    return this->writeCallback ? this->writeCallback(cx, writer) : false;
  }
};

class ForwardingProxyHandler : public js::BaseProxyHandler {
  ProxyTraps mTraps;
  const void* mExtra;

 public:
  ForwardingProxyHandler(const ProxyTraps& aTraps, const void* aExtra)
      : js::BaseProxyHandler(&HandlerFamily), mTraps(aTraps), mExtra(aExtra) {}

  const void* getExtra() const { return mExtra; }

  virtual bool finalizeInBackground(const JS::Value& priv) const override {
    return false;
  }

  DEFER_TO_TRAP_OR_BASE_CLASS(BaseProxyHandler)

  virtual bool getOwnPropertyDescriptor(
      JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
      JS::MutableHandle<JS::PropertyDescriptor> desc) const override {
    return mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc);
  }

  virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy,
                              JS::HandleId id,
                              JS::Handle<JS::PropertyDescriptor> desc,
                              JS::ObjectOpResult& result) const override {
    return mTraps.defineProperty(cx, proxy, id, desc, result);
  }

  virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy,
                               JS::MutableHandleIdVector props) const override {
    return mTraps.ownPropertyKeys(cx, proxy, props);
  }

  virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                       JS::ObjectOpResult& result) const override {
    return mTraps.delete_(cx, proxy, id, result);
  }

  virtual bool getPrototypeIfOrdinary(
      JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
      JS::MutableHandleObject protop) const override {
    return mTraps.getPrototypeIfOrdinary(cx, proxy, isOrdinary, protop);
  }

  virtual bool preventExtensions(JSContext* cx, JS::HandleObject proxy,
                                 JS::ObjectOpResult& result) const override {
    return mTraps.preventExtensions(cx, proxy, result);
  }

  virtual bool isExtensible(JSContext* cx, JS::HandleObject proxy,
                            bool* succeeded) const override {
    return mTraps.isExtensible(cx, proxy, succeeded);
  }
};

extern "C" {

JSPrincipals* CreateRustJSPrincipal(
    const void* origin, void (*destroy)(JSPrincipals* principal),
    bool (*write)(JSContext* cx, JSStructuredCloneWriter* writer)) {
  return new RustJSPrincipal(origin, destroy, write);
}

const void* GetPrincipalOrigin(JSPrincipals* principal) {
  return static_cast<RustJSPrincipal*>(principal)->getOrigin();
}

bool InvokeGetOwnPropertyDescriptor(
    const void* handler, JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
    JS::MutableHandle<JS::PropertyDescriptor> desc) {
  return static_cast<const ForwardingProxyHandler*>(handler)
      ->getOwnPropertyDescriptor(cx, proxy, id, desc);
}

bool InvokeHasOwn(const void* handler, JSContext* cx, JS::HandleObject proxy,
                  JS::HandleId id, bool* bp) {
  return static_cast<const js::BaseProxyHandler*>(handler)->hasOwn(cx, proxy,
                                                                   id, bp);
}

JS::Value RUST_JS_NumberValue(double d) { return JS_NumberValue(d); }

const JSJitInfo* RUST_FUNCTION_VALUE_TO_JITINFO(JS::Value v) {
  return FUNCTION_VALUE_TO_JITINFO(v);
}

JS::CallArgs CreateCallArgsFromVp(unsigned argc, JS::Value* vp) {
  return JS::CallArgsFromVp(argc, vp);
}

bool CallJitGetterOp(const JSJitInfo* info, JSContext* cx,
                     JS::HandleObject thisObj, void* specializedThis,
                     unsigned argc, JS::Value* vp) {
  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  return info->getter(cx, thisObj, specializedThis, JSJitGetterCallArgs(args));
}

bool CallJitSetterOp(const JSJitInfo* info, JSContext* cx,
                     JS::HandleObject thisObj, void* specializedThis,
                     unsigned argc, JS::Value* vp) {
  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  return info->setter(cx, thisObj, specializedThis, JSJitSetterCallArgs(args));
}

bool CallJitMethodOp(const JSJitInfo* info, JSContext* cx,
                     JS::HandleObject thisObj, void* specializedThis,
                     uint32_t argc, JS::Value* vp) {
  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  return info->method(cx, thisObj, specializedThis, JSJitMethodCallArgs(args));
}

const void* CreateProxyHandler(const ProxyTraps* aTraps, const void* aExtra) {
  return new ForwardingProxyHandler(*aTraps, aExtra);
}

const void* CreateWrapperProxyHandler(const ProxyTraps* aTraps) {
  return new WrapperProxyHandler(*aTraps);
}

const void* GetCrossCompartmentWrapper() {
  return &js::CrossCompartmentWrapper::singleton;
}

const void* GetSecurityWrapper() {
  return &js::CrossCompartmentSecurityWrapper::singleton;
}

void DeleteCompileOptions(JS::ReadOnlyCompileOptions* aOpts) {
  delete static_cast<JS::OwningCompileOptions*>(aOpts);
}

JS::ReadOnlyCompileOptions* NewCompileOptions(JSContext* aCx, const char* aFile,
                                              unsigned aLine) {
  JS::CompileOptions opts(aCx);
  opts.setFileAndLine(aFile, aLine);

  JS::OwningCompileOptions* owned = new JS::OwningCompileOptions(aCx);
  if (!owned) {
    return nullptr;
  }

  if (!owned->copy(aCx, opts)) {
    DeleteCompileOptions(owned);
    return nullptr;
  }

  return owned;
}

JSObject* NewProxyObject(JSContext* aCx, const void* aHandler,
                         JS::HandleValue aPriv, JSObject* proto,
                         JSObject* parent, JSObject* call,
                         JSObject* construct) {
  js::ProxyOptions options;
  return js::NewProxyObject(aCx, (js::BaseProxyHandler*)aHandler, aPriv, proto,
                            options);
}

JSObject* WrapperNew(JSContext* aCx, JS::HandleObject aObj,
                     const void* aHandler, const JSClass* aClass) {
  js::WrapperOptions options;
  if (aClass) {
    options.setClass(aClass);
  }
  return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
}

JSObject* WrapperNewSingleton(JSContext* aCx, JS::HandleObject aObj,
                              const void* aHandler, const JSClass* aClass) {
  js::WrapperOptions options;
  if (aClass) {
    options.setClass(aClass);
  }
  return js::Wrapper::NewSingleton(aCx, aObj, (const js::Wrapper*)aHandler,
                                   options);
}

const JSClass WindowProxyClass = PROXY_CLASS_DEF(
    "Proxy", JSCLASS_HAS_RESERVED_SLOTS(1)); /* additional class flags */

const JSClass* GetWindowProxyClass() { return &WindowProxyClass; }

JS::Value GetProxyReservedSlot(JSObject* obj, uint32_t slot) {
  return js::GetProxyReservedSlot(obj, slot);
}

void SetProxyReservedSlot(JSObject* obj, uint32_t slot, const JS::Value* val) {
  js::SetProxyReservedSlot(obj, slot, *val);
}

JSObject* NewWindowProxy(JSContext* aCx, JS::HandleObject aObj,
                         const void* aHandler) {
  return WrapperNewSingleton(aCx, aObj, aHandler, &WindowProxyClass);
}

JS::Value GetProxyPrivate(JSObject* obj) { return js::GetProxyPrivate(obj); }

void SetProxyPrivate(JSObject* obj, const JS::Value* expando) {
  js::SetProxyPrivate(obj, *expando);
}

bool RUST_JSID_IS_INT(JS::HandleId id) { return JSID_IS_INT(id); }

jsid int_to_jsid(int32_t i) { return INT_TO_JSID(i); }

int32_t RUST_JSID_TO_INT(JS::HandleId id) { return JSID_TO_INT(id); }

bool RUST_JSID_IS_STRING(JS::HandleId id) { return JSID_IS_STRING(id); }

JSString* RUST_JSID_TO_STRING(JS::HandleId id) { return JSID_TO_STRING(id); }

jsid RUST_SYMBOL_TO_JSID(JS::Symbol* sym) { return SYMBOL_TO_JSID(sym); }

void RUST_SET_JITINFO(JSFunction* func, const JSJitInfo* info) {
  SET_JITINFO(func, info);
}

jsid RUST_INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str) {
  return JS::PropertyKey::fromPinnedString(str);
}

const JSErrorFormatString* RUST_js_GetErrorMessage(void* userRef,
                                                   uint32_t errorNumber) {
  return js::GetErrorMessage(userRef, errorNumber);
}

bool IsProxyHandlerFamily(JSObject* obj) {
  auto family = js::GetProxyHandler(obj)->family();
  return family == &HandlerFamily;
}

const void* GetProxyHandlerFamily() { return &HandlerFamily; }

const void* GetProxyHandlerExtra(JSObject* obj) {
  const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
  assert(handler->family() == &HandlerFamily);
  return static_cast<const ForwardingProxyHandler*>(handler)->getExtra();
}

const void* GetProxyHandler(JSObject* obj) {
  const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
  assert(handler->family() == &HandlerFamily);
  return handler;
}

void ReportError(JSContext* aCx, const char* aError) {
#ifdef DEBUG
  for (const char* p = aError; *p; ++p) {
    assert(*p != '%');
  }
#endif
  JS_ReportErrorASCII(aCx, "%s", aError);
}

bool IsWrapper(JSObject* obj) { return js::IsWrapper(obj); }

JSObject* UnwrapObjectStatic(JSObject* obj) {
  return js::CheckedUnwrapStatic(obj);
}

JSObject* UncheckedUnwrapObject(JSObject* obj, bool stopAtOuter) {
  return js::UncheckedUnwrap(obj, stopAtOuter);
}

JS::PersistentRootedIdVector* CreateRootedIdVector(JSContext* cx) {
  return new JS::PersistentRootedIdVector(cx);
}

bool AppendToRootedIdVector(JS::PersistentRootedIdVector* v, jsid id) {
  return v->append(id);
}

const jsid* SliceRootedIdVector(const JS::PersistentRootedIdVector* v,
                                size_t* length) {
  *length = v->length();
  return v->begin();
}

void DestroyRootedIdVector(JS::PersistentRootedIdVector* v) { delete v; }

JS::MutableHandleIdVector GetMutableHandleIdVector(
    JS::PersistentRootedIdVector* v) {
  return JS::MutableHandleIdVector(v);
}

JS::PersistentRootedObjectVector* CreateRootedObjectVector(JSContext* aCx) {
  JS::PersistentRootedObjectVector* vec =
      new JS::PersistentRootedObjectVector(aCx);
  return vec;
}

bool AppendToRootedObjectVector(JS::PersistentRootedObjectVector* v,
                                JSObject* obj) {
  return v->append(obj);
}

void DeleteRootedObjectVector(JS::PersistentRootedObjectVector* v) { delete v; }

#if defined(__linux__)
#  include <malloc.h>
#elif defined(__APPLE__)
#  include <malloc/malloc.h>
#elif defined(__MINGW32__) || defined(__MINGW64__)
// nothing needed here
#elif defined(_MSC_VER)
// nothing needed here
#else
#  error "unsupported platform"
#endif

// SpiderMonkey-in-Rust currently uses system malloc, not jemalloc.
static size_t MallocSizeOf(const void* aPtr) {
#if defined(__linux__)
  return malloc_usable_size((void*)aPtr);
#elif defined(__APPLE__)
  return malloc_size((void*)aPtr);
#elif defined(__MINGW32__) || defined(__MINGW64__)
  return _msize((void*)aPtr);
#elif defined(_MSC_VER)
  return _msize((void*)aPtr);
#else
#  error "unsupported platform"
#endif
}

bool CollectServoSizes(JSContext* cx, JS::ServoSizes* sizes) {
  mozilla::PodZero(sizes);
  return JS::AddServoSizeOf(cx, MallocSizeOf,
                            /* ObjectPrivateVisitor = */ nullptr, sizes);
}

void CallValueTracer(JSTracer* trc, JS::Heap<JS::Value>* valuep,
                     const char* name) {
  JS::TraceEdge(trc, valuep, name);
}

void CallIdTracer(JSTracer* trc, JS::Heap<jsid>* idp, const char* name) {
  JS::TraceEdge(trc, idp, name);
}

void CallObjectTracer(JSTracer* trc, JS::Heap<JSObject*>* objp,
                      const char* name) {
  JS::TraceEdge(trc, objp, name);
}

void CallStringTracer(JSTracer* trc, JS::Heap<JSString*>* strp,
                      const char* name) {
  JS::TraceEdge(trc, strp, name);
}

void CallBigIntTracer(JSTracer* trc, JS::Heap<JS::BigInt*>* bip,
                      const char* name) {
  JS::TraceEdge(trc, bip, name);
}

void CallScriptTracer(JSTracer* trc, JS::Heap<JSScript*>* scriptp,
                      const char* name) {
  JS::TraceEdge(trc, scriptp, name);
}

void CallFunctionTracer(JSTracer* trc, JS::Heap<JSFunction*>* funp,
                        const char* name) {
  JS::TraceEdge(trc, funp, name);
}

void CallUnbarrieredObjectTracer(JSTracer* trc, JSObject** objp,
                                 const char* name) {
  js::UnsafeTraceManuallyBarrieredEdge(trc, objp, name);
}

bool IsDebugBuild() {
#ifdef JS_DEBUG
  return true;
#else
  return false;
#endif
}

#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type)                    \
  void Get##Type##ArrayLengthAndData(JSObject* obj, uint32_t* length,     \
                                     bool* isSharedMemory, type** data) { \
    js::Get##Type##ArrayLengthAndData(obj, length, isSharedMemory, data); \
  }

JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)

#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR

JSAutoStructuredCloneBuffer* NewJSAutoStructuredCloneBuffer(
    JS::StructuredCloneScope scope,
    const JSStructuredCloneCallbacks* callbacks) {
  return js_new<JSAutoStructuredCloneBuffer>(scope, callbacks, nullptr);
}

JSStructuredCloneData* DeleteJSAutoStructuredCloneBuffer(
    JSAutoStructuredCloneBuffer* buf) {
  js_delete(buf);
}

size_t GetLengthOfJSStructuredCloneData(JSStructuredCloneData* data) {
  assert(data != nullptr);

  size_t len = 0;

  data->ForEachDataChunk([&](const char* bytes, size_t size) {
    len += size;
    return true;
  });

  return len;
}

void CopyJSStructuredCloneData(JSStructuredCloneData* src, uint8_t* dest) {
  assert(src != nullptr);
  assert(dest != nullptr);

  size_t bytes_copied = 0;

  src->ForEachDataChunk([&](const char* bytes, size_t size) {
    memcpy(dest, bytes, size);
    dest += size;
    return true;
  });
}

bool WriteBytesToJSStructuredCloneData(const uint8_t* src, size_t len,
                                       JSStructuredCloneData* dest) {
  assert(src != nullptr);
  assert(dest != nullptr);

  return dest->AppendBytes(reinterpret_cast<const char*>(src), len);
}

char* JSEncodeStringToUTF8(JSContext* cx, JS::HandleString string) {
  return JS_EncodeStringToUTF8(cx, string).release();
}

}  // extern "C"
