https://github.com/mozilla/gecko-dev
Tip revision: b4eac7e9c15ee516a96303642dcd9e827037c88f authored by seabld on 20 October 2012, 21:10:21 UTC
Added tag SEAMONKEY_2_14b1_RELEASE for changeset FIREFOX_17_0b2_BUILD1. CLOSED TREE a=release
Added tag SEAMONKEY_2_14b1_RELEASE for changeset FIREFOX_17_0b2_BUILD1. CLOSED TREE a=release
Tip revision: b4eac7e
XPCCallContext.cpp
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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/. */
/* Call context. */
#include "mozilla/Util.h"
#include "xpcprivate.h"
using namespace mozilla;
XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage,
JSContext* cx /* = nullptr */,
JSObject* obj /* = nullptr */,
JSObject* funobj /* = nullptr */,
jsid name /* = JSID_VOID */,
unsigned argc /* = NO_ARGS */,
jsval *argv /* = nullptr */,
jsval *rval /* = nullptr */)
: mState(INIT_FAILED),
mXPC(nsXPConnect::GetXPConnect()),
mXPCContext(nullptr),
mJSContext(cx),
mContextPopRequired(false),
mDestroyJSContextInDestructor(false),
mCallerLanguage(callerLanguage)
{
Init(callerLanguage, callerLanguage == NATIVE_CALLER, obj, funobj,
INIT_SHOULD_LOOKUP_WRAPPER, name, argc, argv, rval);
}
XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage,
JSContext* cx,
JSBool callBeginRequest,
JSObject* obj,
JSObject* flattenedJSObject,
XPCWrappedNative* wrapper,
XPCWrappedNativeTearOff* tearOff)
: mState(INIT_FAILED),
mXPC(nsXPConnect::GetXPConnect()),
mXPCContext(nullptr),
mJSContext(cx),
mContextPopRequired(false),
mDestroyJSContextInDestructor(false),
mCallerLanguage(callerLanguage),
mFlattenedJSObject(flattenedJSObject),
mWrapper(wrapper),
mTearOff(tearOff)
{
Init(callerLanguage, callBeginRequest, obj, nullptr,
WRAPPER_PASSED_TO_CONSTRUCTOR, JSID_VOID, NO_ARGS,
nullptr, nullptr);
}
void
XPCCallContext::Init(XPCContext::LangType callerLanguage,
JSBool callBeginRequest,
JSObject* obj,
JSObject* funobj,
WrapperInitOptions wrapperInitOptions,
jsid name,
unsigned argc,
jsval *argv,
jsval *rval)
{
if (!mXPC)
return;
XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
if (!stack) {
// If we don't have a stack we're probably in shutdown.
mJSContext = nullptr;
return;
}
JSContext *topJSContext = stack->Peek();
if (!mJSContext) {
// This is slightly questionable. If called without an explicit
// JSContext (generally a call to a wrappedJS) we will use the JSContext
// on the top of the JSContext stack - if there is one - *before*
// falling back on the safe JSContext.
// This is good AND bad because it makes calls from JS -> native -> JS
// have JS stack 'continuity' for purposes of stack traces etc.
// Note: this *is* what the pre-XPCCallContext xpconnect did too.
if (topJSContext) {
mJSContext = topJSContext;
} else {
mJSContext = stack->GetSafeJSContext();
if (!mJSContext)
return;
}
}
if (topJSContext != mJSContext) {
if (!stack->Push(mJSContext)) {
NS_ERROR("bad!");
return;
}
mContextPopRequired = true;
}
// Get into the request as early as we can to avoid problems with scanning
// callcontexts on other threads from within the gc callbacks.
NS_ASSERTION(!callBeginRequest || mCallerLanguage == NATIVE_CALLER,
"Don't call JS_BeginRequest unless the caller is native.");
if (callBeginRequest)
JS_BeginRequest(mJSContext);
mXPCContext = XPCContext::GetXPCContext(mJSContext);
mPrevCallerLanguage = mXPCContext->SetCallingLangType(mCallerLanguage);
// hook into call context chain.
mPrevCallContext = XPCJSRuntime::Get()->SetCallContext(this);
// We only need to addref xpconnect once so only do it if this is the first
// context in the chain.
if (!mPrevCallContext)
NS_ADDREF(mXPC);
mState = HAVE_CONTEXT;
if (!obj)
return;
mScopeForNewJSObjects = obj;
mState = HAVE_SCOPE;
mMethodIndex = 0xDEAD;
mState = HAVE_OBJECT;
mTearOff = nullptr;
if (wrapperInitOptions == INIT_SHOULD_LOOKUP_WRAPPER) {
mWrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(mJSContext, obj,
funobj,
&mFlattenedJSObject,
&mTearOff);
if (mWrapper) {
mFlattenedJSObject = mWrapper->GetFlatJSObject();
if (mTearOff)
mScriptableInfo = nullptr;
else
mScriptableInfo = mWrapper->GetScriptableInfo();
} else {
NS_ABORT_IF_FALSE(!mFlattenedJSObject || IS_SLIM_WRAPPER(mFlattenedJSObject),
"should have a slim wrapper");
}
}
if (!JSID_IS_VOID(name))
SetName(name);
if (argc != NO_ARGS)
SetArgsAndResultPtr(argc, argv, rval);
CHECK_STATE(HAVE_OBJECT);
}
void
XPCCallContext::SetName(jsid name)
{
CHECK_STATE(HAVE_OBJECT);
mName = name;
if (mTearOff) {
mSet = nullptr;
mInterface = mTearOff->GetInterface();
mMember = mInterface->FindMember(name);
mStaticMemberIsLocal = true;
if (mMember && !mMember->IsConstant())
mMethodIndex = mMember->GetIndex();
} else {
mSet = mWrapper ? mWrapper->GetSet() : nullptr;
if (mSet &&
mSet->FindMember(name, &mMember, &mInterface,
mWrapper->HasProto() ?
mWrapper->GetProto()->GetSet() :
nullptr,
&mStaticMemberIsLocal)) {
if (mMember && !mMember->IsConstant())
mMethodIndex = mMember->GetIndex();
} else {
mMember = nullptr;
mInterface = nullptr;
mStaticMemberIsLocal = false;
}
}
mState = HAVE_NAME;
}
void
XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
JSBool isSetter)
{
CHECK_STATE(HAVE_CONTEXT);
// We are going straight to the method info and need not do a lookup
// by id.
// don't be tricked if method is called with wrong 'this'
if (mTearOff && mTearOff->GetInterface() != iface)
mTearOff = nullptr;
mSet = nullptr;
mInterface = iface;
mMember = member;
mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0);
mName = mMember->GetName();
if (mState < HAVE_NAME)
mState = HAVE_NAME;
}
void
XPCCallContext::SetArgsAndResultPtr(unsigned argc,
jsval *argv,
jsval *rval)
{
CHECK_STATE(HAVE_OBJECT);
if (mState < HAVE_NAME) {
mSet = nullptr;
mInterface = nullptr;
mMember = nullptr;
mStaticMemberIsLocal = false;
}
mArgc = argc;
mArgv = argv;
mRetVal = rval;
mState = HAVE_ARGS;
}
nsresult
XPCCallContext::CanCallNow()
{
nsresult rv;
if (!HasInterfaceAndMember())
return NS_ERROR_UNEXPECTED;
if (mState < HAVE_ARGS)
return NS_ERROR_UNEXPECTED;
if (!mTearOff) {
mTearOff = mWrapper->FindTearOff(*this, mInterface, false, &rv);
if (!mTearOff || mTearOff->GetInterface() != mInterface) {
mTearOff = nullptr;
return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED;
}
}
// Refresh in case FindTearOff extended the set
mSet = mWrapper->GetSet();
mState = READY_TO_CALL;
return NS_OK;
}
void
XPCCallContext::SystemIsBeingShutDown()
{
// XXX This is pretty questionable since the per thread cleanup stuff
// can be making this call on one thread for call contexts on another
// thread.
NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext");
mXPCContext = nullptr;
mState = SYSTEM_SHUTDOWN;
if (mPrevCallContext)
mPrevCallContext->SystemIsBeingShutDown();
}
XPCCallContext::~XPCCallContext()
{
// do cleanup...
bool shouldReleaseXPC = false;
if (mXPCContext) {
mXPCContext->SetCallingLangType(mPrevCallerLanguage);
DebugOnly<XPCCallContext*> old = XPCJSRuntime::Get()->SetCallContext(mPrevCallContext);
NS_ASSERTION(old == this, "bad pop from per thread data");
shouldReleaseXPC = mPrevCallContext == nullptr;
}
// NB: Needs to happen before the context stack pop.
if (mJSContext && mCallerLanguage == NATIVE_CALLER)
JS_EndRequest(mJSContext);
if (mContextPopRequired) {
XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
NS_ASSERTION(stack, "bad!");
if (stack) {
DebugOnly<JSContext*> poppedCX = stack->Pop();
NS_ASSERTION(poppedCX == mJSContext, "bad pop");
}
}
if (mJSContext) {
if (mDestroyJSContextInDestructor) {
#ifdef DEBUG_xpc_hacker
printf("!xpc - doing deferred destruction of JSContext @ %p\n",
mJSContext);
#endif
NS_ASSERTION(!XPCJSRuntime::Get()->GetJSContextStack()->
DEBUG_StackHasJSContext(mJSContext),
"JSContext still in threadjscontextstack!");
JS_DestroyContext(mJSContext);
}
}
#ifdef DEBUG
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
NS_ASSERTION(!mScratchStrings[i].mInUse, "Uh, string wrapper still in use!");
}
#endif
if (shouldReleaseXPC && mXPC)
NS_RELEASE(mXPC);
}
XPCReadableJSStringWrapper *
XPCCallContext::NewStringWrapper(const PRUnichar *str, uint32_t len)
{
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
StringWrapperEntry& ent = mScratchStrings[i];
if (!ent.mInUse) {
ent.mInUse = true;
// Construct the string using placement new.
return new (ent.mString.addr()) XPCReadableJSStringWrapper(str, len);
}
}
// All our internal string wrappers are used, allocate a new string.
return new XPCReadableJSStringWrapper(str, len);
}
void
XPCCallContext::DeleteString(nsAString *string)
{
for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
StringWrapperEntry& ent = mScratchStrings[i];
if (string == ent.mString.addr()) {
// One of our internal strings is no longer in use, mark
// it as such and destroy the string.
ent.mInUse = false;
ent.mString.addr()->~XPCReadableJSStringWrapper();
return;
}
}
// We're done with a string that's not one of our internal
// strings, delete it.
delete string;
}
/* readonly attribute nsISupports Callee; */
NS_IMETHODIMP
XPCCallContext::GetCallee(nsISupports * *aCallee)
{
nsISupports* temp = mWrapper ? mWrapper->GetIdentityObject() : nullptr;
NS_IF_ADDREF(temp);
*aCallee = temp;
return NS_OK;
}
/* readonly attribute uint16_t CalleeMethodIndex; */
NS_IMETHODIMP
XPCCallContext::GetCalleeMethodIndex(uint16_t *aCalleeMethodIndex)
{
*aCalleeMethodIndex = mMethodIndex;
return NS_OK;
}
/* readonly attribute nsIXPConnectWrappedNative CalleeWrapper; */
NS_IMETHODIMP
XPCCallContext::GetCalleeWrapper(nsIXPConnectWrappedNative * *aCalleeWrapper)
{
nsIXPConnectWrappedNative* temp = mWrapper;
NS_IF_ADDREF(temp);
*aCalleeWrapper = temp;
return NS_OK;
}
/* readonly attribute XPCNativeInterface CalleeInterface; */
NS_IMETHODIMP
XPCCallContext::GetCalleeInterface(nsIInterfaceInfo * *aCalleeInterface)
{
nsIInterfaceInfo* temp = mInterface->GetInterfaceInfo();
NS_IF_ADDREF(temp);
*aCalleeInterface = temp;
return NS_OK;
}
/* readonly attribute nsIClassInfo CalleeClassInfo; */
NS_IMETHODIMP
XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo)
{
nsIClassInfo* temp = mWrapper ? mWrapper->GetClassInfo() : nullptr;
NS_IF_ADDREF(temp);
*aCalleeClassInfo = temp;
return NS_OK;
}
/* readonly attribute JSContextPtr JSContext; */
NS_IMETHODIMP
XPCCallContext::GetJSContext(JSContext * *aJSContext)
{
JS_AbortIfWrongThread(JS_GetRuntime(mJSContext));
*aJSContext = mJSContext;
return NS_OK;
}
/* readonly attribute uint32_t Argc; */
NS_IMETHODIMP
XPCCallContext::GetArgc(uint32_t *aArgc)
{
*aArgc = (uint32_t) mArgc;
return NS_OK;
}
/* readonly attribute JSValPtr ArgvPtr; */
NS_IMETHODIMP
XPCCallContext::GetArgvPtr(jsval * *aArgvPtr)
{
*aArgvPtr = mArgv;
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetPreviousCallContext(nsAXPCNativeCallContext **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = GetPrevCallContext();
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetLanguage(uint16_t *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = GetCallerLanguage();
return NS_OK;
}
#ifdef DEBUG
// static
void
XPCLazyCallContext::AssertContextIsTopOfStack(JSContext* cx)
{
XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack();
JSContext *topJSContext = stack->Peek();
NS_ASSERTION(cx == topJSContext, "wrong context on XPCJSContextStack!");
}
#endif