CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / jsexn.cpp
blobc527f8ec3bb1d64a7fe0221d21655a60718d9c14
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.
44 #include <stdlib.h>
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsstdint.h"
48 #include "jsbit.h"
49 #include "jsutil.h"
50 #include "jsprf.h"
51 #include "jsapi.h"
52 #include "jscntxt.h"
53 #include "jsversion.h"
54 #include "jsdbgapi.h"
55 #include "jsexn.h"
56 #include "jsfun.h"
57 #include "jsinterp.h"
58 #include "jsnum.h"
59 #include "jsobj.h"
60 #include "jsopcode.h"
61 #include "jsscope.h"
62 #include "jsscript.h"
63 #include "jsstaticcheck.h"
64 #include "jswrapper.h"
66 #include "jscntxtinlines.h"
67 #include "jsinterpinlines.h"
68 #include "jsobjinlines.h"
70 using namespace js;
71 using namespace js::gc;
73 /* Forward declarations for js_ErrorClass's initializer. */
74 static JSBool
75 Exception(JSContext *cx, uintN argc, Value *vp);
77 static void
78 exn_trace(JSTracer *trc, JSObject *obj);
80 static void
81 exn_finalize(JSContext *cx, JSObject *obj);
83 static JSBool
84 exn_enumerate(JSContext *cx, JSObject *obj);
86 static JSBool
87 exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
88 JSObject **objp);
90 Class js_ErrorClass = {
91 js_Error_str,
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 */
98 exn_enumerate,
99 (JSResolveOp)exn_resolve,
100 ConvertStub,
101 exn_finalize,
102 NULL, /* reserved0 */
103 NULL, /* checkAccess */
104 NULL, /* call */
105 NULL, /* construct */
106 NULL, /* xdrObject */
107 NULL, /* hasInstance */
108 JS_CLASS_TRACE(exn_trace)
111 typedef struct JSStackTraceElem {
112 JSString *funName;
113 size_t argc;
114 const char *filename;
115 uintN ulineno;
116 } JSStackTraceElem;
118 typedef struct JSExnPrivate {
119 /* A copy of the JSErrorReport originally generated. */
120 JSErrorReport *errorReport;
121 JSString *message;
122 JSString *filename;
123 uintN lineno;
124 size_t stackDepth;
125 JSStackTraceElem stackElems[1];
126 } JSExnPrivate;
128 static JSString *
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:
137 * JSErrorReport
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);
150 size_t filenameSize;
151 size_t linebufSize;
152 size_t uclinebufSize;
153 size_t ucmessageSize;
154 size_t i, argsArraySize, argsCopySize, argSize;
155 size_t mallocSize;
156 JSErrorReport *copy;
157 uint8 *cursor;
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;
164 ucmessageSize = 0;
165 argsArraySize = 0;
166 argsCopySize = 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. */
174 JS_ASSERT(i != 0);
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);
186 if (!cursor)
187 return NULL;
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);
200 cursor += 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 -
218 report->uclinebuf);
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 -
228 report->linebuf);
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;
245 #undef JS_CHARS_SIZE
246 return copy;
249 static jsval *
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);
263 static JSBool
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;
271 jsid callerid;
272 JSStackFrame *fp, *fpstop;
273 size_t stackDepth, valueCount, size;
274 JSBool overflow;
275 JSExnPrivate *priv;
276 JSStackTraceElem *elem;
277 jsval *values;
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)
291 : NULL;
292 older = JS_SetErrorReporter(cx, NULL);
293 state = JS_SaveExceptionState(cx);
295 callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
296 stackDepth = 0;
297 valueCount = 0;
298 for (fp = js_GetTopStackFrame(cx); fp; fp = fp->prev()) {
299 if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
300 Value v = NullValue();
301 if (checkAccess &&
302 !checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) {
303 break;
305 valueCount += fp->numActualArgs();
307 ++stackDepth;
309 JS_RestoreExceptionState(cx, state);
310 JS_SetErrorReporter(cx, older);
311 fpstop = fp;
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);
318 if (overflow) {
319 js_ReportAllocationOverflow(cx);
320 return JS_FALSE;
322 priv = (JSExnPrivate *)cx->malloc(size);
323 if (!priv)
324 return JS_FALSE;
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
329 * here.
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;
342 elem->argc = 0;
343 } else {
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;
351 elem->ulineno = 0;
352 elem->filename = NULL;
353 if (fp->isScriptFrame()) {
354 elem->filename = fp->script()->filename;
355 if (fp->pc(cx))
356 elem->ulineno = js_FramePCToLineNumber(cx, fp);
358 ++elem;
360 JS_ASSERT(priv->stackElems + stackDepth == elem);
361 JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
363 exnObject->setPrivate(priv);
365 if (report) {
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
370 * TokenStream.
372 priv->errorReport = CopyErrorReport(cx, report);
373 if (!priv->errorReport) {
374 /* The finalizer realeases priv since it is in the private slot. */
375 return JS_FALSE;
379 return JS_TRUE;
382 static inline JSExnPrivate *
383 GetExnPrivate(JSContext *cx, JSObject *obj)
385 return (JSExnPrivate *) obj->getPrivate();
388 static void
389 exn_trace(JSTracer *trc, JSObject *obj)
391 JSExnPrivate *priv;
392 JSStackTraceElem *elem;
393 size_t vcount, i;
394 jsval *vp, v;
396 priv = GetExnPrivate(trc->context, obj);
397 if (priv) {
398 if (priv->message)
399 MarkString(trc, priv->message, "exception message");
400 if (priv->filename)
401 MarkString(trc, priv->filename, "exception filename");
403 elem = priv->stackElems;
404 for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
405 if (elem->funName)
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) {
413 v = *vp;
414 JS_CALL_VALUE_TRACER(trc, v, "stack trace argument");
419 static void
420 exn_finalize(JSContext *cx, JSObject *obj)
422 JSExnPrivate *priv;
424 priv = GetExnPrivate(cx, obj);
425 if (priv) {
426 if (priv->errorReport)
427 cx->free(priv->errorReport);
428 cx->free(priv);
432 static JSBool
433 exn_enumerate(JSContext *cx, JSObject *obj)
435 JSAtomState *atomState;
436 uintN i;
437 JSAtom *atom;
438 JSObject *pobj;
439 JSProperty *prop;
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))
453 return JS_FALSE;
455 return JS_TRUE;
458 static JSBool
459 exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
460 JSObject **objp)
462 JSExnPrivate *priv;
463 JSString *str;
464 JSAtom *atom;
465 JSString *stack;
466 const char *prop;
467 jsval v;
469 *objp = NULL;
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);
478 goto define;
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);
485 goto define;
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);
492 goto define;
495 atom = cx->runtime->atomState.stackAtom;
496 if (str == ATOM_TO_STRING(atom)) {
497 stack = StackTraceToString(cx, priv);
498 if (!stack)
499 return JS_FALSE;
501 /* Allow to GC all things that were used to build stack trace. */
502 priv->stackDepth = 0;
503 prop = js_stack_str;
504 v = STRING_TO_JSVAL(stack);
505 goto define;
508 return JS_TRUE;
510 define:
511 if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE))
512 return JS_FALSE;
513 *objp = obj;
514 return JS_TRUE;
517 JSErrorReport *
518 js_ErrorFromException(JSContext *cx, jsval exn)
520 JSObject *obj;
521 JSExnPrivate *priv;
523 if (JSVAL_IS_PRIMITIVE(exn))
524 return NULL;
525 obj = JSVAL_TO_OBJECT(exn);
526 if (obj->getClass() != &js_ErrorClass)
527 return NULL;
528 priv = GetExnPrivate(cx, obj);
529 if (!priv)
530 return NULL;
531 return priv->errorReport;
534 static JSString *
535 ValueToShortSource(JSContext *cx, jsval v)
537 JSString *str;
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));
544 if (!ac.enter())
545 return NULL;
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]");
560 } else {
562 * XXX Avoid toString on objects, it takes too long and uses too much
563 * memory, for too many classes (see Mozilla bug 166743).
565 char buf[100];
566 JS_snprintf(buf, sizeof buf, "[object %s]",
567 JSVAL_TO_OBJECT(v)->getClass()->name);
568 str = JS_NewStringCopyZ(cx, buf);
571 ac.leave();
573 if (!str || !cx->compartment->wrap(cx, &str))
574 return NULL;
575 return str;
578 static JSString *
579 StackTraceToString(JSContext *cx, JSExnPrivate *priv)
581 jschar *stackbuf;
582 size_t stacklen, stackmax;
583 JSStackTraceElem *elem, *endElem;
584 jsval *values;
585 size_t i;
586 JSString *str;
587 const char *cp;
588 char ulnbuf[11];
590 /* After this point, failing control flow must goto bad. */
591 stackbuf = NULL;
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) \
598 JS_BEGIN_MACRO \
599 if (stacklen == stackmax) { \
600 void *ptr_; \
601 if (stackmax >= STACK_LENGTH_LIMIT) \
602 goto done; \
603 stackmax = stackmax ? 2 * stackmax : 64; \
604 ptr_ = cx->realloc(stackbuf, (stackmax+1) * sizeof(jschar)); \
605 if (!ptr_) \
606 goto bad; \
607 stackbuf = (jschar *) ptr_; \
609 stackbuf[stacklen++] = (c); \
610 JS_END_MACRO
612 #define APPEND_STRING_TO_STACK(str) \
613 JS_BEGIN_MACRO \
614 JSString *str_ = str; \
615 size_t length_ = str_->length(); \
616 const jschar *chars_ = str_->getChars(cx); \
617 if (!chars_) \
618 goto bad; \
620 if (length_ > stackmax - stacklen) { \
621 void *ptr_; \
622 if (stackmax >= STACK_LENGTH_LIMIT || \
623 length_ >= STACK_LENGTH_LIMIT - stacklen) { \
624 goto done; \
626 stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \
627 ptr_ = cx->realloc(stackbuf, (stackmax+1) * sizeof(jschar)); \
628 if (!ptr_) \
629 goto bad; \
630 stackbuf = (jschar *) ptr_; \
632 js_strncpy(stackbuf + stacklen, chars_, length_); \
633 stacklen += length_; \
634 JS_END_MACRO
636 values = GetStackTraceValueBuffer(priv);
637 elem = priv->stackElems;
638 for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
639 if (elem->funName) {
640 APPEND_STRING_TO_STACK(elem->funName);
641 APPEND_CHAR_TO_STACK('(');
642 for (i = 0; i != elem->argc; i++, values++) {
643 if (i > 0)
644 APPEND_CHAR_TO_STACK(',');
645 str = ValueToShortSource(cx, *values);
646 if (!str)
647 goto bad;
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
667 done:
668 if (stacklen == 0) {
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));
679 if (shrunk)
680 stackbuf = (jschar *) shrunk;
683 stackbuf[stacklen] = 0;
684 str = js_NewString(cx, stackbuf, stacklen);
685 if (str)
686 return str;
688 bad:
689 if (stackbuf)
690 cx->free(stackbuf);
691 return NULL;
694 /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
695 with these two functions. */
696 static JSString *
697 FilenameToString(JSContext *cx, const char *filename)
699 return JS_NewStringCopyZ(cx, filename);
702 static JSBool
703 Exception(JSContext *cx, uintN argc, Value *vp)
705 JSString *message, *filename;
706 JSStackFrame *fp;
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();
716 Value protov;
717 if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
718 return JS_FALSE;
720 JSObject *errProto = &protov.toObject();
721 JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent());
722 if (!obj)
723 return JS_FALSE;
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;
734 if (argc != 0) {
735 message = js_ValueToString(cx, argv[0]);
736 if (!message)
737 return JS_FALSE;
738 argv[0].setString(message);
739 } else {
740 message = cx->runtime->emptyString;
743 /* Set the 'fileName' property. */
744 if (argc > 1) {
745 filename = js_ValueToString(cx, argv[1]);
746 if (!filename)
747 return JS_FALSE;
748 argv[1].setString(filename);
749 fp = NULL;
750 } else {
751 fp = js_GetScriptedCaller(cx, NULL);
752 if (fp) {
753 filename = FilenameToString(cx, fp->script()->filename);
754 if (!filename)
755 return JS_FALSE;
756 } else {
757 filename = cx->runtime->emptyString;
761 /* Set the 'lineNumber' property. */
762 uint32_t lineno;
763 if (argc > 2) {
764 if (!ValueToECMAUint32(cx, argv[2], &lineno))
765 return JS_FALSE;
766 } else {
767 if (!fp)
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)) {
774 return JS_FALSE;
777 vp->setObject(*obj);
778 return JS_TRUE;
782 * Convert to string.
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.
788 static JSBool
789 exn_toString(JSContext *cx, uintN argc, Value *vp)
791 jsval v;
792 JSString *name, *message, *result;
793 jschar *chars, *cp;
794 size_t name_length, message_length, length;
796 JSObject *obj = ToObject(cx, &vp[1]);
797 if (!obj)
798 return JS_FALSE;
799 if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), Valueify(&v)))
800 return JS_FALSE;
801 name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString;
802 vp->setString(name);
804 if (!JS_GetProperty(cx, obj, js_message_str, &v))
805 return JS_FALSE;
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));
814 if (!chars)
815 return JS_FALSE;
817 if (name_length) {
818 const jschar *name_chars = name->getChars(cx);
819 if (!name_chars)
820 return JS_FALSE;
821 js_strncpy(cp, name_chars, name_length);
822 cp += name_length;
823 *cp++ = ':'; *cp++ = ' ';
825 const jschar *message_chars = message->getChars(cx);
826 if (!message_chars)
827 return JS_FALSE;
828 js_strncpy(cp, message_chars, message_length);
829 cp += message_length;
830 *cp = 0;
832 result = js_NewString(cx, chars, length);
833 if (!result) {
834 cx->free(chars);
835 return JS_FALSE;
837 } else {
838 result = name;
841 vp->setString(result);
842 return JS_TRUE;
845 #if JS_HAS_TOSOURCE
847 * Return a string that may eval to something similar to the original object.
849 static JSBool
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;
855 jschar *chars, *cp;
857 JSObject *obj = ToObject(cx, &vp[1]);
858 if (!obj)
859 return false;
860 if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), vp))
861 return false;
862 name = js_ValueToString(cx, *vp);
863 if (!name)
864 return false;
865 vp->setString(name);
868 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroots), Valueify(localroots));
870 #ifdef __GNUC__
871 message = filename = NULL;
872 #endif
873 if (!JS_GetProperty(cx, obj, js_message_str, &localroots[0]) ||
874 !(message = js_ValueToSource(cx, Valueify(localroots[0])))) {
875 return false;
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])))) {
881 return false;
883 localroots[1] = STRING_TO_JSVAL(filename);
885 if (!JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]))
886 return false;
887 uint32_t lineno;
888 if (!ValueToECMAUint32(cx, Valueify(localroots[2]), &lineno))
889 return false;
891 if (lineno != 0) {
892 lineno_as_str = js_ValueToString(cx, Valueify(localroots[2]));
893 if (!lineno_as_str)
894 return false;
895 lineno_length = lineno_as_str->length();
896 } else {
897 lineno_as_str = NULL;
898 lineno_length = 0;
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;
910 if (lineno_as_str) {
911 /* append lineno as ``, {lineno_as_str}'' */
912 length += 2 + lineno_length;
914 } else {
915 if (lineno_as_str) {
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));
925 if (!chars)
926 return false;
928 *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
929 const jschar *name_chars = name->getChars(cx);
930 if (!name_chars)
931 return false;
932 js_strncpy(cp, name_chars, name_length);
933 cp += name_length;
934 *cp++ = '(';
935 const jschar *message_chars = message->getChars(cx);
936 if (!message_chars)
937 return false;
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);
947 if (!filename_chars)
948 return false;
949 js_strncpy(cp, filename_chars, filename_length);
950 cp += filename_length;
951 } else {
952 if (lineno_as_str) {
954 * no filename, but have line number,
955 * need to append ``, "", {lineno_as_str}''
957 *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
960 if (lineno_as_str) {
961 /* append lineno as ``, {lineno_as_str}'' */
962 *cp++ = ','; *cp++ = ' ';
963 const jschar *lineno_chars = lineno_as_str->getChars(cx);
964 if (!lineno_chars)
965 return false;
966 js_strncpy(cp, lineno_chars, lineno_length);
967 cp += lineno_length;
970 *cp++ = ')'; *cp++ = ')'; *cp = 0;
972 result = js_NewString(cx, chars, length);
973 if (!result) {
974 cx->free(chars);
975 return false;
977 vp->setString(result);
978 return true;
981 #endif
983 static JSFunctionSpec exception_methods[] = {
984 #if JS_HAS_TOSOURCE
985 JS_FN(js_toSource_str, exn_toSource, 0,0),
986 #endif
987 JS_FN(js_toString_str, exn_toString, 0,0),
988 JS_FS_END
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);
1009 JSObject *
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))
1024 return NULL;
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];
1036 JSObject *proto =
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,
1041 NULL, NULL);
1042 if (!proto)
1043 return NULL;
1044 JS_ASSERT(proto->privateData == NULL);
1046 if (i == JSEXN_ERR)
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)) {
1063 return NULL;
1067 return error_proto;
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);
1080 if (!errorString)
1081 errorString = js_GetErrorMessage(userRef, locale, errorNumber);
1082 return errorString;
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},
1090 #include "js.msg"
1091 #undef MSG_DEF
1093 #endif /* DEBUG */
1095 JSBool
1096 js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
1097 JSErrorCallback callback, void *userRef)
1099 JSErrNum errorNumber;
1100 const JSErrorFormatString *errorString;
1101 JSExnType exn;
1102 jsval tv[4];
1103 JSBool ok;
1104 JSObject *errProto, *errObject;
1105 JSString *messageStr, *filenameStr;
1108 * Tell our caller to report immediately if this report is just a warning.
1110 JS_ASSERT(reportp);
1111 if (JSREPORT_IS_WARNING(reportp->flags))
1112 return JS_FALSE;
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);
1118 else
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);
1128 #endif
1131 * Return false (no exception raised) if no exception is associated
1132 * with the given error number.
1134 if (exn == JSEXN_NONE)
1135 return JS_FALSE;
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)
1144 return JS_FALSE;
1146 MUST_FLOW_THROUGH("out");
1147 cx->generatingError = JS_TRUE;
1149 /* Protect the newly-created strings below from nesting GCs. */
1150 PodArrayZero(tv);
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);
1159 if (!ok)
1160 goto out;
1161 tv[0] = OBJECT_TO_JSVAL(errProto);
1163 errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent());
1164 if (!errObject) {
1165 ok = JS_FALSE;
1166 goto out;
1168 tv[1] = OBJECT_TO_JSVAL(errObject);
1170 messageStr = JS_NewStringCopyZ(cx, message);
1171 if (!messageStr) {
1172 ok = JS_FALSE;
1173 goto out;
1175 tv[2] = STRING_TO_JSVAL(messageStr);
1177 filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
1178 if (!filenameStr) {
1179 ok = JS_FALSE;
1180 goto out;
1182 tv[3] = STRING_TO_JSVAL(filenameStr);
1184 ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
1185 reportp->lineno, reportp);
1186 if (!ok)
1187 goto out;
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;
1194 out:
1195 cx->generatingError = JS_FALSE;
1196 return ok;
1199 JSBool
1200 js_ReportUncaughtException(JSContext *cx)
1202 jsval exn;
1203 JSObject *exnObject;
1204 jsval roots[5];
1205 JSErrorReport *reportp, report;
1206 JSString *str;
1207 const char *bytes;
1209 if (!JS_IsExceptionPending(cx))
1210 return true;
1212 if (!JS_GetPendingException(cx, &exn))
1213 return false;
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)) {
1225 exnObject = NULL;
1226 } else {
1227 exnObject = JSVAL_TO_OBJECT(exn);
1228 roots[0] = 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;
1237 if (!str) {
1238 bytes = "unknown (can't convert to string)";
1239 } else {
1240 roots[1] = STRING_TO_JSVAL(str);
1241 if (!bytesStorage.encode(cx, str))
1242 return false;
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]))
1249 return false;
1250 if (JSVAL_IS_STRING(roots[2])) {
1251 bytesStorage.clear();
1252 if (!bytesStorage.encode(cx, str))
1253 return false;
1254 bytes = bytesStorage.ptr();
1257 if (!JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]))
1258 return false;
1259 str = js_ValueToString(cx, Valueify(roots[3]));
1260 if (!str || !filename.encode(cx, str))
1261 return false;
1263 if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]))
1264 return false;
1265 uint32_t lineno;
1266 if (!ValueToECMAUint32(cx, Valueify(roots[4]), &lineno))
1267 return false;
1269 reportp = &report;
1270 PodZero(&report);
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)
1276 return false;
1280 if (!reportp) {
1281 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1282 JSMSG_UNCAUGHT_EXCEPTION, bytes);
1283 } else {
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);
1293 return true;