/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */

/* private inline methods (#include'd by xpcprivate.h). */

#ifndef xpcinlines_h___
#define xpcinlines_h___

#include <algorithm>

#include "js/PropertyAndElement.h"  // JS_HasProperty, JS_HasPropertyById

/***************************************************************************/

inline void XPCJSRuntime::AddSubjectToFinalizationWJS(
    nsXPCWrappedJS* wrappedJS) {
  mSubjectToFinalizationWJS.insertBack(wrappedJS);
}

/***************************************************************************/

inline bool XPCCallContext::IsValid() const { return mState != INIT_FAILED; }

inline XPCJSContext* XPCCallContext::GetContext() const {
  CHECK_STATE(HAVE_CONTEXT);
  return mXPCJSContext;
}

inline JSContext* XPCCallContext::GetJSContext() const {
  CHECK_STATE(HAVE_CONTEXT);
  return mJSContext;
}

inline XPCCallContext* XPCCallContext::GetPrevCallContext() const {
  CHECK_STATE(HAVE_CONTEXT);
  return mPrevCallContext;
}

inline XPCWrappedNative* XPCCallContext::GetWrapper() const {
  if (mState == INIT_FAILED) {
    return nullptr;
  }

  CHECK_STATE(HAVE_OBJECT);
  return mWrapper;
}

inline bool XPCCallContext::CanGetTearOff() const {
  return mState >= HAVE_OBJECT;
}

inline XPCWrappedNativeTearOff* XPCCallContext::GetTearOff() const {
  CHECK_STATE(HAVE_OBJECT);
  return mTearOff;
}

inline nsIXPCScriptable* XPCCallContext::GetScriptable() const {
  CHECK_STATE(HAVE_OBJECT);
  return mScriptable;
}

inline XPCNativeSet* XPCCallContext::GetSet() const {
  CHECK_STATE(HAVE_NAME);
  return mSet;
}

inline XPCNativeInterface* XPCCallContext::GetInterface() const {
  CHECK_STATE(HAVE_NAME);
  return mInterface;
}

inline XPCNativeMember* XPCCallContext::GetMember() const {
  CHECK_STATE(HAVE_NAME);
  return mMember;
}

inline bool XPCCallContext::HasInterfaceAndMember() const {
  return mState >= HAVE_NAME && mInterface && mMember;
}

inline bool XPCCallContext::GetStaticMemberIsLocal() const {
  CHECK_STATE(HAVE_NAME);
  return mStaticMemberIsLocal;
}

inline unsigned XPCCallContext::GetArgc() const {
  CHECK_STATE(READY_TO_CALL);
  return mArgc;
}

inline JS::Value* XPCCallContext::GetArgv() const {
  CHECK_STATE(READY_TO_CALL);
  return mArgv;
}

inline void XPCCallContext::SetRetVal(const JS::Value& val) {
  CHECK_STATE(HAVE_ARGS);
  if (mRetVal) {
    *mRetVal = val;
  }
}

inline jsid XPCCallContext::GetResolveName() const {
  CHECK_STATE(HAVE_CONTEXT);
  return GetContext()->GetResolveName();
}

inline jsid XPCCallContext::SetResolveName(JS::HandleId name) {
  CHECK_STATE(HAVE_CONTEXT);
  return GetContext()->SetResolveName(name);
}

inline XPCWrappedNative* XPCCallContext::GetResolvingWrapper() const {
  CHECK_STATE(HAVE_OBJECT);
  return GetContext()->GetResolvingWrapper();
}

inline XPCWrappedNative* XPCCallContext::SetResolvingWrapper(
    XPCWrappedNative* w) {
  CHECK_STATE(HAVE_OBJECT);
  return GetContext()->SetResolvingWrapper(w);
}

inline uint16_t XPCCallContext::GetMethodIndex() const {
  CHECK_STATE(HAVE_OBJECT);
  return mMethodIndex;
}

/***************************************************************************/
inline XPCNativeInterface* XPCNativeMember::GetInterface() const {
  XPCNativeMember* arrayStart =
      const_cast<XPCNativeMember*>(this - mIndexInInterface);
  size_t arrayStartOffset = XPCNativeInterface::OffsetOfMembers();
  char* xpcNativeInterfaceStart =
      reinterpret_cast<char*>(arrayStart) - arrayStartOffset;
  return reinterpret_cast<XPCNativeInterface*>(xpcNativeInterfaceStart);
}

/***************************************************************************/

inline const nsIID* XPCNativeInterface::GetIID() const { return &mInfo->IID(); }

inline const char* XPCNativeInterface::GetNameString() const {
  return mInfo->Name();
}

inline XPCNativeMember* XPCNativeInterface::FindMember(jsid name) const {
  const XPCNativeMember* member = mMembers;
  for (int i = (int)mMemberCount; i > 0; i--, member++) {
    if (member->GetName() == name) {
      return const_cast<XPCNativeMember*>(member);
    }
  }
  return nullptr;
}

/* static */
inline size_t XPCNativeInterface::OffsetOfMembers() {
  return offsetof(XPCNativeInterface, mMembers);
}

/***************************************************************************/

inline XPCNativeSetKey::XPCNativeSetKey(XPCNativeSet* baseSet,
                                        XPCNativeInterface* addition)
    : mCx(nullptr), mBaseSet(baseSet), mAddition(addition) {
  MOZ_ASSERT(mBaseSet);
  MOZ_ASSERT(mAddition);
  MOZ_ASSERT(!mBaseSet->HasInterface(mAddition));
}

/***************************************************************************/

inline bool XPCNativeSet::FindMember(jsid name, XPCNativeMember** pMember,
                                     uint16_t* pInterfaceIndex) const {
  XPCNativeInterface* const* iface;
  int count = (int)mInterfaceCount;
  int i;

  // look for interface names first

  for (i = 0, iface = mInterfaces; i < count; i++, iface++) {
    if (name == (*iface)->GetName()) {
      if (pMember) {
        *pMember = nullptr;
      }
      if (pInterfaceIndex) {
        *pInterfaceIndex = (uint16_t)i;
      }
      return true;
    }
  }

  // look for method names
  for (i = 0, iface = mInterfaces; i < count; i++, iface++) {
    XPCNativeMember* member = (*iface)->FindMember(name);
    if (member) {
      if (pMember) {
        *pMember = member;
      }
      if (pInterfaceIndex) {
        *pInterfaceIndex = (uint16_t)i;
      }
      return true;
    }
  }
  return false;
}

inline bool XPCNativeSet::FindMember(
    jsid name, XPCNativeMember** pMember,
    RefPtr<XPCNativeInterface>* pInterface) const {
  uint16_t index;
  if (!FindMember(name, pMember, &index)) {
    return false;
  }
  *pInterface = mInterfaces[index];
  return true;
}

inline bool XPCNativeSet::FindMember(JS::HandleId name,
                                     XPCNativeMember** pMember,
                                     RefPtr<XPCNativeInterface>* pInterface,
                                     XPCNativeSet* protoSet,
                                     bool* pIsLocal) const {
  XPCNativeMember* Member;
  RefPtr<XPCNativeInterface> Interface;
  XPCNativeMember* protoMember;

  if (!FindMember(name, &Member, &Interface)) {
    return false;
  }

  *pMember = Member;

  *pIsLocal = !Member || !protoSet ||
              (protoSet != this &&
               !protoSet->MatchesSetUpToInterface(this, Interface) &&
               (!protoSet->FindMember(name, &protoMember, (uint16_t*)nullptr) ||
                protoMember != Member));

  *pInterface = std::move(Interface);

  return true;
}

inline bool XPCNativeSet::HasInterface(XPCNativeInterface* aInterface) const {
  XPCNativeInterface* const* pp = mInterfaces;

  for (int i = (int)mInterfaceCount; i > 0; i--, pp++) {
    if (aInterface == *pp) {
      return true;
    }
  }
  return false;
}

inline bool XPCNativeSet::MatchesSetUpToInterface(
    const XPCNativeSet* other, XPCNativeInterface* iface) const {
  int count = std::min(int(mInterfaceCount), int(other->mInterfaceCount));

  XPCNativeInterface* const* pp1 = mInterfaces;
  XPCNativeInterface* const* pp2 = other->mInterfaces;

  for (int i = (int)count; i > 0; i--, pp1++, pp2++) {
    XPCNativeInterface* cur = (*pp1);
    if (cur != (*pp2)) {
      return false;
    }
    if (cur == iface) {
      return true;
    }
  }
  return false;
}

/***************************************************************************/

inline JSObject* XPCWrappedNativeTearOff::GetJSObjectPreserveColor() const {
  return mJSObject.unbarrieredGetPtr();
}

inline JSObject* XPCWrappedNativeTearOff::GetJSObject() { return mJSObject; }

inline void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj) {
  MOZ_ASSERT(!IsMarked());
  mJSObject = JSObj;
}

inline void XPCWrappedNativeTearOff::JSObjectMoved(JSObject* obj,
                                                   const JSObject* old) {
  MOZ_ASSERT(!IsMarked());
  MOZ_ASSERT(mJSObject == old);
  mJSObject = obj;
}

inline XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff() {
  MOZ_COUNT_DTOR(XPCWrappedNativeTearOff);
  MOZ_ASSERT(!(GetInterface() || GetNative() || GetJSObjectPreserveColor()),
             "tearoff not empty in dtor");
}

/***************************************************************************/

inline void XPCWrappedNative::SweepTearOffs() {
  for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to;
       to = to->GetNextTearOff()) {
    bool marked = to->IsMarked();
    to->Unmark();
    if (marked) {
      continue;
    }

    // If this tearoff does not have a live dedicated JSObject,
    // then let's recycle it.
    if (!to->GetJSObjectPreserveColor()) {
      to->SetNative(nullptr);
      to->SetInterface(nullptr);
    }
  }
}

/***************************************************************************/

inline bool xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj,
                                     jsid idArg) {
  JS::RootedId id(cx, idArg);
  bool dummy;
  return JS_HasPropertyById(cx, obj, id, &dummy);
}

inline jsid GetJSIDByIndex(JSContext* cx, unsigned index) {
  XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
  return xpcrt->GetStringID(index);
}

inline bool ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx) {
  XPCThrower::ThrowBadParam(rv, paramNum, ccx);
  return false;
}

inline void ThrowBadResult(nsresult result, XPCCallContext& ccx) {
  XPCThrower::ThrowBadResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, result, ccx);
}

/***************************************************************************/

inline void xpc::CleanupValue(const nsXPTType& aType, void* aValue,
                              uint32_t aArrayLen) {
  // Check if we can do a cheap early return, and only perform the inner call
  // if we can't. We never have to clean up null pointer types or arithmetic
  // types.
  //
  // NOTE: We can skip zeroing arithmetic types in CleanupValue, as they are
  // already in a valid state.
  if (aType.IsArithmetic() || (aType.IsPointer() && !*(void**)aValue)) {
    return;
  }
  xpc::InnerCleanupValue(aType, aValue, aArrayLen);
}

/***************************************************************************/

#endif /* xpcinlines_h___ */
