/[jscoverage]/trunk/js/js.cpp
ViewVC logotype

Diff of /trunk/js/js.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 459 by siliconforks, Tue Dec 9 03:37:47 2008 UTC revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC
# Line 43  Line 43 
43   */   */
44  #include "jsstddef.h"  #include "jsstddef.h"
45  #include <errno.h>  #include <errno.h>
46    #include <math.h>
47  #include <stdio.h>  #include <stdio.h>
48  #include <stdlib.h>  #include <stdlib.h>
49  #include <string.h>  #include <string.h>
50    #include <signal.h>
51  #include <locale.h>  #include <locale.h>
52  #include "jstypes.h"  #include "jstypes.h"
53  #include "jsarena.h"  #include "jsarena.h"
# Line 56  Line 58 
58  #include "jsatom.h"  #include "jsatom.h"
59  #include "jsbuiltins.h"  #include "jsbuiltins.h"
60  #include "jscntxt.h"  #include "jscntxt.h"
61    #include "jsdate.h"
62  #include "jsdbgapi.h"  #include "jsdbgapi.h"
63  #include "jsemit.h"  #include "jsemit.h"
64  #include "jsfun.h"  #include "jsfun.h"
65  #include "jsgc.h"  #include "jsgc.h"
66    #include "jsiter.h"
67  #include "jslock.h"  #include "jslock.h"
68  #include "jsnum.h"  #include "jsnum.h"
69  #include "jsobj.h"  #include "jsobj.h"
# Line 67  Line 71 
71  #include "jsscope.h"  #include "jsscope.h"
72  #include "jsscript.h"  #include "jsscript.h"
73    
74    #include "prmjtime.h"
75    
76  #ifdef LIVECONNECT  #ifdef LIVECONNECT
77  #include "jsjava.h"  #include "jsjava.h"
78  #endif  #endif
# Line 94  Line 100 
100  typedef enum JSShellExitCode {  typedef enum JSShellExitCode {
101      EXITCODE_RUNTIME_ERROR      = 3,      EXITCODE_RUNTIME_ERROR      = 3,
102      EXITCODE_FILE_NOT_FOUND     = 4,      EXITCODE_FILE_NOT_FOUND     = 4,
103      EXITCODE_OUT_OF_MEMORY      = 5      EXITCODE_OUT_OF_MEMORY      = 5,
104        EXITCODE_TIMEOUT            = 6
105  } JSShellExitCode;  } JSShellExitCode;
106    
107  size_t gStackChunkSize = 8192;  size_t gStackChunkSize = 8192;
108    
109  /* Assume that we can not use more than 5e5 bytes of C stack by default. */  /* Assume that we can not use more than 5e5 bytes of C stack by default. */
110  static size_t gMaxStackSize = 500000;  static size_t gMaxStackSize = 500000;
111    
112    #ifdef JS_THREADSAFE
113    static PRUintn gStackBaseThreadIndex;
114    #else
115  static jsuword gStackBase;  static jsuword gStackBase;
116    #endif
117    
118  static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;  static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
119    
120  static JSBool gEnableBranchCallback = JS_FALSE;  /*
121  static uint32 gBranchCount;   * Limit the timeout to 30 minutes to prevent an overflow on platfoms
122  static uint32 gBranchLimit;   * that represent the time internally in microseconds using 32-bit int.
123     */
124    static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
125    static jsdouble gTimeoutInterval = -1.0;
126    static volatile bool gCanceled = false;
127    
128    static JSBool
129    SetTimeoutValue(JSContext *cx, jsdouble t);
130    
131    static bool
132    InitWatchdog(JSRuntime *rt);
133    
134    static void
135    KillWatchdog();
136    
137    static bool
138    ScheduleWatchdog(JSRuntime *rt, jsdouble t);
139    
140    static void
141    CancelExecution(JSRuntime *rt);
142    
143    /*
144     * Watchdog thread state.
145     */
146    #ifdef JS_THREADSAFE
147    
148    static PRLock *gWatchdogLock = NULL;
149    static PRCondVar *gWatchdogWakeup = NULL;
150    static PRThread *gWatchdogThread = NULL;
151    static bool gWatchdogHasTimeout = false;
152    static PRIntervalTime gWatchdogTimeout = 0;
153    
154    static PRCondVar *gSleepWakeup = NULL;
155    
156    /*
157     * Holding the gcLock already guarantees that the context list is locked when
158     * the watchdog thread walks it.
159     */
160    
161    #define WITH_LOCKED_CONTEXT_LIST(x)             \
162        JS_BEGIN_MACRO                              \
163            x;                                      \
164        JS_END_MACRO
165    
166    #else
167    
168    static JSRuntime *gRuntime = NULL;
169    
170    /*
171     * Since signal handlers can't block, we must disable them before manipulating
172     * the context list.
173     */
174    #define WITH_LOCKED_CONTEXT_LIST(x)                                           \
175        JS_BEGIN_MACRO                                                            \
176            ScheduleWatchdog(gRuntime, -1);                                       \
177            x;                                                                    \
178            ScheduleWatchdog(gRuntime, gTimeoutInterval);                         \
179        JS_END_MACRO
180    #endif
181    
182  int gExitCode = 0;  int gExitCode = 0;
183  JSBool gQuitting = JS_FALSE;  JSBool gQuitting = JS_FALSE;
# Line 133  Line 203 
203    
204  #ifdef EDITLINE  #ifdef EDITLINE
205  JS_BEGIN_EXTERN_C  JS_BEGIN_EXTERN_C
206  extern char     *readline(const char *prompt);  JS_EXTERN_API(char)    *readline(const char *prompt);
207  extern void     add_history(char *line);  JS_EXTERN_API(void)     add_history(char *line);
208  JS_END_EXTERN_C  JS_END_EXTERN_C
209  #endif  #endif
210    
211  static JSBool  static char *
212  GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {  GetLine(FILE *file, const char * prompt)
213    {
214        size_t size;
215        char *buffer;
216  #ifdef EDITLINE  #ifdef EDITLINE
217      /*      /*
218       * Use readline only if file is stdin, because there's no way to specify       * Use readline only if file is stdin, because there's no way to specify
# Line 147  Line 220 
220       */       */
221      if (file == stdin) {      if (file == stdin) {
222          char *linep = readline(prompt);          char *linep = readline(prompt);
223            /*
224             * We set it to zero to avoid complaining about inappropriate ioctl
225             * for device in the case of EOF. Looks like errno == 251 if line is
226             * finished with EOF and errno == 25 (EINVAL on Mac) if there is
227             * nothing left to read.
228             */
229            if (errno == 251 || errno == 25 || errno == EINVAL)
230                errno = 0;
231          if (!linep)          if (!linep)
232              return JS_FALSE;              return NULL;
233          if (linep[0] != '\0')          if (linep[0] != '\0')
234              add_history(linep);              add_history(linep);
235          strcpy(bufp, linep);          return linep;
236          JS_free(cx, linep);      }
         bufp += strlen(bufp);  
         *bufp++ = '\n';  
         *bufp = '\0';  
     } else  
237  #endif  #endif
238      {      size_t len = 0;
239          char line[256];      if (*prompt != '\0') {
240          fprintf(gOutFile, prompt);          fprintf(gOutFile, "%s", prompt);
241          fflush(gOutFile);          fflush(gOutFile);
         if (!fgets(line, sizeof line, file))  
             return JS_FALSE;  
         strcpy(bufp, line);  
242      }      }
243      return JS_TRUE;      size = 80;
244        buffer = (char *) malloc(size);
245        if (!buffer)
246            return NULL;
247        char *current = buffer;
248        while (fgets(current, size - len, file)) {
249            len += strlen(current);
250            char *t = buffer + len - 1;
251            if (*t == '\n') {
252                /* Line was read. We remove '\n' and exit. */
253                *t = '\0';
254                return buffer;
255            }
256            if (len + 1 == size) {
257                size = size * 2;
258                char *tmp = (char *) realloc(buffer, size);
259                if (!tmp) {
260                    free(buffer);
261                    return NULL;
262                }
263                buffer = tmp;
264            }
265            current = buffer + len;
266        }
267        if (len && !ferror(file))
268            return buffer;
269        free(buffer);
270        return NULL;
271    }
272    
273    /*
274     * State to store as JSContext private.
275     *
276     * We declare such timestamp as volatile as they are updated in the operation
277     * callback without taking any locks. Any possible race can only lead to more
278     * frequent callback calls. This is safe as the callback does everything based
279     * on timing.
280     */
281    struct JSShellContextData {
282        volatile JSIntervalTime startTime;
283    };
284    
285    static JSShellContextData *
286    NewContextData()
287    {
288        /* Prevent creation of new contexts after we have been canceled. */
289        if (gCanceled)
290            return NULL;
291    
292        JSShellContextData *data = (JSShellContextData *)
293                                   calloc(sizeof(JSShellContextData), 1);
294        if (!data)
295            return NULL;
296        data->startTime = js_IntervalNow();
297        return data;
298    }
299    
300    static inline JSShellContextData *
301    GetContextData(JSContext *cx)
302    {
303        JSShellContextData *data = (JSShellContextData *) JS_GetContextPrivate(cx);
304    
305        JS_ASSERT(data);
306        return data;
307  }  }
308    
309  static JSBool  static JSBool
310  my_BranchCallback(JSContext *cx, JSScript *script)  ShellOperationCallback(JSContext *cx)
311  {  {
312      if (++gBranchCount == gBranchLimit) {      if (!gCanceled)
313          if (script) {          return JS_TRUE;
314              if (script->filename)  
315                  fprintf(gErrFile, "%s:", script->filename);      JS_ClearPendingException(cx);
316              fprintf(gErrFile, "%u: script branch callback (%u callbacks)\n",      return JS_FALSE;
                     script->lineno, gBranchLimit);  
         } else {  
             fprintf(gErrFile, "native branch callback (%u callbacks)\n",  
                     gBranchLimit);  
         }  
         gBranchCount = 0;  
         return JS_FALSE;  
     }  
 #ifdef JS_THREADSAFE  
     if ((gBranchCount & 0xff) == 1) {  
 #endif  
         if ((gBranchCount & 0x3fff) == 1)  
             JS_MaybeGC(cx);  
 #ifdef JS_THREADSAFE  
         else  
             JS_YieldRequest(cx);  
     }  
 #endif  
     return JS_TRUE;  
317  }  }
318    
319  static void  static void
320  SetContextOptions(JSContext *cx)  SetThreadStackLimit(JSContext *cx)
321  {  {
322      jsuword stackLimit;      jsuword stackLimit;
323    
# Line 209  Line 327 
327           */           */
328          stackLimit = 0;          stackLimit = 0;
329      } else {      } else {
330            jsuword stackBase;
331    #ifdef JS_THREADSAFE
332            stackBase = (jsuword) PR_GetThreadPrivate(gStackBaseThreadIndex);
333    #else
334            stackBase = gStackBase;
335    #endif
336            JS_ASSERT(stackBase != 0);
337  #if JS_STACK_GROWTH_DIRECTION > 0  #if JS_STACK_GROWTH_DIRECTION > 0
338          stackLimit = gStackBase + gMaxStackSize;          stackLimit = stackBase + gMaxStackSize;
339  #else  #else
340          stackLimit = gStackBase - gMaxStackSize;          stackLimit = stackBase - gMaxStackSize;
341  #endif  #endif
342      }      }
343      JS_SetThreadStackLimit(cx, stackLimit);      JS_SetThreadStackLimit(cx, stackLimit);
344    
345    }
346    
347    static void
348    SetContextOptions(JSContext *cx)
349    {
350        SetThreadStackLimit(cx);
351      JS_SetScriptStackQuota(cx, gScriptStackQuota);      JS_SetScriptStackQuota(cx, gScriptStackQuota);
352      if (gEnableBranchCallback) {      JS_SetOperationCallback(cx, ShellOperationCallback);
         JS_SetBranchCallback(cx, my_BranchCallback);  
         JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK);  
     }  
353  }  }
354    
355  static void  static void
# Line 230  Line 359 
359      JSScript *script;      JSScript *script;
360      jsval result;      jsval result;
361      JSString *str;      JSString *str;
362      char buffer[4096];      char *buffer;
363      char *bufp;      size_t size;
364      int lineno;      int lineno;
365      int startline;      int startline;
366      FILE *file;      FILE *file;
# Line 287  Line 416 
416      /* It's an interactive filehandle; drop into read-eval-print loop. */      /* It's an interactive filehandle; drop into read-eval-print loop. */
417      lineno = 1;      lineno = 1;
418      hitEOF = JS_FALSE;      hitEOF = JS_FALSE;
419        buffer = NULL;
420      do {      do {
         bufp = buffer;  
         *bufp = '\0';  
   
421          /*          /*
422           * Accumulate lines until we get a 'compilable unit' - one that either           * Accumulate lines until we get a 'compilable unit' - one that either
423           * generates an error (before running out of source) or that compiles           * generates an error (before running out of source) or that compiles
# Line 298  Line 425 
425           * coincides with the end of a line.           * coincides with the end of a line.
426           */           */
427          startline = lineno;          startline = lineno;
428            size_t len = 0; /* initialize to avoid warnings */
429          do {          do {
430              if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {              ScheduleWatchdog(cx->runtime, -1);
431                jsrefcount rc = JS_SuspendRequest(cx);
432                gCanceled = false;
433                errno = 0;
434                char *line = GetLine(file, startline == lineno ? "js> " : "");
435                if (!line) {
436                    JS_ResumeRequest(cx, rc);
437                    if (errno) {
438                        JS_ReportError(cx, strerror(errno));
439                        free(buffer);
440                        return;
441                    }
442                  hitEOF = JS_TRUE;                  hitEOF = JS_TRUE;
443                  break;                  break;
444              }              }
445              bufp += strlen(bufp);              if (!buffer) {
446                    buffer = line;
447                    len = strlen(buffer);
448                    size = len + 1;
449                } else {
450                    /*
451                     * len + 1 is required to store '\n' in the end of line.
452                     */
453                    size_t newlen = strlen(line) + (len ? len + 1 : 0);
454                    if (newlen + 1 > size) {
455                        size = newlen + 1 > size * 2 ? newlen + 1 : size * 2;
456                        char *newBuf = (char *) realloc(buffer, size);
457                        if (!newBuf) {
458                            free(buffer);
459                            free(line);
460                            JS_ResumeRequest(cx, rc);
461                            JS_ReportOutOfMemory(cx);
462                            return;
463                        }
464                        buffer = newBuf;
465                    }
466                    char *current = buffer + len;
467                    if (startline != lineno)
468                        *current++ = '\n';
469                    strcpy(current, line);
470                    len = newlen;
471                    free(line);
472                }
473              lineno++;              lineno++;
474          } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));              JS_ResumeRequest(cx, rc);
475                if (!ScheduleWatchdog(cx->runtime, gTimeoutInterval)) {
476                    hitEOF = JS_TRUE;
477                    break;
478                }
479            } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, len));
480    
481          /* Clear any pending exception from previous failed compiles.  */          if (hitEOF && !buffer)
482                break;
483    
484            /* Clear any pending exception from previous failed compiles. */
485          JS_ClearPendingException(cx);          JS_ClearPendingException(cx);
486          script = JS_CompileScript(cx, obj, buffer, strlen(buffer), "typein",  
487            /* Even though we're interactive, we have a compile-n-go opportunity. */
488            oldopts = JS_GetOptions(cx);
489            if (!compileOnly)
490                JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO);
491            script = JS_CompileScript(cx, obj, buffer, len, "typein",
492                                    startline);                                    startline);
493            if (!compileOnly)
494                JS_SetOptions(cx, oldopts);
495    
496          if (script) {          if (script) {
497              if (!compileOnly) {              if (!compileOnly) {
498                  ok = JS_ExecuteScript(cx, obj, script, &result);                  ok = JS_ExecuteScript(cx, obj, script, &result);
# Line 324  Line 506 
506              }              }
507              JS_DestroyScript(cx, script);              JS_DestroyScript(cx, script);
508          }          }
509            *buffer = '\0';
510      } while (!hitEOF && !gQuitting);      } while (!hitEOF && !gQuitting);
511    
512        free(buffer);
513      fprintf(gOutFile, "\n");      fprintf(gOutFile, "\n");
514      if (file != stdin)      if (file != stdin)
515          fclose(file);          fclose(file);
# Line 335  Line 520 
520  usage(void)  usage(void)
521  {  {
522      fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());      fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
523      fprintf(gErrFile, "usage: js [-zKPswWxCi] [-b branchlimit] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "      fprintf(gErrFile, "usage: js [-zKPswWxCij] [-t timeoutSeconds] [-c stackchunksize] [-o option] [-v version] [-f scriptfile] [-e script] [-S maxstacksize] "
524  #ifdef JS_GC_ZEAL  #ifdef JS_GC_ZEAL
525  "[-Z gczeal] "  "[-Z gczeal] "
526  #endif  #endif
# Line 380  Line 565 
565              break;              break;
566          }          }
567          switch (argv[i][1]) {          switch (argv[i][1]) {
           case 'b':  
568            case 'c':            case 'c':
569            case 'f':            case 'f':
570            case 'e':            case 'e':
571            case 'v':            case 'v':
572            case 'S':            case 'S':
573              case 't':
574  #ifdef JS_GC_ZEAL  #ifdef JS_GC_ZEAL
575            case 'Z':            case 'Z':
576  #endif  #endif
# Line 501  Line 686 
686              }              }
687              break;              break;
688    
689          case 'b':          case 't':
690              gBranchLimit = atoi(argv[++i]);              if (++i == argc)
691              gEnableBranchCallback = (gBranchLimit != 0);                  return usage();
692    
693                if (!SetTimeoutValue(cx, atof(argv[i])))
694                    return JS_FALSE;
695    
696              break;              break;
697    
698          case 'c':          case 'c':
# Line 516  Line 705 
705                  return usage();                  return usage();
706    
707              Process(cx, obj, argv[i], JS_FALSE);              Process(cx, obj, argv[i], JS_FALSE);
708                if (gExitCode != 0)
709                    return gExitCode;
710    
711              /*              /*
712               * XXX: js -f foo.js should interpret foo.js and then               * XXX: js -f foo.js should interpret foo.js and then
# Line 760  Line 951 
951      return JS_TRUE;      return JS_TRUE;
952  }  }
953    
 #ifdef JS_TRACER  
 static jsval JS_FASTCALL  
 Print_tn(JSContext *cx, JSString *str)  
 {  
     char *bytes = JS_EncodeString(cx, str);  
     if (!bytes)  
         return JSVAL_ERROR_COOKIE;  
     fprintf(gOutFile, "%s\n", bytes);  
     JS_free(cx, bytes);  
     fflush(gOutFile);  
     return JSVAL_VOID;  
 }  
 #endif  
   
954  static JSBool  static JSBool
955  Print(JSContext *cx, uintN argc, jsval *vp)  Print(JSContext *cx, uintN argc, jsval *vp)
956  {  {
# Line 817  Line 994 
994      return JS_FALSE;      return JS_FALSE;
995  }  }
996    
997    static const char *
998    ToSource(JSContext *cx, jsval *vp)
999    {
1000        JSString *str = JS_ValueToSource(cx, *vp);
1001        if (str) {
1002            *vp = STRING_TO_JSVAL(str);
1003            return JS_GetStringBytes(str);
1004        }
1005        JS_ClearPendingException(cx);
1006        return "<<error converting value to string>>";
1007    }
1008    
1009    static JSBool
1010    AssertEq(JSContext *cx, uintN argc, jsval *vp)
1011    {
1012        if (argc != 2) {
1013            JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1014                                 (argc > 2) ? JSSMSG_TOO_MANY_ARGS : JSSMSG_NOT_ENOUGH_ARGS,
1015                                 "assertEq");
1016            return JS_FALSE;
1017        }
1018    
1019        jsval *argv = JS_ARGV(cx, vp);
1020        if (!js_StrictlyEqual(cx, argv[0], argv[1])) {
1021            const char *actual = ToSource(cx, &argv[0]);
1022            const char *expected = ToSource(cx, &argv[1]);
1023            JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_ASSERT_EQ_FAILED,
1024                                 actual, expected);
1025            return JS_FALSE;
1026        }
1027        JS_SET_RVAL(cx, vp, JSVAL_VOID);
1028        return JS_TRUE;
1029    }
1030    
1031  static JSBool  static JSBool
1032  GC(JSContext *cx, uintN argc, jsval *vp)  GC(JSContext *cx, uintN argc, jsval *vp)
1033  {  {
# Line 827  Line 1038 
1038      preBytes = rt->gcBytes;      preBytes = rt->gcBytes;
1039      JS_GC(cx);      JS_GC(cx);
1040    
1041      fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",      char buf[256];
1042              (unsigned long)preBytes, (unsigned long)rt->gcBytes,      JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n",
1043                    (unsigned long)preBytes, (unsigned long)rt->gcBytes,
1044  #ifdef XP_UNIX  #ifdef XP_UNIX
1045              (unsigned long)sbrk(0)                  (unsigned long)sbrk(0)
1046  #else  #else
1047              0                  0
1048  #endif  #endif
1049              );                  );
1050  #ifdef JS_GCMETER  #ifdef JS_GCMETER
1051      js_DumpGCStats(rt, stdout);      js_DumpGCStats(rt, stdout);
1052  #endif  #endif
1053      *vp = JSVAL_VOID;      *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
1054      return JS_TRUE;      return JS_TRUE;
1055  }  }
1056    
# Line 866  Line 1078 
1078          param = JSGC_MAX_BYTES;          param = JSGC_MAX_BYTES;
1079      } else if (strcmp(paramName, "maxMallocBytes") == 0) {      } else if (strcmp(paramName, "maxMallocBytes") == 0) {
1080          param = JSGC_MAX_MALLOC_BYTES;          param = JSGC_MAX_MALLOC_BYTES;
1081        } else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
1082            param = JSGC_STACKPOOL_LIFESPAN;
1083        } else if (strcmp(paramName, "gcBytes") == 0) {
1084            param = JSGC_BYTES;
1085        } else if (strcmp(paramName, "gcNumber") == 0) {
1086            param = JSGC_NUMBER;
1087        } else if (strcmp(paramName, "gcTriggerFactor") == 0) {
1088            param = JSGC_TRIGGER_FACTOR;
1089      } else {      } else {
1090          JS_ReportError(cx,          JS_ReportError(cx,
1091                         "the first argument argument must be either maxBytes "                         "the first argument argument must be maxBytes, "
1092                         "or maxMallocBytes");                         "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
1093                           "gcNumber or gcTriggerFactor");
1094          return JS_FALSE;          return JS_FALSE;
1095      }      }
1096    
1097      if (!JS_ValueToECMAUint32(cx, argc < 2 ? JSVAL_VOID : vp[3], &value))      if (argc == 1) {
1098            value = JS_GetGCParameter(cx->runtime, param);
1099            return JS_NewNumberValue(cx, value, &vp[0]);
1100        }
1101    
1102        if (param == JSGC_NUMBER ||
1103            param == JSGC_BYTES) {
1104            JS_ReportError(cx, "Attempt to change read-only parameter %s",
1105                           paramName);
1106          return JS_FALSE;          return JS_FALSE;
1107      if (value == 0) {      }
1108    
1109        if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
1110          JS_ReportError(cx,          JS_ReportError(cx,
1111                         "the second argument must be convertable to uint32 with "                         "the second argument must be convertable to uint32 "
1112                         "non-zero value");                         "with non-zero value");
1113            return JS_FALSE;
1114        }
1115        if (param == JSGC_TRIGGER_FACTOR && value < 100) {
1116            JS_ReportError(cx,
1117                           "the gcTriggerFactor value must be >= 100");
1118          return JS_FALSE;          return JS_FALSE;
1119      }      }
1120      JS_SetGCParameter(cx->runtime, param, value);      JS_SetGCParameter(cx->runtime, param, value);
# Line 1055  Line 1291 
1291  static JSScript *  static JSScript *
1292  ValueToScript(JSContext *cx, jsval v)  ValueToScript(JSContext *cx, jsval v)
1293  {  {
1294      JSScript *script;      JSScript *script = NULL;
1295      JSFunction *fun;      JSFunction *fun;
1296    
1297      if (!JSVAL_IS_PRIMITIVE(v) &&      if (!JSVAL_IS_PRIMITIVE(v)) {
1298          JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {          JSObject *obj = JSVAL_TO_OBJECT(v);
1299          script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));          JSClass *clasp = JS_GET_CLASS(cx, obj);
1300      } else {  
1301            if (clasp == &js_ScriptClass) {
1302                script = (JSScript *) JS_GetPrivate(cx, obj);
1303            } else if (clasp == &js_GeneratorClass) {
1304                JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
1305                fun = gen->frame.fun;
1306                script = FUN_SCRIPT(fun);
1307            }
1308        }
1309    
1310        if (!script) {
1311          fun = JS_ValueToFunction(cx, v);          fun = JS_ValueToFunction(cx, v);
1312          if (!fun)          if (!fun)
1313              return NULL;              return NULL;
1314          script = FUN_SCRIPT(fun);          script = FUN_SCRIPT(fun);
1315      }          if (!script) {
1316                JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
1317      if (!script) {                                   JSSMSG_SCRIPTS_ONLY);
1318          JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,          }
                              JSSMSG_SCRIPTS_ONLY);  
1319      }      }
1320    
1321      return script;      return script;
# Line 1084  Line 1329 
1329      uintN intarg;      uintN intarg;
1330      JSScript *script;      JSScript *script;
1331    
1332      *scriptp = cx->fp->down->script;      *scriptp = JS_GetScriptedCaller(cx, NULL)->script;
1333      *ip = 0;      *ip = 0;
1334      if (argc != 0) {      if (argc != 0) {
1335          v = argv[0];          v = argv[0];
# Line 1171  Line 1416 
1416          JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);          JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);
1417          return JS_FALSE;          return JS_FALSE;
1418      }      }
1419      script = cx->fp->down->script;      script = JS_GetScriptedCaller(cx, NULL)->script;
1420      if (!GetTrapArgs(cx, argc, argv, &script, &i))      if (!GetTrapArgs(cx, argc, argv, &script, &i))
1421          return JS_FALSE;          return JS_FALSE;
1422      lineno = (i == 0) ? script->lineno : (uintN)i;      lineno = (i == 0) ? script->lineno : (uintN)i;
# Line 1201  Line 1446 
1446  #ifdef DEBUG  #ifdef DEBUG
1447    
1448  static void  static void
1449  UpdateSwitchTableBounds(JSScript *script, uintN offset,  UpdateSwitchTableBounds(JSContext *cx, JSScript *script, uintN offset,
1450                          uintN *start, uintN *end)                          uintN *start, uintN *end)
1451  {  {
1452      jsbytecode *pc;      jsbytecode *pc;
# Line 1210  Line 1455 
1455      jsint low, high, n;      jsint low, high, n;
1456    
1457      pc = script->code + offset;      pc = script->code + offset;
1458      op = (JSOp) *pc;      op = js_GetOpcode(cx, script, pc);
1459      switch (op) {      switch (op) {
1460        case JSOP_TABLESWITCHX:        case JSOP_TABLESWITCHX:
1461          jmplen = JUMPX_OFFSET_LEN;          jmplen = JUMPX_OFFSET_LEN;
# Line 1273  Line 1518 
1518              if (switchTableStart <= offset && offset < switchTableEnd) {              if (switchTableStart <= offset && offset < switchTableEnd) {
1519                  name = "case";                  name = "case";
1520              } else {              } else {
1521                  JS_ASSERT(script->code[offset] == JSOP_NOP);                  JS_ASSERT(js_GetOpcode(cx, script, script->code + offset) == JSOP_NOP);
1522              }              }
1523          }          }
1524          fprintf(gOutFile, "%3u: %5u [%4u] %-8s",          fprintf(gOutFile, "%3u: %5u [%4u] %-8s",
# Line 1331  Line 1576 
1576              caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);              caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);
1577              if (caseOff)              if (caseOff)
1578                  fprintf(gOutFile, " first case offset %u", caseOff);                  fprintf(gOutFile, " first case offset %u", caseOff);
1579              UpdateSwitchTableBounds(script, offset,              UpdateSwitchTableBounds(cx, script, offset,
1580                                      &switchTableStart, &switchTableEnd);                                      &switchTableStart, &switchTableEnd);
1581              break;              break;
1582            case SRC_CATCH:            case SRC_CATCH:
# Line 1413  Line 1658 
1658              return JS_FALSE;              return JS_FALSE;
1659          if (VALUE_IS_FUNCTION(cx, argv[i])) {          if (VALUE_IS_FUNCTION(cx, argv[i])) {
1660              JSFunction *fun = JS_ValueToFunction(cx, argv[i]);              JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
1661              if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {              if (fun && (fun->flags & ~7U)) {
1662                  uint16 flags = fun->flags;                  uint16 flags = fun->flags;
1663                  fputs("flags:", stdout);                  fputs("flags:", stdout);
1664    
# Line 1428  Line 1673 
1673                  SHOW_FLAG(THISP_NUMBER);                  SHOW_FLAG(THISP_NUMBER);
1674                  SHOW_FLAG(THISP_BOOLEAN);                  SHOW_FLAG(THISP_BOOLEAN);
1675                  SHOW_FLAG(EXPR_CLOSURE);                  SHOW_FLAG(EXPR_CLOSURE);
1676                  SHOW_FLAG(INTERPRETED);                  SHOW_FLAG(TRACEABLE);
1677    
1678  #undef SHOW_FLAG  #undef SHOW_FLAG
1679    
1680                    if (FUN_NULL_CLOSURE(fun))
1681                        fputs(" NULL_CLOSURE", stdout);
1682                    else if (FUN_FLAT_CLOSURE(fun))
1683                        fputs(" FLAT_CLOSURE", stdout);
1684                  putchar('\n');                  putchar('\n');
1685              }              }
1686          }          }
# Line 1566  Line 1816 
1816  static JSBool  static JSBool
1817  Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)  Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1818  {  {
1819      JSBool bval;      FILE *file;
     JSString *str;  
1820    
 #if JS_THREADED_INTERP  
     JS_ReportError(cx, "tracing not supported in JS_THREADED_INTERP builds");  
     return JS_FALSE;  
 #else  
1821      if (argc == 0) {      if (argc == 0) {
1822          *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);          *rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
1823          return JS_TRUE;          return JS_TRUE;
# Line 1580  Line 1825 
1825    
1826      switch (JS_TypeOfValue(cx, argv[0])) {      switch (JS_TypeOfValue(cx, argv[0])) {
1827        case JSTYPE_NUMBER:        case JSTYPE_NUMBER:
1828          bval = JSVAL_IS_INT(argv[0])        case JSTYPE_BOOLEAN: {
1829                 ? JSVAL_TO_INT(argv[0])          JSBool bval;
1830                 : (jsint) *JSVAL_TO_DOUBLE(argv[0]);          if (!JS_ValueToBoolean(cx, argv[0], &bval))
1831                goto bad_argument;
1832            file = bval ? stderr : NULL;
1833          break;          break;
1834        case JSTYPE_BOOLEAN:        }
1835          bval = JSVAL_TO_BOOLEAN(argv[0]);        case JSTYPE_STRING: {
1836            char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
1837            file = fopen(name, "w");
1838            if (!file) {
1839                JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
1840                               name, strerror(errno));
1841                return JS_FALSE;
1842            }
1843          break;          break;
1844          }
1845        default:        default:
1846          str = JS_ValueToString(cx, argv[0]);            goto bad_argument;
         if (!str)  
             return JS_FALSE;  
         JS_ReportError(cx, "tracing: illegal argument %s",  
                        JS_GetStringBytes(str));  
         return JS_FALSE;  
1847      }      }
1848      cx->tracefp = bval ? stderr : NULL;      if (cx->tracefp && cx->tracefp != stderr)
1849          fclose((FILE *)cx->tracefp);
1850        cx->tracefp = file;
1851        cx->tracePrevPc = NULL;
1852      return JS_TRUE;      return JS_TRUE;
1853  #endif  
1854     bad_argument:
1855        JSString *str = JS_ValueToString(cx, argv[0]);
1856        if (!str)
1857            return JS_FALSE;
1858        JS_ReportError(cx, "tracing: illegal argument %s",
1859                       JS_GetStringBytes(str));
1860        return JS_FALSE;
1861  }  }
1862    
1863  static void  static void
# Line 1948  Line 2208 
2208  static JSBool  static JSBool
2209  Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)  Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2210  {  {
     JSFunction *fun;  
2211      JSObject *funobj, *parent, *clone;      JSObject *funobj, *parent, *clone;
2212    
2213      fun = JS_ValueToFunction(cx, argv[0]);      if (VALUE_IS_FUNCTION(cx, argv[0])) {
2214      if (!fun)          funobj = JSVAL_TO_OBJECT(argv[0]);
2215          return JS_FALSE;      } else {
2216      funobj = JS_GetFunctionObject(fun);          JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
2217            if (!fun)
2218                return JS_FALSE;
2219            funobj = JS_GetFunctionObject(fun);
2220        }
2221      if (argc > 1) {      if (argc > 1) {
2222          if (!JS_ValueToObject(cx, argv[1], &parent))          if (!JS_ValueToObject(cx, argv[1], &parent))
2223              return JS_FALSE;              return JS_FALSE;
# Line 1993  Line 2256 
2256    
2257      if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))      if (!JS_ValueToObject(cx, argc == 0 ? JSVAL_VOID : vp[2], &vobj))
2258          return JS_FALSE;          return JS_FALSE;
2259      if (!vobj)      if (!vobj) {
2260            *vp = JSVAL_VOID;
2261          return JS_TRUE;          return JS_TRUE;
2262        }
2263    
2264      aobj = JS_NewArrayObject(cx, 0, NULL);      aobj = JS_NewArrayObject(cx, 0, NULL);
2265      if (!aobj)      if (!aobj)
# Line 2005  Line 2270 
2270      if (!ok)      if (!ok)
2271          return JS_FALSE;          return JS_FALSE;
2272      pd = pda.array;      pd = pda.array;
2273      for (i = 0; i < pda.length; i++) {      for (i = 0; i < pda.length; i++, pd++) {
2274          pdobj = JS_NewObject(cx, NULL, NULL, NULL);          pdobj = JS_NewObject(cx, NULL, NULL, NULL);
2275          if (!pdobj) {          if (!pdobj) {
2276              ok = JS_FALSE;              ok = JS_FALSE;
# Line 2341  Line 2606 
2606      return cpx->isInner ? cpx->outer : obj;      return cpx->isInner ? cpx->outer : obj;
2607  }  }
2608    
2609    static JSBool
2610    split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
2611    
2612  static JSObject *  static JSObject *
2613  split_innerObject(JSContext *cx, JSObject *obj)  split_innerObject(JSContext *cx, JSObject *obj)
2614  {  {
# Line 2365  Line 2633 
2633      JS_ConvertStub, split_finalize,      JS_ConvertStub, split_finalize,
2634      NULL, NULL, NULL, NULL, NULL, NULL,      NULL, NULL, NULL, NULL, NULL, NULL,
2635      split_mark, NULL},      split_mark, NULL},
2636      NULL, split_outerObject, split_innerObject,      split_equality, split_outerObject, split_innerObject,
2637      NULL, NULL, NULL, NULL, NULL      NULL, NULL, NULL, NULL, NULL
2638  };  };
2639    
2640    static JSBool
2641    split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
2642    {
2643        *bp = JS_FALSE;
2644        if (JSVAL_IS_PRIMITIVE(v))
2645            return JS_TRUE;
2646    
2647        JSObject *obj2 = JSVAL_TO_OBJECT(v);
2648        if (JS_GET_CLASS(cx, obj2) != &split_global_class.base)
2649            return JS_TRUE;
2650    
2651        ComplexObject *cpx = (ComplexObject *) JS_GetPrivate(cx, obj2);
2652        JS_ASSERT(!cpx->isInner);
2653    
2654        ComplexObject *ourCpx = (ComplexObject *) JS_GetPrivate(cx, obj);
2655        JS_ASSERT(!ourCpx->isInner);
2656    
2657        *bp = (cpx == ourCpx);
2658        return JS_TRUE;
2659    }
2660    
2661  JSObject *  JSObject *
2662  split_create_outer(JSContext *cx)  split_create_outer(JSContext *cx)
2663  {  {
# Line 2497  Line 2786 
2786      if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))      if (!JS_ConvertArguments(cx, argc, argv, "S / o", &str, &sobj))
2787          return JS_FALSE;          return JS_FALSE;
2788    
2789      scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize);      WITH_LOCKED_CONTEXT_LIST(
2790            scx = JS_NewContext(JS_GetRuntime(cx), gStackChunkSize)
2791        );
2792      if (!scx) {      if (!scx) {
2793          JS_ReportOutOfMemory(cx);          JS_ReportOutOfMemory(cx);
2794          return JS_FALSE;          return JS_FALSE;
2795      }      }
2796        JS_SetOptions(scx, JS_GetOptions(cx));
2797    
 #ifdef JS_THREADSAFE  
2798      JS_BeginRequest(scx);      JS_BeginRequest(scx);
 #endif  
2799      src = JS_GetStringChars(str);      src = JS_GetStringChars(str);
2800      srclen = JS_GetStringLength(str);      srclen = JS_GetStringLength(str);
2801      lazy = JS_FALSE;      lazy = JS_FALSE;
# Line 2548  Line 2838 
2838      }      }
2839    
2840  out:  out:
 #ifdef JS_THREADSAFE  
2841      JS_EndRequest(scx);      JS_EndRequest(scx);
2842  #endif      WITH_LOCKED_CONTEXT_LIST(
2843      JS_DestroyContext(scx);          JS_DestroyContextNoGC(scx)
2844        );
2845      return ok;      return ok;
2846  }  }
2847    
 static int32 JS_FASTCALL  
 ShapeOf_tn(JSObject *obj)  
 {  
     if (!obj)  
         return 0;  
     if (!OBJ_IS_NATIVE(obj))  
         return -1;  
     return OBJ_SHAPE(obj);  
 }  
   
2848  static JSBool  static JSBool
2849  ShapeOf(JSContext *cx, uintN argc, jsval *vp)  ShapeOf(JSContext *cx, uintN argc, jsval *vp)
2850  {  {
# Line 2573  Line 2853 
2853          JS_ReportError(cx, "shapeOf: object expected");          JS_ReportError(cx, "shapeOf: object expected");
2854          return JS_FALSE;          return JS_FALSE;
2855      }      }
2856      return JS_NewNumberValue(cx, ShapeOf_tn(JSVAL_TO_OBJECT(v)), vp);      JSObject *obj = JSVAL_TO_OBJECT(v);
2857        if (!obj) {
2858            *vp = JSVAL_ZERO;
2859            return JS_TRUE;
2860        }
2861        if (!OBJ_IS_NATIVE(obj)) {
2862            *vp = INT_TO_JSVAL(-1);
2863            return JS_TRUE;
2864        }
2865        return JS_NewNumberValue(cx, OBJ_SHAPE(obj), vp);
2866  }  }
2867    
2868  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
2869    
2870    /*
2871     * Check that t1 comes strictly before t2. The function correctly deals with
2872     * PRIntervalTime wrap-around between t2 and t1 assuming that t2 and t1 stays
2873     * within INT32_MAX from each other. We use MAX_TIMEOUT_INTERVAL to enforce
2874     * this restriction.
2875     */
2876    static bool
2877    IsBefore(PRIntervalTime t1, PRIntervalTime t2)
2878    {
2879        return int32(t1 - t2) < 0;
2880    }
2881    
2882  static JSBool  static JSBool
2883  Sleep_fn(JSContext *cx, uintN argc, jsval *vp)  Sleep_fn(JSContext *cx, uintN argc, jsval *vp)
2884  {  {
2885      jsdouble t_secs;      PRIntervalTime t_ticks;
     PRUint32 t_ticks;  
     jsrefcount rc;  
2886    
2887      if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))      if (argc == 0) {
2888          return JS_FALSE;          t_ticks = 0;
2889        } else {
2890            jsdouble t_secs;
2891    
2892      if (t_secs < 0 || JSDOUBLE_IS_NaN(t_secs))          if (!JS_ValueToNumber(cx, argc == 0 ? JSVAL_VOID : vp[2], &t_secs))
2893          t_secs = 0;              return JS_FALSE;
2894    
2895      rc = JS_SuspendRequest(cx);          /* NB: The next condition also filter out NaNs. */
2896      t_ticks = (PRUint32)(PR_TicksPerSecond() * t_secs);          if (!(t_secs <= MAX_TIMEOUT_INTERVAL)) {
2897      if (PR_Sleep(t_ticks) == PR_SUCCESS)              JS_ReportError(cx, "Excessive sleep interval");
2898          *vp = JSVAL_TRUE;              return JS_FALSE;
2899      else          }
2900          *vp = JSVAL_FALSE;          t_ticks = (t_secs <= 0.0)
2901      JS_ResumeRequest(cx, rc);                    ? 0
2902      return JS_TRUE;                    : PRIntervalTime(PR_TicksPerSecond() * t_secs);
2903        }
2904        if (t_ticks == 0) {
2905            JS_YieldRequest(cx);
2906        } else {
2907            jsrefcount rc = JS_SuspendRequest(cx);
2908            PR_Lock(gWatchdogLock);
2909            PRIntervalTime to_wakeup = PR_IntervalNow() + t_ticks;
2910            for (;;) {
2911                PR_WaitCondVar(gSleepWakeup, t_ticks);
2912                if (gCanceled)
2913                    break;
2914                PRIntervalTime now = PR_IntervalNow();
2915                if (!IsBefore(now, to_wakeup))
2916                    break;
2917                t_ticks = to_wakeup - now;
2918            }
2919            PR_Unlock(gWatchdogLock);
2920            JS_ResumeRequest(cx, rc);
2921        }
2922        return !gCanceled;
2923  }  }
2924    
2925  typedef struct ScatterThreadData ScatterThreadData;  typedef struct ScatterThreadData ScatterThreadData;
# Line 2641  Line 2962 
2962  static void  static void
2963  RunScatterThread(void *arg)  RunScatterThread(void *arg)
2964  {  {
2965        int stackDummy;
2966      ScatterThreadData *td;      ScatterThreadData *td;
2967      ScatterStatus st;      ScatterStatus st;
2968      JSContext *cx;      JSContext *cx;
2969    
2970        if (PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy))
2971            return;
2972    
2973      td = (ScatterThreadData *)arg;      td = (ScatterThreadData *)arg;
2974      cx = td->cx;      cx = td->cx;
2975    
2976      /* Wait for go signal. */      /* Wait for our signal. */
2977      PR_Lock(td->shared->lock);      PR_Lock(td->shared->lock);
2978      while ((st = td->shared->status) == SCATTER_WAIT)      while ((st = td->shared->status) == SCATTER_WAIT)
2979          PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);          PR_WaitCondVar(td->shared->cvar, PR_INTERVAL_NO_TIMEOUT);
# Line 2657  Line 2982 
2982      if (st == SCATTER_CANCEL)      if (st == SCATTER_CANCEL)
2983          return;          return;
2984    
2985      /* We are go. */      /* We are good to go. */
2986      JS_SetContextThread(cx);      JS_SetContextThread(cx);
2987      JS_SetThreadStackLimit(cx, 0);      SetThreadStackLimit(cx);
2988      JS_BeginRequest(cx);      JS_BeginRequest(cx);
2989      DoScatteredWork(cx, td);      DoScatteredWork(cx, td);
2990      JS_EndRequest(cx);      JS_EndRequest(cx);
# Line 2684  Line 3009 
3009      JSBool ok;      JSBool ok;
3010      jsrefcount rc;      jsrefcount rc;
3011    
     if (!gEnableBranchCallback) {  
         /* Enable the branch callback, for periodic scope-sharing. */  
         gEnableBranchCallback = JS_TRUE;  
         JS_SetBranchCallback(cx, my_BranchCallback);  
         JS_ToggleOptions(cx, JSOPTION_NATIVE_BRANCH_CALLBACK);  
     }  
   
3012      sd.lock = NULL;      sd.lock = NULL;
3013      sd.cvar = NULL;      sd.cvar = NULL;
3014      sd.results = NULL;      sd.results = NULL;
# Line 2757  Line 3075 
3075      }      }
3076    
3077      for (i = 1; i < n; i++) {      for (i = 1; i < n; i++) {
3078          JSContext *newcx = JS_NewContext(JS_GetRuntime(cx), 8192);          JSContext *newcx;
3079            WITH_LOCKED_CONTEXT_LIST(
3080                newcx = JS_NewContext(JS_GetRuntime(cx), 8192)
3081            );
3082          if (!newcx)          if (!newcx)
3083              goto fail;              goto fail;
3084          JS_SetGlobalObject(newcx, JS_GetGlobalObject(cx));          JS_SetGlobalObject(newcx, JS_GetGlobalObject(cx));
# Line 2815  Line 3136 
3136              acx = sd.threads[i].cx;              acx = sd.threads[i].cx;
3137              if (acx) {              if (acx) {
3138                  JS_SetContextThread(acx);                  JS_SetContextThread(acx);
3139                  JS_DestroyContext(acx);                  WITH_LOCKED_CONTEXT_LIST(
3140                        JS_DestroyContext(acx)
3141                    );
3142              }              }
3143          }          }
3144          free(sd.threads);          free(sd.threads);
# Line 2837  Line 3160 
3160      goto out;      goto out;
3161  }  }
3162    
3163    static bool
3164    InitWatchdog(JSRuntime *rt)
3165    {
3166        JS_ASSERT(!gWatchdogThread);
3167        gWatchdogLock = PR_NewLock();
3168        if (gWatchdogLock) {
3169            gWatchdogWakeup = PR_NewCondVar(gWatchdogLock);
3170            if (gWatchdogWakeup) {
3171                gSleepWakeup = PR_NewCondVar(gWatchdogLock);
3172                if (gSleepWakeup)
3173                    return true;
3174                PR_DestroyCondVar(gWatchdogWakeup);
3175            }
3176            PR_DestroyLock(gWatchdogLock);
3177        }
3178        return false;
3179    }
3180    
3181    static void
3182    KillWatchdog()
3183    {
3184        PRThread *thread;
3185    
3186        PR_Lock(gWatchdogLock);
3187        thread = gWatchdogThread;
3188        if (thread) {
3189            /*
3190             * The watchdog thread is running, tell it to terminate waking it up
3191             * if necessary.
3192             */
3193            gWatchdogThread = NULL;
3194            PR_NotifyCondVar(gWatchdogWakeup);
3195        }
3196        PR_Unlock(gWatchdogLock);
3197        if (thread)
3198            PR_JoinThread(thread);
3199        PR_DestroyCondVar(gSleepWakeup);
3200        PR_DestroyCondVar(gWatchdogWakeup);
3201        PR_DestroyLock(gWatchdogLock);
3202    }
3203    
3204    static void
3205    WatchdogMain(void *arg)
3206    {
3207        JSRuntime *rt = (JSRuntime *) arg;
3208    
3209        PR_Lock(gWatchdogLock);
3210        while (gWatchdogThread) {
3211            PRIntervalTime now = PR_IntervalNow();
3212             if (gWatchdogHasTimeout && !IsBefore(now, gWatchdogTimeout)) {
3213                /*
3214                 * The timeout has just expired. Trigger the operation callback
3215                 * outside the lock.
3216                 */
3217                gWatchdogHasTimeout = false;
3218                PR_Unlock(gWatchdogLock);
3219                CancelExecution(rt);
3220                PR_Lock(gWatchdogLock);
3221    
3222                /* Wake up any threads doing sleep. */
3223                PR_NotifyAllCondVar(gSleepWakeup);
3224            } else {
3225                PRIntervalTime sleepDuration = gWatchdogHasTimeout
3226                                               ? gWatchdogTimeout - now
3227                                               : PR_INTERVAL_NO_TIMEOUT;
3228    #ifdef DEBUG
3229                PRStatus status =
3230  #endif  #endif
3231                    PR_WaitCondVar(gWatchdogWakeup, sleepDuration);
3232                JS_ASSERT(status == PR_SUCCESS);
3233            }
3234        }
3235        PR_Unlock(gWatchdogLock);
3236    }
3237    
3238  JS_DEFINE_TRCINFO_1(Print, (2, (static, JSVAL_FAIL, Print_tn, CONTEXT, STRING, 0, 0)))  static bool
3239  JS_DEFINE_TRCINFO_1(ShapeOf, (1, (static, INT32, ShapeOf_tn, OBJECT, 0, 0)))  ScheduleWatchdog(JSRuntime *rt, jsdouble t)
3240    {
3241        if (t <= 0) {
3242            PR_Lock(gWatchdogLock);
3243            gWatchdogHasTimeout = false;
3244            PR_Unlock(gWatchdogLock);
3245            return true;
3246        }
3247    
3248        PRIntervalTime interval = PRIntervalTime(ceil(t * PR_TicksPerSecond()));
3249        PRIntervalTime timeout = PR_IntervalNow() + interval;
3250        PR_Lock(gWatchdogLock);
3251        if (!gWatchdogThread) {
3252            JS_ASSERT(!gWatchdogHasTimeout);
3253            gWatchdogThread = PR_CreateThread(PR_USER_THREAD,
3254                                              WatchdogMain,
3255                                              rt,
3256                                              PR_PRIORITY_NORMAL,
3257                                              PR_LOCAL_THREAD,
3258                                              PR_JOINABLE_THREAD,
3259                                              0);
3260            if (!gWatchdogThread) {
3261                PR_Unlock(gWatchdogLock);
3262                return false;
3263            }
3264        } else if (!gWatchdogHasTimeout || IsBefore(timeout, gWatchdogTimeout)) {
3265             PR_NotifyCondVar(gWatchdogWakeup);
3266        }
3267        gWatchdogHasTimeout = true;
3268        gWatchdogTimeout = timeout;
3269        PR_Unlock(gWatchdogLock);
3270        return true;
3271    }
3272    
3273    #else /* !JS_THREADSAFE */
3274    
3275    #ifdef XP_WIN
3276    static HANDLE gTimerHandle = 0;
3277    
3278    VOID CALLBACK
3279    TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
3280    {
3281        CancelExecution((JSRuntime *) lpParameter);
3282    }
3283    
3284    #else
3285    
3286    static void
3287    AlarmHandler(int sig)
3288    {
3289        CancelExecution(gRuntime);
3290    }
3291    
3292    #endif
3293    
3294    static bool
3295    InitWatchdog(JSRuntime *rt)
3296    {
3297        gRuntime = rt;
3298        return true;
3299    }
3300    
3301    static void
3302    KillWatchdog()
3303    {
3304        ScheduleWatchdog(gRuntime, -1);
3305    }
3306    
3307    static bool
3308    ScheduleWatchdog(JSRuntime *rt, jsdouble t)
3309    {
3310    #ifdef XP_WIN
3311        if (gTimerHandle) {
3312            DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
3313            gTimerHandle = 0;
3314        }
3315        if (t > 0 &&
3316            !CreateTimerQueueTimer(&gTimerHandle,
3317                                   NULL,
3318                                   (WAITORTIMERCALLBACK)TimerCallback,
3319                                   rt,
3320                                   DWORD(ceil(t * 1000.0)),
3321                                   0,
3322                                   WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE)) {
3323            gTimerHandle = 0;
3324            return false;
3325        }
3326    #else
3327        /* FIXME: use setitimer when available for sub-second resolution. */
3328        if (t <= 0) {
3329            alarm(0);
3330            signal(SIGALRM, NULL);
3331        } else {
3332            signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
3333            alarm(ceil(t));
3334        }
3335    #endif
3336        return true;
3337    }
3338    
3339    #endif /* !JS_THREADSAFE */
3340    
3341    static void
3342    CancelExecution(JSRuntime *rt)
3343    {
3344        gCanceled = true;
3345        if (gExitCode == 0)
3346            gExitCode = EXITCODE_TIMEOUT;
3347        JS_TriggerAllOperationCallbacks(rt);
3348    
3349        static const char msg[] = "Script runs for too long, terminating.\n";
3350    #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
3351        /* It is not safe to call fputs from signals. */
3352        write(2, msg, sizeof(msg) - 1);
3353    #else
3354        fputs(msg, stderr);
3355    #endif
3356    }
3357    
3358    static JSBool
3359    SetTimeoutValue(JSContext *cx, jsdouble t)
3360    {
3361        /* NB: The next condition also filter out NaNs. */
3362        if (!(t <= MAX_TIMEOUT_INTERVAL)) {
3363            JS_ReportError(cx, "Excessive timeout value");
3364            return JS_FALSE;
3365        }
3366        gTimeoutInterval = t;
3367        if (!ScheduleWatchdog(cx->runtime, t)) {
3368            JS_ReportError(cx, "Failed to create the watchdog");
3369            return JS_FALSE;
3370        }
3371        return JS_TRUE;
3372    }
3373    
3374    static JSBool
3375    Timeout(JSContext *cx, uintN argc, jsval *vp)
3376    {
3377        if (argc == 0)
3378            return JS_NewNumberValue(cx, gTimeoutInterval, vp);
3379    
3380        if (argc > 1) {
3381            JS_ReportError(cx, "Wrong number of arguments");
3382            return JS_FALSE;
3383        }
3384    
3385        jsdouble t;
3386        if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
3387            return JS_FALSE;
3388    
3389        *vp = JSVAL_VOID;
3390        return SetTimeoutValue(cx, t);
3391    }
3392    
3393    static JSBool
3394    Elapsed(JSContext *cx, uintN argc, jsval *vp)
3395    {
3396        if (argc == 0) {
3397            double d = 0.0;
3398            JSShellContextData *data = GetContextData(cx);
3399            if (data)
3400                d = js_IntervalNow() - data->startTime;
3401            return JS_NewNumberValue(cx, d, vp);
3402        }
3403        JS_ReportError(cx, "Wrong number of arguments");
3404        return JS_FALSE;
3405    }
3406    
3407    #ifdef XP_UNIX
3408    
3409    #include <fcntl.h>
3410    #include <sys/stat.h>
3411    
3412    /*
3413     * Returns a JS_malloc'd string (that the caller needs to JS_free)
3414     * containing the directory (non-leaf) part of |from| prepended to |leaf|.
3415     * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.
3416     * Returns NULL to indicate an error.
3417     */
3418    static char *
3419    MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)
3420    {
3421        size_t dirlen;
3422        char *dir;
3423        const char *slash = NULL, *cp;
3424    
3425        cp = from;
3426        while (*cp) {
3427            if (*cp == '/'
3428    #ifdef XP_WIN
3429                || *cp == '\\'
3430    #endif
3431               ) {
3432                slash = cp;
3433            }
3434    
3435            ++cp;
3436        }
3437    
3438        if (!slash) {
3439            /* We were given a leaf or |from| was empty. */
3440            return JS_strdup(cx, leaf);
3441        }
3442    
3443        /* Else, we were given a real pathname, return that + the leaf. */
3444        dirlen = slash - from + 1;
3445        dir = (char*) JS_malloc(cx, dirlen + strlen(leaf) + 1);
3446        if (!dir)
3447            return NULL;
3448    
3449        strncpy(dir, from, dirlen);
3450        strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */
3451    
3452        return dir;
3453    }
3454    
3455    #endif // XP_UNIX
3456    
3457    static JSBool
3458    Snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3459    {
3460        JSString *str;
3461        const char *filename;
3462        const char *pathname;
3463        JSStackFrame *fp;
3464        JSBool ok;
3465        size_t cc, len;
3466        char *buf;
3467        FILE *file;
3468    
3469        str = JS_ValueToString(cx, argv[0]);
3470        if (!str)
3471            return JS_FALSE;
3472        filename = JS_GetStringBytes(str);
3473    
3474        /* Get the currently executing script's name. */
3475        fp = JS_GetScriptedCaller(cx, NULL);
3476        JS_ASSERT(fp && fp->script->filename);
3477    #ifdef XP_UNIX
3478        pathname = MakeAbsolutePathname(cx, fp->script->filename, filename);
3479        if (!pathname)
3480            return JS_FALSE;
3481    #else
3482        pathname = filename;
3483    #endif
3484    
3485        ok = JS_FALSE;
3486        len = 0;
3487        buf = NULL;
3488        file = fopen(pathname, "rb");
3489        if (!file) {
3490            JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));
3491        } else {
3492            if (fseek(file, 0, SEEK_END) == EOF) {
3493                JS_ReportError(cx, "can't seek end of %s", pathname);
3494            } else {
3495                len = ftell(file);
3496                if (fseek(file, 0, SEEK_SET) == EOF) {
3497                    JS_ReportError(cx, "can't seek start of %s", pathname);
3498                } else {
3499                    buf = (char*) JS_malloc(cx, len + 1);
3500                    if (buf) {
3501                        cc = fread(buf, 1, len, file);
3502                        if (cc != len) {
3503                            JS_free(cx, buf);
3504                            JS_ReportError(cx, "can't read %s: %s", pathname,
3505                                           (ptrdiff_t(cc) < 0) ? strerror(errno) : "short read");
3506                        } else {
3507                            len = (size_t)cc;
3508                            ok = JS_TRUE;
3509                        }
3510                    }
3511                }
3512            }
3513            fclose(file);
3514        }
3515        JS_free(cx, (void*)pathname);
3516        if (!ok) {
3517            JS_free(cx, buf);
3518            return ok;
3519        }
3520    
3521        buf[len] = '\0';
3522        str = JS_NewString(cx, buf, len);
3523        if (!str) {
3524            JS_free(cx, buf);
3525            return JS_FALSE;
3526        }
3527        *rval = STRING_TO_JSVAL(str);
3528        return JS_TRUE;
3529    }
3530    
3531  /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */  /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
3532  static JSFunctionSpec shell_functions[] = {  static JSFunctionSpec shell_functions[] = {
# Line 2848  Line 3534 
3534      JS_FS("options",        Options,        0,0,0),      JS_FS("options",        Options,        0,0,0),
3535      JS_FS("load",           Load,           1,0,0),      JS_FS("load",           Load,           1,0,0),
3536      JS_FN("readline",       ReadLine,       0,0),      JS_FN("readline",       ReadLine,       0,0),
3537      JS_TN("print",          Print,          0,0, Print_trcinfo),      JS_FN("print",          Print,          0,0),
3538      JS_FS("help",           Help,           0,0,0),      JS_FS("help",           Help,           0,0,0),
3539      JS_FS("quit",           Quit,           0,0,0),      JS_FS("quit",           Quit,           0,0,0),
3540        JS_FN("assertEq",       AssertEq,       2,0),
3541      JS_FN("gc",             GC,             0,0),      JS_FN("gc",             GC,             0,0),
3542      JS_FN("gcparam",        GCParameter,    2,0),      JS_FN("gcparam",        GCParameter,    2,0),
3543      JS_FN("countHeap",      CountHeap,      0,0),      JS_FN("countHeap",      CountHeap,      0,0),
# Line 2886  Line 3573 
3573      JS_FN("getslx",         GetSLX,         1,0),      JS_FN("getslx",         GetSLX,         1,0),
3574      JS_FN("toint32",        ToInt32,        1,0),      JS_FN("toint32",        ToInt32,        1,0),
3575      JS_FS("evalcx",         EvalInContext,  1,0,0),      JS_FS("evalcx",         EvalInContext,  1,0,0),
3576      JS_TN("shapeOf",        ShapeOf,        1,0, ShapeOf_trcinfo),      JS_FN("shapeOf",        ShapeOf,        1,0),
3577  #ifdef MOZ_SHARK  #ifdef MOZ_SHARK
3578      JS_FS("startShark",      js_StartShark,      0,0,0),      JS_FS("startShark",     js_StartShark,      0,0,0),
3579      JS_FS("stopShark",       js_StopShark,       0,0,0),      JS_FS("stopShark",      js_StopShark,       0,0,0),
3580      JS_FS("connectShark",    js_ConnectShark,    0,0,0),      JS_FS("connectShark",   js_ConnectShark,    0,0,0),
3581      JS_FS("disconnectShark", js_DisconnectShark, 0,0,0),      JS_FS("disconnectShark",js_DisconnectShark, 0,0,0),
3582  #endif  #endif
3583  #ifdef MOZ_CALLGRIND  #ifdef MOZ_CALLGRIND
3584      JS_FS("startCallgrind",  js_StartCallgrind,  0,0,0),      JS_FS("startCallgrind", js_StartCallgrind,  0,0,0),
3585      JS_FS("stopCallgrind",   js_StopCallgrind,   0,0,0),      JS_FS("stopCallgrind",  js_StopCallgrind,   0,0,0),
3586      JS_FS("dumpCallgrind",   js_DumpCallgrind,   1,0,0),      JS_FS("dumpCallgrind",  js_DumpCallgrind,   1,0,0),
3587  #endif  #endif
3588  #ifdef MOZ_VTUNE  #ifdef MOZ_VTUNE
3589      JS_FS("startVtune",      js_StartVtune,    1,0,0),      JS_FS("startVtune",     js_StartVtune,  1,0,0),
3590      JS_FS("stopVtune",       js_StopVtune,     0,0,0),      JS_FS("stopVtune",      js_StopVtune,   0,0,0),
3591      JS_FS("pauseVtune",      js_PauseVtune,    0,0,0),      JS_FS("pauseVtune",     js_PauseVtune,  0,0,0),
3592      JS_FS("resumeVtune",     js_ResumeVtune,   0,0,0),      JS_FS("resumeVtune",    js_ResumeVtune, 0,0,0),
3593  #endif  #endif
3594  #ifdef DEBUG_ARRAYS  #ifdef DEBUG_ARRAYS
3595      JS_FS("arrayInfo",       js_ArrayInfo,       1,0,0),      JS_FS("arrayInfo",      js_ArrayInfo,   1,0,0),
3596  #endif  #endif
3597  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
3598      JS_FN("sleep",          Sleep_fn,       1,0),      JS_FN("sleep",          Sleep_fn,       1,0),
3599      JS_FN("scatter",        Scatter,        1,0),      JS_FN("scatter",        Scatter,        1,0),
3600  #endif  #endif
3601        JS_FS("snarf",          Snarf,        0,0,0),
3602        JS_FN("timeout",        Timeout,        1,0),
3603        JS_FN("elapsed",        Elapsed,        0,0),
3604      JS_FS_END      JS_FS_END
3605  };  };
3606    
# Line 2926  Line 3616 
3616  "print([exp ...])         Evaluate and print expressions",  "print([exp ...])         Evaluate and print expressions",
3617  "help([name ...])         Display usage and help messages",  "help([name ...])         Display usage and help messages",
3618  "quit()                   Quit the shell",  "quit()                   Quit the shell",
3619    "assertEq(actual, expected)\n"
3620    "                         Throw if the two arguments are not ===",
3621  "gc()                     Run the garbage collector",  "gc()                     Run the garbage collector",
3622  "gcparam(name, value)\n"  "gcparam(name, value)\n"
3623  "  Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"  "  Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n"
# Line 2953  Line 3645 
3645  "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"  "dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
3646  "  Interface to JS_DumpHeap with output sent to file",  "  Interface to JS_DumpHeap with output sent to file",
3647  "notes([fun])             Show source notes for functions",  "notes([fun])             Show source notes for functions",
3648  "tracing([toggle])        Turn tracing on or off",  "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
3649    "                         With filename, send to file.\n",
3650  "stats([string ...])      Dump 'arena', 'atom', 'global' stats",  "stats([string ...])      Dump 'arena', 'atom', 'global' stats",
3651  #endif  #endif
3652  #ifdef TEST_CVTARGS  #ifdef TEST_CVTARGS
# Line 2974  Line 3667 
3667  "shapeOf(obj)             Get the shape of obj (an implementation detail)",  "shapeOf(obj)             Get the shape of obj (an implementation detail)",
3668  #ifdef MOZ_SHARK  #ifdef MOZ_SHARK
3669  "startShark()             Start a Shark session.\n"  "startShark()             Start a Shark session.\n"
3670  "                         Shark must be running with programatic sampling.",  "                         Shark must be running with programatic sampling",
3671  "stopShark()              Stop a running Shark session.",  "stopShark()              Stop a running Shark session",
3672  "connectShark()           Connect to Shark.\n"  "connectShark()           Connect to Shark.\n"
3673  "                         The -k switch does this automatically.",  "                         The -k switch does this automatically",
3674  "disconnectShark()        Disconnect from Shark.",  "disconnectShark()        Disconnect from Shark",
3675  #endif  #endif
3676  #ifdef MOZ_CALLGRIND  #ifdef MOZ_CALLGRIND
3677  "startCallgrind()         Start callgrind instrumentation.\n",  "startCallgrind()         Start callgrind instrumentation",
3678  "stopCallgrind()          Stop callgrind instumentation.",  "stopCallgrind()          Stop callgrind instumentation",
3679  "dumpCallgrind([name])    Dump callgrind counters.\n",  "dumpCallgrind([name])    Dump callgrind counters",
3680  #endif  #endif
3681  #ifdef MOZ_VTUNE  #ifdef MOZ_VTUNE
3682  "startVtune([filename])   Start vtune instrumentation.\n",  "startVtune([filename])   Start vtune instrumentation",
3683  "stopVtune()              Stop vtune instumentation.",  "stopVtune()              Stop vtune instumentation",
3684  "pauseVtune()             Pause vtune collection.\n",  "pauseVtune()             Pause vtune collection",
3685  "resumeVtune()            Resume vtune collection.\n",  "resumeVtune()            Resume vtune collection",
3686  #endif  #endif
3687  #ifdef DEBUG_ARRAYS  #ifdef DEBUG_ARRAYS
3688  "arrayInfo(a1, a2, ...)   Report statistics about arrays.",  "arrayInfo(a1, a2, ...)   Report statistics about arrays",
3689  #endif  #endif
3690  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
3691  "sleep(dt)                Sleep for dt seconds",  "sleep(dt)                Sleep for dt seconds",
3692  "scatter(fns)             Call functions concurrently (ignoring errors)",  "scatter(fns)             Call functions concurrently (ignoring errors)",
3693  #endif  #endif
3694    "snarf(filename)          Read filename into returned string",
3695    "timeout([seconds])\n"
3696    "  Get/Set the limit in seconds for the execution time for the current context.\n"
3697    "  A negative value (default) means that the execution time is unlimited.",
3698    "elapsed()                Execution time elapsed for the current context.\n",
3699  };  };
3700    
3701  /* Help messages must match shell functions. */  /* Help messages must match shell functions. */
# Line 3116  Line 3814 
3814   * they're being called for tutorial purposes.   * they're being called for tutorial purposes.
3815   */   */
3816  enum its_tinyid {  enum its_tinyid {
3817      ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY      ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY,
3818        ITS_CUSTOM, ITS_CUSTOMRDONLY
3819  };  };
3820    
3821    static JSBool
3822    its_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3823    {
3824      jsval *val = (jsval *) JS_GetPrivate(cx, obj);
3825      *vp = val ? *val : JSVAL_VOID;
3826      return JS_TRUE;
3827    }
3828    
3829    static JSBool
3830    its_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
3831    {
3832      jsval *val = (jsval *) JS_GetPrivate(cx, obj);
3833      if (val) {
3834          *val = *vp;
3835          return JS_TRUE;
3836      }
3837    
3838      val = new jsval;
3839      if (!val) {
3840          JS_ReportOutOfMemory(cx);
3841          return JS_FALSE;
3842      }
3843    
3844      if (!JS_AddRoot(cx, val)) {
3845          delete val;
3846          return JS_FALSE;
3847      }
3848    
3849      if (!JS_SetPrivate(cx, obj, (void*)val)) {
3850          JS_RemoveRoot(cx, val);
3851          delete val;
3852          return JS_FALSE;
3853      }
3854    
3855      *val = *vp;
3856      return JS_TRUE;
3857    }
3858    
3859  static JSPropertySpec its_props[] = {  static JSPropertySpec its_props[] = {
3860      {"color",           ITS_COLOR,      JSPROP_ENUMERATE,       NULL, NULL},      {"color",           ITS_COLOR,      JSPROP_ENUMERATE,       NULL, NULL},
3861      {"height",          ITS_HEIGHT,     JSPROP_ENUMERATE,       NULL, NULL},      {"height",          ITS_HEIGHT,     JSPROP_ENUMERATE,       NULL, NULL},
# Line 3126  Line 3863 
3863      {"funny",           ITS_FUNNY,      JSPROP_ENUMERATE,       NULL, NULL},      {"funny",           ITS_FUNNY,      JSPROP_ENUMERATE,       NULL, NULL},
3864      {"array",           ITS_ARRAY,      JSPROP_ENUMERATE,       NULL, NULL},      {"array",           ITS_ARRAY,      JSPROP_ENUMERATE,       NULL, NULL},
3865      {"rdonly",          ITS_RDONLY,     JSPROP_READONLY,        NULL, NULL},      {"rdonly",          ITS_RDONLY,     JSPROP_READONLY,        NULL, NULL},
3866        {"custom",          ITS_CUSTOM,     JSPROP_ENUMERATE,
3867                            its_getter,     its_setter},
3868        {"customRdOnly",    ITS_CUSTOMRDONLY, JSPROP_ENUMERATE | JSPROP_READONLY,
3869                            its_getter,     its_setter},
3870      {NULL,0,0,NULL,NULL}      {NULL,0,0,NULL,NULL}
3871  };  };
3872    
# Line 3341  Line 4082 
4082  static void  static void
4083  its_finalize(JSContext *cx, JSObject *obj)  its_finalize(JSContext *cx, JSObject *obj)
4084  {  {
4085        jsval *rootedVal;
4086      if (its_noisy)      if (its_noisy)
4087          fprintf(gOutFile, "finalizing it\n");          fprintf(gOutFile, "finalizing it\n");
4088        rootedVal = (jsval *) JS_GetPrivate(cx, obj);
4089        if (rootedVal) {
4090          JS_RemoveRoot(cx, rootedVal);
4091          JS_SetPrivate(cx, obj, NULL);
4092          delete rootedVal;
4093        }
4094  }  }
4095    
4096  static JSClass its_class = {  static JSClass its_class = {
4097      "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,      "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
4098      its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,      its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
4099      (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,      (JSEnumerateOp)its_enumerate, (JSResolveOp)its_resolve,
4100      its_convert,      its_finalize,      its_convert,      its_finalize,
# Line 3579  Line 4327 
4327  }  }
4328    
4329  JSClass global_class = {  JSClass global_class = {
4330      "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,      "global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE,
4331      JS_PropertyStub,  JS_PropertyStub,      JS_PropertyStub,  JS_PropertyStub,
4332      JS_PropertyStub,  JS_PropertyStub,      JS_PropertyStub,  JS_PropertyStub,
4333      global_enumerate, (JSResolveOp) global_resolve,      global_enumerate, (JSResolveOp) global_resolve,
4334      JS_ConvertStub,   JS_FinalizeStub,      JS_ConvertStub,   its_finalize,
4335      JSCLASS_NO_OPTIONAL_MEMBERS      JSCLASS_NO_OPTIONAL_MEMBERS
4336  };  };
4337    
# Line 3761  Line 4509 
4509      return ok;      return ok;
4510  }  }
4511    
 #include <fcntl.h>  
 #include <sys/stat.h>  
   
 /*  
  * Returns a JS_malloc'd string (that the caller needs to JS_free)  
  * containing the directory (non-leaf) part of |from| prepended to |leaf|.  
  * If |from| is empty or a leaf, MakeAbsolutePathname returns a copy of leaf.  
  * Returns NULL to indicate an error.  
  */  
 static char *  
 MakeAbsolutePathname(JSContext *cx, const char *from, const char *leaf)  
 {  
     size_t dirlen;  
     char *dir;  
     const char *slash = NULL, *cp;  
   
     cp = from;  
     while (*cp) {  
         if (*cp == '/'  
 #ifdef XP_WIN  
             || *cp == '\\'  
 #endif  
            ) {  
             slash = cp;  
         }  
   
         ++cp;  
     }  
   
     if (!slash) {  
         /* We were given a leaf or |from| was empty. */  
         return JS_strdup(cx, leaf);  
     }  
   
     /* Else, we were given a real pathname, return that + the leaf. */  
     dirlen = slash - from + 1;  
     dir = (char*) JS_malloc(cx, dirlen + strlen(leaf) + 1);  
     if (!dir)  
         return NULL;  
   
     strncpy(dir, from, dirlen);  
     strcpy(dir + dirlen, leaf); /* Note: we can't use strcat here. */  
   
     return dir;  
 }  
   
 static JSBool  
 snarf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)  
 {  
     JSString *str;  
     const char *filename;  
     char *pathname;  
     JSStackFrame *fp;  
     JSBool ok;  
     off_t cc, len;  
     char *buf;  
     FILE *file;  
   
     str = JS_ValueToString(cx, argv[0]);  
     if (!str)  
         return JS_FALSE;  
     filename = JS_GetStringBytes(str);  
   
     /* Get the currently executing script's name. */  
     fp = JS_GetScriptedCaller(cx, NULL);  
     JS_ASSERT(fp && fp->script->filename);  
     pathname = MakeAbsolutePathname(cx, fp->script->filename, filename);  
     if (!pathname)  
         return JS_FALSE;  
   
     ok = JS_FALSE;  
     len = 0;  
     buf = NULL;  
     file = fopen(pathname, "rb");  
     if (!file) {  
         JS_ReportError(cx, "can't open %s: %s", pathname, strerror(errno));  
     } else {  
         if (fseek(file, 0, SEEK_END) == EOF) {  
             JS_ReportError(cx, "can't seek end of %s", pathname);  
         } else {  
             len = ftell(file);  
             if (len == -1 || fseek(file, 0, SEEK_SET) == EOF) {  
                 JS_ReportError(cx, "can't seek start of %s", pathname);  
             } else {  
                 buf = (char*) JS_malloc(cx, len + 1);  
                 if (buf) {  
                     cc = fread(buf, 1, len, file);  
                     if (cc != len) {  
                         JS_free(cx, buf);  
                         JS_ReportError(cx, "can't read %s: %s", pathname,  
                                        (cc < 0) ? strerror(errno)  
                                                 : "short read");  
                     } else {  
                         len = (size_t)cc;  
                         ok = JS_TRUE;  
                     }  
                 }  
             }  
         }  
         fclose(file);  
     }  
     JS_free(cx, pathname);  
     if (!ok) {  
         JS_free(cx, buf);  
         return ok;  
     }  
   
     buf[len] = '\0';  
     str = JS_NewString(cx, buf, len);  
     if (!str) {  
         JS_free(cx, buf);  
         return JS_FALSE;  
     }  
     *rval = STRING_TO_JSVAL(str);  
     return JS_TRUE;  
 }  
   
4512  #endif /* NARCISSUS */  #endif /* NARCISSUS */
4513    
4514  static JSBool  static JSBool
4515  ContextCallback(JSContext *cx, uintN contextOp)  ContextCallback(JSContext *cx, uintN contextOp)
4516  {  {
4517      if (contextOp == JSCONTEXT_NEW) {      JSShellContextData *data;
4518        
4519        switch (contextOp) {
4520          case JSCONTEXT_NEW: {
4521            data = NewContextData();
4522            if (!data)
4523                return JS_FALSE;
4524            JS_SetContextPrivate(cx, data);
4525          JS_SetErrorReporter(cx, my_ErrorReporter);          JS_SetErrorReporter(cx, my_ErrorReporter);
4526          JS_SetVersion(cx, JSVERSION_LATEST);          JS_SetVersion(cx, JSVERSION_LATEST);
4527          SetContextOptions(cx);          SetContextOptions(cx);
4528            break;
4529          case JSCONTEXT_DESTROY:
4530            data = GetContextData(cx);
4531            JS_SetContextPrivate(cx, NULL);
4532            free(data);
4533            break;
4534          }
4535    
4536          default:
4537            break;
4538      }      }
4539      return JS_TRUE;      return JS_TRUE;
4540  }  }
# Line 3916  Line 4564 
4564      CheckHelpMessages();      CheckHelpMessages();
4565      setlocale(LC_ALL, "");      setlocale(LC_ALL, "");
4566    
4567      gStackBase = (jsuword)&stackDummy;  #ifdef JS_THREADSAFE
4568        if (PR_FAILURE == PR_NewThreadPrivateIndex(&gStackBaseThreadIndex, NULL) ||
4569            PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy)) {
4570            return 1;
4571        }
4572    #else
4573        gStackBase = (jsuword) &stackDummy;
4574    #endif
4575    
4576  #ifdef XP_OS2  #ifdef XP_OS2
4577     /* these streams are normally line buffered on OS/2 and need a \n, *     /* these streams are normally line buffered on OS/2 and need a \n, *
# Line 3934  Line 4589 
4589      rt = JS_NewRuntime(64L * 1024L * 1024L);      rt = JS_NewRuntime(64L * 1024L * 1024L);
4590      if (!rt)      if (!rt)
4591          return 1;          return 1;
4592    
4593        if (!InitWatchdog(rt))
4594            return 1;
4595    
4596      JS_SetContextCallback(rt, ContextCallback);      JS_SetContextCallback(rt, ContextCallback);
4597    
4598      cx = JS_NewContext(rt, gStackChunkSize);      WITH_LOCKED_CONTEXT_LIST(
4599            cx = JS_NewContext(rt, gStackChunkSize)
4600        );
4601      if (!cx)      if (!cx)
4602          return 1;          return 1;
4603    
4604  #ifdef JS_THREADSAFE      JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
4605    
4606      JS_BeginRequest(cx);      JS_BeginRequest(cx);
 #endif  
4607    
4608      glob = JS_NewObject(cx, &global_class, NULL, NULL);      glob = JS_NewObject(cx, &global_class, NULL, NULL);
4609      if (!glob)      if (!glob)
# Line 3964  Line 4625 
4625      if (!JS_DefineFunctions(cx, it, its_methods))      if (!JS_DefineFunctions(cx, it, its_methods))
4626          return 1;          return 1;
4627    
4628        if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
4629                               its_setter, 0))
4630            return 1;
4631        if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
4632                               its_setter, JSPROP_READONLY))
4633            return 1;
4634    
4635  #ifdef JSDEBUGGER  #ifdef JSDEBUGGER
4636      /*      /*
4637      * XXX A command line option to enable debugging (or not) would be good      * XXX A command line option to enable debugging (or not) would be good
# Line 4010  Line 4678 
4678          jsval v;          jsval v;
4679          static const char Object_prototype[] = "Object.prototype";          static const char Object_prototype[] = "Object.prototype";
4680    
         if (!JS_DefineFunction(cx, glob, "snarf", snarf, 1, 0))  
             return 1;  
4681          if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0))          if (!JS_DefineFunction(cx, glob, "evaluate", Evaluate, 3, 0))
4682              return 1;              return 1;
4683    
# Line 4039  Line 4705 
4705      }      }
4706  #endif  /* JSDEBUGGER */  #endif  /* JSDEBUGGER */
4707    
 #ifdef JS_THREADSAFE  
4708      JS_EndRequest(cx);      JS_EndRequest(cx);
 #endif  
4709    
4710      JS_DestroyContext(cx);      WITH_LOCKED_CONTEXT_LIST(
4711            JS_DestroyContext(cx)
4712        );
4713    
4714        KillWatchdog();
4715    
4716      JS_DestroyRuntime(rt);      JS_DestroyRuntime(rt);
4717      JS_ShutDown();      JS_ShutDown();
4718      return result;      return result;

Legend:
Removed from v.459  
changed lines
  Added in v.460

  ViewVC Help
Powered by ViewVC 1.1.24