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

Contents of /trunk/js/jscntxt.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (show annotations)
Sun Sep 12 15:13:23 2010 UTC (9 years ago) by siliconforks
File size: 54486 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

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

  ViewVC Help
Powered by ViewVC 1.1.24