Bug 506786 - JSScope::trace method. r=brendan.
[mozilla-central.git] / js / src / jscntxt.cpp
blob42b579b363bf1a86e9487c0cf2b66a57531fa07c
1 /* -*- Mode: C++; tab-width: 4; 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 <stdarg.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsstdint.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 "jspubtd.h"
65 #include "jsscan.h"
66 #include "jsscope.h"
67 #include "jsscript.h"
68 #include "jsstaticcheck.h"
69 #include "jsstr.h"
70 #include "jstracer.h"
72 static void
73 FreeContext(JSContext *cx);
75 static void
76 InitThreadData(JSThreadData *data)
78 #ifdef DEBUG
79 /* The data must be already zeroed. */
80 for (size_t i = 0; i != sizeof(*data); ++i)
81 JS_ASSERT(reinterpret_cast<uint8*>(data)[i] == 0);
82 #endif
83 #ifdef JS_TRACER
84 js_InitJIT(&data->traceMonitor);
85 #endif
88 static void
89 FinishThreadData(JSThreadData *data)
91 #ifdef DEBUG
92 /* All GC-related things must be already removed at this point. */
93 for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i)
94 JS_ASSERT(!data->scriptsToGC[i]);
95 #endif
97 js_FinishGSNCache(&data->gsnCache);
98 js_FinishPropertyCache(&data->propertyCache);
99 #if defined JS_TRACER
100 js_FinishJIT(&data->traceMonitor);
101 #endif
104 static void
105 PurgeThreadData(JSContext *cx, JSThreadData *data)
107 js_PurgeGSNCache(&data->gsnCache);
109 /* FIXME: bug 506341. */
110 js_PurgePropertyCache(cx, &data->propertyCache);
112 # ifdef JS_TRACER
113 JSTraceMonitor *tm = &data->traceMonitor;
114 tm->reservedDoublePoolPtr = tm->reservedDoublePool;
117 * FIXME: bug 506117. We should flush only if (cx->runtime->gcRegenShapes),
118 * but we can't yet, because traces may embed sprop and object references,
119 * and we don't yet mark such embedded refs.
121 tm->needFlush = JS_TRUE;
123 if (tm->recorder)
124 tm->recorder->deepAbort();
127 * We want to keep tm->reservedObjects after the GC. So, unless we are
128 * shutting down, we don't purge them here and rather mark them during
129 * the GC, see MarkReservedObjects in jsgc.cpp.
131 if (cx->runtime->state == JSRTS_LANDING)
132 tm->reservedObjects = NULL;
133 # endif
135 /* Destroy eval'ed scripts. */
136 js_DestroyScriptsToGC(cx, data);
139 #ifdef JS_THREADSAFE
141 static JSThread *
142 NewThread(jsword id)
144 JS_ASSERT(js_CurrentThreadId() == id);
145 JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread));
146 if (!thread)
147 return NULL;
148 JS_INIT_CLIST(&thread->contextList);
149 thread->id = id;
150 InitThreadData(&thread->data);
151 return thread;
154 static void
155 DestroyThread(JSThread *thread)
157 /* The thread must have zero contexts. */
158 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
159 JS_ASSERT(!thread->titleToShare);
160 FinishThreadData(&thread->data);
161 js_free(thread);
164 JSBool
165 js_InitContextThread(JSContext *cx)
167 JS_ASSERT(!cx->thread);
168 jsword id = js_CurrentThreadId();
169 JSRuntime *rt = cx->runtime;
170 JS_LOCK_GC(rt);
173 * We must not race with a GC that accesses cx->thread for JSContext
174 * instances on all threads, see bug 476934.
176 js_WaitForGC(rt);
177 JSThreadsHashEntry *entry = (JSThreadsHashEntry *)
178 JS_DHashTableOperate(&rt->threads,
179 (const void *) id,
180 JS_DHASH_LOOKUP);
181 JSThread *thread;
182 if (JS_DHASH_ENTRY_IS_BUSY(&entry->base)) {
183 thread = entry->thread;
184 JS_ASSERT(thread->id == id);
185 } else {
186 JS_UNLOCK_GC(rt);
187 thread = NewThread(id);
188 if (!thread)
189 return false;
190 JS_LOCK_GC(rt);
191 js_WaitForGC(rt);
192 entry = (JSThreadsHashEntry *)
193 JS_DHashTableOperate(&rt->threads, (const void *) id,
194 JS_DHASH_ADD);
195 if (!entry) {
196 JS_UNLOCK_GC(rt);
197 DestroyThread(thread);
198 return false;
201 /* Another thread cannot initialize entry->thread. */
202 JS_ASSERT(!entry->thread);
203 entry->thread = thread;
206 JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
207 cx->thread = thread;
208 return true;
211 void
212 js_ClearContextThread(JSContext *cx)
214 JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
215 JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
216 cx->thread = NULL;
219 static JSBool
220 thread_matchEntry(JSDHashTable *table,
221 const JSDHashEntryHdr *hdr,
222 const void *key)
224 const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr;
226 return entry->thread->id == (jsword) key;
229 static const JSDHashTableOps threads_ops = {
230 JS_DHashAllocTable,
231 JS_DHashFreeTable,
232 JS_DHashVoidPtrKeyStub,
233 thread_matchEntry,
234 JS_DHashMoveEntryStub,
235 JS_DHashClearEntryStub,
236 JS_DHashFinalizeStub,
237 NULL
240 static JSDHashOperator
241 thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
242 void * /* arg */)
244 JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr;
245 JSThread *thread = entry->thread;
247 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
248 DestroyThread(thread);
249 return JS_DHASH_REMOVE;
252 static JSDHashOperator
253 thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
254 void *arg)
256 JSContext* cx = (JSContext *) arg;
257 JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
259 if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
260 JS_ASSERT(cx->thread != thread);
261 js_DestroyScriptsToGC(cx, &thread->data);
262 DestroyThread(thread);
263 return JS_DHASH_REMOVE;
265 PurgeThreadData(cx, &thread->data);
266 memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists));
267 return JS_DHASH_NEXT;
270 #endif /* JS_THREADSAFE */
272 JSBool
273 js_InitThreads(JSRuntime *rt)
275 #ifdef JS_THREADSAFE
276 if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL,
277 sizeof(JSThreadsHashEntry), 4)) {
278 rt->threads.ops = NULL;
279 return false;
281 #else
282 InitThreadData(&rt->threadData);
283 #endif
284 return true;
287 void
288 js_FinishThreads(JSRuntime *rt)
290 #ifdef JS_THREADSAFE
291 if (!rt->threads.ops)
292 return;
293 JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL);
294 JS_DHashTableFinish(&rt->threads);
295 rt->threads.ops = NULL;
296 #else
297 FinishThreadData(&rt->threadData);
298 #endif
301 void
302 js_PurgeThreads(JSContext *cx)
304 #ifdef JS_THREADSAFE
305 JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx);
306 #else
307 PurgeThreadData(cx, &cx->runtime->threadData);
308 #endif
312 * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version
313 * associated with scripts, so in addition to storing them in cx->options we
314 * duplicate them in cx->version (script->version, etc.) and ensure each bit
315 * remains synchronized between the two through these two functions.
317 void
318 js_SyncOptionsToVersion(JSContext* cx)
320 if (cx->options & JSOPTION_XML)
321 cx->version |= JSVERSION_HAS_XML;
322 else
323 cx->version &= ~JSVERSION_HAS_XML;
324 if (cx->options & JSOPTION_ANONFUNFIX)
325 cx->version |= JSVERSION_ANONFUNFIX;
326 else
327 cx->version &= ~JSVERSION_ANONFUNFIX;
330 inline void
331 js_SyncVersionToOptions(JSContext* cx)
333 if (cx->version & JSVERSION_HAS_XML)
334 cx->options |= JSOPTION_XML;
335 else
336 cx->options &= ~JSOPTION_XML;
337 if (cx->version & JSVERSION_ANONFUNFIX)
338 cx->options |= JSOPTION_ANONFUNFIX;
339 else
340 cx->options &= ~JSOPTION_ANONFUNFIX;
343 void
344 js_OnVersionChange(JSContext *cx)
346 #ifdef DEBUG
347 JSVersion version = JSVERSION_NUMBER(cx);
349 JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
350 #endif
353 void
354 js_SetVersion(JSContext *cx, JSVersion version)
356 cx->version = version;
357 js_SyncVersionToOptions(cx);
358 js_OnVersionChange(cx);
361 JSContext *
362 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
364 JSContext *cx;
365 JSBool ok, first;
366 JSContextCallback cxCallback;
369 * We need to initialize the new context fully before adding it to the
370 * runtime list. After that it can be accessed from another thread via
371 * js_ContextIterator.
373 cx = (JSContext *) js_calloc(sizeof *cx);
374 if (!cx)
375 return NULL;
377 cx->runtime = rt;
378 cx->debugHooks = &rt->globalDebugHooks;
379 #if JS_STACK_GROWTH_DIRECTION > 0
380 cx->stackLimit = (jsuword) -1;
381 #endif
382 cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
383 JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
384 JS_ASSERT(cx->version == JSVERSION_DEFAULT);
385 VOUCH_DOES_NOT_REQUIRE_STACK();
386 JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
387 &cx->scriptStackQuota);
389 JS_INIT_ARENA_POOL(&cx->tempPool, "temp",
390 1024, /* FIXME: bug 421435 */
391 sizeof(jsdouble), &cx->scriptStackQuota);
393 js_InitRegExpStatics(cx);
394 JS_ASSERT(cx->resolveFlags == 0);
396 if (!js_InitContextBusyArrayTable(cx)) {
397 FreeContext(cx);
398 return NULL;
401 #ifdef JS_THREADSAFE
402 if (!js_InitContextThread(cx)) {
403 FreeContext(cx);
404 return NULL;
406 #endif
409 * Here the GC lock is still held after js_InitContextThread took it and
410 * the GC is not running on another thread.
412 for (;;) {
413 if (rt->state == JSRTS_UP) {
414 JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
415 first = JS_FALSE;
416 break;
418 if (rt->state == JSRTS_DOWN) {
419 JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList));
420 first = JS_TRUE;
421 rt->state = JSRTS_LAUNCHING;
422 break;
424 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
427 * During the above wait after we are notified about the state change
428 * but before we wake up, another thread could enter the GC from
429 * js_DestroyContext, bug 478336. So we must wait here to ensure that
430 * when we exit the loop with the first flag set to true, that GC is
431 * finished.
433 js_WaitForGC(rt);
435 JS_APPEND_LINK(&cx->link, &rt->contextList);
436 JS_UNLOCK_GC(rt);
439 * If cx is the first context on this runtime, initialize well-known atoms,
440 * keywords, numbers, and strings. If one of these steps should fail, the
441 * runtime will be left in a partially initialized state, with zeroes and
442 * nulls stored in the default-initialized remainder of the struct. We'll
443 * clean the runtime up under js_DestroyContext, because cx will be "last"
444 * as well as "first".
446 if (first) {
447 #ifdef JS_THREADSAFE
448 JS_BeginRequest(cx);
449 #endif
450 ok = js_InitCommonAtoms(cx);
453 * scriptFilenameTable may be left over from a previous episode of
454 * non-zero contexts alive in rt, so don't re-init the table if it's
455 * not necessary.
457 if (ok && !rt->scriptFilenameTable)
458 ok = js_InitRuntimeScriptState(rt);
459 if (ok)
460 ok = js_InitRuntimeNumberState(cx);
461 if (ok)
462 ok = js_InitRuntimeStringState(cx);
463 #ifdef JS_THREADSAFE
464 JS_EndRequest(cx);
465 #endif
466 if (!ok) {
467 js_DestroyContext(cx, JSDCM_NEW_FAILED);
468 return NULL;
471 JS_LOCK_GC(rt);
472 rt->state = JSRTS_UP;
473 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
474 JS_UNLOCK_GC(rt);
477 cxCallback = rt->cxCallback;
478 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
479 js_DestroyContext(cx, JSDCM_NEW_FAILED);
480 return NULL;
483 return cx;
486 #if defined DEBUG && defined XP_UNIX
487 # include <stdio.h>
489 class JSAutoFile {
490 public:
491 JSAutoFile() : mFile(NULL) {}
493 ~JSAutoFile() {
494 if (mFile)
495 fclose(mFile);
498 FILE *open(const char *fname, const char *mode) {
499 return mFile = fopen(fname, mode);
501 operator FILE *() {
502 return mFile;
505 private:
506 FILE *mFile;
509 #ifdef JS_EVAL_CACHE_METERING
510 static void
511 DumpEvalCacheMeter(JSContext *cx)
513 struct {
514 const char *name;
515 ptrdiff_t offset;
516 } table[] = {
517 #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
518 EVAL_CACHE_METER_LIST(frob)
519 #undef frob
521 JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
523 static JSAutoFile fp;
524 if (!fp) {
525 fp.open("/tmp/evalcache.stats", "w");
526 if (!fp)
527 return;
530 fprintf(fp, "eval cache meter (%p):\n",
531 #ifdef JS_THREADSAFE
532 (void *) cx->thread
533 #else
534 (void *) cx->runtime
535 #endif
537 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
538 fprintf(fp, "%-8.8s %llu\n",
539 table[i].name, *(uint64 *)((uint8 *)ecm + table[i].offset));
541 fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
542 fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
543 fflush(fp);
545 # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
546 #endif
548 #ifdef JS_FUNCTION_METERING
549 static void
550 DumpFunctionMeter(JSContext *cx)
552 struct {
553 const char *name;
554 ptrdiff_t offset;
555 } table[] = {
556 #define frob(x) { #x, offsetof(JSFunctionMeter, x) }
557 FUNCTION_KIND_METER_LIST(frob)
558 #undef frob
560 JSFunctionMeter *fm = &cx->runtime->functionMeter;
562 static JSAutoFile fp;
563 if (!fp) {
564 fp.open("/tmp/function.stats", "a");
565 if (!fp)
566 return;
569 fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
570 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
571 fprintf(fp, "%-11.11s %d\n",
572 table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
574 fflush(fp);
576 # define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
577 #endif
579 #endif /* DEBUG && XP_UNIX */
581 #ifndef DUMP_EVAL_CACHE_METER
582 # define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
583 #endif
585 #ifndef DUMP_FUNCTION_METER
586 # define DUMP_FUNCTION_METER(cx) ((void) 0)
587 #endif
589 void
590 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
592 JSRuntime *rt;
593 JSContextCallback cxCallback;
594 JSBool last;
596 rt = cx->runtime;
597 #ifdef JS_THREADSAFE
599 * For API compatibility we allow to destroy contexts without a thread in
600 * optimized builds. We assume that the embedding knows that an OOM error
601 * cannot happen in JS_SetContextThread.
603 JS_ASSERT(cx->thread && CURRENT_THREAD_IS_ME(cx->thread));
604 if (!cx->thread)
605 JS_SetContextThread(cx);
607 JS_ASSERT_IF(rt->gcRunning, cx->outstandingRequests == 0);
608 #endif
610 if (mode != JSDCM_NEW_FAILED) {
611 cxCallback = rt->cxCallback;
612 if (cxCallback) {
614 * JSCONTEXT_DESTROY callback is not allowed to fail and must
615 * return true.
617 #ifdef DEBUG
618 JSBool callbackStatus =
619 #endif
620 cxCallback(cx, JSCONTEXT_DESTROY);
621 JS_ASSERT(callbackStatus);
625 JS_LOCK_GC(rt);
626 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
627 #ifdef JS_THREADSAFE
629 * Typically we are called outside a request, so ensure that the GC is not
630 * running before removing the context from rt->contextList, see bug 477021.
632 if (cx->requestDepth == 0)
633 js_WaitForGC(rt);
634 #endif
635 JS_REMOVE_LINK(&cx->link);
636 last = (rt->contextList.next == &rt->contextList);
637 if (last)
638 rt->state = JSRTS_LANDING;
639 if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
640 #ifdef JS_THREADSAFE
641 || cx->requestDepth != 0
642 #endif
644 JS_ASSERT(!rt->gcRunning);
646 JS_UNLOCK_GC(rt);
648 if (last) {
649 #ifdef JS_THREADSAFE
651 * If cx is not in a request already, begin one now so that we wait
652 * for any racing GC started on a not-last context to finish, before
653 * we plow ahead and unpin atoms. Note that even though we begin a
654 * request here if necessary, we end all requests on cx below before
655 * forcing a final GC. This lets any not-last context destruction
656 * racing in another thread try to force or maybe run the GC, but by
657 * that point, rt->state will not be JSRTS_UP, and that GC attempt
658 * will return early.
660 if (cx->requestDepth == 0)
661 JS_BeginRequest(cx);
662 #endif
664 /* Unlock and clear GC things held by runtime pointers. */
665 js_FinishRuntimeNumberState(cx);
666 js_FinishRuntimeStringState(cx);
668 /* Unpin all common atoms before final GC. */
669 js_FinishCommonAtoms(cx);
671 /* Clear debugging state to remove GC roots. */
672 JS_ClearAllTraps(cx);
673 JS_ClearAllWatchPoints(cx);
676 /* Remove more GC roots in regExpStatics, then collect garbage. */
677 JS_ClearRegExpRoots(cx);
679 #ifdef JS_THREADSAFE
681 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
682 * end our request here in case we are "last" -- in that event, another
683 * js_DestroyContext that was not last might be waiting in the GC for our
684 * request to end. We'll let it run below, just before we do the truly
685 * final GC and then free atom state.
687 while (cx->requestDepth != 0)
688 JS_EndRequest(cx);
689 #endif
691 if (last) {
692 /* Clear builtin functions, which are recreated on demand. */
693 memset(rt->builtinFunctions, 0, sizeof rt->builtinFunctions);
695 js_GC(cx, GC_LAST_CONTEXT);
696 DUMP_EVAL_CACHE_METER(cx);
697 DUMP_FUNCTION_METER(cx);
700 * Free the script filename table if it exists and is empty. Do this
701 * after the last GC to avoid finalizers tripping on free memory.
703 if (rt->scriptFilenameTable &&
704 rt->scriptFilenameTable->nentries == 0) {
705 js_FinishRuntimeScriptState(rt);
708 /* Take the runtime down, now that it has no contexts or atoms. */
709 JS_LOCK_GC(rt);
710 rt->state = JSRTS_DOWN;
711 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
712 } else {
713 if (mode == JSDCM_FORCE_GC)
714 js_GC(cx, GC_NORMAL);
715 else if (mode == JSDCM_MAYBE_GC)
716 JS_MaybeGC(cx);
717 JS_LOCK_GC(rt);
718 js_WaitForGC(rt);
721 #ifdef JS_THREADSAFE
722 js_ClearContextThread(cx);
723 #endif
724 JS_UNLOCK_GC(rt);
725 FreeContext(cx);
728 static void
729 FreeContext(JSContext *cx)
731 JSArgumentFormatMap *map;
732 JSLocalRootStack *lrs;
733 JSLocalRootChunk *lrc;
735 #ifdef JS_THREADSAFE
736 JS_ASSERT(!cx->thread);
737 #endif
739 /* Free the stuff hanging off of cx. */
740 js_FreeRegExpStatics(cx);
741 VOUCH_DOES_NOT_REQUIRE_STACK();
742 JS_FinishArenaPool(&cx->stackPool);
743 JS_FinishArenaPool(&cx->tempPool);
745 if (cx->lastMessage)
746 js_free(cx->lastMessage);
748 /* Remove any argument formatters. */
749 map = cx->argumentFormatMap;
750 while (map) {
751 JSArgumentFormatMap *temp = map;
752 map = map->next;
753 cx->free(temp);
756 /* Destroy the busy array table. */
757 if (cx->busyArrayTable) {
758 JS_HashTableDestroy(cx->busyArrayTable);
759 cx->busyArrayTable = NULL;
762 /* Destroy the resolve recursion damper. */
763 if (cx->resolvingTable) {
764 JS_DHashTableDestroy(cx->resolvingTable);
765 cx->resolvingTable = NULL;
768 lrs = cx->localRootStack;
769 if (lrs) {
770 while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
771 lrs->topChunk = lrc->down;
772 cx->free(lrc);
774 cx->free(lrs);
777 /* Finally, free cx itself. */
778 js_free(cx);
781 JSBool
782 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
784 JSCList *cl;
786 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
787 if (cl == &cx->link)
788 return JS_TRUE;
790 JS_RUNTIME_METER(rt, deadContexts);
791 return JS_FALSE;
794 JSContext *
795 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
797 JSContext *cx = *iterp;
799 if (unlocked)
800 JS_LOCK_GC(rt);
801 cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next);
802 if (&cx->link == &rt->contextList)
803 cx = NULL;
804 *iterp = cx;
805 if (unlocked)
806 JS_UNLOCK_GC(rt);
807 return cx;
810 JS_FRIEND_API(JSContext *)
811 js_NextActiveContext(JSRuntime *rt, JSContext *cx)
813 JSContext *iter = cx;
814 #ifdef JS_THREADSAFE
815 while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
816 if (cx->requestDepth)
817 break;
819 return cx;
820 #else
821 return js_ContextIterator(rt, JS_FALSE, &iter);
822 #endif
825 #ifdef JS_THREADSAFE
827 uint32
828 js_CountThreadRequests(JSContext *cx)
830 JSCList *head, *link;
831 uint32 nrequests;
833 JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
834 head = &cx->thread->contextList;
835 nrequests = 0;
836 for (link = head->next; link != head; link = link->next) {
837 JSContext *acx = CX_FROM_THREAD_LINKS(link);
838 JS_ASSERT(acx->thread == cx->thread);
839 if (acx->requestDepth)
840 nrequests++;
842 return nrequests;
846 * If the GC is running and we're called on another thread, wait for this GC
847 * activation to finish. We can safely wait here without fear of deadlock (in
848 * the case where we are called within a request on another thread's context)
849 * because the GC doesn't set rt->gcRunning until after it has waited for all
850 * active requests to end.
852 * We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid
853 * expensive calls when the GC is not running.
855 void
856 js_WaitForGC(JSRuntime *rt)
858 JS_ASSERT_IF(rt->gcRunning, rt->gcLevel > 0);
859 if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
860 do {
861 JS_AWAIT_GC_DONE(rt);
862 } while (rt->gcRunning);
866 uint32
867 js_DiscountRequestsForGC(JSContext *cx)
869 uint32 requestDebit;
871 JS_ASSERT(cx->thread);
872 JS_ASSERT(cx->runtime->gcThread != cx->thread);
874 #ifdef JS_TRACER
875 if (JS_ON_TRACE(cx)) {
876 JS_UNLOCK_GC(cx->runtime);
877 js_LeaveTrace(cx);
878 JS_LOCK_GC(cx->runtime);
880 #endif
882 requestDebit = js_CountThreadRequests(cx);
883 if (requestDebit != 0) {
884 JSRuntime *rt = cx->runtime;
885 JS_ASSERT(requestDebit <= rt->requestCount);
886 rt->requestCount -= requestDebit;
887 if (rt->requestCount == 0)
888 JS_NOTIFY_REQUEST_DONE(rt);
890 return requestDebit;
893 void
894 js_RecountRequestsAfterGC(JSRuntime *rt, uint32 requestDebit)
896 while (rt->gcLevel > 0) {
897 JS_ASSERT(rt->gcThread);
898 JS_AWAIT_GC_DONE(rt);
900 if (requestDebit != 0)
901 rt->requestCount += requestDebit;
904 #endif
906 static JSDHashNumber
907 resolving_HashKey(JSDHashTable *table, const void *ptr)
909 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
911 return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id;
914 JS_PUBLIC_API(JSBool)
915 resolving_MatchEntry(JSDHashTable *table,
916 const JSDHashEntryHdr *hdr,
917 const void *ptr)
919 const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
920 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
922 return entry->key.obj == key->obj && entry->key.id == key->id;
925 static const JSDHashTableOps resolving_dhash_ops = {
926 JS_DHashAllocTable,
927 JS_DHashFreeTable,
928 resolving_HashKey,
929 resolving_MatchEntry,
930 JS_DHashMoveEntryStub,
931 JS_DHashClearEntryStub,
932 JS_DHashFinalizeStub,
933 NULL
936 JSBool
937 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
938 JSResolvingEntry **entryp)
940 JSDHashTable *table;
941 JSResolvingEntry *entry;
943 table = cx->resolvingTable;
944 if (!table) {
945 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
946 sizeof(JSResolvingEntry),
947 JS_DHASH_MIN_SIZE);
948 if (!table)
949 goto outofmem;
950 cx->resolvingTable = table;
953 entry = (JSResolvingEntry *)
954 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
955 if (!entry)
956 goto outofmem;
958 if (entry->flags & flag) {
959 /* An entry for (key, flag) exists already -- dampen recursion. */
960 entry = NULL;
961 } else {
962 /* Fill in key if we were the first to add entry, then set flag. */
963 if (!entry->key.obj)
964 entry->key = *key;
965 entry->flags |= flag;
967 *entryp = entry;
968 return JS_TRUE;
970 outofmem:
971 JS_ReportOutOfMemory(cx);
972 return JS_FALSE;
975 void
976 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
977 JSResolvingEntry *entry, uint32 generation)
979 JSDHashTable *table;
982 * Clear flag from entry->flags and return early if other flags remain.
983 * We must take care to re-lookup entry if the table has changed since
984 * it was found by js_StartResolving.
986 table = cx->resolvingTable;
987 if (!entry || table->generation != generation) {
988 entry = (JSResolvingEntry *)
989 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
991 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
992 entry->flags &= ~flag;
993 if (entry->flags)
994 return;
997 * Do a raw remove only if fewer entries were removed than would cause
998 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
999 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
1000 * compressing or shrinking the table as needed.
1002 if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
1003 JS_DHashTableRawRemove(table, &entry->hdr);
1004 else
1005 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
1008 JSBool
1009 js_EnterLocalRootScope(JSContext *cx)
1011 JSLocalRootStack *lrs;
1012 int mark;
1014 lrs = cx->localRootStack;
1015 if (!lrs) {
1016 lrs = (JSLocalRootStack *) cx->malloc(sizeof *lrs);
1017 if (!lrs)
1018 return JS_FALSE;
1019 lrs->scopeMark = JSLRS_NULL_MARK;
1020 lrs->rootCount = 0;
1021 lrs->topChunk = &lrs->firstChunk;
1022 lrs->firstChunk.down = NULL;
1023 cx->localRootStack = lrs;
1026 /* Push lrs->scopeMark to save it for restore when leaving. */
1027 mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
1028 if (mark < 0)
1029 return JS_FALSE;
1030 lrs->scopeMark = (uint32) mark;
1031 return JS_TRUE;
1034 void
1035 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
1037 JSLocalRootStack *lrs;
1038 uint32 mark, m, n;
1039 JSLocalRootChunk *lrc;
1041 /* Defend against buggy native callers. */
1042 lrs = cx->localRootStack;
1043 JS_ASSERT(lrs && lrs->rootCount != 0);
1044 if (!lrs || lrs->rootCount == 0)
1045 return;
1047 mark = lrs->scopeMark;
1048 JS_ASSERT(mark != JSLRS_NULL_MARK);
1049 if (mark == JSLRS_NULL_MARK)
1050 return;
1052 /* Free any chunks being popped by this leave operation. */
1053 m = mark >> JSLRS_CHUNK_SHIFT;
1054 n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
1055 while (n > m) {
1056 lrc = lrs->topChunk;
1057 JS_ASSERT(lrc != &lrs->firstChunk);
1058 lrs->topChunk = lrc->down;
1059 cx->free(lrc);
1060 --n;
1064 * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
1065 * it on the caller's scope, or store it in lastInternalResult if we are
1066 * leaving the outermost scope. We don't need to allocate a new lrc
1067 * because we can overwrite the old mark's slot with rval.
1069 lrc = lrs->topChunk;
1070 m = mark & JSLRS_CHUNK_MASK;
1071 lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
1072 if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
1073 if (mark == 0) {
1074 cx->weakRoots.lastInternalResult = rval;
1075 } else {
1077 * Increment m to avoid the "else if (m == 0)" case below. If
1078 * rval is not a GC-thing, that case would take care of freeing
1079 * any chunk that contained only the old mark. Since rval *is*
1080 * a GC-thing here, we want to reuse that old mark's slot.
1082 lrc->roots[m++] = rval;
1083 ++mark;
1086 lrs->rootCount = (uint32) mark;
1089 * Free the stack eagerly, risking malloc churn. The alternative would
1090 * require an lrs->entryCount member, maintained by Enter and Leave, and
1091 * tested by the GC in addition to the cx->localRootStack non-null test.
1093 * That approach would risk hoarding 264 bytes (net) per context. Right
1094 * now it seems better to give fresh (dirty in CPU write-back cache, and
1095 * the data is no longer needed) memory back to the malloc heap.
1097 if (mark == 0) {
1098 cx->localRootStack = NULL;
1099 cx->free(lrs);
1100 } else if (m == 0) {
1101 lrs->topChunk = lrc->down;
1102 cx->free(lrc);
1106 void
1107 js_ForgetLocalRoot(JSContext *cx, jsval v)
1109 JSLocalRootStack *lrs;
1110 uint32 i, j, m, n, mark;
1111 JSLocalRootChunk *lrc, *lrc2;
1112 jsval top;
1114 lrs = cx->localRootStack;
1115 JS_ASSERT(lrs && lrs->rootCount);
1116 if (!lrs || lrs->rootCount == 0)
1117 return;
1119 /* Prepare to pop the top-most value from the stack. */
1120 n = lrs->rootCount - 1;
1121 m = n & JSLRS_CHUNK_MASK;
1122 lrc = lrs->topChunk;
1123 top = lrc->roots[m];
1125 /* Be paranoid about calls on an empty scope. */
1126 mark = lrs->scopeMark;
1127 JS_ASSERT(mark < n);
1128 if (mark >= n)
1129 return;
1131 /* If v was not the last root pushed in the top scope, find it. */
1132 if (top != v) {
1133 /* Search downward in case v was recently pushed. */
1134 i = n;
1135 j = m;
1136 lrc2 = lrc;
1137 while (--i > mark) {
1138 if (j == 0)
1139 lrc2 = lrc2->down;
1140 j = i & JSLRS_CHUNK_MASK;
1141 if (lrc2->roots[j] == v)
1142 break;
1145 /* If we didn't find v in this scope, assert and bail out. */
1146 JS_ASSERT(i != mark);
1147 if (i == mark)
1148 return;
1150 /* Swap top and v so common tail code can pop v. */
1151 lrc2->roots[j] = top;
1154 /* Pop the last value from the stack. */
1155 lrc->roots[m] = JSVAL_NULL;
1156 lrs->rootCount = n;
1157 if (m == 0) {
1158 JS_ASSERT(n != 0);
1159 JS_ASSERT(lrc != &lrs->firstChunk);
1160 lrs->topChunk = lrc->down;
1161 cx->free(lrc);
1166 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
1168 uint32 n, m;
1169 JSLocalRootChunk *lrc;
1171 n = lrs->rootCount;
1172 m = n & JSLRS_CHUNK_MASK;
1173 if (n == 0 || m != 0) {
1175 * At start of first chunk, or not at start of a non-first top chunk.
1176 * Check for lrs->rootCount overflow.
1178 if ((uint32)(n + 1) == 0) {
1179 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1180 JSMSG_TOO_MANY_LOCAL_ROOTS);
1181 return -1;
1183 lrc = lrs->topChunk;
1184 JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
1185 } else {
1187 * After lrs->firstChunk, trying to index at a power-of-two chunk
1188 * boundary: need a new chunk.
1190 lrc = (JSLocalRootChunk *) cx->malloc(sizeof *lrc);
1191 if (!lrc)
1192 return -1;
1193 lrc->down = lrs->topChunk;
1194 lrs->topChunk = lrc;
1196 lrs->rootCount = n + 1;
1197 lrc->roots[m] = v;
1198 return (int) n;
1201 void
1202 js_TraceLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
1204 uint32 n, m, mark;
1205 JSLocalRootChunk *lrc;
1206 jsval v;
1208 n = lrs->rootCount;
1209 if (n == 0)
1210 return;
1212 mark = lrs->scopeMark;
1213 lrc = lrs->topChunk;
1214 do {
1215 while (--n > mark) {
1216 m = n & JSLRS_CHUNK_MASK;
1217 v = lrc->roots[m];
1218 JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
1219 JS_SET_TRACING_INDEX(trc, "local_root", n);
1220 js_CallValueTracerIfGCThing(trc, v);
1221 if (m == 0)
1222 lrc = lrc->down;
1224 m = n & JSLRS_CHUNK_MASK;
1225 mark = JSVAL_TO_INT(lrc->roots[m]);
1226 if (m == 0)
1227 lrc = lrc->down;
1228 } while (n != 0);
1229 JS_ASSERT(!lrc);
1232 static void
1233 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
1236 * Check the error report, and set a JavaScript-catchable exception
1237 * if the error is defined to have an associated exception. If an
1238 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
1239 * on the error report, and exception-aware hosts should ignore it.
1241 JS_ASSERT(reportp);
1242 if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
1243 reportp->flags |= JSREPORT_EXCEPTION;
1246 * Call the error reporter only if an exception wasn't raised.
1248 * If an exception was raised, then we call the debugErrorHook
1249 * (if present) to give it a chance to see the error before it
1250 * propagates out of scope. This is needed for compatability
1251 * with the old scheme.
1253 if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp)) {
1254 js_ReportErrorAgain(cx, message, reportp);
1255 } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
1256 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1257 /* test local in case debugErrorHook changed on another thread */
1258 if (hook)
1259 hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
1263 /* The report must be initially zeroed. */
1264 static void
1265 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
1267 JSStackFrame *fp;
1270 * Walk stack until we find a frame that is associated with some script
1271 * rather than a native frame.
1273 for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
1274 if (fp->regs) {
1275 report->filename = fp->script->filename;
1276 report->lineno = js_FramePCToLineNumber(cx, fp);
1277 break;
1283 * We don't post an exception in this case, since doing so runs into
1284 * complications of pre-allocating an exception object which required
1285 * running the Exception class initializer early etc.
1286 * Instead we just invoke the errorReporter with an "Out Of Memory"
1287 * type message, and then hope the process ends swiftly.
1289 void
1290 js_ReportOutOfMemory(JSContext *cx)
1292 #ifdef JS_TRACER
1294 * If we are in a builtin called directly from trace, don't report an
1295 * error. We will retry in the interpreter instead.
1297 if (JS_ON_TRACE(cx) && !cx->bailExit)
1298 return;
1299 #endif
1301 JSErrorReport report;
1302 JSErrorReporter onError = cx->errorReporter;
1304 /* Get the message for this error, but we won't expand any arguments. */
1305 const JSErrorFormatString *efs =
1306 js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
1307 const char *msg = efs ? efs->format : "Out of memory";
1309 /* Fill out the report, but don't do anything that requires allocation. */
1310 memset(&report, 0, sizeof (struct JSErrorReport));
1311 report.flags = JSREPORT_ERROR;
1312 report.errorNumber = JSMSG_OUT_OF_MEMORY;
1313 PopulateReportBlame(cx, &report);
1316 * If debugErrorHook is present then we give it a chance to veto sending
1317 * the error on to the regular ErrorReporter. We also clear a pending
1318 * exception if any now so the hooks can replace the out-of-memory error
1319 * by a script-catchable exception.
1321 cx->throwing = JS_FALSE;
1322 if (onError) {
1323 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1324 if (hook &&
1325 !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
1326 onError = NULL;
1330 if (onError)
1331 onError(cx, msg, &report);
1334 void
1335 js_ReportOutOfScriptQuota(JSContext *cx)
1337 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1338 JSMSG_SCRIPT_STACK_QUOTA);
1341 void
1342 js_ReportOverRecursed(JSContext *cx)
1344 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1347 void
1348 js_ReportAllocationOverflow(JSContext *cx)
1350 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
1353 JSBool
1354 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
1356 char *message;
1357 jschar *ucmessage;
1358 size_t messagelen;
1359 JSErrorReport report;
1360 JSBool warning;
1362 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1363 return JS_TRUE;
1365 message = JS_vsmprintf(format, ap);
1366 if (!message)
1367 return JS_FALSE;
1368 messagelen = strlen(message);
1370 memset(&report, 0, sizeof (struct JSErrorReport));
1371 report.flags = flags;
1372 report.errorNumber = JSMSG_USER_DEFINED_ERROR;
1373 report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
1374 PopulateReportBlame(cx, &report);
1376 warning = JSREPORT_IS_WARNING(report.flags);
1377 if (warning && JS_HAS_WERROR_OPTION(cx)) {
1378 report.flags &= ~JSREPORT_WARNING;
1379 warning = JS_FALSE;
1382 ReportError(cx, message, &report);
1383 js_free(message);
1384 cx->free(ucmessage);
1385 return warning;
1389 * The arguments from ap need to be packaged up into an array and stored
1390 * into the report struct.
1392 * The format string addressed by the error number may contain operands
1393 * identified by the format {N}, where N is a decimal digit. Each of these
1394 * is to be replaced by the Nth argument from the va_list. The complete
1395 * message is placed into reportp->ucmessage converted to a JSString.
1397 * Returns true if the expansion succeeds (can fail if out of memory).
1399 JSBool
1400 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
1401 void *userRef, const uintN errorNumber,
1402 char **messagep, JSErrorReport *reportp,
1403 JSBool *warningp, JSBool charArgs, va_list ap)
1405 const JSErrorFormatString *efs;
1406 int i;
1407 int argCount;
1409 *warningp = JSREPORT_IS_WARNING(reportp->flags);
1410 if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
1411 reportp->flags &= ~JSREPORT_WARNING;
1412 *warningp = JS_FALSE;
1415 *messagep = NULL;
1417 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1418 if (!callback || callback == js_GetErrorMessage)
1419 efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
1420 else
1421 efs = callback(userRef, NULL, errorNumber);
1422 if (efs) {
1423 size_t totalArgsLength = 0;
1424 size_t argLengths[10]; /* only {0} thru {9} supported */
1425 argCount = efs->argCount;
1426 JS_ASSERT(argCount <= 10);
1427 if (argCount > 0) {
1429 * Gather the arguments into an array, and accumulate
1430 * their sizes. We allocate 1 more than necessary and
1431 * null it out to act as the caboose when we free the
1432 * pointers later.
1434 reportp->messageArgs = (const jschar **)
1435 cx->malloc(sizeof(jschar *) * (argCount + 1));
1436 if (!reportp->messageArgs)
1437 return JS_FALSE;
1438 reportp->messageArgs[argCount] = NULL;
1439 for (i = 0; i < argCount; i++) {
1440 if (charArgs) {
1441 char *charArg = va_arg(ap, char *);
1442 size_t charArgLength = strlen(charArg);
1443 reportp->messageArgs[i]
1444 = js_InflateString(cx, charArg, &charArgLength);
1445 if (!reportp->messageArgs[i])
1446 goto error;
1447 } else {
1448 reportp->messageArgs[i] = va_arg(ap, jschar *);
1450 argLengths[i] = js_strlen(reportp->messageArgs[i]);
1451 totalArgsLength += argLengths[i];
1453 /* NULL-terminate for easy copying. */
1454 reportp->messageArgs[i] = NULL;
1457 * Parse the error format, substituting the argument X
1458 * for {X} in the format.
1460 if (argCount > 0) {
1461 if (efs->format) {
1462 jschar *buffer, *fmt, *out;
1463 int expandedArgs = 0;
1464 size_t expandedLength;
1465 size_t len = strlen(efs->format);
1467 buffer = fmt = js_InflateString (cx, efs->format, &len);
1468 if (!buffer)
1469 goto error;
1470 expandedLength = len
1471 - (3 * argCount) /* exclude the {n} */
1472 + totalArgsLength;
1475 * Note - the above calculation assumes that each argument
1476 * is used once and only once in the expansion !!!
1478 reportp->ucmessage = out = (jschar *)
1479 cx->malloc((expandedLength + 1) * sizeof(jschar));
1480 if (!out) {
1481 cx->free(buffer);
1482 goto error;
1484 while (*fmt) {
1485 if (*fmt == '{') {
1486 if (isdigit(fmt[1])) {
1487 int d = JS7_UNDEC(fmt[1]);
1488 JS_ASSERT(d < argCount);
1489 js_strncpy(out, reportp->messageArgs[d],
1490 argLengths[d]);
1491 out += argLengths[d];
1492 fmt += 3;
1493 expandedArgs++;
1494 continue;
1497 *out++ = *fmt++;
1499 JS_ASSERT(expandedArgs == argCount);
1500 *out = 0;
1501 cx->free(buffer);
1502 *messagep =
1503 js_DeflateString(cx, reportp->ucmessage,
1504 (size_t)(out - reportp->ucmessage));
1505 if (!*messagep)
1506 goto error;
1508 } else {
1510 * Zero arguments: the format string (if it exists) is the
1511 * entire message.
1513 if (efs->format) {
1514 size_t len;
1515 *messagep = JS_strdup(cx, efs->format);
1516 if (!*messagep)
1517 goto error;
1518 len = strlen(*messagep);
1519 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1520 if (!reportp->ucmessage)
1521 goto error;
1525 if (*messagep == NULL) {
1526 /* where's the right place for this ??? */
1527 const char *defaultErrorMessage
1528 = "No error message available for error number %d";
1529 size_t nbytes = strlen(defaultErrorMessage) + 16;
1530 *messagep = (char *)cx->malloc(nbytes);
1531 if (!*messagep)
1532 goto error;
1533 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1535 return JS_TRUE;
1537 error:
1538 if (reportp->messageArgs) {
1539 /* free the arguments only if we allocated them */
1540 if (charArgs) {
1541 i = 0;
1542 while (reportp->messageArgs[i])
1543 cx->free((void *)reportp->messageArgs[i++]);
1545 cx->free((void *)reportp->messageArgs);
1546 reportp->messageArgs = NULL;
1548 if (reportp->ucmessage) {
1549 cx->free((void *)reportp->ucmessage);
1550 reportp->ucmessage = NULL;
1552 if (*messagep) {
1553 cx->free((void *)*messagep);
1554 *messagep = NULL;
1556 return JS_FALSE;
1559 JSBool
1560 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1561 void *userRef, const uintN errorNumber,
1562 JSBool charArgs, va_list ap)
1564 JSErrorReport report;
1565 char *message;
1566 JSBool warning;
1568 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1569 return JS_TRUE;
1571 memset(&report, 0, sizeof (struct JSErrorReport));
1572 report.flags = flags;
1573 report.errorNumber = errorNumber;
1574 PopulateReportBlame(cx, &report);
1576 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1577 &message, &report, &warning, charArgs, ap)) {
1578 return JS_FALSE;
1581 ReportError(cx, message, &report);
1583 if (message)
1584 cx->free(message);
1585 if (report.messageArgs) {
1587 * js_ExpandErrorArguments owns its messageArgs only if it had to
1588 * inflate the arguments (from regular |char *|s).
1590 if (charArgs) {
1591 int i = 0;
1592 while (report.messageArgs[i])
1593 cx->free((void *)report.messageArgs[i++]);
1595 cx->free((void *)report.messageArgs);
1597 if (report.ucmessage)
1598 cx->free((void *)report.ucmessage);
1600 return warning;
1603 JS_FRIEND_API(void)
1604 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1606 JSErrorReporter onError;
1608 if (!message)
1609 return;
1611 if (cx->lastMessage)
1612 js_free(cx->lastMessage);
1613 cx->lastMessage = JS_strdup(cx, message);
1614 if (!cx->lastMessage)
1615 return;
1616 onError = cx->errorReporter;
1619 * If debugErrorHook is present then we give it a chance to veto
1620 * sending the error on to the regular ErrorReporter.
1622 if (onError) {
1623 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1624 if (hook &&
1625 !hook(cx, cx->lastMessage, reportp,
1626 cx->debugHooks->debugErrorHookData)) {
1627 onError = NULL;
1630 if (onError)
1631 onError(cx, cx->lastMessage, reportp);
1634 void
1635 js_ReportIsNotDefined(JSContext *cx, const char *name)
1637 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1640 JSBool
1641 js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
1642 JSString *fallback)
1644 char *bytes;
1645 JSBool ok;
1647 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1648 if (!bytes)
1649 return JS_FALSE;
1651 if (strcmp(bytes, js_undefined_str) == 0 ||
1652 strcmp(bytes, js_null_str) == 0) {
1653 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1654 js_GetErrorMessage, NULL,
1655 JSMSG_NO_PROPERTIES, bytes,
1656 NULL, NULL);
1657 } else if (JSVAL_IS_VOID(v)) {
1658 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1659 js_GetErrorMessage, NULL,
1660 JSMSG_NULL_OR_UNDEFINED, bytes,
1661 js_undefined_str, NULL);
1662 } else {
1663 JS_ASSERT(JSVAL_IS_NULL(v));
1664 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1665 js_GetErrorMessage, NULL,
1666 JSMSG_NULL_OR_UNDEFINED, bytes,
1667 js_null_str, NULL);
1670 cx->free(bytes);
1671 return ok;
1674 void
1675 js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
1677 char argbuf[11];
1678 char *bytes;
1679 JSAtom *atom;
1681 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1682 bytes = NULL;
1683 if (VALUE_IS_FUNCTION(cx, *vp)) {
1684 atom = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp))->atom;
1685 bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp,
1686 ATOM_TO_STRING(atom));
1687 if (!bytes)
1688 return;
1690 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1691 JSMSG_MISSING_FUN_ARG, argbuf,
1692 bytes ? bytes : "");
1693 cx->free(bytes);
1696 JSBool
1697 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1698 intN spindex, jsval v, JSString *fallback,
1699 const char *arg1, const char *arg2)
1701 char *bytes;
1702 JSBool ok;
1704 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1705 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1706 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1707 if (!bytes)
1708 return JS_FALSE;
1710 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1711 NULL, errorNumber, bytes, arg1, arg2);
1712 cx->free(bytes);
1713 return ok;
1716 #if defined DEBUG && defined XP_UNIX
1717 /* For gdb usage. */
1718 void js_traceon(JSContext *cx) { cx->tracefp = stderr; cx->tracePrevPc = NULL; }
1719 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1720 #endif
1722 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1723 #define MSG_DEF(name, number, count, exception, format) \
1724 { format, count, exception } ,
1725 #include "js.msg"
1726 #undef MSG_DEF
1729 JS_FRIEND_API(const JSErrorFormatString *)
1730 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1732 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1733 return &js_ErrorFormatString[errorNumber];
1734 return NULL;
1737 JSBool
1738 js_InvokeOperationCallback(JSContext *cx)
1740 JS_ASSERT(cx->operationCallbackFlag);
1743 * Reset the callback flag first, then yield. If another thread is racing
1744 * us here we will accumulate another callback request which will be
1745 * serviced at the next opportunity.
1747 cx->operationCallbackFlag = 0;
1750 * Unless we are going to run the GC, we automatically yield the current
1751 * context every time the operation callback is hit since we might be
1752 * called as a result of an impending GC, which would deadlock if we do
1753 * not yield. Operation callbacks are supposed to happen rarely (seconds,
1754 * not milliseconds) so it is acceptable to yield at every callback.
1756 if (cx->runtime->gcIsNeeded)
1757 js_GC(cx, GC_NORMAL);
1758 #ifdef JS_THREADSAFE
1759 else
1760 JS_YieldRequest(cx);
1761 #endif
1763 JSOperationCallback cb = cx->operationCallback;
1766 * Important: Additional callbacks can occur inside the callback handler
1767 * if it re-enters the JS engine. The embedding must ensure that the
1768 * callback is disconnected before attempting such re-entry.
1771 return !cb || cb(cx);
1774 void
1775 js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
1777 JSContext *acx, *iter;
1778 #ifdef JS_THREADSAFE
1779 if (!gcLocked)
1780 JS_LOCK_GC(rt);
1781 #endif
1782 iter = NULL;
1783 while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
1784 JS_TriggerOperationCallback(acx);
1785 #ifdef JS_THREADSAFE
1786 if (!gcLocked)
1787 JS_UNLOCK_GC(rt);
1788 #endif
1791 JSStackFrame *
1792 js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1794 if (!fp)
1795 fp = js_GetTopStackFrame(cx);
1796 while (fp) {
1797 if (fp->script)
1798 return fp;
1799 fp = fp->down;
1801 return NULL;
1804 jsbytecode*
1805 js_GetCurrentBytecodePC(JSContext* cx)
1807 jsbytecode *pc, *imacpc;
1809 #ifdef JS_TRACER
1810 if (JS_ON_TRACE(cx)) {
1811 pc = cx->bailExit->pc;
1812 imacpc = cx->bailExit->imacpc;
1813 } else
1814 #endif
1816 JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */
1817 JSStackFrame* fp = cx->fp;
1818 if (fp && fp->regs) {
1819 pc = fp->regs->pc;
1820 imacpc = fp->imacpc;
1821 } else {
1822 return NULL;
1827 * If we are inside GetProperty_tn or similar, return a pointer to the
1828 * current instruction in the script, not the CALL instruction in the
1829 * imacro, for the benefit of callers doing bytecode inspection.
1831 return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;