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

Contents of /trunk/js/jscntxt.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 43030 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


1 /* -*- Mode: C; tab-width: 8; 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 "jsscan.h"
65 #include "jsscope.h"
66 #include "jsscript.h"
67 #include "jsstr.h"
68 #include "jstracer.h"
69
70 #ifdef JS_THREADSAFE
71 #include "prtypes.h"
72
73 /*
74 * The index for JSThread info, returned by PR_NewThreadPrivateIndex. The
75 * index value is visible and shared by all threads, but the data associated
76 * with it is private to each thread.
77 */
78 static PRUintn threadTPIndex;
79 static JSBool tpIndexInited = JS_FALSE;
80
81 JS_BEGIN_EXTERN_C
82 JSBool
83 js_InitThreadPrivateIndex(void (*ptr)(void *))
84 {
85 PRStatus status;
86
87 if (tpIndexInited)
88 return JS_TRUE;
89
90 status = PR_NewThreadPrivateIndex(&threadTPIndex, ptr);
91
92 if (status == PR_SUCCESS)
93 tpIndexInited = JS_TRUE;
94 return status == PR_SUCCESS;
95 }
96 JS_END_EXTERN_C
97
98 /*
99 * Callback function to delete a JSThread info when the thread that owns it
100 * is destroyed.
101 */
102 void
103 js_ThreadDestructorCB(void *ptr)
104 {
105 JSThread *thread = (JSThread *)ptr;
106
107 if (!thread)
108 return;
109
110 /*
111 * Check that this thread properly called either JS_DestroyContext or
112 * JS_ClearContextThread on each JSContext it created or used.
113 */
114 JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
115 GSN_CACHE_CLEAR(&thread->gsnCache);
116 #if defined JS_TRACER
117 js_FinishJIT(&thread->traceMonitor);
118 #endif
119 free(thread);
120 }
121
122 /*
123 * Get current thread-local JSThread info, creating one if it doesn't exist.
124 * Each thread has a unique JSThread pointer.
125 *
126 * Since we are dealing with thread-local data, no lock is needed.
127 *
128 * Return a pointer to the thread local info, NULL if the system runs out
129 * of memory, or it failed to set thread private data (neither case is very
130 * likely; both are probably due to out-of-memory). It is up to the caller
131 * to report an error, if possible.
132 */
133 JSThread *
134 js_GetCurrentThread(JSRuntime *rt)
135 {
136 JSThread *thread;
137
138 thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex);
139 if (!thread) {
140 thread = (JSThread *) malloc(sizeof(JSThread));
141 if (!thread)
142 return NULL;
143 #ifdef DEBUG
144 memset(thread, JS_FREE_PATTERN, sizeof(JSThread));
145 #endif
146 if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) {
147 free(thread);
148 return NULL;
149 }
150
151 JS_INIT_CLIST(&thread->contextList);
152 thread->id = js_CurrentThreadId();
153 thread->gcMallocBytes = 0;
154 #ifdef JS_TRACER
155 memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor));
156 js_InitJIT(&thread->traceMonitor);
157 #endif
158 thread->scriptsToGC = NULL;
159
160 /*
161 * js_SetContextThread initializes the remaining fields as necessary.
162 */
163 }
164 return thread;
165 }
166
167 /*
168 * Sets current thread as owning thread of a context by assigning the
169 * thread-private info to the context. If the current thread doesn't have
170 * private JSThread info, create one.
171 */
172 JSBool
173 js_SetContextThread(JSContext *cx)
174 {
175 JSThread *thread = js_GetCurrentThread(cx->runtime);
176
177 if (!thread) {
178 JS_ReportOutOfMemory(cx);
179 return JS_FALSE;
180 }
181
182 /*
183 * Clear caches on each transition from 0 to 1 context active on the
184 * current thread. See bug 425828.
185 */
186 if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
187 memset(&thread->gsnCache, 0, sizeof(thread->gsnCache));
188 memset(&thread->propertyCache, 0, sizeof(thread->propertyCache));
189 }
190
191 /* Assert that the previous cx->thread called JS_ClearContextThread(). */
192 JS_ASSERT(!cx->thread || cx->thread == thread);
193 if (!cx->thread)
194 JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
195 cx->thread = thread;
196 return JS_TRUE;
197 }
198
199 /* Remove the owning thread info of a context. */
200 void
201 js_ClearContextThread(JSContext *cx)
202 {
203 /*
204 * If cx is associated with a thread, this must be called only from that
205 * thread. If not, this is a harmless no-op.
206 */
207 JS_ASSERT(cx->thread == js_GetCurrentThread(cx->runtime) || !cx->thread);
208 JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
209 cx->thread = NULL;
210 }
211
212 #endif /* JS_THREADSAFE */
213
214 void
215 js_OnVersionChange(JSContext *cx)
216 {
217 #ifdef DEBUG
218 JSVersion version = JSVERSION_NUMBER(cx);
219
220 JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
221 #endif
222 }
223
224 void
225 js_SetVersion(JSContext *cx, JSVersion version)
226 {
227 cx->version = version;
228 js_OnVersionChange(cx);
229 }
230
231 JSContext *
232 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
233 {
234 JSContext *cx;
235 JSBool ok, first;
236 JSContextCallback cxCallback;
237
238 cx = (JSContext *) malloc(sizeof *cx);
239 if (!cx)
240 return NULL;
241 memset(cx, 0, sizeof *cx);
242
243 cx->runtime = rt;
244 JS_ClearOperationCallback(cx);
245 cx->debugHooks = &rt->globalDebugHooks;
246 #if JS_STACK_GROWTH_DIRECTION > 0
247 cx->stackLimit = (jsuword)-1;
248 #endif
249 cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
250 #ifdef JS_THREADSAFE
251 cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
252 JS_INIT_CLIST(&cx->threadLinks);
253 js_SetContextThread(cx);
254 #endif
255
256 JS_LOCK_GC(rt);
257 for (;;) {
258 first = (rt->contextList.next == &rt->contextList);
259 if (rt->state == JSRTS_UP) {
260 JS_ASSERT(!first);
261 break;
262 }
263 if (rt->state == JSRTS_DOWN) {
264 JS_ASSERT(first);
265 rt->state = JSRTS_LAUNCHING;
266 break;
267 }
268 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
269 }
270 JS_APPEND_LINK(&cx->links, &rt->contextList);
271 JS_UNLOCK_GC(rt);
272
273 /*
274 * First we do the infallible, every-time per-context initializations.
275 * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
276 * or the stuff under 'if (first)' below) fail, at least the version
277 * and arena-pools will be valid and safe to use (say, from the last GC
278 * done by js_DestroyContext).
279 */
280 cx->version = JSVERSION_DEFAULT;
281 JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
282 &cx->scriptStackQuota);
283
284 JS_INIT_ARENA_POOL(&cx->tempPool, "temp",
285 1024, /* FIXME: bug 421435 */
286 sizeof(jsdouble), &cx->scriptStackQuota);
287
288 /*
289 * To avoid multiple allocations in InitMatch() (in jsregexp.c), the arena
290 * size parameter should be at least as big as:
291 * INITIAL_BACKTRACK
292 * + (sizeof(REProgState) * INITIAL_STATESTACK)
293 * + (offsetof(REMatchState, parens) + avgParanSize * sizeof(RECapture))
294 */
295 JS_INIT_ARENA_POOL(&cx->regexpPool, "regexp",
296 12 * 1024 - 40, /* FIXME: bug 421435 */
297 sizeof(void *), &cx->scriptStackQuota);
298
299 if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
300 js_DestroyContext(cx, JSDCM_NEW_FAILED);
301 return NULL;
302 }
303
304 cx->resolveFlags = 0;
305
306 /*
307 * If cx is the first context on this runtime, initialize well-known atoms,
308 * keywords, numbers, and strings. If one of these steps should fail, the
309 * runtime will be left in a partially initialized state, with zeroes and
310 * nulls stored in the default-initialized remainder of the struct. We'll
311 * clean the runtime up under js_DestroyContext, because cx will be "last"
312 * as well as "first".
313 */
314 if (first) {
315 #ifdef JS_THREADSAFE
316 JS_BeginRequest(cx);
317 #endif
318 ok = js_InitCommonAtoms(cx);
319
320 /*
321 * scriptFilenameTable may be left over from a previous episode of
322 * non-zero contexts alive in rt, so don't re-init the table if it's
323 * not necessary.
324 */
325 if (ok && !rt->scriptFilenameTable)
326 ok = js_InitRuntimeScriptState(rt);
327 if (ok)
328 ok = js_InitRuntimeNumberState(cx);
329 if (ok)
330 ok = js_InitRuntimeStringState(cx);
331 #ifdef JS_THREADSAFE
332 JS_EndRequest(cx);
333 #endif
334 if (!ok) {
335 js_DestroyContext(cx, JSDCM_NEW_FAILED);
336 return NULL;
337 }
338
339 JS_LOCK_GC(rt);
340 rt->state = JSRTS_UP;
341 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
342 JS_UNLOCK_GC(rt);
343 }
344
345 cxCallback = rt->cxCallback;
346 if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
347 js_DestroyContext(cx, JSDCM_NEW_FAILED);
348 return NULL;
349 }
350
351 return cx;
352 }
353
354 void
355 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
356 {
357 JSRuntime *rt;
358 JSContextCallback cxCallback;
359 JSBool last;
360 JSArgumentFormatMap *map;
361 JSLocalRootStack *lrs;
362 JSLocalRootChunk *lrc;
363
364 rt = cx->runtime;
365
366 if (mode != JSDCM_NEW_FAILED) {
367 cxCallback = rt->cxCallback;
368 if (cxCallback) {
369 /*
370 * JSCONTEXT_DESTROY callback is not allowed to fail and must
371 * return true.
372 */
373 #ifdef DEBUG
374 JSBool callbackStatus =
375 #endif
376 cxCallback(cx, JSCONTEXT_DESTROY);
377 JS_ASSERT(callbackStatus);
378 }
379 }
380
381 /* Remove cx from context list first. */
382 JS_LOCK_GC(rt);
383 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
384 JS_REMOVE_LINK(&cx->links);
385 last = (rt->contextList.next == &rt->contextList);
386 if (last)
387 rt->state = JSRTS_LANDING;
388 #ifdef JS_THREADSAFE
389 js_RevokeGCLocalFreeLists(cx);
390 #endif
391 JS_UNLOCK_GC(rt);
392
393 if (last) {
394 #ifdef JS_THREADSAFE
395 /*
396 * If cx is not in a request already, begin one now so that we wait
397 * for any racing GC started on a not-last context to finish, before
398 * we plow ahead and unpin atoms. Note that even though we begin a
399 * request here if necessary, we end all requests on cx below before
400 * forcing a final GC. This lets any not-last context destruction
401 * racing in another thread try to force or maybe run the GC, but by
402 * that point, rt->state will not be JSRTS_UP, and that GC attempt
403 * will return early.
404 */
405 if (cx->requestDepth == 0)
406 JS_BeginRequest(cx);
407 #endif
408
409 /* Unlock and clear GC things held by runtime pointers. */
410 js_FinishRuntimeNumberState(cx);
411 js_FinishRuntimeStringState(cx);
412
413 /* Unpin all common atoms before final GC. */
414 js_FinishCommonAtoms(cx);
415
416 /* Clear debugging state to remove GC roots. */
417 JS_ClearAllTraps(cx);
418 JS_ClearAllWatchPoints(cx);
419 }
420
421 /*
422 * Remove more GC roots in regExpStatics, then collect garbage.
423 * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
424 * XXX this function call to wait for any racing GC to complete, in the
425 * XXX case where JS_DestroyContext is called outside of a request on cx
426 */
427 js_FreeRegExpStatics(cx, &cx->regExpStatics);
428
429 #ifdef JS_THREADSAFE
430 /*
431 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
432 * end our request here in case we are "last" -- in that event, another
433 * js_DestroyContext that was not last might be waiting in the GC for our
434 * request to end. We'll let it run below, just before we do the truly
435 * final GC and then free atom state.
436 *
437 * At this point, cx must be inaccessible to other threads. It's off the
438 * rt->contextList, and it should not be reachable via any object private
439 * data structure.
440 */
441 while (cx->requestDepth != 0)
442 JS_EndRequest(cx);
443 #endif
444
445 if (last) {
446 js_GC(cx, GC_LAST_CONTEXT);
447
448 /*
449 * Free the script filename table if it exists and is empty. Do this
450 * after the last GC to avoid finalizers tripping on free memory.
451 */
452 if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
453 js_FinishRuntimeScriptState(rt);
454
455 /* Take the runtime down, now that it has no contexts or atoms. */
456 JS_LOCK_GC(rt);
457 rt->state = JSRTS_DOWN;
458 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
459 JS_UNLOCK_GC(rt);
460 } else {
461 if (mode == JSDCM_FORCE_GC)
462 js_GC(cx, GC_NORMAL);
463 else if (mode == JSDCM_MAYBE_GC)
464 JS_MaybeGC(cx);
465 }
466
467 /* Free the stuff hanging off of cx. */
468 JS_FinishArenaPool(&cx->stackPool);
469 JS_FinishArenaPool(&cx->tempPool);
470 JS_FinishArenaPool(&cx->regexpPool);
471
472 if (cx->lastMessage)
473 free(cx->lastMessage);
474
475 /* Remove any argument formatters. */
476 map = cx->argumentFormatMap;
477 while (map) {
478 JSArgumentFormatMap *temp = map;
479 map = map->next;
480 JS_free(cx, temp);
481 }
482
483 /* Destroy the resolve recursion damper. */
484 if (cx->resolvingTable) {
485 JS_DHashTableDestroy(cx->resolvingTable);
486 cx->resolvingTable = NULL;
487 }
488
489 lrs = cx->localRootStack;
490 if (lrs) {
491 while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
492 lrs->topChunk = lrc->down;
493 JS_free(cx, lrc);
494 }
495 JS_free(cx, lrs);
496 }
497
498 #ifdef JS_THREADSAFE
499 js_ClearContextThread(cx);
500 #endif
501
502 /* Finally, free cx itself. */
503 free(cx);
504 }
505
506 JSBool
507 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
508 {
509 JSCList *cl;
510
511 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
512 if (cl == &cx->links)
513 return JS_TRUE;
514 }
515 JS_RUNTIME_METER(rt, deadContexts);
516 return JS_FALSE;
517 }
518
519 JSContext *
520 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
521 {
522 JSContext *cx = *iterp;
523
524 if (unlocked)
525 JS_LOCK_GC(rt);
526 cx = (JSContext *) (cx ? cx->links.next : rt->contextList.next);
527 if (&cx->links == &rt->contextList)
528 cx = NULL;
529 *iterp = cx;
530 if (unlocked)
531 JS_UNLOCK_GC(rt);
532 return cx;
533 }
534
535 static JSDHashNumber
536 resolving_HashKey(JSDHashTable *table, const void *ptr)
537 {
538 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
539
540 return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id;
541 }
542
543 JS_PUBLIC_API(JSBool)
544 resolving_MatchEntry(JSDHashTable *table,
545 const JSDHashEntryHdr *hdr,
546 const void *ptr)
547 {
548 const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
549 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
550
551 return entry->key.obj == key->obj && entry->key.id == key->id;
552 }
553
554 static const JSDHashTableOps resolving_dhash_ops = {
555 JS_DHashAllocTable,
556 JS_DHashFreeTable,
557 resolving_HashKey,
558 resolving_MatchEntry,
559 JS_DHashMoveEntryStub,
560 JS_DHashClearEntryStub,
561 JS_DHashFinalizeStub,
562 NULL
563 };
564
565 JSBool
566 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
567 JSResolvingEntry **entryp)
568 {
569 JSDHashTable *table;
570 JSResolvingEntry *entry;
571
572 table = cx->resolvingTable;
573 if (!table) {
574 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
575 sizeof(JSResolvingEntry),
576 JS_DHASH_MIN_SIZE);
577 if (!table)
578 goto outofmem;
579 cx->resolvingTable = table;
580 }
581
582 entry = (JSResolvingEntry *)
583 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
584 if (!entry)
585 goto outofmem;
586
587 if (entry->flags & flag) {
588 /* An entry for (key, flag) exists already -- dampen recursion. */
589 entry = NULL;
590 } else {
591 /* Fill in key if we were the first to add entry, then set flag. */
592 if (!entry->key.obj)
593 entry->key = *key;
594 entry->flags |= flag;
595 }
596 *entryp = entry;
597 return JS_TRUE;
598
599 outofmem:
600 JS_ReportOutOfMemory(cx);
601 return JS_FALSE;
602 }
603
604 void
605 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
606 JSResolvingEntry *entry, uint32 generation)
607 {
608 JSDHashTable *table;
609
610 /*
611 * Clear flag from entry->flags and return early if other flags remain.
612 * We must take care to re-lookup entry if the table has changed since
613 * it was found by js_StartResolving.
614 */
615 table = cx->resolvingTable;
616 if (!entry || table->generation != generation) {
617 entry = (JSResolvingEntry *)
618 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
619 }
620 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
621 entry->flags &= ~flag;
622 if (entry->flags)
623 return;
624
625 /*
626 * Do a raw remove only if fewer entries were removed than would cause
627 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
628 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
629 * compressing or shrinking the table as needed.
630 */
631 if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
632 JS_DHashTableRawRemove(table, &entry->hdr);
633 else
634 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
635 }
636
637 JSBool
638 js_EnterLocalRootScope(JSContext *cx)
639 {
640 JSLocalRootStack *lrs;
641 int mark;
642
643 lrs = cx->localRootStack;
644 if (!lrs) {
645 lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
646 if (!lrs)
647 return JS_FALSE;
648 lrs->scopeMark = JSLRS_NULL_MARK;
649 lrs->rootCount = 0;
650 lrs->topChunk = &lrs->firstChunk;
651 lrs->firstChunk.down = NULL;
652 cx->localRootStack = lrs;
653 }
654
655 /* Push lrs->scopeMark to save it for restore when leaving. */
656 mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
657 if (mark < 0)
658 return JS_FALSE;
659 lrs->scopeMark = (uint32) mark;
660 return JS_TRUE;
661 }
662
663 void
664 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
665 {
666 JSLocalRootStack *lrs;
667 uint32 mark, m, n;
668 JSLocalRootChunk *lrc;
669
670 /* Defend against buggy native callers. */
671 lrs = cx->localRootStack;
672 JS_ASSERT(lrs && lrs->rootCount != 0);
673 if (!lrs || lrs->rootCount == 0)
674 return;
675
676 mark = lrs->scopeMark;
677 JS_ASSERT(mark != JSLRS_NULL_MARK);
678 if (mark == JSLRS_NULL_MARK)
679 return;
680
681 /* Free any chunks being popped by this leave operation. */
682 m = mark >> JSLRS_CHUNK_SHIFT;
683 n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
684 while (n > m) {
685 lrc = lrs->topChunk;
686 JS_ASSERT(lrc != &lrs->firstChunk);
687 lrs->topChunk = lrc->down;
688 JS_free(cx, lrc);
689 --n;
690 }
691
692 /*
693 * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
694 * it on the caller's scope, or store it in lastInternalResult if we are
695 * leaving the outermost scope. We don't need to allocate a new lrc
696 * because we can overwrite the old mark's slot with rval.
697 */
698 lrc = lrs->topChunk;
699 m = mark & JSLRS_CHUNK_MASK;
700 lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
701 if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
702 if (mark == 0) {
703 cx->weakRoots.lastInternalResult = rval;
704 } else {
705 /*
706 * Increment m to avoid the "else if (m == 0)" case below. If
707 * rval is not a GC-thing, that case would take care of freeing
708 * any chunk that contained only the old mark. Since rval *is*
709 * a GC-thing here, we want to reuse that old mark's slot.
710 */
711 lrc->roots[m++] = rval;
712 ++mark;
713 }
714 }
715 lrs->rootCount = (uint32) mark;
716
717 /*
718 * Free the stack eagerly, risking malloc churn. The alternative would
719 * require an lrs->entryCount member, maintained by Enter and Leave, and
720 * tested by the GC in addition to the cx->localRootStack non-null test.
721 *
722 * That approach would risk hoarding 264 bytes (net) per context. Right
723 * now it seems better to give fresh (dirty in CPU write-back cache, and
724 * the data is no longer needed) memory back to the malloc heap.
725 */
726 if (mark == 0) {
727 cx->localRootStack = NULL;
728 JS_free(cx, lrs);
729 } else if (m == 0) {
730 lrs->topChunk = lrc->down;
731 JS_free(cx, lrc);
732 }
733 }
734
735 void
736 js_ForgetLocalRoot(JSContext *cx, jsval v)
737 {
738 JSLocalRootStack *lrs;
739 uint32 i, j, m, n, mark;
740 JSLocalRootChunk *lrc, *lrc2;
741 jsval top;
742
743 lrs = cx->localRootStack;
744 JS_ASSERT(lrs && lrs->rootCount);
745 if (!lrs || lrs->rootCount == 0)
746 return;
747
748 /* Prepare to pop the top-most value from the stack. */
749 n = lrs->rootCount - 1;
750 m = n & JSLRS_CHUNK_MASK;
751 lrc = lrs->topChunk;
752 top = lrc->roots[m];
753
754 /* Be paranoid about calls on an empty scope. */
755 mark = lrs->scopeMark;
756 JS_ASSERT(mark < n);
757 if (mark >= n)
758 return;
759
760 /* If v was not the last root pushed in the top scope, find it. */
761 if (top != v) {
762 /* Search downward in case v was recently pushed. */
763 i = n;
764 j = m;
765 lrc2 = lrc;
766 while (--i > mark) {
767 if (j == 0)
768 lrc2 = lrc2->down;
769 j = i & JSLRS_CHUNK_MASK;
770 if (lrc2->roots[j] == v)
771 break;
772 }
773
774 /* If we didn't find v in this scope, assert and bail out. */
775 JS_ASSERT(i != mark);
776 if (i == mark)
777 return;
778
779 /* Swap top and v so common tail code can pop v. */
780 lrc2->roots[j] = top;
781 }
782
783 /* Pop the last value from the stack. */
784 lrc->roots[m] = JSVAL_NULL;
785 lrs->rootCount = n;
786 if (m == 0) {
787 JS_ASSERT(n != 0);
788 JS_ASSERT(lrc != &lrs->firstChunk);
789 lrs->topChunk = lrc->down;
790 JS_free(cx, lrc);
791 }
792 }
793
794 int
795 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
796 {
797 uint32 n, m;
798 JSLocalRootChunk *lrc;
799
800 n = lrs->rootCount;
801 m = n & JSLRS_CHUNK_MASK;
802 if (n == 0 || m != 0) {
803 /*
804 * At start of first chunk, or not at start of a non-first top chunk.
805 * Check for lrs->rootCount overflow.
806 */
807 if ((uint32)(n + 1) == 0) {
808 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
809 JSMSG_TOO_MANY_LOCAL_ROOTS);
810 return -1;
811 }
812 lrc = lrs->topChunk;
813 JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
814 } else {
815 /*
816 * After lrs->firstChunk, trying to index at a power-of-two chunk
817 * boundary: need a new chunk.
818 */
819 lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc);
820 if (!lrc)
821 return -1;
822 lrc->down = lrs->topChunk;
823 lrs->topChunk = lrc;
824 }
825 lrs->rootCount = n + 1;
826 lrc->roots[m] = v;
827 return (int) n;
828 }
829
830 void
831 js_TraceLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
832 {
833 uint32 n, m, mark;
834 JSLocalRootChunk *lrc;
835 jsval v;
836
837 n = lrs->rootCount;
838 if (n == 0)
839 return;
840
841 mark = lrs->scopeMark;
842 lrc = lrs->topChunk;
843 do {
844 while (--n > mark) {
845 m = n & JSLRS_CHUNK_MASK;
846 v = lrc->roots[m];
847 JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
848 JS_SET_TRACING_INDEX(trc, "local_root", n);
849 js_CallValueTracerIfGCThing(trc, v);
850 if (m == 0)
851 lrc = lrc->down;
852 }
853 m = n & JSLRS_CHUNK_MASK;
854 mark = JSVAL_TO_INT(lrc->roots[m]);
855 if (m == 0)
856 lrc = lrc->down;
857 } while (n != 0);
858 JS_ASSERT(!lrc);
859 }
860
861 static void
862 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
863 {
864 /*
865 * Check the error report, and set a JavaScript-catchable exception
866 * if the error is defined to have an associated exception. If an
867 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
868 * on the error report, and exception-aware hosts should ignore it.
869 */
870 JS_ASSERT(reportp);
871 if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
872 reportp->flags |= JSREPORT_EXCEPTION;
873
874 /*
875 * Call the error reporter only if an exception wasn't raised.
876 *
877 * If an exception was raised, then we call the debugErrorHook
878 * (if present) to give it a chance to see the error before it
879 * propagates out of scope. This is needed for compatability
880 * with the old scheme.
881 */
882 if (!cx->fp || !js_ErrorToException(cx, message, reportp)) {
883 js_ReportErrorAgain(cx, message, reportp);
884 } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
885 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
886 /* test local in case debugErrorHook changed on another thread */
887 if (hook)
888 hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
889 }
890 }
891
892 /*
893 * We don't post an exception in this case, since doing so runs into
894 * complications of pre-allocating an exception object which required
895 * running the Exception class initializer early etc.
896 * Instead we just invoke the errorReporter with an "Out Of Memory"
897 * type message, and then hope the process ends swiftly.
898 */
899 void
900 js_ReportOutOfMemory(JSContext *cx)
901 {
902 JSStackFrame *fp;
903 JSErrorReport report;
904 JSErrorReporter onError = cx->errorReporter;
905
906 /* Get the message for this error, but we won't expand any arguments. */
907 const JSErrorFormatString *efs =
908 js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
909 const char *msg = efs ? efs->format : "Out of memory";
910
911 /* Fill out the report, but don't do anything that requires allocation. */
912 memset(&report, 0, sizeof (struct JSErrorReport));
913 report.flags = JSREPORT_ERROR;
914 report.errorNumber = JSMSG_OUT_OF_MEMORY;
915
916 /*
917 * Walk stack until we find a frame that is associated with some script
918 * rather than a native frame.
919 */
920 for (fp = cx->fp; fp; fp = fp->down) {
921 if (fp->regs) {
922 report.filename = fp->script->filename;
923 report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
924 break;
925 }
926 }
927
928 /*
929 * If debugErrorHook is present then we give it a chance to veto sending
930 * the error on to the regular ErrorReporter. We also clear a pending
931 * exception if any now so the hooks can replace the out-of-memory error
932 * by a script-catchable exception.
933 */
934 cx->throwing = JS_FALSE;
935 if (onError) {
936 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
937 if (hook &&
938 !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
939 onError = NULL;
940 }
941 }
942
943 if (onError)
944 onError(cx, msg, &report);
945 }
946
947 void
948 js_ReportOutOfScriptQuota(JSContext *cx)
949 {
950 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
951 JSMSG_SCRIPT_STACK_QUOTA);
952 }
953
954 void
955 js_ReportOverRecursed(JSContext *cx)
956 {
957 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
958 }
959
960 void
961 js_ReportAllocationOverflow(JSContext *cx)
962 {
963 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
964 }
965
966 JSBool
967 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
968 {
969 char *message;
970 jschar *ucmessage;
971 size_t messagelen;
972 JSStackFrame *fp;
973 JSErrorReport report;
974 JSBool warning;
975
976 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
977 return JS_TRUE;
978
979 message = JS_vsmprintf(format, ap);
980 if (!message)
981 return JS_FALSE;
982 messagelen = strlen(message);
983
984 memset(&report, 0, sizeof (struct JSErrorReport));
985 report.flags = flags;
986 report.errorNumber = JSMSG_USER_DEFINED_ERROR;
987 report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
988
989 /* Find the top-most active script frame, for best line number blame. */
990 for (fp = cx->fp; fp; fp = fp->down) {
991 if (fp->regs) {
992 report.filename = fp->script->filename;
993 report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
994 break;
995 }
996 }
997
998 warning = JSREPORT_IS_WARNING(report.flags);
999 if (warning && JS_HAS_WERROR_OPTION(cx)) {
1000 report.flags &= ~JSREPORT_WARNING;
1001 warning = JS_FALSE;
1002 }
1003
1004 ReportError(cx, message, &report);
1005 free(message);
1006 JS_free(cx, ucmessage);
1007 return warning;
1008 }
1009
1010 /*
1011 * The arguments from ap need to be packaged up into an array and stored
1012 * into the report struct.
1013 *
1014 * The format string addressed by the error number may contain operands
1015 * identified by the format {N}, where N is a decimal digit. Each of these
1016 * is to be replaced by the Nth argument from the va_list. The complete
1017 * message is placed into reportp->ucmessage converted to a JSString.
1018 *
1019 * Returns true if the expansion succeeds (can fail if out of memory).
1020 */
1021 JSBool
1022 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
1023 void *userRef, const uintN errorNumber,
1024 char **messagep, JSErrorReport *reportp,
1025 JSBool *warningp, JSBool charArgs, va_list ap)
1026 {
1027 const JSErrorFormatString *efs;
1028 int i;
1029 int argCount;
1030
1031 *warningp = JSREPORT_IS_WARNING(reportp->flags);
1032 if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
1033 reportp->flags &= ~JSREPORT_WARNING;
1034 *warningp = JS_FALSE;
1035 }
1036
1037 *messagep = NULL;
1038
1039 /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1040 if (!callback || callback == js_GetErrorMessage)
1041 efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
1042 else
1043 efs = callback(userRef, NULL, errorNumber);
1044 if (efs) {
1045 size_t totalArgsLength = 0;
1046 size_t argLengths[10]; /* only {0} thru {9} supported */
1047 argCount = efs->argCount;
1048 JS_ASSERT(argCount <= 10);
1049 if (argCount > 0) {
1050 /*
1051 * Gather the arguments into an array, and accumulate
1052 * their sizes. We allocate 1 more than necessary and
1053 * null it out to act as the caboose when we free the
1054 * pointers later.
1055 */
1056 reportp->messageArgs = (const jschar **)
1057 JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
1058 if (!reportp->messageArgs)
1059 return JS_FALSE;
1060 reportp->messageArgs[argCount] = NULL;
1061 for (i = 0; i < argCount; i++) {
1062 if (charArgs) {
1063 char *charArg = va_arg(ap, char *);
1064 size_t charArgLength = strlen(charArg);
1065 reportp->messageArgs[i]
1066 = js_InflateString(cx, charArg, &charArgLength);
1067 if (!reportp->messageArgs[i])
1068 goto error;
1069 } else {
1070 reportp->messageArgs[i] = va_arg(ap, jschar *);
1071 }
1072 argLengths[i] = js_strlen(reportp->messageArgs[i]);
1073 totalArgsLength += argLengths[i];
1074 }
1075 /* NULL-terminate for easy copying. */
1076 reportp->messageArgs[i] = NULL;
1077 }
1078 /*
1079 * Parse the error format, substituting the argument X
1080 * for {X} in the format.
1081 */
1082 if (argCount > 0) {
1083 if (efs->format) {
1084 jschar *buffer, *fmt, *out;
1085 int expandedArgs = 0;
1086 size_t expandedLength;
1087 size_t len = strlen(efs->format);
1088
1089 buffer = fmt = js_InflateString (cx, efs->format, &len);
1090 if (!buffer)
1091 goto error;
1092 expandedLength = len
1093 - (3 * argCount) /* exclude the {n} */
1094 + totalArgsLength;
1095
1096 /*
1097 * Note - the above calculation assumes that each argument
1098 * is used once and only once in the expansion !!!
1099 */
1100 reportp->ucmessage = out = (jschar *)
1101 JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
1102 if (!out) {
1103 JS_free (cx, buffer);
1104 goto error;
1105 }
1106 while (*fmt) {
1107 if (*fmt == '{') {
1108 if (isdigit(fmt[1])) {
1109 int d = JS7_UNDEC(fmt[1]);
1110 JS_ASSERT(d < argCount);
1111 js_strncpy(out, reportp->messageArgs[d],
1112 argLengths[d]);
1113 out += argLengths[d];
1114 fmt += 3;
1115 expandedArgs++;
1116 continue;
1117 }
1118 }
1119 *out++ = *fmt++;
1120 }
1121 JS_ASSERT(expandedArgs == argCount);
1122 *out = 0;
1123 JS_free (cx, buffer);
1124 *messagep =
1125 js_DeflateString(cx, reportp->ucmessage,
1126 (size_t)(out - reportp->ucmessage));
1127 if (!*messagep)
1128 goto error;
1129 }
1130 } else {
1131 /*
1132 * Zero arguments: the format string (if it exists) is the
1133 * entire message.
1134 */
1135 if (efs->format) {
1136 size_t len;
1137 *messagep = JS_strdup(cx, efs->format);
1138 if (!*messagep)
1139 goto error;
1140 len = strlen(*messagep);
1141 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1142 if (!reportp->ucmessage)
1143 goto error;
1144 }
1145 }
1146 }
1147 if (*messagep == NULL) {
1148 /* where's the right place for this ??? */
1149 const char *defaultErrorMessage
1150 = "No error message available for error number %d";
1151 size_t nbytes = strlen(defaultErrorMessage) + 16;
1152 *messagep = (char *)JS_malloc(cx, nbytes);
1153 if (!*messagep)
1154 goto error;
1155 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1156 }
1157 return JS_TRUE;
1158
1159 error:
1160 if (reportp->messageArgs) {
1161 /* free the arguments only if we allocated them */
1162 if (charArgs) {
1163 i = 0;
1164 while (reportp->messageArgs[i])
1165 JS_free(cx, (void *)reportp->messageArgs[i++]);
1166 }
1167 JS_free(cx, (void *)reportp->messageArgs);
1168 reportp->messageArgs = NULL;
1169 }
1170 if (reportp->ucmessage) {
1171 JS_free(cx, (void *)reportp->ucmessage);
1172 reportp->ucmessage = NULL;
1173 }
1174 if (*messagep) {
1175 JS_free(cx, (void *)*messagep);
1176 *messagep = NULL;
1177 }
1178 return JS_FALSE;
1179 }
1180
1181 JSBool
1182 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1183 void *userRef, const uintN errorNumber,
1184 JSBool charArgs, va_list ap)
1185 {
1186 JSStackFrame *fp;
1187 JSErrorReport report;
1188 char *message;
1189 JSBool warning;
1190
1191 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1192 return JS_TRUE;
1193
1194 memset(&report, 0, sizeof (struct JSErrorReport));
1195 report.flags = flags;
1196 report.errorNumber = errorNumber;
1197
1198 /*
1199 * If we can't find out where the error was based on the current frame,
1200 * see if the next frame has a script/pc combo we can use.
1201 */
1202 for (fp = cx->fp; fp; fp = fp->down) {
1203 if (fp->regs) {
1204 report.filename = fp->script->filename;
1205 report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
1206 break;
1207 }
1208 }
1209
1210 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1211 &message, &report, &warning, charArgs, ap)) {
1212 return JS_FALSE;
1213 }
1214
1215 ReportError(cx, message, &report);
1216
1217 if (message)
1218 JS_free(cx, message);
1219 if (report.messageArgs) {
1220 /*
1221 * js_ExpandErrorArguments owns its messageArgs only if it had to
1222 * inflate the arguments (from regular |char *|s).
1223 */
1224 if (charArgs) {
1225 int i = 0;
1226 while (report.messageArgs[i])
1227 JS_free(cx, (void *)report.messageArgs[i++]);
1228 }
1229 JS_free(cx, (void *)report.messageArgs);
1230 }
1231 if (report.ucmessage)
1232 JS_free(cx, (void *)report.ucmessage);
1233
1234 return warning;
1235 }
1236
1237 JS_FRIEND_API(void)
1238 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1239 {
1240 JSErrorReporter onError;
1241
1242 if (!message)
1243 return;
1244
1245 if (cx->lastMessage)
1246 free(cx->lastMessage);
1247 cx->lastMessage = JS_strdup(cx, message);
1248 if (!cx->lastMessage)
1249 return;
1250 onError = cx->errorReporter;
1251
1252 /*
1253 * If debugErrorHook is present then we give it a chance to veto
1254 * sending the error on to the regular ErrorReporter.
1255 */
1256 if (onError) {
1257 JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1258 if (hook &&
1259 !hook(cx, cx->lastMessage, reportp,
1260 cx->debugHooks->debugErrorHookData)) {
1261 onError = NULL;
1262 }
1263 }
1264 if (onError)
1265 onError(cx, cx->lastMessage, reportp);
1266 }
1267
1268 void
1269 js_ReportIsNotDefined(JSContext *cx, const char *name)
1270 {
1271 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1272 }
1273
1274 JSBool
1275 js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
1276 JSString *fallback)
1277 {
1278 char *bytes;
1279 JSBool ok;
1280
1281 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1282 if (!bytes)
1283 return JS_FALSE;
1284
1285 if (strcmp(bytes, js_undefined_str) == 0 ||
1286 strcmp(bytes, js_null_str) == 0) {
1287 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1288 js_GetErrorMessage, NULL,
1289 JSMSG_NO_PROPERTIES, bytes,
1290 NULL, NULL);
1291 } else if (JSVAL_IS_VOID(v)) {
1292 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1293 js_GetErrorMessage, NULL,
1294 JSMSG_NULL_OR_UNDEFINED, bytes,
1295 js_undefined_str, NULL);
1296 } else {
1297 JS_ASSERT(JSVAL_IS_NULL(v));
1298 ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1299 js_GetErrorMessage, NULL,
1300 JSMSG_NULL_OR_UNDEFINED, bytes,
1301 js_null_str, NULL);
1302 }
1303
1304 JS_free(cx, bytes);
1305 return ok;
1306 }
1307
1308 void
1309 js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
1310 {
1311 char argbuf[11];
1312 char *bytes;
1313 JSAtom *atom;
1314
1315 JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1316 bytes = NULL;
1317 if (VALUE_IS_FUNCTION(cx, *vp)) {
1318 atom = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp))->atom;
1319 bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp,
1320 ATOM_TO_STRING(atom));
1321 if (!bytes)
1322 return;
1323 }
1324 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1325 JSMSG_MISSING_FUN_ARG, argbuf,
1326 bytes ? bytes : "");
1327 JS_free(cx, bytes);
1328 }
1329
1330 JSBool
1331 js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1332 intN spindex, jsval v, JSString *fallback,
1333 const char *arg1, const char *arg2)
1334 {
1335 char *bytes;
1336 JSBool ok;
1337
1338 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1339 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1340 bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1341 if (!bytes)
1342 return JS_FALSE;
1343
1344 ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1345 NULL, errorNumber, bytes, arg1, arg2);
1346 JS_free(cx, bytes);
1347 return ok;
1348 }
1349
1350 #if defined DEBUG && defined XP_UNIX
1351 /* For gdb usage. */
1352 void js_traceon(JSContext *cx) { cx->tracefp = stderr; }
1353 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1354 #endif
1355
1356 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1357 #define MSG_DEF(name, number, count, exception, format) \
1358 { format, count, exception } ,
1359 #include "js.msg"
1360 #undef MSG_DEF
1361 };
1362
1363 JS_FRIEND_API(const JSErrorFormatString *)
1364 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1365 {
1366 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1367 return &js_ErrorFormatString[errorNumber];
1368 return NULL;
1369 }
1370
1371 JSBool
1372 js_ResetOperationCount(JSContext *cx)
1373 {
1374 JSScript *script;
1375
1376 JS_ASSERT(cx->operationCount <= 0);
1377 JS_ASSERT(cx->operationLimit > 0);
1378
1379 cx->operationCount = (int32) cx->operationLimit;
1380 if (cx->operationCallbackIsSet)
1381 return cx->operationCallback(cx);
1382
1383 if (cx->operationCallback) {
1384 /*
1385 * Invoke the deprecated branch callback. It may be called only when
1386 * the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK
1387 * is set.
1388 */
1389 script = cx->fp ? cx->fp->script : NULL;
1390 if (script || JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK))
1391 return ((JSBranchCallback) cx->operationCallback)(cx, script);
1392 }
1393 return JS_TRUE;
1394 }

  ViewVC Help
Powered by ViewVC 1.1.24