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 */
55 #include "jsversion.h"
74 * The index for JSThread info, returned by PR_NewThreadPrivateIndex. The
75 * index value is visible and shared by all threads, but the data associated
76 * with it is private to each thread.
78 static PRUintn threadTPIndex
;
79 static JSBool tpIndexInited
= JS_FALSE
;
83 js_InitThreadPrivateIndex(void (*ptr
)(void *))
90 status
= PR_NewThreadPrivateIndex(&threadTPIndex
, ptr
);
92 if (status
== PR_SUCCESS
)
93 tpIndexInited
= JS_TRUE
;
94 return status
== PR_SUCCESS
;
100 js_CleanupThreadPrivateData()
104 return PR_SetThreadPrivate(threadTPIndex
, NULL
) == PR_SUCCESS
;
109 * Callback function to delete a JSThread info when the thread that owns it
113 js_ThreadDestructorCB(void *ptr
)
115 JSThread
*thread
= (JSThread
*)ptr
;
121 * Check that this thread properly called either JS_DestroyContext or
122 * JS_ClearContextThread on each JSContext it created or used.
124 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread
->contextList
));
125 GSN_CACHE_CLEAR(&thread
->gsnCache
);
126 #if defined JS_TRACER
127 js_FinishJIT(&thread
->traceMonitor
);
133 * Get current thread-local JSThread info, creating one if it doesn't exist.
134 * Each thread has a unique JSThread pointer.
136 * Since we are dealing with thread-local data, no lock is needed.
138 * Return a pointer to the thread local info, NULL if the system runs out
139 * of memory, or it failed to set thread private data (neither case is very
140 * likely; both are probably due to out-of-memory). It is up to the caller
141 * to report an error, if possible.
144 js_GetCurrentThread(JSRuntime
*rt
)
148 thread
= (JSThread
*)PR_GetThreadPrivate(threadTPIndex
);
150 thread
= (JSThread
*) malloc(sizeof(JSThread
));
154 memset(thread
, JS_FREE_PATTERN
, sizeof(JSThread
));
156 if (PR_FAILURE
== PR_SetThreadPrivate(threadTPIndex
, thread
)) {
161 JS_INIT_CLIST(&thread
->contextList
);
162 thread
->id
= js_CurrentThreadId();
163 thread
->gcMallocBytes
= 0;
165 memset(&thread
->traceMonitor
, 0, sizeof(thread
->traceMonitor
));
166 js_InitJIT(&thread
->traceMonitor
);
168 thread
->scriptsToGC
= NULL
;
171 * js_SetContextThread initializes the remaining fields as necessary.
178 * Sets current thread as owning thread of a context by assigning the
179 * thread-private info to the context. If the current thread doesn't have
180 * private JSThread info, create one.
183 js_SetContextThread(JSContext
*cx
)
185 JSThread
*thread
= js_GetCurrentThread(cx
->runtime
);
188 JS_ReportOutOfMemory(cx
);
193 * Clear caches on each transition from 0 to 1 context active on the
194 * current thread. See bug 425828.
196 if (JS_CLIST_IS_EMPTY(&thread
->contextList
)) {
197 memset(&thread
->gsnCache
, 0, sizeof(thread
->gsnCache
));
198 memset(&thread
->propertyCache
, 0, sizeof(thread
->propertyCache
));
201 /* Assert that the previous cx->thread called JS_ClearContextThread(). */
202 JS_ASSERT(!cx
->thread
|| cx
->thread
== thread
);
204 JS_APPEND_LINK(&cx
->threadLinks
, &thread
->contextList
);
209 /* Remove the owning thread info of a context. */
211 js_ClearContextThread(JSContext
*cx
)
214 * If cx is associated with a thread, this must be called only from that
215 * thread. If not, this is a harmless no-op.
217 JS_ASSERT(cx
->thread
== js_GetCurrentThread(cx
->runtime
) || !cx
->thread
);
218 JS_REMOVE_AND_INIT_LINK(&cx
->threadLinks
);
222 #endif /* JS_THREADSAFE */
225 js_OnVersionChange(JSContext
*cx
)
228 JSVersion version
= JSVERSION_NUMBER(cx
);
230 JS_ASSERT(version
== JSVERSION_DEFAULT
|| version
>= JSVERSION_ECMA_3
);
235 js_SetVersion(JSContext
*cx
, JSVersion version
)
237 cx
->version
= version
;
238 js_OnVersionChange(cx
);
242 js_NewContext(JSRuntime
*rt
, size_t stackChunkSize
)
246 JSContextCallback cxCallback
;
248 cx
= (JSContext
*) malloc(sizeof *cx
);
251 memset(cx
, 0, sizeof *cx
);
254 JS_ClearOperationCallback(cx
);
255 cx
->debugHooks
= &rt
->globalDebugHooks
;
256 #if JS_STACK_GROWTH_DIRECTION > 0
257 cx
->stackLimit
= (jsuword
)-1;
259 cx
->scriptStackQuota
= JS_DEFAULT_SCRIPT_STACK_QUOTA
;
261 cx
->gcLocalFreeLists
= (JSGCFreeListSet
*) &js_GCEmptyFreeListSet
;
262 JS_INIT_CLIST(&cx
->threadLinks
);
263 js_SetContextThread(cx
);
268 first
= (rt
->contextList
.next
== &rt
->contextList
);
269 if (rt
->state
== JSRTS_UP
) {
273 if (rt
->state
== JSRTS_DOWN
) {
275 rt
->state
= JSRTS_LAUNCHING
;
278 JS_WAIT_CONDVAR(rt
->stateChange
, JS_NO_TIMEOUT
);
280 JS_APPEND_LINK(&cx
->links
, &rt
->contextList
);
284 * First we do the infallible, every-time per-context initializations.
285 * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
286 * or the stuff under 'if (first)' below) fail, at least the version
287 * and arena-pools will be valid and safe to use (say, from the last GC
288 * done by js_DestroyContext).
290 cx
->version
= JSVERSION_DEFAULT
;
291 JS_INIT_ARENA_POOL(&cx
->stackPool
, "stack", stackChunkSize
, sizeof(jsval
),
292 &cx
->scriptStackQuota
);
294 JS_INIT_ARENA_POOL(&cx
->tempPool
, "temp",
295 1024, /* FIXME: bug 421435 */
296 sizeof(jsdouble
), &cx
->scriptStackQuota
);
299 * To avoid multiple allocations in InitMatch() (in jsregexp.c), the arena
300 * size parameter should be at least as big as:
302 * + (sizeof(REProgState) * INITIAL_STATESTACK)
303 * + (offsetof(REMatchState, parens) + avgParanSize * sizeof(RECapture))
305 JS_INIT_ARENA_POOL(&cx
->regexpPool
, "regexp",
306 12 * 1024 - 40, /* FIXME: bug 421435 */
307 sizeof(void *), &cx
->scriptStackQuota
);
309 if (!js_InitRegExpStatics(cx
, &cx
->regExpStatics
)) {
310 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
314 cx
->resolveFlags
= 0;
317 * If cx is the first context on this runtime, initialize well-known atoms,
318 * keywords, numbers, and strings. If one of these steps should fail, the
319 * runtime will be left in a partially initialized state, with zeroes and
320 * nulls stored in the default-initialized remainder of the struct. We'll
321 * clean the runtime up under js_DestroyContext, because cx will be "last"
322 * as well as "first".
328 ok
= js_InitCommonAtoms(cx
);
331 * scriptFilenameTable may be left over from a previous episode of
332 * non-zero contexts alive in rt, so don't re-init the table if it's
335 if (ok
&& !rt
->scriptFilenameTable
)
336 ok
= js_InitRuntimeScriptState(rt
);
338 ok
= js_InitRuntimeNumberState(cx
);
340 ok
= js_InitRuntimeStringState(cx
);
345 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
350 rt
->state
= JSRTS_UP
;
351 JS_NOTIFY_ALL_CONDVAR(rt
->stateChange
);
355 cxCallback
= rt
->cxCallback
;
356 if (cxCallback
&& !cxCallback(cx
, JSCONTEXT_NEW
)) {
357 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
365 js_DestroyContext(JSContext
*cx
, JSDestroyContextMode mode
)
368 JSContextCallback cxCallback
;
370 JSArgumentFormatMap
*map
;
371 JSLocalRootStack
*lrs
;
372 JSLocalRootChunk
*lrc
;
376 if (mode
!= JSDCM_NEW_FAILED
) {
377 cxCallback
= rt
->cxCallback
;
380 * JSCONTEXT_DESTROY callback is not allowed to fail and must
384 JSBool callbackStatus
=
386 cxCallback(cx
, JSCONTEXT_DESTROY
);
387 JS_ASSERT(callbackStatus
);
391 /* Remove cx from context list first. */
393 JS_ASSERT(rt
->state
== JSRTS_UP
|| rt
->state
== JSRTS_LAUNCHING
);
394 JS_REMOVE_LINK(&cx
->links
);
395 last
= (rt
->contextList
.next
== &rt
->contextList
);
397 rt
->state
= JSRTS_LANDING
;
399 js_RevokeGCLocalFreeLists(cx
);
406 * If cx is not in a request already, begin one now so that we wait
407 * for any racing GC started on a not-last context to finish, before
408 * we plow ahead and unpin atoms. Note that even though we begin a
409 * request here if necessary, we end all requests on cx below before
410 * forcing a final GC. This lets any not-last context destruction
411 * racing in another thread try to force or maybe run the GC, but by
412 * that point, rt->state will not be JSRTS_UP, and that GC attempt
415 if (cx
->requestDepth
== 0)
419 /* Unlock and clear GC things held by runtime pointers. */
420 js_FinishRuntimeNumberState(cx
);
421 js_FinishRuntimeStringState(cx
);
423 /* Unpin all common atoms before final GC. */
424 js_FinishCommonAtoms(cx
);
426 /* Clear debugging state to remove GC roots. */
427 JS_ClearAllTraps(cx
);
428 JS_ClearAllWatchPoints(cx
);
432 * Remove more GC roots in regExpStatics, then collect garbage.
433 * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
434 * XXX this function call to wait for any racing GC to complete, in the
435 * XXX case where JS_DestroyContext is called outside of a request on cx
437 js_FreeRegExpStatics(cx
, &cx
->regExpStatics
);
441 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
442 * end our request here in case we are "last" -- in that event, another
443 * js_DestroyContext that was not last might be waiting in the GC for our
444 * request to end. We'll let it run below, just before we do the truly
445 * final GC and then free atom state.
447 * At this point, cx must be inaccessible to other threads. It's off the
448 * rt->contextList, and it should not be reachable via any object private
451 while (cx
->requestDepth
!= 0)
456 js_GC(cx
, GC_LAST_CONTEXT
);
459 * Free the script filename table if it exists and is empty. Do this
460 * after the last GC to avoid finalizers tripping on free memory.
462 if (rt
->scriptFilenameTable
&& rt
->scriptFilenameTable
->nentries
== 0)
463 js_FinishRuntimeScriptState(rt
);
465 /* Take the runtime down, now that it has no contexts or atoms. */
467 rt
->state
= JSRTS_DOWN
;
468 JS_NOTIFY_ALL_CONDVAR(rt
->stateChange
);
471 if (mode
== JSDCM_FORCE_GC
)
472 js_GC(cx
, GC_NORMAL
);
473 else if (mode
== JSDCM_MAYBE_GC
)
477 /* Free the stuff hanging off of cx. */
478 JS_FinishArenaPool(&cx
->stackPool
);
479 JS_FinishArenaPool(&cx
->tempPool
);
480 JS_FinishArenaPool(&cx
->regexpPool
);
483 free(cx
->lastMessage
);
485 /* Remove any argument formatters. */
486 map
= cx
->argumentFormatMap
;
488 JSArgumentFormatMap
*temp
= map
;
493 /* Destroy the resolve recursion damper. */
494 if (cx
->resolvingTable
) {
495 JS_DHashTableDestroy(cx
->resolvingTable
);
496 cx
->resolvingTable
= NULL
;
499 lrs
= cx
->localRootStack
;
501 while ((lrc
= lrs
->topChunk
) != &lrs
->firstChunk
) {
502 lrs
->topChunk
= lrc
->down
;
509 js_ClearContextThread(cx
);
512 /* Finally, free cx itself. */
517 js_ValidContextPointer(JSRuntime
*rt
, JSContext
*cx
)
521 for (cl
= rt
->contextList
.next
; cl
!= &rt
->contextList
; cl
= cl
->next
) {
522 if (cl
== &cx
->links
)
525 JS_RUNTIME_METER(rt
, deadContexts
);
530 js_ContextIterator(JSRuntime
*rt
, JSBool unlocked
, JSContext
**iterp
)
532 JSContext
*cx
= *iterp
;
536 cx
= (JSContext
*) (cx
? cx
->links
.next
: rt
->contextList
.next
);
537 if (&cx
->links
== &rt
->contextList
)
546 resolving_HashKey(JSDHashTable
*table
, const void *ptr
)
548 const JSResolvingKey
*key
= (const JSResolvingKey
*)ptr
;
550 return ((JSDHashNumber
)JS_PTR_TO_UINT32(key
->obj
) >> JSVAL_TAGBITS
) ^ key
->id
;
553 JS_PUBLIC_API(JSBool
)
554 resolving_MatchEntry(JSDHashTable
*table
,
555 const JSDHashEntryHdr
*hdr
,
558 const JSResolvingEntry
*entry
= (const JSResolvingEntry
*)hdr
;
559 const JSResolvingKey
*key
= (const JSResolvingKey
*)ptr
;
561 return entry
->key
.obj
== key
->obj
&& entry
->key
.id
== key
->id
;
564 static const JSDHashTableOps resolving_dhash_ops
= {
568 resolving_MatchEntry
,
569 JS_DHashMoveEntryStub
,
570 JS_DHashClearEntryStub
,
571 JS_DHashFinalizeStub
,
576 js_StartResolving(JSContext
*cx
, JSResolvingKey
*key
, uint32 flag
,
577 JSResolvingEntry
**entryp
)
580 JSResolvingEntry
*entry
;
582 table
= cx
->resolvingTable
;
584 table
= JS_NewDHashTable(&resolving_dhash_ops
, NULL
,
585 sizeof(JSResolvingEntry
),
589 cx
->resolvingTable
= table
;
592 entry
= (JSResolvingEntry
*)
593 JS_DHashTableOperate(table
, key
, JS_DHASH_ADD
);
597 if (entry
->flags
& flag
) {
598 /* An entry for (key, flag) exists already -- dampen recursion. */
601 /* Fill in key if we were the first to add entry, then set flag. */
604 entry
->flags
|= flag
;
610 JS_ReportOutOfMemory(cx
);
615 js_StopResolving(JSContext
*cx
, JSResolvingKey
*key
, uint32 flag
,
616 JSResolvingEntry
*entry
, uint32 generation
)
621 * Clear flag from entry->flags and return early if other flags remain.
622 * We must take care to re-lookup entry if the table has changed since
623 * it was found by js_StartResolving.
625 table
= cx
->resolvingTable
;
626 if (!entry
|| table
->generation
!= generation
) {
627 entry
= (JSResolvingEntry
*)
628 JS_DHashTableOperate(table
, key
, JS_DHASH_LOOKUP
);
630 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
));
631 entry
->flags
&= ~flag
;
636 * Do a raw remove only if fewer entries were removed than would cause
637 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
638 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
639 * compressing or shrinking the table as needed.
641 if (table
->removedCount
< JS_DHASH_TABLE_SIZE(table
) >> 2)
642 JS_DHashTableRawRemove(table
, &entry
->hdr
);
644 JS_DHashTableOperate(table
, key
, JS_DHASH_REMOVE
);
648 js_EnterLocalRootScope(JSContext
*cx
)
650 JSLocalRootStack
*lrs
;
653 lrs
= cx
->localRootStack
;
655 lrs
= (JSLocalRootStack
*) JS_malloc(cx
, sizeof *lrs
);
658 lrs
->scopeMark
= JSLRS_NULL_MARK
;
660 lrs
->topChunk
= &lrs
->firstChunk
;
661 lrs
->firstChunk
.down
= NULL
;
662 cx
->localRootStack
= lrs
;
665 /* Push lrs->scopeMark to save it for restore when leaving. */
666 mark
= js_PushLocalRoot(cx
, lrs
, INT_TO_JSVAL(lrs
->scopeMark
));
669 lrs
->scopeMark
= (uint32
) mark
;
674 js_LeaveLocalRootScopeWithResult(JSContext
*cx
, jsval rval
)
676 JSLocalRootStack
*lrs
;
678 JSLocalRootChunk
*lrc
;
680 /* Defend against buggy native callers. */
681 lrs
= cx
->localRootStack
;
682 JS_ASSERT(lrs
&& lrs
->rootCount
!= 0);
683 if (!lrs
|| lrs
->rootCount
== 0)
686 mark
= lrs
->scopeMark
;
687 JS_ASSERT(mark
!= JSLRS_NULL_MARK
);
688 if (mark
== JSLRS_NULL_MARK
)
691 /* Free any chunks being popped by this leave operation. */
692 m
= mark
>> JSLRS_CHUNK_SHIFT
;
693 n
= (lrs
->rootCount
- 1) >> JSLRS_CHUNK_SHIFT
;
696 JS_ASSERT(lrc
!= &lrs
->firstChunk
);
697 lrs
->topChunk
= lrc
->down
;
703 * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
704 * it on the caller's scope, or store it in lastInternalResult if we are
705 * leaving the outermost scope. We don't need to allocate a new lrc
706 * because we can overwrite the old mark's slot with rval.
709 m
= mark
& JSLRS_CHUNK_MASK
;
710 lrs
->scopeMark
= (uint32
) JSVAL_TO_INT(lrc
->roots
[m
]);
711 if (JSVAL_IS_GCTHING(rval
) && !JSVAL_IS_NULL(rval
)) {
713 cx
->weakRoots
.lastInternalResult
= rval
;
716 * Increment m to avoid the "else if (m == 0)" case below. If
717 * rval is not a GC-thing, that case would take care of freeing
718 * any chunk that contained only the old mark. Since rval *is*
719 * a GC-thing here, we want to reuse that old mark's slot.
721 lrc
->roots
[m
++] = rval
;
725 lrs
->rootCount
= (uint32
) mark
;
728 * Free the stack eagerly, risking malloc churn. The alternative would
729 * require an lrs->entryCount member, maintained by Enter and Leave, and
730 * tested by the GC in addition to the cx->localRootStack non-null test.
732 * That approach would risk hoarding 264 bytes (net) per context. Right
733 * now it seems better to give fresh (dirty in CPU write-back cache, and
734 * the data is no longer needed) memory back to the malloc heap.
737 cx
->localRootStack
= NULL
;
740 lrs
->topChunk
= lrc
->down
;
746 js_ForgetLocalRoot(JSContext
*cx
, jsval v
)
748 JSLocalRootStack
*lrs
;
749 uint32 i
, j
, m
, n
, mark
;
750 JSLocalRootChunk
*lrc
, *lrc2
;
753 lrs
= cx
->localRootStack
;
754 JS_ASSERT(lrs
&& lrs
->rootCount
);
755 if (!lrs
|| lrs
->rootCount
== 0)
758 /* Prepare to pop the top-most value from the stack. */
759 n
= lrs
->rootCount
- 1;
760 m
= n
& JSLRS_CHUNK_MASK
;
764 /* Be paranoid about calls on an empty scope. */
765 mark
= lrs
->scopeMark
;
770 /* If v was not the last root pushed in the top scope, find it. */
772 /* Search downward in case v was recently pushed. */
779 j
= i
& JSLRS_CHUNK_MASK
;
780 if (lrc2
->roots
[j
] == v
)
784 /* If we didn't find v in this scope, assert and bail out. */
785 JS_ASSERT(i
!= mark
);
789 /* Swap top and v so common tail code can pop v. */
790 lrc2
->roots
[j
] = top
;
793 /* Pop the last value from the stack. */
794 lrc
->roots
[m
] = JSVAL_NULL
;
798 JS_ASSERT(lrc
!= &lrs
->firstChunk
);
799 lrs
->topChunk
= lrc
->down
;
805 js_PushLocalRoot(JSContext
*cx
, JSLocalRootStack
*lrs
, jsval v
)
808 JSLocalRootChunk
*lrc
;
811 m
= n
& JSLRS_CHUNK_MASK
;
812 if (n
== 0 || m
!= 0) {
814 * At start of first chunk, or not at start of a non-first top chunk.
815 * Check for lrs->rootCount overflow.
817 if ((uint32
)(n
+ 1) == 0) {
818 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
819 JSMSG_TOO_MANY_LOCAL_ROOTS
);
823 JS_ASSERT(n
!= 0 || lrc
== &lrs
->firstChunk
);
826 * After lrs->firstChunk, trying to index at a power-of-two chunk
827 * boundary: need a new chunk.
829 lrc
= (JSLocalRootChunk
*) JS_malloc(cx
, sizeof *lrc
);
832 lrc
->down
= lrs
->topChunk
;
835 lrs
->rootCount
= n
+ 1;
841 js_TraceLocalRoots(JSTracer
*trc
, JSLocalRootStack
*lrs
)
844 JSLocalRootChunk
*lrc
;
851 mark
= lrs
->scopeMark
;
855 m
= n
& JSLRS_CHUNK_MASK
;
857 JS_ASSERT(JSVAL_IS_GCTHING(v
) && v
!= JSVAL_NULL
);
858 JS_SET_TRACING_INDEX(trc
, "local_root", n
);
859 js_CallValueTracerIfGCThing(trc
, v
);
863 m
= n
& JSLRS_CHUNK_MASK
;
864 mark
= JSVAL_TO_INT(lrc
->roots
[m
]);
872 ReportError(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
875 * Check the error report, and set a JavaScript-catchable exception
876 * if the error is defined to have an associated exception. If an
877 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
878 * on the error report, and exception-aware hosts should ignore it.
881 if (reportp
->errorNumber
== JSMSG_UNCAUGHT_EXCEPTION
)
882 reportp
->flags
|= JSREPORT_EXCEPTION
;
885 * Call the error reporter only if an exception wasn't raised.
887 * If an exception was raised, then we call the debugErrorHook
888 * (if present) to give it a chance to see the error before it
889 * propagates out of scope. This is needed for compatability
890 * with the old scheme.
892 if (!JS_IsRunning(cx
) || !js_ErrorToException(cx
, message
, reportp
)) {
893 js_ReportErrorAgain(cx
, message
, reportp
);
894 } else if (cx
->debugHooks
->debugErrorHook
&& cx
->errorReporter
) {
895 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
896 /* test local in case debugErrorHook changed on another thread */
898 hook(cx
, message
, reportp
, cx
->debugHooks
->debugErrorHookData
);
902 /* The report must be initially zeroed. */
904 PopulateReportBlame(JSContext
*cx
, JSErrorReport
*report
)
909 * Walk stack until we find a frame that is associated with some script
910 * rather than a native frame.
912 for (fp
= js_GetTopStackFrame(cx
); fp
; fp
= fp
->down
) {
914 report
->filename
= fp
->script
->filename
;
915 report
->lineno
= js_FramePCToLineNumber(cx
, fp
);
922 * We don't post an exception in this case, since doing so runs into
923 * complications of pre-allocating an exception object which required
924 * running the Exception class initializer early etc.
925 * Instead we just invoke the errorReporter with an "Out Of Memory"
926 * type message, and then hope the process ends swiftly.
929 js_ReportOutOfMemory(JSContext
*cx
)
931 JSErrorReport report
;
932 JSErrorReporter onError
= cx
->errorReporter
;
934 /* Get the message for this error, but we won't expand any arguments. */
935 const JSErrorFormatString
*efs
=
936 js_GetLocalizedErrorMessage(cx
, NULL
, NULL
, JSMSG_OUT_OF_MEMORY
);
937 const char *msg
= efs
? efs
->format
: "Out of memory";
939 /* Fill out the report, but don't do anything that requires allocation. */
940 memset(&report
, 0, sizeof (struct JSErrorReport
));
941 report
.flags
= JSREPORT_ERROR
;
942 report
.errorNumber
= JSMSG_OUT_OF_MEMORY
;
943 PopulateReportBlame(cx
, &report
);
946 * If debugErrorHook is present then we give it a chance to veto sending
947 * the error on to the regular ErrorReporter. We also clear a pending
948 * exception if any now so the hooks can replace the out-of-memory error
949 * by a script-catchable exception.
951 cx
->throwing
= JS_FALSE
;
953 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
955 !hook(cx
, msg
, &report
, cx
->debugHooks
->debugErrorHookData
)) {
961 onError(cx
, msg
, &report
);
965 js_ReportOutOfScriptQuota(JSContext
*cx
)
967 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
968 JSMSG_SCRIPT_STACK_QUOTA
);
972 js_ReportOverRecursed(JSContext
*cx
)
974 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_OVER_RECURSED
);
978 js_ReportAllocationOverflow(JSContext
*cx
)
980 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_ALLOC_OVERFLOW
);
984 js_ReportErrorVA(JSContext
*cx
, uintN flags
, const char *format
, va_list ap
)
989 JSErrorReport report
;
992 if ((flags
& JSREPORT_STRICT
) && !JS_HAS_STRICT_OPTION(cx
))
995 message
= JS_vsmprintf(format
, ap
);
998 messagelen
= strlen(message
);
1000 memset(&report
, 0, sizeof (struct JSErrorReport
));
1001 report
.flags
= flags
;
1002 report
.errorNumber
= JSMSG_USER_DEFINED_ERROR
;
1003 report
.ucmessage
= ucmessage
= js_InflateString(cx
, message
, &messagelen
);
1004 PopulateReportBlame(cx
, &report
);
1006 warning
= JSREPORT_IS_WARNING(report
.flags
);
1007 if (warning
&& JS_HAS_WERROR_OPTION(cx
)) {
1008 report
.flags
&= ~JSREPORT_WARNING
;
1012 ReportError(cx
, message
, &report
);
1014 JS_free(cx
, ucmessage
);
1019 * The arguments from ap need to be packaged up into an array and stored
1020 * into the report struct.
1022 * The format string addressed by the error number may contain operands
1023 * identified by the format {N}, where N is a decimal digit. Each of these
1024 * is to be replaced by the Nth argument from the va_list. The complete
1025 * message is placed into reportp->ucmessage converted to a JSString.
1027 * Returns true if the expansion succeeds (can fail if out of memory).
1030 js_ExpandErrorArguments(JSContext
*cx
, JSErrorCallback callback
,
1031 void *userRef
, const uintN errorNumber
,
1032 char **messagep
, JSErrorReport
*reportp
,
1033 JSBool
*warningp
, JSBool charArgs
, va_list ap
)
1035 const JSErrorFormatString
*efs
;
1039 *warningp
= JSREPORT_IS_WARNING(reportp
->flags
);
1040 if (*warningp
&& JS_HAS_WERROR_OPTION(cx
)) {
1041 reportp
->flags
&= ~JSREPORT_WARNING
;
1042 *warningp
= JS_FALSE
;
1047 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1048 if (!callback
|| callback
== js_GetErrorMessage
)
1049 efs
= js_GetLocalizedErrorMessage(cx
, userRef
, NULL
, errorNumber
);
1051 efs
= callback(userRef
, NULL
, errorNumber
);
1053 size_t totalArgsLength
= 0;
1054 size_t argLengths
[10]; /* only {0} thru {9} supported */
1055 argCount
= efs
->argCount
;
1056 JS_ASSERT(argCount
<= 10);
1059 * Gather the arguments into an array, and accumulate
1060 * their sizes. We allocate 1 more than necessary and
1061 * null it out to act as the caboose when we free the
1064 reportp
->messageArgs
= (const jschar
**)
1065 JS_malloc(cx
, sizeof(jschar
*) * (argCount
+ 1));
1066 if (!reportp
->messageArgs
)
1068 reportp
->messageArgs
[argCount
] = NULL
;
1069 for (i
= 0; i
< argCount
; i
++) {
1071 char *charArg
= va_arg(ap
, char *);
1072 size_t charArgLength
= strlen(charArg
);
1073 reportp
->messageArgs
[i
]
1074 = js_InflateString(cx
, charArg
, &charArgLength
);
1075 if (!reportp
->messageArgs
[i
])
1078 reportp
->messageArgs
[i
] = va_arg(ap
, jschar
*);
1080 argLengths
[i
] = js_strlen(reportp
->messageArgs
[i
]);
1081 totalArgsLength
+= argLengths
[i
];
1083 /* NULL-terminate for easy copying. */
1084 reportp
->messageArgs
[i
] = NULL
;
1087 * Parse the error format, substituting the argument X
1088 * for {X} in the format.
1092 jschar
*buffer
, *fmt
, *out
;
1093 int expandedArgs
= 0;
1094 size_t expandedLength
;
1095 size_t len
= strlen(efs
->format
);
1097 buffer
= fmt
= js_InflateString (cx
, efs
->format
, &len
);
1100 expandedLength
= len
1101 - (3 * argCount
) /* exclude the {n} */
1105 * Note - the above calculation assumes that each argument
1106 * is used once and only once in the expansion !!!
1108 reportp
->ucmessage
= out
= (jschar
*)
1109 JS_malloc(cx
, (expandedLength
+ 1) * sizeof(jschar
));
1111 JS_free (cx
, buffer
);
1116 if (isdigit(fmt
[1])) {
1117 int d
= JS7_UNDEC(fmt
[1]);
1118 JS_ASSERT(d
< argCount
);
1119 js_strncpy(out
, reportp
->messageArgs
[d
],
1121 out
+= argLengths
[d
];
1129 JS_ASSERT(expandedArgs
== argCount
);
1131 JS_free (cx
, buffer
);
1133 js_DeflateString(cx
, reportp
->ucmessage
,
1134 (size_t)(out
- reportp
->ucmessage
));
1140 * Zero arguments: the format string (if it exists) is the
1145 *messagep
= JS_strdup(cx
, efs
->format
);
1148 len
= strlen(*messagep
);
1149 reportp
->ucmessage
= js_InflateString(cx
, *messagep
, &len
);
1150 if (!reportp
->ucmessage
)
1155 if (*messagep
== NULL
) {
1156 /* where's the right place for this ??? */
1157 const char *defaultErrorMessage
1158 = "No error message available for error number %d";
1159 size_t nbytes
= strlen(defaultErrorMessage
) + 16;
1160 *messagep
= (char *)JS_malloc(cx
, nbytes
);
1163 JS_snprintf(*messagep
, nbytes
, defaultErrorMessage
, errorNumber
);
1168 if (reportp
->messageArgs
) {
1169 /* free the arguments only if we allocated them */
1172 while (reportp
->messageArgs
[i
])
1173 JS_free(cx
, (void *)reportp
->messageArgs
[i
++]);
1175 JS_free(cx
, (void *)reportp
->messageArgs
);
1176 reportp
->messageArgs
= NULL
;
1178 if (reportp
->ucmessage
) {
1179 JS_free(cx
, (void *)reportp
->ucmessage
);
1180 reportp
->ucmessage
= NULL
;
1183 JS_free(cx
, (void *)*messagep
);
1190 js_ReportErrorNumberVA(JSContext
*cx
, uintN flags
, JSErrorCallback callback
,
1191 void *userRef
, const uintN errorNumber
,
1192 JSBool charArgs
, va_list ap
)
1194 JSErrorReport report
;
1198 if ((flags
& JSREPORT_STRICT
) && !JS_HAS_STRICT_OPTION(cx
))
1201 memset(&report
, 0, sizeof (struct JSErrorReport
));
1202 report
.flags
= flags
;
1203 report
.errorNumber
= errorNumber
;
1204 PopulateReportBlame(cx
, &report
);
1206 if (!js_ExpandErrorArguments(cx
, callback
, userRef
, errorNumber
,
1207 &message
, &report
, &warning
, charArgs
, ap
)) {
1211 ReportError(cx
, message
, &report
);
1214 JS_free(cx
, message
);
1215 if (report
.messageArgs
) {
1217 * js_ExpandErrorArguments owns its messageArgs only if it had to
1218 * inflate the arguments (from regular |char *|s).
1222 while (report
.messageArgs
[i
])
1223 JS_free(cx
, (void *)report
.messageArgs
[i
++]);
1225 JS_free(cx
, (void *)report
.messageArgs
);
1227 if (report
.ucmessage
)
1228 JS_free(cx
, (void *)report
.ucmessage
);
1234 js_ReportErrorAgain(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
1236 JSErrorReporter onError
;
1241 if (cx
->lastMessage
)
1242 free(cx
->lastMessage
);
1243 cx
->lastMessage
= JS_strdup(cx
, message
);
1244 if (!cx
->lastMessage
)
1246 onError
= cx
->errorReporter
;
1249 * If debugErrorHook is present then we give it a chance to veto
1250 * sending the error on to the regular ErrorReporter.
1253 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
1255 !hook(cx
, cx
->lastMessage
, reportp
,
1256 cx
->debugHooks
->debugErrorHookData
)) {
1261 onError(cx
, cx
->lastMessage
, reportp
);
1265 js_ReportIsNotDefined(JSContext
*cx
, const char *name
)
1267 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_DEFINED
, name
);
1271 js_ReportIsNullOrUndefined(JSContext
*cx
, intN spindex
, jsval v
,
1277 bytes
= js_DecompileValueGenerator(cx
, spindex
, v
, fallback
);
1281 if (strcmp(bytes
, js_undefined_str
) == 0 ||
1282 strcmp(bytes
, js_null_str
) == 0) {
1283 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1284 js_GetErrorMessage
, NULL
,
1285 JSMSG_NO_PROPERTIES
, bytes
,
1287 } else if (JSVAL_IS_VOID(v
)) {
1288 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1289 js_GetErrorMessage
, NULL
,
1290 JSMSG_NULL_OR_UNDEFINED
, bytes
,
1291 js_undefined_str
, NULL
);
1293 JS_ASSERT(JSVAL_IS_NULL(v
));
1294 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1295 js_GetErrorMessage
, NULL
,
1296 JSMSG_NULL_OR_UNDEFINED
, bytes
,
1305 js_ReportMissingArg(JSContext
*cx
, jsval
*vp
, uintN arg
)
1311 JS_snprintf(argbuf
, sizeof argbuf
, "%u", arg
);
1313 if (VALUE_IS_FUNCTION(cx
, *vp
)) {
1314 atom
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(*vp
))->atom
;
1315 bytes
= js_DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, *vp
,
1316 ATOM_TO_STRING(atom
));
1320 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1321 JSMSG_MISSING_FUN_ARG
, argbuf
,
1322 bytes
? bytes
: "");
1327 js_ReportValueErrorFlags(JSContext
*cx
, uintN flags
, const uintN errorNumber
,
1328 intN spindex
, jsval v
, JSString
*fallback
,
1329 const char *arg1
, const char *arg2
)
1334 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
>= 1);
1335 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
<= 3);
1336 bytes
= js_DecompileValueGenerator(cx
, spindex
, v
, fallback
);
1340 ok
= JS_ReportErrorFlagsAndNumber(cx
, flags
, js_GetErrorMessage
,
1341 NULL
, errorNumber
, bytes
, arg1
, arg2
);
1346 #if defined DEBUG && defined XP_UNIX
1347 /* For gdb usage. */
1348 void js_traceon(JSContext
*cx
) { cx
->tracefp
= stderr
; }
1349 void js_traceoff(JSContext
*cx
) { cx
->tracefp
= NULL
; }
1352 JSErrorFormatString js_ErrorFormatString
[JSErr_Limit
] = {
1353 #define MSG_DEF(name, number, count, exception, format) \
1354 { format, count, exception } ,
1359 JS_FRIEND_API(const JSErrorFormatString
*)
1360 js_GetErrorMessage(void *userRef
, const char *locale
, const uintN errorNumber
)
1362 if ((errorNumber
> 0) && (errorNumber
< JSErr_Limit
))
1363 return &js_ErrorFormatString
[errorNumber
];
1368 js_ResetOperationCount(JSContext
*cx
)
1373 JS_ASSERT(cx
->operationCount
<= 0);
1374 JS_ASSERT(cx
->operationLimit
> 0);
1376 cx
->operationCount
= (int32
) cx
->operationLimit
;
1377 if (cx
->operationCallbackIsSet
)
1378 return cx
->operationCallback(cx
);
1380 if (cx
->operationCallback
) {
1382 * Invoke the deprecated branch callback. It may be called only when
1383 * the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK
1386 fp
= js_GetTopStackFrame(cx
);
1387 script
= fp
? fp
->script
: NULL
;
1388 if (script
|| JS_HAS_OPTION(cx
, JSOPTION_NATIVE_BRANCH_CALLBACK
))
1389 return ((JSBranchCallback
) cx
->operationCallback
)(cx
, script
);
1395 /* This is defined in jstracer.cpp in JS_TRACER builds. */
1396 extern JS_FORCES_STACK JSStackFrame
*
1397 js_GetTopStackFrame(JSContext
*cx
)
1404 js_GetScriptedCaller(JSContext
*cx
, JSStackFrame
*fp
)
1407 fp
= js_GetTopStackFrame(cx
);