Bug 558971 - Parser arena allocation overhead is too high. r=sayrer/shaver
[mozilla-central.git] / js / src / jscntxt.cpp
blobeaeb870098717b937aa40b5260369d4223bee7a4
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 <new>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include "jstypes.h"
49 #include "jsstdint.h"
50 #include "jsarena.h" /* Added by JSIFY */
51 #include "jsutil.h" /* Added by JSIFY */
52 #include "jsclist.h"
53 #include "jsprf.h"
54 #include "jsatom.h"
55 #include "jscntxt.h"
56 #include "jsversion.h"
57 #include "jsdbgapi.h"
58 #include "jsexn.h"
59 #include "jsfun.h"
60 #include "jsgc.h"
61 #include "jslock.h"
62 #include "jsmath.h"
63 #include "jsnum.h"
64 #include "jsobj.h"
65 #include "jsopcode.h"
66 #include "jspubtd.h"
67 #include "jsscan.h"
68 #include "jsscope.h"
69 #include "jsscript.h"
70 #include "jsstaticcheck.h"
71 #include "jsstr.h"
72 #include "jstracer.h"
74 using namespace js;
76 static const size_t ARENA_HEADER_SIZE_HACK = 40;
77 static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
79 static void
80 FreeContext(JSContext *cx);
82 static void
83 MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs);
85 #ifdef DEBUG
86 bool
87 CallStack::contains(JSStackFrame *fp)
89 JSStackFrame *start;
90 JSStackFrame *stop;
91 if (isSuspended()) {
92 start = suspendedFrame;
93 stop = initialFrame->down;
94 } else {
95 start = cx->fp;
96 stop = cx->activeCallStack()->initialFrame->down;
98 for (JSStackFrame *f = start; f != stop; f = f->down) {
99 if (f == fp)
100 return true;
102 return false;
104 #endif
106 bool
107 JSThreadData::init()
109 #ifdef DEBUG
110 /* The data must be already zeroed. */
111 for (size_t i = 0; i != sizeof(*this); ++i)
112 JS_ASSERT(reinterpret_cast<uint8*>(this)[i] == 0);
113 #endif
114 #ifdef JS_TRACER
115 InitJIT(&traceMonitor);
116 #endif
117 dtoaState = js_NewDtoaState();
118 if (!dtoaState) {
119 finish();
120 return false;
122 return true;
125 void
126 JSThreadData::finish()
128 #ifdef DEBUG
129 /* All GC-related things must be already removed at this point. */
130 JS_ASSERT(gcFreeLists.isEmpty());
131 for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
132 JS_ASSERT(!scriptsToGC[i]);
133 for (size_t i = 0; i != JS_ARRAY_LENGTH(nativeEnumCache); ++i)
134 JS_ASSERT(!nativeEnumCache[i]);
135 JS_ASSERT(!localRootStack);
136 #endif
138 if (dtoaState)
139 js_DestroyDtoaState(dtoaState);
141 js_FinishGSNCache(&gsnCache);
142 propertyCache.~PropertyCache();
143 #if defined JS_TRACER
144 FinishJIT(&traceMonitor);
145 #endif
148 void
149 JSThreadData::mark(JSTracer *trc)
151 #ifdef JS_TRACER
152 traceMonitor.mark(trc);
153 #endif
154 if (localRootStack)
155 MarkLocalRoots(trc, localRootStack);
158 void
159 JSThreadData::purge(JSContext *cx)
161 cachedIteratorObject = NULL;
163 purgeGCFreeLists();
165 js_PurgeGSNCache(&gsnCache);
167 /* FIXME: bug 506341. */
168 propertyCache.purge(cx);
170 #ifdef JS_TRACER
172 * If we are about to regenerate shapes, we have to flush the JIT cache,
173 * which will eventually abort any current recording.
175 if (cx->runtime->gcRegenShapes)
176 traceMonitor.needFlush = JS_TRUE;
177 #endif
179 /* Destroy eval'ed scripts. */
180 js_DestroyScriptsToGC(cx, this);
182 js_PurgeCachedNativeEnumerators(cx, this);
185 void
186 JSThreadData::purgeGCFreeLists()
188 if (!localRootStack) {
189 gcFreeLists.purge();
190 } else {
191 JS_ASSERT(gcFreeLists.isEmpty());
192 localRootStack->gcFreeLists.purge();
196 #ifdef JS_THREADSAFE
198 static JSThread *
199 NewThread(jsword id)
201 JS_ASSERT(js_CurrentThreadId() == id);
202 JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread));
203 if (!thread)
204 return NULL;
205 JS_INIT_CLIST(&thread->contextList);
206 thread->id = id;
207 if (!thread->data.init()) {
208 js_free(thread);
209 return NULL;
211 return thread;
214 static void
215 DestroyThread(JSThread *thread)
217 /* The thread must have zero contexts. */
218 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
219 JS_ASSERT(!thread->titleToShare);
220 thread->data.finish();
221 js_free(thread);
224 JSThread *
225 js_CurrentThread(JSRuntime *rt)
227 jsword id = js_CurrentThreadId();
228 JS_LOCK_GC(rt);
231 * We must not race with a GC that accesses cx->thread for JSContext
232 * instances on all threads, see bug 476934.
234 js_WaitForGC(rt);
235 JSThreadsHashEntry *entry = (JSThreadsHashEntry *)
236 JS_DHashTableOperate(&rt->threads,
237 (const void *) id,
238 JS_DHASH_LOOKUP);
239 JSThread *thread;
240 if (JS_DHASH_ENTRY_IS_BUSY(&entry->base)) {
241 thread = entry->thread;
242 JS_ASSERT(thread->id == id);
243 } else {
244 JS_UNLOCK_GC(rt);
245 thread = NewThread(id);
246 if (!thread)
247 return NULL;
248 JS_LOCK_GC(rt);
249 js_WaitForGC(rt);
250 entry = (JSThreadsHashEntry *)
251 JS_DHashTableOperate(&rt->threads, (const void *) id,
252 JS_DHASH_ADD);
253 if (!entry) {
254 JS_UNLOCK_GC(rt);
255 DestroyThread(thread);
256 return NULL;
259 /* Another thread cannot initialize entry->thread. */
260 JS_ASSERT(!entry->thread);
261 entry->thread = thread;
264 return thread;
267 JSBool
268 js_InitContextThread(JSContext *cx)
270 JSThread *thread = js_CurrentThread(cx->runtime);
271 if (!thread)
272 return false;
274 JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
275 cx->thread = thread;
276 return true;
279 void
280 js_ClearContextThread(JSContext *cx)
282 JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
283 JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
284 cx->thread = NULL;
287 static JSBool
288 thread_matchEntry(JSDHashTable *table,
289 const JSDHashEntryHdr *hdr,
290 const void *key)
292 const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr;
294 return entry->thread->id == (jsword) key;
297 static const JSDHashTableOps threads_ops = {
298 JS_DHashAllocTable,
299 JS_DHashFreeTable,
300 JS_DHashVoidPtrKeyStub,
301 thread_matchEntry,
302 JS_DHashMoveEntryStub,
303 JS_DHashClearEntryStub,
304 JS_DHashFinalizeStub,
305 NULL
308 static JSDHashOperator
309 thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
310 void * /* arg */)
312 JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr;
313 JSThread *thread = entry->thread;
315 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
316 DestroyThread(thread);
317 return JS_DHASH_REMOVE;
320 static JSDHashOperator
321 thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
322 void *arg)
324 JSContext* cx = (JSContext *) arg;
325 JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
327 if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
328 JS_ASSERT(cx->thread != thread);
329 js_DestroyScriptsToGC(cx, &thread->data);
332 * The following is potentially suboptimal as it also zeros the caches
333 * in data, but the code simplicity wins here.
335 thread->data.purgeGCFreeLists();
336 js_PurgeCachedNativeEnumerators(cx, &thread->data);
337 DestroyThread(thread);
338 return JS_DHASH_REMOVE;
340 thread->data.purge(cx);
341 thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT;
342 return JS_DHASH_NEXT;
345 static JSDHashOperator
346 thread_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
347 void *arg)
349 JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
350 thread->data.mark((JSTracer *) arg);
351 return JS_DHASH_NEXT;
354 #endif /* JS_THREADSAFE */
356 JSThreadData *
357 js_CurrentThreadData(JSRuntime *rt)
359 #ifdef JS_THREADSAFE
360 JSThread *thread = js_CurrentThread(rt);
361 if (!thread)
362 return NULL;
364 return &thread->data;
365 #else
366 return &rt->threadData;
367 #endif
370 JSBool
371 js_InitThreads(JSRuntime *rt)
373 #ifdef JS_THREADSAFE
374 if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL,
375 sizeof(JSThreadsHashEntry), 4)) {
376 rt->threads.ops = NULL;
377 return false;
379 #else
380 rt->threadData.init();
381 #endif
382 return true;
385 void
386 js_FinishThreads(JSRuntime *rt)
388 #ifdef JS_THREADSAFE
389 if (!rt->threads.ops)
390 return;
391 JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL);
392 JS_DHashTableFinish(&rt->threads);
393 rt->threads.ops = NULL;
394 #else
395 rt->threadData.finish();
396 #endif
399 void
400 js_PurgeThreads(JSContext *cx)
402 #ifdef JS_THREADSAFE
403 JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx);
404 #else
405 cx->runtime->threadData.purge(cx);
406 #endif
409 void
410 js_TraceThreads(JSRuntime *rt, JSTracer *trc)
412 #ifdef JS_THREADSAFE
413 JS_DHashTableEnumerate(&rt->threads, thread_marker, trc);
414 #else
415 rt->threadData.mark(trc);
416 #endif
420 * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version
421 * associated with scripts, so in addition to storing them in cx->options we
422 * duplicate them in cx->version (script->version, etc.) and ensure each bit
423 * remains synchronized between the two through these two functions.
425 void
426 js_SyncOptionsToVersion(JSContext* cx)
428 if (cx->options & JSOPTION_XML)
429 cx->version |= JSVERSION_HAS_XML;
430 else
431 cx->version &= ~JSVERSION_HAS_XML;
432 if (cx->options & JSOPTION_ANONFUNFIX)
433 cx->version |= JSVERSION_ANONFUNFIX;
434 else
435 cx->version &= ~JSVERSION_ANONFUNFIX;
438 inline void
439 js_SyncVersionToOptions(JSContext* cx)
441 if (cx->version & JSVERSION_HAS_XML)
442 cx->options |= JSOPTION_XML;
443 else
444 cx->options &= ~JSOPTION_XML;
445 if (cx->version & JSVERSION_ANONFUNFIX)
446 cx->options |= JSOPTION_ANONFUNFIX;
447 else
448 cx->options &= ~JSOPTION_ANONFUNFIX;
451 void
452 js_OnVersionChange(JSContext *cx)
454 #ifdef DEBUG
455 JSVersion version = JSVERSION_NUMBER(cx);
457 JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
458 #endif
461 void
462 js_SetVersion(JSContext *cx, JSVersion version)
464 cx->version = version;
465 js_SyncVersionToOptions(cx);
466 js_OnVersionChange(cx);
469 JSContext *
470 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
472 JSContext *cx;
473 JSBool ok, first;
474 JSContextCallback cxCallback;
477 * We need to initialize the new context fully before adding it to the
478 * runtime list. After that it can be accessed from another thread via
479 * js_ContextIterator.
481 void *mem = js_calloc(sizeof *cx);
482 if (!mem)
483 return NULL;
485 cx = new (mem) JSContext(rt);
486 cx->debugHooks = &rt->globalDebugHooks;
487 #if JS_STACK_GROWTH_DIRECTION > 0
488 cx->stackLimit = (jsuword) -1;
489 #endif
490 cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
491 JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
492 JS_ASSERT(cx->version == JSVERSION_DEFAULT);
493 VOUCH_DOES_NOT_REQUIRE_STACK();
494 JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
495 &cx->scriptStackQuota);
497 JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble),
498 &cx->scriptStackQuota);
500 js_InitRegExpStatics(cx);
501 JS_ASSERT(cx->resolveFlags == 0);
503 #ifdef JS_THREADSAFE
504 if (!js_InitContextThread(cx)) {
505 FreeContext(cx);
506 return NULL;
508 #endif
511 * Here the GC lock is still held after js_InitContextThread took it and
512 * the GC is not running on another thread.
514 for (;;) {
515 if (rt->state == JSRTS_UP) {
516 JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
517 first = JS_FALSE;
518 break;
520 if (rt->state == JSRTS_DOWN) {
521 JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList));
522 first = JS_TRUE;
523 rt->state = JSRTS_LAUNCHING;
524 break;
526 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
529 * During the above wait after we are notified about the state change
530 * but before we wake up, another thread could enter the GC from
531 * js_DestroyContext, bug 478336. So we must wait here to ensure that
532 * when we exit the loop with the first flag set to true, that GC is
533 * finished.
535 js_WaitForGC(rt);
537 JS_APPEND_LINK(&cx->link, &rt->contextList);
538 JS_UNLOCK_GC(rt);
540 js_InitRandom(cx);
543 * If cx is the first context on this runtime, initialize well-known atoms,
544 * keywords, numbers, and strings. If one of these steps should fail, the
545 * runtime will be left in a partially initialized state, with zeroes and
546 * nulls stored in the default-initialized remainder of the struct. We'll
547 * clean the runtime up under js_DestroyContext, because cx will be "last"
548 * as well as "first".
550 if (first) {
551 #ifdef JS_THREADSAFE
552 JS_BeginRequest(cx);
553 #endif
554 ok = js_InitCommonAtoms(cx);
557 * scriptFilenameTable may be left over from a previous episode of
558 * non-zero contexts alive in rt, so don't re-init the table if it's
559 * not necessary.
561 if (ok && !rt->scriptFilenameTable)
562 ok = js_InitRuntimeScriptState(rt);
563 if (ok)
564 ok = js_InitRuntimeNumberState(cx);
565 if (ok)
566 ok = JSScope::initRuntimeState(cx);
568 #ifdef JS_THREADSAFE
569 JS_EndRequest(cx);
570 #endif
571 if (!ok) {
572 js_DestroyContext(cx, JSDCM_NEW_FAILED);
573 return NULL;
576 AutoLockGC lock(rt);
577 rt->state = JSRTS_UP;
578 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
581 cxCallback = rt->cxCallback;
582 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
583 js_DestroyContext(cx, JSDCM_NEW_FAILED);
584 return NULL;
587 /* Using ContextAllocPolicy, so init after JSContext is ready. */
588 if (!cx->busyArrays.init()) {
589 FreeContext(cx);
590 return NULL;
593 return cx;
596 #if defined DEBUG && defined XP_UNIX
597 # include <stdio.h>
599 class JSAutoFile {
600 public:
601 JSAutoFile() : mFile(NULL) {}
603 ~JSAutoFile() {
604 if (mFile)
605 fclose(mFile);
608 FILE *open(const char *fname, const char *mode) {
609 return mFile = fopen(fname, mode);
611 operator FILE *() {
612 return mFile;
615 private:
616 FILE *mFile;
619 #ifdef JS_EVAL_CACHE_METERING
620 static void
621 DumpEvalCacheMeter(JSContext *cx)
623 struct {
624 const char *name;
625 ptrdiff_t offset;
626 } table[] = {
627 #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
628 EVAL_CACHE_METER_LIST(frob)
629 #undef frob
631 JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
633 static JSAutoFile fp;
634 if (!fp) {
635 fp.open("/tmp/evalcache.stats", "w");
636 if (!fp)
637 return;
640 fprintf(fp, "eval cache meter (%p):\n",
641 #ifdef JS_THREADSAFE
642 (void *) cx->thread
643 #else
644 (void *) cx->runtime
645 #endif
647 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
648 fprintf(fp, "%-8.8s %llu\n",
649 table[i].name,
650 (unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset));
652 fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
653 fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
654 fflush(fp);
656 # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
657 #endif
659 #ifdef JS_FUNCTION_METERING
660 static void
661 DumpFunctionMeter(JSContext *cx)
663 struct {
664 const char *name;
665 ptrdiff_t offset;
666 } table[] = {
667 #define frob(x) { #x, offsetof(JSFunctionMeter, x) }
668 FUNCTION_KIND_METER_LIST(frob)
669 #undef frob
671 JSFunctionMeter *fm = &cx->runtime->functionMeter;
673 static JSAutoFile fp;
674 if (!fp) {
675 fp.open("/tmp/function.stats", "a");
676 if (!fp)
677 return;
680 fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
681 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
682 fprintf(fp, "%-11.11s %d\n",
683 table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
685 fflush(fp);
687 # define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
688 #endif
690 #endif /* DEBUG && XP_UNIX */
692 #ifndef DUMP_EVAL_CACHE_METER
693 # define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
694 #endif
696 #ifndef DUMP_FUNCTION_METER
697 # define DUMP_FUNCTION_METER(cx) ((void) 0)
698 #endif
700 #ifdef JS_PROTO_CACHE_METERING
701 static void
702 DumpProtoCacheMeter(JSContext *cx)
704 JSClassProtoCache::Stats *stats = &cx->runtime->classProtoCacheStats;
705 FILE *fp = fopen("/tmp/protocache.stats", "a");
706 fprintf(fp,
707 "hit ratio %g%%\n",
708 double(stats->hit) * 100.0 / double(stats->probe));
709 fclose(fp);
712 # define DUMP_PROTO_CACHE_METER(cx) DumpProtoCacheMeter(cx)
713 #else
714 # define DUMP_PROTO_CACHE_METER(cx) ((void) 0)
715 #endif
718 void
719 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
721 JSRuntime *rt;
722 JSContextCallback cxCallback;
723 JSBool last;
725 rt = cx->runtime;
726 #ifdef JS_THREADSAFE
728 * For API compatibility we allow to destroy contexts without a thread in
729 * optimized builds. We assume that the embedding knows that an OOM error
730 * cannot happen in JS_SetContextThread.
732 JS_ASSERT(cx->thread && CURRENT_THREAD_IS_ME(cx->thread));
733 if (!cx->thread)
734 JS_SetContextThread(cx);
736 JS_ASSERT_IF(rt->gcRunning, cx->outstandingRequests == 0);
737 #endif
739 if (mode != JSDCM_NEW_FAILED) {
740 cxCallback = rt->cxCallback;
741 if (cxCallback) {
743 * JSCONTEXT_DESTROY callback is not allowed to fail and must
744 * return true.
746 #ifdef DEBUG
747 JSBool callbackStatus =
748 #endif
749 cxCallback(cx, JSCONTEXT_DESTROY);
750 JS_ASSERT(callbackStatus);
754 JS_LOCK_GC(rt);
755 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
756 #ifdef JS_THREADSAFE
758 * Typically we are called outside a request, so ensure that the GC is not
759 * running before removing the context from rt->contextList, see bug 477021.
761 if (cx->requestDepth == 0)
762 js_WaitForGC(rt);
763 #endif
764 JS_REMOVE_LINK(&cx->link);
765 last = (rt->contextList.next == &rt->contextList);
766 if (last)
767 rt->state = JSRTS_LANDING;
768 if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
769 #ifdef JS_THREADSAFE
770 || cx->requestDepth != 0
771 #endif
773 JS_ASSERT(!rt->gcRunning);
775 JS_UNLOCK_GC(rt);
777 if (last) {
778 #ifdef JS_THREADSAFE
780 * If cx is not in a request already, begin one now so that we wait
781 * for any racing GC started on a not-last context to finish, before
782 * we plow ahead and unpin atoms. Note that even though we begin a
783 * request here if necessary, we end all requests on cx below before
784 * forcing a final GC. This lets any not-last context destruction
785 * racing in another thread try to force or maybe run the GC, but by
786 * that point, rt->state will not be JSRTS_UP, and that GC attempt
787 * will return early.
789 if (cx->requestDepth == 0)
790 JS_BeginRequest(cx);
791 #endif
793 JSScope::finishRuntimeState(cx);
794 js_FinishRuntimeNumberState(cx);
796 /* Unpin all common atoms before final GC. */
797 js_FinishCommonAtoms(cx);
799 /* Clear debugging state to remove GC roots. */
800 JS_ClearAllTraps(cx);
801 JS_ClearAllWatchPoints(cx);
804 /* Remove more GC roots in regExpStatics, then collect garbage. */
805 JS_ClearRegExpRoots(cx);
807 #ifdef JS_THREADSAFE
809 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
810 * end our request here in case we are "last" -- in that event, another
811 * js_DestroyContext that was not last might be waiting in the GC for our
812 * request to end. We'll let it run below, just before we do the truly
813 * final GC and then free atom state.
815 while (cx->requestDepth != 0)
816 JS_EndRequest(cx);
817 #endif
819 if (last) {
820 js_GC(cx, GC_LAST_CONTEXT);
821 DUMP_EVAL_CACHE_METER(cx);
822 DUMP_FUNCTION_METER(cx);
823 DUMP_PROTO_CACHE_METER(cx);
825 /* Take the runtime down, now that it has no contexts or atoms. */
826 JS_LOCK_GC(rt);
827 rt->state = JSRTS_DOWN;
828 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
829 } else {
830 if (mode == JSDCM_FORCE_GC)
831 js_GC(cx, GC_NORMAL);
832 else if (mode == JSDCM_MAYBE_GC)
833 JS_MaybeGC(cx);
834 JS_LOCK_GC(rt);
835 js_WaitForGC(rt);
838 #ifdef JS_THREADSAFE
839 js_ClearContextThread(cx);
840 #endif
841 JS_UNLOCK_GC(rt);
842 FreeContext(cx);
845 static void
846 FreeContext(JSContext *cx)
848 #ifdef JS_THREADSAFE
849 JS_ASSERT(!cx->thread);
850 #endif
852 /* Free the stuff hanging off of cx. */
853 js_FreeRegExpStatics(cx);
854 VOUCH_DOES_NOT_REQUIRE_STACK();
855 JS_FinishArenaPool(&cx->stackPool);
856 JS_FinishArenaPool(&cx->tempPool);
858 if (cx->lastMessage)
859 js_free(cx->lastMessage);
861 /* Remove any argument formatters. */
862 JSArgumentFormatMap *map = cx->argumentFormatMap;
863 while (map) {
864 JSArgumentFormatMap *temp = map;
865 map = map->next;
866 cx->free(temp);
869 /* Destroy the resolve recursion damper. */
870 if (cx->resolvingTable) {
871 JS_DHashTableDestroy(cx->resolvingTable);
872 cx->resolvingTable = NULL;
875 /* Finally, free cx itself. */
876 cx->~JSContext();
877 js_free(cx);
880 JSBool
881 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
883 JSCList *cl;
885 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
886 if (cl == &cx->link)
887 return JS_TRUE;
889 JS_RUNTIME_METER(rt, deadContexts);
890 return JS_FALSE;
893 JSContext *
894 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
896 JSContext *cx = *iterp;
898 Conditionally<AutoLockGC> lockIf(!!unlocked, rt);
899 cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next);
900 if (&cx->link == &rt->contextList)
901 cx = NULL;
902 *iterp = cx;
903 return cx;
906 JS_FRIEND_API(JSContext *)
907 js_NextActiveContext(JSRuntime *rt, JSContext *cx)
909 JSContext *iter = cx;
910 #ifdef JS_THREADSAFE
911 while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
912 if (cx->requestDepth)
913 break;
915 return cx;
916 #else
917 return js_ContextIterator(rt, JS_FALSE, &iter);
918 #endif
921 #ifdef JS_THREADSAFE
923 uint32
924 js_CountThreadRequests(JSContext *cx)
926 JSCList *head, *link;
927 uint32 nrequests;
929 JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
930 head = &cx->thread->contextList;
931 nrequests = 0;
932 for (link = head->next; link != head; link = link->next) {
933 JSContext *acx = CX_FROM_THREAD_LINKS(link);
934 JS_ASSERT(acx->thread == cx->thread);
935 if (acx->requestDepth)
936 nrequests++;
938 return nrequests;
942 * If the GC is running and we're called on another thread, wait for this GC
943 * activation to finish. We can safely wait here without fear of deadlock (in
944 * the case where we are called within a request on another thread's context)
945 * because the GC doesn't set rt->gcRunning until after it has waited for all
946 * active requests to end.
948 * We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid
949 * expensive calls when the GC is not running.
951 void
952 js_WaitForGC(JSRuntime *rt)
954 JS_ASSERT_IF(rt->gcRunning, rt->gcLevel > 0);
955 if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
956 do {
957 JS_AWAIT_GC_DONE(rt);
958 } while (rt->gcRunning);
962 #endif
964 static JSDHashNumber
965 resolving_HashKey(JSDHashTable *table, const void *ptr)
967 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
969 return (JSDHashNumber(uintptr_t(key->obj)) >> JSVAL_TAGBITS) ^ key->id;
972 JS_PUBLIC_API(JSBool)
973 resolving_MatchEntry(JSDHashTable *table,
974 const JSDHashEntryHdr *hdr,
975 const void *ptr)
977 const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
978 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
980 return entry->key.obj == key->obj && entry->key.id == key->id;
983 static const JSDHashTableOps resolving_dhash_ops = {
984 JS_DHashAllocTable,
985 JS_DHashFreeTable,
986 resolving_HashKey,
987 resolving_MatchEntry,
988 JS_DHashMoveEntryStub,
989 JS_DHashClearEntryStub,
990 JS_DHashFinalizeStub,
991 NULL
994 JSBool
995 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
996 JSResolvingEntry **entryp)
998 JSDHashTable *table;
999 JSResolvingEntry *entry;
1001 table = cx->resolvingTable;
1002 if (!table) {
1003 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
1004 sizeof(JSResolvingEntry),
1005 JS_DHASH_MIN_SIZE);
1006 if (!table)
1007 goto outofmem;
1008 cx->resolvingTable = table;
1011 entry = (JSResolvingEntry *)
1012 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
1013 if (!entry)
1014 goto outofmem;
1016 if (entry->flags & flag) {
1017 /* An entry for (key, flag) exists already -- dampen recursion. */
1018 entry = NULL;
1019 } else {
1020 /* Fill in key if we were the first to add entry, then set flag. */
1021 if (!entry->key.obj)
1022 entry->key = *key;
1023 entry->flags |= flag;
1025 *entryp = entry;
1026 return JS_TRUE;
1028 outofmem:
1029 JS_ReportOutOfMemory(cx);
1030 return JS_FALSE;
1033 void
1034 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
1035 JSResolvingEntry *entry, uint32 generation)
1037 JSDHashTable *table;
1040 * Clear flag from entry->flags and return early if other flags remain.
1041 * We must take care to re-lookup entry if the table has changed since
1042 * it was found by js_StartResolving.
1044 table = cx->resolvingTable;
1045 if (!entry || table->generation != generation) {
1046 entry = (JSResolvingEntry *)
1047 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
1049 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
1050 entry->flags &= ~flag;
1051 if (entry->flags)
1052 return;
1055 * Do a raw remove only if fewer entries were removed than would cause
1056 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
1057 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
1058 * compressing or shrinking the table as needed.
1060 if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
1061 JS_DHashTableRawRemove(table, &entry->hdr);
1062 else
1063 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
1066 JSBool
1067 js_EnterLocalRootScope(JSContext *cx)
1069 JSThreadData *td = JS_THREAD_DATA(cx);
1070 JSLocalRootStack *lrs = td->localRootStack;
1071 if (!lrs) {
1072 lrs = (JSLocalRootStack *) js_malloc(sizeof *lrs);
1073 if (!lrs) {
1074 js_ReportOutOfMemory(cx);
1075 return false;
1077 lrs->scopeMark = JSLRS_NULL_MARK;
1078 lrs->rootCount = 0;
1079 lrs->topChunk = &lrs->firstChunk;
1080 lrs->firstChunk.down = NULL;
1081 td->gcFreeLists.moveTo(&lrs->gcFreeLists);
1082 td->localRootStack = lrs;
1085 /* Push lrs->scopeMark to save it for restore when leaving. */
1086 int mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
1087 if (mark < 0)
1088 return JS_FALSE;
1089 lrs->scopeMark = (uint32) mark;
1090 return true;
1093 void
1094 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
1096 JSLocalRootStack *lrs;
1097 uint32 mark, m, n;
1098 JSLocalRootChunk *lrc;
1100 /* Defend against buggy native callers. */
1101 lrs = JS_THREAD_DATA(cx)->localRootStack;
1102 JS_ASSERT(lrs && lrs->rootCount != 0);
1103 if (!lrs || lrs->rootCount == 0)
1104 return;
1106 mark = lrs->scopeMark;
1107 JS_ASSERT(mark != JSLRS_NULL_MARK);
1108 if (mark == JSLRS_NULL_MARK)
1109 return;
1111 /* Free any chunks being popped by this leave operation. */
1112 m = mark >> JSLRS_CHUNK_SHIFT;
1113 n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
1114 while (n > m) {
1115 lrc = lrs->topChunk;
1116 JS_ASSERT(lrc != &lrs->firstChunk);
1117 lrs->topChunk = lrc->down;
1118 js_free(lrc);
1119 --n;
1123 * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
1124 * it on the caller's scope, or store it in lastInternalResult if we are
1125 * leaving the outermost scope. We don't need to allocate a new lrc
1126 * because we can overwrite the old mark's slot with rval.
1128 lrc = lrs->topChunk;
1129 m = mark & JSLRS_CHUNK_MASK;
1130 lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
1131 if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
1132 if (mark == 0) {
1133 cx->weakRoots.lastInternalResult = rval;
1134 } else {
1136 * Increment m to avoid the "else if (m == 0)" case below. If
1137 * rval is not a GC-thing, that case would take care of freeing
1138 * any chunk that contained only the old mark. Since rval *is*
1139 * a GC-thing here, we want to reuse that old mark's slot.
1141 lrc->roots[m++] = rval;
1142 ++mark;
1145 lrs->rootCount = (uint32) mark;
1148 * Free the stack eagerly, risking malloc churn. The alternative would
1149 * require an lrs->entryCount member, maintained by Enter and Leave, and
1150 * tested by the GC in addition to the cx->localRootStack non-null test.
1152 * That approach would risk hoarding 264 bytes (net) per context. Right
1153 * now it seems better to give fresh (dirty in CPU write-back cache, and
1154 * the data is no longer needed) memory back to the malloc heap.
1156 if (mark == 0) {
1157 JSThreadData *td = JS_THREAD_DATA(cx);
1158 JS_ASSERT(td->gcFreeLists.isEmpty());
1159 lrs->gcFreeLists.moveTo(&td->gcFreeLists);
1160 td->localRootStack = NULL;
1161 js_free(lrs);
1162 } else if (m == 0) {
1163 lrs->topChunk = lrc->down;
1164 js_free(lrc);
1168 void
1169 js_ForgetLocalRoot(JSContext *cx, jsval v)
1171 JSLocalRootStack *lrs;
1172 uint32 i, j, m, n, mark;
1173 JSLocalRootChunk *lrc, *lrc2;
1174 jsval top;
1176 lrs = JS_THREAD_DATA(cx)->localRootStack;
1177 JS_ASSERT(lrs && lrs->rootCount);
1178 if (!lrs || lrs->rootCount == 0)
1179 return;
1181 /* Prepare to pop the top-most value from the stack. */
1182 n = lrs->rootCount - 1;
1183 m = n & JSLRS_CHUNK_MASK;
1184 lrc = lrs->topChunk;
1185 top = lrc->roots[m];
1187 /* Be paranoid about calls on an empty scope. */
1188 mark = lrs->scopeMark;
1189 JS_ASSERT(mark < n);
1190 if (mark >= n)
1191 return;
1193 /* If v was not the last root pushed in the top scope, find it. */
1194 if (top != v) {
1195 /* Search downward in case v was recently pushed. */
1196 i = n;
1197 j = m;
1198 lrc2 = lrc;
1199 while (--i > mark) {
1200 if (j == 0)
1201 lrc2 = lrc2->down;
1202 j = i & JSLRS_CHUNK_MASK;
1203 if (lrc2->roots[j] == v)
1204 break;
1207 /* If we didn't find v in this scope, assert and bail out. */
1208 JS_ASSERT(i != mark);
1209 if (i == mark)
1210 return;
1212 /* Swap top and v so common tail code can pop v. */
1213 lrc2->roots[j] = top;
1216 /* Pop the last value from the stack. */
1217 lrc->roots[m] = JSVAL_NULL;
1218 lrs->rootCount = n;
1219 if (m == 0) {
1220 JS_ASSERT(n != 0);
1221 JS_ASSERT(lrc != &lrs->firstChunk);
1222 lrs->topChunk = lrc->down;
1223 cx->free(lrc);
1228 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
1230 uint32 n, m;
1231 JSLocalRootChunk *lrc;
1233 n = lrs->rootCount;
1234 m = n & JSLRS_CHUNK_MASK;
1235 if (n == 0 || m != 0) {
1237 * At start of first chunk, or not at start of a non-first top chunk.
1238 * Check for lrs->rootCount overflow.
1240 if ((uint32)(n + 1) == 0) {
1241 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1242 JSMSG_TOO_MANY_LOCAL_ROOTS);
1243 return -1;
1245 lrc = lrs->topChunk;
1246 JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
1247 } else {
1249 * After lrs->firstChunk, trying to index at a power-of-two chunk
1250 * boundary: need a new chunk.
1252 lrc = (JSLocalRootChunk *) js_malloc(sizeof *lrc);
1253 if (!lrc) {
1254 js_ReportOutOfMemory(cx);
1255 return -1;
1257 lrc->down = lrs->topChunk;
1258 lrs->topChunk = lrc;
1260 lrs->rootCount = n + 1;
1261 lrc->roots[m] = v;
1262 return (int) n;
1265 static void
1266 MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
1268 uint32 n, m, mark;
1269 JSLocalRootChunk *lrc;
1270 jsval v;
1272 n = lrs->rootCount;
1273 if (n == 0)
1274 return;
1276 mark = lrs->scopeMark;
1277 lrc = lrs->topChunk;
1278 do {
1279 while (--n > mark) {
1280 m = n & JSLRS_CHUNK_MASK;
1281 v = lrc->roots[m];
1282 JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
1283 JS_SET_TRACING_INDEX(trc, "local_root", n);
1284 js_CallValueTracerIfGCThing(trc, v);
1285 if (m == 0)
1286 lrc = lrc->down;
1288 m = n & JSLRS_CHUNK_MASK;
1289 mark = JSVAL_TO_INT(lrc->roots[m]);
1290 if (m == 0)
1291 lrc = lrc->down;
1292 } while (n != 0);
1293 JS_ASSERT(!lrc);
1296 static void
1297 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
1298 JSErrorCallback callback, void *userRef)
1301 * Check the error report, and set a JavaScript-catchable exception
1302 * if the error is defined to have an associated exception. If an
1303 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
1304 * on the error report, and exception-aware hosts should ignore it.
1306 JS_ASSERT(reportp);
1307 if ((!callback || callback == js_GetErrorMessage) &&
1308 reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
1309 reportp->flags |= JSREPORT_EXCEPTION;
1312 * Call the error reporter only if an exception wasn't raised.
1314 * If an exception was raised, then we call the debugErrorHook
1315 * (if present) to give it a chance to see the error before it
1316 * propagates out of scope. This is needed for compatability
1317 * with the old scheme.
1319 if (!JS_IsRunning(cx) ||
1320 !js_ErrorToException(cx, message, reportp, callback, userRef)) {
1321 js_ReportErrorAgain(cx, message, reportp);
1322 } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
1323 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1324 /* test local in case debugErrorHook changed on another thread */
1325 if (hook)
1326 hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
1330 /* The report must be initially zeroed. */
1331 static void
1332 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
1334 JSStackFrame *fp;
1337 * Walk stack until we find a frame that is associated with some script
1338 * rather than a native frame.
1340 for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
1341 if (fp->regs) {
1342 report->filename = fp->script->filename;
1343 report->lineno = js_FramePCToLineNumber(cx, fp);
1344 break;
1350 * We don't post an exception in this case, since doing so runs into
1351 * complications of pre-allocating an exception object which required
1352 * running the Exception class initializer early etc.
1353 * Instead we just invoke the errorReporter with an "Out Of Memory"
1354 * type message, and then hope the process ends swiftly.
1356 void
1357 js_ReportOutOfMemory(JSContext *cx)
1359 #ifdef JS_TRACER
1361 * If we are in a builtin called directly from trace, don't report an
1362 * error. We will retry in the interpreter instead.
1364 if (JS_ON_TRACE(cx) && !cx->bailExit)
1365 return;
1366 #endif
1368 JSErrorReport report;
1369 JSErrorReporter onError = cx->errorReporter;
1371 /* Get the message for this error, but we won't expand any arguments. */
1372 const JSErrorFormatString *efs =
1373 js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
1374 const char *msg = efs ? efs->format : "Out of memory";
1376 /* Fill out the report, but don't do anything that requires allocation. */
1377 PodZero(&report);
1378 report.flags = JSREPORT_ERROR;
1379 report.errorNumber = JSMSG_OUT_OF_MEMORY;
1380 PopulateReportBlame(cx, &report);
1383 * If debugErrorHook is present then we give it a chance to veto sending
1384 * the error on to the regular ErrorReporter. We also clear a pending
1385 * exception if any now so the hooks can replace the out-of-memory error
1386 * by a script-catchable exception.
1388 cx->throwing = JS_FALSE;
1389 if (onError) {
1390 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1391 if (hook &&
1392 !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
1393 onError = NULL;
1397 if (onError)
1398 onError(cx, msg, &report);
1401 void
1402 js_ReportOutOfScriptQuota(JSContext *cx)
1404 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1405 JSMSG_SCRIPT_STACK_QUOTA);
1408 void
1409 js_ReportOverRecursed(JSContext *cx)
1411 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1414 void
1415 js_ReportAllocationOverflow(JSContext *cx)
1417 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
1421 * Given flags and the state of cx, decide whether we should report an
1422 * error, a warning, or just continue execution normally. Return
1423 * true if we should continue normally, without reporting anything;
1424 * otherwise, adjust *flags as appropriate and return false.
1426 static bool
1427 checkReportFlags(JSContext *cx, uintN *flags)
1429 if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
1431 * Error in strict code; warning with strict option; okay otherwise.
1432 * We assume that if the top frame is a native, then it is strict if
1433 * the nearest scripted frame is strict, see bug 536306.
1435 JSStackFrame *fp = js_GetScriptedCaller(cx, NULL);
1436 if (fp && fp->script->strictModeCode)
1437 *flags &= ~JSREPORT_WARNING;
1438 else if (JS_HAS_STRICT_OPTION(cx))
1439 *flags |= JSREPORT_WARNING;
1440 else
1441 return true;
1442 } else if (JSREPORT_IS_STRICT(*flags)) {
1443 /* Warning/error only when JSOPTION_STRICT is set. */
1444 if (!JS_HAS_STRICT_OPTION(cx))
1445 return true;
1448 /* Warnings become errors when JSOPTION_WERROR is set. */
1449 if (JSREPORT_IS_WARNING(*flags) && JS_HAS_WERROR_OPTION(cx))
1450 *flags &= ~JSREPORT_WARNING;
1452 return false;
1455 JSBool
1456 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
1458 char *message;
1459 jschar *ucmessage;
1460 size_t messagelen;
1461 JSErrorReport report;
1462 JSBool warning;
1464 if (checkReportFlags(cx, &flags))
1465 return JS_TRUE;
1467 message = JS_vsmprintf(format, ap);
1468 if (!message)
1469 return JS_FALSE;
1470 messagelen = strlen(message);
1472 PodZero(&report);
1473 report.flags = flags;
1474 report.errorNumber = JSMSG_USER_DEFINED_ERROR;
1475 report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
1476 PopulateReportBlame(cx, &report);
1478 warning = JSREPORT_IS_WARNING(report.flags);
1480 ReportError(cx, message, &report, NULL, NULL);
1481 js_free(message);
1482 cx->free(ucmessage);
1483 return warning;
1487 * The arguments from ap need to be packaged up into an array and stored
1488 * into the report struct.
1490 * The format string addressed by the error number may contain operands
1491 * identified by the format {N}, where N is a decimal digit. Each of these
1492 * is to be replaced by the Nth argument from the va_list. The complete
1493 * message is placed into reportp->ucmessage converted to a JSString.
1495 * Returns true if the expansion succeeds (can fail if out of memory).
1497 JSBool
1498 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
1499 void *userRef, const uintN errorNumber,
1500 char **messagep, JSErrorReport *reportp,
1501 bool charArgs, va_list ap)
1503 const JSErrorFormatString *efs;
1504 int i;
1505 int argCount;
1507 *messagep = NULL;
1509 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1510 if (!callback || callback == js_GetErrorMessage)
1511 efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
1512 else
1513 efs = callback(userRef, NULL, errorNumber);
1514 if (efs) {
1515 size_t totalArgsLength = 0;
1516 size_t argLengths[10]; /* only {0} thru {9} supported */
1517 argCount = efs->argCount;
1518 JS_ASSERT(argCount <= 10);
1519 if (argCount > 0) {
1521 * Gather the arguments into an array, and accumulate
1522 * their sizes. We allocate 1 more than necessary and
1523 * null it out to act as the caboose when we free the
1524 * pointers later.
1526 reportp->messageArgs = (const jschar **)
1527 cx->malloc(sizeof(jschar *) * (argCount + 1));
1528 if (!reportp->messageArgs)
1529 return JS_FALSE;
1530 reportp->messageArgs[argCount] = NULL;
1531 for (i = 0; i < argCount; i++) {
1532 if (charArgs) {
1533 char *charArg = va_arg(ap, char *);
1534 size_t charArgLength = strlen(charArg);
1535 reportp->messageArgs[i]
1536 = js_InflateString(cx, charArg, &charArgLength);
1537 if (!reportp->messageArgs[i])
1538 goto error;
1539 } else {
1540 reportp->messageArgs[i] = va_arg(ap, jschar *);
1542 argLengths[i] = js_strlen(reportp->messageArgs[i]);
1543 totalArgsLength += argLengths[i];
1545 /* NULL-terminate for easy copying. */
1546 reportp->messageArgs[i] = NULL;
1549 * Parse the error format, substituting the argument X
1550 * for {X} in the format.
1552 if (argCount > 0) {
1553 if (efs->format) {
1554 jschar *buffer, *fmt, *out;
1555 int expandedArgs = 0;
1556 size_t expandedLength;
1557 size_t len = strlen(efs->format);
1559 buffer = fmt = js_InflateString (cx, efs->format, &len);
1560 if (!buffer)
1561 goto error;
1562 expandedLength = len
1563 - (3 * argCount) /* exclude the {n} */
1564 + totalArgsLength;
1567 * Note - the above calculation assumes that each argument
1568 * is used once and only once in the expansion !!!
1570 reportp->ucmessage = out = (jschar *)
1571 cx->malloc((expandedLength + 1) * sizeof(jschar));
1572 if (!out) {
1573 cx->free(buffer);
1574 goto error;
1576 while (*fmt) {
1577 if (*fmt == '{') {
1578 if (isdigit(fmt[1])) {
1579 int d = JS7_UNDEC(fmt[1]);
1580 JS_ASSERT(d < argCount);
1581 js_strncpy(out, reportp->messageArgs[d],
1582 argLengths[d]);
1583 out += argLengths[d];
1584 fmt += 3;
1585 expandedArgs++;
1586 continue;
1589 *out++ = *fmt++;
1591 JS_ASSERT(expandedArgs == argCount);
1592 *out = 0;
1593 cx->free(buffer);
1594 *messagep =
1595 js_DeflateString(cx, reportp->ucmessage,
1596 (size_t)(out - reportp->ucmessage));
1597 if (!*messagep)
1598 goto error;
1600 } else {
1602 * Zero arguments: the format string (if it exists) is the
1603 * entire message.
1605 if (efs->format) {
1606 size_t len;
1607 *messagep = JS_strdup(cx, efs->format);
1608 if (!*messagep)
1609 goto error;
1610 len = strlen(*messagep);
1611 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1612 if (!reportp->ucmessage)
1613 goto error;
1617 if (*messagep == NULL) {
1618 /* where's the right place for this ??? */
1619 const char *defaultErrorMessage
1620 = "No error message available for error number %d";
1621 size_t nbytes = strlen(defaultErrorMessage) + 16;
1622 *messagep = (char *)cx->malloc(nbytes);
1623 if (!*messagep)
1624 goto error;
1625 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1627 return JS_TRUE;
1629 error:
1630 if (reportp->messageArgs) {
1631 /* free the arguments only if we allocated them */
1632 if (charArgs) {
1633 i = 0;
1634 while (reportp->messageArgs[i])
1635 cx->free((void *)reportp->messageArgs[i++]);
1637 cx->free((void *)reportp->messageArgs);
1638 reportp->messageArgs = NULL;
1640 if (reportp->ucmessage) {
1641 cx->free((void *)reportp->ucmessage);
1642 reportp->ucmessage = NULL;
1644 if (*messagep) {
1645 cx->free((void *)*messagep);
1646 *messagep = NULL;
1648 return JS_FALSE;
1651 JSBool
1652 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1653 void *userRef, const uintN errorNumber,
1654 JSBool charArgs, va_list ap)
1656 JSErrorReport report;
1657 char *message;
1658 JSBool warning;
1660 if (checkReportFlags(cx, &flags))
1661 return JS_TRUE;
1662 warning = JSREPORT_IS_WARNING(flags);
1664 PodZero(&report);
1665 report.flags = flags;
1666 report.errorNumber = errorNumber;
1667 PopulateReportBlame(cx, &report);
1669 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1670 &message, &report, !!charArgs, ap)) {
1671 return JS_FALSE;
1674 ReportError(cx, message, &report, callback, userRef);
1676 if (message)
1677 cx->free(message);
1678 if (report.messageArgs) {
1680 * js_ExpandErrorArguments owns its messageArgs only if it had to
1681 * inflate the arguments (from regular |char *|s).
1683 if (charArgs) {
1684 int i = 0;
1685 while (report.messageArgs[i])
1686 cx->free((void *)report.messageArgs[i++]);
1688 cx->free((void *)report.messageArgs);
1690 if (report.ucmessage)
1691 cx->free((void *)report.ucmessage);
1693 return warning;
1696 JS_FRIEND_API(void)
1697 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1699 JSErrorReporter onError;
1701 if (!message)
1702 return;
1704 if (cx->lastMessage)
1705 js_free(cx->lastMessage);
1706 cx->lastMessage = JS_strdup(cx, message);
1707 if (!cx->lastMessage)
1708 return;
1709 onError = cx->errorReporter;
1712 * If debugErrorHook is present then we give it a chance to veto
1713 * sending the error on to the regular ErrorReporter.
1715 if (onError) {
1716 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1717 if (hook &&
1718 !hook(cx, cx->lastMessage, reportp,
1719 cx->debugHooks->debugErrorHookData)) {
1720 onError = NULL;
1723 if (onError)
1724 onError(cx, cx->lastMessage, reportp);
1727 void
1728 js_ReportIsNotDefined(JSContext *cx, const char *name)
1730 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1733 JSBool
1734 js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
1735 JSString *fallback)
1737 char *bytes;
1738 JSBool ok;
1740 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1741 if (!bytes)
1742 return JS_FALSE;
1744 if (strcmp(bytes, js_undefined_str) == 0 ||
1745 strcmp(bytes, js_null_str) == 0) {
1746 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1747 js_GetErrorMessage, NULL,
1748 JSMSG_NO_PROPERTIES, bytes,
1749 NULL, NULL);
1750 } else if (JSVAL_IS_VOID(v)) {
1751 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1752 js_GetErrorMessage, NULL,
1753 JSMSG_UNEXPECTED_TYPE, bytes,
1754 js_undefined_str, NULL);
1755 } else {
1756 JS_ASSERT(JSVAL_IS_NULL(v));
1757 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1758 js_GetErrorMessage, NULL,
1759 JSMSG_UNEXPECTED_TYPE, bytes,
1760 js_null_str, NULL);
1763 cx->free(bytes);
1764 return ok;
1767 void
1768 js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
1770 char argbuf[11];
1771 char *bytes;
1772 JSAtom *atom;
1774 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1775 bytes = NULL;
1776 if (VALUE_IS_FUNCTION(cx, *vp)) {
1777 atom = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp))->atom;
1778 bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp,
1779 ATOM_TO_STRING(atom));
1780 if (!bytes)
1781 return;
1783 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1784 JSMSG_MISSING_FUN_ARG, argbuf,
1785 bytes ? bytes : "");
1786 cx->free(bytes);
1789 JSBool
1790 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1791 intN spindex, jsval v, JSString *fallback,
1792 const char *arg1, const char *arg2)
1794 char *bytes;
1795 JSBool ok;
1797 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1798 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1799 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1800 if (!bytes)
1801 return JS_FALSE;
1803 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1804 NULL, errorNumber, bytes, arg1, arg2);
1805 cx->free(bytes);
1806 return ok;
1809 #if defined DEBUG && defined XP_UNIX
1810 /* For gdb usage. */
1811 void js_traceon(JSContext *cx) { cx->tracefp = stderr; cx->tracePrevPc = NULL; }
1812 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1813 #endif
1815 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1816 #define MSG_DEF(name, number, count, exception, format) \
1817 { format, count, exception } ,
1818 #include "js.msg"
1819 #undef MSG_DEF
1822 JS_FRIEND_API(const JSErrorFormatString *)
1823 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1825 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1826 return &js_ErrorFormatString[errorNumber];
1827 return NULL;
1830 JSBool
1831 js_InvokeOperationCallback(JSContext *cx)
1833 JS_ASSERT(cx->operationCallbackFlag);
1836 * Reset the callback flag first, then yield. If another thread is racing
1837 * us here we will accumulate another callback request which will be
1838 * serviced at the next opportunity.
1840 cx->operationCallbackFlag = 0;
1843 * Unless we are going to run the GC, we automatically yield the current
1844 * context every time the operation callback is hit since we might be
1845 * called as a result of an impending GC, which would deadlock if we do
1846 * not yield. Operation callbacks are supposed to happen rarely (seconds,
1847 * not milliseconds) so it is acceptable to yield at every callback.
1849 JSRuntime *rt = cx->runtime;
1850 if (rt->gcIsNeeded) {
1851 js_GC(cx, GC_NORMAL);
1854 * On trace we can exceed the GC quota, see comments in NewGCArena. So
1855 * we check the quota and report OOM here when we are off trace.
1857 bool delayedOutOfMemory;
1858 JS_LOCK_GC(rt);
1859 delayedOutOfMemory = (rt->gcBytes > rt->gcMaxBytes);
1860 JS_UNLOCK_GC(rt);
1861 if (delayedOutOfMemory) {
1862 js_ReportOutOfMemory(cx);
1863 return false;
1866 #ifdef JS_THREADSAFE
1867 else {
1868 JS_YieldRequest(cx);
1870 #endif
1872 JSOperationCallback cb = cx->operationCallback;
1875 * Important: Additional callbacks can occur inside the callback handler
1876 * if it re-enters the JS engine. The embedding must ensure that the
1877 * callback is disconnected before attempting such re-entry.
1880 return !cb || cb(cx);
1883 void
1884 js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
1886 #ifdef JS_THREADSAFE
1887 Conditionally<AutoLockGC> lockIf(!gcLocked, rt);
1888 #endif
1889 JSContext *iter = NULL;
1890 while (JSContext *acx = js_ContextIterator(rt, JS_FALSE, &iter))
1891 JS_TriggerOperationCallback(acx);
1894 JSStackFrame *
1895 js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1897 if (!fp)
1898 fp = js_GetTopStackFrame(cx);
1899 while (fp) {
1900 if (fp->script)
1901 return fp;
1902 fp = fp->down;
1904 return NULL;
1907 jsbytecode*
1908 js_GetCurrentBytecodePC(JSContext* cx)
1910 jsbytecode *pc, *imacpc;
1912 #ifdef JS_TRACER
1913 if (JS_ON_TRACE(cx)) {
1914 pc = cx->bailExit->pc;
1915 imacpc = cx->bailExit->imacpc;
1916 } else
1917 #endif
1919 JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */
1920 JSStackFrame* fp = cx->fp;
1921 if (fp && fp->regs) {
1922 pc = fp->regs->pc;
1923 imacpc = fp->imacpc;
1924 } else {
1925 return NULL;
1930 * If we are inside GetProperty_tn or similar, return a pointer to the
1931 * current instruction in the script, not the CALL instruction in the
1932 * imacro, for the benefit of callers doing bytecode inspection.
1934 return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;
1937 bool
1938 js_CurrentPCIsInImacro(JSContext *cx)
1940 #ifdef JS_TRACER
1941 VOUCH_DOES_NOT_REQUIRE_STACK();
1942 return (JS_ON_TRACE(cx) ? cx->bailExit->imacpc : cx->fp->imacpc) != NULL;
1943 #else
1944 return false;
1945 #endif
1948 CallStack *
1949 JSContext::containingCallStack(JSStackFrame *target)
1951 /* The context may have nothing running. */
1952 CallStack *cs = currentCallStack;
1953 if (!cs)
1954 return NULL;
1956 /* The active callstack's top frame is cx->fp. */
1957 if (fp) {
1958 JS_ASSERT(activeCallStack() == cs);
1959 JSStackFrame *f = fp;
1960 JSStackFrame *stop = cs->getInitialFrame()->down;
1961 for (; f != stop; f = f->down) {
1962 if (f == target)
1963 return cs;
1965 cs = cs->getPrevious();
1968 /* A suspended callstack's top frame is its suspended frame. */
1969 for (; cs; cs = cs->getPrevious()) {
1970 JSStackFrame *f = cs->getSuspendedFrame();
1971 JSStackFrame *stop = cs->getInitialFrame()->down;
1972 for (; f != stop; f = f->down) {
1973 if (f == target)
1974 return cs;
1978 return NULL;
1981 void
1982 JSContext::checkMallocGCPressure(void *p)
1984 if (!p) {
1985 js_ReportOutOfMemory(this);
1986 return;
1989 #ifdef JS_THREADSAFE
1990 JS_ASSERT(thread->gcThreadMallocBytes <= 0);
1991 ptrdiff_t n = JS_GC_THREAD_MALLOC_LIMIT - thread->gcThreadMallocBytes;
1992 thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT;
1994 AutoLockGC lock(runtime);
1995 runtime->gcMallocBytes -= n;
1996 if (runtime->isGCMallocLimitReached())
1997 #endif
1999 JS_ASSERT(runtime->isGCMallocLimitReached());
2000 runtime->gcMallocBytes = -1;
2003 * Empty the GC free lists to trigger a last-ditch GC when allocating
2004 * any GC thing later on this thread. This minimizes the amount of
2005 * checks on the fast path of the GC allocator. Note that we cannot
2006 * touch the free lists on other threads as their manipulation is not
2007 * thread-safe.
2009 JS_THREAD_DATA(this)->purgeGCFreeLists();
2010 js_TriggerGC(this, true);
2015 bool
2016 JSContext::isConstructing()
2018 #ifdef JS_TRACER
2019 if (JS_ON_TRACE(this)) {
2020 JS_ASSERT(bailExit);
2021 return *bailExit->pc == JSOP_NEW;
2023 #endif
2024 JSStackFrame *fp = js_GetTopStackFrame(this);
2025 return fp && (fp->flags & JSFRAME_CONSTRUCTING);
2029 * Release pool's arenas if the stackPool has existed for longer than the
2030 * limit specified by gcEmptyArenaPoolLifespan.
2032 inline void
2033 FreeOldArenas(JSRuntime *rt, JSArenaPool *pool)
2035 JSArena *a = pool->current;
2036 if (a == pool->first.next && a->avail == a->base + sizeof(int64)) {
2037 int64 age = JS_Now() - *(int64 *) a->base;
2038 if (age > int64(rt->gcEmptyArenaPoolLifespan) * 1000)
2039 JS_FreeArenaPool(pool);
2043 void
2044 JSContext::purge()
2046 FreeOldArenas(runtime, &stackPool);
2047 FreeOldArenas(runtime, &regexpPool);
2048 classProtoCache.purge();