1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS standard exception implementation.
50 #include "jsutil.h" /* Added by JSIFY */
54 #include "jsversion.h"
64 #include "jsstaticcheck.h"
66 /* Forward declarations for js_ErrorClass's initializer. */
68 Exception(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
71 exn_finalize(JSContext
*cx
, JSObject
*obj
);
74 exn_trace(JSTracer
*trc
, JSObject
*obj
);
77 exn_finalize(JSContext
*cx
, JSObject
*obj
);
80 exn_enumerate(JSContext
*cx
, JSObject
*obj
);
83 exn_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
86 JSClass js_ErrorClass
= {
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
96 typedef struct JSStackTraceElem
{
103 typedef struct JSExnPrivate
{
104 /* A copy of the JSErrorReport originally generated. */
105 JSErrorReport
*errorReport
;
110 JSStackTraceElem stackElems
[1];
114 StackTraceToString(JSContext
*cx
, JSExnPrivate
*priv
);
116 static JSErrorReport
*
117 CopyErrorReport(JSContext
*cx
, JSErrorReport
*report
)
120 * We use a single malloc block to make a deep copy of JSErrorReport with
121 * the following layout:
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.
132 JS_STATIC_ASSERT(sizeof(JSErrorReport
) % sizeof(const char *) == 0);
133 JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar
) == 0);
137 size_t uclinebufSize
;
138 size_t ucmessageSize
;
139 size_t i
, argsArraySize
, argsCopySize
, argSize
;
144 #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
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;
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
]);
158 /* Non-null messageArgs should have at least one non-null arg. */
160 argsArraySize
= (i
+ 1) * sizeof(const jschar
*);
165 * The mallocSize can not overflow since it represents the sum of the
166 * sizes of already allocated objects.
168 mallocSize
= sizeof(JSErrorReport
) + argsArraySize
+ argsCopySize
+
169 ucmessageSize
+ uclinebufSize
+ linebufSize
+ filenameSize
;
170 cursor
= (uint8
*)JS_malloc(cx
, mallocSize
);
174 copy
= (JSErrorReport
*)cursor
;
175 memset(cursor
, 0, sizeof(JSErrorReport
));
176 cursor
+= sizeof(JSErrorReport
);
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
);
187 copy
->messageArgs
[i
] = NULL
;
188 JS_ASSERT(cursor
== (uint8
*)copy
->messageArgs
[0] + argsCopySize
);
191 if (report
->ucmessage
) {
192 copy
->ucmessage
= (const jschar
*)cursor
;
193 memcpy(cursor
, report
->ucmessage
, ucmessageSize
);
194 cursor
+= ucmessageSize
;
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
-
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
-
217 if (report
->filename
) {
218 copy
->filename
= (const char *)cursor
;
219 memcpy(cursor
, report
->filename
, filenameSize
);
221 JS_ASSERT(cursor
+ filenameSize
== (uint8
*)copy
+ mallocSize
);
223 /* Copy non-pointer members. */
224 copy
->lineno
= report
->lineno
;
225 copy
->errorNumber
= report
->errorNumber
;
227 /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
228 copy
->flags
= report
->flags
;
235 GetStackTraceValueBuffer(JSExnPrivate
*priv
)
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.
243 JS_STATIC_ASSERT(sizeof(JSStackTraceElem
) % sizeof(jsval
) == 0);
245 return (jsval
*)(priv
->stackElems
+ priv
->stackDepth
);
249 InitExnPrivate(JSContext
*cx
, JSObject
*exnObject
, JSString
*message
,
250 JSString
*filename
, uintN lineno
, JSErrorReport
*report
)
252 JSSecurityCallbacks
*callbacks
;
253 JSCheckAccessOp checkAccess
;
254 JSErrorReporter older
;
255 JSExceptionState
*state
;
257 JSStackFrame
*fp
, *fpstop
;
258 size_t stackDepth
, valueCount
, size
;
261 JSStackTraceElem
*elem
;
264 JS_ASSERT(OBJ_GET_CLASS(cx
, exnObject
) == &js_ErrorClass
);
267 * Prepare stack trace data.
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.
273 callbacks
= JS_GetSecurityCallbacks(cx
);
274 checkAccess
= callbacks
275 ? callbacks
->checkObjectAccess
277 older
= JS_SetErrorReporter(cx
, NULL
);
278 state
= JS_SaveExceptionState(cx
);
280 callerid
= ATOM_KEY(cx
->runtime
->atomState
.callerAtom
);
283 for (fp
= js_GetTopStackFrame(cx
); fp
; fp
= fp
->down
) {
284 if (fp
->fun
&& fp
->argv
) {
287 !checkAccess(cx
, fp
->callee
, callerid
, JSACC_READ
, &v
)) {
290 valueCount
+= fp
->argc
;
294 JS_RestoreExceptionState(cx
, state
);
295 JS_SetErrorReporter(cx
, older
);
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
);
304 js_ReportAllocationOverflow(cx
);
307 priv
= (JSExnPrivate
*)JS_malloc(cx
, size
);
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
316 priv
->errorReport
= NULL
;
317 priv
->message
= message
;
318 priv
->filename
= filename
;
319 priv
->lineno
= lineno
;
320 priv
->stackDepth
= stackDepth
;
322 values
= GetStackTraceValueBuffer(priv
);
323 elem
= priv
->stackElems
;
324 for (fp
= js_GetTopStackFrame(cx
); fp
!= fpstop
; fp
= fp
->down
) {
326 elem
->funName
= NULL
;
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
));
337 elem
->filename
= NULL
;
339 elem
->filename
= fp
->script
->filename
;
341 elem
->ulineno
= js_FramePCToLineNumber(cx
, fp
);
345 JS_ASSERT(priv
->stackElems
+ stackDepth
== elem
);
346 JS_ASSERT(GetStackTraceValueBuffer(priv
) + valueCount
== values
);
348 STOBJ_SET_SLOT(exnObject
, JSSLOT_PRIVATE
, PRIVATE_TO_JSVAL(priv
));
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
357 priv
->errorReport
= CopyErrorReport(cx
, report
);
358 if (!priv
->errorReport
) {
359 /* The finalizer realeases priv since it is in the private slot. */
367 static JSExnPrivate
*
368 GetExnPrivate(JSContext
*cx
, JSObject
*obj
)
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
))
377 priv
= (JSExnPrivate
*)JSVAL_TO_PRIVATE(privateValue
);
383 exn_trace(JSTracer
*trc
, JSObject
*obj
)
386 JSStackTraceElem
*elem
;
390 priv
= GetExnPrivate(trc
->context
, obj
);
393 JS_CALL_STRING_TRACER(trc
, priv
->message
, "exception message");
395 JS_CALL_STRING_TRACER(trc
, priv
->filename
, "exception filename");
397 elem
= priv
->stackElems
;
398 for (vcount
= i
= 0; i
!= priv
->stackDepth
; ++i
, ++elem
) {
400 JS_CALL_STRING_TRACER(trc
, elem
->funName
,
401 "stack trace function name");
403 if (IS_GC_MARKING_TRACER(trc
) && elem
->filename
)
404 js_MarkScriptFilename(elem
->filename
);
405 vcount
+= elem
->argc
;
407 vp
= GetStackTraceValueBuffer(priv
);
408 for (i
= 0; i
!= vcount
; ++i
, ++vp
) {
410 JS_CALL_VALUE_TRACER(trc
, v
, "stack trace argument");
416 exn_finalize(JSContext
*cx
, JSObject
*obj
)
420 priv
= GetExnPrivate(cx
, obj
);
422 if (priv
->errorReport
)
423 JS_free(cx
, priv
->errorReport
);
429 exn_enumerate(JSContext
*cx
, JSObject
*obj
)
431 JSAtomState
*atomState
;
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
),
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
))
451 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
457 exn_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
468 priv
= GetExnPrivate(cx
, obj
);
469 if (priv
&& JSVAL_IS_STRING(id
)) {
470 str
= JSVAL_TO_STRING(id
);
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
);
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
);
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
);
493 atom
= cx
->runtime
->atomState
.stackAtom
;
494 if (str
== ATOM_TO_STRING(atom
)) {
495 stack
= StackTraceToString(cx
, priv
);
499 /* Allow to GC all things that were used to build stack trace. */
500 priv
->stackDepth
= 0;
502 v
= STRING_TO_JSVAL(stack
);
509 if (!JS_DefineProperty(cx
, obj
, prop
, v
, NULL
, NULL
, JSPROP_ENUMERATE
))
516 js_ErrorFromException(JSContext
*cx
, jsval exn
)
521 if (JSVAL_IS_PRIMITIVE(exn
))
523 obj
= JSVAL_TO_OBJECT(exn
);
524 if (OBJ_GET_CLASS(cx
, obj
) != &js_ErrorClass
)
526 priv
= GetExnPrivate(cx
, obj
);
529 return priv
->errorReport
;
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.
545 #define MAKE_EXCEPTION_CTOR(name) \
547 name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \
549 return Exception(cx, obj, argc, argv, rval); \
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
)
561 #undef MAKE_EXCEPTION_CTOR
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
}
576 ValueToShortSource(JSContext
*cx
, jsval v
)
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
)) {
585 * XXX Avoid function decompilation bloat for now.
587 str
= JS_GetFunctionId(JS_ValueToFunction(cx
, v
));
588 if (!str
&& !(str
= js_ValueToSource(cx
, v
))) {
590 * Continue to soldier on if the function couldn't be
591 * converted into a string.
593 JS_ClearPendingException(cx
);
594 str
= JS_NewStringCopyZ(cx
, "[unknown function]");
598 * XXX Avoid toString on objects, it takes too long and uses too much
599 * memory, for too many classes (see Mozilla bug 166743).
602 JS_snprintf(buf
, sizeof buf
, "[object %s]",
603 OBJ_GET_CLASS(cx
, JSVAL_TO_OBJECT(v
))->name
);
604 str
= JS_NewStringCopyZ(cx
, buf
);
610 StackTraceToString(JSContext
*cx
, JSExnPrivate
*priv
)
613 size_t stacklen
, stackmax
;
614 JSStackTraceElem
*elem
, *endElem
;
621 /* After this point, failing control flow must goto bad. */
623 stacklen
= stackmax
= 0;
625 /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
626 #define STACK_LENGTH_LIMIT JS_BIT(20)
628 #define APPEND_CHAR_TO_STACK(c) \
630 if (stacklen == stackmax) { \
632 if (stackmax >= STACK_LENGTH_LIMIT) \
634 stackmax = stackmax ? 2 * stackmax : 64; \
635 ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
638 stackbuf = (jschar *) ptr_; \
640 stackbuf[stacklen++] = (c); \
643 #define APPEND_STRING_TO_STACK(str) \
645 JSString *str_ = str; \
649 JSSTRING_CHARS_AND_LENGTH(str_, chars_, length_); \
650 if (length_ > stackmax - stacklen) { \
652 if (stackmax >= STACK_LENGTH_LIMIT || \
653 length_ >= STACK_LENGTH_LIMIT - stacklen) { \
656 stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \
657 ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
660 stackbuf = (jschar *) ptr_; \
662 js_strncpy(stackbuf + stacklen, chars_, length_); \
663 stacklen += length_; \
666 values
= GetStackTraceValueBuffer(priv
);
667 elem
= priv
->stackElems
;
668 for (endElem
= elem
+ priv
->stackDepth
; elem
!= endElem
; elem
++) {
670 APPEND_STRING_TO_STACK(elem
->funName
);
671 APPEND_CHAR_TO_STACK('(');
672 for (i
= 0; i
!= elem
->argc
; i
++, values
++) {
674 APPEND_CHAR_TO_STACK(',');
675 str
= ValueToShortSource(cx
, *values
);
678 APPEND_STRING_TO_STACK(str
);
680 APPEND_CHAR_TO_STACK(')');
682 APPEND_CHAR_TO_STACK('@');
683 if (elem
->filename
) {
684 for (cp
= elem
->filename
; *cp
; cp
++)
685 APPEND_CHAR_TO_STACK(*cp
);
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');
693 #undef APPEND_CHAR_TO_STACK
694 #undef APPEND_STRING_TO_STACK
695 #undef STACK_LENGTH_LIMIT
699 JS_ASSERT(!stackbuf
);
700 return cx
->runtime
->emptyString
;
702 if (stacklen
< stackmax
) {
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.
708 void *shrunk
= JS_realloc(cx
, stackbuf
, (stacklen
+1) * sizeof(jschar
));
710 stackbuf
= (jschar
*) shrunk
;
713 stackbuf
[stacklen
] = 0;
714 str
= js_NewString(cx
, stackbuf
, stacklen
);
720 JS_free(cx
, stackbuf
);
724 /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
725 with these two functions. */
727 FilenameToString(JSContext
*cx
, const char *filename
)
729 return JS_NewStringCopyZ(cx
, filename
);
733 StringToFilename(JSContext
*cx
, JSString
*str
)
735 return js_GetStringBytes(cx
, str
);
739 Exception(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
742 JSString
*message
, *filename
;
745 if (!JS_IsConstructing(cx
)) {
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.
753 if (!OBJ_GET_PROPERTY(cx
, JSVAL_TO_OBJECT(argv
[-2]),
754 ATOM_TO_JSID(cx
->runtime
->atomState
755 .classPrototypeAtom
),
758 obj
= js_NewObject(cx
, &js_ErrorClass
, JSVAL_TO_OBJECT(*rval
), NULL
, 0);
761 *rval
= OBJECT_TO_JSVAL(obj
);
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.
768 if (OBJ_GET_CLASS(cx
, obj
) == &js_ErrorClass
)
769 STOBJ_SET_SLOT(obj
, JSSLOT_PRIVATE
, JSVAL_VOID
);
771 /* Set the 'message' property. */
773 message
= js_ValueToString(cx
, argv
[0]);
776 argv
[0] = STRING_TO_JSVAL(message
);
778 message
= cx
->runtime
->emptyString
;
781 /* Set the 'fileName' property. */
783 filename
= js_ValueToString(cx
, argv
[1]);
786 argv
[1] = STRING_TO_JSVAL(filename
);
789 fp
= js_GetScriptedCaller(cx
, NULL
);
791 filename
= FilenameToString(cx
, fp
->script
->filename
);
795 filename
= cx
->runtime
->emptyString
;
799 /* Set the 'lineNumber' property. */
801 lineno
= js_ValueToECMAUint32(cx
, &argv
[2]);
802 if (JSVAL_IS_NULL(argv
[2]))
806 fp
= js_GetScriptedCaller(cx
, NULL
);
807 lineno
= (fp
&& fp
->regs
) ? js_FramePCToLineNumber(cx
, fp
) : 0;
810 return (OBJ_GET_CLASS(cx
, obj
) != &js_ErrorClass
) ||
811 InitExnPrivate(cx
, obj
, message
, filename
, lineno
, NULL
);
817 * This method only uses JavaScript-modifiable properties name, message. It
818 * is left to the host to check for private data and report filename and line
819 * number information along with this message.
822 exn_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
826 JSString
*name
, *message
, *result
;
828 size_t name_length
, message_length
, length
;
830 obj
= JS_THIS_OBJECT(cx
, vp
);
832 !OBJ_GET_PROPERTY(cx
, obj
,
833 ATOM_TO_JSID(cx
->runtime
->atomState
.nameAtom
),
837 name
= JSVAL_IS_STRING(v
) ? JSVAL_TO_STRING(v
) : cx
->runtime
->emptyString
;
838 *vp
= STRING_TO_JSVAL(name
);
840 if (!JS_GetProperty(cx
, obj
, js_message_str
, &v
))
842 message
= JSVAL_IS_STRING(v
) ? JSVAL_TO_STRING(v
)
843 : cx
->runtime
->emptyString
;
845 if (JSSTRING_LENGTH(message
) != 0) {
846 name_length
= JSSTRING_LENGTH(name
);
847 message_length
= JSSTRING_LENGTH(message
);
848 length
= (name_length
? name_length
+ 2 : 0) + message_length
;
849 cp
= chars
= (jschar
*) JS_malloc(cx
, (length
+ 1) * sizeof(jschar
));
854 js_strncpy(cp
, JSSTRING_CHARS(name
), name_length
);
856 *cp
++ = ':'; *cp
++ = ' ';
858 js_strncpy(cp
, JSSTRING_CHARS(message
), message_length
);
859 cp
+= message_length
;
862 result
= js_NewString(cx
, chars
, length
);
871 *vp
= STRING_TO_JSVAL(result
);
877 * Return a string that may eval to something similar to the original object.
880 exn_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
883 JSString
*name
, *message
, *filename
, *lineno_as_str
, *result
;
884 jsval localroots
[3] = {JSVAL_NULL
, JSVAL_NULL
, JSVAL_NULL
};
885 JSTempValueRooter tvr
;
888 size_t lineno_length
, name_length
, message_length
, filename_length
, length
;
891 obj
= JS_THIS_OBJECT(cx
, vp
);
893 !OBJ_GET_PROPERTY(cx
, obj
,
894 ATOM_TO_JSID(cx
->runtime
->atomState
.nameAtom
),
898 name
= js_ValueToString(cx
, *vp
);
901 *vp
= STRING_TO_JSVAL(name
);
903 MUST_FLOW_THROUGH("out");
904 JS_PUSH_TEMP_ROOT(cx
, 3, localroots
, &tvr
);
907 message
= filename
= NULL
;
909 ok
= JS_GetProperty(cx
, obj
, js_message_str
, &localroots
[0]) &&
910 (message
= js_ValueToSource(cx
, localroots
[0]));
913 localroots
[0] = STRING_TO_JSVAL(message
);
915 ok
= JS_GetProperty(cx
, obj
, js_fileName_str
, &localroots
[1]) &&
916 (filename
= js_ValueToSource(cx
, localroots
[1]));
919 localroots
[1] = STRING_TO_JSVAL(filename
);
921 ok
= JS_GetProperty(cx
, obj
, js_lineNumber_str
, &localroots
[2]);
924 lineno
= js_ValueToECMAUint32 (cx
, &localroots
[2]);
925 ok
= !JSVAL_IS_NULL(localroots
[2]);
930 lineno_as_str
= js_ValueToString(cx
, localroots
[2]);
931 if (!lineno_as_str
) {
935 lineno_length
= JSSTRING_LENGTH(lineno_as_str
);
937 lineno_as_str
= NULL
;
941 /* Magic 8, for the characters in ``(new ())''. */
942 name_length
= JSSTRING_LENGTH(name
);
943 message_length
= JSSTRING_LENGTH(message
);
944 length
= 8 + name_length
+ message_length
;
946 filename_length
= JSSTRING_LENGTH(filename
);
947 if (filename_length
!= 0) {
948 /* append filename as ``, {filename}'' */
949 length
+= 2 + filename_length
;
951 /* append lineno as ``, {lineno_as_str}'' */
952 length
+= 2 + lineno_length
;
957 * no filename, but have line number,
958 * need to append ``, "", {lineno_as_str}''
960 length
+= 6 + lineno_length
;
964 cp
= chars
= (jschar
*) JS_malloc(cx
, (length
+ 1) * sizeof(jschar
));
970 *cp
++ = '('; *cp
++ = 'n'; *cp
++ = 'e'; *cp
++ = 'w'; *cp
++ = ' ';
971 js_strncpy(cp
, JSSTRING_CHARS(name
), name_length
);
974 if (message_length
!= 0) {
975 js_strncpy(cp
, JSSTRING_CHARS(message
), message_length
);
976 cp
+= message_length
;
979 if (filename_length
!= 0) {
980 /* append filename as ``, {filename}'' */
981 *cp
++ = ','; *cp
++ = ' ';
982 js_strncpy(cp
, JSSTRING_CHARS(filename
), filename_length
);
983 cp
+= filename_length
;
987 * no filename, but have line number,
988 * need to append ``, "", {lineno_as_str}''
990 *cp
++ = ','; *cp
++ = ' '; *cp
++ = '"'; *cp
++ = '"';
994 /* append lineno as ``, {lineno_as_str}'' */
995 *cp
++ = ','; *cp
++ = ' ';
996 js_strncpy(cp
, JSSTRING_CHARS(lineno_as_str
), lineno_length
);
1000 *cp
++ = ')'; *cp
++ = ')'; *cp
= 0;
1002 result
= js_NewString(cx
, chars
, length
);
1008 *vp
= STRING_TO_JSVAL(result
);
1012 JS_POP_TEMP_ROOT(cx
, &tvr
);
1017 static JSFunctionSpec exception_methods
[] = {
1019 JS_FN(js_toSource_str
, exn_toSource
, 0,0),
1021 JS_FN(js_toString_str
, exn_toString
, 0,0),
1026 js_InitExceptionClasses(JSContext
*cx
, JSObject
*obj
)
1028 JSObject
*obj_proto
, *protos
[JSEXN_LIMIT
];
1032 * If lazy class initialization occurs for any Error subclass, then all
1033 * classes are initialized, starting with Error. To avoid reentry and
1034 * redundant initialization, we must not pass a null proto parameter to
1035 * js_NewObject below, when called for the Error superclass. We need to
1036 * ensure that Object.prototype is the proto of Error.prototype.
1038 * See the equivalent code to ensure that parent_proto is non-null when
1039 * JS_InitClass calls js_NewObject, in jsapi.c.
1041 if (!js_GetClassPrototype(cx
, obj
, INT_TO_JSID(JSProto_Object
),
1046 if (!js_EnterLocalRootScope(cx
))
1049 /* Initialize the prototypes first. */
1050 for (i
= 0; exceptions
[i
].name
!= 0; i
++) {
1053 JSString
*nameString
;
1054 int protoIndex
= exceptions
[i
].protoIndex
;
1056 /* Make the prototype for the current constructor name. */
1057 protos
[i
] = js_NewObject(cx
, &js_ErrorClass
,
1058 (protoIndex
!= JSEXN_NONE
)
1059 ? protos
[protoIndex
]
1065 /* So exn_finalize knows whether to destroy private data. */
1066 STOBJ_SET_SLOT(protos
[i
], JSSLOT_PRIVATE
, JSVAL_VOID
);
1068 /* Make a constructor function for the current name. */
1069 atom
= cx
->runtime
->atomState
.classAtoms
[exceptions
[i
].key
];
1070 fun
= js_DefineFunction(cx
, obj
, atom
, exceptions
[i
].native
, 3, 0);
1074 /* Make this constructor make objects of class Exception. */
1075 FUN_CLASP(fun
) = &js_ErrorClass
;
1077 /* Make the prototype and constructor links. */
1078 if (!js_SetClassPrototype(cx
, FUN_OBJECT(fun
), protos
[i
],
1079 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
1083 /* proto bootstrap bit from JS_InitClass omitted. */
1084 nameString
= JS_NewStringCopyZ(cx
, exceptions
[i
].name
);
1088 /* Add the name property to the prototype. */
1089 if (!JS_DefineProperty(cx
, protos
[i
], js_name_str
,
1090 STRING_TO_JSVAL(nameString
),
1092 JSPROP_ENUMERATE
)) {
1096 /* Finally, stash the constructor for later uses. */
1097 if (!js_SetClassObject(cx
, obj
, exceptions
[i
].key
, FUN_OBJECT(fun
)))
1101 js_LeaveLocalRootScope(cx
);
1102 if (exceptions
[i
].name
)
1106 * Add an empty message property. (To Exception.prototype only,
1107 * because this property will be the same for all the exception
1110 if (!JS_DefineProperty(cx
, protos
[0], js_message_str
,
1111 STRING_TO_JSVAL(cx
->runtime
->emptyString
),
1112 NULL
, NULL
, JSPROP_ENUMERATE
)) {
1115 if (!JS_DefineProperty(cx
, protos
[0], js_fileName_str
,
1116 STRING_TO_JSVAL(cx
->runtime
->emptyString
),
1117 NULL
, NULL
, JSPROP_ENUMERATE
)) {
1120 if (!JS_DefineProperty(cx
, protos
[0], js_lineNumber_str
,
1122 NULL
, NULL
, JSPROP_ENUMERATE
)) {
1127 * Add methods only to Exception.prototype, because ostensibly all
1128 * exception types delegate to that.
1130 if (!JS_DefineFunctions(cx
, protos
[0], exception_methods
))
1136 const JSErrorFormatString
*
1137 js_GetLocalizedErrorMessage(JSContext
* cx
, void *userRef
, const char *locale
,
1138 const uintN errorNumber
)
1140 const JSErrorFormatString
*errorString
= NULL
;
1142 if (cx
->localeCallbacks
&& cx
->localeCallbacks
->localeGetErrorMessage
) {
1143 errorString
= cx
->localeCallbacks
1144 ->localeGetErrorMessage(userRef
, locale
, errorNumber
);
1147 errorString
= js_GetErrorMessage(userRef
, locale
, errorNumber
);
1151 #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
1152 /* For use below... get character strings for error name and exception name */
1153 static struct exnname
{ char *name
; char *exception
; } errortoexnname
[] = {
1154 #define MSG_DEF(name, number, count, exception, format) \
1155 {#name, #exception},
1162 js_ErrorToException(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
)
1164 JSErrNum errorNumber
;
1165 const JSErrorFormatString
*errorString
;
1168 JSTempValueRooter tvr
;
1170 JSObject
*errProto
, *errObject
;
1171 JSString
*messageStr
, *filenameStr
;
1174 * Tell our caller to report immediately if this report is just a warning.
1177 if (JSREPORT_IS_WARNING(reportp
->flags
))
1180 /* Find the exception index associated with this error. */
1181 errorNumber
= (JSErrNum
) reportp
->errorNumber
;
1182 errorString
= js_GetLocalizedErrorMessage(cx
, NULL
, NULL
, errorNumber
);
1183 exn
= errorString
? (JSExnType
) errorString
->exnType
: JSEXN_NONE
;
1184 JS_ASSERT(exn
< JSEXN_LIMIT
);
1186 #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
1187 /* Print the error name and the associated exception name to stderr */
1188 fprintf(stderr
, "%s\t%s\n",
1189 errortoexnname
[errorNumber
].name
,
1190 errortoexnname
[errorNumber
].exception
);
1194 * Return false (no exception raised) if no exception is associated
1195 * with the given error number.
1197 if (exn
== JSEXN_NONE
)
1201 * Prevent runaway recursion, via cx->generatingError. If an out-of-memory
1202 * error occurs, no exception object will be created, but we don't assume
1203 * that OOM is the only kind of error that subroutines of this function
1204 * called below might raise.
1206 if (cx
->generatingError
)
1209 MUST_FLOW_THROUGH("out");
1210 cx
->generatingError
= JS_TRUE
;
1212 /* Protect the newly-created strings below from nesting GCs. */
1213 memset(tv
, 0, sizeof tv
);
1214 JS_PUSH_TEMP_ROOT(cx
, JS_ARRAY_LENGTH(tv
), tv
, &tvr
);
1217 * Try to get an appropriate prototype by looking up the corresponding
1218 * exception constructor name in the scope chain of the current context's
1219 * top stack frame, or in the global object if no frame is active.
1221 ok
= js_GetClassPrototype(cx
, NULL
, INT_TO_JSID(exceptions
[exn
].key
),
1225 tv
[0] = OBJECT_TO_JSVAL(errProto
);
1227 errObject
= js_NewObject(cx
, &js_ErrorClass
, errProto
, NULL
, 0);
1232 tv
[1] = OBJECT_TO_JSVAL(errObject
);
1234 messageStr
= JS_NewStringCopyZ(cx
, message
);
1239 tv
[2] = STRING_TO_JSVAL(messageStr
);
1241 filenameStr
= JS_NewStringCopyZ(cx
, reportp
->filename
);
1246 tv
[3] = STRING_TO_JSVAL(filenameStr
);
1248 ok
= InitExnPrivate(cx
, errObject
, messageStr
, filenameStr
,
1249 reportp
->lineno
, reportp
);
1253 JS_SetPendingException(cx
, OBJECT_TO_JSVAL(errObject
));
1255 /* Flag the error report passed in to indicate an exception was raised. */
1256 reportp
->flags
|= JSREPORT_EXCEPTION
;
1259 JS_POP_TEMP_ROOT(cx
, &tvr
);
1260 cx
->generatingError
= JS_FALSE
;
1265 js_ReportUncaughtException(JSContext
*cx
)
1268 JSObject
*exnObject
;
1270 JSTempValueRooter tvr
;
1271 JSErrorReport
*reportp
, report
;
1276 if (!JS_IsExceptionPending(cx
))
1279 if (!JS_GetPendingException(cx
, &exn
))
1282 memset(roots
, 0, sizeof roots
);
1283 JS_PUSH_TEMP_ROOT(cx
, JS_ARRAY_LENGTH(roots
), roots
, &tvr
);
1286 * Because js_ValueToString below could error and an exception object
1287 * could become unrooted, we must root exnObject. Later, if exnObject is
1288 * non-null, we need to root other intermediates, so allocate an operand
1289 * stack segment to protect all of these values.
1291 if (JSVAL_IS_PRIMITIVE(exn
)) {
1294 exnObject
= JSVAL_TO_OBJECT(exn
);
1298 JS_ClearPendingException(cx
);
1299 reportp
= js_ErrorFromException(cx
, exn
);
1301 /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */
1302 str
= js_ValueToString(cx
, exn
);
1304 bytes
= "unknown (can't convert to string)";
1306 roots
[1] = STRING_TO_JSVAL(str
);
1307 bytes
= js_GetStringBytes(cx
, str
);
1317 OBJ_GET_CLASS(cx
, exnObject
) == &js_ErrorClass
) {
1318 const char *filename
;
1321 ok
= JS_GetProperty(cx
, exnObject
, js_message_str
, &roots
[2]);
1324 if (JSVAL_IS_STRING(roots
[2])) {
1325 bytes
= js_GetStringBytes(cx
, JSVAL_TO_STRING(roots
[2]));
1332 ok
= JS_GetProperty(cx
, exnObject
, js_fileName_str
, &roots
[3]);
1335 str
= js_ValueToString(cx
, roots
[3]);
1340 filename
= StringToFilename(cx
, str
);
1346 ok
= JS_GetProperty(cx
, exnObject
, js_lineNumber_str
, &roots
[4]);
1349 lineno
= js_ValueToECMAUint32 (cx
, &roots
[4]);
1350 ok
= !JSVAL_IS_NULL(roots
[4]);
1355 memset(&report
, 0, sizeof report
);
1356 report
.filename
= filename
;
1357 report
.lineno
= (uintN
) lineno
;
1361 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1362 JSMSG_UNCAUGHT_EXCEPTION
, bytes
);
1364 /* Flag the error as an exception. */
1365 reportp
->flags
|= JSREPORT_EXCEPTION
;
1367 /* Pass the exception object. */
1368 JS_SetPendingException(cx
, exn
);
1369 js_ReportErrorAgain(cx
, bytes
, reportp
);
1370 JS_ClearPendingException(cx
);
1374 JS_POP_TEMP_ROOT(cx
, &tvr
);