Back to index

lightning-sunbird  0.9+nobinonly
xpcshell.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=2 sw=4 et tw=80:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *   John Bandhauer <jband@netscape.com>
00027  *   Pierre Phaneuf <pp@ludusdesign.com>
00028  *   IBM Corp.
00029  *
00030  * Alternatively, the contents of this file may be used under the terms of
00031  * either of the GNU General Public License Version 2 or later (the "GPL"),
00032  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00033  * in which case the provisions of the GPL or the LGPL are applicable instead
00034  * of those above. If you wish to allow use of your version of this file only
00035  * under the terms of either the GPL or the LGPL, and not to allow others to
00036  * use your version of this file under the terms of the MPL, indicate your
00037  * decision by deleting the provisions above and replace them with the notice
00038  * and other provisions required by the GPL or the LGPL. If you do not delete
00039  * the provisions above, a recipient may use your version of this file under
00040  * the terms of any one of the MPL, the GPL or the LGPL.
00041  *
00042  * ***** END LICENSE BLOCK ***** */
00043 
00044 /* XPConnect JavaScript interactive shell. */
00045 
00046 #include <stdio.h>
00047 #include "nsIXPConnect.h"
00048 #include "nsIXPCScriptable.h"
00049 #include "nsIInterfaceInfo.h"
00050 #include "nsIInterfaceInfoManager.h"
00051 #include "nsIXPCScriptable.h"
00052 #include "nsIServiceManager.h"
00053 #include "nsIComponentManager.h"
00054 #include "nsIComponentRegistrar.h"
00055 #include "jsapi.h"
00056 #include "jsprf.h"
00057 #include "nscore.h"
00058 #include "nsMemory.h"
00059 #include "nsIGenericFactory.h"
00060 #include "nsIJSRuntimeService.h"
00061 #include "nsCOMPtr.h"
00062 #include "nsIXPCSecurityManager.h"
00063 
00064 #ifndef XPCONNECT_STANDALONE
00065 #include "nsIScriptSecurityManager.h"
00066 #include "nsIPrincipal.h"
00067 #endif
00068 
00069 // all this crap is needed to do the interactive shell stuff
00070 #include <stdlib.h>
00071 #include <errno.h>
00072 #if defined(XP_WIN) || defined(XP_OS2)
00073 #include <io.h>     /* for isatty() */
00074 #elif defined(XP_UNIX) || defined(XP_BEOS)
00075 #include <unistd.h>     /* for isatty() */
00076 #endif
00077 
00078 #include "nsIJSContextStack.h"
00079 
00080 /***************************************************************************/
00081 
00082 #ifdef JS_THREADSAFE
00083 #define DoBeginRequest(cx) JS_BeginRequest((cx))
00084 #define DoEndRequest(cx)   JS_EndRequest((cx))
00085 #else
00086 #define DoBeginRequest(cx) ((void)0)
00087 #define DoEndRequest(cx)   ((void)0)
00088 #endif
00089 
00090 /***************************************************************************/
00091 
00092 #define EXITCODE_RUNTIME_ERROR 3
00093 #define EXITCODE_FILE_NOT_FOUND 4
00094 
00095 FILE *gOutFile = NULL;
00096 FILE *gErrFile = NULL;
00097 
00098 int gExitCode = 0;
00099 JSBool gQuitting = JS_FALSE;
00100 static JSBool reportWarnings = JS_TRUE;
00101 static JSBool compileOnly = JS_FALSE;
00102 
00103 JSPrincipals *gJSPrincipals = nsnull;
00104 
00105 JS_STATIC_DLL_CALLBACK(void)
00106 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
00107 {
00108     int i, j, k, n;
00109     char *prefix = NULL, *tmp;
00110     const char *ctmp;
00111 
00112     if (!report) {
00113         fprintf(gErrFile, "%s\n", message);
00114         return;
00115     }
00116 
00117     /* Conditionally ignore reported warnings. */
00118     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
00119         return;
00120 
00121     if (report->filename)
00122         prefix = JS_smprintf("%s:", report->filename);
00123     if (report->lineno) {
00124         tmp = prefix;
00125         prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
00126         JS_free(cx, tmp);
00127     }
00128     if (JSREPORT_IS_WARNING(report->flags)) {
00129         tmp = prefix;
00130         prefix = JS_smprintf("%s%swarning: ",
00131                              tmp ? tmp : "",
00132                              JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
00133         JS_free(cx, tmp);
00134     }
00135 
00136     /* embedded newlines -- argh! */
00137     while ((ctmp = strchr(message, '\n')) != 0) {
00138         ctmp++;
00139         if (prefix) fputs(prefix, gErrFile);
00140         fwrite(message, 1, ctmp - message, gErrFile);
00141         message = ctmp;
00142     }
00143     /* If there were no filename or lineno, the prefix might be empty */
00144     if (prefix)
00145         fputs(prefix, gErrFile);
00146     fputs(message, gErrFile);
00147 
00148     if (!report->linebuf) {
00149         fputc('\n', gErrFile);
00150         goto out;
00151     }
00152 
00153     fprintf(gErrFile, ":\n%s%s\n%s", prefix, report->linebuf, prefix);
00154     n = report->tokenptr - report->linebuf;
00155     for (i = j = 0; i < n; i++) {
00156         if (report->linebuf[i] == '\t') {
00157             for (k = (j + 8) & ~7; j < k; j++) {
00158                 fputc('.', gErrFile);
00159             }
00160             continue;
00161         }
00162         fputc('.', gErrFile);
00163         j++;
00164     }
00165     fputs("^\n", gErrFile);
00166  out:
00167     if (!JSREPORT_IS_WARNING(report->flags))
00168         gExitCode = EXITCODE_RUNTIME_ERROR;
00169     JS_free(cx, prefix);
00170 }
00171 
00172 JS_STATIC_DLL_CALLBACK(JSBool)
00173 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00174 {
00175     uintN i, n;
00176     JSString *str;
00177 
00178     for (i = n = 0; i < argc; i++) {
00179         str = JS_ValueToString(cx, argv[i]);
00180         if (!str)
00181             return JS_FALSE;
00182         fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str));
00183     }
00184     n++;
00185     if (n)
00186         fputc('\n', gOutFile);
00187     return JS_TRUE;
00188 }
00189 
00190 JS_STATIC_DLL_CALLBACK(JSBool)
00191 Dump(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00192 {
00193     JSString *str;
00194     if (!argc)
00195         return JS_TRUE;
00196     
00197     str = JS_ValueToString(cx, argv[0]);
00198     if (!str)
00199         return JS_FALSE;
00200 
00201     char *bytes = JS_GetStringBytes(str);
00202     bytes = strdup(bytes);
00203 
00204     fputs(bytes, gOutFile);
00205     free(bytes);
00206     return JS_TRUE;
00207 }
00208 
00209 JS_STATIC_DLL_CALLBACK(JSBool)
00210 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00211 {
00212     uintN i;
00213     JSString *str;
00214     const char *filename;
00215     JSScript *script;
00216     JSBool ok;
00217     jsval result;
00218     FILE *file;
00219 
00220     for (i = 0; i < argc; i++) {
00221         str = JS_ValueToString(cx, argv[i]);
00222         if (!str)
00223             return JS_FALSE;
00224         argv[i] = STRING_TO_JSVAL(str);
00225         filename = JS_GetStringBytes(str);
00226         file = fopen(filename, "r");
00227         script = JS_CompileFileHandleForPrincipals(cx, obj, filename, file,
00228                                                    gJSPrincipals);
00229         if (!script)
00230             ok = JS_FALSE;
00231         else {
00232             ok = !compileOnly
00233                  ? JS_ExecuteScript(cx, obj, script, &result)
00234                  : JS_TRUE;
00235             JS_DestroyScript(cx, script);
00236         }
00237         if (!ok)
00238             return JS_FALSE;
00239     }
00240     return JS_TRUE;
00241 }
00242 
00243 JS_STATIC_DLL_CALLBACK(JSBool)
00244 Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00245 {
00246     if (argc > 0 && JSVAL_IS_INT(argv[0]))
00247         *rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(JSVAL_TO_INT(argv[0]))));
00248     else
00249         *rval = INT_TO_JSVAL(JS_GetVersion(cx));
00250     return JS_TRUE;
00251 }
00252 
00253 JS_STATIC_DLL_CALLBACK(JSBool)
00254 BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00255 {
00256     fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
00257     return JS_TRUE;
00258 }
00259 
00260 JS_STATIC_DLL_CALLBACK(JSBool)
00261 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00262 {
00263 #ifdef LIVECONNECT
00264     JSJ_SimpleShutdown();
00265 #endif
00266 
00267     gExitCode = 0;
00268     JS_ConvertArguments(cx, argc, argv,"/ i", &gExitCode);
00269 
00270     gQuitting = JS_TRUE;
00271 //    exit(0);
00272     return JS_FALSE;
00273 }
00274 
00275 JS_STATIC_DLL_CALLBACK(JSBool)
00276 DumpXPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00277 {
00278     int32 depth = 2;
00279 
00280     if (argc > 0) {
00281         if (!JS_ValueToInt32(cx, argv[0], &depth))
00282             return JS_FALSE;
00283     }
00284 
00285     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
00286     if(xpc)
00287         xpc->DebugDump((int16)depth);
00288     return JS_TRUE;
00289 }
00290 
00291 /* XXX needed only by GC() */
00292 #include "jscntxt.h"
00293 
00294 #ifdef GC_MARK_DEBUG
00295 extern "C" JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
00296 #endif
00297 
00298 JS_STATIC_DLL_CALLBACK(JSBool)
00299 GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00300 {
00301     JSRuntime *rt;
00302     uint32 preBytes;
00303 
00304     rt = cx->runtime;
00305     preBytes = rt->gcBytes;
00306 #ifdef GC_MARK_DEBUG
00307     if (argc && JSVAL_IS_STRING(argv[0])) {
00308         char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
00309         FILE *file = fopen(name, "w");
00310         if (!file) {
00311             fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
00312             return JS_FALSE;
00313         }
00314         js_DumpGCHeap = file;
00315     } else {
00316         js_DumpGCHeap = stdout;
00317     }
00318 #endif
00319     JS_GC(cx);
00320 #ifdef GC_MARK_DEBUG
00321     if (js_DumpGCHeap != stdout)
00322         fclose(js_DumpGCHeap);
00323     js_DumpGCHeap = NULL;
00324 #endif
00325     fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
00326            (unsigned long)preBytes, (unsigned long)rt->gcBytes,
00327 #ifdef XP_UNIX
00328            (unsigned long)sbrk(0)
00329 #else
00330            0
00331 #endif
00332            );
00333 #ifdef JS_GCMETER
00334     js_DumpGCStats(rt, stdout);
00335 #endif
00336     return JS_TRUE;
00337 }
00338 
00339 JS_STATIC_DLL_CALLBACK(JSBool)
00340 Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00341 {
00342     if (argc > 0 && !JSVAL_IS_PRIMITIVE(argv[0])) {
00343         JS_ClearScope(cx, JSVAL_TO_OBJECT(argv[0]));
00344     } else {
00345         JS_ReportError(cx, "'clear' requires an object");
00346         return JS_FALSE;
00347     }    
00348     return JS_TRUE;
00349 }
00350 
00351 static JSFunctionSpec glob_functions[] = {
00352     {"print",           Print,          0},
00353     {"load",            Load,           1},
00354     {"quit",            Quit,           0},
00355     {"version",         Version,        1},
00356     {"build",           BuildDate,      0},
00357     {"dumpXPC",         DumpXPC,        1},
00358     {"dump",            Dump,           1},
00359     {"gc",              GC,             0},
00360     {"clear",           Clear,          1},
00361     {0}
00362 };
00363 
00364 JSClass global_class = {
00365     "global", 0,
00366     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
00367     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
00368 };
00369 
00370 static JSBool
00371 env_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00372 {
00373 /* XXX porting may be easy, but these don't seem to supply setenv by default */
00374 #if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
00375     JSString *idstr, *valstr;
00376     const char *name, *value;
00377     int rv;
00378 
00379     idstr = JS_ValueToString(cx, id);
00380     valstr = JS_ValueToString(cx, *vp);
00381     if (!idstr || !valstr)
00382         return JS_FALSE;
00383     name = JS_GetStringBytes(idstr);
00384     value = JS_GetStringBytes(valstr);
00385 #if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX
00386     {
00387         char *waste = JS_smprintf("%s=%s", name, value);
00388         if (!waste) {
00389             JS_ReportOutOfMemory(cx);
00390             return JS_FALSE;
00391         }
00392         rv = putenv(waste);
00393 #ifdef XP_WIN
00394         /*
00395          * HPUX9 at least still has the bad old non-copying putenv.
00396          *
00397          * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
00398          * that will crash if you pass it an auto char array (so it must place
00399          * its argument directly in the char *environ[] array).
00400          */
00401         free(waste);
00402 #endif
00403     }
00404 #else
00405     rv = setenv(name, value, 1);
00406 #endif
00407     if (rv < 0) {
00408         JS_ReportError(cx, "can't set envariable %s to %s", name, value);
00409         return JS_FALSE;
00410     }
00411     *vp = STRING_TO_JSVAL(valstr);
00412 #endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
00413     return JS_TRUE;
00414 }
00415 
00416 static JSBool
00417 env_enumerate(JSContext *cx, JSObject *obj)
00418 {
00419     static JSBool reflected;
00420     char **evp, *name, *value;
00421     JSString *valstr;
00422     JSBool ok;
00423 
00424     if (reflected)
00425         return JS_TRUE;
00426 
00427     for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
00428         value = strchr(name, '=');
00429         if (!value)
00430             continue;
00431         *value++ = '\0';
00432         valstr = JS_NewStringCopyZ(cx, value);
00433         if (!valstr) {
00434             ok = JS_FALSE;
00435         } else {
00436             ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
00437                                    NULL, NULL, JSPROP_ENUMERATE);
00438         }
00439         value[-1] = '=';
00440         if (!ok)
00441             return JS_FALSE;
00442     }
00443 
00444     reflected = JS_TRUE;
00445     return JS_TRUE;
00446 }
00447 
00448 static JSBool
00449 env_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
00450             JSObject **objp)
00451 {
00452     JSString *idstr, *valstr;
00453     const char *name, *value;
00454 
00455     if (flags & JSRESOLVE_ASSIGNING)
00456         return JS_TRUE;
00457 
00458     idstr = JS_ValueToString(cx, id);
00459     if (!idstr)
00460         return JS_FALSE;
00461     name = JS_GetStringBytes(idstr);
00462     value = getenv(name);
00463     if (value) {
00464         valstr = JS_NewStringCopyZ(cx, value);
00465         if (!valstr)
00466             return JS_FALSE;
00467         if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
00468                                NULL, NULL, JSPROP_ENUMERATE)) {
00469             return JS_FALSE;
00470         }
00471         *objp = obj;
00472     }
00473     return JS_TRUE;
00474 }
00475 
00476 static JSClass env_class = {
00477     "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
00478     JS_PropertyStub,  JS_PropertyStub,
00479     JS_PropertyStub,  env_setProperty,
00480     env_enumerate, (JSResolveOp) env_resolve,
00481     JS_ConvertStub,   JS_FinalizeStub
00482 };
00483 
00484 /***************************************************************************/
00485 
00486 typedef enum JSShellErrNum {
00487 #define MSG_DEF(name, number, count, exception, format) \
00488     name = number,
00489 #include "jsshell.msg"
00490 #undef MSG_DEF
00491     JSShellErr_Limit
00492 #undef MSGDEF
00493 } JSShellErrNum;
00494 
00495 JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {
00496 #if JS_HAS_DFLT_MSG_STRINGS
00497 #define MSG_DEF(name, number, count, exception, format) \
00498     { format, count } ,
00499 #else
00500 #define MSG_DEF(name, number, count, exception, format) \
00501     { NULL, count } ,
00502 #endif
00503 #include "jsshell.msg"
00504 #undef MSG_DEF
00505 };
00506 
00507 JS_STATIC_DLL_CALLBACK(const JSErrorFormatString *)
00508 my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
00509 {
00510     if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
00511             return &jsShell_ErrorFormatString[errorNumber];
00512         else
00513             return NULL;
00514 }
00515 
00516 #ifdef EDITLINE
00517 extern "C" {
00518 extern char     *readline(const char *prompt);
00519 extern void     add_history(char *line);
00520 }
00521 #endif
00522 
00523 static JSBool
00524 GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
00525 #ifdef EDITLINE
00526     /*
00527      * Use readline only if file is stdin, because there's no way to specify
00528      * another handle.  Are other filehandles interactive?
00529      */
00530     if (file == stdin) {
00531         char *linep = readline(prompt);
00532         if (!linep)
00533             return JS_FALSE;
00534         if (*linep)
00535             add_history(linep);
00536         strcpy(bufp, linep);
00537         JS_free(cx, linep);
00538         bufp += strlen(bufp);
00539         *bufp++ = '\n';
00540         *bufp = '\0';
00541     } else
00542 #endif
00543     {
00544         char line[256];
00545         fprintf(gOutFile, prompt);
00546         fflush(gOutFile);
00547         if (!fgets(line, sizeof line, file))
00548             return JS_FALSE;
00549         strcpy(bufp, line);
00550     }
00551     return JS_TRUE;
00552 }
00553 
00554 static void
00555 ProcessFile(JSContext *cx, JSObject *obj, const char *filename, FILE *file)
00556 {
00557     JSScript *script;
00558     jsval result;
00559     int lineno, startline;
00560     JSBool ok, hitEOF;
00561     char *bufp, buffer[4096];
00562     JSString *str;
00563 
00564     if (!isatty(fileno(file))) {
00565         /*
00566          * It's not interactive - just execute it.
00567          *
00568          * Support the UNIX #! shell hack; gobble the first line if it starts
00569          * with '#'.  TODO - this isn't quite compatible with sharp variables,
00570          * as a legal js program (using sharp variables) might start with '#'.
00571          * But that would require multi-character lookahead.
00572          */
00573         int ch = fgetc(file);
00574         if (ch == '#') {
00575             while((ch = fgetc(file)) != EOF) {
00576                 if(ch == '\n' || ch == '\r')
00577                     break;
00578             }
00579         }
00580         ungetc(ch, file);
00581         DoBeginRequest(cx);
00582 
00583         script = JS_CompileFileHandleForPrincipals(cx, obj, filename, file,
00584                                                    gJSPrincipals);
00585 
00586         if (script) {
00587             if (!compileOnly)
00588                 (void)JS_ExecuteScript(cx, obj, script, &result);
00589             JS_DestroyScript(cx, script);
00590         }
00591         DoEndRequest(cx);
00592         return;
00593     }
00594 
00595     /* It's an interactive filehandle; drop into read-eval-print loop. */
00596     lineno = 1;
00597     hitEOF = JS_FALSE;
00598     do {
00599         bufp = buffer;
00600         *bufp = '\0';
00601 
00602         /*
00603          * Accumulate lines until we get a 'compilable unit' - one that either
00604          * generates an error (before running out of source) or that compiles
00605          * cleanly.  This should be whenever we get a complete statement that
00606          * coincides with the end of a line.
00607          */
00608         startline = lineno;
00609         do {
00610             if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
00611                 hitEOF = JS_TRUE;
00612                 break;
00613             }
00614             bufp += strlen(bufp);
00615             lineno++;
00616         } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
00617         
00618         DoBeginRequest(cx);
00619         /* Clear any pending exception from previous failed compiles.  */
00620         JS_ClearPendingException(cx);
00621         script = JS_CompileScriptForPrincipals(cx, obj, gJSPrincipals, buffer,
00622                                                strlen(buffer), "typein", startline);
00623         if (script) {
00624             JSErrorReporter older;
00625 
00626             if (!compileOnly) {
00627                 ok = JS_ExecuteScript(cx, obj, script, &result);
00628                 if (ok && result != JSVAL_VOID) {
00629                     /* Suppress error reports from JS_ValueToString(). */
00630                     older = JS_SetErrorReporter(cx, NULL);
00631                     str = JS_ValueToString(cx, result);
00632                     JS_SetErrorReporter(cx, older);
00633     
00634                     if (str)
00635                         fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));
00636                     else
00637                         ok = JS_FALSE;
00638                 }
00639 #if 0
00640 #if JS_HAS_ERROR_EXCEPTIONS
00641                 /*
00642                  * Require that any time we return failure, an exception has
00643                  * been set.
00644                  */
00645                 JS_ASSERT(ok || JS_IsExceptionPending(cx));
00646     
00647                 /*
00648                  * Also that any time an exception has been set, we've
00649                  * returned failure.
00650                  */
00651                 JS_ASSERT(!JS_IsExceptionPending(cx) || !ok);
00652 #endif /* JS_HAS_ERROR_EXCEPTIONS */
00653 #endif
00654             }
00655             JS_DestroyScript(cx, script);
00656         }
00657         DoEndRequest(cx);
00658     } while (!hitEOF && !gQuitting);
00659     fprintf(gOutFile, "\n");
00660     return;
00661 }
00662 
00663 static void
00664 Process(JSContext *cx, JSObject *obj, const char *filename)
00665 {
00666     FILE *file;
00667 
00668     if (!filename || strcmp(filename, "-") == 0) {
00669         file = stdin;
00670     } else {
00671         file = fopen(filename, "r");
00672         if (!file) {
00673             JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
00674                                  JSSMSG_CANT_OPEN,
00675                                  filename, strerror(errno));
00676             gExitCode = EXITCODE_FILE_NOT_FOUND;
00677             return;
00678         }
00679     }
00680 
00681     ProcessFile(cx, obj, filename, file);
00682 }
00683 
00684 static int
00685 usage(void)
00686 {
00687     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
00688     fprintf(gErrFile, "usage: xpcshell [-PswWxC] [-v version] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
00689     return 2;
00690 }
00691 
00692 extern JSClass global_class;
00693 
00694 static int
00695 ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
00696 {
00697     const char rcfilename[] = "xpcshell.js";
00698     FILE *rcfile;
00699     int i, j, length;
00700     JSObject *argsObj;
00701     char *filename = NULL;
00702     JSBool isInteractive = JS_TRUE;
00703 
00704     rcfile = fopen(rcfilename, "r");
00705     if (rcfile) {
00706         printf("[loading '%s'...]\n", rcfilename);
00707         ProcessFile(cx, obj, rcfilename, rcfile);
00708     }
00709 
00710     /*
00711      * Scan past all optional arguments so we can create the arguments object
00712      * before processing any -f options, which must interleave properly with
00713      * -v and -w options.  This requires two passes, and without getopt, we'll
00714      * have to keep the option logic here and in the second for loop in sync.
00715      */
00716     for (i = 0; i < argc; i++) {
00717         if (argv[i][0] != '-' || argv[i][1] == '\0') {
00718             ++i;
00719             break;
00720         }
00721         switch (argv[i][1]) {
00722           case 'v':
00723           case 'f':
00724           case 'e':
00725             ++i;
00726             break;
00727           default:;
00728         }
00729     }
00730 
00731     /*
00732      * Create arguments early and define it to root it, so it's safe from any
00733      * GC calls nested below, and so it is available to -f <file> arguments.
00734      */
00735     argsObj = JS_NewArrayObject(cx, 0, NULL);
00736     if (!argsObj)
00737         return 1;
00738     if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),
00739                            NULL, NULL, 0)) {
00740         return 1;
00741     }
00742 
00743     length = argc - i;
00744     for (j = 0; j < length; j++) {
00745         JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
00746         if (!str)
00747             return 1;
00748         if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
00749                               NULL, NULL, JSPROP_ENUMERATE)) {
00750             return 1;
00751         }
00752     }
00753 
00754     for (i = 0; i < argc; i++) {
00755         if (argv[i][0] != '-' || argv[i][1] == '\0') {
00756             filename = argv[i++];
00757             isInteractive = JS_FALSE;
00758             break;
00759         }
00760         switch (argv[i][1]) {
00761         case 'v':
00762             if (++i == argc) {
00763                 return usage();
00764             }
00765             JS_SetVersion(cx, JSVersion(atoi(argv[i])));
00766             break;
00767         case 'W':
00768             reportWarnings = JS_FALSE;
00769             break;
00770         case 'w':
00771             reportWarnings = JS_TRUE;
00772             break;
00773         case 's':
00774             JS_ToggleOptions(cx, JSOPTION_STRICT);
00775             break;
00776         case 'x':
00777             JS_ToggleOptions(cx, JSOPTION_XML);
00778             break;
00779         case 'P':
00780             if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
00781                 JSObject *gobj;
00782 
00783                 if (!JS_SealObject(cx, obj, JS_TRUE))
00784                     return JS_FALSE;
00785                 gobj = JS_NewObject(cx, &global_class, NULL, NULL);
00786                 if (!gobj)
00787                     return JS_FALSE;
00788                 if (!JS_SetPrototype(cx, gobj, obj))
00789                     return JS_FALSE;
00790                 JS_SetParent(cx, gobj, NULL);
00791                 JS_SetGlobalObject(cx, gobj);
00792                 obj = gobj;
00793             }
00794             break;
00795         case 'f':
00796             if (++i == argc) {
00797                 return usage();
00798             }
00799             Process(cx, obj, argv[i]);
00800             /*
00801              * XXX: js -f foo.js should interpret foo.js and then
00802              * drop into interactive mode, but that breaks test
00803              * harness. Just execute foo.js for now.
00804              */
00805             isInteractive = JS_FALSE;
00806             break;
00807 
00808         case 'e':
00809         {
00810             jsval rval;
00811 
00812             if (++i == argc) {
00813                 return usage();
00814             }
00815 
00816             JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), 
00817                               "-e", 1, &rval);
00818 
00819             isInteractive = JS_FALSE;
00820             break;
00821         }
00822         case 'C':
00823             compileOnly = JS_TRUE;
00824             isInteractive = JS_FALSE;
00825             break;
00826 
00827         default:
00828             return usage();
00829         }
00830     }
00831 
00832     if (filename || isInteractive)
00833         Process(cx, obj, filename);
00834     return gExitCode;
00835 }
00836 
00837 /***************************************************************************/
00838 
00839 class FullTrustSecMan : public nsIXPCSecurityManager
00840 {
00841 public:
00842   NS_DECL_ISUPPORTS
00843   NS_DECL_NSIXPCSECURITYMANAGER
00844   FullTrustSecMan();
00845 };
00846 
00847 NS_IMPL_ISUPPORTS1(FullTrustSecMan, nsIXPCSecurityManager)
00848 
00849 FullTrustSecMan::FullTrustSecMan()
00850 {
00851 }
00852 
00853 NS_IMETHODIMP
00854 FullTrustSecMan::CanCreateWrapper(JSContext * aJSContext, const nsIID & aIID, nsISupports *aObj, nsIClassInfo *aClassInfo, void * *aPolicy)
00855 {
00856     return NS_OK;
00857 }
00858 
00859 NS_IMETHODIMP
00860 FullTrustSecMan::CanCreateInstance(JSContext * aJSContext, const nsCID & aCID)
00861 {
00862     return NS_OK;
00863 }
00864 
00865 NS_IMETHODIMP
00866 FullTrustSecMan::CanGetService(JSContext * aJSContext, const nsCID & aCID)
00867 {
00868     return NS_OK;
00869 }
00870 
00871 /* 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); */
00872 NS_IMETHODIMP 
00873 FullTrustSecMan::CanAccess(PRUint32 aAction, nsIXPCNativeCallContext *aCallContext, JSContext * aJSContext, JSObject * aJSObject, nsISupports *aObj, nsIClassInfo *aClassInfo, jsval aName, void * *aPolicy)
00874 {
00875     return NS_OK;
00876 }
00877 
00878 /***************************************************************************/
00879 
00880 // #define TEST_InitClassesWithNewWrappedGlobal
00881 
00882 #ifdef TEST_InitClassesWithNewWrappedGlobal
00883 // XXX hacky test code...
00884 #include "xpctest.h"
00885 
00886 class TestGlobal : public nsIXPCTestNoisy, public nsIXPCScriptable
00887 {
00888 public:
00889     NS_DECL_ISUPPORTS
00890     NS_DECL_NSIXPCTESTNOISY
00891     NS_DECL_NSIXPCSCRIPTABLE
00892 
00893     TestGlobal(){}
00894 };
00895 
00896 NS_IMPL_ISUPPORTS2(TestGlobal, nsIXPCTestNoisy, nsIXPCScriptable)
00897 
00898 // The nsIXPCScriptable map declaration that will generate stubs for us...
00899 #define XPC_MAP_CLASSNAME           TestGlobal
00900 #define XPC_MAP_QUOTED_CLASSNAME   "TestGlobal"
00901 #define XPC_MAP_FLAGS               nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY |\
00902                                     nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY |\
00903                                     nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY
00904 #include "xpc_map_end.h" /* This will #undef the above */
00905 
00906 NS_IMETHODIMP TestGlobal::Squawk() {return NS_OK;}
00907 
00908 #endif
00909 
00910 // uncomment to install the test 'this' translator
00911 // #define TEST_TranslateThis
00912 
00913 #ifdef TEST_TranslateThis
00914 
00915 #include "xpctest.h"
00916 
00917 class nsXPCFunctionThisTranslator : public nsIXPCFunctionThisTranslator
00918 {
00919 public:
00920   NS_DECL_ISUPPORTS
00921   NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
00922 
00923   nsXPCFunctionThisTranslator();
00924   virtual ~nsXPCFunctionThisTranslator();
00925   /* additional members */
00926 };
00927 
00928 /* Implementation file */
00929 NS_IMPL_ISUPPORTS1(nsXPCFunctionThisTranslator, nsIXPCFunctionThisTranslator)
00930 
00931 nsXPCFunctionThisTranslator::nsXPCFunctionThisTranslator()
00932 {
00933   /* member initializers and constructor code */
00934 }
00935 
00936 nsXPCFunctionThisTranslator::~nsXPCFunctionThisTranslator()
00937 {
00938   /* destructor code */
00939 #ifdef DEBUG_jband
00940     printf("destroying nsXPCFunctionThisTranslator\n");
00941 #endif
00942 }
00943 
00944 /* nsISupports TranslateThis (in nsISupports aInitialThis, in nsIInterfaceInfo aInterfaceInfo, in PRUint16 aMethodIndex, out PRBool aHideFirstParamFromJS, out nsIIDPtr aIIDOfResult); */
00945 NS_IMETHODIMP 
00946 nsXPCFunctionThisTranslator::TranslateThis(nsISupports *aInitialThis, 
00947                                            nsIInterfaceInfo *aInterfaceInfo, 
00948                                            PRUint16 aMethodIndex, 
00949                                            PRBool *aHideFirstParamFromJS, 
00950                                            nsIID * *aIIDOfResult, 
00951                                            nsISupports **_retval)
00952 {
00953     NS_IF_ADDREF(aInitialThis);
00954     *_retval = aInitialThis;
00955     *aHideFirstParamFromJS = JS_FALSE;
00956     *aIIDOfResult = nsnull;
00957     return NS_OK;
00958 }
00959 
00960 #endif
00961 
00962 int
00963 main(int argc, char **argv, char **envp)
00964 {
00965     JSRuntime *rt;
00966     JSContext *cx;
00967     JSObject *glob, *envobj;
00968     int result;
00969     nsresult rv;
00970 
00971     gErrFile = stderr;
00972     gOutFile = stdout;
00973     {
00974         nsCOMPtr<nsIServiceManager> servMan;
00975         rv = NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
00976         if (NS_FAILED(rv)) {
00977             printf("NS_InitXPCOM failed!\n");
00978             return 1;
00979         }
00980         {
00981             nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
00982             NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
00983             if (registrar)
00984                 registrar->AutoRegister(nsnull);
00985         }
00986 
00987         nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
00988         // get the JSRuntime from the runtime svc
00989         if (!rtsvc) {
00990             printf("failed to get nsJSRuntimeService!\n");
00991             return 1;
00992         }
00993     
00994         if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
00995             printf("failed to get JSRuntime from nsJSRuntimeService!\n");
00996             return 1;
00997         }
00998 
00999         cx = JS_NewContext(rt, 8192);
01000         if (!cx) {
01001             printf("JS_NewContext failed!\n");
01002             return 1;
01003         }
01004 
01005         JS_SetErrorReporter(cx, my_ErrorReporter);
01006 
01007         nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
01008         if (!xpc) {
01009             printf("failed to get nsXPConnect service!\n");
01010             return 1;
01011         }
01012 
01013         // Since the caps security system might set a default security manager
01014         // we will be sure that the secman on this context gives full trust.
01015         // That way we can avoid getting principals from the caps security manager
01016         // just to shut it up. Also, note that even though our secman will allow
01017         // anything, we set the flags to '0' so it ought never get called anyway.
01018         nsCOMPtr<nsIXPCSecurityManager> secman =
01019             NS_STATIC_CAST(nsIXPCSecurityManager*, new FullTrustSecMan());
01020         xpc->SetSecurityManagerForJSContext(cx, secman, 0);
01021 
01022         //    xpc->SetCollectGarbageOnMainThreadOnly(PR_TRUE);
01023         //    xpc->SetDeferReleasesUntilAfterGarbageCollection(PR_TRUE);
01024 
01025 #ifndef XPCONNECT_STANDALONE
01026         // Fetch the system principal and store it away in a global, to use for
01027         // script compilation in Load() and ProcessFile() (including interactive
01028         // eval loop)
01029         {
01030             nsCOMPtr<nsIPrincipal> princ;
01031 
01032             nsCOMPtr<nsIScriptSecurityManager> securityManager =
01033                 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
01034             if (NS_SUCCEEDED(rv) && securityManager) {
01035                 rv = securityManager->GetSystemPrincipal(getter_AddRefs(princ));
01036                 if (NS_FAILED(rv)) {
01037                     fprintf(gErrFile, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n");
01038                 } else {
01039                     // fetch the JS principals and stick in a global
01040                     rv = princ->GetJSPrincipals(cx, &gJSPrincipals);
01041                     if (NS_FAILED(rv)) {
01042                         fprintf(gErrFile, "+++ Failed to obtain JS principals from SystemPrincipal.\n");
01043                     }
01044                 }
01045             } else {
01046                 fprintf(gErrFile, "+++ Failed to get ScriptSecurityManager service, running without principals");
01047             }
01048         }
01049 #endif
01050 
01051 #ifdef TEST_TranslateThis
01052         nsCOMPtr<nsIXPCFunctionThisTranslator>
01053             translator(new nsXPCFunctionThisTranslator);
01054         xpc->SetFunctionThisTranslator(NS_GET_IID(nsITestXPCFunctionCallback), translator, nsnull);
01055 #endif
01056     
01057         nsCOMPtr<nsIJSContextStack> cxstack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
01058         if (!cxstack) {
01059             printf("failed to get the nsThreadJSContextStack service!\n");
01060             return 1;
01061         }
01062 
01063         if(NS_FAILED(cxstack->Push(cx))) {
01064             printf("failed to push the current JSContext on the nsThreadJSContextStack!\n");
01065             return 1;
01066         }
01067 
01068         nsCOMPtr<nsIXPCScriptable> backstagePass;
01069         nsresult rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass));
01070         if (NS_FAILED(rv)) {
01071             fprintf(gErrFile, "+++ Failed to get backstage pass from rtsvc: %8x\n",
01072                     rv);
01073             return 1;
01074         }
01075 
01076         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
01077         rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
01078                                                   NS_GET_IID(nsISupports),
01079                                                   nsIXPConnect::
01080                                                       FLAG_SYSTEM_GLOBAL_OBJECT,
01081                                                   getter_AddRefs(holder));
01082         if (NS_FAILED(rv))
01083             return 1;
01084         
01085         rv = holder->GetJSObject(&glob);
01086         if (NS_FAILED(rv)) {
01087             NS_ASSERTION(glob == nsnull, "bad GetJSObject?");
01088             return 1;
01089         }
01090         if (!JS_DefineFunctions(cx, glob, glob_functions))
01091             return 1;
01092 
01093         envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
01094         if (!envobj || !JS_SetPrivate(cx, envobj, envp))
01095             return 1;
01096 
01097         argc--;
01098         argv++;
01099 
01100         result = ProcessArgs(cx, glob, argv, argc);
01101 
01102 
01103 //#define TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN 1
01104 
01105 #ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN
01106         // test of late call and release (see below)
01107         nsCOMPtr<nsIJSContextStack> bogus;
01108         xpc->WrapJS(cx, glob, NS_GET_IID(nsIJSContextStack),
01109                     (void**) getter_AddRefs(bogus));
01110 #endif
01111 
01112         JS_ClearScope(cx, glob);
01113         JS_GC(cx);
01114         JSContext *oldcx;
01115         cxstack->Pop(&oldcx);
01116         NS_ASSERTION(oldcx == cx, "JS thread context push/pop mismatch");
01117         cxstack = nsnull;
01118         JS_GC(cx);
01119         JS_DestroyContext(cx);
01120         xpc->SyncJSContexts();
01121     } // this scopes the nsCOMPtrs
01122     // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
01123     rv = NS_ShutdownXPCOM( NULL );
01124     NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
01125 
01126 #ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN
01127     // test of late call and release (see above)
01128     JSContext* bogusCX;
01129     bogus->Peek(&bogusCX);
01130     bogus = nsnull;
01131 #endif
01132 
01133     return result;
01134 }