Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / js / src / jsexn.cpp
blob25a2d85151c1322dd265b48298436f3150c6d5dc
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
45 #include "jsstddef.h"
46 #include <stdlib.h>
47 #include <string.h>
48 #include "jstypes.h"
49 #include "jsbit.h"
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jscntxt.h"
54 #include "jsversion.h"
55 #include "jsdbgapi.h"
56 #include "jsexn.h"
57 #include "jsfun.h"
58 #include "jsinterp.h"
59 #include "jsnum.h"
60 #include "jsobj.h"
61 #include "jsopcode.h"
62 #include "jsscope.h"
63 #include "jsscript.h"
64 #include "jsstaticcheck.h"
66 /* Forward declarations for js_ErrorClass's initializer. */
67 static JSBool
68 Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
70 static void
71 exn_finalize(JSContext *cx, JSObject *obj);
73 static void
74 exn_trace(JSTracer *trc, JSObject *obj);
76 static void
77 exn_finalize(JSContext *cx, JSObject *obj);
79 static JSBool
80 exn_enumerate(JSContext *cx, JSObject *obj);
82 static JSBool
83 exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
84 JSObject **objp);
86 JSClass js_ErrorClass = {
87 js_Error_str,
88 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_MARK_IS_TRACE |
89 JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
90 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
91 exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize,
92 NULL, NULL, NULL, Exception,
93 NULL, NULL, JS_CLASS_TRACE(exn_trace), NULL
96 typedef struct JSStackTraceElem {
97 JSString *funName;
98 size_t argc;
99 const char *filename;
100 uintN ulineno;
101 } JSStackTraceElem;
103 typedef struct JSExnPrivate {
104 /* A copy of the JSErrorReport originally generated. */
105 JSErrorReport *errorReport;
106 JSString *message;
107 JSString *filename;
108 uintN lineno;
109 size_t stackDepth;
110 JSStackTraceElem stackElems[1];
111 } JSExnPrivate;
113 static JSString *
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:
122 * JSErrorReport
123 * array of copies of report->messageArgs
124 * jschar array with characters for all messageArgs
125 * jschar array with characters for ucmessage
126 * jschar array with characters for uclinebuf and uctokenptr
127 * char array with characters for linebuf and tokenptr
128 * char array with characters for filename
129 * Such layout together with the properties enforced by the following
130 * asserts does not need any extra alignment padding.
132 JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);
133 JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0);
135 size_t filenameSize;
136 size_t linebufSize;
137 size_t uclinebufSize;
138 size_t ucmessageSize;
139 size_t i, argsArraySize, argsCopySize, argSize;
140 size_t mallocSize;
141 JSErrorReport *copy;
142 uint8 *cursor;
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;
149 ucmessageSize = 0;
150 argsArraySize = 0;
151 argsCopySize = 0;
152 if (report->ucmessage) {
153 ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
154 if (report->messageArgs) {
155 for (i = 0; report->messageArgs[i]; ++i)
156 argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
158 /* Non-null messageArgs should have at least one non-null arg. */
159 JS_ASSERT(i != 0);
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);
171 if (!cursor)
172 return NULL;
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);
185 cursor += 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 -
203 report->uclinebuf);
207 if (report->linebuf) {
208 copy->linebuf = (const char *)cursor;
209 memcpy(cursor, report->linebuf, linebufSize);
210 cursor += linebufSize;
211 if (report->tokenptr) {
212 copy->tokenptr = copy->linebuf + (report->tokenptr -
213 report->linebuf);
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;
230 #undef JS_CHARS_SIZE
231 return copy;
234 static jsval *
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);
248 static JSBool
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;
256 jsval callerid, v;
257 JSStackFrame *fp, *fpstop;
258 size_t stackDepth, valueCount, size;
259 JSBool overflow;
260 JSExnPrivate *priv;
261 JSStackTraceElem *elem;
262 jsval *values;
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
276 : NULL;
277 older = JS_SetErrorReporter(cx, NULL);
278 state = JS_SaveExceptionState(cx);
280 callerid = ATOM_KEY(cx->runtime->atomState.callerAtom);
281 stackDepth = 0;
282 valueCount = 0;
283 for (fp = js_GetTopStackFrame(cx); fp; fp = fp->down) {
284 if (fp->fun && fp->argv) {
285 v = JSVAL_NULL;
286 if (checkAccess &&
287 !checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) {
288 break;
290 valueCount += fp->argc;
292 ++stackDepth;
294 JS_RestoreExceptionState(cx, state);
295 JS_SetErrorReporter(cx, older);
296 fpstop = fp;
298 size = offsetof(JSExnPrivate, stackElems);
299 overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem));
300 size += stackDepth * sizeof(JSStackTraceElem);
301 overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval));
302 size += valueCount * sizeof(jsval);
303 if (overflow) {
304 js_ReportAllocationOverflow(cx);
305 return JS_FALSE;
307 priv = (JSExnPrivate *)JS_malloc(cx, size);
308 if (!priv)
309 return JS_FALSE;
312 * We initialize errorReport with a copy of report after setting the
313 * private slot, to prevent GC accessing a junk value we clear the field
314 * here.
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) {
325 if (!fp->fun) {
326 elem->funName = NULL;
327 elem->argc = 0;
328 } else {
329 elem->funName = fp->fun->atom
330 ? ATOM_TO_STRING(fp->fun->atom)
331 : cx->runtime->emptyString;
332 elem->argc = fp->argc;
333 memcpy(values, fp->argv, fp->argc * sizeof(jsval));
334 values += fp->argc;
336 elem->ulineno = 0;
337 elem->filename = NULL;
338 if (fp->script) {
339 elem->filename = fp->script->filename;
340 if (fp->regs)
341 elem->ulineno = js_FramePCToLineNumber(cx, fp);
343 ++elem;
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));
350 if (report) {
352 * Construct a new copy of the error report struct. We can't use the
353 * error report struct that was passed in, because it's allocated on
354 * the stack, and also because it may point to transient data in the
355 * JSTokenStream.
357 priv->errorReport = CopyErrorReport(cx, report);
358 if (!priv->errorReport) {
359 /* The finalizer realeases priv since it is in the private slot. */
360 return JS_FALSE;
364 return JS_TRUE;
367 static JSExnPrivate *
368 GetExnPrivate(JSContext *cx, JSObject *obj)
370 jsval privateValue;
371 JSExnPrivate *priv;
373 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass);
374 privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
375 if (JSVAL_IS_VOID(privateValue))
376 return NULL;
377 priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue);
378 JS_ASSERT(priv);
379 return priv;
382 static void
383 exn_trace(JSTracer *trc, JSObject *obj)
385 JSExnPrivate *priv;
386 JSStackTraceElem *elem;
387 size_t vcount, i;
388 jsval *vp, v;
390 priv = GetExnPrivate(trc->context, obj);
391 if (priv) {
392 if (priv->message)
393 JS_CALL_STRING_TRACER(trc, priv->message, "exception message");
394 if (priv->filename)
395 JS_CALL_STRING_TRACER(trc, priv->filename, "exception filename");
397 elem = priv->stackElems;
398 for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
399 if (elem->funName) {
400 JS_CALL_STRING_TRACER(trc, elem->funName,
401 "stack trace function name");
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) {
409 v = *vp;
410 JS_CALL_VALUE_TRACER(trc, v, "stack trace argument");
415 static void
416 exn_finalize(JSContext *cx, JSObject *obj)
418 JSExnPrivate *priv;
420 priv = GetExnPrivate(cx, obj);
421 if (priv) {
422 if (priv->errorReport)
423 JS_free(cx, priv->errorReport);
424 JS_free(cx, priv);
428 static JSBool
429 exn_enumerate(JSContext *cx, JSObject *obj)
431 JSAtomState *atomState;
432 uintN i;
433 JSAtom *atom;
434 JSObject *pobj;
435 JSProperty *prop;
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))
449 return JS_FALSE;
450 if (prop)
451 OBJ_DROP_PROPERTY(cx, pobj, prop);
453 return JS_TRUE;
456 static JSBool
457 exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
458 JSObject **objp)
460 JSExnPrivate *priv;
461 JSString *str;
462 JSAtom *atom;
463 JSString *stack;
464 const char *prop;
465 jsval v;
467 *objp = NULL;
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);
476 goto define;
479 atom = cx->runtime->atomState.fileNameAtom;
480 if (str == ATOM_TO_STRING(atom)) {
481 prop = js_fileName_str;
482 v = STRING_TO_JSVAL(priv->filename);
483 goto define;
486 atom = cx->runtime->atomState.lineNumberAtom;
487 if (str == ATOM_TO_STRING(atom)) {
488 prop = js_lineNumber_str;
489 v = INT_TO_JSVAL(priv->lineno);
490 goto define;
493 atom = cx->runtime->atomState.stackAtom;
494 if (str == ATOM_TO_STRING(atom)) {
495 stack = StackTraceToString(cx, priv);
496 if (!stack)
497 return JS_FALSE;
499 /* Allow to GC all things that were used to build stack trace. */
500 priv->stackDepth = 0;
501 prop = js_stack_str;
502 v = STRING_TO_JSVAL(stack);
503 goto define;
506 return JS_TRUE;
508 define:
509 if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE))
510 return JS_FALSE;
511 *objp = obj;
512 return JS_TRUE;
515 JSErrorReport *
516 js_ErrorFromException(JSContext *cx, jsval exn)
518 JSObject *obj;
519 JSExnPrivate *priv;
521 if (JSVAL_IS_PRIMITIVE(exn))
522 return NULL;
523 obj = JSVAL_TO_OBJECT(exn);
524 if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass)
525 return NULL;
526 priv = GetExnPrivate(cx, obj);
527 if (!priv)
528 return NULL;
529 return priv->errorReport;
532 struct JSExnSpec {
533 int protoIndex;
534 const char *name;
535 JSProtoKey key;
536 JSNative native;
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) \
546 static JSBool \
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}
575 static JSString *
576 ValueToShortSource(JSContext *cx, jsval v)
578 JSString *str;
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]");
596 } else {
598 * XXX Avoid toString on objects, it takes too long and uses too much
599 * memory, for too many classes (see Mozilla bug 166743).
601 char buf[100];
602 JS_snprintf(buf, sizeof buf, "[object %s]",
603 OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name);
604 str = JS_NewStringCopyZ(cx, buf);
606 return str;
609 static JSString *
610 StackTraceToString(JSContext *cx, JSExnPrivate *priv)
612 jschar *stackbuf;
613 size_t stacklen, stackmax;
614 JSStackTraceElem *elem, *endElem;
615 jsval *values;
616 size_t i;
617 JSString *str;
618 const char *cp;
619 char ulnbuf[11];
621 /* After this point, failing control flow must goto bad. */
622 stackbuf = NULL;
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) \
629 JS_BEGIN_MACRO \
630 if (stacklen == stackmax) { \
631 void *ptr_; \
632 if (stackmax >= STACK_LENGTH_LIMIT) \
633 goto done; \
634 stackmax = stackmax ? 2 * stackmax : 64; \
635 ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
636 if (!ptr_) \
637 goto bad; \
638 stackbuf = (jschar *) ptr_; \
640 stackbuf[stacklen++] = (c); \
641 JS_END_MACRO
643 #define APPEND_STRING_TO_STACK(str) \
644 JS_BEGIN_MACRO \
645 JSString *str_ = str; \
646 jschar *chars_; \
647 size_t length_; \
649 JSSTRING_CHARS_AND_LENGTH(str_, chars_, length_); \
650 if (length_ > stackmax - stacklen) { \
651 void *ptr_; \
652 if (stackmax >= STACK_LENGTH_LIMIT || \
653 length_ >= STACK_LENGTH_LIMIT - stacklen) { \
654 goto done; \
656 stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \
657 ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
658 if (!ptr_) \
659 goto bad; \
660 stackbuf = (jschar *) ptr_; \
662 js_strncpy(stackbuf + stacklen, chars_, length_); \
663 stacklen += length_; \
664 JS_END_MACRO
666 values = GetStackTraceValueBuffer(priv);
667 elem = priv->stackElems;
668 for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
669 if (elem->funName) {
670 APPEND_STRING_TO_STACK(elem->funName);
671 APPEND_CHAR_TO_STACK('(');
672 for (i = 0; i != elem->argc; i++, values++) {
673 if (i > 0)
674 APPEND_CHAR_TO_STACK(',');
675 str = ValueToShortSource(cx, *values);
676 if (!str)
677 goto bad;
678 APPEND_STRING_TO_STACK(str);
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
697 done:
698 if (stacklen == 0) {
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));
709 if (shrunk)
710 stackbuf = (jschar *) shrunk;
713 stackbuf[stacklen] = 0;
714 str = js_NewString(cx, stackbuf, stacklen);
715 if (str)
716 return str;
718 bad:
719 if (stackbuf)
720 JS_free(cx, stackbuf);
721 return NULL;
724 /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
725 with these two functions. */
726 static JSString *
727 FilenameToString(JSContext *cx, const char *filename)
729 return JS_NewStringCopyZ(cx, filename);
732 static const char *
733 StringToFilename(JSContext *cx, JSString *str)
735 return js_GetStringBytes(cx, str);
738 static JSBool
739 Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
741 uint32 lineno;
742 JSString *message, *filename;
743 JSStackFrame *fp;
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),
756 rval))
757 return JS_FALSE;
758 obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL, 0);
759 if (!obj)
760 return JS_FALSE;
761 *rval = OBJECT_TO_JSVAL(obj);
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. */
772 if (argc != 0) {
773 message = js_ValueToString(cx, argv[0]);
774 if (!message)
775 return JS_FALSE;
776 argv[0] = STRING_TO_JSVAL(message);
777 } else {
778 message = cx->runtime->emptyString;
781 /* Set the 'fileName' property. */
782 if (argc > 1) {
783 filename = js_ValueToString(cx, argv[1]);
784 if (!filename)
785 return JS_FALSE;
786 argv[1] = STRING_TO_JSVAL(filename);
787 fp = NULL;
788 } else {
789 fp = js_GetScriptedCaller(cx, NULL);
790 if (fp) {
791 filename = FilenameToString(cx, fp->script->filename);
792 if (!filename)
793 return JS_FALSE;
794 } else {
795 filename = cx->runtime->emptyString;
799 /* Set the 'lineNumber' property. */
800 if (argc > 2) {
801 lineno = js_ValueToECMAUint32(cx, &argv[2]);
802 if (JSVAL_IS_NULL(argv[2]))
803 return JS_FALSE;
804 } else {
805 if (!fp)
806 fp = js_GetScriptedCaller(cx, NULL);
807 lineno = (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0;
810 return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) ||
811 InitExnPrivate(cx, obj, message, filename, lineno, NULL);
815 * Convert to string.
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.
821 static JSBool
822 exn_toString(JSContext *cx, uintN argc, jsval *vp)
824 JSObject *obj;
825 jsval v;
826 JSString *name, *message, *result;
827 jschar *chars, *cp;
828 size_t name_length, message_length, length;
830 obj = JS_THIS_OBJECT(cx, vp);
831 if (!obj ||
832 !OBJ_GET_PROPERTY(cx, obj,
833 ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
834 &v)) {
835 return JS_FALSE;
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))
841 return JS_FALSE;
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));
850 if (!chars)
851 return JS_FALSE;
853 if (name_length) {
854 js_strncpy(cp, JSSTRING_CHARS(name), name_length);
855 cp += name_length;
856 *cp++ = ':'; *cp++ = ' ';
858 js_strncpy(cp, JSSTRING_CHARS(message), message_length);
859 cp += message_length;
860 *cp = 0;
862 result = js_NewString(cx, chars, length);
863 if (!result) {
864 JS_free(cx, chars);
865 return JS_FALSE;
867 } else {
868 result = name;
871 *vp = STRING_TO_JSVAL(result);
872 return JS_TRUE;
875 #if JS_HAS_TOSOURCE
877 * Return a string that may eval to something similar to the original object.
879 static JSBool
880 exn_toSource(JSContext *cx, uintN argc, jsval *vp)
882 JSObject *obj;
883 JSString *name, *message, *filename, *lineno_as_str, *result;
884 jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
885 JSTempValueRooter tvr;
886 JSBool ok;
887 uint32 lineno;
888 size_t lineno_length, name_length, message_length, filename_length, length;
889 jschar *chars, *cp;
891 obj = JS_THIS_OBJECT(cx, vp);
892 if (!obj ||
893 !OBJ_GET_PROPERTY(cx, obj,
894 ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
895 vp)) {
896 return JS_FALSE;
898 name = js_ValueToString(cx, *vp);
899 if (!name)
900 return JS_FALSE;
901 *vp = STRING_TO_JSVAL(name);
903 MUST_FLOW_THROUGH("out");
904 JS_PUSH_TEMP_ROOT(cx, 3, localroots, &tvr);
906 #ifdef __GNUC__
907 message = filename = NULL;
908 #endif
909 ok = JS_GetProperty(cx, obj, js_message_str, &localroots[0]) &&
910 (message = js_ValueToSource(cx, localroots[0]));
911 if (!ok)
912 goto out;
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]));
917 if (!ok)
918 goto out;
919 localroots[1] = STRING_TO_JSVAL(filename);
921 ok = JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]);
922 if (!ok)
923 goto out;
924 lineno = js_ValueToECMAUint32 (cx, &localroots[2]);
925 ok = !JSVAL_IS_NULL(localroots[2]);
926 if (!ok)
927 goto out;
929 if (lineno != 0) {
930 lineno_as_str = js_ValueToString(cx, localroots[2]);
931 if (!lineno_as_str) {
932 ok = JS_FALSE;
933 goto out;
935 lineno_length = JSSTRING_LENGTH(lineno_as_str);
936 } else {
937 lineno_as_str = NULL;
938 lineno_length = 0;
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;
950 if (lineno_as_str) {
951 /* append lineno as ``, {lineno_as_str}'' */
952 length += 2 + lineno_length;
954 } else {
955 if (lineno_as_str) {
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));
965 if (!chars) {
966 ok = JS_FALSE;
967 goto out;
970 *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
971 js_strncpy(cp, JSSTRING_CHARS(name), name_length);
972 cp += name_length;
973 *cp++ = '(';
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;
984 } else {
985 if (lineno_as_str) {
987 * no filename, but have line number,
988 * need to append ``, "", {lineno_as_str}''
990 *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
993 if (lineno_as_str) {
994 /* append lineno as ``, {lineno_as_str}'' */
995 *cp++ = ','; *cp++ = ' ';
996 js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length);
997 cp += lineno_length;
1000 *cp++ = ')'; *cp++ = ')'; *cp = 0;
1002 result = js_NewString(cx, chars, length);
1003 if (!result) {
1004 JS_free(cx, chars);
1005 ok = JS_FALSE;
1006 goto out;
1008 *vp = STRING_TO_JSVAL(result);
1009 ok = JS_TRUE;
1011 out:
1012 JS_POP_TEMP_ROOT(cx, &tvr);
1013 return ok;
1015 #endif
1017 static JSFunctionSpec exception_methods[] = {
1018 #if JS_HAS_TOSOURCE
1019 JS_FN(js_toSource_str, exn_toSource, 0,0),
1020 #endif
1021 JS_FN(js_toString_str, exn_toString, 0,0),
1022 JS_FS_END
1025 JSObject *
1026 js_InitExceptionClasses(JSContext *cx, JSObject *obj)
1028 JSObject *obj_proto, *protos[JSEXN_LIMIT];
1029 int i;
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),
1042 &obj_proto)) {
1043 return NULL;
1046 if (!js_EnterLocalRootScope(cx))
1047 return NULL;
1049 /* Initialize the prototypes first. */
1050 for (i = 0; exceptions[i].name != 0; i++) {
1051 JSAtom *atom;
1052 JSFunction *fun;
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]
1060 : obj_proto,
1061 obj, 0);
1062 if (!protos[i])
1063 break;
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);
1071 if (!fun)
1072 break;
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)) {
1080 break;
1083 /* proto bootstrap bit from JS_InitClass omitted. */
1084 nameString = JS_NewStringCopyZ(cx, exceptions[i].name);
1085 if (!nameString)
1086 break;
1088 /* Add the name property to the prototype. */
1089 if (!JS_DefineProperty(cx, protos[i], js_name_str,
1090 STRING_TO_JSVAL(nameString),
1091 NULL, NULL,
1092 JSPROP_ENUMERATE)) {
1093 break;
1096 /* Finally, stash the constructor for later uses. */
1097 if (!js_SetClassObject(cx, obj, exceptions[i].key, FUN_OBJECT(fun)))
1098 break;
1101 js_LeaveLocalRootScope(cx);
1102 if (exceptions[i].name)
1103 return NULL;
1106 * Add an empty message property. (To Exception.prototype only,
1107 * because this property will be the same for all the exception
1108 * protos.)
1110 if (!JS_DefineProperty(cx, protos[0], js_message_str,
1111 STRING_TO_JSVAL(cx->runtime->emptyString),
1112 NULL, NULL, JSPROP_ENUMERATE)) {
1113 return NULL;
1115 if (!JS_DefineProperty(cx, protos[0], js_fileName_str,
1116 STRING_TO_JSVAL(cx->runtime->emptyString),
1117 NULL, NULL, JSPROP_ENUMERATE)) {
1118 return NULL;
1120 if (!JS_DefineProperty(cx, protos[0], js_lineNumber_str,
1121 INT_TO_JSVAL(0),
1122 NULL, NULL, JSPROP_ENUMERATE)) {
1123 return NULL;
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))
1131 return NULL;
1133 return protos[0];
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);
1146 if (!errorString)
1147 errorString = js_GetErrorMessage(userRef, locale, errorNumber);
1148 return errorString;
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},
1156 #include "js.msg"
1157 #undef MSG_DEF
1159 #endif /* DEBUG */
1161 JSBool
1162 js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
1164 JSErrNum errorNumber;
1165 const JSErrorFormatString *errorString;
1166 JSExnType exn;
1167 jsval tv[4];
1168 JSTempValueRooter tvr;
1169 JSBool ok;
1170 JSObject *errProto, *errObject;
1171 JSString *messageStr, *filenameStr;
1174 * Tell our caller to report immediately if this report is just a warning.
1176 JS_ASSERT(reportp);
1177 if (JSREPORT_IS_WARNING(reportp->flags))
1178 return JS_FALSE;
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);
1191 #endif
1194 * Return false (no exception raised) if no exception is associated
1195 * with the given error number.
1197 if (exn == JSEXN_NONE)
1198 return JS_FALSE;
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)
1207 return JS_FALSE;
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),
1222 &errProto);
1223 if (!ok)
1224 goto out;
1225 tv[0] = OBJECT_TO_JSVAL(errProto);
1227 errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0);
1228 if (!errObject) {
1229 ok = JS_FALSE;
1230 goto out;
1232 tv[1] = OBJECT_TO_JSVAL(errObject);
1234 messageStr = JS_NewStringCopyZ(cx, message);
1235 if (!messageStr) {
1236 ok = JS_FALSE;
1237 goto out;
1239 tv[2] = STRING_TO_JSVAL(messageStr);
1241 filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
1242 if (!filenameStr) {
1243 ok = JS_FALSE;
1244 goto out;
1246 tv[3] = STRING_TO_JSVAL(filenameStr);
1248 ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
1249 reportp->lineno, reportp);
1250 if (!ok)
1251 goto out;
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;
1258 out:
1259 JS_POP_TEMP_ROOT(cx, &tvr);
1260 cx->generatingError = JS_FALSE;
1261 return ok;
1264 JSBool
1265 js_ReportUncaughtException(JSContext *cx)
1267 jsval exn;
1268 JSObject *exnObject;
1269 jsval roots[5];
1270 JSTempValueRooter tvr;
1271 JSErrorReport *reportp, report;
1272 JSString *str;
1273 const char *bytes;
1274 JSBool ok;
1276 if (!JS_IsExceptionPending(cx))
1277 return JS_TRUE;
1279 if (!JS_GetPendingException(cx, &exn))
1280 return JS_FALSE;
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)) {
1292 exnObject = NULL;
1293 } else {
1294 exnObject = JSVAL_TO_OBJECT(exn);
1295 roots[0] = 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);
1303 if (!str) {
1304 bytes = "unknown (can't convert to string)";
1305 } else {
1306 roots[1] = STRING_TO_JSVAL(str);
1307 bytes = js_GetStringBytes(cx, str);
1308 if (!bytes) {
1309 ok = JS_FALSE;
1310 goto out;
1313 ok = JS_TRUE;
1315 if (!reportp &&
1316 exnObject &&
1317 OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) {
1318 const char *filename;
1319 uint32 lineno;
1321 ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]);
1322 if (!ok)
1323 goto out;
1324 if (JSVAL_IS_STRING(roots[2])) {
1325 bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2]));
1326 if (!bytes) {
1327 ok = JS_FALSE;
1328 goto out;
1332 ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]);
1333 if (!ok)
1334 goto out;
1335 str = js_ValueToString(cx, roots[3]);
1336 if (!str) {
1337 ok = JS_FALSE;
1338 goto out;
1340 filename = StringToFilename(cx, str);
1341 if (!filename) {
1342 ok = JS_FALSE;
1343 goto out;
1346 ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]);
1347 if (!ok)
1348 goto out;
1349 lineno = js_ValueToECMAUint32 (cx, &roots[4]);
1350 ok = !JSVAL_IS_NULL(roots[4]);
1351 if (!ok)
1352 goto out;
1354 reportp = &report;
1355 memset(&report, 0, sizeof report);
1356 report.filename = filename;
1357 report.lineno = (uintN) lineno;
1360 if (!reportp) {
1361 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1362 JSMSG_UNCAUGHT_EXCEPTION, bytes);
1363 } else {
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);
1373 out:
1374 JS_POP_TEMP_ROOT(cx, &tvr);
1375 return ok;