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

Contents of /trunk/js/jsexn.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.24