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 */
76 * The index for JSThread info, returned by PR_NewThreadPrivateIndex. The
77 * index value is visible and shared by all threads, but the data associated
78 * with it is private to each thread.
80 static PRUintn threadTPIndex
;
81 static JSBool tpIndexInited
= JS_FALSE
;
84 js_InitThreadPrivateIndex(void (JS_DLL_CALLBACK
*ptr
)(void *))
91 status
= PR_NewThreadPrivateIndex(&threadTPIndex
, ptr
);
93 if (status
== PR_SUCCESS
)
94 tpIndexInited
= JS_TRUE
;
95 return status
== PR_SUCCESS
;
99 * Callback function to delete a JSThread info when the thread that owns it
103 js_ThreadDestructorCB(void *ptr
)
105 JSThread
*thread
= (JSThread
*)ptr
;
111 * Check that this thread properly called either JS_DestroyContext or
112 * JS_ClearContextThread on each JSContext it created or used.
114 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread
->contextList
));
115 GSN_CACHE_CLEAR(&thread
->gsnCache
);
116 #if defined JS_TRACER
117 js_FinishJIT(&thread
->traceMonitor
);
123 * Get current thread-local JSThread info, creating one if it doesn't exist.
124 * Each thread has a unique JSThread pointer.
126 * Since we are dealing with thread-local data, no lock is needed.
128 * Return a pointer to the thread local info, NULL if the system runs out
129 * of memory, or it failed to set thread private data (neither case is very
130 * likely; both are probably due to out-of-memory). It is up to the caller
131 * to report an error, if possible.
134 js_GetCurrentThread(JSRuntime
*rt
)
138 thread
= (JSThread
*)PR_GetThreadPrivate(threadTPIndex
);
140 thread
= (JSThread
*) malloc(sizeof(JSThread
));
144 memset(thread
, JS_FREE_PATTERN
, sizeof(JSThread
));
146 if (PR_FAILURE
== PR_SetThreadPrivate(threadTPIndex
, thread
)) {
151 JS_INIT_CLIST(&thread
->contextList
);
152 thread
->id
= js_CurrentThreadId();
153 thread
->gcMallocBytes
= 0;
155 memset(&thread
->traceMonitor
, 0, sizeof(thread
->traceMonitor
));
156 js_InitJIT(&thread
->traceMonitor
);
158 thread
->scriptsToGC
= NULL
;
161 * js_SetContextThread initializes the remaining fields as necessary.
168 * Sets current thread as owning thread of a context by assigning the
169 * thread-private info to the context. If the current thread doesn't have
170 * private JSThread info, create one.
173 js_SetContextThread(JSContext
*cx
)
175 JSThread
*thread
= js_GetCurrentThread(cx
->runtime
);
178 JS_ReportOutOfMemory(cx
);
183 * Clear gcFreeLists and caches on each transition from 0 to 1 context
184 * active on the current thread. See bug 351602 and bug 425828.
186 if (JS_CLIST_IS_EMPTY(&thread
->contextList
)) {
187 memset(thread
->gcFreeLists
, 0, sizeof(thread
->gcFreeLists
));
188 memset(&thread
->gsnCache
, 0, sizeof(thread
->gsnCache
));
189 memset(&thread
->propertyCache
, 0, sizeof(thread
->propertyCache
));
192 /* Assert that the previous cx->thread called JS_ClearContextThread(). */
193 JS_ASSERT(!cx
->thread
|| cx
->thread
== thread
);
195 JS_APPEND_LINK(&cx
->threadLinks
, &thread
->contextList
);
200 /* Remove the owning thread info of a context. */
202 js_ClearContextThread(JSContext
*cx
)
205 * If cx is associated with a thread, this must be called only from that
206 * thread. If not, this is a harmless no-op.
208 JS_ASSERT(cx
->thread
== js_GetCurrentThread(cx
->runtime
) || !cx
->thread
);
209 JS_REMOVE_AND_INIT_LINK(&cx
->threadLinks
);
211 if (JS_CLIST_IS_EMPTY(&cx
->thread
->contextList
)) {
212 memset(cx
->thread
->gcFreeLists
, JS_FREE_PATTERN
,
213 sizeof(cx
->thread
->gcFreeLists
));
219 #endif /* JS_THREADSAFE */
222 js_OnVersionChange(JSContext
*cx
)
225 JSVersion version
= JSVERSION_NUMBER(cx
);
227 JS_ASSERT(version
== JSVERSION_DEFAULT
|| version
>= JSVERSION_ECMA_3
);
232 js_SetVersion(JSContext
*cx
, JSVersion version
)
234 cx
->version
= version
;
235 js_OnVersionChange(cx
);
239 js_NewContext(JSRuntime
*rt
, size_t stackChunkSize
)
243 JSContextCallback cxCallback
;
245 cx
= (JSContext
*) malloc(sizeof *cx
);
248 memset(cx
, 0, sizeof *cx
);
251 JS_ClearOperationCallback(cx
);
252 cx
->debugHooks
= &rt
->globalDebugHooks
;
253 #if JS_STACK_GROWTH_DIRECTION > 0
254 cx
->stackLimit
= (jsuword
)-1;
256 cx
->scriptStackQuota
= JS_DEFAULT_SCRIPT_STACK_QUOTA
;
258 JS_INIT_CLIST(&cx
->threadLinks
);
259 js_SetContextThread(cx
);
264 first
= (rt
->contextList
.next
== &rt
->contextList
);
265 if (rt
->state
== JSRTS_UP
) {
269 if (rt
->state
== JSRTS_DOWN
) {
271 rt
->state
= JSRTS_LAUNCHING
;
274 JS_WAIT_CONDVAR(rt
->stateChange
, JS_NO_TIMEOUT
);
276 JS_APPEND_LINK(&cx
->links
, &rt
->contextList
);
280 * First we do the infallible, every-time per-context initializations.
281 * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
282 * or the stuff under 'if (first)' below) fail, at least the version
283 * and arena-pools will be valid and safe to use (say, from the last GC
284 * done by js_DestroyContext).
286 cx
->version
= JSVERSION_DEFAULT
;
287 JS_INIT_ARENA_POOL(&cx
->stackPool
, "stack", stackChunkSize
, sizeof(jsval
),
288 &cx
->scriptStackQuota
);
289 JS_INIT_ARENA_POOL(&cx
->tempPool
, "temp", 1024, sizeof(jsdouble
),
290 &cx
->scriptStackQuota
);
292 if (!js_InitRegExpStatics(cx
, &cx
->regExpStatics
)) {
293 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
298 * If cx is the first context on this runtime, initialize well-known atoms,
299 * keywords, numbers, and strings. If one of these steps should fail, the
300 * runtime will be left in a partially initialized state, with zeroes and
301 * nulls stored in the default-initialized remainder of the struct. We'll
302 * clean the runtime up under js_DestroyContext, because cx will be "last"
303 * as well as "first".
309 ok
= js_InitCommonAtoms(cx
);
312 * scriptFilenameTable may be left over from a previous episode of
313 * non-zero contexts alive in rt, so don't re-init the table if it's
316 if (ok
&& !rt
->scriptFilenameTable
)
317 ok
= js_InitRuntimeScriptState(rt
);
319 ok
= js_InitRuntimeNumberState(cx
);
321 ok
= js_InitRuntimeStringState(cx
);
326 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
331 rt
->state
= JSRTS_UP
;
332 JS_NOTIFY_ALL_CONDVAR(rt
->stateChange
);
336 cxCallback
= rt
->cxCallback
;
337 if (cxCallback
&& !cxCallback(cx
, JSCONTEXT_NEW
)) {
338 js_DestroyContext(cx
, JSDCM_NEW_FAILED
);
346 js_DestroyContext(JSContext
*cx
, JSDestroyContextMode mode
)
349 JSContextCallback cxCallback
;
351 JSArgumentFormatMap
*map
;
352 JSLocalRootStack
*lrs
;
353 JSLocalRootChunk
*lrc
;
357 if (mode
!= JSDCM_NEW_FAILED
) {
358 cxCallback
= rt
->cxCallback
;
361 * JSCONTEXT_DESTROY callback is not allowed to fail and must
365 JSBool callbackStatus
=
367 cxCallback(cx
, JSCONTEXT_DESTROY
);
368 JS_ASSERT(callbackStatus
);
372 /* Remove cx from context list first. */
374 JS_ASSERT(rt
->state
== JSRTS_UP
|| rt
->state
== JSRTS_LAUNCHING
);
375 JS_REMOVE_LINK(&cx
->links
);
376 last
= (rt
->contextList
.next
== &rt
->contextList
);
378 rt
->state
= JSRTS_LANDING
;
384 * If cx is not in a request already, begin one now so that we wait
385 * for any racing GC started on a not-last context to finish, before
386 * we plow ahead and unpin atoms. Note that even though we begin a
387 * request here if necessary, we end all requests on cx below before
388 * forcing a final GC. This lets any not-last context destruction
389 * racing in another thread try to force or maybe run the GC, but by
390 * that point, rt->state will not be JSRTS_UP, and that GC attempt
393 if (cx
->requestDepth
== 0)
397 /* Unlock and clear GC things held by runtime pointers. */
398 js_FinishRuntimeNumberState(cx
);
399 js_FinishRuntimeStringState(cx
);
401 /* Unpin all common atoms before final GC. */
402 js_FinishCommonAtoms(cx
);
404 /* Clear debugging state to remove GC roots. */
405 JS_ClearAllTraps(cx
);
406 JS_ClearAllWatchPoints(cx
);
410 * Remove more GC roots in regExpStatics, then collect garbage.
411 * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
412 * XXX this function call to wait for any racing GC to complete, in the
413 * XXX case where JS_DestroyContext is called outside of a request on cx
415 js_FreeRegExpStatics(cx
, &cx
->regExpStatics
);
419 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
420 * end our request here in case we are "last" -- in that event, another
421 * js_DestroyContext that was not last might be waiting in the GC for our
422 * request to end. We'll let it run below, just before we do the truly
423 * final GC and then free atom state.
425 * At this point, cx must be inaccessible to other threads. It's off the
426 * rt->contextList, and it should not be reachable via any object private
429 while (cx
->requestDepth
!= 0)
434 js_GC(cx
, GC_LAST_CONTEXT
);
437 * Free the script filename table if it exists and is empty. Do this
438 * after the last GC to avoid finalizers tripping on free memory.
440 if (rt
->scriptFilenameTable
&& rt
->scriptFilenameTable
->nentries
== 0)
441 js_FinishRuntimeScriptState(rt
);
443 /* Take the runtime down, now that it has no contexts or atoms. */
445 rt
->state
= JSRTS_DOWN
;
446 JS_NOTIFY_ALL_CONDVAR(rt
->stateChange
);
449 if (mode
== JSDCM_FORCE_GC
)
450 js_GC(cx
, GC_NORMAL
);
451 else if (mode
== JSDCM_MAYBE_GC
)
455 /* Free the stuff hanging off of cx. */
456 JS_FinishArenaPool(&cx
->stackPool
);
457 JS_FinishArenaPool(&cx
->tempPool
);
460 free(cx
->lastMessage
);
462 /* Remove any argument formatters. */
463 map
= cx
->argumentFormatMap
;
465 JSArgumentFormatMap
*temp
= map
;
470 /* Destroy the resolve recursion damper. */
471 if (cx
->resolvingTable
) {
472 JS_DHashTableDestroy(cx
->resolvingTable
);
473 cx
->resolvingTable
= NULL
;
476 lrs
= cx
->localRootStack
;
478 while ((lrc
= lrs
->topChunk
) != &lrs
->firstChunk
) {
479 lrs
->topChunk
= lrc
->down
;
486 js_ClearContextThread(cx
);
489 /* Finally, free cx itself. */
494 js_ValidContextPointer(JSRuntime
*rt
, JSContext
*cx
)
498 for (cl
= rt
->contextList
.next
; cl
!= &rt
->contextList
; cl
= cl
->next
) {
499 if (cl
== &cx
->links
)
502 JS_RUNTIME_METER(rt
, deadContexts
);
507 js_ContextIterator(JSRuntime
*rt
, JSBool unlocked
, JSContext
**iterp
)
509 JSContext
*cx
= *iterp
;
514 cx
= (JSContext
*)&rt
->contextList
;
515 cx
= (JSContext
*)cx
->links
.next
;
516 if (&cx
->links
== &rt
->contextList
)
524 JS_STATIC_DLL_CALLBACK(JSDHashNumber
)
525 resolving_HashKey(JSDHashTable
*table
, const void *ptr
)
527 const JSResolvingKey
*key
= (const JSResolvingKey
*)ptr
;
529 return ((JSDHashNumber
)JS_PTR_TO_UINT32(key
->obj
) >> JSVAL_TAGBITS
) ^ key
->id
;
532 JS_PUBLIC_API(JSBool
)
533 resolving_MatchEntry(JSDHashTable
*table
,
534 const JSDHashEntryHdr
*hdr
,
537 const JSResolvingEntry
*entry
= (const JSResolvingEntry
*)hdr
;
538 const JSResolvingKey
*key
= (const JSResolvingKey
*)ptr
;
540 return entry
->key
.obj
== key
->obj
&& entry
->key
.id
== key
->id
;
543 static const JSDHashTableOps resolving_dhash_ops
= {
547 resolving_MatchEntry
,
548 JS_DHashMoveEntryStub
,
549 JS_DHashClearEntryStub
,
550 JS_DHashFinalizeStub
,
555 js_StartResolving(JSContext
*cx
, JSResolvingKey
*key
, uint32 flag
,
556 JSResolvingEntry
**entryp
)
559 JSResolvingEntry
*entry
;
561 table
= cx
->resolvingTable
;
563 table
= JS_NewDHashTable(&resolving_dhash_ops
, NULL
,
564 sizeof(JSResolvingEntry
),
568 cx
->resolvingTable
= table
;
571 entry
= (JSResolvingEntry
*)
572 JS_DHashTableOperate(table
, key
, JS_DHASH_ADD
);
576 if (entry
->flags
& flag
) {
577 /* An entry for (key, flag) exists already -- dampen recursion. */
580 /* Fill in key if we were the first to add entry, then set flag. */
583 entry
->flags
|= flag
;
589 JS_ReportOutOfMemory(cx
);
594 js_StopResolving(JSContext
*cx
, JSResolvingKey
*key
, uint32 flag
,
595 JSResolvingEntry
*entry
, uint32 generation
)
600 * Clear flag from entry->flags and return early if other flags remain.
601 * We must take care to re-lookup entry if the table has changed since
602 * it was found by js_StartResolving.
604 table
= cx
->resolvingTable
;
605 if (!entry
|| table
->generation
!= generation
) {
606 entry
= (JSResolvingEntry
*)
607 JS_DHashTableOperate(table
, key
, JS_DHASH_LOOKUP
);
609 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
));
610 entry
->flags
&= ~flag
;
615 * Do a raw remove only if fewer entries were removed than would cause
616 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
617 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
618 * compressing or shrinking the table as needed.
620 if (table
->removedCount
< JS_DHASH_TABLE_SIZE(table
) >> 2)
621 JS_DHashTableRawRemove(table
, &entry
->hdr
);
623 JS_DHashTableOperate(table
, key
, JS_DHASH_REMOVE
);
627 js_EnterLocalRootScope(JSContext
*cx
)
629 JSLocalRootStack
*lrs
;
632 lrs
= cx
->localRootStack
;
634 lrs
= (JSLocalRootStack
*) JS_malloc(cx
, sizeof *lrs
);
637 lrs
->scopeMark
= JSLRS_NULL_MARK
;
639 lrs
->topChunk
= &lrs
->firstChunk
;
640 lrs
->firstChunk
.down
= NULL
;
641 cx
->localRootStack
= lrs
;
644 /* Push lrs->scopeMark to save it for restore when leaving. */
645 mark
= js_PushLocalRoot(cx
, lrs
, INT_TO_JSVAL(lrs
->scopeMark
));
648 lrs
->scopeMark
= (uint32
) mark
;
653 js_LeaveLocalRootScopeWithResult(JSContext
*cx
, jsval rval
)
655 JSLocalRootStack
*lrs
;
657 JSLocalRootChunk
*lrc
;
659 /* Defend against buggy native callers. */
660 lrs
= cx
->localRootStack
;
661 JS_ASSERT(lrs
&& lrs
->rootCount
!= 0);
662 if (!lrs
|| lrs
->rootCount
== 0)
665 mark
= lrs
->scopeMark
;
666 JS_ASSERT(mark
!= JSLRS_NULL_MARK
);
667 if (mark
== JSLRS_NULL_MARK
)
670 /* Free any chunks being popped by this leave operation. */
671 m
= mark
>> JSLRS_CHUNK_SHIFT
;
672 n
= (lrs
->rootCount
- 1) >> JSLRS_CHUNK_SHIFT
;
675 JS_ASSERT(lrc
!= &lrs
->firstChunk
);
676 lrs
->topChunk
= lrc
->down
;
682 * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
683 * it on the caller's scope, or store it in lastInternalResult if we are
684 * leaving the outermost scope. We don't need to allocate a new lrc
685 * because we can overwrite the old mark's slot with rval.
688 m
= mark
& JSLRS_CHUNK_MASK
;
689 lrs
->scopeMark
= (uint32
) JSVAL_TO_INT(lrc
->roots
[m
]);
690 if (JSVAL_IS_GCTHING(rval
) && !JSVAL_IS_NULL(rval
)) {
692 cx
->weakRoots
.lastInternalResult
= rval
;
695 * Increment m to avoid the "else if (m == 0)" case below. If
696 * rval is not a GC-thing, that case would take care of freeing
697 * any chunk that contained only the old mark. Since rval *is*
698 * a GC-thing here, we want to reuse that old mark's slot.
700 lrc
->roots
[m
++] = rval
;
704 lrs
->rootCount
= (uint32
) mark
;
707 * Free the stack eagerly, risking malloc churn. The alternative would
708 * require an lrs->entryCount member, maintained by Enter and Leave, and
709 * tested by the GC in addition to the cx->localRootStack non-null test.
711 * That approach would risk hoarding 264 bytes (net) per context. Right
712 * now it seems better to give fresh (dirty in CPU write-back cache, and
713 * the data is no longer needed) memory back to the malloc heap.
716 cx
->localRootStack
= NULL
;
719 lrs
->topChunk
= lrc
->down
;
725 js_ForgetLocalRoot(JSContext
*cx
, jsval v
)
727 JSLocalRootStack
*lrs
;
728 uint32 i
, j
, m
, n
, mark
;
729 JSLocalRootChunk
*lrc
, *lrc2
;
732 lrs
= cx
->localRootStack
;
733 JS_ASSERT(lrs
&& lrs
->rootCount
);
734 if (!lrs
|| lrs
->rootCount
== 0)
737 /* Prepare to pop the top-most value from the stack. */
738 n
= lrs
->rootCount
- 1;
739 m
= n
& JSLRS_CHUNK_MASK
;
743 /* Be paranoid about calls on an empty scope. */
744 mark
= lrs
->scopeMark
;
749 /* If v was not the last root pushed in the top scope, find it. */
751 /* Search downward in case v was recently pushed. */
758 j
= i
& JSLRS_CHUNK_MASK
;
759 if (lrc2
->roots
[j
] == v
)
763 /* If we didn't find v in this scope, assert and bail out. */
764 JS_ASSERT(i
!= mark
);
768 /* Swap top and v so common tail code can pop v. */
769 lrc2
->roots
[j
] = top
;
772 /* Pop the last value from the stack. */
773 lrc
->roots
[m
] = JSVAL_NULL
;
777 JS_ASSERT(lrc
!= &lrs
->firstChunk
);
778 lrs
->topChunk
= lrc
->down
;
784 js_PushLocalRoot(JSContext
*cx
, JSLocalRootStack
*lrs
, jsval v
)
787 JSLocalRootChunk
*lrc
;
790 m
= n
& JSLRS_CHUNK_MASK
;
791 if (n
== 0 || m
!= 0) {
793 * At start of first chunk, or not at start of a non-first top chunk.
794 * Check for lrs->rootCount overflow.
796 if ((uint32
)(n
+ 1) == 0) {
797 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
798 JSMSG_TOO_MANY_LOCAL_ROOTS
);
802 JS_ASSERT(n
!= 0 || lrc
== &lrs
->firstChunk
);
805 * After lrs->firstChunk, trying to index at a power-of-two chunk
806 * boundary: need a new chunk.
808 lrc
= (JSLocalRootChunk
*) JS_malloc(cx
, sizeof *lrc
);
811 lrc
->down
= lrs
->topChunk
;
814 lrs
->rootCount
= n
+ 1;
820 js_TraceLocalRoots(JSTracer
*trc
, JSLocalRootStack
*lrs
)
823 JSLocalRootChunk
*lrc
;
830 mark
= lrs
->scopeMark
;
834 m
= n
& JSLRS_CHUNK_MASK
;
836 JS_ASSERT(JSVAL_IS_GCTHING(v
) && v
!= JSVAL_NULL
);
837 JS_SET_TRACING_INDEX(trc
, "local_root", n
);
838 js_CallValueTracerIfGCThing(trc
, v
);
842 m
= n
& JSLRS_CHUNK_MASK
;
843 mark
= JSVAL_TO_INT(lrc
->roots
[m
]);
851 ReportError(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
854 * Check the error report, and set a JavaScript-catchable exception
855 * if the error is defined to have an associated exception. If an
856 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
857 * on the error report, and exception-aware hosts should ignore it.
860 if (reportp
->errorNumber
== JSMSG_UNCAUGHT_EXCEPTION
)
861 reportp
->flags
|= JSREPORT_EXCEPTION
;
864 * Call the error reporter only if an exception wasn't raised.
866 * If an exception was raised, then we call the debugErrorHook
867 * (if present) to give it a chance to see the error before it
868 * propagates out of scope. This is needed for compatability
869 * with the old scheme.
871 if (!js_ErrorToException(cx
, message
, reportp
)) {
872 js_ReportErrorAgain(cx
, message
, reportp
);
873 } else if (cx
->debugHooks
->debugErrorHook
&& cx
->errorReporter
) {
874 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
875 /* test local in case debugErrorHook changed on another thread */
877 hook(cx
, message
, reportp
, cx
->debugHooks
->debugErrorHookData
);
882 * We don't post an exception in this case, since doing so runs into
883 * complications of pre-allocating an exception object which required
884 * running the Exception class initializer early etc.
885 * Instead we just invoke the errorReporter with an "Out Of Memory"
886 * type message, and then hope the process ends swiftly.
889 js_ReportOutOfMemory(JSContext
*cx
)
892 JSErrorReport report
;
893 JSErrorReporter onError
= cx
->errorReporter
;
895 /* Get the message for this error, but we won't expand any arguments. */
896 const JSErrorFormatString
*efs
=
897 js_GetLocalizedErrorMessage(cx
, NULL
, NULL
, JSMSG_OUT_OF_MEMORY
);
898 const char *msg
= efs
? efs
->format
: "Out of memory";
900 /* Fill out the report, but don't do anything that requires allocation. */
901 memset(&report
, 0, sizeof (struct JSErrorReport
));
902 report
.flags
= JSREPORT_ERROR
;
903 report
.errorNumber
= JSMSG_OUT_OF_MEMORY
;
906 * Walk stack until we find a frame that is associated with some script
907 * rather than a native frame.
909 for (fp
= cx
->fp
; fp
; fp
= fp
->down
) {
911 report
.filename
= fp
->script
->filename
;
912 report
.lineno
= js_PCToLineNumber(cx
, fp
->script
, fp
->regs
->pc
);
918 * If debugErrorHook is present then we give it a chance to veto sending
919 * the error on to the regular ErrorReporter. We also clear a pending
920 * exception if any now so the hooks can replace the out-of-memory error
921 * by a script-catchable exception.
923 cx
->throwing
= JS_FALSE
;
925 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
927 !hook(cx
, msg
, &report
, cx
->debugHooks
->debugErrorHookData
)) {
933 onError(cx
, msg
, &report
);
937 js_ReportOutOfScriptQuota(JSContext
*cx
)
939 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
940 JSMSG_SCRIPT_STACK_QUOTA
);
944 js_ReportOverRecursed(JSContext
*cx
)
946 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_OVER_RECURSED
);
950 js_ReportAllocationOverflow(JSContext
*cx
)
952 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_ALLOC_OVERFLOW
);
956 js_ReportErrorVA(JSContext
*cx
, uintN flags
, const char *format
, va_list ap
)
962 JSErrorReport report
;
965 if ((flags
& JSREPORT_STRICT
) && !JS_HAS_STRICT_OPTION(cx
))
968 message
= JS_vsmprintf(format
, ap
);
971 messagelen
= strlen(message
);
973 memset(&report
, 0, sizeof (struct JSErrorReport
));
974 report
.flags
= flags
;
975 report
.errorNumber
= JSMSG_USER_DEFINED_ERROR
;
976 report
.ucmessage
= ucmessage
= js_InflateString(cx
, message
, &messagelen
);
978 /* Find the top-most active script frame, for best line number blame. */
979 for (fp
= cx
->fp
; fp
; fp
= fp
->down
) {
981 report
.filename
= fp
->script
->filename
;
982 report
.lineno
= js_PCToLineNumber(cx
, fp
->script
, fp
->regs
->pc
);
987 warning
= JSREPORT_IS_WARNING(report
.flags
);
988 if (warning
&& JS_HAS_WERROR_OPTION(cx
)) {
989 report
.flags
&= ~JSREPORT_WARNING
;
993 ReportError(cx
, message
, &report
);
995 JS_free(cx
, ucmessage
);
1000 * The arguments from ap need to be packaged up into an array and stored
1001 * into the report struct.
1003 * The format string addressed by the error number may contain operands
1004 * identified by the format {N}, where N is a decimal digit. Each of these
1005 * is to be replaced by the Nth argument from the va_list. The complete
1006 * message is placed into reportp->ucmessage converted to a JSString.
1008 * Returns true if the expansion succeeds (can fail if out of memory).
1011 js_ExpandErrorArguments(JSContext
*cx
, JSErrorCallback callback
,
1012 void *userRef
, const uintN errorNumber
,
1013 char **messagep
, JSErrorReport
*reportp
,
1014 JSBool
*warningp
, JSBool charArgs
, va_list ap
)
1016 const JSErrorFormatString
*efs
;
1020 *warningp
= JSREPORT_IS_WARNING(reportp
->flags
);
1021 if (*warningp
&& JS_HAS_WERROR_OPTION(cx
)) {
1022 reportp
->flags
&= ~JSREPORT_WARNING
;
1023 *warningp
= JS_FALSE
;
1028 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1029 if (!callback
|| callback
== js_GetErrorMessage
)
1030 efs
= js_GetLocalizedErrorMessage(cx
, userRef
, NULL
, errorNumber
);
1032 efs
= callback(userRef
, NULL
, errorNumber
);
1034 size_t totalArgsLength
= 0;
1035 size_t argLengths
[10]; /* only {0} thru {9} supported */
1036 argCount
= efs
->argCount
;
1037 JS_ASSERT(argCount
<= 10);
1040 * Gather the arguments into an array, and accumulate
1041 * their sizes. We allocate 1 more than necessary and
1042 * null it out to act as the caboose when we free the
1045 reportp
->messageArgs
= (const jschar
**)
1046 JS_malloc(cx
, sizeof(jschar
*) * (argCount
+ 1));
1047 if (!reportp
->messageArgs
)
1049 reportp
->messageArgs
[argCount
] = NULL
;
1050 for (i
= 0; i
< argCount
; i
++) {
1052 char *charArg
= va_arg(ap
, char *);
1053 size_t charArgLength
= strlen(charArg
);
1054 reportp
->messageArgs
[i
]
1055 = js_InflateString(cx
, charArg
, &charArgLength
);
1056 if (!reportp
->messageArgs
[i
])
1059 reportp
->messageArgs
[i
] = va_arg(ap
, jschar
*);
1061 argLengths
[i
] = js_strlen(reportp
->messageArgs
[i
]);
1062 totalArgsLength
+= argLengths
[i
];
1064 /* NULL-terminate for easy copying. */
1065 reportp
->messageArgs
[i
] = NULL
;
1068 * Parse the error format, substituting the argument X
1069 * for {X} in the format.
1073 jschar
*buffer
, *fmt
, *out
;
1074 int expandedArgs
= 0;
1075 size_t expandedLength
;
1076 size_t len
= strlen(efs
->format
);
1078 buffer
= fmt
= js_InflateString (cx
, efs
->format
, &len
);
1081 expandedLength
= len
1082 - (3 * argCount
) /* exclude the {n} */
1086 * Note - the above calculation assumes that each argument
1087 * is used once and only once in the expansion !!!
1089 reportp
->ucmessage
= out
= (jschar
*)
1090 JS_malloc(cx
, (expandedLength
+ 1) * sizeof(jschar
));
1092 JS_free (cx
, buffer
);
1097 if (isdigit(fmt
[1])) {
1098 int d
= JS7_UNDEC(fmt
[1]);
1099 JS_ASSERT(d
< argCount
);
1100 js_strncpy(out
, reportp
->messageArgs
[d
],
1102 out
+= argLengths
[d
];
1110 JS_ASSERT(expandedArgs
== argCount
);
1112 JS_free (cx
, buffer
);
1114 js_DeflateString(cx
, reportp
->ucmessage
,
1115 (size_t)(out
- reportp
->ucmessage
));
1121 * Zero arguments: the format string (if it exists) is the
1126 *messagep
= JS_strdup(cx
, efs
->format
);
1129 len
= strlen(*messagep
);
1130 reportp
->ucmessage
= js_InflateString(cx
, *messagep
, &len
);
1131 if (!reportp
->ucmessage
)
1136 if (*messagep
== NULL
) {
1137 /* where's the right place for this ??? */
1138 const char *defaultErrorMessage
1139 = "No error message available for error number %d";
1140 size_t nbytes
= strlen(defaultErrorMessage
) + 16;
1141 *messagep
= (char *)JS_malloc(cx
, nbytes
);
1144 JS_snprintf(*messagep
, nbytes
, defaultErrorMessage
, errorNumber
);
1149 if (reportp
->messageArgs
) {
1150 /* free the arguments only if we allocated them */
1153 while (reportp
->messageArgs
[i
])
1154 JS_free(cx
, (void *)reportp
->messageArgs
[i
++]);
1156 JS_free(cx
, (void *)reportp
->messageArgs
);
1157 reportp
->messageArgs
= NULL
;
1159 if (reportp
->ucmessage
) {
1160 JS_free(cx
, (void *)reportp
->ucmessage
);
1161 reportp
->ucmessage
= NULL
;
1164 JS_free(cx
, (void *)*messagep
);
1171 js_ReportErrorNumberVA(JSContext
*cx
, uintN flags
, JSErrorCallback callback
,
1172 void *userRef
, const uintN errorNumber
,
1173 JSBool charArgs
, va_list ap
)
1176 JSErrorReport report
;
1180 if ((flags
& JSREPORT_STRICT
) && !JS_HAS_STRICT_OPTION(cx
))
1183 memset(&report
, 0, sizeof (struct JSErrorReport
));
1184 report
.flags
= flags
;
1185 report
.errorNumber
= errorNumber
;
1188 * If we can't find out where the error was based on the current frame,
1189 * see if the next frame has a script/pc combo we can use.
1191 for (fp
= cx
->fp
; fp
; fp
= fp
->down
) {
1193 report
.filename
= fp
->script
->filename
;
1194 report
.lineno
= js_PCToLineNumber(cx
, fp
->script
, fp
->regs
->pc
);
1199 if (!js_ExpandErrorArguments(cx
, callback
, userRef
, errorNumber
,
1200 &message
, &report
, &warning
, charArgs
, ap
)) {
1204 ReportError(cx
, message
, &report
);
1207 JS_free(cx
, message
);
1208 if (report
.messageArgs
) {
1210 * js_ExpandErrorArguments owns its messageArgs only if it had to
1211 * inflate the arguments (from regular |char *|s).
1215 while (report
.messageArgs
[i
])
1216 JS_free(cx
, (void *)report
.messageArgs
[i
++]);
1218 JS_free(cx
, (void *)report
.messageArgs
);
1220 if (report
.ucmessage
)
1221 JS_free(cx
, (void *)report
.ucmessage
);
1227 js_ReportErrorAgain(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
1229 JSErrorReporter onError
;
1234 if (cx
->lastMessage
)
1235 free(cx
->lastMessage
);
1236 cx
->lastMessage
= JS_strdup(cx
, message
);
1237 if (!cx
->lastMessage
)
1239 onError
= cx
->errorReporter
;
1242 * If debugErrorHook is present then we give it a chance to veto
1243 * sending the error on to the regular ErrorReporter.
1246 JSDebugErrorHook hook
= cx
->debugHooks
->debugErrorHook
;
1248 !hook(cx
, cx
->lastMessage
, reportp
,
1249 cx
->debugHooks
->debugErrorHookData
)) {
1254 onError(cx
, cx
->lastMessage
, reportp
);
1258 js_ReportIsNotDefined(JSContext
*cx
, const char *name
)
1260 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_DEFINED
, name
);
1264 js_ReportIsNullOrUndefined(JSContext
*cx
, intN spindex
, jsval v
,
1270 bytes
= js_DecompileValueGenerator(cx
, spindex
, v
, fallback
);
1274 if (strcmp(bytes
, js_undefined_str
) == 0 ||
1275 strcmp(bytes
, js_null_str
) == 0) {
1276 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1277 js_GetErrorMessage
, NULL
,
1278 JSMSG_NO_PROPERTIES
, bytes
,
1280 } else if (JSVAL_IS_VOID(v
)) {
1281 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1282 js_GetErrorMessage
, NULL
,
1283 JSMSG_NULL_OR_UNDEFINED
, bytes
,
1284 js_undefined_str
, NULL
);
1286 JS_ASSERT(JSVAL_IS_NULL(v
));
1287 ok
= JS_ReportErrorFlagsAndNumber(cx
, JSREPORT_ERROR
,
1288 js_GetErrorMessage
, NULL
,
1289 JSMSG_NULL_OR_UNDEFINED
, bytes
,
1298 js_ReportMissingArg(JSContext
*cx
, jsval
*vp
, uintN arg
)
1304 JS_snprintf(argbuf
, sizeof argbuf
, "%u", arg
);
1306 if (VALUE_IS_FUNCTION(cx
, *vp
)) {
1307 atom
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(*vp
))->atom
;
1308 bytes
= js_DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, *vp
,
1309 ATOM_TO_STRING(atom
));
1313 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1314 JSMSG_MISSING_FUN_ARG
, argbuf
,
1315 bytes
? bytes
: "");
1320 js_ReportValueErrorFlags(JSContext
*cx
, uintN flags
, const uintN errorNumber
,
1321 intN spindex
, jsval v
, JSString
*fallback
,
1322 const char *arg1
, const char *arg2
)
1327 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
>= 1);
1328 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
<= 3);
1329 bytes
= js_DecompileValueGenerator(cx
, spindex
, v
, fallback
);
1333 ok
= JS_ReportErrorFlagsAndNumber(cx
, flags
, js_GetErrorMessage
,
1334 NULL
, errorNumber
, bytes
, arg1
, arg2
);
1339 #if defined DEBUG && defined XP_UNIX
1340 /* For gdb usage. */
1341 void js_traceon(JSContext
*cx
) { cx
->tracefp
= stderr
; }
1342 void js_traceoff(JSContext
*cx
) { cx
->tracefp
= NULL
; }
1345 JSErrorFormatString js_ErrorFormatString
[JSErr_Limit
] = {
1346 #define MSG_DEF(name, number, count, exception, format) \
1347 { format, count, exception } ,
1352 const JSErrorFormatString
*
1353 js_GetErrorMessage(void *userRef
, const char *locale
, const uintN errorNumber
)
1355 if ((errorNumber
> 0) && (errorNumber
< JSErr_Limit
))
1356 return &js_ErrorFormatString
[errorNumber
];
1361 js_ResetOperationCount(JSContext
*cx
)
1365 JS_ASSERT(cx
->operationCount
<= 0);
1366 JS_ASSERT(cx
->operationLimit
> 0);
1368 cx
->operationCount
= (int32
) cx
->operationLimit
;
1369 if (cx
->operationCallbackIsSet
)
1370 return cx
->operationCallback(cx
);
1372 if (cx
->operationCallback
) {
1374 * Invoke the deprecated branch callback. It may be called only when
1375 * the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK
1378 script
= cx
->fp
? cx
->fp
->script
: NULL
;
1379 if (script
|| JS_HAS_OPTION(cx
, JSOPTION_NATIVE_BRANCH_CALLBACK
))
1380 return ((JSBranchCallback
) cx
->operationCallback
)(cx
, script
);