/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla 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/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or 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 the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Bandhauer * Pierre Phaneuf * IBM Corp. * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* XPConnect JavaScript interactive shell. */ #include #include "nsIXPConnect.h" #include "nsIXPCScriptable.h" #include "nsIInterfaceInfo.h" #include "nsIInterfaceInfoManager.h" #include "nsIXPCScriptable.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "jsapi.h" #include "jsprf.h" #include "nscore.h" #include "nsMemory.h" #include "nsIGenericFactory.h" #include "nsIJSRuntimeService.h" #include "nsCOMPtr.h" #include "nsIXPCSecurityManager.h" // all this crap is needed to do the interactive shell stuff #include #include #if defined(XP_WIN) || defined(XP_OS2) #include /* for isatty() */ #elif defined(XP_UNIX) || defined(XP_BEOS) #include /* for isatty() */ #elif defined(XP_MAC) #include #include #endif #include "nsIJSContextStack.h" /***************************************************************************/ #ifdef JS_THREADSAFE #define DoBeginRequest(cx) JS_BeginRequest((cx)) #define DoEndRequest(cx) JS_EndRequest((cx)) #else #define DoBeginRequest(cx) ((void)0) #define DoEndRequest(cx) ((void)0) #endif /***************************************************************************/ #define EXITCODE_RUNTIME_ERROR 3 #define EXITCODE_FILE_NOT_FOUND 4 FILE *gOutFile = NULL; FILE *gErrFile = NULL; int gExitCode = 0; JSBool gQuitting = JS_FALSE; static JSBool reportWarnings = JS_TRUE; JS_STATIC_DLL_CALLBACK(void) my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { int i, j, k, n; char *prefix = NULL, *tmp; const char *ctmp; if (!report) { fprintf(gErrFile, "%s\n", message); return; } /* Conditionally ignore reported warnings. */ if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) return; if (report->filename) prefix = JS_smprintf("%s:", report->filename); if (report->lineno) { tmp = prefix; prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno); JS_free(cx, tmp); } if (JSREPORT_IS_WARNING(report->flags)) { tmp = prefix; prefix = JS_smprintf("%s%swarning: ", tmp ? tmp : "", JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); JS_free(cx, tmp); } /* embedded newlines -- argh! */ while ((ctmp = strchr(message, '\n')) != 0) { ctmp++; if (prefix) fputs(prefix, gErrFile); fwrite(message, 1, ctmp - message, gErrFile); message = ctmp; } /* If there were no filename or lineno, the prefix might be empty */ if (prefix) fputs(prefix, gErrFile); fputs(message, gErrFile); if (!report->linebuf) { fputc('\n', gErrFile); goto out; } fprintf(gErrFile, ":\n%s%s\n%s", prefix, report->linebuf, prefix); n = report->tokenptr - report->linebuf; for (i = j = 0; i < n; i++) { if (report->linebuf[i] == '\t') { for (k = (j + 8) & ~7; j < k; j++) { fputc('.', gErrFile); } continue; } fputc('.', gErrFile); j++; } fputs("^\n", gErrFile); out: if (!JSREPORT_IS_WARNING(report->flags)) gExitCode = EXITCODE_RUNTIME_ERROR; JS_free(cx, prefix); } JS_STATIC_DLL_CALLBACK(JSBool) Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uintN i, n; JSString *str; for (i = n = 0; i < argc; i++) { str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str)); } n++; if (n) fputc('\n', gOutFile); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Dump(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; if (!argc) return JS_TRUE; str = JS_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; char *bytes = JS_GetStringBytes(str); bytes = strdup(bytes); #ifdef XP_MAC for (char *c = bytes; *c; c++) if (*c == '\r') *c = '\n'; #endif fputs(bytes, gOutFile); nsMemory::Free(bytes); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uintN i; JSString *str; const char *filename; JSScript *script; JSBool ok; jsval result; for (i = 0; i < argc; i++) { str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; argv[i] = STRING_TO_JSVAL(str); filename = JS_GetStringBytes(str); script = JS_CompileFile(cx, obj, filename); if (!script) ok = JS_FALSE; else { ok = JS_ExecuteScript(cx, obj, script, &result); JS_DestroyScript(cx, script); } if (!ok) return JS_FALSE; } return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc > 0 && JSVAL_IS_INT(argv[0])) *rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(JSVAL_TO_INT(argv[0])))); else *rval = INT_TO_JSVAL(JS_GetVersion(cx)); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__); return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { #ifdef LIVECONNECT JSJ_SimpleShutdown(); #endif gExitCode = 0; JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode); gQuitting = JS_TRUE; // exit(0); return JS_FALSE; } JS_STATIC_DLL_CALLBACK(JSBool) DumpXPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { int32 depth = 2; if (argc > 0) { if (!JS_ValueToInt32(cx, argv[0], &depth)) return JS_FALSE; } nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if(xpc) xpc->DebugDump((int16)depth); return JS_TRUE; } /* XXX needed only by GC() */ #include "jscntxt.h" #ifdef GC_MARK_DEBUG extern "C" JS_FRIEND_DATA(FILE *) js_DumpGCHeap; #endif JS_STATIC_DLL_CALLBACK(JSBool) GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSRuntime *rt; uint32 preBytes; rt = cx->runtime; preBytes = rt->gcBytes; #ifdef GC_MARK_DEBUG if (argc && JSVAL_IS_STRING(argv[0])) { char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])); FILE *file = fopen(name, "w"); if (!file) { fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno)); return JS_FALSE; } js_DumpGCHeap = file; } else { js_DumpGCHeap = stdout; } #endif JS_GC(cx); #ifdef GC_MARK_DEBUG if (js_DumpGCHeap != stdout) fclose(js_DumpGCHeap); js_DumpGCHeap = NULL; #endif fprintf(gOutFile, "before %lu, after %lu, break %08lx\n", (unsigned long)preBytes, (unsigned long)rt->gcBytes, #ifdef XP_UNIX (unsigned long)sbrk(0) #else 0 #endif ); #ifdef JS_GCMETER js_DumpGCStats(rt, stdout); #endif return JS_TRUE; } JS_STATIC_DLL_CALLBACK(JSBool) Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc > 0 && !JSVAL_IS_PRIMITIVE(argv[0])) { JS_ClearScope(cx, JSVAL_TO_OBJECT(argv[0])); } else { JS_ReportError(cx, "'clear' requires an object"); return JS_FALSE; } return JS_TRUE; } static JSFunctionSpec glob_functions[] = { {"print", Print, 0}, {"load", Load, 1}, {"quit", Quit, 0}, {"version", Version, 1}, {"build", BuildDate, 0}, {"dumpXPC", DumpXPC, 1}, {"dump", Dump, 1}, {"gc", GC, 0}, {"clear", Clear, 1}, {0} }; JSClass global_class = { "global", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; static JSBool env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { /* XXX porting may be easy, but these don't seem to supply setenv by default */ #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS JSString *idstr, *valstr; const char *name, *value; int rv; idstr = JS_ValueToString(cx, id); valstr = JS_ValueToString(cx, *vp); if (!idstr || !valstr) return JS_FALSE; name = JS_GetStringBytes(idstr); value = JS_GetStringBytes(valstr); #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX { char *waste = JS_smprintf("%s=%s", name, value); if (!waste) { JS_ReportOutOfMemory(cx); return JS_FALSE; } rv = putenv(waste); #ifdef XP_WIN /* * HPUX9 at least still has the bad old non-copying putenv. * * Per mail from , OSF1 also has a putenv * that will crash if you pass it an auto char array (so it must place * its argument directly in the char *environ[] array). */ free(waste); #endif } #else rv = setenv(name, value, 1); #endif if (rv < 0) { JS_ReportError(cx, "can't set envariable %s to %s", name, value); return JS_FALSE; } *vp = STRING_TO_JSVAL(valstr); #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */ return JS_TRUE; } static JSBool env_enumerate(JSContext *cx, JSObject *obj) { static JSBool reflected; char **evp, *name, *value; JSString *valstr; JSBool ok; if (reflected) return JS_TRUE; for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) { value = strchr(name, '='); if (!value) continue; *value++ = '\0'; valstr = JS_NewStringCopyZ(cx, value); if (!valstr) { ok = JS_FALSE; } else { ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), NULL, NULL, JSPROP_ENUMERATE); } value[-1] = '='; if (!ok) return JS_FALSE; } reflected = JS_TRUE; return JS_TRUE; } static JSBool env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSString *idstr, *valstr; const char *name, *value; if (flags & JSRESOLVE_ASSIGNING) return JS_TRUE; idstr = JS_ValueToString(cx, id); if (!idstr) return JS_FALSE; name = JS_GetStringBytes(idstr); value = getenv(name); if (value) { valstr = JS_NewStringCopyZ(cx, value); if (!valstr) return JS_FALSE; if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } *objp = obj; } return JS_TRUE; } static JSClass env_class = { "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, env_setProperty, env_enumerate, (JSResolveOp) env_resolve, JS_ConvertStub, JS_FinalizeStub }; /***************************************************************************/ typedef enum JSShellErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, #include "jsshell.msg" #undef MSG_DEF JSShellErr_Limit #undef MSGDEF } JSShellErrNum; JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = { #if JS_HAS_DFLT_MSG_STRINGS #define MSG_DEF(name, number, count, exception, format) \ { format, count } , #else #define MSG_DEF(name, number, count, exception, format) \ { NULL, count } , #endif #include "jsshell.msg" #undef MSG_DEF }; JS_STATIC_DLL_CALLBACK(const JSErrorFormatString *) my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) { if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit)) return &jsShell_ErrorFormatString[errorNumber]; else return NULL; } #ifdef EDITLINE extern "C" { extern char *readline(const char *prompt); extern void add_history(char *line); } #endif static JSBool GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) { #ifdef EDITLINE /* * Use readline only if file is stdin, because there's no way to specify * another handle. Are other filehandles interactive? */ if (file == stdin) { char *linep = readline(prompt); if (!linep) return JS_FALSE; if (*linep) add_history(linep); strcpy(bufp, linep); JS_free(cx, linep); bufp += strlen(bufp); *bufp++ = '\n'; *bufp = '\0'; } else #endif { char line[256]; fprintf(gOutFile, prompt); fflush(gOutFile); if (!fgets(line, sizeof line, file)) return JS_FALSE; strcpy(bufp, line); } return JS_TRUE; } static void ProcessFile(JSContext *cx, JSObject *obj, const char *filename, FILE *file) { JSScript *script; jsval result; int lineno, startline; JSBool ok, hitEOF; char *bufp, buffer[4096]; JSString *str; if (!isatty(fileno(file))) { /* * It's not interactive - just execute it. * * Support the UNIX #! shell hack; gobble the first line if it starts * with '#'. TODO - this isn't quite compatible with sharp variables, * as a legal js program (using sharp variables) might start with '#'. * But that would require multi-character lookahead. */ int ch = fgetc(file); if (ch == '#') { while((ch = fgetc(file)) != EOF) { if(ch == '\n' || ch == '\r') break; } } ungetc(ch, file); DoBeginRequest(cx); script = JS_CompileFileHandle(cx, obj, filename, file); if (script) { (void)JS_ExecuteScript(cx, obj, script, &result); JS_DestroyScript(cx, script); } DoEndRequest(cx); return; } /* It's an interactive filehandle; drop into read-eval-print loop. */ lineno = 1; hitEOF = JS_FALSE; do { bufp = buffer; *bufp = '\0'; /* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line. */ startline = lineno; do { if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) { hitEOF = JS_TRUE; break; } bufp += strlen(bufp); lineno++; } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); DoBeginRequest(cx); /* Clear any pending exception from previous failed compiles. */ JS_ClearPendingException(cx); script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein", startline); if (script) { JSErrorReporter older; ok = JS_ExecuteScript(cx, obj, script, &result); if (ok && result != JSVAL_VOID) { /* Suppress error reports from JS_ValueToString(). */ older = JS_SetErrorReporter(cx, NULL); str = JS_ValueToString(cx, result); JS_SetErrorReporter(cx, older); if (str) fprintf(gOutFile, "%s\n", JS_GetStringBytes(str)); else ok = JS_FALSE; } #if 0 #if JS_HAS_ERROR_EXCEPTIONS /* * Require that any time we return failure, an exception has * been set. */ JS_ASSERT(ok || JS_IsExceptionPending(cx)); /* * Also that any time an exception has been set, we've * returned failure. */ JS_ASSERT(!JS_IsExceptionPending(cx) || !ok); #endif /* JS_HAS_ERROR_EXCEPTIONS */ #endif JS_DestroyScript(cx, script); } DoEndRequest(cx); } while (!hitEOF && !gQuitting); fprintf(gOutFile, "\n"); return; } static void Process(JSContext *cx, JSObject *obj, const char *filename) { FILE *file; if (!filename || strcmp(filename, "-") == 0) { file = stdin; } else { file = fopen(filename, "r"); if (!file) { JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_CANT_OPEN, filename, strerror(errno)); gExitCode = EXITCODE_FILE_NOT_FOUND; return; } } ProcessFile(cx, obj, filename, file); } static int usage(void) { fprintf(gErrFile, "%s\n", JS_GetImplementationVersion()); fprintf(gErrFile, "usage: xpcshell [-PswW] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n"); return 2; } extern JSClass global_class; static int ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) { const char rcfilename[] = "xpcshell.js"; FILE *rcfile; int i, j, length; JSObject *argsObj; char *filename = NULL; JSBool isInteractive = JS_TRUE; rcfile = fopen(rcfilename, "r"); if (rcfile) { printf("[loading '%s'...]\n", rcfilename); ProcessFile(cx, obj, rcfilename, rcfile); } /* * Scan past all optional arguments so we can create the arguments object * before processing any -f options, which must interleave properly with * -v and -w options. This requires two passes, and without getopt, we'll * have to keep the option logic here and in the second for loop in sync. */ for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { ++i; break; } switch (argv[i][1]) { case 'v': case 'f': ++i; break; } } /* * Create arguments early and define it to root it, so it's safe from any * GC calls nested below, and so it is available to -f arguments. */ argsObj = JS_NewArrayObject(cx, 0, NULL); if (!argsObj) return 1; if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0)) { return 1; } length = argc - i; for (j = 0; j < length; j++) { JSString *str = JS_NewStringCopyZ(cx, argv[i++]); if (!str) return 1; if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), NULL, NULL, JSPROP_ENUMERATE)) { return 1; } } for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { filename = argv[i++]; isInteractive = JS_FALSE; break; } switch (argv[i][1]) { case 'v': if (++i == argc) { return usage(); } JS_SetVersion(cx, JSVersion(atoi(argv[i]))); break; case 'W': reportWarnings = JS_FALSE; break; case 'w': reportWarnings = JS_TRUE; break; case 's': JS_ToggleOptions(cx, JSOPTION_STRICT); break; case 'P': if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { JSObject *gobj; if (!JS_SealObject(cx, obj, JS_TRUE)) return JS_FALSE; gobj = JS_NewObject(cx, &global_class, NULL, NULL); if (!gobj) return JS_FALSE; if (!JS_SetPrototype(cx, gobj, obj)) return JS_FALSE; JS_SetParent(cx, gobj, NULL); JS_SetGlobalObject(cx, gobj); obj = gobj; } break; case 'f': if (++i == argc) { return usage(); } Process(cx, obj, argv[i]); /* * XXX: js -f foo.js should interpret foo.js and then * drop into interactive mode, but that breaks test * harness. Just execute foo.js for now. */ isInteractive = JS_FALSE; break; default: return usage(); } } if (filename || isInteractive) Process(cx, obj, filename); return gExitCode; } /***************************************************************************/ class FullTrustSecMan : public nsIXPCSecurityManager { public: NS_DECL_ISUPPORTS NS_DECL_NSIXPCSECURITYMANAGER FullTrustSecMan(); }; NS_IMPL_ISUPPORTS1(FullTrustSecMan, nsIXPCSecurityManager) FullTrustSecMan::FullTrustSecMan() { } NS_IMETHODIMP FullTrustSecMan::CanCreateWrapper(JSContext * aJSContext, const nsIID & aIID, nsISupports *aObj, nsIClassInfo *aClassInfo, void * *aPolicy) { return NS_OK; } NS_IMETHODIMP FullTrustSecMan::CanCreateInstance(JSContext * aJSContext, const nsCID & aCID) { return NS_OK; } NS_IMETHODIMP FullTrustSecMan::CanGetService(JSContext * aJSContext, const nsCID & aCID) { return NS_OK; } /* void CanAccess (in PRUint32 aAction, in nsIXPCNativeCallContext aCallContext, in JSContextPtr aJSContext, in JSObjectPtr aJSObject, in nsISupports aObj, in nsIClassInfo aClassInfo, in JSVal aName, inout voidPtr aPolicy); */ NS_IMETHODIMP FullTrustSecMan::CanAccess(PRUint32 aAction, nsIXPCNativeCallContext *aCallContext, JSContext * aJSContext, JSObject * aJSObject, nsISupports *aObj, nsIClassInfo *aClassInfo, jsval aName, void * *aPolicy) { return NS_OK; } /***************************************************************************/ #if defined(XP_MAC) #include #include static void initConsole(StringPtr consoleName, const char* startupMessage, int *argc, char** *argv) { SIOUXSettings.autocloseonquit = true; SIOUXSettings.asktosaveonclose = false; SIOUXSettings.userwindowtitle = consoleName; SIOUXSettings.standalone = true; SIOUXSettings.setupmenus = true; SIOUXSettings.toppixel = 42; SIOUXSettings.leftpixel = 6; SIOUXSettings.rows = 40; SIOUXSettings.columns = 100; // SIOUXSettings.initializeTB = false; // SIOUXSettings.showstatusline = true; puts(startupMessage); /* set up a buffer for stderr (otherwise it's a pig). */ setvbuf(stderr, (char *) malloc(BUFSIZ), _IOLBF, BUFSIZ); static char* mac_argv[] = { "xpcshell", NULL }; *argc = 1; *argv = mac_argv; } #endif // #define TEST_InitClassesWithNewWrappedGlobal #ifdef TEST_InitClassesWithNewWrappedGlobal // XXX hacky test code... #include "xpctest.h" class TestGlobal : public nsIXPCTestNoisy, public nsIXPCScriptable { public: NS_DECL_ISUPPORTS NS_DECL_NSIXPCTESTNOISY NS_DECL_NSIXPCSCRIPTABLE TestGlobal(){} }; NS_IMPL_ISUPPORTS2(TestGlobal, nsIXPCTestNoisy, nsIXPCScriptable) // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME TestGlobal #define XPC_MAP_QUOTED_CLASSNAME "TestGlobal" #define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY |\ nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY |\ nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP TestGlobal::Squawk() {return NS_OK;} #endif // uncomment to install the test 'this' translator // #define TEST_TranslateThis #ifdef TEST_TranslateThis #include "xpctest.h" class nsXPCFunctionThisTranslator : public nsIXPCFunctionThisTranslator { public: NS_DECL_ISUPPORTS NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR nsXPCFunctionThisTranslator(); virtual ~nsXPCFunctionThisTranslator(); /* additional members */ }; /* Implementation file */ NS_IMPL_ISUPPORTS1(nsXPCFunctionThisTranslator, nsIXPCFunctionThisTranslator) nsXPCFunctionThisTranslator::nsXPCFunctionThisTranslator() { /* member initializers and constructor code */ } nsXPCFunctionThisTranslator::~nsXPCFunctionThisTranslator() { /* destructor code */ #ifdef DEBUG_jband printf("destroying nsXPCFunctionThisTranslator\n"); #endif } /* nsISupports TranslateThis (in nsISupports aInitialThis, in nsIInterfaceInfo aInterfaceInfo, in PRUint16 aMethodIndex, out PRBool aHideFirstParamFromJS, out nsIIDPtr aIIDOfResult); */ NS_IMETHODIMP nsXPCFunctionThisTranslator::TranslateThis(nsISupports *aInitialThis, nsIInterfaceInfo *aInterfaceInfo, PRUint16 aMethodIndex, PRBool *aHideFirstParamFromJS, nsIID * *aIIDOfResult, nsISupports **_retval) { NS_IF_ADDREF(aInitialThis); *_retval = aInitialThis; *aHideFirstParamFromJS = JS_FALSE; *aIIDOfResult = nsnull; return NS_OK; } #endif int main(int argc, char **argv, char **envp) { JSRuntime *rt; JSContext *cx; JSObject *glob, *envobj; int result; nsresult rv; #if defined(XP_MAC) initConsole("\pXPConnect Shell", "Welcome to the XPConnect Shell.\n", &argc, &argv); #endif gErrFile = stderr; gOutFile = stdout; { nsCOMPtr servMan; rv = NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull); if (NS_FAILED(rv)) { printf("NS_InitXPCOM failed!\n"); return 1; } { nsCOMPtr registrar = do_QueryInterface(servMan); NS_ASSERTION(registrar, "Null nsIComponentRegistrar"); if (registrar) registrar->AutoRegister(nsnull); } nsCOMPtr rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); // get the JSRuntime from the runtime svc if (!rtsvc) { printf("failed to get nsJSRuntimeService!\n"); return 1; } if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) { printf("failed to get JSRuntime from nsJSRuntimeService!\n"); return 1; } cx = JS_NewContext(rt, 8192); if (!cx) { printf("JS_NewContext failed!\n"); return 1; } JS_SetErrorReporter(cx, my_ErrorReporter); nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); if (!xpc) { printf("failed to get nsXPConnect service!\n"); return 1; } // Since the caps security system might set a default security manager // we will be sure that the secman on this context gives full trust. // That way we can avoid getting principals from the caps security manager // just to shut it up. Also, note that even though our secman will allow // anything, we set the flags to '0' so it ought never get called anyway. nsCOMPtr secman = NS_STATIC_CAST(nsIXPCSecurityManager*, new FullTrustSecMan()); xpc->SetSecurityManagerForJSContext(cx, secman, 0); // xpc->SetCollectGarbageOnMainThreadOnly(PR_TRUE); // xpc->SetDeferReleasesUntilAfterGarbageCollection(PR_TRUE); #ifdef TEST_TranslateThis nsCOMPtr translator(new nsXPCFunctionThisTranslator); xpc->SetFunctionThisTranslator(NS_GET_IID(nsITestXPCFunctionCallback), translator, nsnull); #endif nsCOMPtr cxstack = do_GetService("@mozilla.org/js/xpc/ContextStack;1"); if (!cxstack) { printf("failed to get the nsThreadJSContextStack service!\n"); return 1; } if(NS_FAILED(cxstack->Push(cx))) { printf("failed to push the current JSContext on the nsThreadJSContextStack!\n"); return 1; } glob = JS_NewObject(cx, &global_class, NULL, NULL); if (!glob) return 1; if (!JS_InitStandardClasses(cx, glob)) return 1; if (!JS_DefineFunctions(cx, glob, glob_functions)) return 1; if (NS_FAILED(xpc->InitClasses(cx, glob))) return 1; envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0); if (!envobj || !JS_SetPrivate(cx, envobj, envp)) return 1; argc--; argv++; result = ProcessArgs(cx, glob, argv, argc); #ifdef TEST_InitClassesWithNewWrappedGlobal // quick hacky test... JSContext* foo = JS_NewContext(rt, 8192); nsCOMPtr bar(new TestGlobal()); nsCOMPtr baz; xpc->InitClassesWithNewWrappedGlobal(foo, bar, NS_GET_IID(nsIXPCTestNoisy), PR_TRUE, getter_AddRefs(baz)); bar = nsnull; baz = nsnull; JS_GC(foo); JS_DestroyContext(foo); #endif //#define TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN 1 #ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN // test of late call and release (see below) nsCOMPtr bogus; xpc->WrapJS(cx, glob, NS_GET_IID(nsIJSContextStack), (void**) getter_AddRefs(bogus)); #endif JS_ClearScope(cx, glob); JS_GC(cx); JSContext *oldcx; cxstack->Pop(&oldcx); NS_ASSERTION(oldcx == cx, "JS thread context push/pop mismatch"); cxstack = nsnull; JS_GC(cx); JS_DestroyContext(cx); xpc->SyncJSContexts(); } // this scopes the nsCOMPtrs // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM rv = NS_ShutdownXPCOM( NULL ); NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed"); #ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN // test of late call and release (see above) JSContext* bogusCX; bogus->Peek(&bogusCX); bogus = nsnull; #endif return result; }