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

Contents of /trunk/js/jscntxt.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (12 years, 7 months ago) by siliconforks
File size: 53234 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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

  ViewVC Help
Powered by ViewVC 1.1.24