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.
53 #include "jsversion.h"
63 #include "jsstaticcheck.h"
64 #include "jswrapper.h"
66 #include "jscntxtinlines.h"
67 #include "jsinterpinlines.h"
68 #include "jsobjinlines.h"
71 using namespace js::gc
;
73 /* Forward declarations for js_ErrorClass's initializer. */
75 Exception(JSContext
*cx
, uintN argc
, Value
*vp
);
78 exn_trace(JSTracer
*trc
, JSObject
*obj
);
81 exn_finalize(JSContext
*cx
, JSObject
*obj
);
84 exn_enumerate(JSContext
*cx
, JSObject
*obj
);
87 exn_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
90 Class js_ErrorClass
= {
92 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
| JSCLASS_MARK_IS_TRACE
|
93 JSCLASS_HAS_CACHED_PROTO(JSProto_Error
),
94 PropertyStub
, /* addProperty */
95 PropertyStub
, /* delProperty */
96 PropertyStub
, /* getProperty */
97 StrictPropertyStub
, /* setProperty */
99 (JSResolveOp
)exn_resolve
,
102 NULL
, /* reserved0 */
103 NULL
, /* checkAccess */
105 NULL
, /* construct */
106 NULL
, /* xdrObject */
107 NULL
, /* hasInstance */
108 JS_CLASS_TRACE(exn_trace
)
111 typedef struct JSStackTraceElem
{
114 const char *filename
;
118 typedef struct JSExnPrivate
{
119 /* A copy of the JSErrorReport originally generated. */
120 JSErrorReport
*errorReport
;
125 JSStackTraceElem stackElems
[1];
129 StackTraceToString(JSContext
*cx
, JSExnPrivate
*priv
);
131 static JSErrorReport
*
132 CopyErrorReport(JSContext
*cx
, JSErrorReport
*report
)
135 * We use a single malloc block to make a deep copy of JSErrorReport with
136 * the following layout:
138 * array of copies of report->messageArgs
139 * jschar array with characters for all messageArgs
140 * jschar array with characters for ucmessage
141 * jschar array with characters for uclinebuf and uctokenptr
142 * char array with characters for linebuf and tokenptr
143 * char array with characters for filename
144 * Such layout together with the properties enforced by the following
145 * asserts does not need any extra alignment padding.
147 JS_STATIC_ASSERT(sizeof(JSErrorReport
) % sizeof(const char *) == 0);
148 JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar
) == 0);
152 size_t uclinebufSize
;
153 size_t ucmessageSize
;
154 size_t i
, argsArraySize
, argsCopySize
, argSize
;
159 #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
161 filenameSize
= report
->filename
? strlen(report
->filename
) + 1 : 0;
162 linebufSize
= report
->linebuf
? strlen(report
->linebuf
) + 1 : 0;
163 uclinebufSize
= report
->uclinebuf
? JS_CHARS_SIZE(report
->uclinebuf
) : 0;
167 if (report
->ucmessage
) {
168 ucmessageSize
= JS_CHARS_SIZE(report
->ucmessage
);
169 if (report
->messageArgs
) {
170 for (i
= 0; report
->messageArgs
[i
]; ++i
)
171 argsCopySize
+= JS_CHARS_SIZE(report
->messageArgs
[i
]);
173 /* Non-null messageArgs should have at least one non-null arg. */
175 argsArraySize
= (i
+ 1) * sizeof(const jschar
*);
180 * The mallocSize can not overflow since it represents the sum of the
181 * sizes of already allocated objects.
183 mallocSize
= sizeof(JSErrorReport
) + argsArraySize
+ argsCopySize
+
184 ucmessageSize
+ uclinebufSize
+ linebufSize
+ filenameSize
;
185 cursor
= (uint8
*)cx
->malloc(mallocSize
);
189 copy
= (JSErrorReport
*)cursor
;
190 memset(cursor
, 0, sizeof(JSErrorReport
));
191 cursor
+= sizeof(JSErrorReport
);
193 if (argsArraySize
!= 0) {
194 copy
->messageArgs
= (const jschar
**)cursor
;
195 cursor
+= argsArraySize
;
196 for (i
= 0; report
->messageArgs
[i
]; ++i
) {
197 copy
->messageArgs
[i
] = (const jschar
*)cursor
;
198 argSize
= JS_CHARS_SIZE(report
->messageArgs
[i
]);
199 memcpy(cursor
, report
->messageArgs
[i
], argSize
);
202 copy
->messageArgs
[i
] = NULL
;
203 JS_ASSERT(cursor
== (uint8
*)copy
->messageArgs
[0] + argsCopySize
);
206 if (report
->ucmessage
) {
207 copy
->ucmessage
= (const jschar
*)cursor
;
208 memcpy(cursor
, report
->ucmessage
, ucmessageSize
);
209 cursor
+= ucmessageSize
;
212 if (report
->uclinebuf
) {
213 copy
->uclinebuf
= (const jschar
*)cursor
;
214 memcpy(cursor
, report
->uclinebuf
, uclinebufSize
);
215 cursor
+= uclinebufSize
;
216 if (report
->uctokenptr
) {
217 copy
->uctokenptr
= copy
->uclinebuf
+ (report
->uctokenptr
-
222 if (report
->linebuf
) {
223 copy
->linebuf
= (const char *)cursor
;
224 memcpy(cursor
, report
->linebuf
, linebufSize
);
225 cursor
+= linebufSize
;
226 if (report
->tokenptr
) {
227 copy
->tokenptr
= copy
->linebuf
+ (report
->tokenptr
-
232 if (report
->filename
) {
233 copy
->filename
= (const char *)cursor
;
234 memcpy(cursor
, report
->filename
, filenameSize
);
236 JS_ASSERT(cursor
+ filenameSize
== (uint8
*)copy
+ mallocSize
);
238 /* Copy non-pointer members. */
239 copy
->lineno
= report
->lineno
;
240 copy
->errorNumber
= report
->errorNumber
;
242 /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
243 copy
->flags
= report
->flags
;
250 GetStackTraceValueBuffer(JSExnPrivate
*priv
)
253 * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals
254 * that helps to produce more informative stack traces. The following
255 * assert allows us to assume that no gap after stackElems is necessary to
256 * align the buffer properly.
258 JS_STATIC_ASSERT(sizeof(JSStackTraceElem
) % sizeof(jsval
) == 0);
260 return (jsval
*)(priv
->stackElems
+ priv
->stackDepth
);
264 InitExnPrivate(JSContext
*cx
, JSObject
*exnObject
, JSString
*message
,
265 JSString
*filename
, uintN lineno
, JSErrorReport
*report
)
267 JSSecurityCallbacks
*callbacks
;
268 CheckAccessOp checkAccess
;
269 JSErrorReporter older
;
270 JSExceptionState
*state
;
272 JSStackFrame
*fp
, *fpstop
;
273 size_t stackDepth
, valueCount
, size
;
276 JSStackTraceElem
*elem
;
279 JS_ASSERT(exnObject
->getClass() == &js_ErrorClass
);
282 * Prepare stack trace data.
284 * Set aside any error reporter for cx and save its exception state
285 * so we can suppress any checkAccess failures. Such failures should stop
286 * the backtrace procedure, not result in a failure of this constructor.
288 callbacks
= JS_GetSecurityCallbacks(cx
);
289 checkAccess
= callbacks
290 ? Valueify(callbacks
->checkObjectAccess
)
292 older
= JS_SetErrorReporter(cx
, NULL
);
293 state
= JS_SaveExceptionState(cx
);
295 callerid
= ATOM_TO_JSID(cx
->runtime
->atomState
.callerAtom
);
298 for (fp
= js_GetTopStackFrame(cx
); fp
; fp
= fp
->prev()) {
299 if (fp
->isFunctionFrame() && !fp
->isEvalFrame()) {
300 Value v
= NullValue();
302 !checkAccess(cx
, &fp
->callee(), callerid
, JSACC_READ
, &v
)) {
305 valueCount
+= fp
->numActualArgs();
309 JS_RestoreExceptionState(cx
, state
);
310 JS_SetErrorReporter(cx
, older
);
313 size
= offsetof(JSExnPrivate
, stackElems
);
314 overflow
= (stackDepth
> ((size_t)-1 - size
) / sizeof(JSStackTraceElem
));
315 size
+= stackDepth
* sizeof(JSStackTraceElem
);
316 overflow
|= (valueCount
> ((size_t)-1 - size
) / sizeof(jsval
));
317 size
+= valueCount
* sizeof(jsval
);
319 js_ReportAllocationOverflow(cx
);
322 priv
= (JSExnPrivate
*)cx
->malloc(size
);
327 * We initialize errorReport with a copy of report after setting the
328 * private slot, to prevent GC accessing a junk value we clear the field
331 priv
->errorReport
= NULL
;
332 priv
->message
= message
;
333 priv
->filename
= filename
;
334 priv
->lineno
= lineno
;
335 priv
->stackDepth
= stackDepth
;
337 values
= GetStackTraceValueBuffer(priv
);
338 elem
= priv
->stackElems
;
339 for (fp
= js_GetTopStackFrame(cx
); fp
!= fpstop
; fp
= fp
->prev()) {
340 if (!fp
->isFunctionFrame() || fp
->isEvalFrame()) {
341 elem
->funName
= NULL
;
344 elem
->funName
= fp
->fun()->atom
345 ? ATOM_TO_STRING(fp
->fun()->atom
)
346 : cx
->runtime
->emptyString
;
347 elem
->argc
= fp
->numActualArgs();
348 fp
->forEachCanonicalActualArg(CopyTo(Valueify(values
)));
349 values
+= elem
->argc
;
352 elem
->filename
= NULL
;
353 if (fp
->isScriptFrame()) {
354 elem
->filename
= fp
->script()->filename
;
356 elem
->ulineno
= js_FramePCToLineNumber(cx
, fp
);
360 JS_ASSERT(priv
->stackElems
+ stackDepth
== elem
);
361 JS_ASSERT(GetStackTraceValueBuffer(priv
) + valueCount
== values
);
363 exnObject
->setPrivate(priv
);
367 * Construct a new copy of the error report struct. We can't use the
368 * error report struct that was passed in, because it's allocated on
369 * the stack, and also because it may point to transient data in the
372 priv
->errorReport
= CopyErrorReport(cx
, report
);
373 if (!priv
->errorReport
) {
374 /* The finalizer realeases priv since it is in the private slot. */
382 static inline JSExnPrivate
*
383 GetExnPrivate(JSContext
*cx
, JSObject
*obj
)
385 return (JSExnPrivate
*) obj
->getPrivate();
389 exn_trace(JSTracer
*trc
, JSObject
*obj
)
392 JSStackTraceElem
*elem
;
396 priv
= GetExnPrivate(trc
->context
, obj
);
399 MarkString(trc
, priv
->message
, "exception message");
401 MarkString(trc
, priv
->filename
, "exception filename");
403 elem
= priv
->stackElems
;
404 for (vcount
= i
= 0; i
!= priv
->stackDepth
; ++i
, ++elem
) {
406 MarkString(trc
, elem
->funName
, "stack trace function name");
407 if (IS_GC_MARKING_TRACER(trc
) && elem
->filename
)
408 js_MarkScriptFilename(elem
->filename
);
409 vcount
+= elem
->argc
;
411 vp
= GetStackTraceValueBuffer(priv
);
412 for (i
= 0; i
!= vcount
; ++i
, ++vp
) {
414 JS_CALL_VALUE_TRACER(trc
, v
, "stack trace argument");
420 exn_finalize(JSContext
*cx
, JSObject
*obj
)
424 priv
= GetExnPrivate(cx
, obj
);
426 if (priv
->errorReport
)
427 cx
->free(priv
->errorReport
);
433 exn_enumerate(JSContext
*cx
, JSObject
*obj
)
435 JSAtomState
*atomState
;
441 JS_STATIC_ASSERT(sizeof(JSAtomState
) <= (size_t)(uint16
)-1);
442 static const uint16 offsets
[] = {
443 (uint16
)offsetof(JSAtomState
, messageAtom
),
444 (uint16
)offsetof(JSAtomState
, fileNameAtom
),
445 (uint16
)offsetof(JSAtomState
, lineNumberAtom
),
446 (uint16
)offsetof(JSAtomState
, stackAtom
),
449 atomState
= &cx
->runtime
->atomState
;
450 for (i
= 0; i
!= JS_ARRAY_LENGTH(offsets
); ++i
) {
451 atom
= *(JSAtom
**)((uint8
*)atomState
+ offsets
[i
]);
452 if (!js_LookupProperty(cx
, obj
, ATOM_TO_JSID(atom
), &pobj
, &prop
))
459 exn_resolve(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
470 priv
= GetExnPrivate(cx
, obj
);
471 if (priv
&& JSID_IS_ATOM(id
)) {
472 str
= JSID_TO_STRING(id
);
474 atom
= cx
->runtime
->atomState
.messageAtom
;
475 if (str
== ATOM_TO_STRING(atom
)) {
476 prop
= js_message_str
;
477 v
= STRING_TO_JSVAL(priv
->message
);
481 atom
= cx
->runtime
->atomState
.fileNameAtom
;
482 if (str
== ATOM_TO_STRING(atom
)) {
483 prop
= js_fileName_str
;
484 v
= STRING_TO_JSVAL(priv
->filename
);
488 atom
= cx
->runtime
->atomState
.lineNumberAtom
;
489 if (str
== ATOM_TO_STRING(atom
)) {
490 prop
= js_lineNumber_str
;
491 v
= INT_TO_JSVAL(priv
->lineno
);
495 atom
= cx
->runtime
->atomState
.stackAtom
;
496 if (str
== ATOM_TO_STRING(atom
)) {
497 stack
= StackTraceToString(cx
, priv
);
501 /* Allow to GC all things that were used to build stack trace. */
502 priv
->stackDepth
= 0;
504 v
= STRING_TO_JSVAL(stack
);
511 if (!JS_DefineProperty(cx
, obj
, prop
, v
, NULL
, NULL
, JSPROP_ENUMERATE
))
518 js_ErrorFromException(JSContext
*cx
, jsval exn
)
523 if (JSVAL_IS_PRIMITIVE(exn
))
525 obj
= JSVAL_TO_OBJECT(exn
);
526 if (obj
->getClass() != &js_ErrorClass
)
528 priv
= GetExnPrivate(cx
, obj
);
531 return priv
->errorReport
;
535 ValueToShortSource(JSContext
*cx
, jsval v
)
539 /* Avoid toSource bloat and fallibility for object types. */
540 if (JSVAL_IS_PRIMITIVE(v
))
541 return js_ValueToSource(cx
, Valueify(v
));
543 AutoCompartment
ac(cx
, JSVAL_TO_OBJECT(v
));
547 if (VALUE_IS_FUNCTION(cx
, v
)) {
549 * XXX Avoid function decompilation bloat for now.
551 str
= JS_GetFunctionId(JS_ValueToFunction(cx
, v
));
552 if (!str
&& !(str
= js_ValueToSource(cx
, Valueify(v
)))) {
554 * Continue to soldier on if the function couldn't be
555 * converted into a string.
557 JS_ClearPendingException(cx
);
558 str
= JS_NewStringCopyZ(cx
, "[unknown function]");
562 * XXX Avoid toString on objects, it takes too long and uses too much
563 * memory, for too many classes (see Mozilla bug 166743).
566 JS_snprintf(buf
, sizeof buf
, "[object %s]",
567 JSVAL_TO_OBJECT(v
)->getClass()->name
);
568 str
= JS_NewStringCopyZ(cx
, buf
);
573 if (!str
|| !cx
->compartment
->wrap(cx
, &str
))
579 StackTraceToString(JSContext
*cx
, JSExnPrivate
*priv
)
582 size_t stacklen
, stackmax
;
583 JSStackTraceElem
*elem
, *endElem
;
590 /* After this point, failing control flow must goto bad. */
592 stacklen
= stackmax
= 0;
594 /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
595 #define STACK_LENGTH_LIMIT JS_BIT(20)
597 #define APPEND_CHAR_TO_STACK(c) \
599 if (stacklen == stackmax) { \
601 if (stackmax >= STACK_LENGTH_LIMIT) \
603 stackmax = stackmax ? 2 * stackmax : 64; \
604 ptr_ = cx->realloc(stackbuf, (stackmax+1) * sizeof(jschar)); \
607 stackbuf = (jschar *) ptr_; \
609 stackbuf[stacklen++] = (c); \
612 #define APPEND_STRING_TO_STACK(str) \
614 JSString *str_ = str; \
615 size_t length_ = str_->length(); \
616 const jschar *chars_ = str_->getChars(cx); \
620 if (length_ > stackmax - stacklen) { \
622 if (stackmax >= STACK_LENGTH_LIMIT || \
623 length_ >= STACK_LENGTH_LIMIT - stacklen) { \
626 stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \
627 ptr_ = cx->realloc(stackbuf, (stackmax+1) * sizeof(jschar)); \
630 stackbuf = (jschar *) ptr_; \
632 js_strncpy(stackbuf + stacklen, chars_, length_); \
633 stacklen += length_; \
636 values
= GetStackTraceValueBuffer(priv
);
637 elem
= priv
->stackElems
;
638 for (endElem
= elem
+ priv
->stackDepth
; elem
!= endElem
; elem
++) {
640 APPEND_STRING_TO_STACK(elem
->funName
);
641 APPEND_CHAR_TO_STACK('(');
642 for (i
= 0; i
!= elem
->argc
; i
++, values
++) {
644 APPEND_CHAR_TO_STACK(',');
645 str
= ValueToShortSource(cx
, *values
);
648 APPEND_STRING_TO_STACK(str
);
650 APPEND_CHAR_TO_STACK(')');
652 APPEND_CHAR_TO_STACK('@');
653 if (elem
->filename
) {
654 for (cp
= elem
->filename
; *cp
; cp
++)
655 APPEND_CHAR_TO_STACK(*cp
);
657 APPEND_CHAR_TO_STACK(':');
658 JS_snprintf(ulnbuf
, sizeof ulnbuf
, "%u", elem
->ulineno
);
659 for (cp
= ulnbuf
; *cp
; cp
++)
660 APPEND_CHAR_TO_STACK(*cp
);
661 APPEND_CHAR_TO_STACK('\n');
663 #undef APPEND_CHAR_TO_STACK
664 #undef APPEND_STRING_TO_STACK
665 #undef STACK_LENGTH_LIMIT
669 JS_ASSERT(!stackbuf
);
670 return cx
->runtime
->emptyString
;
672 if (stacklen
< stackmax
) {
674 * Realloc can fail when shrinking on some FreeBSD versions, so
675 * don't use JS_realloc here; simply let the oversized allocation
676 * be owned by the string in that rare case.
678 void *shrunk
= cx
->realloc(stackbuf
, (stacklen
+1) * sizeof(jschar
));
680 stackbuf
= (jschar
*) shrunk
;
683 stackbuf
[stacklen
] = 0;
684 str
= js_NewString(cx
, stackbuf
, stacklen
);
694 /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
695 with these two functions. */
697 FilenameToString(JSContext
*cx
, const char *filename
)
699 return JS_NewStringCopyZ(cx
, filename
);
703 Exception(JSContext
*cx
, uintN argc
, Value
*vp
)
705 JSString
*message
, *filename
;
709 * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
710 * called as functions, without operator new. But as we do not give
711 * each constructor a distinct JSClass, whose .name member is used by
712 * NewNativeClassInstance to find the class prototype, we must get the
713 * class prototype ourselves.
715 JSObject
&callee
= vp
[0].toObject();
717 if (!callee
.getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &protov
))
720 JSObject
*errProto
= &protov
.toObject();
721 JSObject
*obj
= NewNativeClassInstance(cx
, &js_ErrorClass
, errProto
, errProto
->getParent());
726 * If it's a new object of class Exception, then null out the private
727 * data so that the finalizer doesn't attempt to free it.
729 if (obj
->getClass() == &js_ErrorClass
)
730 obj
->setPrivate(NULL
);
732 /* Set the 'message' property. */
733 Value
*argv
= vp
+ 2;
735 message
= js_ValueToString(cx
, argv
[0]);
738 argv
[0].setString(message
);
740 message
= cx
->runtime
->emptyString
;
743 /* Set the 'fileName' property. */
745 filename
= js_ValueToString(cx
, argv
[1]);
748 argv
[1].setString(filename
);
751 fp
= js_GetScriptedCaller(cx
, NULL
);
753 filename
= FilenameToString(cx
, fp
->script()->filename
);
757 filename
= cx
->runtime
->emptyString
;
761 /* Set the 'lineNumber' property. */
764 if (!ValueToECMAUint32(cx
, argv
[2], &lineno
))
768 fp
= js_GetScriptedCaller(cx
, NULL
);
769 lineno
= (fp
&& fp
->pc(cx
)) ? js_FramePCToLineNumber(cx
, fp
) : 0;
772 if (obj
->getClass() == &js_ErrorClass
&&
773 !InitExnPrivate(cx
, obj
, message
, filename
, lineno
, NULL
)) {
784 * This method only uses JavaScript-modifiable properties name, message. It
785 * is left to the host to check for private data and report filename and line
786 * number information along with this message.
789 exn_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
792 JSString
*name
, *message
, *result
;
794 size_t name_length
, message_length
, length
;
796 JSObject
*obj
= ToObject(cx
, &vp
[1]);
799 if (!obj
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.nameAtom
), Valueify(&v
)))
801 name
= JSVAL_IS_STRING(v
) ? JSVAL_TO_STRING(v
) : cx
->runtime
->emptyString
;
804 if (!JS_GetProperty(cx
, obj
, js_message_str
, &v
))
806 message
= JSVAL_IS_STRING(v
) ? JSVAL_TO_STRING(v
)
807 : cx
->runtime
->emptyString
;
809 if (message
->length() != 0) {
810 name_length
= name
->length();
811 message_length
= message
->length();
812 length
= (name_length
? name_length
+ 2 : 0) + message_length
;
813 cp
= chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
818 const jschar
*name_chars
= name
->getChars(cx
);
821 js_strncpy(cp
, name_chars
, name_length
);
823 *cp
++ = ':'; *cp
++ = ' ';
825 const jschar
*message_chars
= message
->getChars(cx
);
828 js_strncpy(cp
, message_chars
, message_length
);
829 cp
+= message_length
;
832 result
= js_NewString(cx
, chars
, length
);
841 vp
->setString(result
);
847 * Return a string that may eval to something similar to the original object.
850 exn_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
852 JSString
*name
, *message
, *filename
, *lineno_as_str
, *result
;
853 jsval localroots
[3] = {JSVAL_NULL
, JSVAL_NULL
, JSVAL_NULL
};
854 size_t lineno_length
, name_length
, message_length
, filename_length
, length
;
857 JSObject
*obj
= ToObject(cx
, &vp
[1]);
860 if (!obj
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.nameAtom
), vp
))
862 name
= js_ValueToString(cx
, *vp
);
868 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(localroots
), Valueify(localroots
));
871 message
= filename
= NULL
;
873 if (!JS_GetProperty(cx
, obj
, js_message_str
, &localroots
[0]) ||
874 !(message
= js_ValueToSource(cx
, Valueify(localroots
[0])))) {
877 localroots
[0] = STRING_TO_JSVAL(message
);
879 if (!JS_GetProperty(cx
, obj
, js_fileName_str
, &localroots
[1]) ||
880 !(filename
= js_ValueToSource(cx
, Valueify(localroots
[1])))) {
883 localroots
[1] = STRING_TO_JSVAL(filename
);
885 if (!JS_GetProperty(cx
, obj
, js_lineNumber_str
, &localroots
[2]))
888 if (!ValueToECMAUint32(cx
, Valueify(localroots
[2]), &lineno
))
892 lineno_as_str
= js_ValueToString(cx
, Valueify(localroots
[2]));
895 lineno_length
= lineno_as_str
->length();
897 lineno_as_str
= NULL
;
901 /* Magic 8, for the characters in ``(new ())''. */
902 name_length
= name
->length();
903 message_length
= message
->length();
904 length
= 8 + name_length
+ message_length
;
906 filename_length
= filename
->length();
907 if (filename_length
!= 0) {
908 /* append filename as ``, {filename}'' */
909 length
+= 2 + filename_length
;
911 /* append lineno as ``, {lineno_as_str}'' */
912 length
+= 2 + lineno_length
;
917 * no filename, but have line number,
918 * need to append ``, "", {lineno_as_str}''
920 length
+= 6 + lineno_length
;
924 cp
= chars
= (jschar
*) cx
->malloc((length
+ 1) * sizeof(jschar
));
928 *cp
++ = '('; *cp
++ = 'n'; *cp
++ = 'e'; *cp
++ = 'w'; *cp
++ = ' ';
929 const jschar
*name_chars
= name
->getChars(cx
);
932 js_strncpy(cp
, name_chars
, name_length
);
935 const jschar
*message_chars
= message
->getChars(cx
);
938 if (message_length
!= 0) {
939 js_strncpy(cp
, message_chars
, message_length
);
940 cp
+= message_length
;
943 if (filename_length
!= 0) {
944 /* append filename as ``, {filename}'' */
945 *cp
++ = ','; *cp
++ = ' ';
946 const jschar
*filename_chars
= filename
->getChars(cx
);
949 js_strncpy(cp
, filename_chars
, filename_length
);
950 cp
+= filename_length
;
954 * no filename, but have line number,
955 * need to append ``, "", {lineno_as_str}''
957 *cp
++ = ','; *cp
++ = ' '; *cp
++ = '"'; *cp
++ = '"';
961 /* append lineno as ``, {lineno_as_str}'' */
962 *cp
++ = ','; *cp
++ = ' ';
963 const jschar
*lineno_chars
= lineno_as_str
->getChars(cx
);
966 js_strncpy(cp
, lineno_chars
, lineno_length
);
970 *cp
++ = ')'; *cp
++ = ')'; *cp
= 0;
972 result
= js_NewString(cx
, chars
, length
);
977 vp
->setString(result
);
983 static JSFunctionSpec exception_methods
[] = {
985 JS_FN(js_toSource_str
, exn_toSource
, 0,0),
987 JS_FN(js_toString_str
, exn_toString
, 0,0),
991 /* JSProto_ ordering for exceptions shall match JSEXN_ constants. */
992 JS_STATIC_ASSERT(JSEXN_ERR
== 0);
993 JS_STATIC_ASSERT(JSProto_Error
+ JSEXN_INTERNALERR
== JSProto_InternalError
);
994 JS_STATIC_ASSERT(JSProto_Error
+ JSEXN_EVALERR
== JSProto_EvalError
);
995 JS_STATIC_ASSERT(JSProto_Error
+ JSEXN_RANGEERR
== JSProto_RangeError
);
996 JS_STATIC_ASSERT(JSProto_Error
+ JSEXN_REFERENCEERR
== JSProto_ReferenceError
);
997 JS_STATIC_ASSERT(JSProto_Error
+ JSEXN_SYNTAXERR
== JSProto_SyntaxError
);
998 JS_STATIC_ASSERT(JSProto_Error
+ JSEXN_TYPEERR
== JSProto_TypeError
);
999 JS_STATIC_ASSERT(JSProto_Error
+ JSEXN_URIERR
== JSProto_URIError
);
1001 static JS_INLINE JSProtoKey
1002 GetExceptionProtoKey(intN exn
)
1004 JS_ASSERT(JSEXN_ERR
<= exn
);
1005 JS_ASSERT(exn
< JSEXN_LIMIT
);
1006 return (JSProtoKey
) (JSProto_Error
+ exn
);
1010 js_InitExceptionClasses(JSContext
*cx
, JSObject
*obj
)
1013 * If lazy class initialization occurs for any Error subclass, then all
1014 * classes are initialized, starting with Error. To avoid reentry and
1015 * redundant initialization, we must not pass a null proto parameter to
1016 * NewNonFunction below, when called for the Error superclass. We need to
1017 * ensure that Object.prototype is the proto of Error.prototype.
1019 * See the equivalent code to ensure that parent_proto is non-null when
1020 * js_InitClass calls NewObject, in jsobj.cpp.
1022 JSObject
*obj_proto
;
1023 if (!js_GetClassPrototype(cx
, obj
, JSProto_Object
, &obj_proto
))
1026 /* Define all error constructors. */
1027 Value empty
= StringValue(cx
->runtime
->emptyString
);
1028 jsid nameId
= ATOM_TO_JSID(cx
->runtime
->atomState
.nameAtom
);
1029 jsid messageId
= ATOM_TO_JSID(cx
->runtime
->atomState
.messageAtom
);
1030 jsid fileNameId
= ATOM_TO_JSID(cx
->runtime
->atomState
.fileNameAtom
);
1031 jsid lineNumberId
= ATOM_TO_JSID(cx
->runtime
->atomState
.lineNumberAtom
);
1032 JSObject
*error_proto
= NULL
;
1033 for (intN i
= JSEXN_ERR
; i
!= JSEXN_LIMIT
; i
++) {
1034 JSProtoKey protoKey
= GetExceptionProtoKey(i
);
1035 JSAtom
*atom
= cx
->runtime
->atomState
.classAtoms
[protoKey
];
1037 DefineConstructorAndPrototype(cx
, obj
, protoKey
, atom
,
1038 (i
== JSEXN_ERR
) ? obj_proto
: error_proto
,
1039 &js_ErrorClass
, Exception
, 1,
1040 NULL
, (i
== JSEXN_ERR
) ? exception_methods
: NULL
,
1044 JS_ASSERT(proto
->privateData
== NULL
);
1047 error_proto
= proto
;
1049 /* Add properties to the prototype. */
1050 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DECLARING
);
1051 if (!js_DefineNativeProperty(cx
, proto
, nameId
, StringValue(atom
),
1052 PropertyStub
, StrictPropertyStub
,
1053 JSPROP_ENUMERATE
, 0, 0, NULL
) ||
1054 !js_DefineNativeProperty(cx
, proto
, messageId
, empty
,
1055 PropertyStub
, StrictPropertyStub
,
1056 JSPROP_ENUMERATE
, 0, 0, NULL
) ||
1057 !js_DefineNativeProperty(cx
, proto
, fileNameId
, empty
,
1058 PropertyStub
, StrictPropertyStub
,
1059 JSPROP_ENUMERATE
, 0, 0, NULL
) ||
1060 !js_DefineNativeProperty(cx
, proto
, lineNumberId
, Valueify(JSVAL_ZERO
),
1061 PropertyStub
, StrictPropertyStub
,
1062 JSPROP_ENUMERATE
, 0, 0, NULL
)) {
1070 const JSErrorFormatString
*
1071 js_GetLocalizedErrorMessage(JSContext
* cx
, void *userRef
, const char *locale
,
1072 const uintN errorNumber
)
1074 const JSErrorFormatString
*errorString
= NULL
;
1076 if (cx
->localeCallbacks
&& cx
->localeCallbacks
->localeGetErrorMessage
) {
1077 errorString
= cx
->localeCallbacks
1078 ->localeGetErrorMessage(userRef
, locale
, errorNumber
);
1081 errorString
= js_GetErrorMessage(userRef
, locale
, errorNumber
);
1085 #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
1086 /* For use below... get character strings for error name and exception name */
1087 static struct exnname
{ char *name
; char *exception
; } errortoexnname
[] = {
1088 #define MSG_DEF(name, number, count, exception, format) \
1089 {#name, #exception},
1096 js_ErrorToException(JSContext
*cx
, const char *message
, JSErrorReport
*reportp
,
1097 JSErrorCallback callback
, void *userRef
)
1099 JSErrNum errorNumber
;
1100 const JSErrorFormatString
*errorString
;
1104 JSObject
*errProto
, *errObject
;
1105 JSString
*messageStr
, *filenameStr
;
1108 * Tell our caller to report immediately if this report is just a warning.
1111 if (JSREPORT_IS_WARNING(reportp
->flags
))
1114 /* Find the exception index associated with this error. */
1115 errorNumber
= (JSErrNum
) reportp
->errorNumber
;
1116 if (!callback
|| callback
== js_GetErrorMessage
)
1117 errorString
= js_GetLocalizedErrorMessage(cx
, NULL
, NULL
, errorNumber
);
1119 errorString
= callback(userRef
, NULL
, errorNumber
);
1120 exn
= errorString
? (JSExnType
) errorString
->exnType
: JSEXN_NONE
;
1121 JS_ASSERT(exn
< JSEXN_LIMIT
);
1123 #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
1124 /* Print the error name and the associated exception name to stderr */
1125 fprintf(stderr
, "%s\t%s\n",
1126 errortoexnname
[errorNumber
].name
,
1127 errortoexnname
[errorNumber
].exception
);
1131 * Return false (no exception raised) if no exception is associated
1132 * with the given error number.
1134 if (exn
== JSEXN_NONE
)
1138 * Prevent runaway recursion, via cx->generatingError. If an out-of-memory
1139 * error occurs, no exception object will be created, but we don't assume
1140 * that OOM is the only kind of error that subroutines of this function
1141 * called below might raise.
1143 if (cx
->generatingError
)
1146 MUST_FLOW_THROUGH("out");
1147 cx
->generatingError
= JS_TRUE
;
1149 /* Protect the newly-created strings below from nesting GCs. */
1151 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(tv
), Valueify(tv
));
1154 * Try to get an appropriate prototype by looking up the corresponding
1155 * exception constructor name in the scope chain of the current context's
1156 * top stack frame, or in the global object if no frame is active.
1158 ok
= js_GetClassPrototype(cx
, NULL
, GetExceptionProtoKey(exn
), &errProto
);
1161 tv
[0] = OBJECT_TO_JSVAL(errProto
);
1163 errObject
= NewNativeClassInstance(cx
, &js_ErrorClass
, errProto
, errProto
->getParent());
1168 tv
[1] = OBJECT_TO_JSVAL(errObject
);
1170 messageStr
= JS_NewStringCopyZ(cx
, message
);
1175 tv
[2] = STRING_TO_JSVAL(messageStr
);
1177 filenameStr
= JS_NewStringCopyZ(cx
, reportp
->filename
);
1182 tv
[3] = STRING_TO_JSVAL(filenameStr
);
1184 ok
= InitExnPrivate(cx
, errObject
, messageStr
, filenameStr
,
1185 reportp
->lineno
, reportp
);
1189 JS_SetPendingException(cx
, OBJECT_TO_JSVAL(errObject
));
1191 /* Flag the error report passed in to indicate an exception was raised. */
1192 reportp
->flags
|= JSREPORT_EXCEPTION
;
1195 cx
->generatingError
= JS_FALSE
;
1200 js_ReportUncaughtException(JSContext
*cx
)
1203 JSObject
*exnObject
;
1205 JSErrorReport
*reportp
, report
;
1209 if (!JS_IsExceptionPending(cx
))
1212 if (!JS_GetPendingException(cx
, &exn
))
1215 PodArrayZero(roots
);
1216 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), Valueify(roots
));
1219 * Because js_ValueToString below could error and an exception object
1220 * could become unrooted, we must root exnObject. Later, if exnObject is
1221 * non-null, we need to root other intermediates, so allocate an operand
1222 * stack segment to protect all of these values.
1224 if (JSVAL_IS_PRIMITIVE(exn
)) {
1227 exnObject
= JSVAL_TO_OBJECT(exn
);
1231 JS_ClearPendingException(cx
);
1232 reportp
= js_ErrorFromException(cx
, exn
);
1234 /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */
1235 str
= js_ValueToString(cx
, Valueify(exn
));
1236 JSAutoByteString bytesStorage
;
1238 bytes
= "unknown (can't convert to string)";
1240 roots
[1] = STRING_TO_JSVAL(str
);
1241 if (!bytesStorage
.encode(cx
, str
))
1243 bytes
= bytesStorage
.ptr();
1246 JSAutoByteString filename
;
1247 if (!reportp
&& exnObject
&& exnObject
->getClass() == &js_ErrorClass
) {
1248 if (!JS_GetProperty(cx
, exnObject
, js_message_str
, &roots
[2]))
1250 if (JSVAL_IS_STRING(roots
[2])) {
1251 bytesStorage
.clear();
1252 if (!bytesStorage
.encode(cx
, str
))
1254 bytes
= bytesStorage
.ptr();
1257 if (!JS_GetProperty(cx
, exnObject
, js_fileName_str
, &roots
[3]))
1259 str
= js_ValueToString(cx
, Valueify(roots
[3]));
1260 if (!str
|| !filename
.encode(cx
, str
))
1263 if (!JS_GetProperty(cx
, exnObject
, js_lineNumber_str
, &roots
[4]))
1266 if (!ValueToECMAUint32(cx
, Valueify(roots
[4]), &lineno
))
1271 report
.filename
= filename
.ptr();
1272 report
.lineno
= (uintN
) lineno
;
1273 if (JSVAL_IS_STRING(roots
[2])) {
1274 report
.ucmessage
= js_GetStringChars(cx
, JSVAL_TO_STRING(roots
[2]));
1275 if (!report
.ucmessage
)
1281 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1282 JSMSG_UNCAUGHT_EXCEPTION
, bytes
);
1284 /* Flag the error as an exception. */
1285 reportp
->flags
|= JSREPORT_EXCEPTION
;
1287 /* Pass the exception object. */
1288 JS_SetPendingException(cx
, exn
);
1289 js_ReportErrorAgain(cx
, bytes
, reportp
);
1290 JS_ClearPendingException(cx
);