From 0a174a879e58b96e679d02ee6461a0dbde6e9eed Mon Sep 17 00:00:00 2001 From: "jband%netscape.com" Date: Mon, 18 Oct 1999 02:53:04 +0000 Subject: [PATCH] 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 --- mozilla/js/src/xpconnect/idl/xpctest.idl | 3 + .../js/src/xpconnect/public/nsIXPConnect.h | 36 ++++++ mozilla/js/src/xpconnect/src/nsXPConnect.cpp | 36 +++--- .../xpconnect/src/xpcnativecallcontext.cpp | 112 +++++++++++++++++- mozilla/js/src/xpconnect/src/xpcprivate.h | 64 ++++++++++ .../xpconnect/src/xpcwrappednativeclass.cpp | 24 +++- mozilla/js/src/xpconnect/tests/TestXPC.cpp | 14 +++ .../tests/components/xpctest_echo.cpp | 96 +++++++++++++++ .../tests/components/xpctest_private.h | 2 + .../xpconnect/tests/xpctest_callcontext.js | 63 ++++++++++ 10 files changed, 430 insertions(+), 20 deletions(-) create mode 100644 mozilla/js/src/xpconnect/tests/xpctest_callcontext.js diff --git a/mozilla/js/src/xpconnect/idl/xpctest.idl b/mozilla/js/src/xpconnect/idl/xpctest.idl index e415424495f..d0a4351132a 100644 --- a/mozilla/js/src/xpconnect/idl/xpctest.idl +++ b/mozilla/js/src/xpconnect/idl/xpctest.idl @@ -121,6 +121,9 @@ interface nsIEcho : nsISupports { [iid_is(uuid),retval] out nsQIResult result); void DebugDumpJSStack(); + + void printArgTypes(/* optional params */); + void throwArg(/* optional param */); }; /***************************************************************************/ diff --git a/mozilla/js/src/xpconnect/public/nsIXPConnect.h b/mozilla/js/src/xpconnect/public/nsIXPConnect.h index f11ace3bd39..da16cd40dbd 100644 --- a/mozilla/js/src/xpconnect/public/nsIXPConnect.h +++ b/mozilla/js/src/xpconnect/public/nsIXPConnect.h @@ -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? }; diff --git a/mozilla/js/src/xpconnect/src/nsXPConnect.cpp b/mozilla/js/src/xpconnect/src/nsXPConnect.cpp index 105c7efe664..6c2de9d2da9 100644 --- a/mozilla/js/src/xpconnect/src/nsXPConnect.cpp +++ b/mozilla/js/src/xpconnect/src/nsXPConnect.cpp @@ -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... diff --git a/mozilla/js/src/xpconnect/src/xpcnativecallcontext.cpp b/mozilla/js/src/xpconnect/src/xpcnativecallcontext.cpp index 3701c632a1b..1862b915fc5 100644 --- a/mozilla/js/src/xpconnect/src/xpcnativecallcontext.cpp +++ b/mozilla/js/src/xpconnect/src/xpcnativecallcontext.cpp @@ -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; +} + diff --git a/mozilla/js/src/xpconnect/src/xpcprivate.h b/mozilla/js/src/xpconnect/src/xpcprivate.h index e59e1a45d36..7a74d4eb490 100644 --- a/mozilla/js/src/xpconnect/src/xpcprivate.h +++ b/mozilla/js/src/xpconnect/src/xpcprivate.h @@ -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; }; /***************************************************************************/ diff --git a/mozilla/js/src/xpconnect/src/xpcwrappednativeclass.cpp b/mozilla/js/src/xpconnect/src/xpcwrappednativeclass.cpp index 5110588780d..89caaa00f3f 100644 --- a/mozilla/js/src/xpconnect/src/xpcwrappednativeclass.cpp +++ b/mozilla/js/src/xpconnect/src/xpcwrappednativeclass.cpp @@ -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++) diff --git a/mozilla/js/src/xpconnect/tests/TestXPC.cpp b/mozilla/js/src/xpconnect/tests/TestXPC.cpp index 915a7fb0858..ca273b8cd28 100644 --- a/mozilla/js/src/xpconnect/tests/TestXPC.cpp +++ b/mozilla/js/src/xpconnect/tests/TestXPC.cpp @@ -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 diff --git a/mozilla/js/src/xpconnect/tests/components/xpctest_echo.cpp b/mozilla/js/src/xpconnect/tests/components/xpctest_echo.cpp index faecc8d081a..a496d6e38e5 100644 --- a/mozilla/js/src/xpconnect/tests/components/xpctest_echo.cpp +++ b/mozilla/js/src/xpconnect/tests/components/xpctest_echo.cpp @@ -309,6 +309,102 @@ xpctestEcho::DebugDumpJSStack() return rv; } +/***************************************************/ + +// some tests of nsIXPCNativeCallContext + +#define GET_CALL_CONTEXT \ + nsresult rv; \ + nsCOMPtr 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 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 = ""; + 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 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 diff --git a/mozilla/js/src/xpconnect/tests/components/xpctest_private.h b/mozilla/js/src/xpconnect/tests/components/xpctest_private.h index 2d339a7f6c0..846368e3416 100644 --- a/mozilla/js/src/xpconnect/tests/components/xpctest_private.h +++ b/mozilla/js/src/xpconnect/tests/components/xpctest_private.h @@ -45,9 +45,11 @@ #include "nsIComponentManager.h" #include "nsIGenericFactory.h" #include "nscore.h" +#include "nsCOMPtr.h" #include #include "xpctest.h" +#include "jsapi.h" // {ED132C20-EED1-11d2-BAA4-00805F8A5DD7} #define NS_ECHO_CID \ diff --git a/mozilla/js/src/xpconnect/tests/xpctest_callcontext.js b/mozilla/js/src/xpconnect/tests/xpctest_callcontext.js new file mode 100644 index 00000000000..a6ccb38bc13 --- /dev/null +++ b/mozilla/js/src/xpconnect/tests/xpctest_callcontext.js @@ -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); +} + +