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

Quelle  XPCWrappedNativeInfo.cpp   Sprache: C

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


/* Manage the shared info about interfaces for use by wrappedNatives. */

#include "xpcprivate.h"
#include "XPCMaps.h"
#include "js/Wrapper.h"

#include "mozilla/MemoryReporting.h"
#include "mozilla/SourceLocation.h"
#include "nsIScriptError.h"
#include "nsPrintfCString.h"
#include "nsPointerHashKeys.h"

using namespace JS;
using namespace mozilla;

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

// XPCNativeMember

// static
bool XPCNativeMember::GetCallInfo(JSObject* funobj,
                                  RefPtr<XPCNativeInterface>* pInterface,
                                  XPCNativeMember** pMember) {
  funobj = js::UncheckedUnwrap(funobj);
  Value memberVal =
      js::GetFunctionNativeReserved(funobj, XPC_FUNCTION_NATIVE_MEMBER_SLOT);

  *pMember = static_cast<XPCNativeMember*>(memberVal.toPrivate());
  *pInterface = (*pMember)->GetInterface();

  return true;
}

bool XPCNativeMember::NewFunctionObject(XPCCallContext& ccx,
                                        XPCNativeInterface* iface,
                                        HandleObject parent, Value* pval) {
  MOZ_ASSERT(!IsConstant(),
             "Only call this if you're sure this is not a constant!");

  return Resolve(ccx, iface, parent, pval);
}

bool XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
                              HandleObject parent, Value* vp) {
  MOZ_ASSERT(iface == GetInterface());
  if (IsConstant()) {
    RootedValue resultVal(ccx);
    nsCString name;
    if (NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &resultVal,
                                                         getter_Copies(name))))
      return false;

    *vp = resultVal;

    return true;
  }
  // else...

  // This is a method or attribute - we'll be needing a function object

  int argc;
  JSNative callback;

  if (IsMethod()) {
    const nsXPTMethodInfo* info;
    if (NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info))) {
      return false;
    }

    // Note: ASSUMES that retval is last arg.
    argc = (int)info->ParamCount();
    if (info->HasRetval()) {
      argc--;
    }

    callback = XPC_WN_CallMethod;
  } else {
    argc = 0;
    callback = XPC_WN_GetterSetter;
  }

  jsid name = GetName();
  JS_MarkCrossZoneId(ccx, name);

  JSFunction* fun;
  if (name.isString()) {
    fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, name);
  } else {
    fun = js::NewFunctionWithReserved(ccx, callback, argc, 0, nullptr);
  }
  if (!fun) {
    return false;
  }

  JSObject* funobj = JS_GetFunctionObject(fun);
  if (!funobj) {
    return false;
  }

  js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_NATIVE_MEMBER_SLOT,
                                PrivateValue(this));
  js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_PARENT_OBJECT_SLOT,
                                ObjectValue(*parent));

  vp->setObject(*funobj);

  return true;
}

/***************************************************************************/
// XPCNativeInterface

XPCNativeInterface::~XPCNativeInterface() {
  XPCJSRuntime::Get()->GetIID2NativeInterfaceMap()->Remove(this);
}

// static
already_AddRefed<XPCNativeInterface> XPCNativeInterface::GetNewOrUsed(
    JSContext* cx, const nsIID* iid) {
  RefPtr<XPCNativeInterface> iface;
  XPCJSRuntime* rt = XPCJSRuntime::Get();

  IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
  if (!map) {
    return nullptr;
  }

  iface = map->Find(*iid);

  if (iface) {
    return iface.forget();
  }

  const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByIID(*iid);
  if (!info) {
    return nullptr;
  }

  return NewInstance(cx, map, info);
}

// static
already_AddRefed<XPCNativeInterface> XPCNativeInterface::GetNewOrUsed(
    JSContext* cx, const nsXPTInterfaceInfo* info) {
  RefPtr<XPCNativeInterface> iface;

  XPCJSRuntime* rt = XPCJSRuntime::Get();

  IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
  if (!map) {
    return nullptr;
  }

  iface = map->Find(info->IID());

  if (iface) {
    return iface.forget();
  }

  return NewInstance(cx, map, info);
}

// static
already_AddRefed<XPCNativeInterface> XPCNativeInterface::GetNewOrUsed(
    JSContext* cx, const char* name) {
  const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByName(name);
  return info ? GetNewOrUsed(cx, info) : nullptr;
}

// static
already_AddRefed<XPCNativeInterface> XPCNativeInterface::GetISupports(
    JSContext* cx) {
  // XXX We should optimize this to cache this common XPCNativeInterface.
  return GetNewOrUsed(cx, &NS_GET_IID(nsISupports));
}

// static
already_AddRefed<XPCNativeInterface> XPCNativeInterface::NewInstance(
    JSContext* cx, IID2NativeInterfaceMap* aMap,
    const nsXPTInterfaceInfo* aInfo) {
  // XXX Investigate lazy init? This is a problem given the
  // 'placement new' scheme - we need to at least know how big to make
  // the object. We might do a scan of methods to determine needed size,
  // then make our object, but avoid init'ing *any* members until asked?
  // Find out how often we create these objects w/o really looking at
  // (or using) the members.

  if (aInfo->IsMainProcessScriptableOnly() && !XRE_IsParentProcess()) {
    nsCOMPtr<nsIConsoleService> console(
        do_GetService(NS_CONSOLESERVICE_CONTRACTID));
    if (console) {
      const char* intfNameChars = aInfo->Name();
      nsPrintfCString errorMsg("Use of %s in content process is deprecated.",
                               intfNameChars);

      auto location = JSCallingLocation::Get(cx);
      nsCOMPtr<nsIScriptError> error(
          do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
      error->Init(NS_ConvertUTF8toUTF16(errorMsg), location.FileName(),
                  location.mLine, location.mColumn, nsIScriptError::warningFlag,
                  "chrome javascript"_ns, false /* from private window */,
                  true /* from chrome context */);
      console->LogMessage(error);
    }
  }

  // Make sure the code below does not GC. This means we don't need to trace the
  // PropertyKeys in the MemberVector, or the XPCNativeInterface we create
  // before it's added to the map.
  JS::AutoCheckCannotGC nogc;

  const uint16_t methodCount = aInfo->MethodCount();
  const uint16_t constCount = aInfo->ConstantCount();
  const uint16_t totalCount = methodCount + constCount;

  using MemberVector =
      mozilla::Vector<XPCNativeMember, 16, InfallibleAllocPolicy>;
  MemberVector members;
  MOZ_ALWAYS_TRUE(members.reserve(totalCount));

  // NOTE: since getters and setters share a member, we might not use all
  // of the member objects.

  for (unsigned int i = 0; i < methodCount; i++) {
    const nsXPTMethodInfo& info = aInfo->Method(i);

    // don't reflect Addref or Release
    if (i == 1 || i == 2) {
      continue;
    }

    if (!info.IsReflectable()) {
      continue;
    }

    jsid name;
    if (!info.GetId(cx, name)) {
      NS_ERROR("bad method name");
      return nullptr;
    }

    if (info.IsSetter()) {
      MOZ_ASSERT(!members.empty(), "bad setter");
      // Note: ASSUMES Getter/Setter pairs are next to each other
      // This is a rule of the typelib spec.
      XPCNativeMember* cur = &members.back();
      MOZ_ASSERT(cur->GetName() == name, "bad setter");
      MOZ_ASSERT(cur->IsReadOnlyAttribute(), "bad setter");
      MOZ_ASSERT(cur->GetIndex() == i - 1, "bad setter");
      cur->SetWritableAttribute();
    } else {
      // XXX need better way to find dups
      // MOZ_ASSERT(!LookupMemberByID(name),"duplicate method name");
      size_t indexInInterface = members.length();
      if (indexInInterface == XPCNativeMember::GetMaxIndexInInterface()) {
        NS_WARNING("Too many members in interface");
        return nullptr;
      }
      XPCNativeMember cur;
      cur.SetName(name);
      if (info.IsGetter()) {
        cur.SetReadOnlyAttribute(i);
      } else {
        cur.SetMethod(i);
      }
      cur.SetIndexInInterface(indexInInterface);
      members.infallibleAppend(cur);
    }
  }

  for (unsigned int i = 0; i < constCount; i++) {
    RootedValue constant(cx);
    nsCString namestr;
    if (NS_FAILED(aInfo->GetConstant(i, &constant, getter_Copies(namestr)))) {
      return nullptr;
    }

    RootedString str(cx, JS_AtomizeString(cx, namestr.get()));
    if (!str) {
      NS_ERROR("bad constant name");
      return nullptr;
    }
    jsid name = PropertyKey::NonIntAtom(str);

    // XXX need better way to find dups
    // MOZ_ASSERT(!LookupMemberByID(name),"duplicate method/constant name");
    size_t indexInInterface = members.length();
    if (indexInInterface == XPCNativeMember::GetMaxIndexInInterface()) {
      NS_WARNING("Too many members in interface");
      return nullptr;
    }
    XPCNativeMember cur;
    cur.SetName(name);
    cur.SetConstant(i);
    cur.SetIndexInInterface(indexInInterface);
    members.infallibleAppend(cur);
  }

  const char* bytes = aInfo->Name();
  if (!bytes) {
    return nullptr;
  }
  RootedString str(cx, JS_AtomizeString(cx, bytes));
  if (!str) {
    return nullptr;
  }

  RootedId interfaceName(cx, PropertyKey::NonIntAtom(str));

  // Use placement new to create an object with the right amount of space
  // to hold the members array
  size_t size = sizeof(XPCNativeInterface);
  if (members.length() > 1) {
    size += (members.length() - 1) * sizeof(XPCNativeMember);
  }
  void* place = new char[size];
  if (!place) {
    return nullptr;
  }

  RefPtr<XPCNativeInterface> obj =
      new (place) XPCNativeInterface(aInfo, interfaceName);

  obj->mMemberCount = members.length();
  // copy valid members
  if (!members.empty()) {
    memcpy(obj->mMembers, members.begin(),
           members.length() * sizeof(XPCNativeMember));
  }

  if (!aMap->AddNew(obj)) {
    NS_ERROR("failed to add our interface!");
    return nullptr;
  }

  return obj.forget();
}

// static
void XPCNativeInterface::DestroyInstance(XPCNativeInterface* inst) {
  inst->~XPCNativeInterface();
  delete[] (char*)inst;
}

size_t XPCNativeInterface::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) {
  return mallocSizeOf(this);
}

void XPCNativeInterface::Trace(JSTracer* trc) {
  JS::TraceRoot(trc, &mName, "XPCNativeInterface::mName");

  for (size_t i = 0; i < mMemberCount; i++) {
    JS::PropertyKey key = mMembers[i].GetName();
    JS::TraceRoot(trc, &key, "XPCNativeInterface::mMembers");
    MOZ_ASSERT(mMembers[i].GetName() == key);
  }
}

void IID2NativeInterfaceMap::Trace(JSTracer* trc) {
  for (Map::Enum e(mMap); !e.empty(); e.popFront()) {
    XPCNativeInterface* iface = e.front().value();
    iface->Trace(trc);
  }
}

void XPCNativeInterface::DebugDump(int16_t depth) {
#ifdef DEBUG
  XPC_LOG_ALWAYS(("XPCNativeInterface @ %p"this));
  XPC_LOG_INDENT();
  XPC_LOG_ALWAYS(("name is %s", GetNameString()));
  XPC_LOG_ALWAYS(("mInfo @ %p", mInfo));
  XPC_LOG_OUTDENT();
#endif
}

/***************************************************************************/
// XPCNativeSetKey

HashNumber XPCNativeSetKey::Hash() const {
  HashNumber h = 0;

  if (mBaseSet) {
    // If we ever start using mCx here, adjust the constructors accordingly.
    XPCNativeInterface** current = mBaseSet->GetInterfaceArray();
    uint16_t count = mBaseSet->GetInterfaceCount();
    for (uint16_t i = 0; i < count; i++) {
      h = AddToHash(h, *(current++));
    }
  } else {
    // A newly created set will contain nsISupports first...
    RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports(mCx);
    h = AddToHash(h, isupp.get());

    // ...but no more than once.
    if (isupp == mAddition) {
      return h;
    }
  }

  if (mAddition) {
    h = AddToHash(h, mAddition.get());
  }

  return h;
}

/***************************************************************************/
// XPCNativeSet

XPCNativeSet::~XPCNativeSet() {
  // Remove |this| before we clear the interfaces to ensure that the
  // hashtable look up is correct.

  XPCJSRuntime::Get()->GetNativeSetMap()->Remove(this);

  for (int i = 0; i < mInterfaceCount; i++) {
    NS_RELEASE(mInterfaces[i]);
  }
}

// static
already_AddRefed<XPCNativeSet> XPCNativeSet::GetNewOrUsed(JSContext* cx,
                                                          const nsIID* iid) {
  RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(cx, iid);
  if (!iface) {
    return nullptr;
  }

  XPCNativeSetKey key(cx, iface);

  XPCJSRuntime* xpcrt = XPCJSRuntime::Get();
  NativeSetMap* map = xpcrt->GetNativeSetMap();
  if (!map) {
    return nullptr;
  }

  RefPtr<XPCNativeSet> set = map->Find(&key);

  if (set) {
    return set.forget();
  }

  set = NewInstance(cx, {std::move(iface)});
  if (!set) {
    return nullptr;
  }

  if (!map->AddNew(&key, set)) {
    NS_ERROR("failed to add our set!");
    set = nullptr;
  }

  return set.forget();
}

// static
already_AddRefed<XPCNativeSet> XPCNativeSet::GetNewOrUsed(
    JSContext* cx, nsIClassInfo* classInfo) {
  XPCJSRuntime* xpcrt = XPCJSRuntime::Get();
  ClassInfo2NativeSetMap* map = xpcrt->GetClassInfo2NativeSetMap();
  if (!map) {
    return nullptr;
  }

  RefPtr<XPCNativeSet> set = map->Find(classInfo);

  if (set) {
    return set.forget();
  }

  AutoTArray<nsIID, 4> iids;
  if (NS_FAILED(classInfo->GetInterfaces(iids))) {
    // Note: I'm making it OK for this call to fail so that one can add
    // nsIClassInfo to classes implemented in script without requiring this
    // method to be implemented.

    // Make sure these are set correctly...
    iids.Clear();
  }

  // Try to look up each IID's XPCNativeInterface object.
  nsTArray<RefPtr<XPCNativeInterface>> interfaces(iids.Length());
  for (auto& iid : iids) {
    RefPtr<XPCNativeInterface> iface =
        XPCNativeInterface::GetNewOrUsed(cx, &iid);
    if (iface) {
      interfaces.AppendElement(iface.forget());
    }
  }

  // Build a set from the interfaces specified here.
  if (interfaces.Length() > 0) {
    set = NewInstance(cx, std::move(interfaces));
    if (set) {
      NativeSetMap* map2 = xpcrt->GetNativeSetMap();
      if (!map2) {
        return set.forget();
      }

      XPCNativeSetKey key(set);
      XPCNativeSet* set2 = map2->Add(&key, set);
      if (!set2) {
        NS_ERROR("failed to add our set");
        return nullptr;
      }

      // It is okay to find an existing entry here because
      // we did not look for one before we called Add().
      if (set2 != set) {
        set = set2;
      }
    }
  } else {
    set = GetNewOrUsed(cx, &NS_GET_IID(nsISupports));
  }

  if (set) {
#ifdef DEBUG
    XPCNativeSet* set2 =
#endif
        map->Add(classInfo, set);
    MOZ_ASSERT(set2, "failed to add our set!");
    MOZ_ASSERT(set2 == set, "hashtables inconsistent!");
  }

  return set.forget();
}

// static
void XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo) {
  XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
  ClassInfo2NativeSetMap* map = xpcrt->GetClassInfo2NativeSetMap();
  if (map) {
    map->Remove(classInfo);
  }
}

// static
already_AddRefed<XPCNativeSet> XPCNativeSet::GetNewOrUsed(
    JSContext* cx, XPCNativeSetKey* key) {
  NativeSetMap* map = XPCJSRuntime::Get()->GetNativeSetMap();
  if (!map) {
    return nullptr;
  }

  RefPtr<XPCNativeSet> set = map->Find(key);

  if (set) {
    return set.forget();
  }

  if (key->GetBaseSet()) {
    set = NewInstanceMutate(key);
  } else {
    set = NewInstance(cx, {key->GetAddition()});
  }

  if (!set) {
    return nullptr;
  }

  if (!map->AddNew(key, set)) {
    NS_ERROR("failed to add our set!");
    set = nullptr;
  }

  return set.forget();
}

// static
already_AddRefed<XPCNativeSet> XPCNativeSet::GetNewOrUsed(
    JSContext* cx, XPCNativeSet* firstSet, XPCNativeSet* secondSet,
    bool preserveFirstSetOrder) {
  // Figure out how many interfaces we'll need in the new set.
  uint32_t uniqueCount = firstSet->mInterfaceCount;
  for (uint32_t i = 0; i < secondSet->mInterfaceCount; ++i) {
    if (!firstSet->HasInterface(secondSet->mInterfaces[i])) {
      uniqueCount++;
    }
  }

  // If everything in secondSet was a duplicate, we can just use the first
  // set.
  if (uniqueCount == firstSet->mInterfaceCount) {
    return RefPtr<XPCNativeSet>(firstSet).forget();
  }

  // If the secondSet is just a superset of the first, we can use it provided
  // that the caller doesn't care about ordering.
  if (!preserveFirstSetOrder && uniqueCount == secondSet->mInterfaceCount) {
    return RefPtr<XPCNativeSet>(secondSet).forget();
  }

  // Ok, darn. Now we have to make a new set.
  //
  // It would be faster to just create the new set all at once, but that
  // would involve wrangling with some pretty hairy code - especially since
  // a lot of stuff assumes that sets are created by adding one interface to an
  // existing set. So let's just do the slow and easy thing and hope that the
  // above optimizations handle the common cases.
  RefPtr<XPCNativeSet> currentSet = firstSet;
  for (uint32_t i = 0; i < secondSet->mInterfaceCount; ++i) {
    XPCNativeInterface* iface = secondSet->mInterfaces[i];
    if (!currentSet->HasInterface(iface)) {
      // Create a new augmented set, inserting this interface at the end.
      XPCNativeSetKey key(currentSet, iface);
      currentSet = XPCNativeSet::GetNewOrUsed(cx, &key);
      if (!currentSet) {
        return nullptr;
      }
    }
  }

  // We've got the union set. Hand it back to the caller.
  MOZ_ASSERT(currentSet->mInterfaceCount == uniqueCount);
  return currentSet.forget();
}

// static
already_AddRefed<XPCNativeSet> XPCNativeSet::NewInstance(
    JSContext* cx, nsTArray<RefPtr<XPCNativeInterface>>&& array) {
  if (array.Length() == 0) {
    return nullptr;
  }

  // We impose the invariant:
  // "All sets have exactly one nsISupports interface and it comes first."
  // This is the place where we impose that rule - even if given inputs
  // that don't exactly follow the rule.

  RefPtr<XPCNativeInterface> isup = XPCNativeInterface::GetISupports(cx);
  uint16_t slots = array.Length() + 1;

  for (auto key = array.begin(); key != array.end(); key++) {
    if (*key == isup) {
      slots--;
    }
  }

  // Use placement new to create an object with the right amount of space
  // to hold the members array
  int size = sizeof(XPCNativeSet);
  if (slots > 1) {
    size += (slots - 1) * sizeof(XPCNativeInterface*);
  }
  void* place = new char[size];
  RefPtr<XPCNativeSet> obj = new (place) XPCNativeSet();

  // Stick the nsISupports in front and skip additional nsISupport(s)
  XPCNativeInterface** outp = (XPCNativeInterface**)&obj->mInterfaces;

  NS_ADDREF(*(outp++) = isup);

  for (auto key = array.begin(); key != array.end(); key++) {
    RefPtr<XPCNativeInterface> cur = std::move(*key);
    if (isup == cur) {
      continue;
    }
    *(outp++) = cur.forget().take();
  }
  obj->mInterfaceCount = slots;

  return obj.forget();
}

// static
already_AddRefed<XPCNativeSet> XPCNativeSet::NewInstanceMutate(
    XPCNativeSetKey* key) {
  XPCNativeSet* otherSet = key->GetBaseSet();
  XPCNativeInterface* newInterface = key->GetAddition();

  MOZ_ASSERT(otherSet);

  if (!newInterface) {
    return nullptr;
  }

  // Use placement new to create an object with the right amount of space
  // to hold the members array
  int size = sizeof(XPCNativeSet);
  size += otherSet->mInterfaceCount * sizeof(XPCNativeInterface*);
  void* place = new char[size];
  RefPtr<XPCNativeSet> obj = new (place) XPCNativeSet();

  obj->mInterfaceCount = otherSet->mInterfaceCount + 1;

  XPCNativeInterface** src = otherSet->mInterfaces;
  XPCNativeInterface** dest = obj->mInterfaces;
  for (uint16_t i = 0; i < otherSet->mInterfaceCount; i++) {
    NS_ADDREF(*dest++ = *src++);
  }
  NS_ADDREF(*dest++ = newInterface);

  return obj.forget();
}

// static
void XPCNativeSet::DestroyInstance(XPCNativeSet* inst) {
  inst->~XPCNativeSet();
  delete[] (char*)inst;
}

size_t XPCNativeSet::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) {
  return mallocSizeOf(this);
}

void XPCNativeSet::DebugDump(int16_t depth) {
#ifdef DEBUG
  depth--;
  XPC_LOG_ALWAYS(("XPCNativeSet @ %p"this));
  XPC_LOG_INDENT();

  XPC_LOG_ALWAYS(("mInterfaceCount of %d", mInterfaceCount));
  if (depth) {
    for (uint16_t i = 0; i < mInterfaceCount; i++) {
      mInterfaces[i]->DebugDump(depth);
    }
  }
  XPC_LOG_OUTDENT();
#endif
}

Messung V0.5
C=90 H=93 G=91

¤ Dauer der Verarbeitung: 0.14 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.