1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 /*
41  * JS execution context.
42  */
43 #include "jsstddef.h"
44 #include <stdarg.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsarena.h" /* Added by JSIFY */
49 #include "jsutil.h" /* Added by JSIFY */
50 #include "jsclist.h"
51 #include "jsprf.h"
52 #include "jsatom.h"
53 #include "jscntxt.h"
54 #include "jsconfig.h"
55 #include "jsdbgapi.h"
56 #include "jsexn.h"
57 #include "jsgc.h"
58 #include "jslock.h"
59 #include "jsnum.h"
60 #include "jsobj.h"
61 #include "jsopcode.h"
62 #include "jsscan.h"
63 #include "jsscript.h"
64 #include "jsstr.h"
65
66 JSContext *
67 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
68 0 {
69 0     JSContext *cx;
70 0     JSBool ok, first;
71
72 0     cx = (JSContext *) malloc(sizeof *cx);
73 0     if (!cx)
74 0         return NULL;
75 0     memset(cx, 0, sizeof *cx);
76
77 0     cx->runtime = rt;
78 #if JS_STACK_GROWTH_DIRECTION > 0
79     cx->stackLimit = (jsuword)-1;
80 #endif
81 #ifdef JS_THREADSAFE
82     js_InitContextForLocking(cx);
83 #endif
84
85 0     JS_LOCK_GC(rt);
86 0     for (;;) {
87 0         first = (rt->contextList.next == &rt->contextList);
88 0         if (rt->state == JSRTS_UP) {
89 0             JS_ASSERT(!first);
90 0             break;
91         }
92 0         if (rt->state == JSRTS_DOWN) {
93 0             JS_ASSERT(first);
94 0             rt->state = JSRTS_LAUNCHING;
95             break;
96         }
97 0         JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
98     }
99 0     JS_APPEND_LINK(&cx->links, &rt->contextList);
100 0     JS_UNLOCK_GC(rt);
101
102     /*
103      * First we do the infallible, every-time per-context initializations.
104      * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
105      * or the stuff under 'if (first)' below) fail, at least the version
106      * and arena-pools will be valid and safe to use (say, from the last GC
107      * done by js_DestroyContext).
108      */
109 0     cx->version = JSVERSION_DEFAULT;
110 0     cx->jsop_eq = JSOP_EQ;
111 0     cx->jsop_ne = JSOP_NE;
112 0     JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));
113 0     JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));
114
115 #if JS_HAS_REGEXPS
116 0     if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
117 0         js_DestroyContext(cx, JS_NO_GC);
118 0         return NULL;
119     }
120 #endif
121 #if JS_HAS_EXCEPTIONS
122 0     cx->throwing = JS_FALSE;
123 #endif
124
125     /*
126      * If cx is the first context on this runtime, initialize well-known atoms,
127      * keywords, numbers, and strings.  If one of these steps should fail, the
128      * runtime will be left in a partially initialized state, with zeroes and
129      * nulls stored in the default-initialized remainder of the struct.  We'll
130      * clean the runtime up under js_DestroyContext, because cx will be "last"
131      * as well as "first".
132      */
133 0     if (first) {
134 0         ok = (rt->atomState.liveAtoms == 0)
135              ? js_InitAtomState(cx, &rt->atomState)
136              : js_InitPinnedAtoms(cx, &rt->atomState);
137 0         if (ok)
138 0             ok = js_InitScanner(cx);
139 0         if (ok)
140 0             ok = js_InitRuntimeNumberState(cx);
141 0         if (ok)
142 0             ok = js_InitRuntimeScriptState(cx);
143 0         if (ok)
144 0             ok = js_InitRuntimeStringState(cx);
145 0         if (!ok) {
146 0             js_DestroyContext(cx, JS_NO_GC);
147 0             return NULL;
148         }
149
150 0         JS_LOCK_GC(rt);
151 0         rt->state = JSRTS_UP;
152 0         JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
153 0         JS_UNLOCK_GC(rt);
154     }
155
156 0     return cx;
157 }
158
159 void
160 js_DestroyContext(JSContext *cx, JSGCMode gcmode)
161 0 {
162 0     JSRuntime *rt;
163 0     JSBool last;
164 0     JSArgumentFormatMap *map;
165
166 0     rt = cx->runtime;
167
168     /* Remove cx from context list first. */
169 0     JS_LOCK_GC(rt);
170 0     JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
171 0     JS_REMOVE_LINK(&cx->links);
172 0     last = (rt->contextList.next == &rt->contextList);
173 0     if (last)
174 0         rt->state = JSRTS_LANDING;
175 0     JS_UNLOCK_GC(rt);
176
177 0     if (last) {
178 #ifdef JS_THREADSAFE
179         /*
180          * If cx is not in a request already, begin one now so that we wait
181          * for any racing GC started on a not-last context to finish, before
182          * we plow ahead and unpin atoms.  Note that even though we begin a
183          * request here if necessary, we end all requests on cx below before
184          * forcing a final GC.  This lets any not-last context destruction
185          * racing in another thread try to force or maybe run the GC, but by
186          * that point, rt->state will not be JSRTS_UP, and that GC attempt
187          * will return early.
188          */
189         if (cx->requestDepth == 0)
190             JS_BeginRequest(cx);
191 #endif
192
193         /* Unpin all pinned atoms before final GC. */
194 0         js_UnpinPinnedAtoms(&rt->atomState);
195
196         /* Unlock and clear GC things held by runtime pointers. */
197 0         js_FinishRuntimeNumberState(cx);
198 0         js_FinishRuntimeStringState(cx);
199
200         /* Clear debugging state to remove GC roots. */
201 0         JS_ClearAllTraps(cx);
202 0         JS_ClearAllWatchPoints(cx);
203     }
204
205 #if JS_HAS_REGEXPS
206     /*
207      * Remove more GC roots in regExpStatics, then collect garbage.
208      * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
209      * XXX this function call to wait for any racing GC to complete, in the
210      * XXX case where JS_DestroyContext is called outside of a request on cx
211      */
212 0     js_FreeRegExpStatics(cx, &cx->regExpStatics);
213 #endif
214
215 #ifdef JS_THREADSAFE
216     /*
217      * Destroying a context implicitly calls JS_EndRequest().  Also, we must
218      * end our request here in case we are "last" -- in that event, another
219      * js_DestroyContext that was not last might be waiting in the GC for our
220      * request to end.  We'll let it run below, just before we do the truly
221      * final GC and then free atom state.
222      *
223      * At this point, cx must be inaccessible to other threads.  It's off the
224      * rt->contextList, and it should not be reachable via any object private
225      * data structure.
226      */
227     while (cx->requestDepth != 0)
228         JS_EndRequest(cx);
229 #endif
230
231 0     if (last) {
232         /* Always force, so we wait for any racing GC to finish. */
233 0         js_ForceGC(cx, GC_LAST_CONTEXT);
234
235         /* Iterate until no finalizer removes a GC root or lock. */
236 0         while (rt->gcPoke)
237 0             js_GC(cx, GC_LAST_CONTEXT);
238
239         /* Try to free atom state, now that no unrooted scripts survive. */
240 0         if (rt->atomState.liveAtoms == 0)
241 0             js_FreeAtomState(cx, &rt->atomState);
242
243         /* Now after the last GC can we free the script filename table. */
244 0         js_FinishRuntimeScriptState(cx);
245
246         /* Take the runtime down, now that it has no contexts or atoms. */
247 0         JS_LOCK_GC(rt);
248 0         rt->state = JSRTS_DOWN;
249 0         JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
250 0         JS_UNLOCK_GC(rt);
251     } else {
252 0         if (gcmode == JS_FORCE_GC)
253 0             js_ForceGC(cx, 0);
254 0         else if (gcmode == JS_MAYBE_GC)
255 0             JS_MaybeGC(cx);
256     }
257
258     /* Free the stuff hanging off of cx. */
259 0     JS_FinishArenaPool(&cx->stackPool);
260 0     JS_FinishArenaPool(&cx->tempPool);
261 0     if (cx->lastMessage)
262 0         free(cx->lastMessage);
263
264     /* Remove any argument formatters. */
265 0     map = cx->argumentFormatMap;
266 0     while (map) {
267 0         JSArgumentFormatMap *temp = map;
268 0         map = map->next;
269 0         JS_free(cx, temp);
270     }
271
272     /* Destroy the resolve recursion damper. */
273 0     if (cx->resolvingTable) {
274 0         JS_DHashTableDestroy(cx->resolvingTable);
275 0         cx->resolvingTable = NULL;
276     }
277
278     /* Finally, free cx itself. */
279 0     free(cx);
280 }
281
282 JSBool
283 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
284 0 {
285 0     JSCList *cl;
286
287 0     for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
288 0         if (cl == &cx->links)
289 0             return JS_TRUE;
290     }
291     JS_RUNTIME_METER(rt, deadContexts);
292 0     return JS_FALSE;
293 }
294
295 JSContext *
296 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
297 0 {
298 0     JSContext *cx = *iterp;
299
300 0     if (unlocked)
301 0         JS_LOCK_GC(rt);
302 0     if (!cx)
303 0         cx = (JSContext *)&rt->contextList;
304 0     cx = (JSContext *)cx->links.next;
305 0     if (&cx->links == &rt->contextList)
306 0         cx = NULL;
307 0     *iterp = cx;
308 0     if (unlocked)
309 0         JS_UNLOCK_GC(rt);
310 0     return cx;
311 }
312
313 static void
314 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
315 0 {
316     /*
317      * Check the error report, and set a JavaScript-catchable exception
318      * if the error is defined to have an associated exception.  If an
319      * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
320      * on the error report, and exception-aware hosts should ignore it.
321      */
322 0     if (reportp && reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
323 0         reportp->flags |= JSREPORT_EXCEPTION;
324
325 #if JS_HAS_ERROR_EXCEPTIONS
326     /*
327      * Call the error reporter only if an exception wasn't raised.
328      *
329      * If an exception was raised, then we call the debugErrorHook
330      * (if present) to give it a chance to see the error before it
331      * propagates out of scope.  This is needed for compatability
332      * with the old scheme.
333      */
334 0     if (!js_ErrorToException(cx, message, reportp)) {
335 0         js_ReportErrorAgain(cx, message, reportp);
336 0     } else if (cx->runtime->debugErrorHook && cx->errorReporter) {
337 0         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
338         /* test local in case debugErrorHook changed on another thread */
339 0         if (hook)
340 0             hook(cx, message, reportp, cx->runtime->debugErrorHookData);
341     }
342 #else
343     js_ReportErrorAgain(cx, message, reportp);
344 #endif
345 }
346
347 /*
348  * We don't post an exception in this case, since doing so runs into
349  * complications of pre-allocating an exception object which required
350  * running the Exception class initializer early etc.
351  * Instead we just invoke the errorReporter with an "Out Of Memory"
352  * type message, and then hope the process ends swiftly.
353  */
354 void
355 js_ReportOutOfMemory(JSContext *cx, JSErrorCallback callback)
356 0 {
357 0     JSStackFrame *fp;
358 0     JSErrorReport report;
359 0     JSErrorReporter onError = cx->errorReporter;
360
361     /* Get the message for this error, but we won't expand any arguments. */
362 0     const JSErrorFormatString *efs = callback(NULL, NULL, JSMSG_OUT_OF_MEMORY);
363 0     const char *msg = efs ? efs->format : "Out of memory";
364
365     /* Fill out the report, but don't do anything that requires allocation. */
366 0     memset(&report, 0, sizeof (struct JSErrorReport));
367 0     report.flags = JSREPORT_ERROR;
368 0     report.errorNumber = JSMSG_OUT_OF_MEMORY;
369
370     /*
371      * Walk stack until we find a frame that is associated with some script
372      * rather than a native frame.
373      */
374 0     for (fp = cx->fp; fp; fp = fp->down) {
375 0         if (fp->script && fp->pc) {
376 0             report.filename = fp->script->filename;
377 0             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
378 0             break;
379         }
380     }
381
382     /*
383      * If debugErrorHook is present then we give it a chance to veto
384      * sending the error on to the regular ErrorReporter.
385      */
386 0     if (onError) {
387 0         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
388 0         if (hook &&
389             !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {
390 0             onError = NULL;
391         }
392     }
393
394 0     if (onError)
395 0         onError(cx, msg, &report);
396 }
397
398 JSBool
399 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
400 0 {
401 0     char *last;
402 0     JSStackFrame *fp;
403 0     JSErrorReport report;
404 0     JSBool warning;
405
406 0     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
407 0         return JS_TRUE;
408
409 0     last = JS_vsmprintf(format, ap);
410 0     if (!last)
411 0         return JS_FALSE;
412
413 0     memset(&report, 0, sizeof (struct JSErrorReport));
414 0     report.flags = flags;
415
416     /* Find the top-most active script frame, for best line number blame. */
417 0     for (fp = cx->fp; fp; fp = fp->down) {
418 0         if (fp->script && fp->pc) {
419 0             report.filename = fp->script->filename;
420 0             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
421 0             break;
422         }
423     }
424
425 0     warning = JSREPORT_IS_WARNING(report.flags);
426 0     if (warning && JS_HAS_WERROR_OPTION(cx)) {
427 0         report.flags &= ~JSREPORT_WARNING;
428 0         warning = JS_FALSE;
429     }
430
431 0     ReportError(cx, last, &report);
432 0     free(last);
433 0     return warning;
434 }
435
436 /*
437  * The arguments from ap need to be packaged up into an array and stored
438  * into the report struct.
439  *
440  * The format string addressed by the error number may contain operands
441  * identified by the format {N}, where N is a decimal digit. Each of these
442  * is to be replaced by the Nth argument from the va_list. The complete
443  * message is placed into reportp->ucmessage converted to a JSString.
444  *
445  * Returns true if the expansion succeeds (can fail if out of memory).
446  */
447 JSBool
448 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
449                         void *userRef, const uintN errorNumber,
450                         char **messagep, JSErrorReport *reportp,
451                         JSBool *warningp, JSBool charArgs, va_list ap)
452 0 {
453 0     const JSErrorFormatString *efs;
454 0     int i;
455 0     int argCount;
456
457 0     *warningp = JSREPORT_IS_WARNING(reportp->flags);
458 0     if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
459 0         reportp->flags &= ~JSREPORT_WARNING;
460 0         *warningp = JS_FALSE;
461     }
462
463 0     *messagep = NULL;
464 0     if (callback) {
465 0         efs = callback(userRef, NULL, errorNumber);
466 0         if (efs) {
467 0             size_t totalArgsLength = 0;
468 0             size_t argLengths[10]; /* only {0} thru {9} supported */
469 0             argCount = efs->argCount;
470 0             JS_ASSERT(argCount <= 10);
471 0             if (argCount > 0) {
472                 /*
473                  * Gather the arguments into an array, and accumulate
474                  * their sizes. We allocate 1 more than necessary and
475                  * null it out to act as the caboose when we free the
476                  * pointers later.
477                  */
478 0                 reportp->messageArgs = (const jschar **)
479                     JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
480 0                 if (!reportp->messageArgs)
481 0                     return JS_FALSE;
482 0                 reportp->messageArgs[argCount] = NULL;
483 0                 for (i = 0; i < argCount; i++) {
484 0                     if (charArgs) {
485 0                         char *charArg = va_arg(ap, char *);
486 0                         reportp->messageArgs[i]
487                             = js_InflateString(cx, charArg, strlen(charArg));
488 0                         if (!reportp->messageArgs[i])
489 0                             goto error;
490                     }
491                     else
492 0                         reportp->messageArgs[i] = va_arg(ap, jschar *);
493 0                     argLengths[i] = js_strlen(reportp->messageArgs[i]);
494 0                     totalArgsLength += argLengths[i];
495                 }
496                 /* NULL-terminate for easy copying. */
497 0                 reportp->messageArgs[i] = NULL;
498             }
499             /*
500              * Parse the error format, substituting the argument X
501              * for {X} in the format.
502              */
503 0             if (argCount > 0) {
504 0                 if (efs->format) {
505 0                     const char *fmt;
506 0                     const jschar *arg;
507 0                     jschar *out;
508 0                     int expandedArgs = 0;
509                     size_t expandedLength
510 0                         = strlen(efs->format)
511                             - (3 * argCount) /* exclude the {n} */
512 0                             + totalArgsLength;
513                     /*
514                      * Note - the above calculation assumes that each argument
515                      * is used once and only once in the expansion !!!
516                      */
517 0                     reportp->ucmessage = out = (jschar *)
518                         JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
519 0                     if (!out)
520 0                         goto error;
521 0                     fmt = efs->format;
522 0                     while (*fmt) {
523 0                         if (*fmt == '{') {
524 0                             if (isdigit(fmt[1])) {
525 0                                 int d = JS7_UNDEC(fmt[1]);
526 0                                 JS_ASSERT(expandedArgs < argCount);
527 0                                 arg = reportp->messageArgs[d];
528 0                                 js_strncpy(out, arg, argLengths[d]);
529 0                                 out += argLengths[d];
530 0                                 fmt += 3;
531 0                                 expandedArgs++;
532 0                                 continue;
533                             }
534                         }
535                         /*
536                          * is this kosher?
537                          */
538 0                         *out++ = (unsigned char)(*fmt++);
539                     }
540 0                     JS_ASSERT(expandedArgs == argCount);
541 0                     *out = 0;
542 0                     *messagep =
543                         js_DeflateString(cx, reportp->ucmessage,
544                                          (size_t)(out - reportp->ucmessage));
545 0                     if (!*messagep)
546 0                         goto error;
547                 }
548             } else {
549                 /*
550                  * Zero arguments: the format string (if it exists) is the
551                  * entire message.
552                  */
553 0                 if (efs->format) {
554 0                     *messagep = JS_strdup(cx, efs->format);
555 0                     if (!*messagep)
556 0                         goto error;
557 0                     reportp->ucmessage
558                         = js_InflateString(cx, *messagep, strlen(*messagep));
559 0                     if (!reportp->ucmessage)
560 0                         goto error;
561                 }
562             }
563         }
564     }
565 0     if (*messagep == NULL) {
566         /* where's the right place for this ??? */
567         const char *defaultErrorMessage
568 0             = "No error message available for error number %d";
569 0         size_t nbytes = strlen(defaultErrorMessage) + 16;
570 0         *messagep = (char *)JS_malloc(cx, nbytes);
571 0         if (!*messagep)
572 0             goto error;
573 0         JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
574     }
575 0     return JS_TRUE;
576
577 error:
578 0     if (reportp->messageArgs) {
579 0         i = 0;
580 0         while (reportp->messageArgs[i])
581 0             JS_free(cx, (void *)reportp->messageArgs[i++]);
582 0         JS_free(cx, (void *)reportp->messageArgs);
583 0         reportp->messageArgs = NULL;
584     }
585 0     if (reportp->ucmessage) {
586 0         JS_free(cx, (void *)reportp->ucmessage);
587 0         reportp->ucmessage = NULL;
588     }
589 0     if (*messagep) {
590 0         JS_free(cx, (void *)*messagep);
591 0         *messagep = NULL;
592     }
593 0     return JS_FALSE;
594 }
595
596 JSBool
597 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
598                        void *userRef, const uintN errorNumber,
599                        JSBool charArgs, va_list ap)
600 0 {
601 0     JSStackFrame *fp;
602 0     JSErrorReport report;
603 0     char *message;
604 0     JSBool warning;
605
606 0     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
607 0         return JS_TRUE;
608
609 0     memset(&report, 0, sizeof (struct JSErrorReport));
610 0     report.flags = flags;
611 0     report.errorNumber = errorNumber;
612
613     /*
614      * If we can't find out where the error was based on the current frame,
615      * see if the next frame has a script/pc combo we can use.
616      */
617 0     for (fp = cx->fp; fp; fp = fp->down) {
618 0         if (fp->script && fp->pc) {
619 0             report.filename = fp->script->filename;
620 0             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
621 0             break;
622         }
623     }
624
625 0     if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
626                                  &message, &report, &warning, charArgs, ap)) {
627 0         return JS_FALSE;
628     }
629
630 0     ReportError(cx, message, &report);
631
632 0     if (message)
633 0         JS_free(cx, message);
634 0     if (report.messageArgs) {
635 0         int i = 0;
636 0         while (report.messageArgs[i])
637 0             JS_free(cx, (void *)report.messageArgs[i++]);
638 0         JS_free(cx, (void *)report.messageArgs);
639     }
640 0     if (report.ucmessage)
641 0         JS_free(cx, (void *)report.ucmessage);
642
643 0     return warning;
644 }
645
646 JS_FRIEND_API(void)
647 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
648 0 {
649 0     JSErrorReporter onError;
650
651 0     if (!message)
652 0         return;
653
654 0     if (cx->lastMessage)
655 0         free(cx->lastMessage);
656 0     cx->lastMessage = JS_strdup(cx, message);
657 0     if (!cx->lastMessage)
658 0         return;
659 0     onError = cx->errorReporter;
660
661     /*
662      * If debugErrorHook is present then we give it a chance to veto
663      * sending the error on to the regular ErrorReporter.
664      */
665 0     if (onError) {
666 0         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
667 0         if (hook &&
668             !hook(cx, cx->lastMessage, reportp,
669                   cx->runtime->debugErrorHookData)) {
670 0             onError = NULL;
671         }
672     }
673 0     if (onError)
674 0         onError(cx, cx->lastMessage, reportp);
675 }
676
677 void
678 js_ReportIsNotDefined(JSContext *cx, const char *name)
679 0 {
680 0     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
681 }
682
683 #if defined DEBUG && defined XP_UNIX
684 /* For gdb usage. */
685 void js_traceon(JSContext *cx)  { cx->tracefp = stderr; }
686 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
687 #endif
688
689 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
690 #if JS_HAS_DFLT_MSG_STRINGS
691 #define MSG_DEF(name, number, count, exception, format) \
692     { format, count } ,
693 #else
694 #define MSG_DEF(name, number, count, exception, format) \
695     { NULL, count } ,
696 #endif
697 #include "js.msg"
698 #undef MSG_DEF
699 };
700
701 const JSErrorFormatString *
702 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
703 0 {
704 0     if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
705 0         return &js_ErrorFormatString[errorNumber];
706 0     return NULL;