home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / js.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  26.7 KB  |  1,092 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * JS shell.
  21.  */
  22. #include <errno.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include "prtypes.h"
  27. #ifndef NSPR20
  28. #include "prarena.h"
  29. #else
  30. #include "plarena.h"
  31. #endif
  32. #include "prlog.h"
  33. #include "prprf.h"
  34. #include "jsapi.h"
  35. #include "jsatom.h"
  36. #include "jscntxt.h"
  37. #include "jsdbgapi.h"
  38. #include "jsemit.h"
  39. #include "jsfun.h"
  40. #include "jsgc.h"
  41. #include "jslock.h"
  42. #include "jsobj.h"
  43. #include "jsparse.h"
  44. #include "jsscan.h"
  45. #include "jsscope.h"
  46. #include "jsscript.h"
  47.  
  48. #ifdef XP_UNIX
  49. #include <errno.h>
  50. #include <unistd.h>
  51. #include <sys/types.h>
  52. #include <sys/wait.h>
  53. #endif
  54.  
  55. #ifdef XP_MAC
  56. #define isatty(f) 1
  57. #endif
  58.  
  59. #ifndef JSFILE
  60. # error "JSFILE must be defined for this module to work."
  61. #endif
  62.  
  63. static void
  64. Process(JSContext *cx, JSObject *obj, char *filename)
  65. {
  66.     JSScript script;        /* XXX we know all about this struct */
  67.     JSTokenStream *ts;
  68.     JSCodeGenerator cg;
  69.     jsval result;
  70.     JSString *str;
  71.  
  72.     memset(&script, 0, sizeof script);
  73.     script.filename = filename;
  74.     if (filename && strcmp(filename, "-") != 0) {
  75.     ts = js_NewFileTokenStream(cx, filename);
  76.     } else {
  77.     ts = js_NewBufferTokenStream(cx, NULL, 0);
  78.     if (ts) ts->file = stdin;
  79.     }
  80.     if (!ts) goto out;
  81.     if (isatty(fileno(ts->file)))
  82.     ts->flags |= (TSF_INTERACTIVE | TSF_COMMAND);
  83.  
  84.     js_InitCodeGenerator(cx, &cg, &cx->codePool);
  85.     script.notes = NULL;
  86.     while (!(ts->flags & (TSF_ERROR | TSF_EOF))) {
  87.     script.lineno = ts->lineno;
  88.     if (ts->flags & TSF_INTERACTIVE)
  89.         printf("js> ");
  90.  
  91.     CG_RESET(&cg);
  92.     if (!js_Parse(cx, obj, ts, &cg) || CG_OFFSET(&cg) == 0)
  93.         continue;
  94.  
  95.     script.code = cg.base;
  96.     script.length = CG_OFFSET(&cg);
  97.     script.depth = cg.maxStackDepth;
  98.     if (js_InitAtomMap(cx, &script.atomMap, &cg.atomList)) {
  99.         script.notes = js_FinishTakingSrcNotes(cx, &cg);
  100.         if (script.notes) {
  101.         if (JS_ExecuteScript(cx, obj, &script, &result) &&
  102.             (ts->flags & TSF_INTERACTIVE) &&
  103.             result != JSVAL_VOID) {
  104.             str = JS_ValueToString(cx, result);
  105.             if (str)
  106.             printf("%s\n", JS_GetStringBytes(str));
  107.         }
  108.         free(script.notes);
  109.         }
  110.         js_FreeAtomMap(cx, &script.atomMap);
  111.     }
  112.     }
  113.  
  114. out:
  115.     if (ts)
  116.         (void) js_CloseTokenStream(cx, ts);
  117.     PR_FreeArenaPool(&cx->codePool);
  118.     PR_FreeArenaPool(&cx->tempPool);
  119. }
  120.  
  121. static JSBool
  122. Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  123. {
  124.     if (argc > 0 && JSVAL_IS_INT(argv[0]))
  125.     *rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVAL_TO_INT(argv[0])));
  126.     else
  127.     *rval = INT_TO_JSVAL(JS_GetVersion(cx));
  128.     return JS_TRUE;
  129. }
  130.  
  131. static JSBool
  132. Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  133. {
  134.     uintN i;
  135.     JSString *str;
  136.     const char *filename;
  137.     JSScript *script;
  138.     JSBool ok;
  139.     jsval result;
  140.  
  141.     for (i = 0; i < argc; i++) {
  142.     str = JS_ValueToString(cx, argv[i]);
  143.     if (!str)
  144.         return JS_FALSE;
  145.     argv[i] = STRING_TO_JSVAL(str);
  146.     filename = JS_GetStringBytes(str);
  147.     errno = 0;
  148.     script = JS_CompileFile(cx, obj, filename);
  149.     if (!script) {
  150.         fprintf(stderr, "js: cannot load %s", filename);
  151.         if (errno)
  152.         fprintf(stderr, ": %s", strerror(errno));
  153.         putc('\n', stderr);
  154.         continue;
  155.     }
  156.     ok = JS_ExecuteScript(cx, obj, script, &result);
  157.     JS_DestroyScript(cx, script);
  158.     if (!ok)
  159.         return JS_FALSE;
  160.     }
  161.     return JS_TRUE;
  162. }
  163.  
  164. static JSBool
  165. Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  166. {
  167.     uintN i, n;
  168.     JSString *str;
  169.  
  170.     for (i = n = 0; i < argc; i++) {
  171.     str = JS_ValueToString(cx, argv[i]);
  172.     if (!str)
  173.         return JS_FALSE;
  174.     printf("%s%s", i ? " " : "", JS_GetStringBytes(str));
  175.     n++;
  176.     }
  177.     if (n)
  178.     putchar('\n');
  179.     return JS_TRUE;
  180. }
  181.  
  182. static JSBool
  183. Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
  184.  
  185. static JSBool
  186. Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  187. {
  188.     exit(0);
  189.     return JS_FALSE;
  190. }
  191.  
  192. #ifdef GC_MARK_DEBUG
  193. extern FILE *js_DumpGCHeap;
  194. #endif
  195.  
  196. static JSBool
  197. GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  198. {
  199.     JSRuntime *rt;
  200.     uint32 preBytes;
  201.     
  202.     rt = cx->runtime;
  203.     preBytes = rt->gcBytes;
  204. #ifdef GC_MARK_DEBUG
  205.     js_DumpGCHeap = stdout;
  206. #endif
  207.     js_ForceGC(cx);
  208. #ifdef GC_MARK_DEBUG
  209.     js_DumpGCHeap = NULL;
  210. #endif
  211.     printf("before %lu, after %lu, break %08lx\n",
  212.        (unsigned long)preBytes, (unsigned long)rt->gcBytes,
  213. #ifdef XP_UNIX
  214.        (unsigned long)sbrk(0)
  215. #else
  216.        0
  217. #endif
  218.        );
  219. #ifdef JS_GCMETER
  220.     js_DumpGCStats(rt, stdout);
  221. #endif
  222.     return JS_TRUE;
  223. }
  224.  
  225. static JSBool
  226. GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,
  227.         int32 *ip)
  228. {
  229.     uintN intarg;
  230.     JSFunction *fun;
  231.  
  232.     *scriptp = cx->fp->down->script;
  233.     *ip = 0;
  234.     if (argc != 0) {
  235.     intarg = 0;
  236.     if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) {
  237.         fun = JS_ValueToFunction(cx, argv[0]);
  238.         if (!fun)
  239.         return JS_FALSE;
  240.         *scriptp = fun->script;
  241.         intarg++;
  242.     }
  243.     if (argc > intarg) {
  244.         if (!JS_ValueToInt32(cx, argv[intarg], ip))
  245.         return JS_FALSE;
  246.     }
  247.     }
  248.     return JS_TRUE;
  249. }
  250.  
  251. static JSTrapStatus
  252. TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
  253.         void *closure)
  254. {
  255.     JSString *str;
  256.     JSStackFrame *caller;
  257.  
  258.     str = closure;
  259.     caller = cx->fp->down;
  260.     if (!JS_EvaluateScript(cx, caller->scopeChain,
  261.                JS_GetStringBytes(str), JS_GetStringLength(str),
  262.                caller->script->filename, caller->script->lineno,
  263.                rval)) {
  264.     return JSTRAP_ERROR;
  265.     }
  266.     if (*rval != JSVAL_VOID)
  267.     return JSTRAP_RETURN;
  268.     return JSTRAP_CONTINUE;
  269. }
  270.  
  271. static JSBool
  272. Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  273. {
  274.     JSString *str;
  275.     JSScript *script;
  276.     int32 i;
  277.  
  278.     if (argc == 0) {
  279.     JS_ReportError(cx, "usage: trap [fun] [pc] expr");
  280.     return JS_FALSE;
  281.     }
  282.     argc--;
  283.     str = JS_ValueToString(cx, argv[argc]);
  284.     if (!str)
  285.     return JS_FALSE;
  286.     argv[argc] = STRING_TO_JSVAL(str);
  287.     if (!GetTrapArgs(cx, argc, argv, &script, &i))
  288.     return JS_FALSE;
  289.     return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);
  290. }
  291.  
  292. static JSBool
  293. Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  294. {
  295.     JSScript *script;
  296.     int32 i;
  297.  
  298.     if (!GetTrapArgs(cx, argc, argv, &script, &i))
  299.     return JS_FALSE;
  300.     JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
  301.     return JS_TRUE;
  302. }
  303.  
  304. static JSBool
  305. LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  306. {
  307.     JSScript *script;
  308.     int32 i;
  309.     uintN lineno;
  310.     jsbytecode *pc;
  311.  
  312.     if (argc == 0) {
  313.     JS_ReportError(cx, "usage: line2pc [fun] line");
  314.     return JS_FALSE;
  315.     }
  316.     script = cx->fp->down->script;
  317.     if (!GetTrapArgs(cx, argc, argv, &script, &i))
  318.     return JS_FALSE;
  319.     lineno = (i == 0) ? script->lineno : (uintN)i;
  320.     pc = JS_LineNumberToPC(cx, script, lineno);
  321.     if (!pc)
  322.     return JS_FALSE;
  323.     *rval = INT_TO_JSVAL(pc - script->code);
  324.     return JS_TRUE;
  325. }
  326.  
  327. static JSBool
  328. PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  329. {
  330.     JSScript *script;
  331.     int32 i;
  332.     uintN lineno;
  333.  
  334.     if (!GetTrapArgs(cx, argc, argv, &script, &i))
  335.     return JS_FALSE;
  336.     lineno = JS_PCToLineNumber(cx, script, script->code + i);
  337.     if (!lineno)
  338.     return JS_FALSE;
  339.     *rval = INT_TO_JSVAL(lineno);
  340.     return JS_TRUE;
  341. }
  342.  
  343. #ifdef DEBUG
  344.  
  345. static void
  346. SingleNote(JSContext *cx, JSFunction *fun )
  347. {
  348.     uintN offset, delta;
  349.     jssrcnote *notes, *sn;
  350.     JSSrcNoteType type;
  351.     jsatomid atomIndex;
  352.     JSAtom *atom;
  353.  
  354.     notes = fun->script->notes;
  355.     if (notes) {
  356.     printf("\nSource notes:\n");
  357.     offset = 0;
  358.     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
  359.         delta = SN_DELTA(sn);
  360.         offset += delta;
  361.         printf("%3u: %5u [%4u] %-8s",
  362.            sn - notes, offset, delta, js_SrcNoteName[SN_TYPE(sn)]);
  363.         type = SN_TYPE(sn);
  364.         switch (type) {
  365.           case SRC_SETLINE:
  366.         printf(" lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));
  367.         break;
  368.           case SRC_FOR:
  369.         printf(" cond %u update %u tail %u",
  370.                (uintN) js_GetSrcNoteOffset(sn, 0),
  371.                (uintN) js_GetSrcNoteOffset(sn, 1),
  372.                (uintN) js_GetSrcNoteOffset(sn, 2));
  373.         break;
  374.           case SRC_COMMA:
  375.           case SRC_PCBASE:
  376.         printf(" offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));
  377.         break;
  378.           case SRC_LABEL:
  379.           case SRC_LABELBRACE:
  380.           case SRC_BREAK2LABEL:
  381.           case SRC_CONT2LABEL:
  382.         atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);
  383.         atom = js_GetAtom(cx, &fun->script->atomMap, atomIndex);
  384.         printf(" atom %u (%s)", (uintN)atomIndex, ATOM_BYTES(atom));
  385.         break;
  386.           default:;
  387.         }
  388.         putchar('\n');
  389.     }
  390.     }
  391. }
  392.  
  393. static JSBool
  394. Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  395. {
  396.     uintN i;
  397.     JSFunction *fun;
  398.  
  399.     for (i = 0; i < argc; i++) {
  400.     fun = JS_ValueToFunction(cx, argv[i]);
  401.     if (!fun)
  402.         return JS_FALSE;
  403.  
  404.     SingleNote(cx, fun);
  405.     }
  406.     return JS_TRUE;
  407. }
  408.  
  409. static JSBool
  410. Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  411. {
  412.     JSBool lines;
  413.     uintN i;
  414.     JSFunction *fun;
  415.  
  416.     if (argc > 0 &&
  417.     JSVAL_IS_STRING(argv[0]) &&
  418.     !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {
  419.     lines = JS_TRUE;
  420.     argv++, argc--;
  421.     } else {
  422.     lines = JS_FALSE;
  423.     }
  424.     for (i = 0; i < argc; i++) {
  425.     fun = JS_ValueToFunction(cx, argv[i]);
  426.     if (!fun)
  427.         return JS_FALSE;
  428.  
  429.     js_Disassemble(cx, fun->script, lines, stdout);
  430.     SingleNote(cx, fun);
  431.     }
  432.     return JS_TRUE;
  433. }
  434.  
  435. static JSBool
  436. DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  437. {
  438. #define LINE_BUF_LEN 512
  439.     uintN i, k;
  440.     JSFunction *fun;
  441.     FILE *file;
  442.     char line_buf[LINE_BUF_LEN];
  443.     jsbytecode *pc, *end;
  444.     uintN len;
  445.     uintN lineno;
  446.  
  447.     for (i = 0; i < argc; i++) {
  448.     fun = JS_ValueToFunction(cx, argv[i]);
  449.     if (!fun)
  450.         return JS_FALSE;
  451.  
  452.     if (! fun->script || ! fun->script->filename ) {
  453.         JS_ReportError(cx, "only works on JS scripts read from files");
  454.         return JS_FALSE;
  455.     }
  456.  
  457.     file = fopen(fun->script->filename, "r");
  458.     if (!file) {
  459.         JS_ReportError(cx, "can't open %s: %s", fun->script->filename, strerror(errno));
  460.         return JS_FALSE;
  461.     }
  462.  
  463.     pc = fun->script->code;
  464.     end = pc + fun->script->length;
  465.  
  466.     /* burn the leading lines */
  467.     lineno = JS_PCToLineNumber(cx, fun->script, pc);
  468.     for (k = 0 ; k < lineno-1; k++)
  469.         fgets(line_buf, LINE_BUF_LEN, file);
  470.  
  471.     while (pc < end) {
  472.         lineno = JS_PCToLineNumber(cx, fun->script, pc);
  473.  
  474.         for ( ; k < lineno; k++ ) {
  475.         if (line_buf == fgets(line_buf, LINE_BUF_LEN, file))
  476.             printf(";------------------------- %3u: %s", k+1, line_buf );
  477.         }
  478.  
  479.         len = js_Disassemble1(cx, fun->script, pc, pc - fun->script->code,
  480.                   JS_TRUE, stdout);
  481.         if (!len)
  482.         return JS_FALSE;
  483.         pc += len;
  484.     }
  485.     fclose(file);
  486.     }
  487.     return JS_TRUE;
  488. }
  489.  
  490. static JSBool
  491. Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  492. {
  493.     JSBool bval;
  494.     JSString *str;
  495.  
  496.     if (argc == 0) {
  497.     *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
  498.     return JS_TRUE;
  499.     }
  500.  
  501.     switch (JS_TypeOfValue(cx, argv[0])) {
  502.       case JSTYPE_NUMBER:
  503.     bval = JSVAL_IS_INT(argv[0])
  504.            ? JSVAL_TO_INT(argv[0])
  505.            : (jsint) *JSVAL_TO_DOUBLE(argv[0]);
  506.     break;
  507.       case JSTYPE_BOOLEAN:
  508.     bval = JSVAL_TO_BOOLEAN(argv[0]);
  509.     break;
  510.       default:
  511.     str = JS_ValueToString(cx, argv[0]);
  512.     if (!str)
  513.         return JS_FALSE;
  514.     fprintf(stderr, "tracing: illegal argument %s\n",
  515.         JS_GetStringBytes(str));
  516.     return JS_TRUE;
  517.     }
  518.     cx->tracefp = bval ? stdout : NULL;
  519.     return JS_TRUE;
  520. }
  521.  
  522. int
  523. DumpAtom(PRHashEntry *he, int i, void *arg)
  524. {
  525.     FILE *fp = arg;
  526.     JSAtom *atom = (JSAtom *)he;
  527.  
  528.     fprintf(fp, "%3d %08x %5u %5lu ",
  529.         i, (uintN)he->keyHash, atom->index, (unsigned long)atom->number);
  530.     if (ATOM_IS_STRING(atom))
  531.     fprintf(fp, "\"%s\"\n", ATOM_BYTES(atom));
  532.     else if (ATOM_IS_INT(atom))
  533.     fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
  534.     else
  535.     fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
  536.     return HT_ENUMERATE_NEXT;
  537. }
  538.  
  539. int
  540. DumpSymbol(PRHashEntry *he, int i, void *arg)
  541. {
  542.     FILE *fp = arg;
  543.     JSSymbol *sym = (JSSymbol *)he;
  544.  
  545.     fprintf(fp, "%3d %08x", i, (uintN)he->keyHash);
  546.     if (JSVAL_IS_INT(sym_id(sym)))
  547.     fprintf(fp, " [%ld]\n", (long)JSVAL_TO_INT(sym_id(sym)));
  548.     else
  549.     fprintf(fp, " \"%s\"\n", ATOM_BYTES(sym_atom(sym)));
  550.     return HT_ENUMERATE_NEXT;
  551. }
  552.  
  553. extern JSScopeOps js_list_scope_ops;
  554.  
  555. void
  556. DumpScope(JSObject *obj, PRHashEnumerator dump, FILE *fp)
  557. {
  558.     JSScope *scope;
  559.     JSSymbol *sym;
  560.     int i;
  561.  
  562.     fprintf(fp, "\n%s scope contents:\n", obj->map->clasp->name);
  563.     scope = (JSScope *)obj->map;
  564.     if (scope->ops == &js_list_scope_ops) {
  565.     for (sym = (JSSymbol *)scope->data, i = 0; sym;
  566.          sym = (JSSymbol *)sym->entry.next, i++) {
  567.         DumpSymbol(&sym->entry, i, fp);
  568.     }
  569.     } else {
  570.     PR_HashTableDump(scope->data, dump, fp);
  571.     }
  572. }
  573.  
  574. /* These are callable from gdb. */
  575. void Dsym(JSSymbol *sym) { if (sym) DumpSymbol(&sym->entry, 0, stderr); }
  576. void Datom(JSAtom *atom) { if (atom) DumpAtom(&atom->entry, 0, stderr); }
  577.  
  578. static JSBool
  579. DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  580. {
  581.     uintN i;
  582.     JSString *str;
  583.     const char *bytes;
  584.     JSAtom *atom;
  585.     JSBool ok;
  586.     JSProperty *prop;
  587.  
  588.     for (i = 0; i < argc; i++) {
  589.     str = JS_ValueToString(cx, argv[i]);
  590.     if (!str)
  591.         return JS_FALSE;
  592.     bytes = JS_GetStringBytes(str);
  593.     if (strcmp(bytes, "arena") == 0) {
  594. #ifdef ARENAMETER
  595.         PR_DumpArenaStats(stdout);
  596. #endif
  597.     } else if (strcmp(bytes, "atom") == 0) {
  598.         printf("\natom table contents:\n");
  599.         PR_HashTableDump(cx->runtime->atomState.table, DumpAtom, stdout);
  600.     } else if (strcmp(bytes, "global") == 0) {
  601.         DumpScope(cx->globalObject, DumpSymbol, stdout);
  602.     } else {
  603.         JS_LOCK(cx);
  604.         atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);
  605.         if (!atom) {
  606.         ok = JS_FALSE;
  607.         } else {
  608.         ok = js_FindProperty(cx, (jsval)atom, &obj, &prop);
  609.         js_DropAtom(cx, atom);
  610.         if (ok) {
  611.             if (!prop ||
  612.             !JSVAL_IS_OBJECT(prop->object->slots[prop->slot])) {
  613.             fprintf(stderr, "js: invalid stats argument %s\n",
  614.                 bytes);
  615.             } else {
  616.             obj = JSVAL_TO_OBJECT(prop->object->slots[prop->slot]);
  617.             DumpScope(obj, DumpSymbol, stdout);
  618.             }
  619.         }
  620.         }
  621.         JS_UNLOCK(cx);
  622.         if (!ok)
  623.         return JS_FALSE;
  624.     }
  625.     }
  626.     return JS_TRUE;
  627. }
  628.  
  629. #endif /* DEBUG */
  630.  
  631. #ifdef TEST_EXPORT
  632. static JSBool
  633. DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  634. {
  635.     JSAtom *atom;
  636.     jsval value;
  637.     JSProperty *prop;
  638.  
  639.     if (argc != 2) {
  640.     JS_ReportError(cx, "usage: doexp obj id");
  641.     return JS_FALSE;
  642.     }
  643.     if (!JS_ValueToObject(cx, argv[0], &obj))
  644.     return JS_FALSE;
  645.     argv[0] = OBJECT_TO_JSVAL(obj);
  646.     atom = js_ValueToStringAtom(cx, argv[1]);
  647.     if (!atom)
  648.     return JS_FALSE;
  649.     prop = js_GetProperty(cx, obj, (jsval)atom, &value);
  650.     if (!prop)
  651.     return JS_FALSE;
  652.     prop->flags |= JSPROP_EXPORTED;
  653.     return JS_TRUE;
  654. }
  655. #endif
  656.  
  657. static JSFunctionSpec shell_functions[] = {
  658.     {"version",         Version,        0},
  659.     {"load",            Load,           1},
  660.     {"print",           Print,          0},
  661.     {"help",            Help,           0},
  662.     {"quit",            Quit,           0},
  663.     {"gc",              GC,             0},
  664.     {"trap",            Trap,           3},
  665.     {"untrap",          Untrap,         2},
  666.     {"line2pc",         LineToPC,       0},
  667.     {"pc2line",         PCToLine,       0},
  668. #ifdef DEBUG
  669.     {"dis",             Disassemble,    1},
  670.     {"dissrc",          DisassWithSrc,  1},
  671.     {"notes",           Notes,          1},
  672.     {"tracing",         Tracing,        0},
  673.     {"stats",           DumpStats,      1},
  674. #endif
  675. #ifdef TEST_EXPORT
  676.     {"doexp",           DoExport,       2},
  677. #endif
  678.     {0}
  679. };
  680.  
  681. /* NOTE: These must be kept in sync with the above. */
  682.  
  683. static char *shell_help_messages[] = {
  684.     "version [number]       Get or set JavaScript version number",
  685.     "load ['foo.js' ...]    Load files named by string arguments",
  686.     "print [expr ...]       Evaluate and print expressions",
  687.     "help [name ...]        Display usage and help messages",
  688.     "quit                   Quit mocha",
  689.     "gc                     Run the garbage collector",
  690.     "trap [fun] [pc] expr   Trap bytecode execution",
  691.     "untrap [fun] [pc]      Remove a trap",
  692.     "line2pc [fun] line     Map line number to PC",
  693.     "pc2line [fun] [pc]     Map PC to line number",
  694. #ifdef DEBUG
  695.     "dis [fun]              Disassemble functions into bytecodes",
  696.     "dissrc [fun]           Disassemble functions with source lines",
  697.     "notes [fun]            Show source notes for functions",
  698.     "tracing [toggle]       Turn tracing on or off",
  699.     "stats [string ...]     Dump 'arena', 'atom', 'global' stats",
  700. #endif
  701. #ifdef TEST_EXPORT
  702.     "doexp obj id           Export identified property from object",
  703. #endif
  704.     0
  705. };
  706.  
  707. static void
  708. ShowHelpHeader(void)
  709. {
  710.     printf("%-9s %-22s %s\n", "Command", "Usage", "Description");
  711.     printf("%-9s %-22s %s\n", "=======", "=====", "===========");
  712. }
  713.  
  714. static void
  715. ShowHelpForCommand(uintN n)
  716. {
  717.     printf("%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]);
  718. }
  719.  
  720. static JSBool
  721. Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  722. {
  723.     uintN i, j;
  724.     int did_header, did_something;
  725.     JSType type;
  726.     JSFunction *fun;
  727.     JSString *str;
  728.     const char *bytes;
  729.  
  730.     if (argc == 0) {
  731.     ShowHelpHeader();
  732.     for (i = 0; shell_functions[i].name; i++)
  733.         ShowHelpForCommand(i);
  734.     } else {
  735.     did_header = 0;
  736.     for (i = 0; i < argc; i++) {
  737.         did_something = 0;
  738.         type = JS_TypeOfValue(cx, argv[i]);
  739.         if (type == JSTYPE_FUNCTION) {
  740.         fun = JS_ValueToFunction(cx, argv[i]);
  741.         str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
  742.         } else if (type == JSTYPE_STRING) {
  743.         str = JSVAL_TO_STRING(argv[i]);
  744.         } else {
  745.         str = NULL;
  746.         }
  747.         if (str) {
  748.         bytes = JS_GetStringBytes(str);
  749.         for (j = 0; shell_functions[j].name; j++) {
  750.             if (!strcmp(bytes, shell_functions[j].name)) {
  751.             if (!did_header) {
  752.                 did_header = 1;
  753.                 ShowHelpHeader();
  754.             }
  755.             did_something = 1;
  756.             ShowHelpForCommand(j);
  757.             break;
  758.             }
  759.         }
  760.         }
  761.         if (!did_something) {
  762.         str = JS_ValueToString(cx, argv[i]);
  763.         if (!str)
  764.             return JS_FALSE;
  765.         fprintf(stderr, "Sorry, no help for %s\n",
  766.             JS_GetStringBytes(str));
  767.         }
  768.     }
  769.     }
  770.     return JS_TRUE;
  771. }
  772.  
  773. /*
  774.  * Define a JS object called "it".  Give it class operations that printf why
  775.  * they're being called for tutorial purposes.
  776.  */
  777. enum its_tinyid {
  778.     ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY
  779. };
  780.  
  781. static JSPropertySpec its_props[] = {
  782.     {"color",           ITS_COLOR,    JSPROP_ENUMERATE},
  783.     {"height",          ITS_HEIGHT,    JSPROP_ENUMERATE},
  784.     {"width",           ITS_WIDTH,    JSPROP_ENUMERATE},
  785.     {"funny",           ITS_FUNNY,    JSPROP_ENUMERATE},
  786.     {"array",           ITS_ARRAY,    JSPROP_ENUMERATE},
  787.     {"rdonly",        ITS_RDONLY,    JSPROP_READONLY},
  788.     {0}
  789. };
  790.  
  791. static JSBool its_noisy;    /* whether to be noisy when finalizing it */
  792.  
  793. static JSBool
  794. its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  795. {
  796.     if (its_noisy) {
  797.     printf("adding its property %s,",
  798.            JS_GetStringBytes(JS_ValueToString(cx, id)));
  799.     printf(" initial value %s\n",
  800.            JS_GetStringBytes(JS_ValueToString(cx, *vp)));
  801.     }
  802.     return JS_TRUE;
  803. }
  804.  
  805. static JSBool
  806. its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  807. {
  808.     if (its_noisy) {
  809.     printf("deleting its property %s,",
  810.            JS_GetStringBytes(JS_ValueToString(cx, id)));
  811.     printf(" current value %s\n",
  812.            JS_GetStringBytes(JS_ValueToString(cx, *vp)));
  813.     }
  814.     return JS_TRUE;
  815. }
  816.  
  817. static JSBool
  818. its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  819. {
  820.     if (its_noisy) {
  821.     printf("getting its property %s,",
  822.            JS_GetStringBytes(JS_ValueToString(cx, id)));
  823.     printf(" current value %s\n",
  824.            JS_GetStringBytes(JS_ValueToString(cx, *vp)));
  825.     }
  826.     return JS_TRUE;
  827. }
  828.  
  829. static JSBool
  830. its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  831. {
  832.     if (its_noisy) {
  833.     printf("setting its property %s,",
  834.            JS_GetStringBytes(JS_ValueToString(cx, id)));
  835.     printf(" new value %s\n",
  836.            JS_GetStringBytes(JS_ValueToString(cx, *vp)));
  837.     }
  838.     if (JSVAL_IS_STRING(id) &&
  839.     !strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {
  840.     return JS_ValueToBoolean(cx, *vp, &its_noisy);
  841.     }
  842.     return JS_TRUE;
  843. }
  844.  
  845. static JSBool
  846. its_enumerate(JSContext *cx, JSObject *obj)
  847. {
  848.     if (its_noisy)
  849.     printf("enumerate its properties\n");
  850.     return JS_TRUE;
  851. }
  852.  
  853. static JSBool
  854. its_resolve(JSContext *cx, JSObject *obj, jsval id)
  855. {
  856.     if (its_noisy) {
  857.     printf("resolving its property %s\n",
  858.            JS_GetStringBytes(JS_ValueToString(cx, id)));
  859.     }
  860.     return JS_TRUE;
  861. }
  862.  
  863. static JSBool
  864. its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
  865. {
  866.     if (its_noisy)
  867.     printf("converting it to %s type\n", JS_GetTypeName(cx, type));
  868.     return JS_TRUE;
  869. }
  870.  
  871. static void
  872. its_finalize(JSContext *cx, JSObject *obj)
  873. {
  874.     if (its_noisy)
  875.     printf("finalizing it\n");
  876. }
  877.  
  878. static JSClass its_class = {
  879.     "It", 0,
  880.     its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
  881.     its_enumerate,    its_resolve,      its_convert,      its_finalize
  882. };
  883.  
  884. static void
  885. my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
  886. {
  887.     int i, j, k, n;
  888.  
  889.     fputs("js: ", stderr);
  890.     if (!report) {
  891.     fprintf(stderr, "%s\n", message);
  892.     return;
  893.     }
  894.  
  895.     if (report->filename)
  896.     fprintf(stderr, "%s, ", report->filename);
  897.     if (report->lineno)
  898.     fprintf(stderr, "line %u: ", report->lineno);
  899.     fputs(message, stderr);
  900.     if (!report->linebuf) {
  901.     putc('\n', stderr);
  902.     return;
  903.     }
  904.  
  905.     fprintf(stderr, ":\n%s\n", report->linebuf);
  906.     n = report->tokenptr - report->linebuf;
  907.     for (i = j = 0; i < n; i++) {
  908.     if (report->linebuf[i] == '\t') {
  909.         for (k = (j + 8) & ~7; j < k; j++)
  910.         putc('.', stderr);
  911.         continue;
  912.     }
  913.     putc('.', stderr);
  914.     j++;
  915.     }
  916.     fputs("^\n", stderr);
  917. }
  918.  
  919. #ifdef XP_UNIX
  920. static JSBool
  921. Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  922. {
  923.     JSFunction *fun;
  924.     const char *name, **nargv;
  925.     uintN i, nargc;
  926.     JSString *str;
  927.     pid_t pid;
  928.     int status;
  929.  
  930.     fun = JS_ValueToFunction(cx, argv[-2]);
  931.     if (!fun)
  932.     return JS_FALSE;
  933.     if (!fun->atom)
  934.     return JS_TRUE;
  935.     name = ATOM_BYTES(fun->atom);
  936.     nargc = 1 + argc;
  937.     nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));
  938.     if (!nargv)
  939.     return JS_FALSE;
  940.     nargv[0] = name;
  941.     for (i = 1; i < nargc; i++) {
  942.     str = JS_ValueToString(cx, argv[i-1]);
  943.     if (!str) {
  944.         JS_free(cx, nargv);
  945.         return JS_FALSE;
  946.     }
  947.     nargv[i] = JS_GetStringBytes(str);
  948.     }
  949.     nargv[nargc] = 0;
  950.     pid = fork();
  951.     switch (pid) {
  952.       case -1:
  953.     perror("js");
  954.     break;
  955.       case 0:
  956.     (void) execvp(name, (char **)nargv);
  957.     perror("js");
  958.     exit(127);
  959.       default:
  960.     while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
  961.         ;
  962.     break;
  963.     }
  964.     JS_free(cx, nargv);
  965.     return JS_TRUE;
  966. }
  967. #endif
  968.  
  969. static JSBool
  970. global_resolve(JSContext *cx, JSObject *obj, jsval id)
  971. {
  972. #ifdef XP_UNIX
  973.     char *path, *comp, *full;
  974.     const char *name;
  975.     JSBool ok, found;
  976.     JSFunction *fun;
  977.  
  978.     if (!JSVAL_IS_STRING(id))
  979.     return JS_TRUE;
  980.     path = getenv("PATH");
  981.     if (!path)
  982.     return JS_TRUE;
  983.     path = JS_strdup(cx, path);
  984.     if (!path)
  985.     return JS_FALSE;
  986.     name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  987.     ok = JS_TRUE;
  988.     for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
  989.     if (*comp != '\0') {
  990.         full = PR_smprintf("%s/%s", comp, name);
  991.         if (!full) {
  992.         JS_ReportOutOfMemory(cx);
  993.         ok = JS_FALSE;
  994.         break;
  995.         }
  996.     } else {
  997.         full = (char *)name;
  998.     }
  999.     found = (access(full, X_OK) == 0);
  1000.     if (*comp != '\0')
  1001.         free(full);
  1002.     if (found) {
  1003.         fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE);
  1004.         ok = (fun != NULL);
  1005.         break;
  1006.     }
  1007.     }
  1008.     JS_free(cx, path);
  1009.     return ok;
  1010. #else
  1011.     return JS_TRUE;
  1012. #endif
  1013. }
  1014.  
  1015. static JSClass global_class = {
  1016.     "global", 0,
  1017.     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
  1018.     JS_EnumerateStub, global_resolve,   JS_ConvertStub,   JS_FinalizeStub
  1019. };
  1020.  
  1021. int
  1022. main(int argc, char **argv)
  1023. {
  1024.     int c, i;
  1025.     JSVersion version;
  1026.     JSRuntime *rt;
  1027.     JSContext *cx;
  1028.     JSObject *glob, *it;
  1029.  
  1030. #ifdef XP_OS2
  1031.    /* these streams are normally line buffered on OS/2 and need a \n, *
  1032.     * so we need to unbuffer then to get a reasonable prompt          */
  1033.     setbuf(stdout,0);
  1034.     setbuf(stderr,0);
  1035. #endif
  1036.  
  1037.     version = JSVERSION_DEFAULT;
  1038. #ifdef XP_UNIX
  1039.     while ((c = getopt(argc, argv, "v:")) != -1) {
  1040.     switch (c) {
  1041.       case 'v':
  1042.         version = atoi(optarg);
  1043.         break;
  1044.       default:
  1045.         fprintf(stderr, "usage: js [-v version]\n");
  1046.         return 2;
  1047.     }
  1048.     }
  1049.     argc -= optind;
  1050.     argv += optind;
  1051. #else
  1052.     c = -1;
  1053.     argc--;
  1054.     argv++;
  1055. #endif
  1056.  
  1057.     rt = JS_Init(8L * 1024L * 1024L);
  1058.     if (!rt)
  1059.     return 1;
  1060.     cx = JS_NewContext(rt, 8192);
  1061.     if (!cx)
  1062.     return 1;
  1063.     if (version != JSVERSION_DEFAULT)
  1064.     JS_SetVersion(cx, version);
  1065.  
  1066.     glob = JS_NewObject(cx, &global_class, NULL, NULL);
  1067.     if (!glob)
  1068.     return 1;
  1069.     if (!JS_InitStandardClasses(cx, glob))
  1070.     return 1;
  1071.     JS_SetErrorReporter(cx, my_ErrorReporter);
  1072.     if (!JS_DefineFunctions(cx, glob, shell_functions))
  1073.     return 1;
  1074.  
  1075.     it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
  1076.     if (!it)
  1077.     return 1;
  1078.     if (!JS_DefineProperties(cx, it, its_props))
  1079.     return 1;
  1080.  
  1081.     if (argc > 0) {
  1082.     for (i = 0; i < argc; i++)
  1083.         Process(cx, glob, argv[i]);
  1084.     } else {
  1085.     Process(cx, glob, NULL);
  1086.     }
  1087.  
  1088.     JS_DestroyContext(cx);
  1089.     JS_Finish(rt);
  1090.     return 0;
  1091. }
  1092.