/[jscoverage]/trunk/js/jscntxt.cpp
ViewVC logotype

Contents of /trunk/js/jscntxt.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 10 months ago) by siliconforks
File size: 54487 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=80:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
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.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
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.
24 *
25 * Contributor(s):
26 *
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.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS execution context.
43 */
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"
73
74 static void
75 FreeContext(JSContext *cx);
76
77 static void
78 InitThreadData(JSThreadData *data)
79 {
80 #ifdef DEBUG
81 /* The data must be already zeroed. */
82 for (size_t i = 0; i != sizeof(*data); ++i)
83 JS_ASSERT(reinterpret_cast<uint8*>(data)[i] == 0);
84 #endif
85 #ifdef JS_TRACER
86 js_InitJIT(&data->traceMonitor);
87 #endif
88 js_InitRandom(data);
89 }
90
91 static void
92 FinishThreadData(JSThreadData *data)
93 {
94 #ifdef DEBUG
95 /* All GC-related things must be already removed at this point. */
96 for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i)
97 JS_ASSERT(!data->scriptsToGC[i]);
98 for (size_t i = 0; i != JS_ARRAY_LENGTH(data->nativeEnumCache); ++i)
99 JS_ASSERT(!data->nativeEnumCache[i]);
100 #endif
101
102 js_FinishGSNCache(&data->gsnCache);
103 js_FinishPropertyCache(&data->propertyCache);
104 #if defined JS_TRACER
105 js_FinishJIT(&data->traceMonitor);
106 #endif
107 }
108
109 static void
110 PurgeThreadData(JSContext *cx, JSThreadData *data)
111 {
112 js_PurgeGSNCache(&data->gsnCache);
113
114 /* FIXME: bug 506341. */
115 js_PurgePropertyCache(cx, &data->propertyCache);
116
117 # ifdef JS_TRACER
118 JSTraceMonitor *tm = &data->traceMonitor;
119
120 /*
121 * If we are about to regenerate shapes, we have to flush the JIT cache,
122 * which will eventually abort any current recording.
123 */
124 if (cx->runtime->gcRegenShapes)
125 tm->needFlush = JS_TRUE;
126
127 /*
128 * We want to keep reserved doubles and objects after the GC. So, unless we
129 * are shutting down, we don't purge them here and rather mark them during
130 * the GC, see MarkReservedObjects in jsgc.cpp.
131 */
132 if (cx->runtime->state == JSRTS_LANDING) {
133 tm->reservedDoublePoolPtr = tm->reservedDoublePool;
134 tm->reservedObjects = NULL;
135 }
136 # endif
137
138 /* Destroy eval'ed scripts. */
139 js_DestroyScriptsToGC(cx, data);
140
141 js_PurgeCachedNativeEnumerators(cx, data);
142 }
143
144 #ifdef JS_THREADSAFE
145
146 static JSThread *
147 NewThread(jsword id)
148 {
149 JS_ASSERT(js_CurrentThreadId() == id);
150 JSThread *thread = (JSThread *) js_calloc(sizeof(JSThread));
151 if (!thread)
152 return NULL;
153 JS_INIT_CLIST(&thread->contextList);
154 thread->id = id;
155 InitThreadData(&thread->data);
156 return thread;
157 }
158
159 static void
160 DestroyThread(JSThread *thread)
161 {
162 /* The thread must have zero contexts. */
163 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
164 JS_ASSERT(!thread->titleToShare);
165 FinishThreadData(&thread->data);
166 js_free(thread);
167 }
168
169 JSThread *
170 js_CurrentThread(JSRuntime *rt)
171 {
172 jsword id = js_CurrentThreadId();
173 JS_LOCK_GC(rt);
174
175 /*
176 * We must not race with a GC that accesses cx->thread for JSContext
177 * instances on all threads, see bug 476934.
178 */
179 js_WaitForGC(rt);
180 JSThreadsHashEntry *entry = (JSThreadsHashEntry *)
181 JS_DHashTableOperate(&rt->threads,
182 (const void *) id,
183 JS_DHASH_LOOKUP);
184 JSThread *thread;
185 if (JS_DHASH_ENTRY_IS_BUSY(&entry->base)) {
186 thread = entry->thread;
187 JS_ASSERT(thread->id == id);
188 } else {
189 JS_UNLOCK_GC(rt);
190 thread = NewThread(id);
191 if (!thread)
192 return NULL;
193 JS_LOCK_GC(rt);
194 js_WaitForGC(rt);
195 entry = (JSThreadsHashEntry *)
196 JS_DHashTableOperate(&rt->threads, (const void *) id,
197 JS_DHASH_ADD);
198 if (!entry) {
199 JS_UNLOCK_GC(rt);
200 DestroyThread(thread);
201 return NULL;
202 }
203
204 /* Another thread cannot initialize entry->thread. */
205 JS_ASSERT(!entry->thread);
206 entry->thread = thread;
207 }
208
209 return thread;
210 }
211
212 JSBool
213 js_InitContextThread(JSContext *cx)
214 {
215 JSThread *thread = js_CurrentThread(cx->runtime);
216 if (!thread)
217 return false;
218
219 JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
220 cx->thread = thread;
221 return true;
222 }
223
224 void
225 js_ClearContextThread(JSContext *cx)
226 {
227 JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
228 JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
229 cx->thread = NULL;
230 }
231
232 static JSBool
233 thread_matchEntry(JSDHashTable *table,
234 const JSDHashEntryHdr *hdr,
235 const void *key)
236 {
237 const JSThreadsHashEntry *entry = (const JSThreadsHashEntry *) hdr;
238
239 return entry->thread->id == (jsword) key;
240 }
241
242 static const JSDHashTableOps threads_ops = {
243 JS_DHashAllocTable,
244 JS_DHashFreeTable,
245 JS_DHashVoidPtrKeyStub,
246 thread_matchEntry,
247 JS_DHashMoveEntryStub,
248 JS_DHashClearEntryStub,
249 JS_DHashFinalizeStub,
250 NULL
251 };
252
253 static JSDHashOperator
254 thread_destroyer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
255 void * /* arg */)
256 {
257 JSThreadsHashEntry *entry = (JSThreadsHashEntry *) hdr;
258 JSThread *thread = entry->thread;
259
260 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
261 DestroyThread(thread);
262 return JS_DHASH_REMOVE;
263 }
264
265 static JSDHashOperator
266 thread_purger(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
267 void *arg)
268 {
269 JSContext* cx = (JSContext *) arg;
270 JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
271
272 if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
273 JS_ASSERT(cx->thread != thread);
274 js_DestroyScriptsToGC(cx, &thread->data);
275
276 /*
277 * The following is potentially suboptimal as it also zeros the cache
278 * in data, but the code simplicity wins here.
279 */
280 js_PurgeCachedNativeEnumerators(cx, &thread->data);
281 DestroyThread(thread);
282 return JS_DHASH_REMOVE;
283 }
284 PurgeThreadData(cx, &thread->data);
285 memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists));
286 return JS_DHASH_NEXT;
287 }
288
289 static JSDHashOperator
290 thread_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 /* index */,
291 void *arg)
292 {
293 JSThread *thread = ((JSThreadsHashEntry *) hdr)->thread;
294 thread->data.mark((JSTracer *)arg);
295 return JS_DHASH_NEXT;
296 }
297
298 #endif /* JS_THREADSAFE */
299
300 JSThreadData *
301 js_CurrentThreadData(JSRuntime *rt)
302 {
303 #ifdef JS_THREADSAFE
304 JSThread *thread = js_CurrentThread(rt);
305 if (!thread)
306 return NULL;
307
308 return &thread->data;
309 #else
310 return &rt->threadData;
311 #endif
312 }
313
314 JSBool
315 js_InitThreads(JSRuntime *rt)
316 {
317 #ifdef JS_THREADSAFE
318 if (!JS_DHashTableInit(&rt->threads, &threads_ops, NULL,
319 sizeof(JSThreadsHashEntry), 4)) {
320 rt->threads.ops = NULL;
321 return false;
322 }
323 #else
324 InitThreadData(&rt->threadData);
325 #endif
326 return true;
327 }
328
329 void
330 js_FinishThreads(JSRuntime *rt)
331 {
332 #ifdef JS_THREADSAFE
333 if (!rt->threads.ops)
334 return;
335 JS_DHashTableEnumerate(&rt->threads, thread_destroyer, NULL);
336 JS_DHashTableFinish(&rt->threads);
337 rt->threads.ops = NULL;
338 #else
339 FinishThreadData(&rt->threadData);
340 #endif
341 }
342
343 void
344 js_PurgeThreads(JSContext *cx)
345 {
346 #ifdef JS_THREADSAFE
347 JS_DHashTableEnumerate(&cx->runtime->threads, thread_purger, cx);
348 #else
349 PurgeThreadData(cx, &cx->runtime->threadData);
350 #endif
351 }
352
353 void
354 js_TraceThreads(JSRuntime *rt, JSTracer *trc)
355 {
356 #ifdef JS_THREADSAFE
357 JS_DHashTableEnumerate(&rt->threads, thread_tracer, trc);
358 #else
359 rt->threadData.mark(trc);
360 #endif
361 }
362
363 /*
364 * JSOPTION_XML and JSOPTION_ANONFUNFIX must be part of the JS version
365 * associated with scripts, so in addition to storing them in cx->options we
366 * duplicate them in cx->version (script->version, etc.) and ensure each bit
367 * remains synchronized between the two through these two functions.
368 */
369 void
370 js_SyncOptionsToVersion(JSContext* cx)
371 {
372 if (cx->options & JSOPTION_XML)
373 cx->version |= JSVERSION_HAS_XML;
374 else
375 cx->version &= ~JSVERSION_HAS_XML;
376 if (cx->options & JSOPTION_ANONFUNFIX)
377 cx->version |= JSVERSION_ANONFUNFIX;
378 else
379 cx->version &= ~JSVERSION_ANONFUNFIX;
380 }
381
382 inline void
383 js_SyncVersionToOptions(JSContext* cx)
384 {
385 if (cx->version & JSVERSION_HAS_XML)
386 cx->options |= JSOPTION_XML;
387 else
388 cx->options &= ~JSOPTION_XML;
389 if (cx->version & JSVERSION_ANONFUNFIX)
390 cx->options |= JSOPTION_ANONFUNFIX;
391 else
392 cx->options &= ~JSOPTION_ANONFUNFIX;
393 }
394
395 void
396 js_OnVersionChange(JSContext *cx)
397 {
398 #ifdef DEBUG
399 JSVersion version = JSVERSION_NUMBER(cx);
400
401 JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
402 #endif
403 }
404
405 void
406 js_SetVersion(JSContext *cx, JSVersion version)
407 {
408 cx->version = version;
409 js_SyncVersionToOptions(cx);
410 js_OnVersionChange(cx);
411 }
412
413 JSContext *
414 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
415 {
416 JSContext *cx;
417 JSBool ok, first;
418 JSContextCallback cxCallback;
419
420 /*
421 * We need to initialize the new context fully before adding it to the
422 * runtime list. After that it can be accessed from another thread via
423 * js_ContextIterator.
424 */
425 void *mem = js_calloc(sizeof *cx);
426 if (!mem)
427 return NULL;
428
429 cx = new (mem) JSContext(rt);
430 cx->debugHooks = &rt->globalDebugHooks;
431 #if JS_STACK_GROWTH_DIRECTION > 0
432 cx->stackLimit = (jsuword) -1;
433 #endif
434 cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
435 JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
436 JS_ASSERT(cx->version == JSVERSION_DEFAULT);
437 VOUCH_DOES_NOT_REQUIRE_STACK();
438 JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
439 &cx->scriptStackQuota);
440
441 JS_INIT_ARENA_POOL(&cx->tempPool, "temp",
442 1024, /* FIXME: bug 421435 */
443 sizeof(jsdouble), &cx->scriptStackQuota);
444
445 js_InitRegExpStatics(cx);
446 JS_ASSERT(cx->resolveFlags == 0);
447
448 if (!js_InitContextBusyArrayTable(cx)) {
449 FreeContext(cx);
450 return NULL;
451 }
452
453 #ifdef JS_THREADSAFE
454 if (!js_InitContextThread(cx)) {
455 FreeContext(cx);
456 return NULL;
457 }
458 #endif
459
460 /*
461 * Here the GC lock is still held after js_InitContextThread took it and
462 * the GC is not running on another thread.
463 */
464 for (;;) {
465 if (rt->state == JSRTS_UP) {
466 JS_ASSERT(!JS_CLIST_IS_EMPTY(&rt->contextList));
467 first = JS_FALSE;
468 break;
469 }
470 if (rt->state == JSRTS_DOWN) {
471 JS_ASSERT(JS_CLIST_IS_EMPTY(&rt->contextList));
472 first = JS_TRUE;
473 rt->state = JSRTS_LAUNCHING;
474 break;
475 }
476 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
477
478 /*
479 * During the above wait after we are notified about the state change
480 * but before we wake up, another thread could enter the GC from
481 * js_DestroyContext, bug 478336. So we must wait here to ensure that
482 * when we exit the loop with the first flag set to true, that GC is
483 * finished.
484 */
485 js_WaitForGC(rt);
486 }
487 JS_APPEND_LINK(&cx->link, &rt->contextList);
488 JS_UNLOCK_GC(rt);
489
490 /*
491 * If cx is the first context on this runtime, initialize well-known atoms,
492 * keywords, numbers, and strings. If one of these steps should fail, the
493 * runtime will be left in a partially initialized state, with zeroes and
494 * nulls stored in the default-initialized remainder of the struct. We'll
495 * clean the runtime up under js_DestroyContext, because cx will be "last"
496 * as well as "first".
497 */
498 if (first) {
499 #ifdef JS_THREADSAFE
500 JS_BeginRequest(cx);
501 #endif
502 ok = js_InitCommonAtoms(cx);
503
504 /*
505 * scriptFilenameTable may be left over from a previous episode of
506 * non-zero contexts alive in rt, so don't re-init the table if it's
507 * not necessary.
508 */
509 if (ok && !rt->scriptFilenameTable)
510 ok = js_InitRuntimeScriptState(rt);
511 if (ok)
512 ok = js_InitRuntimeNumberState(cx);
513 if (ok)
514 ok = js_InitRuntimeStringState(cx);
515 #ifdef JS_THREADSAFE
516 JS_EndRequest(cx);
517 #endif
518 if (!ok) {
519 js_DestroyContext(cx, JSDCM_NEW_FAILED);
520 return NULL;
521 }
522
523 JS_LOCK_GC(rt);
524 rt->state = JSRTS_UP;
525 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
526 JS_UNLOCK_GC(rt);
527 }
528
529 cxCallback = rt->cxCallback;
530 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
531 js_DestroyContext(cx, JSDCM_NEW_FAILED);
532 return NULL;
533 }
534
535 return cx;
536 }
537
538 #if defined DEBUG && defined XP_UNIX
539 # include <stdio.h>
540
541 class JSAutoFile {
542 public:
543 JSAutoFile() : mFile(NULL) {}
544
545 ~JSAutoFile() {
546 if (mFile)
547 fclose(mFile);
548 }
549
550 FILE *open(const char *fname, const char *mode) {
551 return mFile = fopen(fname, mode);
552 }
553 operator FILE *() {
554 return mFile;
555 }
556
557 private:
558 FILE *mFile;
559 };
560
561 #ifdef JS_EVAL_CACHE_METERING
562 static void
563 DumpEvalCacheMeter(JSContext *cx)
564 {
565 struct {
566 const char *name;
567 ptrdiff_t offset;
568 } table[] = {
569 #define frob(x) { #x, offsetof(JSEvalCacheMeter, x) }
570 EVAL_CACHE_METER_LIST(frob)
571 #undef frob
572 };
573 JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
574
575 static JSAutoFile fp;
576 if (!fp) {
577 fp.open("/tmp/evalcache.stats", "w");
578 if (!fp)
579 return;
580 }
581
582 fprintf(fp, "eval cache meter (%p):\n",
583 #ifdef JS_THREADSAFE
584 (void *) cx->thread
585 #else
586 (void *) cx->runtime
587 #endif
588 );
589 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
590 fprintf(fp, "%-8.8s %llu\n",
591 table[i].name,
592 (unsigned long long int) *(uint64 *)((uint8 *)ecm + table[i].offset));
593 }
594 fprintf(fp, "hit ratio %g%%\n", ecm->hit * 100. / ecm->probe);
595 fprintf(fp, "avg steps %g\n", double(ecm->step) / ecm->probe);
596 fflush(fp);
597 }
598 # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
599 #endif
600
601 #ifdef JS_FUNCTION_METERING
602 static void
603 DumpFunctionMeter(JSContext *cx)
604 {
605 struct {
606 const char *name;
607 ptrdiff_t offset;
608 } table[] = {
609 #define frob(x) { #x, offsetof(JSFunctionMeter, x) }
610 FUNCTION_KIND_METER_LIST(frob)
611 #undef frob
612 };
613 JSFunctionMeter *fm = &cx->runtime->functionMeter;
614
615 static JSAutoFile fp;
616 if (!fp) {
617 fp.open("/tmp/function.stats", "a");
618 if (!fp)
619 return;
620 }
621
622 fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
623 for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
624 fprintf(fp, "%-11.11s %d\n",
625 table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
626 }
627 fflush(fp);
628 }
629 # define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
630 #endif
631
632 #endif /* DEBUG && XP_UNIX */
633
634 #ifndef DUMP_EVAL_CACHE_METER
635 # define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
636 #endif
637
638 #ifndef DUMP_FUNCTION_METER
639 # define DUMP_FUNCTION_METER(cx) ((void) 0)
640 #endif
641
642 void
643 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
644 {
645 JSRuntime *rt;
646 JSContextCallback cxCallback;
647 JSBool last;
648
649 rt = cx->runtime;
650 #ifdef JS_THREADSAFE
651 /*
652 * For API compatibility we allow to destroy contexts without a thread in
653 * optimized builds. We assume that the embedding knows that an OOM error
654 * cannot happen in JS_SetContextThread.
655 */
656 JS_ASSERT(cx->thread && CURRENT_THREAD_IS_ME(cx->thread));
657 if (!cx->thread)
658 JS_SetContextThread(cx);
659
660 JS_ASSERT_IF(rt->gcRunning, cx->outstandingRequests == 0);
661 #endif
662
663 if (mode != JSDCM_NEW_FAILED) {
664 cxCallback = rt->cxCallback;
665 if (cxCallback) {
666 /*
667 * JSCONTEXT_DESTROY callback is not allowed to fail and must
668 * return true.
669 */
670 #ifdef DEBUG
671 JSBool callbackStatus =
672 #endif
673 cxCallback(cx, JSCONTEXT_DESTROY);
674 JS_ASSERT(callbackStatus);
675 }
676 }
677
678 JS_LOCK_GC(rt);
679 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
680 #ifdef JS_THREADSAFE
681 /*
682 * Typically we are called outside a request, so ensure that the GC is not
683 * running before removing the context from rt->contextList, see bug 477021.
684 */
685 if (cx->requestDepth == 0)
686 js_WaitForGC(rt);
687 #endif
688 JS_REMOVE_LINK(&cx->link);
689 last = (rt->contextList.next == &rt->contextList);
690 if (last)
691 rt->state = JSRTS_LANDING;
692 if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC
693 #ifdef JS_THREADSAFE
694 || cx->requestDepth != 0
695 #endif
696 ) {
697 JS_ASSERT(!rt->gcRunning);
698
699 JS_UNLOCK_GC(rt);
700
701 if (last) {
702 #ifdef JS_THREADSAFE
703 /*
704 * If cx is not in a request already, begin one now so that we wait
705 * for any racing GC started on a not-last context to finish, before
706 * we plow ahead and unpin atoms. Note that even though we begin a
707 * request here if necessary, we end all requests on cx below before
708 * forcing a final GC. This lets any not-last context destruction
709 * racing in another thread try to force or maybe run the GC, but by
710 * that point, rt->state will not be JSRTS_UP, and that GC attempt
711 * will return early.
712 */
713 if (cx->requestDepth == 0)
714 JS_BeginRequest(cx);
715 #endif
716
717 /* Unlock and clear GC things held by runtime pointers. */
718 js_FinishRuntimeNumberState(cx);
719 js_FinishRuntimeStringState(cx);
720
721 /* Unpin all common atoms before final GC. */
722 js_FinishCommonAtoms(cx);
723
724 /* Clear debugging state to remove GC roots. */
725 JS_ClearAllTraps(cx);
726 JS_ClearAllWatchPoints(cx);
727 }
728
729 /* Remove more GC roots in regExpStatics, then collect garbage. */
730 JS_ClearRegExpRoots(cx);
731
732 #ifdef JS_THREADSAFE
733 /*
734 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
735 * end our request here in case we are "last" -- in that event, another
736 * js_DestroyContext that was not last might be waiting in the GC for our
737 * request to end. We'll let it run below, just before we do the truly
738 * final GC and then free atom state.
739 */
740 while (cx->requestDepth != 0)
741 JS_EndRequest(cx);
742 #endif
743
744 if (last) {
745 js_GC(cx, GC_LAST_CONTEXT);
746 DUMP_EVAL_CACHE_METER(cx);
747 DUMP_FUNCTION_METER(cx);
748
749 /* Take the runtime down, now that it has no contexts or atoms. */
750 JS_LOCK_GC(rt);
751 rt->state = JSRTS_DOWN;
752 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
753 } else {
754 if (mode == JSDCM_FORCE_GC)
755 js_GC(cx, GC_NORMAL);
756 else if (mode == JSDCM_MAYBE_GC)
757 JS_MaybeGC(cx);
758 JS_LOCK_GC(rt);
759 js_WaitForGC(rt);
760 }
761 }
762 #ifdef JS_THREADSAFE
763 js_ClearContextThread(cx);
764 #endif
765 JS_UNLOCK_GC(rt);
766 FreeContext(cx);
767 }
768
769 static void
770 FreeContext(JSContext *cx)
771 {
772 JSArgumentFormatMap *map;
773 JSLocalRootStack *lrs;
774 JSLocalRootChunk *lrc;
775
776 #ifdef JS_THREADSAFE
777 JS_ASSERT(!cx->thread);
778 #endif
779
780 /* Free the stuff hanging off of cx. */
781 js_FreeRegExpStatics(cx);
782 VOUCH_DOES_NOT_REQUIRE_STACK();
783 JS_FinishArenaPool(&cx->stackPool);
784 JS_FinishArenaPool(&cx->tempPool);
785
786 if (cx->lastMessage)
787 js_free(cx->lastMessage);
788
789 /* Remove any argument formatters. */
790 map = cx->argumentFormatMap;
791 while (map) {
792 JSArgumentFormatMap *temp = map;
793 map = map->next;
794 cx->free(temp);
795 }
796
797 /* Destroy the busy array table. */
798 if (cx->busyArrayTable) {
799 JS_HashTableDestroy(cx->busyArrayTable);
800 cx->busyArrayTable = NULL;
801 }
802
803 /* Destroy the resolve recursion damper. */
804 if (cx->resolvingTable) {
805 JS_DHashTableDestroy(cx->resolvingTable);
806 cx->resolvingTable = NULL;
807 }
808
809 lrs = cx->localRootStack;
810 if (lrs) {
811 while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
812 lrs->topChunk = lrc->down;
813 cx->free(lrc);
814 }
815 cx->free(lrs);
816 }
817
818 /* Finally, free cx itself. */
819 js_free(cx);
820 }
821
822 JSBool
823 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
824 {
825 JSCList *cl;
826
827 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
828 if (cl == &cx->link)
829 return JS_TRUE;
830 }
831 JS_RUNTIME_METER(rt, deadContexts);
832 return JS_FALSE;
833 }
834
835 JSContext *
836 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
837 {
838 JSContext *cx = *iterp;
839
840 if (unlocked)
841 JS_LOCK_GC(rt);
842 cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next);
843 if (&cx->link == &rt->contextList)
844 cx = NULL;
845 *iterp = cx;
846 if (unlocked)
847 JS_UNLOCK_GC(rt);
848 return cx;
849 }
850
851 JS_FRIEND_API(JSContext *)
852 js_NextActiveContext(JSRuntime *rt, JSContext *cx)
853 {
854 JSContext *iter = cx;
855 #ifdef JS_THREADSAFE
856 while ((cx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {
857 if (cx->requestDepth)
858 break;
859 }
860 return cx;
861 #else
862 return js_ContextIterator(rt, JS_FALSE, &iter);
863 #endif
864 }
865
866 #ifdef JS_THREADSAFE
867
868 uint32
869 js_CountThreadRequests(JSContext *cx)
870 {
871 JSCList *head, *link;
872 uint32 nrequests;
873
874 JS_ASSERT(CURRENT_THREAD_IS_ME(cx->thread));
875 head = &cx->thread->contextList;
876 nrequests = 0;
877 for (link = head->next; link != head; link = link->next) {
878 JSContext *acx = CX_FROM_THREAD_LINKS(link);
879 JS_ASSERT(acx->thread == cx->thread);
880 if (acx->requestDepth)
881 nrequests++;
882 }
883 return nrequests;
884 }
885
886 /*
887 * If the GC is running and we're called on another thread, wait for this GC
888 * activation to finish. We can safely wait here without fear of deadlock (in
889 * the case where we are called within a request on another thread's context)
890 * because the GC doesn't set rt->gcRunning until after it has waited for all
891 * active requests to end.
892 *
893 * We call here js_CurrentThreadId() after checking for rt->gcRunning to avoid
894 * expensive calls when the GC is not running.
895 */
896 void
897 js_WaitForGC(JSRuntime *rt)
898 {
899 JS_ASSERT_IF(rt->gcRunning, rt->gcLevel > 0);
900 if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) {
901 do {
902 JS_AWAIT_GC_DONE(rt);
903 } while (rt->gcRunning);
904 }
905 }
906
907 uint32
908 js_DiscountRequestsForGC(JSContext *cx)
909 {
910 uint32 requestDebit;
911
912 JS_ASSERT(cx->thread);
913 JS_ASSERT(cx->runtime->gcThread != cx->thread);
914
915 #ifdef JS_TRACER
916 if (JS_ON_TRACE(cx)) {
917 JS_UNLOCK_GC(cx->runtime);
918 js_LeaveTrace(cx);
919 JS_LOCK_GC(cx->runtime);
920 }
921 #endif
922
923 requestDebit = js_CountThreadRequests(cx);
924 if (requestDebit != 0) {
925 JSRuntime *rt = cx->runtime;
926 JS_ASSERT(requestDebit <= rt->requestCount);
927 rt->requestCount -= requestDebit;
928 if (rt->requestCount == 0)
929 JS_NOTIFY_REQUEST_DONE(rt);
930 }
931 return requestDebit;
932 }
933
934 void
935 js_RecountRequestsAfterGC(JSRuntime *rt, uint32 requestDebit)
936 {
937 while (rt->gcLevel > 0) {
938 JS_ASSERT(rt->gcThread);
939 JS_AWAIT_GC_DONE(rt);
940 }
941 if (requestDebit != 0)
942 rt->requestCount += requestDebit;
943 }
944
945 #endif
946
947 static JSDHashNumber
948 resolving_HashKey(JSDHashTable *table, const void *ptr)
949 {
950 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
951
952 return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id;
953 }
954
955 JS_PUBLIC_API(JSBool)
956 resolving_MatchEntry(JSDHashTable *table,
957 const JSDHashEntryHdr *hdr,
958 const void *ptr)
959 {
960 const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
961 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
962
963 return entry->key.obj == key->obj && entry->key.id == key->id;
964 }
965
966 static const JSDHashTableOps resolving_dhash_ops = {
967 JS_DHashAllocTable,
968 JS_DHashFreeTable,
969 resolving_HashKey,
970 resolving_MatchEntry,
971 JS_DHashMoveEntryStub,
972 JS_DHashClearEntryStub,
973 JS_DHashFinalizeStub,
974 NULL
975 };
976
977 JSBool
978 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
979 JSResolvingEntry **entryp)
980 {
981 JSDHashTable *table;
982 JSResolvingEntry *entry;
983
984 table = cx->resolvingTable;
985 if (!table) {
986 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
987 sizeof(JSResolvingEntry),
988 JS_DHASH_MIN_SIZE);
989 if (!table)
990 goto outofmem;
991 cx->resolvingTable = table;
992 }
993
994 entry = (JSResolvingEntry *)
995 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
996 if (!entry)
997 goto outofmem;
998
999 if (entry->flags & flag) {
1000 /* An entry for (key, flag) exists already -- dampen recursion. */
1001 entry = NULL;
1002 } else {
1003 /* Fill in key if we were the first to add entry, then set flag. */
1004 if (!entry->key.obj)
1005 entry->key = *key;
1006 entry->flags |= flag;
1007 }
1008 *entryp = entry;
1009 return JS_TRUE;
1010
1011 outofmem:
1012 JS_ReportOutOfMemory(cx);
1013 return JS_FALSE;
1014 }
1015
1016 void
1017 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
1018 JSResolvingEntry *entry, uint32 generation)
1019 {
1020 JSDHashTable *table;
1021
1022 /*
1023 * Clear flag from entry->flags and return early if other flags remain.
1024 * We must take care to re-lookup entry if the table has changed since
1025 * it was found by js_StartResolving.
1026 */
1027 table = cx->resolvingTable;
1028 if (!entry || table->generation != generation) {
1029 entry = (JSResolvingEntry *)
1030 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
1031 }
1032 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
1033 entry->flags &= ~flag;
1034 if (entry->flags)
1035 return;
1036
1037 /*
1038 * Do a raw remove only if fewer entries were removed than would cause
1039 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
1040 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
1041 * compressing or shrinking the table as needed.
1042 */
1043 if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
1044 JS_DHashTableRawRemove(table, &entry->hdr);
1045 else
1046 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
1047 }
1048
1049 JSBool
1050 js_EnterLocalRootScope(JSContext *cx)
1051 {
1052 JSLocalRootStack *lrs;
1053 int mark;
1054
1055 lrs = cx->localRootStack;
1056 if (!lrs) {
1057 lrs = (JSLocalRootStack *) cx->malloc(sizeof *lrs);
1058 if (!lrs)
1059 return JS_FALSE;
1060 lrs->scopeMark = JSLRS_NULL_MARK;
1061 lrs->rootCount = 0;
1062 lrs->topChunk = &lrs->firstChunk;
1063 lrs->firstChunk.down = NULL;
1064 cx->localRootStack = lrs;
1065 }
1066
1067 /* Push lrs->scopeMark to save it for restore when leaving. */
1068 mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
1069 if (mark < 0)
1070 return JS_FALSE;
1071 lrs->scopeMark = (uint32) mark;
1072 return JS_TRUE;
1073 }
1074
1075 void
1076 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
1077 {
1078 JSLocalRootStack *lrs;
1079 uint32 mark, m, n;
1080 JSLocalRootChunk *lrc;
1081
1082 /* Defend against buggy native callers. */
1083 lrs = cx->localRootStack;
1084 JS_ASSERT(lrs && lrs->rootCount != 0);
1085 if (!lrs || lrs->rootCount == 0)
1086 return;
1087
1088 mark = lrs->scopeMark;
1089 JS_ASSERT(mark != JSLRS_NULL_MARK);
1090 if (mark == JSLRS_NULL_MARK)
1091 return;
1092
1093 /* Free any chunks being popped by this leave operation. */
1094 m = mark >> JSLRS_CHUNK_SHIFT;
1095 n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
1096 while (n > m) {
1097 lrc = lrs->topChunk;
1098 JS_ASSERT(lrc != &lrs->firstChunk);
1099 lrs->topChunk = lrc->down;
1100 cx->free(lrc);
1101 --n;
1102 }
1103
1104 /*
1105 * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
1106 * it on the caller's scope, or store it in lastInternalResult if we are
1107 * leaving the outermost scope. We don't need to allocate a new lrc
1108 * because we can overwrite the old mark's slot with rval.
1109 */
1110 lrc = lrs->topChunk;
1111 m = mark & JSLRS_CHUNK_MASK;
1112 lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
1113 if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
1114 if (mark == 0) {
1115 cx->weakRoots.lastInternalResult = rval;
1116 } else {
1117 /*
1118 * Increment m to avoid the "else if (m == 0)" case below. If
1119 * rval is not a GC-thing, that case would take care of freeing
1120 * any chunk that contained only the old mark. Since rval *is*
1121 * a GC-thing here, we want to reuse that old mark's slot.
1122 */
1123 lrc->roots[m++] = rval;
1124 ++mark;
1125 }
1126 }
1127 lrs->rootCount = (uint32) mark;
1128
1129 /*
1130 * Free the stack eagerly, risking malloc churn. The alternative would
1131 * require an lrs->entryCount member, maintained by Enter and Leave, and
1132 * tested by the GC in addition to the cx->localRootStack non-null test.
1133 *
1134 * That approach would risk hoarding 264 bytes (net) per context. Right
1135 * now it seems better to give fresh (dirty in CPU write-back cache, and
1136 * the data is no longer needed) memory back to the malloc heap.
1137 */
1138 if (mark == 0) {
1139 cx->localRootStack = NULL;
1140 cx->free(lrs);
1141 } else if (m == 0) {
1142 lrs->topChunk = lrc->down;
1143 cx->free(lrc);
1144 }
1145 }
1146
1147 void
1148 js_ForgetLocalRoot(JSContext *cx, jsval v)
1149 {
1150 JSLocalRootStack *lrs;
1151 uint32 i, j, m, n, mark;
1152 JSLocalRootChunk *lrc, *lrc2;
1153 jsval top;
1154
1155 lrs = cx->localRootStack;
1156 JS_ASSERT(lrs && lrs->rootCount);
1157 if (!lrs || lrs->rootCount == 0)
1158 return;
1159
1160 /* Prepare to pop the top-most value from the stack. */
1161 n = lrs->rootCount - 1;
1162 m = n & JSLRS_CHUNK_MASK;
1163 lrc = lrs->topChunk;
1164 top = lrc->roots[m];
1165
1166 /* Be paranoid about calls on an empty scope. */
1167 mark = lrs->scopeMark;
1168 JS_ASSERT(mark < n);
1169 if (mark >= n)
1170 return;
1171
1172 /* If v was not the last root pushed in the top scope, find it. */
1173 if (top != v) {
1174 /* Search downward in case v was recently pushed. */
1175 i = n;
1176 j = m;
1177 lrc2 = lrc;
1178 while (--i > mark) {
1179 if (j == 0)
1180 lrc2 = lrc2->down;
1181 j = i & JSLRS_CHUNK_MASK;
1182 if (lrc2->roots[j] == v)
1183 break;
1184 }
1185
1186 /* If we didn't find v in this scope, assert and bail out. */
1187 JS_ASSERT(i != mark);
1188 if (i == mark)
1189 return;
1190
1191 /* Swap top and v so common tail code can pop v. */
1192 lrc2->roots[j] = top;
1193 }
1194
1195 /* Pop the last value from the stack. */
1196 lrc->roots[m] = JSVAL_NULL;
1197 lrs->rootCount = n;
1198 if (m == 0) {
1199 JS_ASSERT(n != 0);
1200 JS_ASSERT(lrc != &lrs->firstChunk);
1201 lrs->topChunk = lrc->down;
1202 cx->free(lrc);
1203 }
1204 }
1205
1206 int
1207 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
1208 {
1209 uint32 n, m;
1210 JSLocalRootChunk *lrc;
1211
1212 n = lrs->rootCount;
1213 m = n & JSLRS_CHUNK_MASK;
1214 if (n == 0 || m != 0) {
1215 /*
1216 * At start of first chunk, or not at start of a non-first top chunk.
1217 * Check for lrs->rootCount overflow.
1218 */
1219 if ((uint32)(n + 1) == 0) {
1220 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1221 JSMSG_TOO_MANY_LOCAL_ROOTS);
1222 return -1;
1223 }
1224 lrc = lrs->topChunk;
1225 JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
1226 } else {
1227 /*
1228 * After lrs->firstChunk, trying to index at a power-of-two chunk
1229 * boundary: need a new chunk.
1230 */
1231 lrc = (JSLocalRootChunk *) cx->malloc(sizeof *lrc);
1232 if (!lrc)
1233 return -1;
1234 lrc->down = lrs->topChunk;
1235 lrs->topChunk = lrc;
1236 }
1237 lrs->rootCount = n + 1;
1238 lrc->roots[m] = v;
1239 return (int) n;
1240 }
1241
1242 void
1243 js_TraceLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
1244 {
1245 uint32 n, m, mark;
1246 JSLocalRootChunk *lrc;
1247 jsval v;
1248
1249 n = lrs->rootCount;
1250 if (n == 0)
1251 return;
1252
1253 mark = lrs->scopeMark;
1254 lrc = lrs->topChunk;
1255 do {
1256 while (--n > mark) {
1257 m = n & JSLRS_CHUNK_MASK;
1258 v = lrc->roots[m];
1259 JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
1260 JS_SET_TRACING_INDEX(trc, "local_root", n);
1261 js_CallValueTracerIfGCThing(trc, v);
1262 if (m == 0)
1263 lrc = lrc->down;
1264 }
1265 m = n & JSLRS_CHUNK_MASK;
1266 mark = JSVAL_TO_INT(lrc->roots[m]);
1267 if (m == 0)
1268 lrc = lrc->down;
1269 } while (n != 0);
1270 JS_ASSERT(!lrc);
1271 }
1272
1273 static void
1274 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
1275 {
1276 /*
1277 * Check the error report, and set a JavaScript-catchable exception
1278 * if the error is defined to have an associated exception. If an
1279 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
1280 * on the error report, and exception-aware hosts should ignore it.
1281 */
1282 JS_ASSERT(reportp);
1283 if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
1284 reportp->flags |= JSREPORT_EXCEPTION;
1285
1286 /*
1287 * Call the error reporter only if an exception wasn't raised.
1288 *
1289 * If an exception was raised, then we call the debugErrorHook
1290 * (if present) to give it a chance to see the error before it
1291 * propagates out of scope. This is needed for compatability
1292 * with the old scheme.
1293 */
1294 if (!JS_IsRunning(cx) || !js_ErrorToException(cx, message, reportp)) {
1295 js_ReportErrorAgain(cx, message, reportp);
1296 } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
1297 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1298 /* test local in case debugErrorHook changed on another thread */
1299 if (hook)
1300 hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
1301 }
1302 }
1303
1304 /* The report must be initially zeroed. */
1305 static void
1306 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
1307 {
1308 JSStackFrame *fp;
1309
1310 /*
1311 * Walk stack until we find a frame that is associated with some script
1312 * rather than a native frame.
1313 */
1314 for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
1315 if (fp->regs) {
1316 report->filename = fp->script->filename;
1317 report->lineno = js_FramePCToLineNumber(cx, fp);
1318 break;
1319 }
1320 }
1321 }
1322
1323 /*
1324 * We don't post an exception in this case, since doing so runs into
1325 * complications of pre-allocating an exception object which required
1326 * running the Exception class initializer early etc.
1327 * Instead we just invoke the errorReporter with an "Out Of Memory"
1328 * type message, and then hope the process ends swiftly.
1329 */
1330 void
1331 js_ReportOutOfMemory(JSContext *cx)
1332 {
1333 #ifdef JS_TRACER
1334 /*
1335 * If we are in a builtin called directly from trace, don't report an
1336 * error. We will retry in the interpreter instead.
1337 */
1338 if (JS_ON_TRACE(cx) && !cx->bailExit)
1339 return;
1340 #endif
1341
1342 JSErrorReport report;
1343 JSErrorReporter onError = cx->errorReporter;
1344
1345 /* Get the message for this error, but we won't expand any arguments. */
1346 const JSErrorFormatString *efs =
1347 js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
1348 const char *msg = efs ? efs->format : "Out of memory";
1349
1350 /* Fill out the report, but don't do anything that requires allocation. */
1351 memset(&report, 0, sizeof (struct JSErrorReport));
1352 report.flags = JSREPORT_ERROR;
1353 report.errorNumber = JSMSG_OUT_OF_MEMORY;
1354 PopulateReportBlame(cx, &report);
1355
1356 /*
1357 * If debugErrorHook is present then we give it a chance to veto sending
1358 * the error on to the regular ErrorReporter. We also clear a pending
1359 * exception if any now so the hooks can replace the out-of-memory error
1360 * by a script-catchable exception.
1361 */
1362 cx->throwing = JS_FALSE;
1363 if (onError) {
1364 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1365 if (hook &&
1366 !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
1367 onError = NULL;
1368 }
1369 }
1370
1371 if (onError)
1372 onError(cx, msg, &report);
1373 }
1374
1375 void
1376 js_ReportOutOfScriptQuota(JSContext *cx)
1377 {
1378 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1379 JSMSG_SCRIPT_STACK_QUOTA);
1380 }
1381
1382 void
1383 js_ReportOverRecursed(JSContext *cx)
1384 {
1385 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1386 }
1387
1388 void
1389 js_ReportAllocationOverflow(JSContext *cx)
1390 {
1391 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
1392 }
1393
1394 JSBool
1395 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
1396 {
1397 char *message;
1398 jschar *ucmessage;
1399 size_t messagelen;
1400 JSErrorReport report;
1401 JSBool warning;
1402
1403 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1404 return JS_TRUE;
1405
1406 message = JS_vsmprintf(format, ap);
1407 if (!message)
1408 return JS_FALSE;
1409 messagelen = strlen(message);
1410
1411 memset(&report, 0, sizeof (struct JSErrorReport));
1412 report.flags = flags;
1413 report.errorNumber = JSMSG_USER_DEFINED_ERROR;
1414 report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
1415 PopulateReportBlame(cx, &report);
1416
1417 warning = JSREPORT_IS_WARNING(report.flags);
1418 if (warning && JS_HAS_WERROR_OPTION(cx)) {
1419 report.flags &= ~JSREPORT_WARNING;
1420 warning = JS_FALSE;
1421 }
1422
1423 ReportError(cx, message, &report);
1424 js_free(message);
1425 cx->free(ucmessage);
1426 return warning;
1427 }
1428
1429 /*
1430 * The arguments from ap need to be packaged up into an array and stored
1431 * into the report struct.
1432 *
1433 * The format string addressed by the error number may contain operands
1434 * identified by the format {N}, where N is a decimal digit. Each of these
1435 * is to be replaced by the Nth argument from the va_list. The complete
1436 * message is placed into reportp->ucmessage converted to a JSString.
1437 *
1438 * Returns true if the expansion succeeds (can fail if out of memory).
1439 */
1440 JSBool
1441 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
1442 void *userRef, const uintN errorNumber,
1443 char **messagep, JSErrorReport *reportp,
1444 JSBool *warningp, JSBool charArgs, va_list ap)
1445 {
1446 const JSErrorFormatString *efs;
1447 int i;
1448 int argCount;
1449
1450 *warningp = JSREPORT_IS_WARNING(reportp->flags);
1451 if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
1452 reportp->flags &= ~JSREPORT_WARNING;
1453 *warningp = JS_FALSE;
1454 }
1455
1456 *messagep = NULL;
1457
1458 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1459 if (!callback || callback == js_GetErrorMessage)
1460 efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
1461 else
1462 efs = callback(userRef, NULL, errorNumber);
1463 if (efs) {
1464 size_t totalArgsLength = 0;
1465 size_t argLengths[10]; /* only {0} thru {9} supported */
1466 argCount = efs->argCount;
1467 JS_ASSERT(argCount <= 10);
1468 if (argCount > 0) {
1469 /*
1470 * Gather the arguments into an array, and accumulate
1471 * their sizes. We allocate 1 more than necessary and
1472 * null it out to act as the caboose when we free the
1473 * pointers later.
1474 */
1475 reportp->messageArgs = (const jschar **)
1476 cx->malloc(sizeof(jschar *) * (argCount + 1));
1477 if (!reportp->messageArgs)
1478 return JS_FALSE;
1479 reportp->messageArgs[argCount] = NULL;
1480 for (i = 0; i < argCount; i++) {
1481 if (charArgs) {
1482 char *charArg = va_arg(ap, char *);
1483 size_t charArgLength = strlen(charArg);
1484 reportp->messageArgs[i]
1485 = js_InflateString(cx, charArg, &charArgLength);
1486 if (!reportp->messageArgs[i])
1487 goto error;
1488 } else {
1489 reportp->messageArgs[i] = va_arg(ap, jschar *);
1490 }
1491 argLengths[i] = js_strlen(reportp->messageArgs[i]);
1492 totalArgsLength += argLengths[i];
1493 }
1494 /* NULL-terminate for easy copying. */
1495 reportp->messageArgs[i] = NULL;
1496 }
1497 /*
1498 * Parse the error format, substituting the argument X
1499 * for {X} in the format.
1500 */
1501 if (argCount > 0) {
1502 if (efs->format) {
1503 jschar *buffer, *fmt, *out;
1504 int expandedArgs = 0;
1505 size_t expandedLength;
1506 size_t len = strlen(efs->format);
1507
1508 buffer = fmt = js_InflateString (cx, efs->format, &len);
1509 if (!buffer)
1510 goto error;
1511 expandedLength = len
1512 - (3 * argCount) /* exclude the {n} */
1513 + totalArgsLength;
1514
1515 /*
1516 * Note - the above calculation assumes that each argument
1517 * is used once and only once in the expansion !!!
1518 */
1519 reportp->ucmessage = out = (jschar *)
1520 cx->malloc((expandedLength + 1) * sizeof(jschar));
1521 if (!out) {
1522 cx->free(buffer);
1523 goto error;
1524 }
1525 while (*fmt) {
1526 if (*fmt == '{') {
1527 if (isdigit(fmt[1])) {
1528 int d = JS7_UNDEC(fmt[1]);
1529 JS_ASSERT(d < argCount);
1530 js_strncpy(out, reportp->messageArgs[d],
1531 argLengths[d]);
1532 out += argLengths[d];
1533 fmt += 3;
1534 expandedArgs++;
1535 continue;
1536 }
1537 }
1538 *out++ = *fmt++;
1539 }
1540 JS_ASSERT(expandedArgs == argCount);
1541 *out = 0;
1542 cx->free(buffer);
1543 *messagep =
1544 js_DeflateString(cx, reportp->ucmessage,
1545 (size_t)(out - reportp->ucmessage));
1546 if (!*messagep)
1547 goto error;
1548 }
1549 } else {
1550 /*
1551 * Zero arguments: the format string (if it exists) is the
1552 * entire message.
1553 */
1554 if (efs->format) {
1555 size_t len;
1556 *messagep = JS_strdup(cx, efs->format);
1557 if (!*messagep)
1558 goto error;
1559 len = strlen(*messagep);
1560 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1561 if (!reportp->ucmessage)
1562 goto error;
1563 }
1564 }
1565 }
1566 if (*messagep == NULL) {
1567 /* where's the right place for this ??? */
1568 const char *defaultErrorMessage
1569 = "No error message available for error number %d";
1570 size_t nbytes = strlen(defaultErrorMessage) + 16;
1571 *messagep = (char *)cx->malloc(nbytes);
1572 if (!*messagep)
1573 goto error;
1574 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1575 }
1576 return JS_TRUE;
1577
1578 error:
1579 if (reportp->messageArgs) {
1580 /* free the arguments only if we allocated them */
1581 if (charArgs) {
1582 i = 0;
1583 while (reportp->messageArgs[i])
1584 cx->free((void *)reportp->messageArgs[i++]);
1585 }
1586 cx->free((void *)reportp->messageArgs);
1587 reportp->messageArgs = NULL;
1588 }
1589 if (reportp->ucmessage) {
1590 cx->free((void *)reportp->ucmessage);
1591 reportp->ucmessage = NULL;
1592 }
1593 if (*messagep) {
1594 cx->free((void *)*messagep);
1595 *messagep = NULL;
1596 }
1597 return JS_FALSE;
1598 }
1599
1600 JSBool
1601 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1602 void *userRef, const uintN errorNumber,
1603 JSBool charArgs, va_list ap)
1604 {
1605 JSErrorReport report;
1606 char *message;
1607 JSBool warning;
1608
1609 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1610 return JS_TRUE;
1611
1612 memset(&report, 0, sizeof (struct JSErrorReport));
1613 report.flags = flags;
1614 report.errorNumber = errorNumber;
1615 PopulateReportBlame(cx, &report);
1616
1617 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1618 &message, &report, &warning, charArgs, ap)) {
1619 return JS_FALSE;
1620 }
1621
1622 ReportError(cx, message, &report);
1623
1624 if (message)
1625 cx->free(message);
1626 if (report.messageArgs) {
1627 /*
1628 * js_ExpandErrorArguments owns its messageArgs only if it had to
1629 * inflate the arguments (from regular |char *|s).
1630 */
1631 if (charArgs) {
1632 int i = 0;
1633 while (report.messageArgs[i])
1634 cx->free((void *)report.messageArgs[i++]);
1635 }
1636 cx->free((void *)report.messageArgs);
1637 }
1638 if (report.ucmessage)
1639 cx->free((void *)report.ucmessage);
1640
1641 return warning;
1642 }
1643
1644 JS_FRIEND_API(void)
1645 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1646 {
1647 JSErrorReporter onError;
1648
1649 if (!message)
1650 return;
1651
1652 if (cx->lastMessage)
1653 js_free(cx->lastMessage);
1654 cx->lastMessage = JS_strdup(cx, message);
1655 if (!cx->lastMessage)
1656 return;
1657 onError = cx->errorReporter;
1658
1659 /*
1660 * If debugErrorHook is present then we give it a chance to veto
1661 * sending the error on to the regular ErrorReporter.
1662 */
1663 if (onError) {
1664 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1665 if (hook &&
1666 !hook(cx, cx->lastMessage, reportp,
1667 cx->debugHooks->debugErrorHookData)) {
1668 onError = NULL;
1669 }
1670 }
1671 if (onError)
1672 onError(cx, cx->lastMessage, reportp);
1673 }
1674
1675 void
1676 js_ReportIsNotDefined(JSContext *cx, const char *name)
1677 {
1678 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1679 }
1680
1681 JSBool
1682 js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
1683 JSString *fallback)
1684 {
1685 char *bytes;
1686 JSBool ok;
1687
1688 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1689 if (!bytes)
1690 return JS_FALSE;
1691
1692 if (strcmp(bytes, js_undefined_str) == 0 ||
1693 strcmp(bytes, js_null_str) == 0) {
1694 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1695 js_GetErrorMessage, NULL,
1696 JSMSG_NO_PROPERTIES, bytes,
1697 NULL, NULL);
1698 } else if (JSVAL_IS_VOID(v)) {
1699 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1700 js_GetErrorMessage, NULL,
1701 JSMSG_NULL_OR_UNDEFINED, bytes,
1702 js_undefined_str, NULL);
1703 } else {
1704 JS_ASSERT(JSVAL_IS_NULL(v));
1705 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1706 js_GetErrorMessage, NULL,
1707 JSMSG_NULL_OR_UNDEFINED, bytes,
1708 js_null_str, NULL);
1709 }
1710
1711 cx->free(bytes);
1712 return ok;
1713 }
1714
1715 void
1716 js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
1717 {
1718 char argbuf[11];
1719 char *bytes;
1720 JSAtom *atom;
1721
1722 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1723 bytes = NULL;
1724 if (VALUE_IS_FUNCTION(cx, *vp)) {
1725 atom = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp))->atom;
1726 bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp,
1727 ATOM_TO_STRING(atom));
1728 if (!bytes)
1729 return;
1730 }
1731 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1732 JSMSG_MISSING_FUN_ARG, argbuf,
1733 bytes ? bytes : "");
1734 cx->free(bytes);
1735 }
1736
1737 JSBool
1738 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1739 intN spindex, jsval v, JSString *fallback,
1740 const char *arg1, const char *arg2)
1741 {
1742 char *bytes;
1743 JSBool ok;
1744
1745 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1746 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1747 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1748 if (!bytes)
1749 return JS_FALSE;
1750
1751 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1752 NULL, errorNumber, bytes, arg1, arg2);
1753 cx->free(bytes);
1754 return ok;
1755 }
1756
1757 #if defined DEBUG && defined XP_UNIX
1758 /* For gdb usage. */
1759 void js_traceon(JSContext *cx) { cx->tracefp = stderr; cx->tracePrevPc = NULL; }
1760 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1761 #endif
1762
1763 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1764 #define MSG_DEF(name, number, count, exception, format) \
1765 { format, count, exception } ,
1766 #include "js.msg"
1767 #undef MSG_DEF
1768 };
1769
1770 JS_FRIEND_API(const JSErrorFormatString *)
1771 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1772 {
1773 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1774 return &js_ErrorFormatString[errorNumber];
1775 return NULL;
1776 }
1777
1778 JSBool
1779 js_InvokeOperationCallback(JSContext *cx)
1780 {
1781 JS_ASSERT(cx->operationCallbackFlag);
1782
1783 /*
1784 * Reset the callback flag first, then yield. If another thread is racing
1785 * us here we will accumulate another callback request which will be
1786 * serviced at the next opportunity.
1787 */
1788 cx->operationCallbackFlag = 0;
1789
1790 /*
1791 * Unless we are going to run the GC, we automatically yield the current
1792 * context every time the operation callback is hit since we might be
1793 * called as a result of an impending GC, which would deadlock if we do
1794 * not yield. Operation callbacks are supposed to happen rarely (seconds,
1795 * not milliseconds) so it is acceptable to yield at every callback.
1796 */
1797 if (cx->runtime->gcIsNeeded)
1798 js_GC(cx, GC_NORMAL);
1799 #ifdef JS_THREADSAFE
1800 else
1801 JS_YieldRequest(cx);
1802 #endif
1803
1804 JSOperationCallback cb = cx->operationCallback;
1805
1806 /*
1807 * Important: Additional callbacks can occur inside the callback handler
1808 * if it re-enters the JS engine. The embedding must ensure that the
1809 * callback is disconnected before attempting such re-entry.
1810 */
1811
1812 return !cb || cb(cx);
1813 }
1814
1815 void
1816 js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
1817 {
1818 JSContext *acx, *iter;
1819 #ifdef JS_THREADSAFE
1820 if (!gcLocked)
1821 JS_LOCK_GC(rt);
1822 #endif
1823 iter = NULL;
1824 while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
1825 JS_TriggerOperationCallback(acx);
1826 #ifdef JS_THREADSAFE
1827 if (!gcLocked)
1828 JS_UNLOCK_GC(rt);
1829 #endif
1830 }
1831
1832 JSStackFrame *
1833 js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1834 {
1835 if (!fp)
1836 fp = js_GetTopStackFrame(cx);
1837 while (fp) {
1838 if (fp->script)
1839 return fp;
1840 fp = fp->down;
1841 }
1842 return NULL;
1843 }
1844
1845 jsbytecode*
1846 js_GetCurrentBytecodePC(JSContext* cx)
1847 {
1848 jsbytecode *pc, *imacpc;
1849
1850 #ifdef JS_TRACER
1851 if (JS_ON_TRACE(cx)) {
1852 pc = cx->bailExit->pc;
1853 imacpc = cx->bailExit->imacpc;
1854 } else
1855 #endif
1856 {
1857 JS_ASSERT_NOT_ON_TRACE(cx); /* for static analysis */
1858 JSStackFrame* fp = cx->fp;
1859 if (fp && fp->regs) {
1860 pc = fp->regs->pc;
1861 imacpc = fp->imacpc;
1862 } else {
1863 return NULL;
1864 }
1865 }
1866
1867 /*
1868 * If we are inside GetProperty_tn or similar, return a pointer to the
1869 * current instruction in the script, not the CALL instruction in the
1870 * imacro, for the benefit of callers doing bytecode inspection.
1871 */
1872 return (*pc == JSOP_CALL && imacpc) ? imacpc : pc;
1873 }
1874
1875 bool
1876 js_CurrentPCIsInImacro(JSContext *cx)
1877 {
1878 #ifdef JS_TRACER
1879 VOUCH_DOES_NOT_REQUIRE_STACK();
1880 return (JS_ON_TRACE(cx) ? cx->bailExit->imacpc : cx->fp->imacpc) != NULL;
1881 #else
1882 return false;
1883 #endif
1884 }

  ViewVC Help
Powered by ViewVC 1.1.24