Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / jscntxt.c
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 ***** */
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"
66 void
67 js_OnVersionChange(JSContext *cx)
68 {
69 #if !JS_BUG_FALLIBLE_EQOPS
70     if (JS_VERSION_IS_1_2(cx)) {
71         cx->jsop_eq = JSOP_NEW_EQ;
72         cx->jsop_ne = JSOP_NEW_NE;
73     } else {
74         cx->jsop_eq = JSOP_EQ;
75         cx->jsop_ne = JSOP_NE;
76     }
77 #endif /* !JS_BUG_FALLIBLE_EQOPS */
78 }
80 void
81 js_SetVersion(JSContext *cx, JSVersion version)
82 {
83     cx->version = version;
84     js_OnVersionChange(cx);
85 }
87 JSContext *
88 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
89 {
90     JSContext *cx;
91     JSBool ok, first;
93     cx = (JSContext *) malloc(sizeof *cx);
94     if (!cx)
95         return NULL;
96     memset(cx, 0, sizeof *cx);
98     cx->runtime = rt;
99 #if JS_STACK_GROWTH_DIRECTION > 0
100     cx->stackLimit = (jsuword)-1;
101 #endif
102 #ifdef JS_THREADSAFE
103     js_InitContextForLocking(cx);
104 #endif
106     JS_LOCK_GC(rt);
107     for (;;) {
108         first = (rt->contextList.next == &rt->contextList);
109         if (rt->state == JSRTS_UP) {
110             JS_ASSERT(!first);
111             break;
112         }
113         if (rt->state == JSRTS_DOWN) {
114             JS_ASSERT(first);
115             rt->state = JSRTS_LAUNCHING;
116             break;
117         }
118         JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
119     }
120     JS_APPEND_LINK(&cx->links, &rt->contextList);
121     JS_UNLOCK_GC(rt);
123     /*
124      * First we do the infallible, every-time per-context initializations.
125      * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
126      * or the stuff under 'if (first)' below) fail, at least the version
127      * and arena-pools will be valid and safe to use (say, from the last GC
128      * done by js_DestroyContext).
129      */
130     cx->version = JSVERSION_DEFAULT;
131     cx->jsop_eq = JSOP_EQ;
132     cx->jsop_ne = JSOP_NE;
133     JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));
134     JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));
136 #if JS_HAS_REGEXPS
137     if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
138         js_DestroyContext(cx, JS_NO_GC);
139         return NULL;
140     }
141 #endif
142 #if JS_HAS_EXCEPTIONS
143     cx->throwing = JS_FALSE;
144 #endif
146     /*
147      * If cx is the first context on this runtime, initialize well-known atoms,
148      * keywords, numbers, and strings.  If one of these steps should fail, the
149      * runtime will be left in a partially initialized state, with zeroes and
150      * nulls stored in the default-initialized remainder of the struct.  We'll
151      * clean the runtime up under js_DestroyContext, because cx will be "last"
152      * as well as "first".
153      */
154     if (first) {
155         /*
156          * Both atomState and the scriptFilenameTable may be left over from a
157          * previous episode of non-zero contexts alive in rt, so don't re-init
158          * either table if it's not necessary.  Just repopulate atomState with
159          * well-known internal atoms, and with the reserved identifiers added
160          * by the scanner.
161          */
162         ok = (rt->atomState.liveAtoms == 0)
163              ? js_InitAtomState(cx, &rt->atomState)
164              : js_InitPinnedAtoms(cx, &rt->atomState);
165         if (ok)
166             ok = js_InitScanner(cx);
167         if (ok && !rt->scriptFilenameTable)
168             ok = js_InitRuntimeScriptState(rt);
169         if (ok)
170             ok = js_InitRuntimeNumberState(cx);
171         if (ok)
172             ok = js_InitRuntimeStringState(cx);
173         if (!ok) {
174             js_DestroyContext(cx, JS_NO_GC);
175             return NULL;
176         }
178         JS_LOCK_GC(rt);
179         rt->state = JSRTS_UP;
180         JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
181         JS_UNLOCK_GC(rt);
182     }
184     return cx;
187 void
188 js_DestroyContext(JSContext *cx, JSGCMode gcmode)
190     JSRuntime *rt;
191     JSBool last;
192     JSArgumentFormatMap *map;
193     JSLocalRootStack *lrs;
194     JSLocalRootChunk *lrc;
196     rt = cx->runtime;
198     /* Remove cx from context list first. */
199     JS_LOCK_GC(rt);
200     JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
201     JS_REMOVE_LINK(&cx->links);
202     last = (rt->contextList.next == &rt->contextList);
203     if (last)
204         rt->state = JSRTS_LANDING;
205     JS_UNLOCK_GC(rt);
207     if (last) {
208 #ifdef JS_THREADSAFE
209         /*
210          * If cx is not in a request already, begin one now so that we wait
211          * for any racing GC started on a not-last context to finish, before
212          * we plow ahead and unpin atoms.  Note that even though we begin a
213          * request here if necessary, we end all requests on cx below before
214          * forcing a final GC.  This lets any not-last context destruction
215          * racing in another thread try to force or maybe run the GC, but by
216          * that point, rt->state will not be JSRTS_UP, and that GC attempt
217          * will return early.
218          */
219         if (cx->requestDepth == 0)
220             JS_BeginRequest(cx);
221 #endif
223         /* Unpin all pinned atoms before final GC. */
224         js_UnpinPinnedAtoms(&rt->atomState);
226         /* Unlock and clear GC things held by runtime pointers. */
227         js_FinishRuntimeNumberState(cx);
228         js_FinishRuntimeStringState(cx);
230         /* Clear debugging state to remove GC roots. */
231         JS_ClearAllTraps(cx);
232         JS_ClearAllWatchPoints(cx);
233     }
235 #if JS_HAS_REGEXPS
236     /*
237      * Remove more GC roots in regExpStatics, then collect garbage.
238      * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
239      * XXX this function call to wait for any racing GC to complete, in the
240      * XXX case where JS_DestroyContext is called outside of a request on cx
241      */
242     js_FreeRegExpStatics(cx, &cx->regExpStatics);
243 #endif
245 #ifdef JS_THREADSAFE
246     /*
247      * Destroying a context implicitly calls JS_EndRequest().  Also, we must
248      * end our request here in case we are "last" -- in that event, another
249      * js_DestroyContext that was not last might be waiting in the GC for our
250      * request to end.  We'll let it run below, just before we do the truly
251      * final GC and then free atom state.
252      *
253      * At this point, cx must be inaccessible to other threads.  It's off the
254      * rt->contextList, and it should not be reachable via any object private
255      * data structure.
256      */
257     while (cx->requestDepth != 0)
258         JS_EndRequest(cx);
259 #endif
261     if (last) {
262         /* Always force, so we wait for any racing GC to finish. */
263         js_ForceGC(cx, GC_LAST_CONTEXT);
265         /* Iterate until no finalizer removes a GC root or lock. */
266         while (rt->gcPoke)
267             js_GC(cx, GC_LAST_CONTEXT);
269         /* Try to free atom state, now that no unrooted scripts survive. */
270         if (rt->atomState.liveAtoms == 0)
271             js_FreeAtomState(cx, &rt->atomState);
273         /* Also free the script filename table if it exists and is empty. */
274         if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
275             js_FinishRuntimeScriptState(rt);
277         /* Take the runtime down, now that it has no contexts or atoms. */
278         JS_LOCK_GC(rt);
279         rt->state = JSRTS_DOWN;
280         JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
281         JS_UNLOCK_GC(rt);
282     } else {
283         if (gcmode == JS_FORCE_GC)
284             js_ForceGC(cx, 0);
285         else if (gcmode == JS_MAYBE_GC)
286             JS_MaybeGC(cx);
287     }
289     /* Free the stuff hanging off of cx. */
290     JS_FinishArenaPool(&cx->stackPool);
291     JS_FinishArenaPool(&cx->tempPool);
292     if (cx->lastMessage)
293         free(cx->lastMessage);
295     /* Remove any argument formatters. */
296     map = cx->argumentFormatMap;
297     while (map) {
298         JSArgumentFormatMap *temp = map;
299         map = map->next;
300         JS_free(cx, temp);
301     }
303     /* Destroy the resolve recursion damper. */
304     if (cx->resolvingTable) {
305         JS_DHashTableDestroy(cx->resolvingTable);
306         cx->resolvingTable = NULL;
307     }
309     lrs = cx->localRootStack;
310     if (lrs) {
311         while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
312             lrs->topChunk = lrc->down;
313             JS_free(cx, lrc);
314         }
315         JS_free(cx, lrs);
316     }
318     /* Finally, free cx itself. */
319     free(cx);
322 JSBool
323 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
325     JSCList *cl;
327     for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
328         if (cl == &cx->links)
329             return JS_TRUE;
330     }
331     JS_RUNTIME_METER(rt, deadContexts);
332     return JS_FALSE;
335 JSContext *
336 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
338     JSContext *cx = *iterp;
340     if (unlocked)
341         JS_LOCK_GC(rt);
342     if (!cx)
343         cx = (JSContext *)&rt->contextList;
344     cx = (JSContext *)cx->links.next;
345     if (&cx->links == &rt->contextList)
346         cx = NULL;
347     *iterp = cx;
348     if (unlocked)
349         JS_UNLOCK_GC(rt);
350     return cx;
353 JS_STATIC_DLL_CALLBACK(const void *)
354 resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
356     JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
358     return &entry->key;
361 JS_STATIC_DLL_CALLBACK(JSDHashNumber)
362 resolving_HashKey(JSDHashTable *table, const void *ptr)
364     const JSResolvingKey *key = (const JSResolvingKey *)ptr;
366     return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id;
369 JS_PUBLIC_API(JSBool)
370 resolving_MatchEntry(JSDHashTable *table,
371                      const JSDHashEntryHdr *hdr,
372                      const void *ptr)
374     const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
375     const JSResolvingKey *key = (const JSResolvingKey *)ptr;
377     return entry->key.obj == key->obj && entry->key.id == key->id;
380 static const JSDHashTableOps resolving_dhash_ops = {
381     JS_DHashAllocTable,
382     JS_DHashFreeTable,
383     resolving_GetKey,
384     resolving_HashKey,
385     resolving_MatchEntry,
386     JS_DHashMoveEntryStub,
387     JS_DHashClearEntryStub,
388     JS_DHashFinalizeStub,
389     NULL
390 };
392 JSBool
393 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
394                   JSResolvingEntry **entryp)
396     JSDHashTable *table;
397     JSResolvingEntry *entry;
399     table = cx->resolvingTable;
400     if (!table) {
401         table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
402                                  sizeof(JSResolvingEntry),
403                                  JS_DHASH_MIN_SIZE);
404         if (!table)
405             goto outofmem;
406         cx->resolvingTable = table;
407     }
409     entry = (JSResolvingEntry *)
410             JS_DHashTableOperate(table, key, JS_DHASH_ADD);
411     if (!entry)
412         goto outofmem;
414     if (entry->flags & flag) {
415         /* An entry for (key, flag) exists already -- dampen recursion. */
416         entry = NULL;
417     } else {
418         /* Fill in key if we were the first to add entry, then set flag. */
419         if (!entry->key.obj)
420             entry->key = *key;
421         entry->flags |= flag;
422     }
423     *entryp = entry;
424     return JS_TRUE;
426 outofmem:
427     JS_ReportOutOfMemory(cx);
428     return JS_FALSE;
431 void
432 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
433                  JSResolvingEntry *entry, uint32 generation)
435     JSDHashTable *table;
437     /*
438      * Clear flag from entry->flags and return early if other flags remain.
439      * We must take care to re-lookup entry if the table has changed since
440      * it was found by js_StartResolving.
441      */
442     table = cx->resolvingTable;
443     if (!entry || table->generation != generation) {
444         entry = (JSResolvingEntry *)
445                 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
446     }
447     JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
448     entry->flags &= ~flag;
449     if (entry->flags)
450         return;
452     /*
453      * Do a raw remove only if fewer entries were removed than would cause
454      * alpha to be less than .5 (alpha is at most .75).  Otherwise, we just
455      * call JS_DHashTableOperate to re-lookup the key and remove its entry,
456      * compressing or shrinking the table as needed.
457      */
458     if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
459         JS_DHashTableRawRemove(table, &entry->hdr);
460     else
461         JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
464 JSBool
465 js_EnterLocalRootScope(JSContext *cx)
467     JSLocalRootStack *lrs;
468     int mark;
470     lrs = cx->localRootStack;
471     if (!lrs) {
472         lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
473         if (!lrs)
474             return JS_FALSE;
475         lrs->scopeMark = JSLRS_NULL_MARK;
476         lrs->rootCount = 0;
477         lrs->topChunk = &lrs->firstChunk;
478         lrs->firstChunk.down = NULL;
479         cx->localRootStack = lrs;
480     }
482     /* Push lrs->scopeMark to save it for restore when leaving. */
483     mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
484     if (mark < 0)
485         return JS_FALSE;
486     lrs->scopeMark = (uint32) mark;
487     return JS_TRUE;
490 void
491 js_LeaveLocalRootScope(JSContext *cx)
493     JSLocalRootStack *lrs;
494     unsigned mark, m, n;
495     JSLocalRootChunk *lrc;
497     /* Defend against buggy native callers. */
498     lrs = cx->localRootStack;
499     JS_ASSERT(lrs && lrs->rootCount != 0);
500     if (!lrs || lrs->rootCount == 0)
501         return;
503     mark = lrs->scopeMark;
504     JS_ASSERT(mark != JSLRS_NULL_MARK);
505     if (mark == JSLRS_NULL_MARK)
506         return;
508     /* Free any chunks being popped by this leave operation. */
509     m = mark >> JSLRS_CHUNK_SHIFT;
510     n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
511     while (n > m) {
512         lrc = lrs->topChunk;
513         JS_ASSERT(lrc != &lrs->firstChunk);
514         lrs->topChunk = lrc->down;
515         JS_free(cx, lrc);
516         --n;
517     }
519     /* Pop the scope, restoring lrs->scopeMark. */
520     lrc = lrs->topChunk;
521     m = mark & JSLRS_CHUNK_MASK;
522     lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
523     lrc->roots[m] = JSVAL_NULL;
524     lrs->rootCount = (uint32) mark;
526     /*
527      * Free the stack eagerly, risking malloc churn.  The alternative would
528      * require an lrs->entryCount member, maintained by Enter and Leave, and
529      * tested by the GC in addition to the cx->localRootStack non-null test.
530      *
531      * That approach would risk hoarding 264 bytes (net) per context.  Right
532      * now it seems better to give fresh (dirty in CPU write-back cache, and
533      * the data is no longer needed) memory back to the malloc heap.
534      */
535     if (mark == 0) {
536         cx->localRootStack = NULL;
537         JS_free(cx, lrs);
538     } else if (m == 0) {
539         lrs->topChunk = lrc->down;
540         JS_free(cx, lrc);
541     }
544 void
545 js_ForgetLocalRoot(JSContext *cx, jsval v)
547     JSLocalRootStack *lrs;
548     unsigned i, j, m, n, mark;
549     JSLocalRootChunk *lrc, *lrc2;
550     jsval top;
552     lrs = cx->localRootStack;
553     JS_ASSERT(lrs && lrs->rootCount);
554     if (!lrs || lrs->rootCount == 0)
555         return;
557     /* Prepare to pop the top-most value from the stack. */
558     n = lrs->rootCount - 1;
559     m = n & JSLRS_CHUNK_MASK;
560     lrc = lrs->topChunk;
561     top = lrc->roots[m];
563     /* Be paranoid about calls on an empty scope. */
564     mark = lrs->scopeMark;
565     JS_ASSERT(mark < n);
566     if (mark >= n)
567         return;
569     /* If v was not the last root pushed in the top scope, find it. */
570     if (top != v) {
571         /* Search downward in case v was recently pushed. */
572         i = n;
573         j = m;
574         lrc2 = lrc;
575         while (--i > mark) {
576             if (j == 0)
577                 lrc2 = lrc2->down;
578             j = i & JSLRS_CHUNK_MASK;
579             if (lrc2->roots[j] == v)
580                 break;
581         }
583         /* If we didn't find v in this scope, assert and bail out. */
584         JS_ASSERT(i != mark);
585         if (i == mark)
586             return;
588         /* Swap top and v so common tail code can pop v. */
589         lrc2->roots[j] = top;
590     }
592     /* Pop the last value from the stack. */
593     lrc->roots[m] = JSVAL_NULL;
594     lrs->rootCount = n;
595     if (m == 0) {
596         JS_ASSERT(n != 0);
597         JS_ASSERT(lrc != &lrs->firstChunk);
598         lrs->topChunk = lrc->down;
599         JS_free(cx, lrc);
600     }
603 int
604 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
606     unsigned n, m;
607     JSLocalRootChunk *lrc;
609     n = lrs->rootCount;
610     m = n & JSLRS_CHUNK_MASK;
611     if (n == 0 || m != 0) {
612         /*
613          * At start of first chunk, or not at start of a non-first top chunk.
614          * Check for lrs->rootCount overflow.
615          */
616         if ((uint32)(n + 1) == 0) {
617             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
618                                  JSMSG_TOO_MANY_LOCAL_ROOTS);
619             return -1;
620         }
621         lrc = lrs->topChunk;
622         JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
623     } else {
624         /*
625          * After lrs->firstChunk, trying to index at a power-of-two chunk
626          * boundary: need a new chunk.
627          */
628         lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc);
629         if (!lrc)
630             return -1;
631         lrc->down = lrs->topChunk;
632         lrs->topChunk = lrc;
633     }
634     lrs->rootCount = n + 1;
635     lrc->roots[m] = v;
636     return (int) n;
639 void
640 js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs)
642     unsigned n, m, mark;
643     JSLocalRootChunk *lrc;
645     n = lrs->rootCount;
646     if (n == 0)
647         return;
649     mark = lrs->scopeMark;
650     lrc = lrs->topChunk;
651     do {
652         while (--n > mark) {
653 #ifdef GC_MARK_DEBUG
654             char name[22];
655             JS_snprintf(name, sizeof name, "<local root %u>", n);
656 #else
657             const char *name = NULL;
658 #endif
659             m = n & JSLRS_CHUNK_MASK;
660             JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m]));
661             JS_MarkGCThing(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name, NULL);
662             if (m == 0)
663                 lrc = lrc->down;
664         }
665         m = n & JSLRS_CHUNK_MASK;
666         mark = JSVAL_TO_INT(lrc->roots[m]);
667         if (m == 0)
668             lrc = lrc->down;
669     } while (n != 0);
670     JS_ASSERT(!lrc);
673 static void
674 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
676     /*
677      * Check the error report, and set a JavaScript-catchable exception
678      * if the error is defined to have an associated exception.  If an
679      * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
680      * on the error report, and exception-aware hosts should ignore it.
681      */
682     if (reportp && reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
683         reportp->flags |= JSREPORT_EXCEPTION;
685 #if JS_HAS_ERROR_EXCEPTIONS
686     /*
687      * Call the error reporter only if an exception wasn't raised.
688      *
689      * If an exception was raised, then we call the debugErrorHook
690      * (if present) to give it a chance to see the error before it
691      * propagates out of scope.  This is needed for compatability
692      * with the old scheme.
693      */
694     if (!js_ErrorToException(cx, message, reportp)) {
695         js_ReportErrorAgain(cx, message, reportp);
696     } else if (cx->runtime->debugErrorHook && cx->errorReporter) {
697         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
698         /* test local in case debugErrorHook changed on another thread */
699         if (hook)
700             hook(cx, message, reportp, cx->runtime->debugErrorHookData);
701     }
702 #else
703     js_ReportErrorAgain(cx, message, reportp);
704 #endif
707 /*
708  * We don't post an exception in this case, since doing so runs into
709  * complications of pre-allocating an exception object which required
710  * running the Exception class initializer early etc.
711  * Instead we just invoke the errorReporter with an "Out Of Memory"
712  * type message, and then hope the process ends swiftly.
713  */
714 void
715 js_ReportOutOfMemory(JSContext *cx, JSErrorCallback callback)
717     JSStackFrame *fp;
718     JSErrorReport report;
719     JSErrorReporter onError = cx->errorReporter;
721     /* Get the message for this error, but we won't expand any arguments. */
722     const JSErrorFormatString *efs = callback(NULL, NULL, JSMSG_OUT_OF_MEMORY);
723     const char *msg = efs ? efs->format : "Out of memory";
725     /* Fill out the report, but don't do anything that requires allocation. */
726     memset(&report, 0, sizeof (struct JSErrorReport));
727     report.flags = JSREPORT_ERROR;
728     report.errorNumber = JSMSG_OUT_OF_MEMORY;
730     /*
731      * Walk stack until we find a frame that is associated with some script
732      * rather than a native frame.
733      */
734     for (fp = cx->fp; fp; fp = fp->down) {
735         if (fp->script && fp->pc) {
736             report.filename = fp->script->filename;
737             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
738             break;
739         }
740     }
742     /*
743      * If debugErrorHook is present then we give it a chance to veto
744      * sending the error on to the regular ErrorReporter.
745      */
746     if (onError) {
747         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
748         if (hook &&
749             !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {
750             onError = NULL;
751         }
752     }
754     if (onError)
755         onError(cx, msg, &report);
758 JSBool
759 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
761     char *last;
762     JSStackFrame *fp;
763     JSErrorReport report;
764     JSBool warning;
766     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
767         return JS_TRUE;
769     last = JS_vsmprintf(format, ap);
770     if (!last)
771         return JS_FALSE;
773     memset(&report, 0, sizeof (struct JSErrorReport));
774     report.flags = flags;
776     /* Find the top-most active script frame, for best line number blame. */
777     for (fp = cx->fp; fp; fp = fp->down) {
778         if (fp->script && fp->pc) {
779             report.filename = fp->script->filename;
780             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
781             break;
782         }
783     }
785     warning = JSREPORT_IS_WARNING(report.flags);
786     if (warning && JS_HAS_WERROR_OPTION(cx)) {
787         report.flags &= ~JSREPORT_WARNING;
788         warning = JS_FALSE;
789     }
791     ReportError(cx, last, &report);
792     free(last);
793     return warning;
796 /*
797  * The arguments from ap need to be packaged up into an array and stored
798  * into the report struct.
799  *
800  * The format string addressed by the error number may contain operands
801  * identified by the format {N}, where N is a decimal digit. Each of these
802  * is to be replaced by the Nth argument from the va_list. The complete
803  * message is placed into reportp->ucmessage converted to a JSString.
804  *
805  * Returns true if the expansion succeeds (can fail if out of memory).
806  */
807 JSBool
808 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
809                         void *userRef, const uintN errorNumber,
810                         char **messagep, JSErrorReport *reportp,
811                         JSBool *warningp, JSBool charArgs, va_list ap)
813     const JSErrorFormatString *efs;
814     int i;
815     int argCount;
817     *warningp = JSREPORT_IS_WARNING(reportp->flags);
818     if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
819         reportp->flags &= ~JSREPORT_WARNING;
820         *warningp = JS_FALSE;
821     }
823     *messagep = NULL;
824     if (callback) {
825         efs = callback(userRef, NULL, errorNumber);
826         if (efs) {
827             size_t totalArgsLength = 0;
828             size_t argLengths[10]; /* only {0} thru {9} supported */
829             argCount = efs->argCount;
830             JS_ASSERT(argCount <= 10);
831             if (argCount > 0) {
832                 /*
833                  * Gather the arguments into an array, and accumulate
834                  * their sizes. We allocate 1 more than necessary and
835                  * null it out to act as the caboose when we free the
836                  * pointers later.
837                  */
838                 reportp->messageArgs = (const jschar **)
839                     JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
840                 if (!reportp->messageArgs)
841                     return JS_FALSE;
842                 reportp->messageArgs[argCount] = NULL;
843                 for (i = 0; i < argCount; i++) {
844                     if (charArgs) {
845                         char *charArg = va_arg(ap, char *);
846                         size_t charArgLength = strlen(charArg);
847                         reportp->messageArgs[i]
848                             = js_InflateString(cx, charArg, &charArgLength);
849                         if (!reportp->messageArgs[i])
850                             goto error;
851                     }
852                     else
853                         reportp->messageArgs[i] = va_arg(ap, jschar *);
854                     argLengths[i] = js_strlen(reportp->messageArgs[i]);
855                     totalArgsLength += argLengths[i];
856                 }
857                 /* NULL-terminate for easy copying. */
858                 reportp->messageArgs[i] = NULL;
859             }
860             /*
861              * Parse the error format, substituting the argument X
862              * for {X} in the format.
863              */
864             if (argCount > 0) {
865                 if (efs->format) {
866                     jschar *buffer, *fmt, *out;
867                     const jschar *arg;
868                     int expandedArgs = 0;
869                     size_t expandedLength;
870                     size_t len = strlen (efs->format);
871                     buffer = fmt = js_InflateString (cx, efs->format, &len);
872                     if (!buffer)
873                         goto error;
874                     expandedLength
875                         = len
876                             - (3 * argCount) /* exclude the {n} */
877                             + totalArgsLength;
878                     /*
879                      * Note - the above calculation assumes that each argument
880                      * is used once and only once in the expansion !!!
881                      */
882                     reportp->ucmessage = out = (jschar *)
883                         JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
884                     if (!out) {
885                         JS_free (cx, buffer);
886                         goto error;
887                     }
888                     while (*fmt) {
889                         if (*fmt == '{') {
890                             if (isdigit(fmt[1])) {
891                                 int d = JS7_UNDEC(fmt[1]);
892                                 JS_ASSERT(d < argCount);
893                                 arg = reportp->messageArgs[d];
894                                 js_strncpy(out, arg, argLengths[d]);
895                                 out += argLengths[d];
896                                 fmt += 3;
897                                 expandedArgs++;
898                                 continue;
899                             }
900                         }
901                          *out++ = *fmt++;
902                     }
903                     JS_ASSERT(expandedArgs == argCount);
904                     *out = 0;
905                     JS_free (cx, buffer);
906                     *messagep =
907                         js_DeflateString(cx, reportp->ucmessage,
908                                          (size_t)(out - reportp->ucmessage));
909                     if (!*messagep)
910                         goto error;
911                 }
912             } else {
913                 /*
914                  * Zero arguments: the format string (if it exists) is the
915                  * entire message.
916                  */
917                 if (efs->format) {
918                     size_t len;
919                     *messagep = JS_strdup(cx, efs->format);
920                     if (!*messagep)
921                         goto error;
922                     len = strlen(*messagep);
923                     reportp->ucmessage
924                         = js_InflateString(cx, *messagep, &len);
925                     if (!reportp->ucmessage)
926                         goto error;
927                 }
928             }
929         }
930     }
931     if (*messagep == NULL) {
932         /* where's the right place for this ??? */
933         const char *defaultErrorMessage
934             = "No error message available for error number %d";
935         size_t nbytes = strlen(defaultErrorMessage) + 16;
936         *messagep = (char *)JS_malloc(cx, nbytes);
937         if (!*messagep)
938             goto error;
939         JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
940     }
941     return JS_TRUE;
943 error:
944     if (reportp->messageArgs) {
945         i = 0;
946         while (reportp->messageArgs[i])
947             JS_free(cx, (void *)reportp->messageArgs[i++]);
948         JS_free(cx, (void *)reportp->messageArgs);
949         reportp->messageArgs = NULL;
950     }
951     if (reportp->ucmessage) {
952         JS_free(cx, (void *)reportp->ucmessage);
953         reportp->ucmessage = NULL;
954     }
955     if (*messagep) {
956         JS_free(cx, (void *)*messagep);
957         *messagep = NULL;
958     }
959     return JS_FALSE;
962 JSBool
963 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
964                        void *userRef, const uintN errorNumber,
965                        JSBool charArgs, va_list ap)
967     JSStackFrame *fp;
968     JSErrorReport report;
969     char *message;
970     JSBool warning;
972     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
973         return JS_TRUE;
975     memset(&report, 0, sizeof (struct JSErrorReport));
976     report.flags = flags;
977     report.errorNumber = errorNumber;
979     /*
980      * If we can't find out where the error was based on the current frame,
981      * see if the next frame has a script/pc combo we can use.
982      */
983     for (fp = cx->fp; fp; fp = fp->down) {
984         if (fp->script && fp->pc) {
985             report.filename = fp->script->filename;
986             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
987             break;
988         }
989     }
991     if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
992                                  &message, &report, &warning, charArgs, ap)) {
993         return JS_FALSE;
994     }
996     ReportError(cx, message, &report);
998     if (message)
999         JS_free(cx, message);
1000     if (report.messageArgs) {
1001         int i = 0;
1002         while (report.messageArgs[i])
1003             JS_free(cx, (void *)report.messageArgs[i++]);
1004         JS_free(cx, (void *)report.messageArgs);
1005     }
1006     if (report.ucmessage)
1007         JS_free(cx, (void *)report.ucmessage);
1009     return warning;
1012 JS_FRIEND_API(void)
1013 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1015     JSErrorReporter onError;
1017     if (!message)
1018         return;
1020     if (cx->lastMessage)
1021         free(cx->lastMessage);
1022     cx->lastMessage = JS_strdup(cx, message);
1023     if (!cx->lastMessage)
1024         return;
1025     onError = cx->errorReporter;
1027     /*
1028      * If debugErrorHook is present then we give it a chance to veto
1029      * sending the error on to the regular ErrorReporter.
1030      */
1031     if (onError) {
1032         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
1033         if (hook &&
1034             !hook(cx, cx->lastMessage, reportp,
1035                   cx->runtime->debugErrorHookData)) {
1036             onError = NULL;
1037         }
1038     }
1039     if (onError)
1040         onError(cx, cx->lastMessage, reportp);
1043 void
1044 js_ReportIsNotDefined(JSContext *cx, const char *name)
1046     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1049 #if defined DEBUG && defined XP_UNIX
1050 /* For gdb usage. */
1051 void js_traceon(JSContext *cx)  { cx->tracefp = stderr; }
1052 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1053 #endif
1055 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1056 #if JS_HAS_DFLT_MSG_STRINGS
1057 #define MSG_DEF(name, number, count, exception, format) \
1058     { format, count } ,
1059 #else
1060 #define MSG_DEF(name, number, count, exception, format) \
1061     { NULL, count } ,
1062 #endif
1063 #include "js.msg"
1064 #undef MSG_DEF
1065 };
1067 const JSErrorFormatString *
1068 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1070     if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1071         return &js_ErrorFormatString[errorNumber];
1072     return NULL;