Bug 463806 - [PATCH][@font-face] Downloaded font activation on Mac may fail due to...
[wine-gecko.git] / js / src / jscntxt.cpp
blob42353aedfd7ae9b60921f83b5a9754877e030fc3
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
44 #include "jsstddef.h"
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include "jstypes.h"
49 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jsclist.h"
52 #include "jsprf.h"
53 #include "jsatom.h"
54 #include "jscntxt.h"
55 #include "jsversion.h"
56 #include "jsdbgapi.h"
57 #include "jsexn.h"
58 #include "jsfun.h"
59 #include "jsgc.h"
60 #include "jslock.h"
61 #include "jsnum.h"
62 #include "jsobj.h"
63 #include "jsopcode.h"
64 #include "jsscan.h"
65 #include "jsscope.h"
66 #include "jsscript.h"
67 #include "jsstr.h"
68 #include "jstracer.h"
70 #ifdef JS_THREADSAFE
71 #include "prtypes.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;
81 JS_BEGIN_EXTERN_C
82 JSBool
83 js_InitThreadPrivateIndex(void (*ptr)(void *))
85 PRStatus status;
87 if (tpIndexInited)
88 return JS_TRUE;
90 status = PR_NewThreadPrivateIndex(&threadTPIndex, ptr);
92 if (status == PR_SUCCESS)
93 tpIndexInited = JS_TRUE;
94 return status == PR_SUCCESS;
96 JS_END_EXTERN_C
98 JS_BEGIN_EXTERN_C
99 JSBool
100 js_CleanupThreadPrivateData()
102 if (!tpIndexInited)
103 return JS_TRUE;
104 return PR_SetThreadPrivate(threadTPIndex, NULL) == PR_SUCCESS;
106 JS_END_EXTERN_C
109 * Callback function to delete a JSThread info when the thread that owns it
110 * is destroyed.
112 void
113 js_ThreadDestructorCB(void *ptr)
115 JSThread *thread = (JSThread *)ptr;
117 if (!thread)
118 return;
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);
128 #endif
129 free(thread);
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.
143 JSThread *
144 js_GetCurrentThread(JSRuntime *rt)
146 JSThread *thread;
148 thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex);
149 if (!thread) {
150 thread = (JSThread *) malloc(sizeof(JSThread));
151 if (!thread)
152 return NULL;
153 #ifdef DEBUG
154 memset(thread, JS_FREE_PATTERN, sizeof(JSThread));
155 #endif
156 if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) {
157 free(thread);
158 return NULL;
161 JS_INIT_CLIST(&thread->contextList);
162 thread->id = js_CurrentThreadId();
163 thread->gcMallocBytes = 0;
164 #ifdef JS_TRACER
165 memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor));
166 js_InitJIT(&thread->traceMonitor);
167 #endif
168 thread->scriptsToGC = NULL;
171 * js_SetContextThread initializes the remaining fields as necessary.
174 return thread;
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.
182 JSBool
183 js_SetContextThread(JSContext *cx)
185 JSThread *thread = js_GetCurrentThread(cx->runtime);
187 if (!thread) {
188 JS_ReportOutOfMemory(cx);
189 return JS_FALSE;
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);
203 if (!cx->thread)
204 JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
205 cx->thread = thread;
206 return JS_TRUE;
209 /* Remove the owning thread info of a context. */
210 void
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);
219 cx->thread = NULL;
222 #endif /* JS_THREADSAFE */
224 void
225 js_OnVersionChange(JSContext *cx)
227 #ifdef DEBUG
228 JSVersion version = JSVERSION_NUMBER(cx);
230 JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
231 #endif
234 void
235 js_SetVersion(JSContext *cx, JSVersion version)
237 cx->version = version;
238 js_OnVersionChange(cx);
241 JSContext *
242 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
244 JSContext *cx;
245 JSBool ok, first;
246 JSContextCallback cxCallback;
248 cx = (JSContext *) malloc(sizeof *cx);
249 if (!cx)
250 return NULL;
251 memset(cx, 0, sizeof *cx);
253 cx->runtime = rt;
254 JS_ClearOperationCallback(cx);
255 cx->debugHooks = &rt->globalDebugHooks;
256 #if JS_STACK_GROWTH_DIRECTION > 0
257 cx->stackLimit = (jsuword)-1;
258 #endif
259 cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
260 #ifdef JS_THREADSAFE
261 cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
262 JS_INIT_CLIST(&cx->threadLinks);
263 js_SetContextThread(cx);
264 #endif
266 JS_LOCK_GC(rt);
267 for (;;) {
268 first = (rt->contextList.next == &rt->contextList);
269 if (rt->state == JSRTS_UP) {
270 JS_ASSERT(!first);
271 break;
273 if (rt->state == JSRTS_DOWN) {
274 JS_ASSERT(first);
275 rt->state = JSRTS_LAUNCHING;
276 break;
278 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
280 JS_APPEND_LINK(&cx->links, &rt->contextList);
281 JS_UNLOCK_GC(rt);
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:
301 * INITIAL_BACKTRACK
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);
311 return NULL;
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".
324 if (first) {
325 #ifdef JS_THREADSAFE
326 JS_BeginRequest(cx);
327 #endif
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
333 * not necessary.
335 if (ok && !rt->scriptFilenameTable)
336 ok = js_InitRuntimeScriptState(rt);
337 if (ok)
338 ok = js_InitRuntimeNumberState(cx);
339 if (ok)
340 ok = js_InitRuntimeStringState(cx);
341 #ifdef JS_THREADSAFE
342 JS_EndRequest(cx);
343 #endif
344 if (!ok) {
345 js_DestroyContext(cx, JSDCM_NEW_FAILED);
346 return NULL;
349 JS_LOCK_GC(rt);
350 rt->state = JSRTS_UP;
351 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
352 JS_UNLOCK_GC(rt);
355 cxCallback = rt->cxCallback;
356 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
357 js_DestroyContext(cx, JSDCM_NEW_FAILED);
358 return NULL;
361 return cx;
364 void
365 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
367 JSRuntime *rt;
368 JSContextCallback cxCallback;
369 JSBool last;
370 JSArgumentFormatMap *map;
371 JSLocalRootStack *lrs;
372 JSLocalRootChunk *lrc;
374 rt = cx->runtime;
376 if (mode != JSDCM_NEW_FAILED) {
377 cxCallback = rt->cxCallback;
378 if (cxCallback) {
380 * JSCONTEXT_DESTROY callback is not allowed to fail and must
381 * return true.
383 #ifdef DEBUG
384 JSBool callbackStatus =
385 #endif
386 cxCallback(cx, JSCONTEXT_DESTROY);
387 JS_ASSERT(callbackStatus);
391 /* Remove cx from context list first. */
392 JS_LOCK_GC(rt);
393 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
394 JS_REMOVE_LINK(&cx->links);
395 last = (rt->contextList.next == &rt->contextList);
396 if (last)
397 rt->state = JSRTS_LANDING;
398 #ifdef JS_THREADSAFE
399 js_RevokeGCLocalFreeLists(cx);
400 #endif
401 JS_UNLOCK_GC(rt);
403 if (last) {
404 #ifdef JS_THREADSAFE
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
413 * will return early.
415 if (cx->requestDepth == 0)
416 JS_BeginRequest(cx);
417 #endif
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);
439 #ifdef JS_THREADSAFE
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
449 * data structure.
451 while (cx->requestDepth != 0)
452 JS_EndRequest(cx);
453 #endif
455 if (last) {
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. */
466 JS_LOCK_GC(rt);
467 rt->state = JSRTS_DOWN;
468 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
469 JS_UNLOCK_GC(rt);
470 } else {
471 if (mode == JSDCM_FORCE_GC)
472 js_GC(cx, GC_NORMAL);
473 else if (mode == JSDCM_MAYBE_GC)
474 JS_MaybeGC(cx);
477 /* Free the stuff hanging off of cx. */
478 JS_FinishArenaPool(&cx->stackPool);
479 JS_FinishArenaPool(&cx->tempPool);
480 JS_FinishArenaPool(&cx->regexpPool);
482 if (cx->lastMessage)
483 free(cx->lastMessage);
485 /* Remove any argument formatters. */
486 map = cx->argumentFormatMap;
487 while (map) {
488 JSArgumentFormatMap *temp = map;
489 map = map->next;
490 JS_free(cx, temp);
493 /* Destroy the resolve recursion damper. */
494 if (cx->resolvingTable) {
495 JS_DHashTableDestroy(cx->resolvingTable);
496 cx->resolvingTable = NULL;
499 lrs = cx->localRootStack;
500 if (lrs) {
501 while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
502 lrs->topChunk = lrc->down;
503 JS_free(cx, lrc);
505 JS_free(cx, lrs);
508 #ifdef JS_THREADSAFE
509 js_ClearContextThread(cx);
510 #endif
512 /* Finally, free cx itself. */
513 free(cx);
516 JSBool
517 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
519 JSCList *cl;
521 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
522 if (cl == &cx->links)
523 return JS_TRUE;
525 JS_RUNTIME_METER(rt, deadContexts);
526 return JS_FALSE;
529 JSContext *
530 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
532 JSContext *cx = *iterp;
534 if (unlocked)
535 JS_LOCK_GC(rt);
536 cx = (JSContext *) (cx ? cx->links.next : rt->contextList.next);
537 if (&cx->links == &rt->contextList)
538 cx = NULL;
539 *iterp = cx;
540 if (unlocked)
541 JS_UNLOCK_GC(rt);
542 return cx;
545 static JSDHashNumber
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,
556 const void *ptr)
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 = {
565 JS_DHashAllocTable,
566 JS_DHashFreeTable,
567 resolving_HashKey,
568 resolving_MatchEntry,
569 JS_DHashMoveEntryStub,
570 JS_DHashClearEntryStub,
571 JS_DHashFinalizeStub,
572 NULL
575 JSBool
576 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
577 JSResolvingEntry **entryp)
579 JSDHashTable *table;
580 JSResolvingEntry *entry;
582 table = cx->resolvingTable;
583 if (!table) {
584 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
585 sizeof(JSResolvingEntry),
586 JS_DHASH_MIN_SIZE);
587 if (!table)
588 goto outofmem;
589 cx->resolvingTable = table;
592 entry = (JSResolvingEntry *)
593 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
594 if (!entry)
595 goto outofmem;
597 if (entry->flags & flag) {
598 /* An entry for (key, flag) exists already -- dampen recursion. */
599 entry = NULL;
600 } else {
601 /* Fill in key if we were the first to add entry, then set flag. */
602 if (!entry->key.obj)
603 entry->key = *key;
604 entry->flags |= flag;
606 *entryp = entry;
607 return JS_TRUE;
609 outofmem:
610 JS_ReportOutOfMemory(cx);
611 return JS_FALSE;
614 void
615 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
616 JSResolvingEntry *entry, uint32 generation)
618 JSDHashTable *table;
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;
632 if (entry->flags)
633 return;
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);
643 else
644 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
647 JSBool
648 js_EnterLocalRootScope(JSContext *cx)
650 JSLocalRootStack *lrs;
651 int mark;
653 lrs = cx->localRootStack;
654 if (!lrs) {
655 lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
656 if (!lrs)
657 return JS_FALSE;
658 lrs->scopeMark = JSLRS_NULL_MARK;
659 lrs->rootCount = 0;
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));
667 if (mark < 0)
668 return JS_FALSE;
669 lrs->scopeMark = (uint32) mark;
670 return JS_TRUE;
673 void
674 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
676 JSLocalRootStack *lrs;
677 uint32 mark, m, n;
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)
684 return;
686 mark = lrs->scopeMark;
687 JS_ASSERT(mark != JSLRS_NULL_MARK);
688 if (mark == JSLRS_NULL_MARK)
689 return;
691 /* Free any chunks being popped by this leave operation. */
692 m = mark >> JSLRS_CHUNK_SHIFT;
693 n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
694 while (n > m) {
695 lrc = lrs->topChunk;
696 JS_ASSERT(lrc != &lrs->firstChunk);
697 lrs->topChunk = lrc->down;
698 JS_free(cx, lrc);
699 --n;
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.
708 lrc = lrs->topChunk;
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)) {
712 if (mark == 0) {
713 cx->weakRoots.lastInternalResult = rval;
714 } else {
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;
722 ++mark;
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.
736 if (mark == 0) {
737 cx->localRootStack = NULL;
738 JS_free(cx, lrs);
739 } else if (m == 0) {
740 lrs->topChunk = lrc->down;
741 JS_free(cx, lrc);
745 void
746 js_ForgetLocalRoot(JSContext *cx, jsval v)
748 JSLocalRootStack *lrs;
749 uint32 i, j, m, n, mark;
750 JSLocalRootChunk *lrc, *lrc2;
751 jsval top;
753 lrs = cx->localRootStack;
754 JS_ASSERT(lrs && lrs->rootCount);
755 if (!lrs || lrs->rootCount == 0)
756 return;
758 /* Prepare to pop the top-most value from the stack. */
759 n = lrs->rootCount - 1;
760 m = n & JSLRS_CHUNK_MASK;
761 lrc = lrs->topChunk;
762 top = lrc->roots[m];
764 /* Be paranoid about calls on an empty scope. */
765 mark = lrs->scopeMark;
766 JS_ASSERT(mark < n);
767 if (mark >= n)
768 return;
770 /* If v was not the last root pushed in the top scope, find it. */
771 if (top != v) {
772 /* Search downward in case v was recently pushed. */
773 i = n;
774 j = m;
775 lrc2 = lrc;
776 while (--i > mark) {
777 if (j == 0)
778 lrc2 = lrc2->down;
779 j = i & JSLRS_CHUNK_MASK;
780 if (lrc2->roots[j] == v)
781 break;
784 /* If we didn't find v in this scope, assert and bail out. */
785 JS_ASSERT(i != mark);
786 if (i == mark)
787 return;
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;
795 lrs->rootCount = n;
796 if (m == 0) {
797 JS_ASSERT(n != 0);
798 JS_ASSERT(lrc != &lrs->firstChunk);
799 lrs->topChunk = lrc->down;
800 JS_free(cx, lrc);
805 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
807 uint32 n, m;
808 JSLocalRootChunk *lrc;
810 n = lrs->rootCount;
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);
820 return -1;
822 lrc = lrs->topChunk;
823 JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
824 } else {
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);
830 if (!lrc)
831 return -1;
832 lrc->down = lrs->topChunk;
833 lrs->topChunk = lrc;
835 lrs->rootCount = n + 1;
836 lrc->roots[m] = v;
837 return (int) n;
840 void
841 js_TraceLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
843 uint32 n, m, mark;
844 JSLocalRootChunk *lrc;
845 jsval v;
847 n = lrs->rootCount;
848 if (n == 0)
849 return;
851 mark = lrs->scopeMark;
852 lrc = lrs->topChunk;
853 do {
854 while (--n > mark) {
855 m = n & JSLRS_CHUNK_MASK;
856 v = lrc->roots[m];
857 JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
858 JS_SET_TRACING_INDEX(trc, "local_root", n);
859 js_CallValueTracerIfGCThing(trc, v);
860 if (m == 0)
861 lrc = lrc->down;
863 m = n & JSLRS_CHUNK_MASK;
864 mark = JSVAL_TO_INT(lrc->roots[m]);
865 if (m == 0)
866 lrc = lrc->down;
867 } while (n != 0);
868 JS_ASSERT(!lrc);
871 static void
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.
880 JS_ASSERT(reportp);
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 */
897 if (hook)
898 hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
902 /* The report must be initially zeroed. */
903 static void
904 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
906 JSStackFrame *fp;
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) {
913 if (fp->regs) {
914 report->filename = fp->script->filename;
915 report->lineno = js_FramePCToLineNumber(cx, fp);
916 break;
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.
928 void
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;
952 if (onError) {
953 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
954 if (hook &&
955 !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
956 onError = NULL;
960 if (onError)
961 onError(cx, msg, &report);
964 void
965 js_ReportOutOfScriptQuota(JSContext *cx)
967 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
968 JSMSG_SCRIPT_STACK_QUOTA);
971 void
972 js_ReportOverRecursed(JSContext *cx)
974 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
977 void
978 js_ReportAllocationOverflow(JSContext *cx)
980 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
983 JSBool
984 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
986 char *message;
987 jschar *ucmessage;
988 size_t messagelen;
989 JSErrorReport report;
990 JSBool warning;
992 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
993 return JS_TRUE;
995 message = JS_vsmprintf(format, ap);
996 if (!message)
997 return JS_FALSE;
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;
1009 warning = JS_FALSE;
1012 ReportError(cx, message, &report);
1013 free(message);
1014 JS_free(cx, ucmessage);
1015 return warning;
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).
1029 JSBool
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;
1036 int i;
1037 int argCount;
1039 *warningp = JSREPORT_IS_WARNING(reportp->flags);
1040 if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
1041 reportp->flags &= ~JSREPORT_WARNING;
1042 *warningp = JS_FALSE;
1045 *messagep = NULL;
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);
1050 else
1051 efs = callback(userRef, NULL, errorNumber);
1052 if (efs) {
1053 size_t totalArgsLength = 0;
1054 size_t argLengths[10]; /* only {0} thru {9} supported */
1055 argCount = efs->argCount;
1056 JS_ASSERT(argCount <= 10);
1057 if (argCount > 0) {
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
1062 * pointers later.
1064 reportp->messageArgs = (const jschar **)
1065 JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
1066 if (!reportp->messageArgs)
1067 return JS_FALSE;
1068 reportp->messageArgs[argCount] = NULL;
1069 for (i = 0; i < argCount; i++) {
1070 if (charArgs) {
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])
1076 goto error;
1077 } else {
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.
1090 if (argCount > 0) {
1091 if (efs->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);
1098 if (!buffer)
1099 goto error;
1100 expandedLength = len
1101 - (3 * argCount) /* exclude the {n} */
1102 + totalArgsLength;
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));
1110 if (!out) {
1111 JS_free (cx, buffer);
1112 goto error;
1114 while (*fmt) {
1115 if (*fmt == '{') {
1116 if (isdigit(fmt[1])) {
1117 int d = JS7_UNDEC(fmt[1]);
1118 JS_ASSERT(d < argCount);
1119 js_strncpy(out, reportp->messageArgs[d],
1120 argLengths[d]);
1121 out += argLengths[d];
1122 fmt += 3;
1123 expandedArgs++;
1124 continue;
1127 *out++ = *fmt++;
1129 JS_ASSERT(expandedArgs == argCount);
1130 *out = 0;
1131 JS_free (cx, buffer);
1132 *messagep =
1133 js_DeflateString(cx, reportp->ucmessage,
1134 (size_t)(out - reportp->ucmessage));
1135 if (!*messagep)
1136 goto error;
1138 } else {
1140 * Zero arguments: the format string (if it exists) is the
1141 * entire message.
1143 if (efs->format) {
1144 size_t len;
1145 *messagep = JS_strdup(cx, efs->format);
1146 if (!*messagep)
1147 goto error;
1148 len = strlen(*messagep);
1149 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1150 if (!reportp->ucmessage)
1151 goto error;
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);
1161 if (!*messagep)
1162 goto error;
1163 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1165 return JS_TRUE;
1167 error:
1168 if (reportp->messageArgs) {
1169 /* free the arguments only if we allocated them */
1170 if (charArgs) {
1171 i = 0;
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;
1182 if (*messagep) {
1183 JS_free(cx, (void *)*messagep);
1184 *messagep = NULL;
1186 return JS_FALSE;
1189 JSBool
1190 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1191 void *userRef, const uintN errorNumber,
1192 JSBool charArgs, va_list ap)
1194 JSErrorReport report;
1195 char *message;
1196 JSBool warning;
1198 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1199 return JS_TRUE;
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)) {
1208 return JS_FALSE;
1211 ReportError(cx, message, &report);
1213 if (message)
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).
1220 if (charArgs) {
1221 int i = 0;
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);
1230 return warning;
1233 JS_FRIEND_API(void)
1234 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1236 JSErrorReporter onError;
1238 if (!message)
1239 return;
1241 if (cx->lastMessage)
1242 free(cx->lastMessage);
1243 cx->lastMessage = JS_strdup(cx, message);
1244 if (!cx->lastMessage)
1245 return;
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.
1252 if (onError) {
1253 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1254 if (hook &&
1255 !hook(cx, cx->lastMessage, reportp,
1256 cx->debugHooks->debugErrorHookData)) {
1257 onError = NULL;
1260 if (onError)
1261 onError(cx, cx->lastMessage, reportp);
1264 void
1265 js_ReportIsNotDefined(JSContext *cx, const char *name)
1267 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1270 JSBool
1271 js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
1272 JSString *fallback)
1274 char *bytes;
1275 JSBool ok;
1277 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1278 if (!bytes)
1279 return JS_FALSE;
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,
1286 NULL, NULL);
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);
1292 } else {
1293 JS_ASSERT(JSVAL_IS_NULL(v));
1294 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1295 js_GetErrorMessage, NULL,
1296 JSMSG_NULL_OR_UNDEFINED, bytes,
1297 js_null_str, NULL);
1300 JS_free(cx, bytes);
1301 return ok;
1304 void
1305 js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
1307 char argbuf[11];
1308 char *bytes;
1309 JSAtom *atom;
1311 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1312 bytes = NULL;
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));
1317 if (!bytes)
1318 return;
1320 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1321 JSMSG_MISSING_FUN_ARG, argbuf,
1322 bytes ? bytes : "");
1323 JS_free(cx, bytes);
1326 JSBool
1327 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1328 intN spindex, jsval v, JSString *fallback,
1329 const char *arg1, const char *arg2)
1331 char *bytes;
1332 JSBool ok;
1334 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1335 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1336 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1337 if (!bytes)
1338 return JS_FALSE;
1340 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1341 NULL, errorNumber, bytes, arg1, arg2);
1342 JS_free(cx, bytes);
1343 return ok;
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; }
1350 #endif
1352 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1353 #define MSG_DEF(name, number, count, exception, format) \
1354 { format, count, exception } ,
1355 #include "js.msg"
1356 #undef MSG_DEF
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];
1364 return NULL;
1367 JSBool
1368 js_ResetOperationCount(JSContext *cx)
1370 JSScript *script;
1371 JSStackFrame *fp;
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
1384 * is set.
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);
1391 return JS_TRUE;
1394 #ifndef JS_TRACER
1395 /* This is defined in jstracer.cpp in JS_TRACER builds. */
1396 extern JS_FORCES_STACK JSStackFrame *
1397 js_GetTopStackFrame(JSContext *cx)
1399 return cx->fp;
1401 #endif
1403 JSStackFrame *
1404 js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1406 if (!fp)
1407 fp = js_GetTopStackFrame(cx);
1408 while (fp) {
1409 if (fp->script)
1410 return fp;
1411 fp = fp->down;
1413 return NULL;