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

Contents of /trunk/js/jsexn.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
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 standard exception implementation.
43 */
44
45 #include "jsstddef.h"
46 #include <stdlib.h>
47 #include <string.h>
48 #include "jstypes.h"
49 #include "jsbit.h"
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jscntxt.h"
54 #include "jsversion.h"
55 #include "jsdbgapi.h"
56 #include "jsexn.h"
57 #include "jsfun.h"
58 #include "jsinterp.h"
59 #include "jsnum.h"
60 #include "jsobj.h"
61 #include "jsopcode.h"
62 #include "jsscope.h"
63 #include "jsscript.h"
64 #include "jsstaticcheck.h"
65
66 /* Forward declarations for js_ErrorClass's initializer. */
67 static JSBool
68 Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
69
70 static void
71 exn_trace(JSTracer *trc, JSObject *obj);
72
73 static void
74 exn_finalize(JSContext *cx, JSObject *obj);
75
76 static JSBool
77 exn_enumerate(JSContext *cx, JSObject *obj);
78
79 static JSBool
80 exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
81 JSObject **objp);
82
83 JSClass js_ErrorClass = {
84 js_Error_str,
85 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_MARK_IS_TRACE |
86 JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
87 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
88 exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize,
89 NULL, NULL, NULL, Exception,
90 NULL, NULL, JS_CLASS_TRACE(exn_trace), NULL
91 };
92
93 typedef struct JSStackTraceElem {
94 JSString *funName;
95 size_t argc;
96 const char *filename;
97 uintN ulineno;
98 } JSStackTraceElem;
99
100 typedef struct JSExnPrivate {
101 /* A copy of the JSErrorReport originally generated. */
102 JSErrorReport *errorReport;
103 JSString *message;
104 JSString *filename;
105 uintN lineno;
106 size_t stackDepth;
107 JSStackTraceElem stackElems[1];
108 } JSExnPrivate;
109
110 static JSString *
111 StackTraceToString(JSContext *cx, JSExnPrivate *priv);
112
113 static JSErrorReport *
114 CopyErrorReport(JSContext *cx, JSErrorReport *report)
115 {
116 /*
117 * We use a single malloc block to make a deep copy of JSErrorReport with
118 * the following layout:
119 * JSErrorReport
120 * array of copies of report->messageArgs
121 * jschar array with characters for all messageArgs
122 * jschar array with characters for ucmessage
123 * jschar array with characters for uclinebuf and uctokenptr
124 * char array with characters for linebuf and tokenptr
125 * char array with characters for filename
126 * Such layout together with the properties enforced by the following
127 * asserts does not need any extra alignment padding.
128 */
129 JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);
130 JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0);
131
132 size_t filenameSize;
133 size_t linebufSize;
134 size_t uclinebufSize;
135 size_t ucmessageSize;
136 size_t i, argsArraySize, argsCopySize, argSize;
137 size_t mallocSize;
138 JSErrorReport *copy;
139 uint8 *cursor;
140
141 #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
142
143 filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
144 linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0;
145 uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0;
146 ucmessageSize = 0;
147 argsArraySize = 0;
148 argsCopySize = 0;
149 if (report->ucmessage) {
150 ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
151 if (report->messageArgs) {
152 for (i = 0; report->messageArgs[i]; ++i)
153 argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
154
155 /* Non-null messageArgs should have at least one non-null arg. */
156 JS_ASSERT(i != 0);
157 argsArraySize = (i + 1) * sizeof(const jschar *);
158 }
159 }
160
161 /*
162 * The mallocSize can not overflow since it represents the sum of the
163 * sizes of already allocated objects.
164 */
165 mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize +
166 ucmessageSize + uclinebufSize + linebufSize + filenameSize;
167 cursor = (uint8 *)JS_malloc(cx, mallocSize);
168 if (!cursor)
169 return NULL;
170
171 copy = (JSErrorReport *)cursor;
172 memset(cursor, 0, sizeof(JSErrorReport));
173 cursor += sizeof(JSErrorReport);
174
175 if (argsArraySize != 0) {
176 copy->messageArgs = (const jschar **)cursor;
177 cursor += argsArraySize;
178 for (i = 0; report->messageArgs[i]; ++i) {
179 copy->messageArgs[i] = (const jschar *)cursor;
180 argSize = JS_CHARS_SIZE(report->messageArgs[i]);
181 memcpy(cursor, report->messageArgs[i], argSize);
182 cursor += argSize;
183 }
184 copy->messageArgs[i] = NULL;
185 JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize);
186 }
187
188 if (report->ucmessage) {
189 copy->ucmessage = (const jschar *)cursor;
190 memcpy(cursor, report->ucmessage, ucmessageSize);
191 cursor += ucmessageSize;
192 }
193
194 if (report->uclinebuf) {
195 copy->uclinebuf = (const jschar *)cursor;
196 memcpy(cursor, report->uclinebuf, uclinebufSize);
197 cursor += uclinebufSize;
198 if (report->uctokenptr) {
199 copy->uctokenptr = copy->uclinebuf + (report->uctokenptr -
200 report->uclinebuf);
201 }
202 }
203
204 if (report->linebuf) {
205 copy->linebuf = (const char *)cursor;
206 memcpy(cursor, report->linebuf, linebufSize);
207 cursor += linebufSize;
208 if (report->tokenptr) {
209 copy->tokenptr = copy->linebuf + (report->tokenptr -
210 report->linebuf);
211 }
212 }
213
214 if (report->filename) {
215 copy->filename = (const char *)cursor;
216 memcpy(cursor, report->filename, filenameSize);
217 }
218 JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize);
219
220 /* Copy non-pointer members. */
221 copy->lineno = report->lineno;
222 copy->errorNumber = report->errorNumber;
223
224 /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
225 copy->flags = report->flags;
226
227 #undef JS_CHARS_SIZE
228 return copy;
229 }
230
231 static jsval *
232 GetStackTraceValueBuffer(JSExnPrivate *priv)
233 {
234 /*
235 * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals
236 * that helps to produce more informative stack traces. The following
237 * assert allows us to assume that no gap after stackElems is necessary to
238 * align the buffer properly.
239 */
240 JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0);
241
242 return (jsval *)(priv->stackElems + priv->stackDepth);
243 }
244
245 static JSBool
246 InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
247 JSString *filename, uintN lineno, JSErrorReport *report)
248 {
249 JSSecurityCallbacks *callbacks;
250 JSCheckAccessOp checkAccess;
251 JSErrorReporter older;
252 JSExceptionState *state;
253 jsval callerid, v;
254 JSStackFrame *fp, *fpstop;
255 size_t stackDepth, valueCount, size;
256 JSBool overflow;
257 JSExnPrivate *priv;
258 JSStackTraceElem *elem;
259 jsval *values;
260
261 JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass);
262
263 /*
264 * Prepare stack trace data.
265 *
266 * Set aside any error reporter for cx and save its exception state
267 * so we can suppress any checkAccess failures. Such failures should stop
268 * the backtrace procedure, not result in a failure of this constructor.
269 */
270 callbacks = JS_GetSecurityCallbacks(cx);
271 checkAccess = callbacks
272 ? callbacks->checkObjectAccess
273 : NULL;
274 older = JS_SetErrorReporter(cx, NULL);
275 state = JS_SaveExceptionState(cx);
276
277 callerid = ATOM_KEY(cx->runtime->atomState.callerAtom);
278 stackDepth = 0;
279 valueCount = 0;
280 for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
281 if (fp->fun && fp->argv) {
282 v = JSVAL_NULL;
283 if (checkAccess &&
284 !checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) {
285 break;
286 }
287 valueCount += fp->argc;
288 }
289 ++stackDepth;
290 }
291 JS_RestoreExceptionState(cx, state);
292 JS_SetErrorReporter(cx, older);
293 fpstop = fp;
294
295 size = offsetof(JSExnPrivate, stackElems);
296 overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem));
297 size += stackDepth * sizeof(JSStackTraceElem);
298 overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval));
299 size += valueCount * sizeof(jsval);
300 if (overflow) {
301 js_ReportAllocationOverflow(cx);
302 return JS_FALSE;
303 }
304 priv = (JSExnPrivate *)JS_malloc(cx, size);
305 if (!priv)
306 return JS_FALSE;
307
308 /*
309 * We initialize errorReport with a copy of report after setting the
310 * private slot, to prevent GC accessing a junk value we clear the field
311 * here.
312 */
313 priv->errorReport = NULL;
314 priv->message = message;
315 priv->filename = filename;
316 priv->lineno = lineno;
317 priv->stackDepth = stackDepth;
318
319 values = GetStackTraceValueBuffer(priv);
320 elem = priv->stackElems;
321 for (fp = js_GetTopStackFrame(cx); fp != fpstop; fp = fp->down) {
322 if (!fp->fun) {
323 elem->funName = NULL;
324 elem->argc = 0;
325 } else {
326 elem->funName = fp->fun->atom
327 ? ATOM_TO_STRING(fp->fun->atom)
328 : cx->runtime->emptyString;
329 elem->argc = fp->argc;
330 memcpy(values, fp->argv, fp->argc * sizeof(jsval));
331 values += fp->argc;
332 }
333 elem->ulineno = 0;
334 elem->filename = NULL;
335 if (fp->script) {
336 elem->filename = fp->script->filename;
337 if (fp->regs)
338 elem->ulineno = js_FramePCToLineNumber(cx, fp);
339 }
340 ++elem;
341 }
342 JS_ASSERT(priv->stackElems + stackDepth == elem);
343 JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
344
345 STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv));
346
347 if (report) {
348 /*
349 * Construct a new copy of the error report struct. We can't use the
350 * error report struct that was passed in, because it's allocated on
351 * the stack, and also because it may point to transient data in the
352 * JSTokenStream.
353 */
354 priv->errorReport = CopyErrorReport(cx, report);
355 if (!priv->errorReport) {
356 /* The finalizer realeases priv since it is in the private slot. */
357 return JS_FALSE;
358 }
359 }
360
361 return JS_TRUE;
362 }
363
364 static JSExnPrivate *
365 GetExnPrivate(JSContext *cx, JSObject *obj)
366 {
367 jsval privateValue;
368 JSExnPrivate *priv;
369
370 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass);
371 privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
372 if (JSVAL_IS_VOID(privateValue))
373 return NULL;
374 priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue);
375 JS_ASSERT(priv);
376 return priv;
377 }
378
379 static void
380 exn_trace(JSTracer *trc, JSObject *obj)
381 {
382 JSExnPrivate *priv;
383 JSStackTraceElem *elem;
384 size_t vcount, i;
385 jsval *vp, v;
386
387 priv = GetExnPrivate(trc->context, obj);
388 if (priv) {
389 if (priv->message)
390 JS_CALL_STRING_TRACER(trc, priv->message, "exception message");
391 if (priv->filename)
392 JS_CALL_STRING_TRACER(trc, priv->filename, "exception filename");
393
394 elem = priv->stackElems;
395 for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
396 if (elem->funName) {
397 JS_CALL_STRING_TRACER(trc, elem->funName,
398 "stack trace function name");
399 }
400 if (IS_GC_MARKING_TRACER(trc) && elem->filename)
401 js_MarkScriptFilename(elem->filename);
402 vcount += elem->argc;
403 }
404 vp = GetStackTraceValueBuffer(priv);
405 for (i = 0; i != vcount; ++i, ++vp) {
406 v = *vp;
407 JS_CALL_VALUE_TRACER(trc, v, "stack trace argument");
408 }
409 }
410 }
411
412 static void
413 exn_finalize(JSContext *cx, JSObject *obj)
414 {
415 JSExnPrivate *priv;
416
417 priv = GetExnPrivate(cx, obj);
418 if (priv) {
419 if (priv->errorReport)
420 JS_free(cx, priv->errorReport);
421 JS_free(cx, priv);
422 }
423 }
424
425 static JSBool
426 exn_enumerate(JSContext *cx, JSObject *obj)
427 {
428 JSAtomState *atomState;
429 uintN i;
430 JSAtom *atom;
431 JSObject *pobj;
432 JSProperty *prop;
433
434 JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1);
435 static const uint16 offsets[] = {
436 (uint16)offsetof(JSAtomState, messageAtom),
437 (uint16)offsetof(JSAtomState, fileNameAtom),
438 (uint16)offsetof(JSAtomState, lineNumberAtom),
439 (uint16)offsetof(JSAtomState, stackAtom),
440 };
441
442 atomState = &cx->runtime->atomState;
443 for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) {
444 atom = *(JSAtom **)((uint8 *)atomState + offsets[i]);
445 if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
446 return JS_FALSE;
447 if (prop)
448 OBJ_DROP_PROPERTY(cx, pobj, prop);
449 }
450 return JS_TRUE;
451 }
452
453 static JSBool
454 exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
455 JSObject **objp)
456 {
457 JSExnPrivate *priv;
458 JSString *str;
459 JSAtom *atom;
460 JSString *stack;
461 const char *prop;
462 jsval v;
463
464 *objp = NULL;
465 priv = GetExnPrivate(cx, obj);
466 if (priv && JSVAL_IS_STRING(id)) {
467 str = JSVAL_TO_STRING(id);
468
469 atom = cx->runtime->atomState.messageAtom;
470 if (str == ATOM_TO_STRING(atom)) {
471 prop = js_message_str;
472 v = STRING_TO_JSVAL(priv->message);
473 goto define;
474 }
475
476 atom = cx->runtime->atomState.fileNameAtom;
477 if (str == ATOM_TO_STRING(atom)) {
478 prop = js_fileName_str;
479 v = STRING_TO_JSVAL(priv->filename);
480 goto define;
481 }
482
483 atom = cx->runtime->atomState.lineNumberAtom;
484 if (str == ATOM_TO_STRING(atom)) {
485 prop = js_lineNumber_str;
486 v = INT_TO_JSVAL(priv->lineno);
487 goto define;
488 }
489
490 atom = cx->runtime->atomState.stackAtom;
491 if (str == ATOM_TO_STRING(atom)) {
492 stack = StackTraceToString(cx, priv);
493 if (!stack)
494 return JS_FALSE;
495
496 /* Allow to GC all things that were used to build stack trace. */
497 priv->stackDepth = 0;
498 prop = js_stack_str;
499 v = STRING_TO_JSVAL(stack);
500 goto define;
501 }
502 }
503 return JS_TRUE;
504
505 define:
506 if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE))
507 return JS_FALSE;
508 *objp = obj;
509 return JS_TRUE;
510 }
511
512 JSErrorReport *
513 js_ErrorFromException(JSContext *cx, jsval exn)
514 {
515 JSObject *obj;
516 JSExnPrivate *priv;
517
518 if (JSVAL_IS_PRIMITIVE(exn))
519 return NULL;
520 obj = JSVAL_TO_OBJECT(exn);
521 if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass)
522 return NULL;
523 priv = GetExnPrivate(cx, obj);
524 if (!priv)
525 return NULL;
526 return priv->errorReport;
527 }
528
529 static JSString *
530 ValueToShortSource(JSContext *cx, jsval v)
531 {
532 JSString *str;
533
534 /* Avoid toSource bloat and fallibility for object types. */
535 if (JSVAL_IS_PRIMITIVE(v)) {
536 str = js_ValueToSource(cx, v);
537 } else if (VALUE_IS_FUNCTION(cx, v)) {
538 /*
539 * XXX Avoid function decompilation bloat for now.
540 */
541 str = JS_GetFunctionId(JS_ValueToFunction(cx, v));
542 if (!str && !(str = js_ValueToSource(cx, v))) {
543 /*
544 * Continue to soldier on if the function couldn't be
545 * converted into a string.
546 */
547 JS_ClearPendingException(cx);
548 str = JS_NewStringCopyZ(cx, "[unknown function]");
549 }
550 } else {
551 /*
552 * XXX Avoid toString on objects, it takes too long and uses too much
553 * memory, for too many classes (see Mozilla bug 166743).
554 */
555 char buf[100];
556 JS_snprintf(buf, sizeof buf, "[object %s]",
557 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name);
558 str = JS_NewStringCopyZ(cx, buf);
559 }
560 return str;
561 }
562
563 static JSString *
564 StackTraceToString(JSContext *cx, JSExnPrivate *priv)
565 {
566 jschar *stackbuf;
567 size_t stacklen, stackmax;
568 JSStackTraceElem *elem, *endElem;
569 jsval *values;
570 size_t i;
571 JSString *str;
572 const char *cp;
573 char ulnbuf[11];
574
575 /* After this point, failing control flow must goto bad. */
576 stackbuf = NULL;
577 stacklen = stackmax = 0;
578
579 /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
580 #define STACK_LENGTH_LIMIT JS_BIT(20)
581
582 #define APPEND_CHAR_TO_STACK(c) \
583 JS_BEGIN_MACRO \
584 if (stacklen == stackmax) { \
585 void *ptr_; \
586 if (stackmax >= STACK_LENGTH_LIMIT) \
587 goto done; \
588 stackmax = stackmax ? 2 * stackmax : 64; \
589 ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
590 if (!ptr_) \
591 goto bad; \
592 stackbuf = (jschar *) ptr_; \
593 } \
594 stackbuf[stacklen++] = (c); \
595 JS_END_MACRO
596
597 #define APPEND_STRING_TO_STACK(str) \
598 JS_BEGIN_MACRO \
599 JSString *str_ = str; \
600 jschar *chars_; \
601 size_t length_; \
602 \
603 JSSTRING_CHARS_AND_LENGTH(str_, chars_, length_); \
604 if (length_ > stackmax - stacklen) { \
605 void *ptr_; \
606 if (stackmax >= STACK_LENGTH_LIMIT || \
607 length_ >= STACK_LENGTH_LIMIT - stacklen) { \
608 goto done; \
609 } \
610 stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \
611 ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
612 if (!ptr_) \
613 goto bad; \
614 stackbuf = (jschar *) ptr_; \
615 } \
616 js_strncpy(stackbuf + stacklen, chars_, length_); \
617 stacklen += length_; \
618 JS_END_MACRO
619
620 values = GetStackTraceValueBuffer(priv);
621 elem = priv->stackElems;
622 for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
623 if (elem->funName) {
624 APPEND_STRING_TO_STACK(elem->funName);
625 APPEND_CHAR_TO_STACK('(');
626 for (i = 0; i != elem->argc; i++, values++) {
627 if (i > 0)
628 APPEND_CHAR_TO_STACK(',');
629 str = ValueToShortSource(cx, *values);
630 if (!str)
631 goto bad;
632 APPEND_STRING_TO_STACK(str);
633 }
634 APPEND_CHAR_TO_STACK(')');
635 }
636 APPEND_CHAR_TO_STACK('@');
637 if (elem->filename) {
638 for (cp = elem->filename; *cp; cp++)
639 APPEND_CHAR_TO_STACK(*cp);
640 }
641 APPEND_CHAR_TO_STACK(':');
642 JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno);
643 for (cp = ulnbuf; *cp; cp++)
644 APPEND_CHAR_TO_STACK(*cp);
645 APPEND_CHAR_TO_STACK('\n');
646 }
647 #undef APPEND_CHAR_TO_STACK
648 #undef APPEND_STRING_TO_STACK
649 #undef STACK_LENGTH_LIMIT
650
651 done:
652 if (stacklen == 0) {
653 JS_ASSERT(!stackbuf);
654 return cx->runtime->emptyString;
655 }
656 if (stacklen < stackmax) {
657 /*
658 * Realloc can fail when shrinking on some FreeBSD versions, so
659 * don't use JS_realloc here; simply let the oversized allocation
660 * be owned by the string in that rare case.
661 */
662 void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar));
663 if (shrunk)
664 stackbuf = (jschar *) shrunk;
665 }
666
667 stackbuf[stacklen] = 0;
668 str = js_NewString(cx, stackbuf, stacklen);
669 if (str)
670 return str;
671
672 bad:
673 if (stackbuf)
674 JS_free(cx, stackbuf);
675 return NULL;
676 }
677
678 /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
679 with these two functions. */
680 static JSString *
681 FilenameToString(JSContext *cx, const char *filename)
682 {
683 return JS_NewStringCopyZ(cx, filename);
684 }
685
686 static const char *
687 StringToFilename(JSContext *cx, JSString *str)
688 {
689 return js_GetStringBytes(cx, str);
690 }
691
692 static JSBool
693 Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
694 {
695 uint32 lineno;
696 JSString *message, *filename;
697 JSStackFrame *fp;
698
699 if (!JS_IsConstructing(cx)) {
700 /*
701 * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
702 * called as functions, without operator new. But as we do not give
703 * each constructor a distinct JSClass, whose .name member is used by
704 * js_NewObject to find the class prototype, we must get the class
705 * prototype ourselves.
706 */
707 if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]),
708 ATOM_TO_JSID(cx->runtime->atomState
709 .classPrototypeAtom),
710 rval))
711 return JS_FALSE;
712 obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL, 0);
713 if (!obj)
714 return JS_FALSE;
715 *rval = OBJECT_TO_JSVAL(obj);
716 }
717
718 /*
719 * If it's a new object of class Exception, then null out the private
720 * data so that the finalizer doesn't attempt to free it.
721 */
722 if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass)
723 STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, JSVAL_VOID);
724
725 /* Set the 'message' property. */
726 if (argc != 0) {
727 message = js_ValueToString(cx, argv[0]);
728 if (!message)
729 return JS_FALSE;
730 argv[0] = STRING_TO_JSVAL(message);
731 } else {
732 message = cx->runtime->emptyString;
733 }
734
735 /* Set the 'fileName' property. */
736 if (argc > 1) {
737 filename = js_ValueToString(cx, argv[1]);
738 if (!filename)
739 return JS_FALSE;
740 argv[1] = STRING_TO_JSVAL(filename);
741 fp = NULL;
742 } else {
743 fp = js_GetScriptedCaller(cx, NULL);
744 if (fp) {
745 filename = FilenameToString(cx, fp->script->filename);
746 if (!filename)
747 return JS_FALSE;
748 } else {
749 filename = cx->runtime->emptyString;
750 }
751 }
752
753 /* Set the 'lineNumber' property. */
754 if (argc > 2) {
755 lineno = js_ValueToECMAUint32(cx, &argv[2]);
756 if (JSVAL_IS_NULL(argv[2]))
757 return JS_FALSE;
758 } else {
759 if (!fp)
760 fp = js_GetScriptedCaller(cx, NULL);
761 lineno = (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0;
762 }
763
764 return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) ||
765 InitExnPrivate(cx, obj, message, filename, lineno, NULL);
766 }
767
768 /*
769 * Convert to string.
770 *
771 * This method only uses JavaScript-modifiable properties name, message. It
772 * is left to the host to check for private data and report filename and line
773 * number information along with this message.
774 */
775 static JSBool
776 exn_toString(JSContext *cx, uintN argc, jsval *vp)
777 {
778 JSObject *obj;
779 jsval v;
780 JSString *name, *message, *result;
781 jschar *chars, *cp;
782 size_t name_length, message_length, length;
783
784 obj = JS_THIS_OBJECT(cx, vp);
785 if (!obj ||
786 !OBJ_GET_PROPERTY(cx, obj,
787 ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
788 &v)) {
789 return JS_FALSE;
790 }
791 name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString;
792 *vp = STRING_TO_JSVAL(name);
793
794 if (!JS_GetProperty(cx, obj, js_message_str, &v))
795 return JS_FALSE;
796 message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v)
797 : cx->runtime->emptyString;
798
799 if (JSSTRING_LENGTH(message) != 0) {
800 name_length = JSSTRING_LENGTH(name);
801 message_length = JSSTRING_LENGTH(message);
802 length = (name_length ? name_length + 2 : 0) + message_length;
803 cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
804 if (!chars)
805 return JS_FALSE;
806
807 if (name_length) {
808 js_strncpy(cp, JSSTRING_CHARS(name), name_length);
809 cp += name_length;
810 *cp++ = ':'; *cp++ = ' ';
811 }
812 js_strncpy(cp, JSSTRING_CHARS(message), message_length);
813 cp += message_length;
814 *cp = 0;
815
816 result = js_NewString(cx, chars, length);
817 if (!result) {
818 JS_free(cx, chars);
819 return JS_FALSE;
820 }
821 } else {
822 result = name;
823 }
824
825 *vp = STRING_TO_JSVAL(result);
826 return JS_TRUE;
827 }
828
829 #if JS_HAS_TOSOURCE
830 /*
831 * Return a string that may eval to something similar to the original object.
832 */
833 static JSBool
834 exn_toSource(JSContext *cx, uintN argc, jsval *vp)
835 {
836 JSObject *obj;
837 JSString *name, *message, *filename, *lineno_as_str, *result;
838 jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
839 JSTempValueRooter tvr;
840 JSBool ok;
841 uint32 lineno;
842 size_t lineno_length, name_length, message_length, filename_length, length;
843 jschar *chars, *cp;
844
845 obj = JS_THIS_OBJECT(cx, vp);
846 if (!obj ||
847 !OBJ_GET_PROPERTY(cx, obj,
848 ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
849 vp)) {
850 return JS_FALSE;
851 }
852 name = js_ValueToString(cx, *vp);
853 if (!name)
854 return JS_FALSE;
855 *vp = STRING_TO_JSVAL(name);
856
857 MUST_FLOW_THROUGH("out");
858 JS_PUSH_TEMP_ROOT(cx, 3, localroots, &tvr);
859
860 #ifdef __GNUC__
861 message = filename = NULL;
862 #endif
863 ok = JS_GetProperty(cx, obj, js_message_str, &localroots[0]) &&
864 (message = js_ValueToSource(cx, localroots[0]));
865 if (!ok)
866 goto out;
867 localroots[0] = STRING_TO_JSVAL(message);
868
869 ok = JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) &&
870 (filename = js_ValueToSource(cx, localroots[1]));
871 if (!ok)
872 goto out;
873 localroots[1] = STRING_TO_JSVAL(filename);
874
875 ok = JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]);
876 if (!ok)
877 goto out;
878 lineno = js_ValueToECMAUint32 (cx, &localroots[2]);
879 ok = !JSVAL_IS_NULL(localroots[2]);
880 if (!ok)
881 goto out;
882
883 if (lineno != 0) {
884 lineno_as_str = js_ValueToString(cx, localroots[2]);
885 if (!lineno_as_str) {
886 ok = JS_FALSE;
887 goto out;
888 }
889 lineno_length = JSSTRING_LENGTH(lineno_as_str);
890 } else {
891 lineno_as_str = NULL;
892 lineno_length = 0;
893 }
894
895 /* Magic 8, for the characters in ``(new ())''. */
896 name_length = JSSTRING_LENGTH(name);
897 message_length = JSSTRING_LENGTH(message);
898 length = 8 + name_length + message_length;
899
900 filename_length = JSSTRING_LENGTH(filename);
901 if (filename_length != 0) {
902 /* append filename as ``, {filename}'' */
903 length += 2 + filename_length;
904 if (lineno_as_str) {
905 /* append lineno as ``, {lineno_as_str}'' */
906 length += 2 + lineno_length;
907 }
908 } else {
909 if (lineno_as_str) {
910 /*
911 * no filename, but have line number,
912 * need to append ``, "", {lineno_as_str}''
913 */
914 length += 6 + lineno_length;
915 }
916 }
917
918 cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
919 if (!chars) {
920 ok = JS_FALSE;
921 goto out;
922 }
923
924 *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
925 js_strncpy(cp, JSSTRING_CHARS(name), name_length);
926 cp += name_length;
927 *cp++ = '(';
928 if (message_length != 0) {
929 js_strncpy(cp, JSSTRING_CHARS(message), message_length);
930 cp += message_length;
931 }
932
933 if (filename_length != 0) {
934 /* append filename as ``, {filename}'' */
935 *cp++ = ','; *cp++ = ' ';
936 js_strncpy(cp, JSSTRING_CHARS(filename), filename_length);
937 cp += filename_length;
938 } else {
939 if (lineno_as_str) {
940 /*
941 * no filename, but have line number,
942 * need to append ``, "", {lineno_as_str}''
943 */
944 *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
945 }
946 }
947 if (lineno_as_str) {
948 /* append lineno as ``, {lineno_as_str}'' */
949 *cp++ = ','; *cp++ = ' ';
950 js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length);
951 cp += lineno_length;
952 }
953
954 *cp++ = ')'; *cp++ = ')'; *cp = 0;
955
956 result = js_NewString(cx, chars, length);
957 if (!result) {
958 JS_free(cx, chars);
959 ok = JS_FALSE;
960 goto out;
961 }
962 *vp = STRING_TO_JSVAL(result);
963 ok = JS_TRUE;
964
965 out:
966 JS_POP_TEMP_ROOT(cx, &tvr);
967 return ok;
968 }
969 #endif
970
971 static JSFunctionSpec exception_methods[] = {
972 #if JS_HAS_TOSOURCE
973 JS_FN(js_toSource_str, exn_toSource, 0,0),
974 #endif
975 JS_FN(js_toString_str, exn_toString, 0,0),
976 JS_FS_END
977 };
978
979 /* JSProto_ ordering for exceptions shall match JSEXN_ constants. */
980 JS_STATIC_ASSERT(JSEXN_ERR == 0);
981 JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR == JSProto_InternalError);
982 JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR == JSProto_EvalError);
983 JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR == JSProto_RangeError);
984 JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError);
985 JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError);
986 JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError);
987 JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError);
988
989 static JS_INLINE JSProtoKey
990 GetExceptionProtoKey(intN exn)
991 {
992 JS_ASSERT(JSEXN_ERR <= exn);
993 JS_ASSERT(exn < JSEXN_LIMIT);
994 return (JSProtoKey) (JSProto_Error + exn);
995 }
996
997 JSObject *
998 js_InitExceptionClasses(JSContext *cx, JSObject *obj)
999 {
1000 jsval roots[3];
1001 JSObject *obj_proto, *error_proto;
1002 jsval empty;
1003
1004 /*
1005 * If lazy class initialization occurs for any Error subclass, then all
1006 * classes are initialized, starting with Error. To avoid reentry and
1007 * redundant initialization, we must not pass a null proto parameter to
1008 * js_NewObject below, when called for the Error superclass. We need to
1009 * ensure that Object.prototype is the proto of Error.prototype.
1010 *
1011 * See the equivalent code to ensure that parent_proto is non-null when
1012 * JS_InitClass calls js_NewObject, in jsapi.c.
1013 */
1014 if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
1015 &obj_proto)) {
1016 return NULL;
1017 }
1018
1019 memset(roots, 0, sizeof(roots));
1020 JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
1021
1022 #ifdef __GNUC__
1023 error_proto = NULL; /* quell GCC overwarning */
1024 #endif
1025
1026 /* Initialize the prototypes first. */
1027 for (intN i = JSEXN_ERR; i != JSEXN_LIMIT; i++) {
1028 JSObject *proto;
1029 JSProtoKey protoKey;
1030 JSAtom *atom;
1031 JSFunction *fun;
1032
1033 /* Make the prototype for the current constructor name. */
1034 proto = js_NewObject(cx, &js_ErrorClass,
1035 (i != JSEXN_ERR) ? error_proto : obj_proto,
1036 obj, 0);
1037 if (!proto)
1038 return NULL;
1039 if (i == JSEXN_ERR) {
1040 error_proto = proto;
1041 roots[0] = OBJECT_TO_JSVAL(proto);
1042 } else {
1043 // We cannot share the root for error_proto and other prototypes
1044 // as error_proto must be rooted until the function returns.
1045 roots[1] = OBJECT_TO_JSVAL(proto);
1046 }
1047
1048 /* So exn_finalize knows whether to destroy private data. */
1049 STOBJ_SET_SLOT(proto, JSSLOT_PRIVATE, JSVAL_VOID);
1050
1051 /* Make a constructor function for the current name. */
1052 protoKey = GetExceptionProtoKey(i);
1053 atom = cx->runtime->atomState.classAtoms[protoKey];
1054 fun = js_DefineFunction(cx, obj, atom, Exception, 3, 0);
1055 if (!fun)
1056 return NULL;
1057 roots[2] = OBJECT_TO_JSVAL(FUN_OBJECT(fun));
1058
1059 /* Make this constructor make objects of class Exception. */
1060 FUN_CLASP(fun) = &js_ErrorClass;
1061
1062 /* Make the prototype and constructor links. */
1063 if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
1064 JSPROP_READONLY | JSPROP_PERMANENT)) {
1065 return NULL;
1066 }
1067
1068 /* Add the name property to the prototype. */
1069 if (!JS_DefineProperty(cx, proto, js_name_str, ATOM_KEY(atom),
1070 NULL, NULL, JSPROP_ENUMERATE)) {
1071 return NULL;
1072 }
1073
1074 /* Finally, stash the constructor for later uses. */
1075 if (!js_SetClassObject(cx, obj, protoKey, FUN_OBJECT(fun)))
1076 return NULL;
1077 }
1078
1079 /*
1080 * Set default values and add methods. We do it only for Error.prototype
1081 * as the rest of exceptions delegate to it.
1082 */
1083 empty = STRING_TO_JSVAL(cx->runtime->emptyString);
1084 if (!JS_DefineProperty(cx, error_proto, js_message_str, empty,
1085 NULL, NULL, JSPROP_ENUMERATE) ||
1086 !JS_DefineProperty(cx, error_proto, js_fileName_str, empty,
1087 NULL, NULL, JSPROP_ENUMERATE) ||
1088 !JS_DefineProperty(cx, error_proto, js_lineNumber_str, JSVAL_ZERO,
1089 NULL, NULL, JSPROP_ENUMERATE) ||
1090 !JS_DefineFunctions(cx, error_proto, exception_methods)) {
1091 return NULL;
1092 }
1093
1094 return error_proto;
1095 }
1096
1097 const JSErrorFormatString*
1098 js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
1099 const uintN errorNumber)
1100 {
1101 const JSErrorFormatString *errorString = NULL;
1102
1103 if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
1104 errorString = cx->localeCallbacks
1105 ->localeGetErrorMessage(userRef, locale, errorNumber);
1106 }
1107 if (!errorString)
1108 errorString = js_GetErrorMessage(userRef, locale, errorNumber);
1109 return errorString;
1110 }
1111
1112 #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
1113 /* For use below... get character strings for error name and exception name */
1114 static struct exnname { char *name; char *exception; } errortoexnname[] = {
1115 #define MSG_DEF(name, number, count, exception, format) \
1116 {#name, #exception},
1117 #include "js.msg"
1118 #undef MSG_DEF
1119 };
1120 #endif /* DEBUG */
1121
1122 JSBool
1123 js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
1124 {
1125 JSErrNum errorNumber;
1126 const JSErrorFormatString *errorString;
1127 JSExnType exn;
1128 jsval tv[4];
1129 JSTempValueRooter tvr;
1130 JSBool ok;
1131 JSObject *errProto, *errObject;
1132 JSString *messageStr, *filenameStr;
1133
1134 /*
1135 * Tell our caller to report immediately if this report is just a warning.
1136 */
1137 JS_ASSERT(reportp);
1138 if (JSREPORT_IS_WARNING(reportp->flags))
1139 return JS_FALSE;
1140
1141 /* Find the exception index associated with this error. */
1142 errorNumber = (JSErrNum) reportp->errorNumber;
1143 errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
1144 exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
1145 JS_ASSERT(exn < JSEXN_LIMIT);
1146
1147 #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
1148 /* Print the error name and the associated exception name to stderr */
1149 fprintf(stderr, "%s\t%s\n",
1150 errortoexnname[errorNumber].name,
1151 errortoexnname[errorNumber].exception);
1152 #endif
1153
1154 /*
1155 * Return false (no exception raised) if no exception is associated
1156 * with the given error number.
1157 */
1158 if (exn == JSEXN_NONE)
1159 return JS_FALSE;
1160
1161 /*
1162 * Prevent runaway recursion, via cx->generatingError. If an out-of-memory
1163 * error occurs, no exception object will be created, but we don't assume
1164 * that OOM is the only kind of error that subroutines of this function
1165 * called below might raise.
1166 */
1167 if (cx->generatingError)
1168 return JS_FALSE;
1169
1170 MUST_FLOW_THROUGH("out");
1171 cx->generatingError = JS_TRUE;
1172
1173 /* Protect the newly-created strings below from nesting GCs. */
1174 memset(tv, 0, sizeof tv);
1175 JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(tv), tv, &tvr);
1176
1177 /*
1178 * Try to get an appropriate prototype by looking up the corresponding
1179 * exception constructor name in the scope chain of the current context's
1180 * top stack frame, or in the global object if no frame is active.
1181 */
1182 ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(GetExceptionProtoKey(exn)),
1183 &errProto);
1184 if (!ok)
1185 goto out;
1186 tv[0] = OBJECT_TO_JSVAL(errProto);
1187
1188 errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0);
1189 if (!errObject) {
1190 ok = JS_FALSE;
1191 goto out;
1192 }
1193 tv[1] = OBJECT_TO_JSVAL(errObject);
1194
1195 messageStr = JS_NewStringCopyZ(cx, message);
1196 if (!messageStr) {
1197 ok = JS_FALSE;
1198 goto out;
1199 }
1200 tv[2] = STRING_TO_JSVAL(messageStr);
1201
1202 filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
1203 if (!filenameStr) {
1204 ok = JS_FALSE;
1205 goto out;
1206 }
1207 tv[3] = STRING_TO_JSVAL(filenameStr);
1208
1209 ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
1210 reportp->lineno, reportp);
1211 if (!ok)
1212 goto out;
1213
1214 JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
1215
1216 /* Flag the error report passed in to indicate an exception was raised. */
1217 reportp->flags |= JSREPORT_EXCEPTION;
1218
1219 out:
1220 JS_POP_TEMP_ROOT(cx, &tvr);
1221 cx->generatingError = JS_FALSE;
1222 return ok;
1223 }
1224
1225 JSBool
1226 js_ReportUncaughtException(JSContext *cx)
1227 {
1228 jsval exn;
1229 JSObject *exnObject;
1230 jsval roots[5];
1231 JSTempValueRooter tvr;
1232 JSErrorReport *reportp, report;
1233 JSString *str;
1234 const char *bytes;
1235 JSBool ok;
1236
1237 if (!JS_IsExceptionPending(cx))
1238 return JS_TRUE;
1239
1240 if (!JS_GetPendingException(cx, &exn))
1241 return JS_FALSE;
1242
1243 memset(roots, 0, sizeof roots);
1244 JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);
1245
1246 /*
1247 * Because js_ValueToString below could error and an exception object
1248 * could become unrooted, we must root exnObject. Later, if exnObject is
1249 * non-null, we need to root other intermediates, so allocate an operand
1250 * stack segment to protect all of these values.
1251 */
1252 if (JSVAL_IS_PRIMITIVE(exn)) {
1253 exnObject = NULL;
1254 } else {
1255 exnObject = JSVAL_TO_OBJECT(exn);
1256 roots[0] = exn;
1257 }
1258
1259 JS_ClearPendingException(cx);
1260 reportp = js_ErrorFromException(cx, exn);
1261
1262 /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */
1263 str = js_ValueToString(cx, exn);
1264 if (!str) {
1265 bytes = "unknown (can't convert to string)";
1266 } else {
1267 roots[1] = STRING_TO_JSVAL(str);
1268 bytes = js_GetStringBytes(cx, str);
1269 if (!bytes) {
1270 ok = JS_FALSE;
1271 goto out;
1272 }
1273 }
1274 ok = JS_TRUE;
1275
1276 if (!reportp &&
1277 exnObject &&
1278 OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) {
1279 const char *filename;
1280 uint32 lineno;
1281
1282 ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]);
1283 if (!ok)
1284 goto out;
1285 if (JSVAL_IS_STRING(roots[2])) {
1286 bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2]));
1287 if (!bytes) {
1288 ok = JS_FALSE;
1289 goto out;
1290 }
1291 }
1292
1293 ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]);
1294 if (!ok)
1295 goto out;
1296 str = js_ValueToString(cx, roots[3]);
1297 if (!str) {
1298 ok = JS_FALSE;
1299 goto out;
1300 }
1301 filename = StringToFilename(cx, str);
1302 if (!filename) {
1303 ok = JS_FALSE;
1304 goto out;
1305 }
1306
1307 ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]);
1308 if (!ok)
1309 goto out;
1310 lineno = js_ValueToECMAUint32 (cx, &roots[4]);
1311 ok = !JSVAL_IS_NULL(roots[4]);
1312 if (!ok)
1313 goto out;
1314
1315 reportp = &report;
1316 memset(&report, 0, sizeof report);
1317 report.filename = filename;
1318 report.lineno = (uintN) lineno;
1319 }
1320
1321 if (!reportp) {
1322 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1323 JSMSG_UNCAUGHT_EXCEPTION, bytes);
1324 } else {
1325 /* Flag the error as an exception. */
1326 reportp->flags |= JSREPORT_EXCEPTION;
1327
1328 /* Pass the exception object. */
1329 JS_SetPendingException(cx, exn);
1330 js_ReportErrorAgain(cx, bytes, reportp);
1331 JS_ClearPendingException(cx);
1332 }
1333
1334 out:
1335 JS_POP_TEMP_ROOT(cx, &tvr);
1336 return ok;
1337 }

  ViewVC Help
Powered by ViewVC 1.1.24