Further exception work - added 'message' to the js_ErrorToException

call, and experimented with copying the original JSErrorReport into
private data.  Much of this to support a toString method for exception
objects.

It's not polished, but I wanted to get toString available quickly.
This commit is contained in:
mccabe%netscape.com 1998-09-02 20:01:34 +00:00
Родитель 7a0083eb26
Коммит 30521bb1b0
5 изменённых файлов: 301 добавлений и 39 удалений

Просмотреть файл

@ -187,7 +187,7 @@ Process(JSContext *cx, JSObject *obj, char *filename)
/* XXX non-i18nized strings... */
if (str) {
fprintf(stderr, "Uncaught javascript exception: %s\n",
fprintf(stderr, "Uncaught javascript exception:\n%s\n",
JS_GetStringBytes(str));
} else {
fprintf(stderr, "Uncaught javascript exception\n");

Просмотреть файл

@ -303,7 +303,7 @@ endif
void
js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallBack callback,
const uintN errorNumber, va_list ap)
const JSErrNum errorNumber, va_list ap)
{
JSStackFrame *fp;
JSErrorReport report, *reportp;
@ -342,7 +342,7 @@ js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallBack callback,
* exception is thrown, then the JSREPORT_EXCEPTION flag will be set
* on the error report, and exception-aware hosts should ignore it.
*/
js_ErrorToException(cx, reportp);
js_ErrorToException(cx, reportp, message);
#endif
js_ReportErrorAgain(cx, message, reportp);

Просмотреть файл

@ -20,9 +20,17 @@
* JS standard exception implementation.
*/
/*
* This is currently very messy, and in flux. Don't anybody think
* I'm going to leave it like this. No no.
*/
#include "jsstddef.h"
#include "prtypes.h"
#include "prassert.h"
#include "prprintf.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jsconfig.h"
@ -33,15 +41,137 @@
# error "JS_HAS_EXCEPTIONS must be defined to use JS_HAS_ERROR_EXCEPTIONS"
#endif
/* Maybe #define RANDOM_CLASS(name, prototype) 4 here? */
JSObject *tlobj;
struct JSExnSpec {
int protoIndex;
JSClass theclass;
};
/*
* This could end up just being the error report, but I
* want to learn how to support random garbage here.
*
* If I go with it, a rambling comment mentioning GC, memory management etc. is
* needed.
*
* I'd rather have the errorReport inline, to avoid the extra malloc'd object
* dangle. But I'll figure that out later, if I still need it.
*/
typedef struct JSExnPrivate {
JSErrorReport *errorReport;
const char *message;
} JSExnPrivate;
/* This must be kept in synch with the exceptions array */
/*
* Copy everything interesting about an error into allocated memory.
*/
static JSExnPrivate *
exn_initPrivate(JSContext *cx, JSErrorReport *report, const char *message)
{
JSExnPrivate *newPrivate;
JSErrorReport * newReport;
char *newMessage;
newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate));
PR_ASSERT(message);
newMessage = (char *)JS_malloc(cx, strlen(message));
strcpy(newMessage, message);
newPrivate->message = newMessage;
/* Copy the error report */
newReport = (JSErrorReport *)JS_malloc(cx, sizeof (JSErrorReport));
if (report->filename) {
newReport->filename =
(const char *)JS_malloc(cx, strlen(report->filename));
/* Ack. Const! */
strcpy((char *)newReport->filename, report->filename);
} else {
newReport->filename = NULL;
}
newReport->lineno = report->lineno;
/*
* We don't need to copy linebuf and tokenptr, because they
* point into the deflated string cache. (currently?)
*/
newReport->linebuf = report->linebuf;
newReport->tokenptr = report->tokenptr;
/*
* But we do need to copy uclinebuf, uctokenptr, because they're
* pointers into internal tokenstream structs, and may go away.
* But only if they're non-null...
*
* NOTE nothing uses this and I'm not really maintaining it until
* I know it's the desired API.
*
* Temporarily disabled, because uclinebuf is 0x10 when I evaluate 'Math()'!
*/
#if 0
if (report->uclinebuf) {
size_t len = js_strlen(report->uclinebuf);
newReport->uclinebuf =
(const jschar *)JS_malloc(cx, len);
js_strncpy(newReport->uclinebuf, report->uclinebuf, len);
newReport->uctokenptr = newReport->uclinebuf + (report->uctokenptr -
report->uclinebuf);
} else
#endif
newReport->uclinebuf = newReport->uctokenptr = NULL;
/* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
newReport->flags = report->flags;
/* Skipping *ucmessage, **messageArgs for now. My guess is that it'll just
* mean copying the pointers, and adding another GC root. Then de-rooting
* them in the finalizer. Dunno if they're rooted in the first place -
* maybe it's only relevant for an exception that goes where it pleases,
* and not for the formerly stack-bound use of the error report.
*/
newPrivate->errorReport = newReport;
return newPrivate;
}
/*
* Undo all the damage done by exn_initPrivate.
*/
void
exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData)
{
PR_ASSERT(privateData->message);
/* ! what does const do? */
JS_free(cx, (void *)privateData->message);
PR_ASSERT(privateData->errorReport);
if (privateData->errorReport->uclinebuf)
JS_free(cx, (void *)privateData->errorReport->uclinebuf);
if (privateData->errorReport->filename)
JS_free(cx, (void *)privateData->errorReport->filename);
JS_free(cx, privateData->errorReport);
JS_free(cx, privateData);
}
/* Destroy associated data... */
static void
exn_finalize(JSContext *cx, JSObject *obj)
{
JSExnPrivate *privateData;
privateData = (JSExnPrivate *)
JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));
if (privateData) {
exn_destroyPrivate(cx, privateData);
}
}
/* This must be kept in synch with the exceptions array below. */
typedef enum JSExnType {
JSEXN_NONE = -1,
JSEXN_EXCEPTION,
@ -62,71 +192,81 @@ typedef enum JSExnType {
#define FLAGS JSCLASS_HAS_PRIVATE
/* Maybe #define RANDOM_CLASS(name, prototype) 4 here? */
struct JSExnSpec {
int protoIndex;
JSClass theclass;
};
/*
* I want to replace all of these with just one class. All we really care
* about is the prototypes, and the constructor names.
*/
static struct JSExnSpec exceptions[] = {
{ JSEXN_NONE, /* No proto? */ {
"Exception", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_EXCEPTION, {
"Error", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_ERR, {
"InternalError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_ERR, {
"SyntaxError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_ERR, {
"ReferenceError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_ERR, {
"CallError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_CALLERR, {
"TargetError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_ERR, {
"ConstructorError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_ERR, {
"ConversionError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_CONVERSIONERR, {
"ToObjectError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_CONVERSIONERR, {
"ToPrimitiveError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_CONVERSIONERR, {
"DefaultValueError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{ JSEXN_ERR, {
"ArrayError", FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, exn_finalize
} },
{0}
};
@ -134,17 +274,77 @@ static struct JSExnSpec exceptions[] = {
static JSBool
Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsval bval;
bval = JSVAL_FALSE;
if (!cx->fp->constructing) {
*rval = bval;
return JS_TRUE;
}
/* gotta null out that private data */
OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_NULL);
return JS_TRUE;
}
/*
* Convert to string. Much of this is taken from js.c.
*
* I should rewrite this to use message, line, file etc. from
* javascript-modifiable properties (which might be lazily created
* from the encapsulated error report.)
*/
static JSBool
exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
JSExnPrivate *privateData;
JSErrorReport *report;
jsval v;
char *name;
JSClass *theclass;
/* Check needed against incompatible target... */
/* Try to include the exception name in the error message. */
theclass = OBJ_GET_CLASS(cx, obj);
name = theclass->name;
v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
if (!JSVAL_IS_NULL(v)) {
char *msgbuf, *tmp;
privateData = JSVAL_TO_PRIVATE(v);
report = privateData->errorReport;
msgbuf = PR_smprintf("%s:", name);
if (report->filename) {
tmp = msgbuf;
msgbuf = PR_smprintf("%s%s:", tmp, report->filename);
JS_free(cx, tmp);
}
if (report->lineno) {
tmp = msgbuf;
msgbuf = PR_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
if (tmp)
JS_free(cx, tmp);
}
PR_ASSERT(privateData->message);
tmp = msgbuf;
msgbuf = PR_smprintf("%s%s", tmp ? tmp : "", privateData->message);
if(tmp)
JS_free(cx, tmp);
str = JS_NewStringCopyZ(cx, msgbuf);
} else {
str = JS_NewStringCopyZ(cx, "some non-engine-thrown exception");
}
if (!str)
return JS_FALSE;
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
static JSFunctionSpec exception_methods[] = {
{js_toString_str, exn_toString, 0},
{0}
};
@ -165,6 +365,9 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj)
exception_methods,
NULL,
NULL);
/* So finalize knows whether to. */
OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_NULL);
}
/*
@ -179,6 +382,25 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj)
return protos[0];
}
JSErrorReport *
js_GetErrorFromException(JSContext *cx, JSObject *errobj) {
JSExnPrivate *privateData;
#if 0
{
JSClass *errobjclass;
/* Assert that we have an Exception object */
/* This assert does the right thing, but we can't use it yet, because
* we're throwing lots of different exception classes. */
errobjclass = OBJ_GET_CLASS(cx, errobj);
PR_ASSERT(errobjclass == &(exceptions[JSEXN_CALLERR].theclass));
}
#endif
privateData = JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, errobj, JSSLOT_PRIVATE));
/* Still OK to return NULL, tho. */
return privateData->errorReport;
}
static JSExnType errorToException[] = {
#define MSG_DEF(name, number, count, exception, format) \
exception,
@ -186,27 +408,67 @@ static JSExnType errorToException[] = {
#undef MSG_DEF
};
#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
/* For use below... get character strings for error name and exception name */
static struct exnname { char *name; char *exception; } errortoexnname[] = {
#define MSG_DEF(name, number, count, exception, format) \
{#name, #exception},
#include "jsmsg.def"
#undef MSG_DEF
};
#endif /* DEBUG */
JSBool
js_ErrorToException(JSContext *cx, JSErrorReport *reportp) {
js_ErrorToException(JSContext *cx, JSErrorReport *reportp, const char *message) {
JSErrNum errorNumber;
JSObject *errobj;
JSExnType exn;
JSExnPrivate *privateData;
PR_ASSERT(reportp);
errorNumber = reportp->errorNumber;
exn = errorToException[errorNumber];
PR_ASSERT(exn < JSEXN_LIMIT);
if (exn > JSEXN_NONE) {
errobj = js_ConstructObject(cx,
&(exceptions[exn].theclass),
NULL, NULL);
JS_SetPendingException(cx, OBJECT_TO_JSVAL(errobj));
reportp->flags |= JSREPORT_EXCEPTION;
return JS_TRUE;
}
return JS_FALSE;
#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
/* Print the error name and the associated exception name to stderr */
fprintf(stderr, "%s\t%s\n",
errortoexnname[errorNumber].name,
errortoexnname[errorNumber].exception);
#endif
/*
* Return false (no exception raised) if no exception is associated
* with the given error number.
*/
if (exn == JSEXN_NONE)
return JS_FALSE;
/*
* Should (?) be js_ConstructObject... switching to NewObject
* in the speculation that it won't require a frame. DefaultValue trouble.
* And it seems to work???? For the record, the trouble was that
* cx->fp was null when trying to construct the object...
*/
errobj = js_NewObject(cx,
&(exceptions[exn].theclass),
NULL, NULL);
/*
* Construct a new copy of the error report, and store it in the
* exception objects' private data. We can't use the error report
* handed in, because it's stack-allocated, and may point to transient
* data in the JSTokenStream.
*/
/* XXX report failure? */
privateData = exn_initPrivate(cx, reportp, message);
OBJ_SET_SLOT(cx, errobj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData));
JS_SetPendingException(cx, OBJECT_TO_JSVAL(errobj));
reportp->flags |= JSREPORT_EXCEPTION;
return JS_TRUE;
}
#endif /* JS_HAS_ERROR_EXCEPTIONS */

Просмотреть файл

@ -40,7 +40,7 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj);
* exception is found, JS_FALSE if none.
*/
extern JSBool
js_ErrorToException(JSContext *cx, JSErrorReport *reportp);
js_ErrorToException(JSContext *cx, JSErrorReport *reportp, const char *message);
PR_END_EXTERN_C

Просмотреть файл

@ -597,7 +597,7 @@ js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, uintN flags,
* XXX it'd probably be best if there was only one call to this
* function, but there seem to be two error reporter call points.
*/
(void)js_ErrorToException(cx, &report);
(void)js_ErrorToException(cx, &report, message);
#endif
(*onError)(cx, message, &report);