Code

fix include paths
[inkscape.git] / src / dom / js / jsexn.c
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 ***** */
40 /*
41  * JS standard exception implementation.
42  */
44 #include "jsstddef.h"
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsbit.h"
49 #include "jsutil.h" /* Added by JSIFY */
50 #include "jsprf.h"
51 #include "jsapi.h"
52 #include "jscntxt.h"
53 #include "jsconfig.h"
54 #include "jsexn.h"
55 #include "jsfun.h"
56 #include "jsinterp.h"
57 #include "jsopcode.h"
58 #include "jsnum.h"
59 #include "jsscript.h"
61 #if JS_HAS_ERROR_EXCEPTIONS
62 #if !JS_HAS_EXCEPTIONS
63 # error "JS_HAS_EXCEPTIONS must be defined to use JS_HAS_ERROR_EXCEPTIONS"
64 #endif
66 /* XXX consider adding rt->atomState.messageAtom */
67 static char js_message_str[]  = "message";
68 static char js_filename_str[] = "fileName";
69 static char js_lineno_str[]   = "lineNumber";
70 static char js_stack_str[]    = "stack";
72 /* Forward declarations for ExceptionClass's initializer. */
73 static JSBool
74 Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
76 static void
77 exn_finalize(JSContext *cx, JSObject *obj);
79 static JSClass ExceptionClass = {
80     "Error",
81     JSCLASS_HAS_PRIVATE,
82     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
83     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   exn_finalize,
84     NULL,             NULL,             NULL,             Exception,
85     NULL,             NULL,             NULL,             0
86 };
88 /*
89  * A copy of the JSErrorReport originally generated.
90  */
91 typedef struct JSExnPrivate {
92     JSErrorReport *errorReport;
93 } JSExnPrivate;
95 /*
96  * Undo all the damage done by exn_newPrivate.
97  */
98 static void
99 exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData)
101     JSErrorReport *report;
102     const jschar **args;
104     if (!privateData)
105         return;
106     report = privateData->errorReport;
107     if (report) {
108         if (report->uclinebuf)
109             JS_free(cx, (void *)report->uclinebuf);
110         if (report->filename)
111             JS_free(cx, (void *)report->filename);
112         if (report->ucmessage)
113             JS_free(cx, (void *)report->ucmessage);
114         if (report->messageArgs) {
115             args = report->messageArgs;
116             while (*args != NULL)
117                 JS_free(cx, (void *)*args++);
118             JS_free(cx, (void *)report->messageArgs);
119         }
120         JS_free(cx, report);
121     }
122     JS_free(cx, privateData);
125 /*
126  * Copy everything interesting about an error into allocated memory.
127  */
128 static JSExnPrivate *
129 exn_newPrivate(JSContext *cx, JSErrorReport *report)
131     intN i;
132     JSExnPrivate *newPrivate;
133     JSErrorReport *newReport;
134     size_t capacity;
136     newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate));
137     if (!newPrivate)
138         return NULL;
139     memset(newPrivate, 0, sizeof (JSExnPrivate));
141     /* Copy the error report */
142     newReport = (JSErrorReport *)JS_malloc(cx, sizeof (JSErrorReport));
143     if (!newReport)
144         goto error;
145     memset(newReport, 0, sizeof (JSErrorReport));
146     newPrivate->errorReport = newReport;
148     if (report->filename != NULL) {
149         newReport->filename = JS_strdup(cx, report->filename);
150         if (!newReport->filename)
151             goto error;
152     } else {
153         newReport->filename = NULL;
154     }
156     newReport->lineno = report->lineno;
158     /*
159      * We don't need to copy linebuf and tokenptr, because they
160      * point into the deflated string cache.  (currently?)
161      */
162     newReport->linebuf = report->linebuf;
163     newReport->tokenptr = report->tokenptr;
165     /*
166      * But we do need to copy uclinebuf, uctokenptr, because they're
167      * pointers into internal tokenstream structs, and may go away.
168      */
169     if (report->uclinebuf != NULL) {
170         capacity = js_strlen(report->uclinebuf) + 1;
171         newReport->uclinebuf =
172             (const jschar *)JS_malloc(cx, capacity * sizeof(jschar));
173         if (!newReport->uclinebuf)
174             goto error;
175         js_strncpy((jschar *)newReport->uclinebuf, report->uclinebuf, capacity);
176         newReport->uctokenptr = newReport->uclinebuf + (report->uctokenptr -
177                                                         report->uclinebuf);
178     } else {
179         newReport->uclinebuf = newReport->uctokenptr = NULL;
180     }
182     if (report->ucmessage != NULL) {
183         capacity = js_strlen(report->ucmessage) + 1;
184         newReport->ucmessage = (const jschar *)
185             JS_malloc(cx, capacity * sizeof(jschar));
186         if (!newReport->ucmessage)
187             goto error;
188         js_strncpy((jschar *)newReport->ucmessage, report->ucmessage, capacity);
190         if (report->messageArgs) {
191             for (i = 0; report->messageArgs[i] != NULL; i++)
192                 continue;
193             JS_ASSERT(i);
194             newReport->messageArgs =
195                 (const jschar **)JS_malloc(cx, (i + 1) * sizeof(jschar *));
196             if (!newReport->messageArgs)
197                 goto error;
198             for (i = 0; report->messageArgs[i] != NULL; i++) {
199                 capacity = js_strlen(report->messageArgs[i]) + 1;
200                 newReport->messageArgs[i] =
201                     (const jschar *)JS_malloc(cx, capacity * sizeof(jschar));
202                 if (!newReport->messageArgs[i])
203                     goto error;
204                 js_strncpy((jschar *)(newReport->messageArgs[i]),
205                            report->messageArgs[i], capacity);
206             }
207             newReport->messageArgs[i] = NULL;
208         } else {
209             newReport->messageArgs = NULL;
210         }
211     } else {
212         newReport->ucmessage = NULL;
213         newReport->messageArgs = NULL;
214     }
215     newReport->errorNumber = report->errorNumber;
217     /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
218     newReport->flags = report->flags;
220     return newPrivate;
221 error:
222     exn_destroyPrivate(cx, newPrivate);
223     return NULL;
226 static void
227 exn_finalize(JSContext *cx, JSObject *obj)
229     JSExnPrivate *privateData;
230     jsval privateValue;
232     privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
234     if (!JSVAL_IS_VOID(privateValue)) {
235         privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue);
236         if (privateData)
237             exn_destroyPrivate(cx, privateData);
238     }
241 JSErrorReport *
242 js_ErrorFromException(JSContext *cx, jsval exn)
244     JSObject *obj;
245     JSExnPrivate *privateData;
246     jsval privateValue;
248     if (JSVAL_IS_PRIMITIVE(exn))
249         return NULL;
250     obj = JSVAL_TO_OBJECT(exn);
251     if (OBJ_GET_CLASS(cx, obj) != &ExceptionClass)
252         return NULL;
253     privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
254     if (JSVAL_IS_VOID(privateValue))
255         return NULL;
256     privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue);
257     if (!privateData)
258         return NULL;
260     JS_ASSERT(privateData->errorReport);
261     return privateData->errorReport;
264 /*
265  * This must be kept in synch with the exceptions array below.
266  * XXX use a jsexn.tbl file a la jsopcode.tbl
267  */
268 typedef enum JSExnType {
269     JSEXN_NONE = -1,
270       JSEXN_ERR,
271         JSEXN_INTERNALERR,
272         JSEXN_EVALERR,
273         JSEXN_RANGEERR,
274         JSEXN_REFERENCEERR,
275         JSEXN_SYNTAXERR,
276         JSEXN_TYPEERR,
277         JSEXN_URIERR,
278         JSEXN_LIMIT
279 } JSExnType;
281 struct JSExnSpec {
282     int protoIndex;
283     const char *name;
284     JSNative native;
285 };
287 /*
288  * All *Error constructors share the same JSClass, ExceptionClass.  But each
289  * constructor function for an *Error class must have a distinct native 'call'
290  * function pointer, in order for instanceof to work properly across multiple
291  * standard class sets.  See jsfun.c:fun_hasInstance.
292  */
293 #define MAKE_EXCEPTION_CTOR(name)                                             \
294 const char js_##name##_str[] = #name;                                         \
295 static JSBool                                                                 \
296 name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)      \
297 {                                                                             \
298     return Exception(cx, obj, argc, argv, rval);                              \
301 MAKE_EXCEPTION_CTOR(Error)
302 MAKE_EXCEPTION_CTOR(InternalError)
303 MAKE_EXCEPTION_CTOR(EvalError)
304 MAKE_EXCEPTION_CTOR(RangeError)
305 MAKE_EXCEPTION_CTOR(ReferenceError)
306 MAKE_EXCEPTION_CTOR(SyntaxError)
307 MAKE_EXCEPTION_CTOR(TypeError)
308 MAKE_EXCEPTION_CTOR(URIError)
310 #undef MAKE_EXCEPTION_CTOR
312 static struct JSExnSpec exceptions[] = {
313     { JSEXN_NONE,       js_Error_str,           Error },
314     { JSEXN_ERR,        js_InternalError_str,   InternalError },
315     { JSEXN_ERR,        js_EvalError_str,       EvalError },
316     { JSEXN_ERR,        js_RangeError_str,      RangeError },
317     { JSEXN_ERR,        js_ReferenceError_str,  ReferenceError },
318     { JSEXN_ERR,        js_SyntaxError_str,     SyntaxError },
319     { JSEXN_ERR,        js_TypeError_str,       TypeError },
320     { JSEXN_ERR,        js_URIError_str,        URIError },
321     {0,NULL,NULL}
322 };
324 static JSBool
325 InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message,
326                     JSString *filename, uintN lineno)
328     JSCheckAccessOp checkAccess;
329     JSErrorReporter older;
330     JSExceptionState *state;
331     jschar *stackbuf;
332     size_t stacklen, stackmax;
333     JSStackFrame *fp;
334     jsval callerid, v;
335     JSBool ok;
336     JSString *argsrc, *stack;
337     uintN i, ulineno;
338     const char *cp;
339     char ulnbuf[11];
341     if (!JS_DefineProperty(cx, obj, js_message_str, STRING_TO_JSVAL(message),
342                            NULL, NULL, JSPROP_ENUMERATE)) {
343         return JS_FALSE;
344     }
346     if (!JS_DefineProperty(cx, obj, js_filename_str,
347                            STRING_TO_JSVAL(filename),
348                            NULL, NULL, JSPROP_ENUMERATE)) {
349         return JS_FALSE;
350     }
352     if (!JS_DefineProperty(cx, obj, js_lineno_str,
353                            INT_TO_JSVAL(lineno),
354                            NULL, NULL, JSPROP_ENUMERATE)) {
355         return JS_FALSE;
356     }
358     /*
359      * Set the 'stack' property.
360      *
361      * First, set aside any error reporter for cx and save its exception state
362      * so we can suppress any checkAccess failures.  Such failures should stop
363      * the backtrace procedure, not result in a failure of this constructor.
364      */
365     checkAccess = cx->runtime->checkObjectAccess;
366     if (checkAccess) {
367         older = JS_SetErrorReporter(cx, NULL);
368         state = JS_SaveExceptionState(cx);
369     }
370 #ifdef __GNUC__         /* suppress bogus gcc warnings */
371     else {
372         older = NULL;
373         state = NULL;
374     }
375 #endif
376     callerid = ATOM_KEY(cx->runtime->atomState.callerAtom);
378     /*
379      * Prepare to allocate a jschar buffer at stackbuf, where stacklen indexes
380      * the next free jschar slot, and with room for at most stackmax non-null
381      * jschars.  If stackbuf is non-null, it always contains an extra slot for
382      * the null terminator we'll store at the end, as a backstop.
383      *
384      * All early returns must goto done after this point, till the after-loop
385      * cleanup code has run!
386      */
387     stackbuf = NULL;
388     stacklen = stackmax = 0;
389     ok = JS_TRUE;
391 #define APPEND_CHAR_TO_STACK(c)                                               \
392     JS_BEGIN_MACRO                                                            \
393         if (stacklen == stackmax) {                                           \
394             void *ptr_;                                                       \
395             stackmax = stackmax ? 2 * stackmax : 64;                          \
396             ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar));   \
397             if (!ptr_) {                                                      \
398                 ok = JS_FALSE;                                                \
399                 goto done;                                                    \
400             }                                                                 \
401             stackbuf = ptr_;                                                  \
402         }                                                                     \
403         stackbuf[stacklen++] = (c);                                           \
404     JS_END_MACRO
406 #define APPEND_STRING_TO_STACK(str)                                           \
407     JS_BEGIN_MACRO                                                            \
408         JSString *str_ = str;                                                 \
409         size_t length_ = JSSTRING_LENGTH(str_);                               \
410         if (stacklen + length_ > stackmax) {                                  \
411             void *ptr_;                                                       \
412             stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_));            \
413             ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar));   \
414             if (!ptr_) {                                                      \
415                 ok = JS_FALSE;                                                \
416                 goto done;                                                    \
417             }                                                                 \
418             stackbuf = ptr_;                                                  \
419         }                                                                     \
420         js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_);       \
421         stacklen += length_;                                                  \
422     JS_END_MACRO
424     for (fp = cx->fp; fp; fp = fp->down) {
425         if (checkAccess) {
426             v = (fp->fun && fp->argv) ? fp->argv[-2] : JSVAL_NULL;
427             if (!JSVAL_IS_PRIMITIVE(v)) {
428                 ok = checkAccess(cx, fp->fun->object, callerid, JSACC_READ, &v);
429                 if (!ok) {
430                     ok = JS_TRUE;
431                     break;
432                 }
433             }
434         }
436         if (fp->fun) {
437             if (fp->fun->atom)
438                 APPEND_STRING_TO_STACK(ATOM_TO_STRING(fp->fun->atom));
440             APPEND_CHAR_TO_STACK('(');
441             for (i = 0; i < fp->argc; i++) {
442                 /* Avoid toSource bloat and fallibility for object types. */
443                 v = fp->argv[i];
444                 if (JSVAL_IS_PRIMITIVE(v)) {
445                     argsrc = js_ValueToSource(cx, v);
446                 } else if (JSVAL_IS_FUNCTION(cx, v)) {
447                     /* XXX Avoid function decompilation bloat for now. */
448                     argsrc = JS_GetFunctionId(JS_ValueToFunction(cx, v));
449                     if (!argsrc)
450                         argsrc = js_ValueToSource(cx, v);
451                 } else {
452                     /* XXX Avoid toString on objects, it takes too long and
453                            uses too much memory, for too many classes (see
454                            Mozilla bug 166743). */
455                     char buf[100];
456                     JS_snprintf(buf, sizeof buf, "[object %s]",
457                                 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name);
458                     argsrc = JS_NewStringCopyZ(cx, buf);
459                 }
460                 if (!argsrc) {
461                     ok = JS_FALSE;
462                     goto done;
463                 }
464                 if (i > 0)
465                     APPEND_CHAR_TO_STACK(',');
466                 APPEND_STRING_TO_STACK(argsrc);
467             }
468             APPEND_CHAR_TO_STACK(')');
469         }
471         APPEND_CHAR_TO_STACK('@');
472         if (fp->script && fp->script->filename) {
473             for (cp = fp->script->filename; *cp; cp++)
474                 APPEND_CHAR_TO_STACK(*cp);
475         }
476         APPEND_CHAR_TO_STACK(':');
477         if (fp->script && fp->pc) {
478             ulineno = js_PCToLineNumber(cx, fp->script, fp->pc);
479             JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", ulineno);
480             for (cp = ulnbuf; *cp; cp++)
481                 APPEND_CHAR_TO_STACK(*cp);
482         } else {
483             APPEND_CHAR_TO_STACK('0');
484         }
485         APPEND_CHAR_TO_STACK('\n');
486     }
488 #undef APPEND_CHAR_TO_STACK
489 #undef APPEND_STRING_TO_STACK
491 done:
492     if (checkAccess) {
493         if (ok)
494             JS_RestoreExceptionState(cx, state);
495         else
496             JS_DropExceptionState(cx, state);
497         JS_SetErrorReporter(cx, older);
498     }
499     if (!ok) {
500         JS_free(cx, stackbuf);
501         return JS_FALSE;
502     }
504     if (!stackbuf) {
505         stack = cx->runtime->emptyString;
506     } else {
507         /* NB: if stackbuf was allocated, it has room for the terminator. */
508         JS_ASSERT(stacklen <= stackmax);
509         if (stacklen < stackmax) {
510             /*
511              * Realloc can fail when shrinking on some FreeBSD versions, so
512              * don't use JS_realloc here; simply let the oversized allocation
513              * be owned by the string in that rare case.
514              */
515             void *shrunk = realloc(stackbuf, (stacklen+1) * sizeof(jschar));
516             if (shrunk)
517                 stackbuf = shrunk;
518         }
519         stackbuf[stacklen] = 0;
520         stack = js_NewString(cx, stackbuf, stacklen, 0);
521         if (!stack) {
522             JS_free(cx, stackbuf);
523             return JS_FALSE;
524         }
525     }
526     return JS_DefineProperty(cx, obj, js_stack_str,
527                              STRING_TO_JSVAL(stack),
528                              NULL, NULL, JSPROP_ENUMERATE);
531 static JSBool
532 Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
534     JSBool ok;
535     jsval pval;
536     int32 lineno;
537     JSString *message, *filename;
539     if (cx->creatingException)
540         return JS_FALSE;
541     cx->creatingException = JS_TRUE;
543     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
544         /*
545          * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
546          * called as functions, without operator new.  But as we do not give
547          * each constructor a distinct JSClass, whose .name member is used by
548          * js_NewObject to find the class prototype, we must get the class
549          * prototype ourselves.
550          */
551         ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]),
552                               (jsid)cx->runtime->atomState.classPrototypeAtom,
553                               &pval);
554         if (!ok)
555             goto out;
556         obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(pval), NULL);
557         if (!obj) {
558             ok = JS_FALSE;
559             goto out;
560         }
561         *rval = OBJECT_TO_JSVAL(obj);
562     }
564     /*
565      * If it's a new object of class Exception, then null out the private
566      * data so that the finalizer doesn't attempt to free it.
567      */
568     if (OBJ_GET_CLASS(cx, obj) == &ExceptionClass)
569         OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID);
571     /* Set the 'message' property. */
572     if (argc != 0) {
573         message = js_ValueToString(cx, argv[0]);
574         if (!message) {
575             ok = JS_FALSE;
576             goto out;
577         }
578         argv[0] = STRING_TO_JSVAL(message);
579     } else {
580         message = cx->runtime->emptyString;
581     }
583     /* Set the 'fileName' property. */
584     if (argc > 1) {
585         filename = js_ValueToString(cx, argv[1]);
586         if (!filename) {
587             ok = JS_FALSE;
588             goto out;
589         }
590         argv[1] = STRING_TO_JSVAL(filename);
591     } else {
592         filename = cx->runtime->emptyString;
593     }
595     /* Set the 'lineNumber' property. */
596     if (argc > 2) {
597         ok = js_ValueToInt32(cx, argv[2], &lineno);
598         if (!ok)
599             goto out;
600     } else {
601         lineno = 0;
602     }
604     ok = InitExceptionObject(cx, obj, message, filename, lineno);
606 out:
607     cx->creatingException = JS_FALSE;
608     return ok;
611 /*
612  * Convert to string.
613  *
614  * This method only uses JavaScript-modifiable properties name, message.  It
615  * is left to the host to check for private data and report filename and line
616  * number information along with this message.
617  */
618 static JSBool
619 exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
621     jsval v;
622     JSString *name, *message, *result;
623     jschar *chars, *cp;
624     size_t name_length, message_length, length;
626     if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v))
627         return JS_FALSE;
628     name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString;
630     if (!JS_GetProperty(cx, obj, js_message_str, &v))
631         return JS_FALSE;
632     message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v)
633                                  : cx->runtime->emptyString;
635     if (JSSTRING_LENGTH(message) != 0) {
636         name_length = JSSTRING_LENGTH(name);
637         message_length = JSSTRING_LENGTH(message);
638         length = (name_length ? name_length + 2 : 0) + message_length;
639         cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar));
640         if (!chars)
641             return JS_FALSE;
643         if (name_length) {
644             js_strncpy(cp, JSSTRING_CHARS(name), name_length);
645             cp += name_length;
646             *cp++ = ':'; *cp++ = ' ';
647         }
648         js_strncpy(cp, JSSTRING_CHARS(message), message_length);
649         cp += message_length;
650         *cp = 0;
652         result = js_NewString(cx, chars, length, 0);
653         if (!result) {
654             JS_free(cx, chars);
655             return JS_FALSE;
656         }
657     } else {
658         result = name;
659     }
661     *rval = STRING_TO_JSVAL(result);
662     return JS_TRUE;
665 #if JS_HAS_TOSOURCE
666 /*
667  * Return a string that may eval to something similar to the original object.
668  */
669 static JSBool
670 exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
672     jsval v;
673     JSString *name, *message, *filename, *lineno_as_str, *result;
674     int32 lineno;
675     size_t lineno_length, name_length, message_length, filename_length, length;
676     jschar *chars, *cp;
678     if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v))
679         return JS_FALSE;
680     name = js_ValueToString(cx, v);
681     if (!name)
682         return JS_FALSE;
684     if (!JS_GetProperty(cx, obj, js_message_str, &v) ||
685         !(message = js_ValueToSource(cx, v))) {
686         return JS_FALSE;
687     }
689     if (!JS_GetProperty(cx, obj, js_filename_str, &v) ||
690         !(filename = js_ValueToSource(cx, v))) {
691         return JS_FALSE;
692     }
694     if (!JS_GetProperty(cx, obj, js_lineno_str, &v) ||
695         !js_ValueToInt32 (cx, v, &lineno)) {
696         return JS_FALSE;
697     }
699     if (lineno != 0) {
700         if (!(lineno_as_str = js_ValueToString(cx, v))) {
701             return JS_FALSE;
702         }
703         lineno_length = JSSTRING_LENGTH(lineno_as_str);
704     } else {
705         lineno_as_str = NULL;
706         lineno_length = 0;
707     }
709     /* Magic 8, for the characters in ``(new ())''. */
710     name_length = JSSTRING_LENGTH(name);
711     message_length = JSSTRING_LENGTH(message);
712     length = 8 + name_length + message_length;
714     filename_length = JSSTRING_LENGTH(filename);
715     if (filename_length != 0) {
716         /* append filename as ``, {filename}'' */
717         length += 2 + filename_length;
718         if (lineno_as_str) {
719             /* append lineno as ``, {lineno_as_str}'' */
720             length += 2 + lineno_length;
721         }
722     } else {
723         if (lineno_as_str) {
724             /*
725              * no filename, but have line number,
726              * need to append ``, "", {lineno_as_str}''
727              */
728             length += 6 + lineno_length;
729         }
730     }
732     cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar));
733     if (!chars)
734         return JS_FALSE;
736     *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
737     js_strncpy(cp, JSSTRING_CHARS(name), name_length);
738     cp += name_length;
739     *cp++ = '(';
740     if (message_length != 0) {
741         js_strncpy(cp, JSSTRING_CHARS(message), message_length);
742         cp += message_length;
743     }
745     if (filename_length != 0) {
746         /* append filename as ``, {filename}'' */
747         *cp++ = ','; *cp++ = ' ';
748         js_strncpy(cp, JSSTRING_CHARS(filename), filename_length);
749         cp += filename_length;
750     } else {
751         if (lineno_as_str) {
752             /*
753              * no filename, but have line number,
754              * need to append ``, "", {lineno_as_str}''
755              */
756             *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
757         }
758     }
759     if (lineno_as_str) {
760         /* append lineno as ``, {lineno_as_str}'' */
761         *cp++ = ','; *cp++ = ' ';
762         js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length);
763         cp += lineno_length;
764     }
766     *cp++ = ')'; *cp++ = ')'; *cp = 0;
768     result = js_NewString(cx, chars, length, 0);
769     if (!result) {
770         JS_free(cx, chars);
771         return JS_FALSE;
772     }
773     *rval = STRING_TO_JSVAL(result);
774     return JS_TRUE;
776 #endif
778 static JSFunctionSpec exception_methods[] = {
779 #if JS_HAS_TOSOURCE
780     {js_toSource_str,   exn_toSource,           0,0,0},
781 #endif
782     {js_toString_str,   exn_toString,           0,0,0},
783     {0,0,0,0,0}
784 };
786 JSObject *
787 js_InitExceptionClasses(JSContext *cx, JSObject *obj)
789     int i;
790     JSObject *protos[JSEXN_LIMIT];
792     /* Initialize the prototypes first. */
793     for (i = 0; exceptions[i].name != 0; i++) {
794         JSAtom *atom;
795         JSFunction *fun;
796         JSString *nameString;
797         int protoIndex = exceptions[i].protoIndex;
799         /* Make the prototype for the current constructor name. */
800         protos[i] = js_NewObject(cx, &ExceptionClass,
801                                  (protoIndex != JSEXN_NONE)
802                                  ? protos[protoIndex]
803                                  : NULL,
804                                  obj);
805         if (!protos[i])
806             return NULL;
808         /* So exn_finalize knows whether to destroy private data. */
809         OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_VOID);
811         atom = js_Atomize(cx, exceptions[i].name, strlen(exceptions[i].name), 0);
812         if (!atom)
813             return NULL;
815         /* Make a constructor function for the current name. */
816         fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0);
817         if (!fun)
818             return NULL;
820         /* Make this constructor make objects of class Exception. */
821         fun->clasp = &ExceptionClass;
823         /* Make the prototype and constructor links. */
824         if (!js_SetClassPrototype(cx, fun->object, protos[i],
825                                   JSPROP_READONLY | JSPROP_PERMANENT)) {
826             return NULL;
827         }
829         /* proto bootstrap bit from JS_InitClass omitted. */
830         nameString = JS_NewStringCopyZ(cx, exceptions[i].name);
831         if (!nameString)
832             return NULL;
834         /* Add the name property to the prototype. */
835         if (!JS_DefineProperty(cx, protos[i], js_name_str,
836                                STRING_TO_JSVAL(nameString),
837                                NULL, NULL,
838                                JSPROP_ENUMERATE)) {
839             return NULL;
840         }
841     }
843     /*
844      * Add an empty message property.  (To Exception.prototype only,
845      * because this property will be the same for all the exception
846      * protos.)
847      */
848     if (!JS_DefineProperty(cx, protos[0], js_message_str,
849                            STRING_TO_JSVAL(cx->runtime->emptyString),
850                            NULL, NULL, JSPROP_ENUMERATE)) {
851         return NULL;
852     }
853     if (!JS_DefineProperty(cx, protos[0], js_filename_str,
854                            STRING_TO_JSVAL(cx->runtime->emptyString),
855                            NULL, NULL, JSPROP_ENUMERATE)) {
856         return NULL;
857     }
858     if (!JS_DefineProperty(cx, protos[0], js_lineno_str,
859                            INT_TO_JSVAL(0),
860                            NULL, NULL, JSPROP_ENUMERATE)) {
861         return NULL;
862     }
864     /*
865      * Add methods only to Exception.prototype, because ostensibly all
866      * exception types delegate to that.
867      */
868     if (!JS_DefineFunctions(cx, protos[0], exception_methods))
869         return NULL;
871     return protos[0];
874 static JSExnType errorToExceptionNum[] = {
875 #define MSG_DEF(name, number, count, exception, format) \
876     exception,
877 #include "js.msg"
878 #undef MSG_DEF
879 };
881 #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
882 /* For use below... get character strings for error name and exception name */
883 static struct exnname { char *name; char *exception; } errortoexnname[] = {
884 #define MSG_DEF(name, number, count, exception, format) \
885     {#name, #exception},
886 #include "js.msg"
887 #undef MSG_DEF
888 };
889 #endif /* DEBUG */
891 JSBool
892 js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
894     JSErrNum errorNumber;
895     JSExnType exn;
896     JSBool ok;
897     JSObject *errProto, *errObject;
898     JSString *messageStr, *filenameStr;
899     uintN lineno;
900     JSExnPrivate *privateData;
902     /*
903      * Tell our caller to report immediately if cx has no active frames, or if
904      * this report is just a warning.
905      */
906     JS_ASSERT(reportp);
907     if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags))
908         return JS_FALSE;
910     /* Find the exception index associated with this error. */
911     errorNumber = (JSErrNum) reportp->errorNumber;
912     exn = errorToExceptionNum[errorNumber];
913     JS_ASSERT(exn < JSEXN_LIMIT);
915 #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
916     /* Print the error name and the associated exception name to stderr */
917     fprintf(stderr, "%s\t%s\n",
918             errortoexnname[errorNumber].name,
919             errortoexnname[errorNumber].exception);
920 #endif
922     /*
923      * Return false (no exception raised) if no exception is associated
924      * with the given error number.
925      */
926     if (exn == JSEXN_NONE)
927         return JS_FALSE;
929     /*
930      * Prevent runaway recursion, just as the Exception native constructor
931      * must do, via cx->creatingException.  If an out-of-memory error occurs,
932      * no exception object will be created, but we don't assume that OOM is
933      * the only kind of error that subroutines of this function called below
934      * might raise.
935      */
936     if (cx->creatingException)
937         return JS_FALSE;
938     cx->creatingException = JS_TRUE;
940     /*
941      * Try to get an appropriate prototype by looking up the corresponding
942      * exception constructor name in the scope chain of the current context's
943      * top stack frame, or in the global object if no frame is active.
944      *
945      * XXXbe hack around JSCLASS_NEW_RESOLVE code in js_LookupProperty that
946      *       checks cx->fp, cx->fp->pc, and js_CodeSpec[*cx->fp->pc] in order
947      *       to compute resolve flags such as JSRESOLVE_ASSIGNING.  The bug
948      *       is that this "internal" js_GetClassPrototype call may trigger a
949      *       resolve of exceptions[exn].name if the global object uses a lazy
950      *       standard class resolver (see JS_ResolveStandardClass), but the
951      *       current frame and bytecode end up affecting the resolve flags.
952      */
953     {
954         JSStackFrame *fp = cx->fp;
955         jsbytecode *pc = NULL;
957         if (fp) {
958             pc = fp->pc;
959             fp->pc = NULL;
960         }
961         ok = js_GetClassPrototype(cx, exceptions[exn].name, &errProto);
962         if (pc)
963             fp->pc = pc;
964         if (!ok)
965             goto out;
966     }
968     errObject = js_NewObject(cx, &ExceptionClass, errProto, NULL);
969     if (!errObject) {
970         ok = JS_FALSE;
971         goto out;
972     }
974     /*
975      * Set the generated Exception object early, so it won't be GC'd by a last
976      * ditch attempt to collect garbage, or a GC that otherwise nests or races
977      * under any of the following calls.  If one of the following calls fails,
978      * it will overwrite this exception object with one of its own (except in
979      * case of OOM errors, of course).
980      */
981     JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
983     messageStr = JS_NewStringCopyZ(cx, message);
984     if (!messageStr) {
985         ok = JS_FALSE;
986         goto out;
987     }
989     if (reportp) {
990         filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
991         if (!filenameStr) {
992             ok = JS_FALSE;
993             goto out;
994         }
995         lineno = reportp->lineno;
996     } else {
997         filenameStr = cx->runtime->emptyString;
998         lineno = 0;
999     }
1000     ok = InitExceptionObject(cx, errObject, messageStr, filenameStr, lineno);
1001     if (!ok)
1002         goto out;
1004     /*
1005      * Construct a new copy of the error report struct, and store it in the
1006      * exception object's private data.  We can't use the error report struct
1007      * that was passed in, because it's stack-allocated, and also because it
1008      * may point to transient data in the JSTokenStream.
1009      */
1010     privateData = exn_newPrivate(cx, reportp);
1011     if (!privateData) {
1012         ok = JS_FALSE;
1013         goto out;
1014     }
1015     OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData));
1017     /* Flag the error report passed in to indicate an exception was raised. */
1018     reportp->flags |= JSREPORT_EXCEPTION;
1020 out:
1021     cx->creatingException = JS_FALSE;
1022     return ok;
1024 #endif /* JS_HAS_ERROR_EXCEPTIONS */
1026 #if JS_HAS_EXCEPTIONS
1028 JSBool
1029 js_ReportUncaughtException(JSContext *cx)
1031     JSObject *exnObject;
1032     JSString *str;
1033     jsval exn;
1034     JSErrorReport *reportp;
1035     const char *bytes;
1037     if (!JS_IsExceptionPending(cx))
1038         return JS_TRUE;
1040     if (!JS_GetPendingException(cx, &exn))
1041         return JS_FALSE;
1043     /*
1044      * Because js_ValueToString below could error and an exception object
1045      * could become unrooted, we must root exnObject.
1046      */
1047     if (JSVAL_IS_PRIMITIVE(exn)) {
1048         exnObject = NULL;
1049     } else {
1050         exnObject = JSVAL_TO_OBJECT(exn);
1051         if (!js_AddRoot(cx, &exnObject, "exn.report.root"))
1052             return JS_FALSE;
1053     }
1055 #if JS_HAS_ERROR_EXCEPTIONS
1056     reportp = js_ErrorFromException(cx, exn);
1057 #else
1058     reportp = NULL;
1059 #endif
1061     str = js_ValueToString(cx, exn);
1062     bytes = str ? js_GetStringBytes(str) : "null";
1064     if (reportp == NULL) {
1065         /*
1066          * XXXmccabe todo: Instead of doing this, synthesize an error report
1067          * struct that includes the filename, lineno where the exception was
1068          * originally thrown.
1069          */
1070         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1071                              JSMSG_UNCAUGHT_EXCEPTION, bytes);
1072     } else {
1073         /* Flag the error as an exception. */
1074         reportp->flags |= JSREPORT_EXCEPTION;
1075         js_ReportErrorAgain(cx, bytes, reportp);
1076     }
1078     if (exnObject != NULL)
1079         js_RemoveRoot(cx->runtime, &exnObject);
1080     JS_ClearPendingException(cx);
1081     return JS_TRUE;
1084 #endif /* JS_HAS_EXCEPTIONS */