1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 /*
41 * JS execution context.
42 */
43 #include "jsstddef.h"
44 #include <stdarg.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsarena.h" /* Added by JSIFY */
49 #include "jsutil.h" /* Added by JSIFY */
50 #include "jsclist.h"
51 #include "jsprf.h"
52 #include "jsatom.h"
53 #include "jscntxt.h"
54 #include "jsconfig.h"
55 #include "jsdbgapi.h"
56 #include "jsexn.h"
57 #include "jsgc.h"
58 #include "jslock.h"
59 #include "jsnum.h"
60 #include "jsobj.h"
61 #include "jsopcode.h"
62 #include "jsscan.h"
63 #include "jsscript.h"
64 #include "jsstr.h"
65
66 JSContext *
js_NewContext(JSRuntime * rt,size_t stackChunkSize)67 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
68 {
69 JSContext *cx;
70 JSBool ok, first;
71
72 cx = (JSContext *) malloc(sizeof *cx);
73 if (!cx)
74 return NULL;
75 memset(cx, 0, sizeof *cx);
76
77 cx->runtime = rt;
78 #if JS_STACK_GROWTH_DIRECTION > 0
79 cx->stackLimit = (jsuword)-1;
80 #endif
81 #ifdef JS_THREADSAFE
82 js_InitContextForLocking(cx);
83 #endif
84
85 JS_LOCK_GC(rt);
86 for (;;) {
87 first = (rt->contextList.next == &rt->contextList);
88 if (rt->state == JSRTS_UP) {
89 JS_ASSERT(!first);
90 break;
91 }
92 if (rt->state == JSRTS_DOWN) {
93 JS_ASSERT(first);
94 rt->state = JSRTS_LAUNCHING;
95 break;
96 }
97 JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
98 }
99 JS_APPEND_LINK(&cx->links, &rt->contextList);
100 JS_UNLOCK_GC(rt);
101
102 /*
103 * First we do the infallible, every-time per-context initializations.
104 * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
105 * or the stuff under 'if (first)' below) fail, at least the version
106 * and arena-pools will be valid and safe to use (say, from the last GC
107 * done by js_DestroyContext).
108 */
109 cx->version = JSVERSION_DEFAULT;
110 cx->jsop_eq = JSOP_EQ;
111 cx->jsop_ne = JSOP_NE;
112 JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));
113 JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));
114
115 #if JS_HAS_REGEXPS
116 if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
117 js_DestroyContext(cx, JS_NO_GC);
118 return NULL;
119 }
120 #endif
121 #if JS_HAS_EXCEPTIONS
122 cx->throwing = JS_FALSE;
123 #endif
124
125 /*
126 * If cx is the first context on this runtime, initialize well-known atoms,
127 * keywords, numbers, and strings. If one of these steps should fail, the
128 * runtime will be left in a partially initialized state, with zeroes and
129 * nulls stored in the default-initialized remainder of the struct. We'll
130 * clean the runtime up under js_DestroyContext, because cx will be "last"
131 * as well as "first".
132 */
133 if (first) {
134 ok = (rt->atomState.liveAtoms == 0)
135 ? js_InitAtomState(cx, &rt->atomState)
136 : js_InitPinnedAtoms(cx, &rt->atomState);
137 if (ok)
138 ok = js_InitScanner(cx);
139 if (ok)
140 ok = js_InitRuntimeNumberState(cx);
141 if (ok)
142 ok = js_InitRuntimeScriptState(cx);
143 if (ok)
144 ok = js_InitRuntimeStringState(cx);
145 if (!ok) {
146 js_DestroyContext(cx, JS_NO_GC);
147 return NULL;
148 }
149
150 JS_LOCK_GC(rt);
151 rt->state = JSRTS_UP;
152 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
153 JS_UNLOCK_GC(rt);
154 }
155
156 return cx;
157 }
158
159 void
js_DestroyContext(JSContext * cx,JSGCMode gcmode)160 js_DestroyContext(JSContext *cx, JSGCMode gcmode)
161 {
162 JSRuntime *rt;
163 JSBool last;
164 JSArgumentFormatMap *map;
165 JSLocalRootStack *lrs;
166 JSLocalRootChunk *lrc;
167
168 rt = cx->runtime;
169
170 /* Remove cx from context list first. */
171 JS_LOCK_GC(rt);
172 JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
173 JS_REMOVE_LINK(&cx->links);
174 last = (rt->contextList.next == &rt->contextList);
175 if (last)
176 rt->state = JSRTS_LANDING;
177 JS_UNLOCK_GC(rt);
178
179 if (last) {
180 #ifdef JS_THREADSAFE
181 /*
182 * If cx is not in a request already, begin one now so that we wait
183 * for any racing GC started on a not-last context to finish, before
184 * we plow ahead and unpin atoms. Note that even though we begin a
185 * request here if necessary, we end all requests on cx below before
186 * forcing a final GC. This lets any not-last context destruction
187 * racing in another thread try to force or maybe run the GC, but by
188 * that point, rt->state will not be JSRTS_UP, and that GC attempt
189 * will return early.
190 */
191 if (cx->requestDepth == 0)
192 JS_BeginRequest(cx);
193 #endif
194
195 /* Unpin all pinned atoms before final GC. */
196 js_UnpinPinnedAtoms(&rt->atomState);
197
198 /* Unlock and clear GC things held by runtime pointers. */
199 js_FinishRuntimeNumberState(cx);
200 js_FinishRuntimeStringState(cx);
201
202 /* Clear debugging state to remove GC roots. */
203 JS_ClearAllTraps(cx);
204 JS_ClearAllWatchPoints(cx);
205 }
206
207 #if JS_HAS_REGEXPS
208 /*
209 * Remove more GC roots in regExpStatics, then collect garbage.
210 * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
211 * XXX this function call to wait for any racing GC to complete, in the
212 * XXX case where JS_DestroyContext is called outside of a request on cx
213 */
214 js_FreeRegExpStatics(cx, &cx->regExpStatics);
215 #endif
216
217 #ifdef JS_THREADSAFE
218 /*
219 * Destroying a context implicitly calls JS_EndRequest(). Also, we must
220 * end our request here in case we are "last" -- in that event, another
221 * js_DestroyContext that was not last might be waiting in the GC for our
222 * request to end. We'll let it run below, just before we do the truly
223 * final GC and then free atom state.
224 *
225 * At this point, cx must be inaccessible to other threads. It's off the
226 * rt->contextList, and it should not be reachable via any object private
227 * data structure.
228 */
229 while (cx->requestDepth != 0)
230 JS_EndRequest(cx);
231 #endif
232
233 if (last) {
234 /* Always force, so we wait for any racing GC to finish. */
235 js_ForceGC(cx, GC_LAST_CONTEXT);
236
237 /* Iterate until no finalizer removes a GC root or lock. */
238 while (rt->gcPoke)
239 js_GC(cx, GC_LAST_CONTEXT);
240
241 /* Try to free atom state, now that no unrooted scripts survive. */
242 if (rt->atomState.liveAtoms == 0)
243 js_FreeAtomState(cx, &rt->atomState);
244
245 /* Now after the last GC can we free the script filename table. */
246 js_FinishRuntimeScriptState(cx);
247
248 /* Take the runtime down, now that it has no contexts or atoms. */
249 JS_LOCK_GC(rt);
250 rt->state = JSRTS_DOWN;
251 JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
252 JS_UNLOCK_GC(rt);
253 } else {
254 if (gcmode == JS_FORCE_GC)
255 js_ForceGC(cx, 0);
256 else if (gcmode == JS_MAYBE_GC)
257 JS_MaybeGC(cx);
258 }
259
260 /* Free the stuff hanging off of cx. */
261 JS_FinishArenaPool(&cx->stackPool);
262 JS_FinishArenaPool(&cx->tempPool);
263 if (cx->lastMessage)
264 free(cx->lastMessage);
265
266 /* Remove any argument formatters. */
267 map = cx->argumentFormatMap;
268 while (map) {
269 JSArgumentFormatMap *temp = map;
270 map = map->next;
271 JS_free(cx, temp);
272 }
273
274 /* Destroy the resolve recursion damper. */
275 if (cx->resolvingTable) {
276 JS_DHashTableDestroy(cx->resolvingTable);
277 cx->resolvingTable = NULL;
278 }
279
280 lrs = cx->localRootStack;
281 if (lrs) {
282 while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
283 lrs->topChunk = lrc->down;
284 JS_free(cx, lrc);
285 }
286 JS_free(cx, lrs);
287 }
288
289 /* Destroy the lint information */
290 if (cx->lint) {
291 JS_free(cx, cx->lint);
292 }
293
294 /* Finally, free cx itself. */
295 free(cx);
296 }
297
298 JSBool
js_ValidContextPointer(JSRuntime * rt,JSContext * cx)299 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
300 {
301 JSCList *cl;
302
303 for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
304 if (cl == &cx->links)
305 return JS_TRUE;
306 }
307 JS_RUNTIME_METER(rt, deadContexts);
308 return JS_FALSE;
309 }
310
311 JSContext *
js_ContextIterator(JSRuntime * rt,JSBool unlocked,JSContext ** iterp)312 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
313 {
314 JSContext *cx = *iterp;
315
316 if (unlocked)
317 JS_LOCK_GC(rt);
318 if (!cx)
319 cx = (JSContext *)&rt->contextList;
320 cx = (JSContext *)cx->links.next;
321 if (&cx->links == &rt->contextList)
322 cx = NULL;
323 *iterp = cx;
324 if (unlocked)
325 JS_UNLOCK_GC(rt);
326 return cx;
327 }
328
329 JS_STATIC_DLL_CALLBACK(const void *)
resolving_GetKey(JSDHashTable * table,JSDHashEntryHdr * hdr)330 resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
331 {
332 JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
333
334 return &entry->key;
335 }
336
337 JS_STATIC_DLL_CALLBACK(JSDHashNumber)
resolving_HashKey(JSDHashTable * table,const void * ptr)338 resolving_HashKey(JSDHashTable *table, const void *ptr)
339 {
340 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
341
342 return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id;
343 }
344
345 JS_PUBLIC_API(JSBool)
resolving_MatchEntry(JSDHashTable * table,const JSDHashEntryHdr * hdr,const void * ptr)346 resolving_MatchEntry(JSDHashTable *table,
347 const JSDHashEntryHdr *hdr,
348 const void *ptr)
349 {
350 const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
351 const JSResolvingKey *key = (const JSResolvingKey *)ptr;
352
353 return entry->key.obj == key->obj && entry->key.id == key->id;
354 }
355
356 static const JSDHashTableOps resolving_dhash_ops = {
357 JS_DHashAllocTable,
358 JS_DHashFreeTable,
359 resolving_GetKey,
360 resolving_HashKey,
361 resolving_MatchEntry,
362 JS_DHashMoveEntryStub,
363 JS_DHashClearEntryStub,
364 JS_DHashFinalizeStub,
365 NULL
366 };
367
368 JSBool
js_StartResolving(JSContext * cx,JSResolvingKey * key,uint32 flag,JSResolvingEntry ** entryp)369 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
370 JSResolvingEntry **entryp)
371 {
372 JSDHashTable *table;
373 JSResolvingEntry *entry;
374
375 table = cx->resolvingTable;
376 if (!table) {
377 table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
378 sizeof(JSResolvingEntry),
379 JS_DHASH_MIN_SIZE);
380 if (!table)
381 goto outofmem;
382 cx->resolvingTable = table;
383 }
384
385 entry = (JSResolvingEntry *)
386 JS_DHashTableOperate(table, key, JS_DHASH_ADD);
387 if (!entry)
388 goto outofmem;
389
390 if (entry->flags & flag) {
391 /* An entry for (key, flag) exists already -- dampen recursion. */
392 entry = NULL;
393 } else {
394 /* Fill in key if we were the first to add entry, then set flag. */
395 if (!entry->key.obj)
396 entry->key = *key;
397 entry->flags |= flag;
398 }
399 *entryp = entry;
400 return JS_TRUE;
401
402 outofmem:
403 JS_ReportOutOfMemory(cx);
404 return JS_FALSE;
405 }
406
407 void
js_StopResolving(JSContext * cx,JSResolvingKey * key,uint32 flag,JSResolvingEntry * entry,uint32 generation)408 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
409 JSResolvingEntry *entry, uint32 generation)
410 {
411 JSDHashTable *table;
412
413 /*
414 * Clear flag from entry->flags and return early if other flags remain.
415 * We must take care to re-lookup entry if the table has changed since
416 * it was found by js_StartResolving.
417 */
418 table = cx->resolvingTable;
419 if (!entry || table->generation != generation) {
420 entry = (JSResolvingEntry *)
421 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
422 }
423 JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
424 entry->flags &= ~flag;
425 if (entry->flags)
426 return;
427
428 /*
429 * Do a raw remove only if fewer entries were removed than would cause
430 * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
431 * call JS_DHashTableOperate to re-lookup the key and remove its entry,
432 * compressing or shrinking the table as needed.
433 */
434 if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
435 JS_DHashTableRawRemove(table, &entry->hdr);
436 else
437 JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
438 }
439
440 JSBool
js_EnterLocalRootScope(JSContext * cx)441 js_EnterLocalRootScope(JSContext *cx)
442 {
443 JSLocalRootStack *lrs;
444 int mark;
445
446 lrs = cx->localRootStack;
447 if (!lrs) {
448 lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
449 if (!lrs)
450 return JS_FALSE;
451 lrs->scopeMark = JSLRS_NULL_MARK;
452 lrs->rootCount = 0;
453 lrs->topChunk = &lrs->firstChunk;
454 lrs->firstChunk.down = NULL;
455 cx->localRootStack = lrs;
456 }
457
458 /* Push lrs->scopeMark to save it for restore when leaving. */
459 mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
460 if (mark < 0)
461 return JS_FALSE;
462 lrs->scopeMark = (uint16) mark;
463 return JS_TRUE;
464 }
465
466 void
js_LeaveLocalRootScope(JSContext * cx)467 js_LeaveLocalRootScope(JSContext *cx)
468 {
469 JSLocalRootStack *lrs;
470 unsigned mark, m, n;
471 JSLocalRootChunk *lrc;
472
473 /* Defend against buggy native callers. */
474 lrs = cx->localRootStack;
475 JS_ASSERT(lrs && lrs->rootCount != 0);
476 if (!lrs || lrs->rootCount == 0)
477 return;
478
479 mark = lrs->scopeMark;
480 JS_ASSERT(mark != JSLRS_NULL_MARK);
481 if (mark == JSLRS_NULL_MARK)
482 return;
483
484 /* Free any chunks being popped by this leave operation. */
485 m = mark >> JSLRS_CHUNK_SHIFT;
486 n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
487 while (n > m) {
488 lrc = lrs->topChunk;
489 JS_ASSERT(lrc != &lrs->firstChunk);
490 lrs->topChunk = lrc->down;
491 JS_free(cx, lrc);
492 --n;
493 }
494
495 /* Pop the scope, restoring lrs->scopeMark. */
496 lrc = lrs->topChunk;
497 m = mark & JSLRS_CHUNK_MASK;
498 lrs->scopeMark = JSVAL_TO_INT(lrc->roots[m]);
499 lrc->roots[m] = JSVAL_NULL;
500 lrs->rootCount = (uint16) mark;
501
502 /*
503 * Free the stack eagerly, risking malloc churn. The alternative would
504 * require an lrs->entryCount member, maintained by Enter and Leave, and
505 * tested by the GC in addition to the cx->localRootStack non-null test.
506 *
507 * That approach would risk hoarding 264 bytes (net) per context. Right
508 * now it seems better to give fresh (dirty in CPU write-back cache, and
509 * the data is no longer needed) memory back to the malloc heap.
510 */
511 if (mark == 0) {
512 cx->localRootStack = NULL;
513 JS_free(cx, lrs);
514 } else if (m == 0) {
515 lrs->topChunk = lrc->down;
516 JS_free(cx, lrc);
517 }
518 }
519
520 void
js_ForgetLocalRoot(JSContext * cx,jsval v)521 js_ForgetLocalRoot(JSContext *cx, jsval v)
522 {
523 JSLocalRootStack *lrs;
524 unsigned i, j, m, n, mark;
525 JSLocalRootChunk *lrc, *lrc2;
526 jsval top;
527
528 lrs = cx->localRootStack;
529 JS_ASSERT(lrs && lrs->rootCount);
530 if (!lrs || lrs->rootCount == 0)
531 return;
532
533 /* Prepare to pop the top-most value from the stack. */
534 n = lrs->rootCount - 1;
535 m = n & JSLRS_CHUNK_MASK;
536 lrc = lrs->topChunk;
537 top = lrc->roots[m];
538
539 /* Be paranoid about calls on an empty scope. */
540 mark = lrs->scopeMark;
541 JS_ASSERT(mark < n);
542 if (mark >= n)
543 return;
544
545 /* If v was not the last root pushed in the top scope, find it. */
546 if (top != v) {
547 /* Search downward in case v was recently pushed. */
548 i = n;
549 j = m;
550 lrc2 = lrc;
551 while (--i > mark) {
552 if (j == 0)
553 lrc2 = lrc2->down;
554 j = i & JSLRS_CHUNK_MASK;
555 if (lrc2->roots[j] == v)
556 break;
557 }
558
559 /* If we didn't find v in this scope, assert and bail out. */
560 JS_ASSERT(i != mark);
561 if (i == mark)
562 return;
563
564 /* Swap top and v so common tail code can pop v. */
565 lrc2->roots[j] = top;
566 }
567
568 /* Pop the last value from the stack. */
569 lrc->roots[m] = JSVAL_NULL;
570 lrs->rootCount = n;
571 if (m == 0) {
572 JS_ASSERT(n != 0);
573 JS_ASSERT(lrc != &lrs->firstChunk);
574 lrs->topChunk = lrc->down;
575 JS_free(cx, lrc);
576 }
577 }
578
579 int
js_PushLocalRoot(JSContext * cx,JSLocalRootStack * lrs,jsval v)580 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
581 {
582 unsigned n, m;
583 JSLocalRootChunk *lrc;
584
585 n = lrs->rootCount;
586 m = n & JSLRS_CHUNK_MASK;
587 if (n == 0 || m != 0) {
588 /*
589 * At start of first chunk, or not at start of a non-first top chunk.
590 * Check for lrs->rootCount overflow.
591 */
592 if ((uint16)(n + 1) == 0) {
593 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
594 JSMSG_TOO_MANY_LOCAL_ROOTS);
595 return -1;
596 }
597 lrc = lrs->topChunk;
598 JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
599 } else {
600 /*
601 * After lrs->firstChunk, trying to index at a power-of-two chunk
602 * boundary: need a new chunk.
603 */
604 lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc);
605 if (!lrc)
606 return -1;
607 lrc->down = lrs->topChunk;
608 lrs->topChunk = lrc;
609 }
610 lrs->rootCount = n + 1;
611 lrc->roots[m] = v;
612 return (int) m;
613 }
614
615 void
js_MarkLocalRoots(JSContext * cx,JSLocalRootStack * lrs)616 js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs)
617 {
618 unsigned n, m, mark;
619 JSLocalRootChunk *lrc;
620
621 n = lrs->rootCount;
622 if (n == 0)
623 return;
624
625 mark = lrs->scopeMark;
626 lrc = lrs->topChunk;
627 while (--n > mark) {
628 #ifdef GC_MARK_DEBUG
629 char name[22];
630 JS_snprintf(name, sizeof name, "<local root %u>", n);
631 #else
632 const char *name = NULL;
633 #endif
634 m = n & JSLRS_CHUNK_MASK;
635 JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m]));
636 JS_MarkGCThing(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name, NULL);
637 if (m == 0)
638 lrc = lrc->down;
639 }
640 }
641
642 static void
ReportError(JSContext * cx,const char * message,JSErrorReport * reportp)643 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
644 {
645 /*
646 * Check the error report, and set a JavaScript-catchable exception
647 * if the error is defined to have an associated exception. If an
648 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
649 * on the error report, and exception-aware hosts should ignore it.
650 */
651 if (reportp && reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
652 reportp->flags |= JSREPORT_EXCEPTION;
653
654 #if JS_HAS_ERROR_EXCEPTIONS
655 /*
656 * Call the error reporter only if an exception wasn't raised.
657 *
658 * If an exception was raised, then we call the debugErrorHook
659 * (if present) to give it a chance to see the error before it
660 * propagates out of scope. This is needed for compatability
661 * with the old scheme.
662 */
663 if (!js_ErrorToException(cx, message, reportp)) {
664 js_ReportErrorAgain(cx, message, reportp);
665 } else if (cx->runtime->debugErrorHook && cx->errorReporter) {
666 JSDebugErrorHook hook = cx->runtime->debugErrorHook;
667 /* test local in case debugErrorHook changed on another thread */
668 if (hook)
669 hook(cx, message, reportp, cx->runtime->debugErrorHookData);
670 }
671 #else
672 js_ReportErrorAgain(cx, message, reportp);
673 #endif
674 }
675
676 /*
677 * We don't post an exception in this case, since doing so runs into
678 * complications of pre-allocating an exception object which required
679 * running the Exception class initializer early etc.
680 * Instead we just invoke the errorReporter with an "Out Of Memory"
681 * type message, and then hope the process ends swiftly.
682 */
683 void
js_ReportOutOfMemory(JSContext * cx,JSErrorCallback callback)684 js_ReportOutOfMemory(JSContext *cx, JSErrorCallback callback)
685 {
686 JSStackFrame *fp;
687 JSErrorReport report;
688 JSErrorReporter onError = cx->errorReporter;
689
690 /* Get the message for this error, but we won't expand any arguments. */
691 const JSErrorFormatString *efs = callback(NULL, NULL, JSMSG_OUT_OF_MEMORY);
692 const char *msg = efs ? efs->format : "Out of memory";
693
694 /* Fill out the report, but don't do anything that requires allocation. */
695 memset(&report, 0, sizeof (struct JSErrorReport));
696 report.flags = JSREPORT_ERROR;
697 report.errorNumber = JSMSG_OUT_OF_MEMORY;
698
699 /*
700 * Walk stack until we find a frame that is associated with some script
701 * rather than a native frame.
702 */
703 for (fp = cx->fp; fp; fp = fp->down) {
704 if (fp->script && fp->pc) {
705 report.filename = fp->script->filename;
706 report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
707 break;
708 }
709 }
710
711 /*
712 * If debugErrorHook is present then we give it a chance to veto
713 * sending the error on to the regular ErrorReporter.
714 */
715 if (onError) {
716 JSDebugErrorHook hook = cx->runtime->debugErrorHook;
717 if (hook &&
718 !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {
719 onError = NULL;
720 }
721 }
722
723 if (onError)
724 onError(cx, msg, &report);
725 }
726
727 JSBool
js_ReportErrorVA(JSContext * cx,uintN flags,const char * format,va_list ap)728 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
729 {
730 char *last;
731 JSStackFrame *fp;
732 JSErrorReport report;
733 JSBool warning;
734
735 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
736 return JS_TRUE;
737
738 last = JS_vsmprintf(format, ap);
739 if (!last)
740 return JS_FALSE;
741
742 memset(&report, 0, sizeof (struct JSErrorReport));
743 report.flags = flags;
744
745 /* Find the top-most active script frame, for best line number blame. */
746 for (fp = cx->fp; fp; fp = fp->down) {
747 if (fp->script && fp->pc) {
748 report.filename = fp->script->filename;
749 report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
750 break;
751 }
752 }
753
754 warning = JSREPORT_IS_WARNING(report.flags);
755 if (warning && JS_HAS_WERROR_OPTION(cx)) {
756 report.flags &= ~JSREPORT_WARNING;
757 warning = JS_FALSE;
758 }
759
760 ReportError(cx, last, &report);
761 free(last);
762 return warning;
763 }
764
765 /*
766 * The arguments from ap need to be packaged up into an array and stored
767 * into the report struct.
768 *
769 * The format string addressed by the error number may contain operands
770 * identified by the format {N}, where N is a decimal digit. Each of these
771 * is to be replaced by the Nth argument from the va_list. The complete
772 * message is placed into reportp->ucmessage converted to a JSString.
773 *
774 * Returns true if the expansion succeeds (can fail if out of memory).
775 */
776 JSBool
js_ExpandErrorArguments(JSContext * cx,JSErrorCallback callback,void * userRef,const uintN errorNumber,char ** messagep,JSErrorReport * reportp,JSBool * warningp,JSBool charArgs,va_list ap)777 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
778 void *userRef, const uintN errorNumber,
779 char **messagep, JSErrorReport *reportp,
780 JSBool *warningp, JSBool charArgs, va_list ap)
781 {
782 const JSErrorFormatString *efs;
783 int i;
784 int argCount;
785
786 *warningp = JSREPORT_IS_WARNING(reportp->flags);
787 if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
788 reportp->flags &= ~JSREPORT_WARNING;
789 *warningp = JS_FALSE;
790 }
791
792 *messagep = NULL;
793 if (callback) {
794 efs = callback(userRef, NULL, errorNumber);
795 if (efs) {
796 size_t totalArgsLength = 0;
797 size_t argLengths[10]; /* only {0} thru {9} supported */
798 argCount = efs->argCount;
799 JS_ASSERT(argCount <= 10);
800 if (argCount > 0) {
801 /*
802 * Gather the arguments into an array, and accumulate
803 * their sizes. We allocate 1 more than necessary and
804 * null it out to act as the caboose when we free the
805 * pointers later.
806 */
807 reportp->messageArgs = (const jschar **)
808 JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
809 if (!reportp->messageArgs)
810 return JS_FALSE;
811 reportp->messageArgs[argCount] = NULL;
812 for (i = 0; i < argCount; i++) {
813 if (charArgs) {
814 char *charArg = va_arg(ap, char *);
815 reportp->messageArgs[i]
816 = js_InflateString(cx, charArg, strlen(charArg));
817 if (!reportp->messageArgs[i])
818 goto error;
819 }
820 else
821 reportp->messageArgs[i] = va_arg(ap, jschar *);
822 argLengths[i] = js_strlen(reportp->messageArgs[i]);
823 totalArgsLength += argLengths[i];
824 }
825 /* NULL-terminate for easy copying. */
826 reportp->messageArgs[i] = NULL;
827 }
828 /*
829 * Parse the error format, substituting the argument X
830 * for {X} in the format.
831 */
832 if (argCount > 0) {
833 if (efs->format) {
834 const char *fmt;
835 const jschar *arg;
836 jschar *out;
837 int expandedArgs = 0;
838 size_t expandedLength
839 = strlen(efs->format)
840 - (3 * argCount) /* exclude the {n} */
841 + totalArgsLength;
842 /*
843 * Note - the above calculation assumes that each argument
844 * is used once and only once in the expansion !!!
845 */
846 reportp->ucmessage = out = (jschar *)
847 JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
848 if (!out)
849 goto error;
850 fmt = efs->format;
851 while (*fmt) {
852 if (*fmt == '{') {
853 if (isdigit(fmt[1])) {
854 int d = JS7_UNDEC(fmt[1]);
855 JS_ASSERT(expandedArgs < argCount);
856 arg = reportp->messageArgs[d];
857 js_strncpy(out, arg, argLengths[d]);
858 out += argLengths[d];
859 fmt += 3;
860 expandedArgs++;
861 continue;
862 }
863 }
864 /*
865 * is this kosher?
866 */
867 *out++ = (unsigned char)(*fmt++);
868 }
869 JS_ASSERT(expandedArgs == argCount);
870 *out = 0;
871 *messagep =
872 js_DeflateString(cx, reportp->ucmessage,
873 (size_t)(out - reportp->ucmessage));
874 if (!*messagep)
875 goto error;
876 }
877 } else {
878 /*
879 * Zero arguments: the format string (if it exists) is the
880 * entire message.
881 */
882 if (efs->format) {
883 *messagep = JS_strdup(cx, efs->format);
884 if (!*messagep)
885 goto error;
886 reportp->ucmessage
887 = js_InflateString(cx, *messagep, strlen(*messagep));
888 if (!reportp->ucmessage)
889 goto error;
890 }
891 }
892 }
893 }
894 if (*messagep == NULL) {
895 /* where's the right place for this ??? */
896 const char *defaultErrorMessage
897 = "No error message available for error number %d";
898 size_t nbytes = strlen(defaultErrorMessage) + 16;
899 *messagep = (char *)JS_malloc(cx, nbytes);
900 if (!*messagep)
901 goto error;
902 JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
903 }
904 return JS_TRUE;
905
906 error:
907 if (reportp->messageArgs) {
908 i = 0;
909 while (reportp->messageArgs[i])
910 JS_free(cx, (void *)reportp->messageArgs[i++]);
911 JS_free(cx, (void *)reportp->messageArgs);
912 reportp->messageArgs = NULL;
913 }
914 if (reportp->ucmessage) {
915 JS_free(cx, (void *)reportp->ucmessage);
916 reportp->ucmessage = NULL;
917 }
918 if (*messagep) {
919 JS_free(cx, (void *)*messagep);
920 *messagep = NULL;
921 }
922 return JS_FALSE;
923 }
924
925 JSBool
js_ReportErrorNumberVA(JSContext * cx,uintN flags,JSErrorCallback callback,void * userRef,const uintN errorNumber,JSBool charArgs,va_list ap)926 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
927 void *userRef, const uintN errorNumber,
928 JSBool charArgs, va_list ap)
929 {
930 JSStackFrame *fp;
931 JSErrorReport report;
932 char *message;
933 JSBool warning;
934
935 if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
936 return JS_TRUE;
937
938 memset(&report, 0, sizeof (struct JSErrorReport));
939 report.flags = flags;
940 report.errorNumber = errorNumber;
941
942 /*
943 * If we can't find out where the error was based on the current frame,
944 * see if the next frame has a script/pc combo we can use.
945 */
946 for (fp = cx->fp; fp; fp = fp->down) {
947 if (fp->script && fp->pc) {
948 report.filename = fp->script->filename;
949 report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
950 break;
951 }
952 }
953
954 if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
955 &message, &report, &warning, charArgs, ap)) {
956 return JS_FALSE;
957 }
958
959 ReportError(cx, message, &report);
960
961 if (message)
962 JS_free(cx, message);
963 if (report.messageArgs) {
964 int i = 0;
965 while (report.messageArgs[i])
966 JS_free(cx, (void *)report.messageArgs[i++]);
967 JS_free(cx, (void *)report.messageArgs);
968 }
969 if (report.ucmessage)
970 JS_free(cx, (void *)report.ucmessage);
971
972 return warning;
973 }
974
975 JS_FRIEND_API(void)
js_ReportErrorAgain(JSContext * cx,const char * message,JSErrorReport * reportp)976 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
977 {
978 JSErrorReporter onError;
979
980 if (!message)
981 return;
982
983 if (cx->lastMessage)
984 free(cx->lastMessage);
985 cx->lastMessage = JS_strdup(cx, message);
986 if (!cx->lastMessage)
987 return;
988 onError = cx->errorReporter;
989
990 /*
991 * If debugErrorHook is present then we give it a chance to veto
992 * sending the error on to the regular ErrorReporter.
993 */
994 if (onError) {
995 JSDebugErrorHook hook = cx->runtime->debugErrorHook;
996 if (hook &&
997 !hook(cx, cx->lastMessage, reportp,
998 cx->runtime->debugErrorHookData)) {
999 onError = NULL;
1000 }
1001 }
1002 if (onError)
1003 onError(cx, cx->lastMessage, reportp);
1004 }
1005
1006 void
js_ReportIsNotDefined(JSContext * cx,const char * name)1007 js_ReportIsNotDefined(JSContext *cx, const char *name)
1008 {
1009 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1010 }
1011
1012 #if defined DEBUG && defined XP_UNIX
1013 /* For gdb usage. */
js_traceon(JSContext * cx)1014 void js_traceon(JSContext *cx) { cx->tracefp = stderr; }
js_traceoff(JSContext * cx)1015 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1016 #endif
1017
1018 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1019 #if JS_HAS_DFLT_MSG_STRINGS
1020 #define MSG_DEF(name, number, count, exception, format) \
1021 { format, count } ,
1022 #else
1023 #define MSG_DEF(name, number, count, exception, format) \
1024 { NULL, count } ,
1025 #endif
1026 #include "js.msg"
1027 #undef MSG_DEF
1028 };
1029
1030 const JSErrorFormatString *
js_GetErrorMessage(void * userRef,const char * locale,const uintN errorNumber)1031 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1032 {
1033 if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1034 return &js_ErrorFormatString[errorNumber];
1035 return NULL;
1036 }
1037