Bug 438861 Autocomplete does not pass back previous results to a second search. r...
[mozilla-central.git] / js / src / jscntxt.cpp
blob8476cd3170cda09d6784dc980cd0f93c191dd78f
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 "jsconfig.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 #ifdef JS_TRACER
69 #include "jstracer.h"
70 #endif
72 #ifdef JS_THREADSAFE
73 #include "prtypes.h"
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;
83 JSBool
84 js_InitThreadPrivateIndex(void (JS_DLL_CALLBACK *ptr)(void *))
86 PRStatus status;
88 if (tpIndexInited)
89 return JS_TRUE;
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
100 * is destroyed.
102 void JS_DLL_CALLBACK
103 js_ThreadDestructorCB(void *ptr)
105 JSThread *thread = (JSThread *)ptr;
107 if (!thread)
108 return;
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);
118 #endif
119 free(thread);
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.
133 JSThread *
134 js_GetCurrentThread(JSRuntime *rt)
136 JSThread *thread;
138 thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex);
139 if (!thread) {
140 thread = (JSThread *) malloc(sizeof(JSThread));
141 if (!thread)
142 return NULL;
143 #ifdef DEBUG
144 memset(thread, JS_FREE_PATTERN, sizeof(JSThread));
145 #endif
146 if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) {
147 free(thread);
148 return NULL;
151 JS_INIT_CLIST(&thread->contextList);
152 thread->id = js_CurrentThreadId();
153 thread->gcMallocBytes = 0;
154 #ifdef JS_TRACER
155 memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor));
156 js_InitJIT(&thread->traceMonitor);
157 #endif
158 thread->scriptsToGC = NULL;
161 * js_SetContextThread initializes the remaining fields as necessary.
164 return thread;
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.
172 JSBool
173 js_SetContextThread(JSContext *cx)
175 JSThread *thread = js_GetCurrentThread(cx->runtime);
177 if (!thread) {
178 JS_ReportOutOfMemory(cx);
179 return JS_FALSE;
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);
194 if (!cx->thread)
195 JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
196 cx->thread = thread;
197 return JS_TRUE;
200 /* Remove the owning thread info of a context. */
201 void
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);
210 #ifdef DEBUG
211 if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) {
212 memset(cx->thread->gcFreeLists, JS_FREE_PATTERN,
213 sizeof(cx->thread->gcFreeLists));
215 #endif
216 cx->thread = NULL;
219 #endif /* JS_THREADSAFE */
221 void
222 js_OnVersionChange(JSContext *cx)
224 #ifdef DEBUG
225 JSVersion version = JSVERSION_NUMBER(cx);
227 JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
228 #endif
231 void
232 js_SetVersion(JSContext *cx, JSVersion version)
234 cx->version = version;
235 js_OnVersionChange(cx);
238 JSContext *
239 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
241 JSContext *cx;
242 JSBool ok, first;
243 JSContextCallback cxCallback;
245 cx = (JSContext *) malloc(sizeof *cx);
246 if (!cx)
247 return NULL;
248 memset(cx, 0, sizeof *cx);
250 cx->runtime = rt;
251 JS_ClearOperationCallback(cx);
252 cx->debugHooks = &rt->globalDebugHooks;
253 #if JS_STACK_GROWTH_DIRECTION > 0
254 cx->stackLimit = (jsuword)-1;
255 #endif
256 cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
257 #ifdef JS_THREADSAFE
258 JS_INIT_CLIST(&cx->threadLinks);
259 js_SetContextThread(cx);
260 #endif
262 JS_LOCK_GC(rt);
263 for (;;) {
264 first = (rt->contextList.next == &rt->contextList);
265 if (rt->state == JSRTS_UP) {
266 JS_ASSERT(!first);
267 break;
269 if (rt->state == JSRTS_DOWN) {
270 JS_ASSERT(first);
271 rt->state = JSRTS_LAUNCHING;
272 break;
274 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
276 JS_APPEND_LINK(&cx->links, &rt->contextList);
277 JS_UNLOCK_GC(rt);
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);
294 return NULL;
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".
305 if (first) {
306 #ifdef JS_THREADSAFE
307 JS_BeginRequest(cx);
308 #endif
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
314 * not necessary.
316 if (ok && !rt->scriptFilenameTable)
317 ok = js_InitRuntimeScriptState(rt);
318 if (ok)
319 ok = js_InitRuntimeNumberState(cx);
320 if (ok)
321 ok = js_InitRuntimeStringState(cx);
322 #ifdef JS_THREADSAFE
323 JS_EndRequest(cx);
324 #endif
325 if (!ok) {
326 js_DestroyContext(cx, JSDCM_NEW_FAILED);
327 return NULL;
330 JS_LOCK_GC(rt);
331 rt->state = JSRTS_UP;
332 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
333 JS_UNLOCK_GC(rt);
336 cxCallback = rt->cxCallback;
337 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
338 js_DestroyContext(cx, JSDCM_NEW_FAILED);
339 return NULL;
342 return cx;
345 void
346 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
348 JSRuntime *rt;
349 JSContextCallback cxCallback;
350 JSBool last;
351 JSArgumentFormatMap *map;
352 JSLocalRootStack *lrs;
353 JSLocalRootChunk *lrc;
355 rt = cx->runtime;
357 if (mode != JSDCM_NEW_FAILED) {
358 cxCallback = rt->cxCallback;
359 if (cxCallback) {
361 * JSCONTEXT_DESTROY callback is not allowed to fail and must
362 * return true.
364 #ifdef DEBUG
365 JSBool callbackStatus =
366 #endif
367 cxCallback(cx, JSCONTEXT_DESTROY);
368 JS_ASSERT(callbackStatus);
372 /* Remove cx from context list first. */
373 JS_LOCK_GC(rt);
374 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
375 JS_REMOVE_LINK(&cx->links);
376 last = (rt->contextList.next == &rt->contextList);
377 if (last)
378 rt->state = JSRTS_LANDING;
379 JS_UNLOCK_GC(rt);
381 if (last) {
382 #ifdef JS_THREADSAFE
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
391 * will return early.
393 if (cx->requestDepth == 0)
394 JS_BeginRequest(cx);
395 #endif
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);
417 #ifdef JS_THREADSAFE
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
427 * data structure.
429 while (cx->requestDepth != 0)
430 JS_EndRequest(cx);
431 #endif
433 if (last) {
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. */
444 JS_LOCK_GC(rt);
445 rt->state = JSRTS_DOWN;
446 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
447 JS_UNLOCK_GC(rt);
448 } else {
449 if (mode == JSDCM_FORCE_GC)
450 js_GC(cx, GC_NORMAL);
451 else if (mode == JSDCM_MAYBE_GC)
452 JS_MaybeGC(cx);
455 /* Free the stuff hanging off of cx. */
456 JS_FinishArenaPool(&cx->stackPool);
457 JS_FinishArenaPool(&cx->tempPool);
459 if (cx->lastMessage)
460 free(cx->lastMessage);
462 /* Remove any argument formatters. */
463 map = cx->argumentFormatMap;
464 while (map) {
465 JSArgumentFormatMap *temp = map;
466 map = map->next;
467 JS_free(cx, temp);
470 /* Destroy the resolve recursion damper. */
471 if (cx->resolvingTable) {
472 JS_DHashTableDestroy(cx->resolvingTable);
473 cx->resolvingTable = NULL;
476 lrs = cx->localRootStack;
477 if (lrs) {
478 while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
479 lrs->topChunk = lrc->down;
480 JS_free(cx, lrc);
482 JS_free(cx, lrs);
485 #ifdef JS_THREADSAFE
486 js_ClearContextThread(cx);
487 #endif
489 /* Finally, free cx itself. */
490 free(cx);
493 JSBool
494 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
496 JSCList *cl;
498 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
499 if (cl == &cx->links)
500 return JS_TRUE;
502 JS_RUNTIME_METER(rt, deadContexts);
503 return JS_FALSE;
506 JSContext *
507 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
509 JSContext *cx = *iterp;
511 if (unlocked)
512 JS_LOCK_GC(rt);
513 if (!cx)
514 cx = (JSContext *)&rt->contextList;
515 cx = (JSContext *)cx->links.next;
516 if (&cx->links == &rt->contextList)
517 cx = NULL;
518 *iterp = cx;
519 if (unlocked)
520 JS_UNLOCK_GC(rt);
521 return cx;
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,
535 const void *ptr)
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 = {
544 JS_DHashAllocTable,
545 JS_DHashFreeTable,
546 resolving_HashKey,
547 resolving_MatchEntry,
548 JS_DHashMoveEntryStub,
549 JS_DHashClearEntryStub,
550 JS_DHashFinalizeStub,
551 NULL
554 JSBool
555 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
556 JSResolvingEntry **entryp)
558 JSDHashTable *table;
559 JSResolvingEntry *entry;
561 table = cx->resolvingTable;
562 if (!table) {
563 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
564 sizeof(JSResolvingEntry),
565 JS_DHASH_MIN_SIZE);
566 if (!table)
567 goto outofmem;
568 cx->resolvingTable = table;
571 entry = (JSResolvingEntry *)
572 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
573 if (!entry)
574 goto outofmem;
576 if (entry->flags & flag) {
577 /* An entry for (key, flag) exists already -- dampen recursion. */
578 entry = NULL;
579 } else {
580 /* Fill in key if we were the first to add entry, then set flag. */
581 if (!entry->key.obj)
582 entry->key = *key;
583 entry->flags |= flag;
585 *entryp = entry;
586 return JS_TRUE;
588 outofmem:
589 JS_ReportOutOfMemory(cx);
590 return JS_FALSE;
593 void
594 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
595 JSResolvingEntry *entry, uint32 generation)
597 JSDHashTable *table;
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;
611 if (entry->flags)
612 return;
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);
622 else
623 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
626 JSBool
627 js_EnterLocalRootScope(JSContext *cx)
629 JSLocalRootStack *lrs;
630 int mark;
632 lrs = cx->localRootStack;
633 if (!lrs) {
634 lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
635 if (!lrs)
636 return JS_FALSE;
637 lrs->scopeMark = JSLRS_NULL_MARK;
638 lrs->rootCount = 0;
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));
646 if (mark < 0)
647 return JS_FALSE;
648 lrs->scopeMark = (uint32) mark;
649 return JS_TRUE;
652 void
653 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
655 JSLocalRootStack *lrs;
656 uint32 mark, m, n;
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)
663 return;
665 mark = lrs->scopeMark;
666 JS_ASSERT(mark != JSLRS_NULL_MARK);
667 if (mark == JSLRS_NULL_MARK)
668 return;
670 /* Free any chunks being popped by this leave operation. */
671 m = mark >> JSLRS_CHUNK_SHIFT;
672 n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
673 while (n > m) {
674 lrc = lrs->topChunk;
675 JS_ASSERT(lrc != &lrs->firstChunk);
676 lrs->topChunk = lrc->down;
677 JS_free(cx, lrc);
678 --n;
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.
687 lrc = lrs->topChunk;
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)) {
691 if (mark == 0) {
692 cx->weakRoots.lastInternalResult = rval;
693 } else {
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;
701 ++mark;
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.
715 if (mark == 0) {
716 cx->localRootStack = NULL;
717 JS_free(cx, lrs);
718 } else if (m == 0) {
719 lrs->topChunk = lrc->down;
720 JS_free(cx, lrc);
724 void
725 js_ForgetLocalRoot(JSContext *cx, jsval v)
727 JSLocalRootStack *lrs;
728 uint32 i, j, m, n, mark;
729 JSLocalRootChunk *lrc, *lrc2;
730 jsval top;
732 lrs = cx->localRootStack;
733 JS_ASSERT(lrs && lrs->rootCount);
734 if (!lrs || lrs->rootCount == 0)
735 return;
737 /* Prepare to pop the top-most value from the stack. */
738 n = lrs->rootCount - 1;
739 m = n & JSLRS_CHUNK_MASK;
740 lrc = lrs->topChunk;
741 top = lrc->roots[m];
743 /* Be paranoid about calls on an empty scope. */
744 mark = lrs->scopeMark;
745 JS_ASSERT(mark < n);
746 if (mark >= n)
747 return;
749 /* If v was not the last root pushed in the top scope, find it. */
750 if (top != v) {
751 /* Search downward in case v was recently pushed. */
752 i = n;
753 j = m;
754 lrc2 = lrc;
755 while (--i > mark) {
756 if (j == 0)
757 lrc2 = lrc2->down;
758 j = i & JSLRS_CHUNK_MASK;
759 if (lrc2->roots[j] == v)
760 break;
763 /* If we didn't find v in this scope, assert and bail out. */
764 JS_ASSERT(i != mark);
765 if (i == mark)
766 return;
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;
774 lrs->rootCount = n;
775 if (m == 0) {
776 JS_ASSERT(n != 0);
777 JS_ASSERT(lrc != &lrs->firstChunk);
778 lrs->topChunk = lrc->down;
779 JS_free(cx, lrc);
784 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
786 uint32 n, m;
787 JSLocalRootChunk *lrc;
789 n = lrs->rootCount;
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);
799 return -1;
801 lrc = lrs->topChunk;
802 JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
803 } else {
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);
809 if (!lrc)
810 return -1;
811 lrc->down = lrs->topChunk;
812 lrs->topChunk = lrc;
814 lrs->rootCount = n + 1;
815 lrc->roots[m] = v;
816 return (int) n;
819 void
820 js_TraceLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
822 uint32 n, m, mark;
823 JSLocalRootChunk *lrc;
824 jsval v;
826 n = lrs->rootCount;
827 if (n == 0)
828 return;
830 mark = lrs->scopeMark;
831 lrc = lrs->topChunk;
832 do {
833 while (--n > mark) {
834 m = n & JSLRS_CHUNK_MASK;
835 v = lrc->roots[m];
836 JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
837 JS_SET_TRACING_INDEX(trc, "local_root", n);
838 js_CallValueTracerIfGCThing(trc, v);
839 if (m == 0)
840 lrc = lrc->down;
842 m = n & JSLRS_CHUNK_MASK;
843 mark = JSVAL_TO_INT(lrc->roots[m]);
844 if (m == 0)
845 lrc = lrc->down;
846 } while (n != 0);
847 JS_ASSERT(!lrc);
850 static void
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.
859 JS_ASSERT(reportp);
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 */
876 if (hook)
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.
888 void
889 js_ReportOutOfMemory(JSContext *cx)
891 JSStackFrame *fp;
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) {
910 if (fp->regs) {
911 report.filename = fp->script->filename;
912 report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
913 break;
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;
924 if (onError) {
925 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
926 if (hook &&
927 !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
928 onError = NULL;
932 if (onError)
933 onError(cx, msg, &report);
936 void
937 js_ReportOutOfScriptQuota(JSContext *cx)
939 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
940 JSMSG_SCRIPT_STACK_QUOTA);
943 void
944 js_ReportOverRecursed(JSContext *cx)
946 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
949 void
950 js_ReportAllocationOverflow(JSContext *cx)
952 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
955 JSBool
956 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
958 char *message;
959 jschar *ucmessage;
960 size_t messagelen;
961 JSStackFrame *fp;
962 JSErrorReport report;
963 JSBool warning;
965 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
966 return JS_TRUE;
968 message = JS_vsmprintf(format, ap);
969 if (!message)
970 return JS_FALSE;
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) {
980 if (fp->regs) {
981 report.filename = fp->script->filename;
982 report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
983 break;
987 warning = JSREPORT_IS_WARNING(report.flags);
988 if (warning && JS_HAS_WERROR_OPTION(cx)) {
989 report.flags &= ~JSREPORT_WARNING;
990 warning = JS_FALSE;
993 ReportError(cx, message, &report);
994 free(message);
995 JS_free(cx, ucmessage);
996 return warning;
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).
1010 JSBool
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;
1017 int i;
1018 int argCount;
1020 *warningp = JSREPORT_IS_WARNING(reportp->flags);
1021 if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
1022 reportp->flags &= ~JSREPORT_WARNING;
1023 *warningp = JS_FALSE;
1026 *messagep = NULL;
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);
1031 else
1032 efs = callback(userRef, NULL, errorNumber);
1033 if (efs) {
1034 size_t totalArgsLength = 0;
1035 size_t argLengths[10]; /* only {0} thru {9} supported */
1036 argCount = efs->argCount;
1037 JS_ASSERT(argCount <= 10);
1038 if (argCount > 0) {
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
1043 * pointers later.
1045 reportp->messageArgs = (const jschar **)
1046 JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
1047 if (!reportp->messageArgs)
1048 return JS_FALSE;
1049 reportp->messageArgs[argCount] = NULL;
1050 for (i = 0; i < argCount; i++) {
1051 if (charArgs) {
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])
1057 goto error;
1058 } else {
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.
1071 if (argCount > 0) {
1072 if (efs->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);
1079 if (!buffer)
1080 goto error;
1081 expandedLength = len
1082 - (3 * argCount) /* exclude the {n} */
1083 + totalArgsLength;
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));
1091 if (!out) {
1092 JS_free (cx, buffer);
1093 goto error;
1095 while (*fmt) {
1096 if (*fmt == '{') {
1097 if (isdigit(fmt[1])) {
1098 int d = JS7_UNDEC(fmt[1]);
1099 JS_ASSERT(d < argCount);
1100 js_strncpy(out, reportp->messageArgs[d],
1101 argLengths[d]);
1102 out += argLengths[d];
1103 fmt += 3;
1104 expandedArgs++;
1105 continue;
1108 *out++ = *fmt++;
1110 JS_ASSERT(expandedArgs == argCount);
1111 *out = 0;
1112 JS_free (cx, buffer);
1113 *messagep =
1114 js_DeflateString(cx, reportp->ucmessage,
1115 (size_t)(out - reportp->ucmessage));
1116 if (!*messagep)
1117 goto error;
1119 } else {
1121 * Zero arguments: the format string (if it exists) is the
1122 * entire message.
1124 if (efs->format) {
1125 size_t len;
1126 *messagep = JS_strdup(cx, efs->format);
1127 if (!*messagep)
1128 goto error;
1129 len = strlen(*messagep);
1130 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1131 if (!reportp->ucmessage)
1132 goto error;
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);
1142 if (!*messagep)
1143 goto error;
1144 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1146 return JS_TRUE;
1148 error:
1149 if (reportp->messageArgs) {
1150 /* free the arguments only if we allocated them */
1151 if (charArgs) {
1152 i = 0;
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;
1163 if (*messagep) {
1164 JS_free(cx, (void *)*messagep);
1165 *messagep = NULL;
1167 return JS_FALSE;
1170 JSBool
1171 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1172 void *userRef, const uintN errorNumber,
1173 JSBool charArgs, va_list ap)
1175 JSStackFrame *fp;
1176 JSErrorReport report;
1177 char *message;
1178 JSBool warning;
1180 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1181 return JS_TRUE;
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) {
1192 if (fp->regs) {
1193 report.filename = fp->script->filename;
1194 report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
1195 break;
1199 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1200 &message, &report, &warning, charArgs, ap)) {
1201 return JS_FALSE;
1204 ReportError(cx, message, &report);
1206 if (message)
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).
1213 if (charArgs) {
1214 int i = 0;
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);
1223 return warning;
1226 JS_FRIEND_API(void)
1227 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1229 JSErrorReporter onError;
1231 if (!message)
1232 return;
1234 if (cx->lastMessage)
1235 free(cx->lastMessage);
1236 cx->lastMessage = JS_strdup(cx, message);
1237 if (!cx->lastMessage)
1238 return;
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.
1245 if (onError) {
1246 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1247 if (hook &&
1248 !hook(cx, cx->lastMessage, reportp,
1249 cx->debugHooks->debugErrorHookData)) {
1250 onError = NULL;
1253 if (onError)
1254 onError(cx, cx->lastMessage, reportp);
1257 void
1258 js_ReportIsNotDefined(JSContext *cx, const char *name)
1260 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1263 JSBool
1264 js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
1265 JSString *fallback)
1267 char *bytes;
1268 JSBool ok;
1270 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1271 if (!bytes)
1272 return JS_FALSE;
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,
1279 NULL, NULL);
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);
1285 } else {
1286 JS_ASSERT(JSVAL_IS_NULL(v));
1287 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1288 js_GetErrorMessage, NULL,
1289 JSMSG_NULL_OR_UNDEFINED, bytes,
1290 js_null_str, NULL);
1293 JS_free(cx, bytes);
1294 return ok;
1297 void
1298 js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
1300 char argbuf[11];
1301 char *bytes;
1302 JSAtom *atom;
1304 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1305 bytes = NULL;
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));
1310 if (!bytes)
1311 return;
1313 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1314 JSMSG_MISSING_FUN_ARG, argbuf,
1315 bytes ? bytes : "");
1316 JS_free(cx, bytes);
1319 JSBool
1320 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1321 intN spindex, jsval v, JSString *fallback,
1322 const char *arg1, const char *arg2)
1324 char *bytes;
1325 JSBool ok;
1327 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1328 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1329 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1330 if (!bytes)
1331 return JS_FALSE;
1333 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1334 NULL, errorNumber, bytes, arg1, arg2);
1335 JS_free(cx, bytes);
1336 return ok;
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; }
1343 #endif
1345 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1346 #define MSG_DEF(name, number, count, exception, format) \
1347 { format, count, exception } ,
1348 #include "js.msg"
1349 #undef MSG_DEF
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];
1357 return NULL;
1360 JSBool
1361 js_ResetOperationCount(JSContext *cx)
1363 JSScript *script;
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
1376 * is set.
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);
1382 return JS_TRUE;