1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
* vim: set ts=8 sw=4 et tw=80:
4
* ***** BEGIN LICENSE BLOCK *****
5
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
7
* The contents of this file are subject to the Mozilla Public License Version
8
* 1.1 (the "License"); you may not use this file except in compliance with
9
* the License. You may obtain a copy of the License at
10
* http://www.mozilla.org/MPL/
12
* Software distributed under the License is distributed on an "AS IS" basis,
13
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14
* for the specific language governing rights and limitations under the
17
* The Original Code is Mozilla Communicator client code, released
20
* The Initial Developer of the Original Code is
21
* Netscape Communications Corporation.
22
* Portions created by the Initial Developer are Copyright (C) 1998
23
* the Initial Developer. All Rights Reserved.
27
* Alternatively, the contents of this file may be used under the terms of
28
* either of the GNU General Public License Version 2 or later (the "GPL"),
29
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30
* in which case the provisions of the GPL or the LGPL are applicable instead
31
* of those above. If you wish to allow use of your version of this file only
32
* under the terms of either the GPL or the LGPL, and not to allow others to
33
* use your version of this file under the terms of the MPL, indicate your
34
* decision by deleting the provisions above and replace them with the notice
35
* and other provisions required by the GPL or the LGPL. If you do not delete
36
* the provisions above, a recipient may use your version of this file under
37
* the terms of any one of the MPL, the GPL or the LGPL.
39
* ***** END LICENSE BLOCK ***** */
42
* JS execution context.
49
#include "jsarena.h" /* Added by JSIFY */
50
#include "jsutil.h" /* Added by JSIFY */
71
* Callback function to delete a JSThread info when the thread that owns it
75
js_ThreadDestructorCB(void *ptr)
77
JSThread *thread = (JSThread *)ptr;
81
while (!JS_CLIST_IS_EMPTY(&thread->contextList)) {
82
/* NB: use a temporary, as the macro evaluates its args many times. */
83
JSCList *link = thread->contextList.next;
85
JS_REMOVE_AND_INIT_LINK(link);
87
GSN_CACHE_CLEAR(&thread->gsnCache);
92
* Get current thread-local JSThread info, creating one if it doesn't exist.
93
* Each thread has a unique JSThread pointer.
95
* Since we are dealing with thread-local data, no lock is needed.
97
* Return a pointer to the thread local info, NULL if the system runs out
98
* of memory, or it failed to set thread private data (neither case is very
99
* likely; both are probably due to out-of-memory). It is up to the caller
100
* to report an error, if possible.
103
js_GetCurrentThread(JSRuntime *rt)
107
thread = (JSThread *)PR_GetThreadPrivate(rt->threadTPIndex);
109
thread = (JSThread *) calloc(1, sizeof(JSThread));
113
if (PR_FAILURE == PR_SetThreadPrivate(rt->threadTPIndex, thread)) {
118
JS_INIT_CLIST(&thread->contextList);
119
thread->id = js_CurrentThreadId();
121
/* js_SetContextThread initialize gcFreeLists as necessary. */
123
memset(thread->gcFreeLists, JS_FREE_PATTERN,
124
sizeof(thread->gcFreeLists));
131
* Sets current thread as owning thread of a context by assigning the
132
* thread-private info to the context. If the current thread doesn't have
133
* private JSThread info, create one.
136
js_SetContextThread(JSContext *cx)
138
JSThread *thread = js_GetCurrentThread(cx->runtime);
141
JS_ReportOutOfMemory(cx);
146
* Clear gcFreeLists on each transition from 0 to 1 context active on the
147
* current thread. See bug 351602.
149
if (JS_CLIST_IS_EMPTY(&thread->contextList))
150
memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists));
153
JS_REMOVE_LINK(&cx->threadLinks);
154
JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
158
/* Remove the owning thread info of a context. */
160
js_ClearContextThread(JSContext *cx)
162
JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
164
if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) {
165
memset(cx->thread->gcFreeLists, JS_FREE_PATTERN,
166
sizeof(cx->thread->gcFreeLists));
172
#endif /* JS_THREADSAFE */
175
js_OnVersionChange(JSContext *cx)
178
JSVersion version = JSVERSION_NUMBER(cx);
180
JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
185
js_SetVersion(JSContext *cx, JSVersion version)
187
cx->version = version;
188
js_OnVersionChange(cx);
192
js_NewContext(JSRuntime *rt, size_t stackChunkSize)
196
JSContextCallback cxCallback;
198
cx = (JSContext *) malloc(sizeof *cx);
201
memset(cx, 0, sizeof *cx);
204
#if JS_STACK_GROWTH_DIRECTION > 0
205
cx->stackLimit = (jsuword)-1;
208
JS_INIT_CLIST(&cx->threadLinks);
209
js_SetContextThread(cx);
214
first = (rt->contextList.next == &rt->contextList);
215
if (rt->state == JSRTS_UP) {
219
if (rt->state == JSRTS_DOWN) {
221
rt->state = JSRTS_LAUNCHING;
224
JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
226
JS_APPEND_LINK(&cx->links, &rt->contextList);
230
* First we do the infallible, every-time per-context initializations.
231
* Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
232
* or the stuff under 'if (first)' below) fail, at least the version
233
* and arena-pools will be valid and safe to use (say, from the last GC
234
* done by js_DestroyContext).
236
cx->version = JSVERSION_DEFAULT;
237
cx->jsop_eq = JSOP_EQ;
238
cx->jsop_ne = JSOP_NE;
239
JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));
240
JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));
242
if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
243
js_DestroyContext(cx, JSDCM_NEW_FAILED);
248
* If cx is the first context on this runtime, initialize well-known atoms,
249
* keywords, numbers, and strings. If one of these steps should fail, the
250
* runtime will be left in a partially initialized state, with zeroes and
251
* nulls stored in the default-initialized remainder of the struct. We'll
252
* clean the runtime up under js_DestroyContext, because cx will be "last"
253
* as well as "first".
260
* Both atomState and the scriptFilenameTable may be left over from a
261
* previous episode of non-zero contexts alive in rt, so don't re-init
262
* either table if it's not necessary. Just repopulate atomState with
263
* well-known internal atoms, and with the reserved identifiers added
266
ok = (rt->atomState.liveAtoms == 0)
267
? js_InitAtomState(cx, &rt->atomState)
268
: js_InitPinnedAtoms(cx, &rt->atomState);
269
if (ok && !rt->scriptFilenameTable)
270
ok = js_InitRuntimeScriptState(rt);
272
ok = js_InitRuntimeNumberState(cx);
274
ok = js_InitRuntimeStringState(cx);
279
js_DestroyContext(cx, JSDCM_NEW_FAILED);
284
rt->state = JSRTS_UP;
285
JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
289
cxCallback = rt->cxCallback;
290
if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
291
js_DestroyContext(cx, JSDCM_NEW_FAILED);
298
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
301
JSContextCallback cxCallback;
303
JSArgumentFormatMap *map;
304
JSLocalRootStack *lrs;
305
JSLocalRootChunk *lrc;
309
if (mode != JSDCM_NEW_FAILED) {
310
cxCallback = rt->cxCallback;
313
* JSCONTEXT_DESTROY callback is not allowed to fail and must
317
JSBool callbackStatus =
319
cxCallback(cx, JSCONTEXT_DESTROY);
320
JS_ASSERT(callbackStatus);
324
/* Remove cx from context list first. */
326
JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
327
JS_REMOVE_LINK(&cx->links);
328
last = (rt->contextList.next == &rt->contextList);
330
rt->state = JSRTS_LANDING;
336
* If cx is not in a request already, begin one now so that we wait
337
* for any racing GC started on a not-last context to finish, before
338
* we plow ahead and unpin atoms. Note that even though we begin a
339
* request here if necessary, we end all requests on cx below before
340
* forcing a final GC. This lets any not-last context destruction
341
* racing in another thread try to force or maybe run the GC, but by
342
* that point, rt->state will not be JSRTS_UP, and that GC attempt
345
if (cx->requestDepth == 0)
349
/* Unpin all pinned atoms before final GC. */
350
js_UnpinPinnedAtoms(&rt->atomState);
352
/* Unlock and clear GC things held by runtime pointers. */
353
js_FinishRuntimeNumberState(cx);
354
js_FinishRuntimeStringState(cx);
356
/* Clear debugging state to remove GC roots. */
357
JS_ClearAllTraps(cx);
358
JS_ClearAllWatchPoints(cx);
362
* Remove more GC roots in regExpStatics, then collect garbage.
363
* XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
364
* XXX this function call to wait for any racing GC to complete, in the
365
* XXX case where JS_DestroyContext is called outside of a request on cx
367
js_FreeRegExpStatics(cx, &cx->regExpStatics);
371
* Destroying a context implicitly calls JS_EndRequest(). Also, we must
372
* end our request here in case we are "last" -- in that event, another
373
* js_DestroyContext that was not last might be waiting in the GC for our
374
* request to end. We'll let it run below, just before we do the truly
375
* final GC and then free atom state.
377
* At this point, cx must be inaccessible to other threads. It's off the
378
* rt->contextList, and it should not be reachable via any object private
381
while (cx->requestDepth != 0)
386
js_GC(cx, GC_LAST_CONTEXT);
388
/* Try to free atom state, now that no unrooted scripts survive. */
389
if (rt->atomState.liveAtoms == 0)
390
js_FreeAtomState(cx, &rt->atomState);
392
/* Also free the script filename table if it exists and is empty. */
393
if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
394
js_FinishRuntimeScriptState(rt);
397
* Free the deflated string cache, but only after the last GC has
398
* collected all unleaked strings.
400
js_FinishDeflatedStringCache(rt);
402
/* Take the runtime down, now that it has no contexts or atoms. */
404
rt->state = JSRTS_DOWN;
405
JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
408
if (mode == JSDCM_FORCE_GC)
409
js_GC(cx, GC_NORMAL);
410
else if (mode == JSDCM_MAYBE_GC)
414
/* Free the stuff hanging off of cx. */
415
JS_FinishArenaPool(&cx->stackPool);
416
JS_FinishArenaPool(&cx->tempPool);
419
free(cx->lastMessage);
421
/* Remove any argument formatters. */
422
map = cx->argumentFormatMap;
424
JSArgumentFormatMap *temp = map;
429
/* Destroy the resolve recursion damper. */
430
if (cx->resolvingTable) {
431
JS_DHashTableDestroy(cx->resolvingTable);
432
cx->resolvingTable = NULL;
435
lrs = cx->localRootStack;
437
while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
438
lrs->topChunk = lrc->down;
445
js_ClearContextThread(cx);
448
/* Finally, free cx itself. */
453
js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
457
for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
458
if (cl == &cx->links)
461
JS_RUNTIME_METER(rt, deadContexts);
466
js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
468
JSContext *cx = *iterp;
473
cx = (JSContext *)&rt->contextList;
474
cx = (JSContext *)cx->links.next;
475
if (&cx->links == &rt->contextList)
483
JS_STATIC_DLL_CALLBACK(const void *)
484
resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
486
JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
491
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
492
resolving_HashKey(JSDHashTable *table, const void *ptr)
494
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
496
return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id;
499
JS_PUBLIC_API(JSBool)
500
resolving_MatchEntry(JSDHashTable *table,
501
const JSDHashEntryHdr *hdr,
504
const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
505
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
507
return entry->key.obj == key->obj && entry->key.id == key->id;
510
static const JSDHashTableOps resolving_dhash_ops = {
515
resolving_MatchEntry,
516
JS_DHashMoveEntryStub,
517
JS_DHashClearEntryStub,
518
JS_DHashFinalizeStub,
523
js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
524
JSResolvingEntry **entryp)
527
JSResolvingEntry *entry;
529
table = cx->resolvingTable;
531
table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
532
sizeof(JSResolvingEntry),
536
cx->resolvingTable = table;
539
entry = (JSResolvingEntry *)
540
JS_DHashTableOperate(table, key, JS_DHASH_ADD);
544
if (entry->flags & flag) {
545
/* An entry for (key, flag) exists already -- dampen recursion. */
548
/* Fill in key if we were the first to add entry, then set flag. */
551
entry->flags |= flag;
557
JS_ReportOutOfMemory(cx);
562
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
563
JSResolvingEntry *entry, uint32 generation)
568
* Clear flag from entry->flags and return early if other flags remain.
569
* We must take care to re-lookup entry if the table has changed since
570
* it was found by js_StartResolving.
572
table = cx->resolvingTable;
573
if (!entry || table->generation != generation) {
574
entry = (JSResolvingEntry *)
575
JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
577
JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
578
entry->flags &= ~flag;
583
* Do a raw remove only if fewer entries were removed than would cause
584
* alpha to be less than .5 (alpha is at most .75). Otherwise, we just
585
* call JS_DHashTableOperate to re-lookup the key and remove its entry,
586
* compressing or shrinking the table as needed.
588
if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
589
JS_DHashTableRawRemove(table, &entry->hdr);
591
JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
595
js_EnterLocalRootScope(JSContext *cx)
597
JSLocalRootStack *lrs;
600
lrs = cx->localRootStack;
602
lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
605
lrs->scopeMark = JSLRS_NULL_MARK;
607
lrs->topChunk = &lrs->firstChunk;
608
lrs->firstChunk.down = NULL;
609
cx->localRootStack = lrs;
612
/* Push lrs->scopeMark to save it for restore when leaving. */
613
mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
616
lrs->scopeMark = (uint32) mark;
621
js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
623
JSLocalRootStack *lrs;
625
JSLocalRootChunk *lrc;
627
/* Defend against buggy native callers. */
628
lrs = cx->localRootStack;
629
JS_ASSERT(lrs && lrs->rootCount != 0);
630
if (!lrs || lrs->rootCount == 0)
633
mark = lrs->scopeMark;
634
JS_ASSERT(mark != JSLRS_NULL_MARK);
635
if (mark == JSLRS_NULL_MARK)
638
/* Free any chunks being popped by this leave operation. */
639
m = mark >> JSLRS_CHUNK_SHIFT;
640
n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
643
JS_ASSERT(lrc != &lrs->firstChunk);
644
lrs->topChunk = lrc->down;
650
* Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
651
* it on the caller's scope, or store it in lastInternalResult if we are
652
* leaving the outermost scope. We don't need to allocate a new lrc
653
* because we can overwrite the old mark's slot with rval.
656
m = mark & JSLRS_CHUNK_MASK;
657
lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
658
if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
660
cx->weakRoots.lastInternalResult = rval;
663
* Increment m to avoid the "else if (m == 0)" case below. If
664
* rval is not a GC-thing, that case would take care of freeing
665
* any chunk that contained only the old mark. Since rval *is*
666
* a GC-thing here, we want to reuse that old mark's slot.
668
lrc->roots[m++] = rval;
672
lrs->rootCount = (uint32) mark;
675
* Free the stack eagerly, risking malloc churn. The alternative would
676
* require an lrs->entryCount member, maintained by Enter and Leave, and
677
* tested by the GC in addition to the cx->localRootStack non-null test.
679
* That approach would risk hoarding 264 bytes (net) per context. Right
680
* now it seems better to give fresh (dirty in CPU write-back cache, and
681
* the data is no longer needed) memory back to the malloc heap.
684
cx->localRootStack = NULL;
687
lrs->topChunk = lrc->down;
693
js_ForgetLocalRoot(JSContext *cx, jsval v)
695
JSLocalRootStack *lrs;
696
uint32 i, j, m, n, mark;
697
JSLocalRootChunk *lrc, *lrc2;
700
lrs = cx->localRootStack;
701
JS_ASSERT(lrs && lrs->rootCount);
702
if (!lrs || lrs->rootCount == 0)
705
/* Prepare to pop the top-most value from the stack. */
706
n = lrs->rootCount - 1;
707
m = n & JSLRS_CHUNK_MASK;
711
/* Be paranoid about calls on an empty scope. */
712
mark = lrs->scopeMark;
717
/* If v was not the last root pushed in the top scope, find it. */
719
/* Search downward in case v was recently pushed. */
726
j = i & JSLRS_CHUNK_MASK;
727
if (lrc2->roots[j] == v)
731
/* If we didn't find v in this scope, assert and bail out. */
732
JS_ASSERT(i != mark);
736
/* Swap top and v so common tail code can pop v. */
737
lrc2->roots[j] = top;
740
/* Pop the last value from the stack. */
741
lrc->roots[m] = JSVAL_NULL;
745
JS_ASSERT(lrc != &lrs->firstChunk);
746
lrs->topChunk = lrc->down;
752
js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
755
JSLocalRootChunk *lrc;
758
m = n & JSLRS_CHUNK_MASK;
759
if (n == 0 || m != 0) {
761
* At start of first chunk, or not at start of a non-first top chunk.
762
* Check for lrs->rootCount overflow.
764
if ((uint32)(n + 1) == 0) {
765
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
766
JSMSG_TOO_MANY_LOCAL_ROOTS);
770
JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
773
* After lrs->firstChunk, trying to index at a power-of-two chunk
774
* boundary: need a new chunk.
776
lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc);
779
lrc->down = lrs->topChunk;
782
lrs->rootCount = n + 1;
788
js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs)
791
JSLocalRootChunk *lrc;
797
mark = lrs->scopeMark;
803
JS_snprintf(name, sizeof name, "<local root %u>", n);
805
m = n & JSLRS_CHUNK_MASK;
806
JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m]));
807
GC_MARK(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name);
811
m = n & JSLRS_CHUNK_MASK;
812
mark = JSVAL_TO_INT(lrc->roots[m]);
820
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
823
* Check the error report, and set a JavaScript-catchable exception
824
* if the error is defined to have an associated exception. If an
825
* exception is thrown, then the JSREPORT_EXCEPTION flag will be set
826
* on the error report, and exception-aware hosts should ignore it.
829
if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
830
reportp->flags |= JSREPORT_EXCEPTION;
833
* Call the error reporter only if an exception wasn't raised.
835
* If an exception was raised, then we call the debugErrorHook
836
* (if present) to give it a chance to see the error before it
837
* propagates out of scope. This is needed for compatability
838
* with the old scheme.
840
if (!js_ErrorToException(cx, message, reportp)) {
841
js_ReportErrorAgain(cx, message, reportp);
842
} else if (cx->runtime->debugErrorHook && cx->errorReporter) {
843
JSDebugErrorHook hook = cx->runtime->debugErrorHook;
844
/* test local in case debugErrorHook changed on another thread */
846
hook(cx, message, reportp, cx->runtime->debugErrorHookData);
851
* We don't post an exception in this case, since doing so runs into
852
* complications of pre-allocating an exception object which required
853
* running the Exception class initializer early etc.
854
* Instead we just invoke the errorReporter with an "Out Of Memory"
855
* type message, and then hope the process ends swiftly.
858
js_ReportOutOfMemory(JSContext *cx)
861
JSErrorReport report;
862
JSErrorReporter onError = cx->errorReporter;
864
/* Get the message for this error, but we won't expand any arguments. */
865
const JSErrorFormatString *efs =
866
js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
867
const char *msg = efs ? efs->format : "Out of memory";
869
/* Fill out the report, but don't do anything that requires allocation. */
870
memset(&report, 0, sizeof (struct JSErrorReport));
871
report.flags = JSREPORT_ERROR;
872
report.errorNumber = JSMSG_OUT_OF_MEMORY;
875
* Walk stack until we find a frame that is associated with some script
876
* rather than a native frame.
878
for (fp = cx->fp; fp; fp = fp->down) {
879
if (fp->script && fp->pc) {
880
report.filename = fp->script->filename;
881
report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
887
* If debugErrorHook is present then we give it a chance to veto
888
* sending the error on to the regular ErrorReporter.
891
JSDebugErrorHook hook = cx->runtime->debugErrorHook;
893
!hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {
899
onError(cx, msg, &report);
903
js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
909
JSErrorReport report;
912
if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
915
message = JS_vsmprintf(format, ap);
918
messagelen = strlen(message);
920
memset(&report, 0, sizeof (struct JSErrorReport));
921
report.flags = flags;
922
report.errorNumber = JSMSG_USER_DEFINED_ERROR;
923
report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
925
/* Find the top-most active script frame, for best line number blame. */
926
for (fp = cx->fp; fp; fp = fp->down) {
927
if (fp->script && fp->pc) {
928
report.filename = fp->script->filename;
929
report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
934
warning = JSREPORT_IS_WARNING(report.flags);
935
if (warning && JS_HAS_WERROR_OPTION(cx)) {
936
report.flags &= ~JSREPORT_WARNING;
940
ReportError(cx, message, &report);
942
JS_free(cx, ucmessage);
947
* The arguments from ap need to be packaged up into an array and stored
948
* into the report struct.
950
* The format string addressed by the error number may contain operands
951
* identified by the format {N}, where N is a decimal digit. Each of these
952
* is to be replaced by the Nth argument from the va_list. The complete
953
* message is placed into reportp->ucmessage converted to a JSString.
955
* Returns true if the expansion succeeds (can fail if out of memory).
958
js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
959
void *userRef, const uintN errorNumber,
960
char **messagep, JSErrorReport *reportp,
961
JSBool *warningp, JSBool charArgs, va_list ap)
963
const JSErrorFormatString *efs;
967
*warningp = JSREPORT_IS_WARNING(reportp->flags);
968
if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
969
reportp->flags &= ~JSREPORT_WARNING;
970
*warningp = JS_FALSE;
975
/* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
976
if (!callback || callback == js_GetErrorMessage)
977
efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
979
efs = callback(userRef, NULL, errorNumber);
981
size_t totalArgsLength = 0;
982
size_t argLengths[10]; /* only {0} thru {9} supported */
983
argCount = efs->argCount;
984
JS_ASSERT(argCount <= 10);
987
* Gather the arguments into an array, and accumulate
988
* their sizes. We allocate 1 more than necessary and
989
* null it out to act as the caboose when we free the
992
reportp->messageArgs = (const jschar **)
993
JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
994
if (!reportp->messageArgs)
996
reportp->messageArgs[argCount] = NULL;
997
for (i = 0; i < argCount; i++) {
999
char *charArg = va_arg(ap, char *);
1000
size_t charArgLength = strlen(charArg);
1001
reportp->messageArgs[i]
1002
= js_InflateString(cx, charArg, &charArgLength);
1003
if (!reportp->messageArgs[i])
1006
reportp->messageArgs[i] = va_arg(ap, jschar *);
1008
argLengths[i] = js_strlen(reportp->messageArgs[i]);
1009
totalArgsLength += argLengths[i];
1011
/* NULL-terminate for easy copying. */
1012
reportp->messageArgs[i] = NULL;
1015
* Parse the error format, substituting the argument X
1016
* for {X} in the format.
1020
jschar *buffer, *fmt, *out;
1021
int expandedArgs = 0;
1022
size_t expandedLength;
1023
size_t len = strlen(efs->format);
1025
buffer = fmt = js_InflateString (cx, efs->format, &len);
1028
expandedLength = len
1029
- (3 * argCount) /* exclude the {n} */
1033
* Note - the above calculation assumes that each argument
1034
* is used once and only once in the expansion !!!
1036
reportp->ucmessage = out = (jschar *)
1037
JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
1039
JS_free (cx, buffer);
1044
if (isdigit(fmt[1])) {
1045
int d = JS7_UNDEC(fmt[1]);
1046
JS_ASSERT(d < argCount);
1047
js_strncpy(out, reportp->messageArgs[d],
1049
out += argLengths[d];
1057
JS_ASSERT(expandedArgs == argCount);
1059
JS_free (cx, buffer);
1061
js_DeflateString(cx, reportp->ucmessage,
1062
(size_t)(out - reportp->ucmessage));
1068
* Zero arguments: the format string (if it exists) is the
1073
*messagep = JS_strdup(cx, efs->format);
1076
len = strlen(*messagep);
1077
reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1078
if (!reportp->ucmessage)
1083
if (*messagep == NULL) {
1084
/* where's the right place for this ??? */
1085
const char *defaultErrorMessage
1086
= "No error message available for error number %d";
1087
size_t nbytes = strlen(defaultErrorMessage) + 16;
1088
*messagep = (char *)JS_malloc(cx, nbytes);
1091
JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1096
if (reportp->messageArgs) {
1097
/* free the arguments only if we allocated them */
1100
while (reportp->messageArgs[i])
1101
JS_free(cx, (void *)reportp->messageArgs[i++]);
1103
JS_free(cx, (void *)reportp->messageArgs);
1104
reportp->messageArgs = NULL;
1106
if (reportp->ucmessage) {
1107
JS_free(cx, (void *)reportp->ucmessage);
1108
reportp->ucmessage = NULL;
1111
JS_free(cx, (void *)*messagep);
1118
js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1119
void *userRef, const uintN errorNumber,
1120
JSBool charArgs, va_list ap)
1123
JSErrorReport report;
1127
if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1130
memset(&report, 0, sizeof (struct JSErrorReport));
1131
report.flags = flags;
1132
report.errorNumber = errorNumber;
1135
* If we can't find out where the error was based on the current frame,
1136
* see if the next frame has a script/pc combo we can use.
1138
for (fp = cx->fp; fp; fp = fp->down) {
1139
if (fp->script && fp->pc) {
1140
report.filename = fp->script->filename;
1141
report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
1146
if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1147
&message, &report, &warning, charArgs, ap)) {
1151
ReportError(cx, message, &report);
1154
JS_free(cx, message);
1155
if (report.messageArgs) {
1157
* js_ExpandErrorArguments owns its messageArgs only if it had to
1158
* inflate the arguments (from regular |char *|s).
1162
while (report.messageArgs[i])
1163
JS_free(cx, (void *)report.messageArgs[i++]);
1165
JS_free(cx, (void *)report.messageArgs);
1167
if (report.ucmessage)
1168
JS_free(cx, (void *)report.ucmessage);
1174
js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1176
JSErrorReporter onError;
1181
if (cx->lastMessage)
1182
free(cx->lastMessage);
1183
cx->lastMessage = JS_strdup(cx, message);
1184
if (!cx->lastMessage)
1186
onError = cx->errorReporter;
1189
* If debugErrorHook is present then we give it a chance to veto
1190
* sending the error on to the regular ErrorReporter.
1193
JSDebugErrorHook hook = cx->runtime->debugErrorHook;
1195
!hook(cx, cx->lastMessage, reportp,
1196
cx->runtime->debugErrorHookData)) {
1201
onError(cx, cx->lastMessage, reportp);
1205
js_ReportIsNotDefined(JSContext *cx, const char *name)
1207
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1210
#if defined DEBUG && defined XP_UNIX
1211
/* For gdb usage. */
1212
void js_traceon(JSContext *cx) { cx->tracefp = stderr; }
1213
void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1216
JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1217
#define MSG_DEF(name, number, count, exception, format) \
1218
{ format, count, exception } ,
1223
const JSErrorFormatString *
1224
js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1226
if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1227
return &js_ErrorFormatString[errorNumber];