r=mccabe (I'll be adding more comments to the headers as requested)

Implement nsIXPCNativeCallContext to meet user feature
requirements. This allows simpler implementation of reflection of
native classes into JavaScript in cases where they need to
support legacy interfaces that include optional parameters and
method name overloading. This also provides a general mechanism
for native methods to discover if they were called from JS code,
exactly what JS parameters were passed, explicitly return jsvals,
and throw explicit jsvals without interference from xpconnect.
With test cases.


git-svn-id: svn://10.0.0.236/trunk@50971 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
jband%netscape.com 1999-10-18 02:53:04 +00:00
parent 6d347761bd
commit 0a174a879e
10 changed files with 430 additions and 20 deletions

View File

@ -121,6 +121,9 @@ interface nsIEcho : nsISupports {
[iid_is(uuid),retval] out nsQIResult result);
void DebugDumpJSStack();
void printArgTypes(/* optional params */);
void throwArg(/* optional param */);
};
/***************************************************************************/

View File

@ -190,6 +190,40 @@ public:
// XXX other methods?
};
/***************************************************************************/
/*
* This is a somewhat special interface. It is available from the global
* nsIXPConnect object when native methods have been called. It is only relevent
* to the currently called native method on the given JSContext/thread. Holding
* a reference past that time (or while other native methods are being called)
* will not assure access to this data.
*/
// {0FA68A60-8289-11d3-BB1A-00805F8A5DD7}
#define NS_IXPCNATIVECALLCONTEXT_IID \
{ 0xfa68a60, 0x8289, 0x11d3, \
{ 0xbb, 0x1a, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
class nsIXPCNativeCallContext : public nsISupports
{
public:
NS_DEFINE_STATIC_IID_ACCESSOR(NS_IXPCNATIVECALLCONTEXT_IID)
NS_IMETHOD GetCallee(nsISupports** calleep) = 0;
NS_IMETHOD GetCalleeMethodIndex(uint16* indexp) = 0;
NS_IMETHOD GetCalleeWrapper(nsIXPConnectWrappedNative** wrapperp) = 0;
NS_IMETHOD GetJSContext(JSContext** cxp) = 0;
NS_IMETHOD GetArgc(PRUint32* argcp) = 0;
NS_IMETHOD GetArgv(jsval** argvp) = 0;
// This may be NULL if the JS caller is ignoring the result of the call.
NS_IMETHOD GetRetValPtr(jsval** retvalp) = 0;
// Set this if JS_SetPendingException has been called. Return NS_OK or
// else this will be ignored and the native method's nsresult will be
// converted into an exception and thrown into JS as is the normal case.
NS_IMETHOD SetExceptionWasThrown(JSBool threw) = 0;
};
/***************************************************************************/
// {EFAE37B0-946D-11d2-BA58-00805F8A5DD7}
#define NS_IXPCONNECT_IID \
@ -267,6 +301,8 @@ public:
NS_IMETHOD DebugDumpJSStack() = 0;
NS_IMETHOD GetCurrentNativeCallContext(nsIXPCNativeCallContext** aCC) = 0;
// XXX other methods?
};

View File

@ -606,23 +606,10 @@ nsXPConnect::GetSecurityManagerForJSContext(JSContext* aJSContext,
NS_IMETHODIMP
nsXPConnect::GetCurrentJSStack(nsIJSStackFrameLocation** aStack)
{
if(!aStack)
{
NS_ASSERTION(0,"called GetCurrentJSStack with null pointer");
return NS_ERROR_NULL_POINTER;
}
nsresult rv;
NS_WITH_SERVICE(nsIJSContextStack, cxstack, "nsThreadJSContextStack", &rv);
if(NS_FAILED(rv))
{
NS_ASSERTION(0,"could not get nsThreadJSContextStack service");
return NS_ERROR_FAILURE;
}
NS_ENSURE_ARG_POINTER(aStack);
JSContext* cx;
if(NS_FAILED(cxstack->Peek(&cx)) || !cx)
if(NS_FAILED(mContextStack->Peek(&cx)) || !cx)
{
// no current context available
*aStack = nsnull;
@ -682,6 +669,25 @@ nsXPConnect::SetPendingException(nsIXPCException* aException)
return NS_OK;
}
NS_IMETHODIMP
nsXPConnect::GetCurrentNativeCallContext(nsIXPCNativeCallContext** aCC)
{
NS_ENSURE_ARG_POINTER(aCC);
JSContext* cx;
XPCContext* xpcc;
if(NS_FAILED(mContextStack->Peek(&cx)) || !cx ||
!(xpcc = mContextMap->Find(cx)))
{
// no current context available
*aCC = nsnull;
return NS_ERROR_FAILURE;
}
// these things are not refcounted
*aCC = xpcc->GetNativeCallContext();
return NS_OK;
}
/***************************************************************************/
// has to go somewhere...

View File

@ -32,6 +32,116 @@
* file under either the NPL or the GPL.
*/
/* placeholder file while makefiles and Mac project is stabilized. */
/* Call Context class for calls into native code. */
#include "xpcprivate.h"
nsXPCNativeCallContext::nsXPCNativeCallContext()
: mData(nsnull)
{
NS_INIT_ISUPPORTS();
}
nsXPCNativeCallContext::~nsXPCNativeCallContext()
{
// empty...
}
NS_IMPL_QUERY_INTERFACE1(nsXPCNativeCallContext, nsIXPCNativeCallContext)
// This is not a refcounted object. We keep one alive per XPCContext
NS_IMETHODIMP_(nsrefcnt)
nsXPCNativeCallContext::AddRef(void)
{
return 1;
}
NS_IMETHODIMP_(nsrefcnt)
nsXPCNativeCallContext::Release(void)
{
return 1;
}
NS_IMETHODIMP
nsXPCNativeCallContext::GetCallee(nsISupports** calleep)
{
NS_ENSURE_ARG_POINTER(calleep);
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
NS_IF_ADDREF(mData->callee);
*calleep = mData->callee;
return NS_OK;
}
NS_IMETHODIMP
nsXPCNativeCallContext::GetCalleeMethodIndex(uint16* indexp)
{
NS_ENSURE_ARG_POINTER(indexp);
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
*indexp = mData->index;
return NS_OK;
}
NS_IMETHODIMP
nsXPCNativeCallContext::GetCalleeWrapper(nsIXPConnectWrappedNative** wrapperp)
{
NS_ENSURE_ARG_POINTER(wrapperp);
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
NS_IF_ADDREF(mData->wrapper);
*wrapperp = mData->wrapper;
return NS_OK;
}
NS_IMETHODIMP
nsXPCNativeCallContext::GetJSContext(JSContext** cxp)
{
NS_ENSURE_ARG_POINTER(cxp);
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
*cxp = mData->cx;
return NS_OK;
}
NS_IMETHODIMP
nsXPCNativeCallContext::GetArgc(PRUint32* argcp)
{
NS_ENSURE_ARG_POINTER(argcp);
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
*argcp = mData->argc;
return NS_OK;
}
NS_IMETHODIMP
nsXPCNativeCallContext::GetArgv(jsval** argvp)
{
NS_ENSURE_ARG_POINTER(argvp);
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
*argvp = mData->argv;
return NS_OK;
}
NS_IMETHODIMP
nsXPCNativeCallContext::GetRetValPtr(jsval** retvalp)
{
NS_ENSURE_ARG_POINTER(retvalp);
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
*retvalp = mData->retvalp;
return NS_OK;
}
NS_IMETHODIMP
nsXPCNativeCallContext::SetExceptionWasThrown(JSBool threw)
{
if(NS_WARN_IF_FALSE(mData,"no active native call"))
return NS_ERROR_FAILURE;
mData->threw = threw;
return NS_OK;
}

View File

@ -158,6 +158,8 @@ class nsXPConnect : public nsIXPConnect
/* pass nsnull to clear pending exception */
NS_IMETHOD SetPendingException(nsIXPCException* aException);
NS_IMETHOD GetCurrentNativeCallContext(nsIXPCNativeCallContext** aCC);
// non-interface implementation
public:
static nsXPConnect* GetXPConnect();
@ -188,6 +190,64 @@ private:
nsIJSContextStack* mContextStack;
};
/***************************************************************************/
struct NativeCallContextData
{
// no ctor or dtor so we have random state at creation.
void init(nsISupports* Acallee,
uint16 Aindex,
nsIXPConnectWrappedNative* Awrapper,
JSContext* Acx,
PRUint32 Aargc,
jsval* Aargv,
jsval* Aretvalp)
{
callee = Acallee;
index = Aindex;
wrapper = Awrapper;
cx = Acx;
argc = Aargc;
argv = Aargv;
retvalp = Aretvalp;
threw = JS_FALSE;
}
nsISupports* callee;
nsIInterfaceInfo* info;
uint16 index;
nsIXPConnectWrappedNative* wrapper;
JSContext* cx;
PRUint32 argc;
jsval* argv;
jsval* retvalp;
JSBool threw;
};
class nsXPCNativeCallContext : public nsIXPCNativeCallContext
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD GetCallee(nsISupports** calleep);
NS_IMETHOD GetCalleeMethodIndex(uint16* indexp);
NS_IMETHOD GetCalleeWrapper(nsIXPConnectWrappedNative** wrapperp);
NS_IMETHOD GetJSContext(JSContext** cxp);
NS_IMETHOD GetArgc(PRUint32* argcp);
NS_IMETHOD GetArgv(jsval** argvp);
NS_IMETHOD GetRetValPtr(jsval** retvalp);
NS_IMETHOD SetExceptionWasThrown(JSBool threw);
nsXPCNativeCallContext();
virtual ~nsXPCNativeCallContext();
NativeCallContextData* SetData(NativeCallContextData* aData)
{NativeCallContextData* temp = mData; mData = aData; return temp;}
private:
NativeCallContextData* mData;
};
/***************************************************************************/
// XPCContext is mostly a dumb class to hold JSContext specific data and
// maps that let us find wrappers created for the given JSContext.
@ -266,6 +326,9 @@ public:
void SetSecurityManagerFlags(PRUint16 f)
{mSecurityManagerFlags = f;}
nsXPCNativeCallContext* GetNativeCallContext()
{return &mNativeCallContext;}
JSBool Init(JSObject* aGlobalObj = nsnull);
void DebugDump(int depth);
@ -293,6 +356,7 @@ private:
nsIXPCSecurityManager* mSecurityManager;
PRUint16 mSecurityManagerFlags;
nsIXPCException* mException;
nsXPCNativeCallContext mNativeCallContext;
};
/***************************************************************************/

View File

@ -429,6 +429,10 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
XPCContext* xpcc = nsXPConnect::GetContext(cx);
nsIXPCSecurityManager* securityManager;
JSBool foundDependentParam;
nsISupports* callee = wrapper->GetNative();
NativeCallContextData ccdata;
NativeCallContextData* oldccdata;
nsXPCNativeCallContext* cc;
#ifdef DEBUG_stats_jband
static int count = 0;
@ -459,7 +463,7 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
(xpcc->GetSecurityManagerFlags() &
nsIXPCSecurityManager::HOOK_CALL_METHOD) &&
NS_OK !=
securityManager->CanCallMethod(cx, mIID, wrapper->GetNative(),
securityManager->CanCallMethod(cx, mIID, callee,
mInfo, vtblIndex, desc->id))
{
// the security manager vetoed. It should have set an exception.
@ -474,7 +478,7 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
(xpcc->GetSecurityManagerFlags() &
nsIXPCSecurityManager::HOOK_GET_PROPERTY) &&
NS_OK !=
securityManager->CanGetProperty(cx, mIID, wrapper->GetNative(),
securityManager->CanGetProperty(cx, mIID, callee,
mInfo, vtblIndex, desc->id))
{
// the security manager vetoed. It should have set an exception.
@ -492,7 +496,7 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
(xpcc->GetSecurityManagerFlags() &
nsIXPCSecurityManager::HOOK_SET_PROPERTY) &&
NS_OK !=
securityManager->CanSetProperty(cx, mIID, wrapper->GetNative(),
securityManager->CanSetProperty(cx, mIID, callee,
mInfo, vtblIndex, desc->id))
{
// the security manager vetoed. It should have set an exception.
@ -777,15 +781,27 @@ nsXPCWrappedNativeClass::CallWrappedMethod(JSContext* cx,
}
}
// setup the call context in case the callee wants to see it
cc = xpcc->GetNativeCallContext(); // can't fail
ccdata.init(callee, vtblIndex, wrapper, cx, argc, argv, vp);
oldccdata = cc->SetData(&ccdata);
// do the invoke
invokeResult = XPTC_InvokeByIndex(wrapper->GetNative(), vtblIndex,
invokeResult = XPTC_InvokeByIndex(callee, vtblIndex,
paramCount, dispatchParams);
xpcc->SetLastResult(invokeResult);
cc->SetData(oldccdata);
if(NS_FAILED(invokeResult))
{
ThrowBadResultException(cx, desc, invokeResult);
goto done;
}
else if(ccdata.threw)
{
// the native callee claims to have already set a JSException
goto done;
}
// now we iterate through the native params to gather and convert results
for(i = 0; i < paramCount; i++)

View File

@ -482,6 +482,20 @@ MyEcho::DebugDumpJSStack()
return rv;
}
/* void printArgTypes (); */
NS_IMETHODIMP
MyEcho::PrintArgTypes(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void throwArg (); */
NS_IMETHODIMP
MyEcho::ThrowArg(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/***************************************************************************/
// security manager test class

View File

@ -309,6 +309,102 @@ xpctestEcho::DebugDumpJSStack()
return rv;
}
/***************************************************/
// some tests of nsIXPCNativeCallContext
#define GET_CALL_CONTEXT \
nsresult rv; \
nsCOMPtr<nsIXPCNativeCallContext> cc; \
NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); \
if(NS_SUCCEEDED(rv)) \
rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc)) /* no ';' */
/* void printArgTypes (); */
NS_IMETHODIMP
xpctestEcho::PrintArgTypes(void)
{
GET_CALL_CONTEXT;
if(NS_FAILED(rv) || !cc)
return NS_ERROR_FAILURE;
nsCOMPtr<nsISupports> callee;
if(NS_FAILED(cc->GetCallee(getter_AddRefs(callee))) || callee != this)
return NS_ERROR_FAILURE;
PRUint32 argc;
if(NS_SUCCEEDED(cc->GetArgc(&argc)))
printf("argc = %d ", (int)argc);
else
return NS_ERROR_FAILURE;
jsval* argv;
if(NS_FAILED(cc->GetArgv(&argv)))
return NS_ERROR_FAILURE;
printf("argv types = [", (int)argc);
for(PRUint32 i = 0; i < argc; i++)
{
const char* type = "<unknown>";
if(JSVAL_IS_OBJECT(argv[i]))
{
if(JSVAL_IS_NULL(argv[i]))
type = "null";
else
type = "object";
}
else if (JSVAL_IS_BOOLEAN(argv[i]))
type = "boolean";
else if (JSVAL_IS_STRING(argv[i]))
type = "string";
else if (JSVAL_IS_DOUBLE(argv[i]))
type = "double";
else if (JSVAL_IS_INT(argv[i]))
type = "int";
else if (JSVAL_IS_VOID(argv[i]))
type = "void";
printf(type);
if(i < argc-1)
printf(", ");
}
printf("]\n");
return NS_OK;
}
/* void throwArg (); */
NS_IMETHODIMP
xpctestEcho::ThrowArg(void)
{
GET_CALL_CONTEXT;
if(NS_FAILED(rv) || !cc)
return NS_ERROR_FAILURE;
nsCOMPtr<nsISupports> callee;
if(NS_FAILED(cc->GetCallee(getter_AddRefs(callee))) || callee != this)
return NS_ERROR_FAILURE;
PRUint32 argc;
if(NS_FAILED(cc->GetArgc(&argc)) || !argc)
return NS_OK;
jsval* argv;
JSContext* cx;
if(NS_FAILED(cc->GetArgv(&argv)) ||
NS_FAILED(cc->GetJSContext(&cx)))
return NS_ERROR_FAILURE;
JS_SetPendingException(cx, argv[0]);
cc->SetExceptionWasThrown(JS_TRUE);
return NS_OK;
}
/***************************************************/
/***************************************************************************/
// static

View File

@ -45,9 +45,11 @@
#include "nsIComponentManager.h"
#include "nsIGenericFactory.h"
#include "nscore.h"
#include "nsCOMPtr.h"
#include <stdio.h>
#include "xpctest.h"
#include "jsapi.h"
// {ED132C20-EED1-11d2-BAA4-00805F8A5DD7}
#define NS_ECHO_CID \

View File

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
/* more xpconnect testing using the nsIEcho interface. */
function nsNativeEcho()
{
var obj = Components.classes.nsEcho.createInstance();
obj = obj.QueryInterface(Components.interfaces.nsIEcho);
return obj;
}
var e = nsNativeEcho();
e.printArgTypes();
e.printArgTypes(1);
e.printArgTypes(1,2);
e.printArgTypes(null, new Object(), 1.1, undefined);
try {
e.throwArg(1);
} catch(e) {
print(e);
}
try {
e.throwArg("this is a string to throw");
} catch(e) {
print(e);
}