gecko-dev/xpcom/base/nsCOMPtr.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

119 lines
3.5 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
2012-05-21 11:12:37 +00:00
/* 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/. */
1999-02-17 01:57:07 +00:00
#include "nsCOMPtr.h"
Bug 1493226, part 1 - Statically prevent trivial calls to do_QueryInterface r=froydnj This patch adds a static assert to enforce that do_QueryInterface is not used to go from a class to a base class, because that can be done more efficiently via a static_cast. This is done by putting the type of the source into the nsQueryInterface type. Once that is done, it is easy to check the source and destination type. This has to be done carefully so that in non-DEBUG builds, where NSCAP_FEATURE_USE_BASE is defined, we don't generate any additional code. The first step is to rename nsQueryInterface to nsQueryInterfaceISupports. In DEBUG builds, I then add a new subtype nsQueryInterface<T>, where T is the type of the object we are going to QI. This class is a thin wrapper around nsQueryInterfaceISupports that only forwards operations to the base class. The main bit of trickery here is PointedToType<T>, which is needed to get the type parameter for nsQueryInterface. This dereferences the type, gets the address of it, then does RemovePointer. This is needed because a wide variety of pointer types are passed in to do_QueryInterface, including RefPtr<>, nsCOMPtr<>, raw pointers, and OwningNonNull<>. PointedToType<RefPtr<T>> is equal to T, PointedToType<T*> is equal to T, and so on. In NSCAP_FEATURE_USE_BASE builds (opt), we only use nsQueryInterfaceISupports, but in debug builds we use nsQueryInterface<> where possible. The latter is also used for the nsCOMPtr<nsISupports> overload, because we can always QI to nsISupports, because that is sometimes used for canonicalization. Another gross bit is that Assert_NoQueryNeeded can no longer use nsCOMPtr<>, because it works by QIing T to T, which is banned by the static analysis. Instead I had to reimplement it by hand. Depends on D7527 Differential Revision: https://phabricator.services.mozilla.com/D7553 --HG-- extra : moz-landing-system : lando
2018-10-04 19:16:28 +00:00
nsresult nsQueryInterfaceISupports::operator()(const nsIID& aIID,
void** aAnswer) const {
nsresult status;
if (mRawPtr) {
status = mRawPtr->QueryInterface(aIID, aAnswer);
} else {
status = NS_ERROR_NULL_POINTER;
}
return status;
}
nsresult nsQueryInterfaceISupportsWithError::operator()(const nsIID& aIID,
void** aAnswer) const {
nsresult status;
if (mRawPtr) {
status = mRawPtr->QueryInterface(aIID, aAnswer);
} else {
status = NS_ERROR_NULL_POINTER;
}
if (mErrorPtr) {
*mErrorPtr = status;
}
return status;
}
1999-02-17 01:57:07 +00:00
void nsCOMPtr_base::assign_with_AddRef(nsISupports* aRawPtr) {
if (aRawPtr) {
NSCAP_ADDREF(this, aRawPtr);
}
assign_assuming_AddRef(aRawPtr);
}
1999-02-17 01:57:07 +00:00
Bug 1493226, part 1 - Statically prevent trivial calls to do_QueryInterface r=froydnj This patch adds a static assert to enforce that do_QueryInterface is not used to go from a class to a base class, because that can be done more efficiently via a static_cast. This is done by putting the type of the source into the nsQueryInterface type. Once that is done, it is easy to check the source and destination type. This has to be done carefully so that in non-DEBUG builds, where NSCAP_FEATURE_USE_BASE is defined, we don't generate any additional code. The first step is to rename nsQueryInterface to nsQueryInterfaceISupports. In DEBUG builds, I then add a new subtype nsQueryInterface<T>, where T is the type of the object we are going to QI. This class is a thin wrapper around nsQueryInterfaceISupports that only forwards operations to the base class. The main bit of trickery here is PointedToType<T>, which is needed to get the type parameter for nsQueryInterface. This dereferences the type, gets the address of it, then does RemovePointer. This is needed because a wide variety of pointer types are passed in to do_QueryInterface, including RefPtr<>, nsCOMPtr<>, raw pointers, and OwningNonNull<>. PointedToType<RefPtr<T>> is equal to T, PointedToType<T*> is equal to T, and so on. In NSCAP_FEATURE_USE_BASE builds (opt), we only use nsQueryInterfaceISupports, but in debug builds we use nsQueryInterface<> where possible. The latter is also used for the nsCOMPtr<nsISupports> overload, because we can always QI to nsISupports, because that is sometimes used for canonicalization. Another gross bit is that Assert_NoQueryNeeded can no longer use nsCOMPtr<>, because it works by QIing T to T, which is banned by the static analysis. Instead I had to reimplement it by hand. Depends on D7527 Differential Revision: https://phabricator.services.mozilla.com/D7553 --HG-- extra : moz-landing-system : lando
2018-10-04 19:16:28 +00:00
void nsCOMPtr_base::assign_from_qi(const nsQueryInterfaceISupports aQI,
const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aQI(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
void nsCOMPtr_base::assign_from_qi_with_error(
const nsQueryInterfaceISupportsWithError& aQI, const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aQI(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
void nsCOMPtr_base::assign_from_gs_cid(const nsGetServiceByCID aGS,
const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aGS(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
void nsCOMPtr_base::assign_from_gs_cid_with_error(
const nsGetServiceByCIDWithError& aGS, const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aGS(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
void nsCOMPtr_base::assign_from_gs_contractid(
const nsGetServiceByContractID aGS, const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aGS(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
void nsCOMPtr_base::assign_from_gs_contractid_with_error(
const nsGetServiceByContractIDWithError& aGS, const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aGS(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
void nsCOMPtr_base::assign_from_query_referent(
const nsQueryReferent& aQueryReferent, const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aQueryReferent(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
void nsCOMPtr_base::assign_from_helper(const nsCOMPtr_helper& aHelper,
const nsIID& aIID) {
void* newRawPtr;
if (NS_FAILED(aHelper(aIID, &newRawPtr))) {
newRawPtr = nullptr;
}
assign_assuming_AddRef(static_cast<nsISupports*>(newRawPtr));
}
1999-02-17 01:57:07 +00:00
void** nsCOMPtr_base::begin_assignment() {
assign_assuming_AddRef(nullptr);
return reinterpret_cast<void**>(&mRawPtr);
}