~mc.../inkscape/inkscape

« back to all changes in this revision

Viewing changes to src/dom/js/jsexn.c

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * ***** BEGIN LICENSE BLOCK *****
 
4
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
5
 *
 
6
 * The contents of this file are subject to the Mozilla Public License Version
 
7
 * 1.1 (the "License"); you may not use this file except in compliance with
 
8
 * the License. You may obtain a copy of the License at
 
9
 * http://www.mozilla.org/MPL/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Mozilla Communicator client code, released
 
17
 * March 31, 1998.
 
18
 *
 
19
 * The Initial Developer of the Original Code is
 
20
 * Netscape Communications Corporation.
 
21
 * Portions created by the Initial Developer are Copyright (C) 1998
 
22
 * the Initial Developer. All Rights Reserved.
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * Alternatively, the contents of this file may be used under the terms of
 
27
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 
28
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
29
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
30
 * of those above. If you wish to allow use of your version of this file only
 
31
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
32
 * use your version of this file under the terms of the MPL, indicate your
 
33
 * decision by deleting the provisions above and replace them with the notice
 
34
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
35
 * the provisions above, a recipient may use your version of this file under
 
36
 * the terms of any one of the MPL, the GPL or the LGPL.
 
37
 *
 
38
 * ***** END LICENSE BLOCK ***** */
 
39
 
 
40
/*
 
41
 * JS standard exception implementation.
 
42
 */
 
43
 
 
44
#include "jsstddef.h"
 
45
#include <stdlib.h>
 
46
#include <string.h>
 
47
#include "jstypes.h"
 
48
#include "jsbit.h"
 
49
#include "jsutil.h" /* Added by JSIFY */
 
50
#include "jsprf.h"
 
51
#include "jsapi.h"
 
52
#include "jscntxt.h"
 
53
#include "jsconfig.h"
 
54
#include "jsexn.h"
 
55
#include "jsfun.h"
 
56
#include "jsinterp.h"
 
57
#include "jsopcode.h"
 
58
#include "jsnum.h"
 
59
#include "jsscript.h"
 
60
 
 
61
#if JS_HAS_ERROR_EXCEPTIONS
 
62
#if !JS_HAS_EXCEPTIONS
 
63
# error "JS_HAS_EXCEPTIONS must be defined to use JS_HAS_ERROR_EXCEPTIONS"
 
64
#endif
 
65
 
 
66
/* XXX consider adding rt->atomState.messageAtom */
 
67
static char js_message_str[]  = "message";
 
68
static char js_filename_str[] = "fileName";
 
69
static char js_lineno_str[]   = "lineNumber";
 
70
static char js_stack_str[]    = "stack";
 
71
 
 
72
/* Forward declarations for ExceptionClass's initializer. */
 
73
static JSBool
 
74
Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 
75
 
 
76
static void
 
77
exn_finalize(JSContext *cx, JSObject *obj);
 
78
 
 
79
static JSClass ExceptionClass = {
 
80
    "Error",
 
81
    JSCLASS_HAS_PRIVATE,
 
82
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
 
83
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   exn_finalize,
 
84
    NULL,             NULL,             NULL,             Exception,
 
85
    NULL,             NULL,             NULL,             0
 
86
};
 
87
 
 
88
/*
 
89
 * A copy of the JSErrorReport originally generated.
 
90
 */
 
91
typedef struct JSExnPrivate {
 
92
    JSErrorReport *errorReport;
 
93
} JSExnPrivate;
 
94
 
 
95
/*
 
96
 * Undo all the damage done by exn_newPrivate.
 
97
 */
 
98
static void
 
99
exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData)
 
100
{
 
101
    JSErrorReport *report;
 
102
    const jschar **args;
 
103
 
 
104
    if (!privateData)
 
105
        return;
 
106
    report = privateData->errorReport;
 
107
    if (report) {
 
108
        if (report->uclinebuf)
 
109
            JS_free(cx, (void *)report->uclinebuf);
 
110
        if (report->filename)
 
111
            JS_free(cx, (void *)report->filename);
 
112
        if (report->ucmessage)
 
113
            JS_free(cx, (void *)report->ucmessage);
 
114
        if (report->messageArgs) {
 
115
            args = report->messageArgs;
 
116
            while (*args != NULL)
 
117
                JS_free(cx, (void *)*args++);
 
118
            JS_free(cx, (void *)report->messageArgs);
 
119
        }
 
120
        JS_free(cx, report);
 
121
    }
 
122
    JS_free(cx, privateData);
 
123
}
 
124
 
 
125
/*
 
126
 * Copy everything interesting about an error into allocated memory.
 
127
 */
 
128
static JSExnPrivate *
 
129
exn_newPrivate(JSContext *cx, JSErrorReport *report)
 
130
{
 
131
    intN i;
 
132
    JSExnPrivate *newPrivate;
 
133
    JSErrorReport *newReport;
 
134
    size_t capacity;
 
135
 
 
136
    newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate));
 
137
    if (!newPrivate)
 
138
        return NULL;
 
139
    memset(newPrivate, 0, sizeof (JSExnPrivate));
 
140
 
 
141
    /* Copy the error report */
 
142
    newReport = (JSErrorReport *)JS_malloc(cx, sizeof (JSErrorReport));
 
143
    if (!newReport)
 
144
        goto error;
 
145
    memset(newReport, 0, sizeof (JSErrorReport));
 
146
    newPrivate->errorReport = newReport;
 
147
 
 
148
    if (report->filename != NULL) {
 
149
        newReport->filename = JS_strdup(cx, report->filename);
 
150
        if (!newReport->filename)
 
151
            goto error;
 
152
    } else {
 
153
        newReport->filename = NULL;
 
154
    }
 
155
 
 
156
    newReport->lineno = report->lineno;
 
157
 
 
158
    /*
 
159
     * We don't need to copy linebuf and tokenptr, because they
 
160
     * point into the deflated string cache.  (currently?)
 
161
     */
 
162
    newReport->linebuf = report->linebuf;
 
163
    newReport->tokenptr = report->tokenptr;
 
164
 
 
165
    /*
 
166
     * But we do need to copy uclinebuf, uctokenptr, because they're
 
167
     * pointers into internal tokenstream structs, and may go away.
 
168
     */
 
169
    if (report->uclinebuf != NULL) {
 
170
        capacity = js_strlen(report->uclinebuf) + 1;
 
171
        newReport->uclinebuf =
 
172
            (const jschar *)JS_malloc(cx, capacity * sizeof(jschar));
 
173
        if (!newReport->uclinebuf)
 
174
            goto error;
 
175
        js_strncpy((jschar *)newReport->uclinebuf, report->uclinebuf, capacity);
 
176
        newReport->uctokenptr = newReport->uclinebuf + (report->uctokenptr -
 
177
                                                        report->uclinebuf);
 
178
    } else {
 
179
        newReport->uclinebuf = newReport->uctokenptr = NULL;
 
180
    }
 
181
 
 
182
    if (report->ucmessage != NULL) {
 
183
        capacity = js_strlen(report->ucmessage) + 1;
 
184
        newReport->ucmessage = (const jschar *)
 
185
            JS_malloc(cx, capacity * sizeof(jschar));
 
186
        if (!newReport->ucmessage)
 
187
            goto error;
 
188
        js_strncpy((jschar *)newReport->ucmessage, report->ucmessage, capacity);
 
189
 
 
190
        if (report->messageArgs) {
 
191
            for (i = 0; report->messageArgs[i] != NULL; i++)
 
192
                continue;
 
193
            JS_ASSERT(i);
 
194
            newReport->messageArgs =
 
195
                (const jschar **)JS_malloc(cx, (i + 1) * sizeof(jschar *));
 
196
            if (!newReport->messageArgs)
 
197
                goto error;
 
198
            for (i = 0; report->messageArgs[i] != NULL; i++) {
 
199
                capacity = js_strlen(report->messageArgs[i]) + 1;
 
200
                newReport->messageArgs[i] =
 
201
                    (const jschar *)JS_malloc(cx, capacity * sizeof(jschar));
 
202
                if (!newReport->messageArgs[i])
 
203
                    goto error;
 
204
                js_strncpy((jschar *)(newReport->messageArgs[i]),
 
205
                           report->messageArgs[i], capacity);
 
206
            }
 
207
            newReport->messageArgs[i] = NULL;
 
208
        } else {
 
209
            newReport->messageArgs = NULL;
 
210
        }
 
211
    } else {
 
212
        newReport->ucmessage = NULL;
 
213
        newReport->messageArgs = NULL;
 
214
    }
 
215
    newReport->errorNumber = report->errorNumber;
 
216
 
 
217
    /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
 
218
    newReport->flags = report->flags;
 
219
 
 
220
    return newPrivate;
 
221
error:
 
222
    exn_destroyPrivate(cx, newPrivate);
 
223
    return NULL;
 
224
}
 
225
 
 
226
static void
 
227
exn_finalize(JSContext *cx, JSObject *obj)
 
228
{
 
229
    JSExnPrivate *privateData;
 
230
    jsval privateValue;
 
231
 
 
232
    privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
 
233
 
 
234
    if (!JSVAL_IS_VOID(privateValue)) {
 
235
        privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue);
 
236
        if (privateData)
 
237
            exn_destroyPrivate(cx, privateData);
 
238
    }
 
239
}
 
240
 
 
241
JSErrorReport *
 
242
js_ErrorFromException(JSContext *cx, jsval exn)
 
243
{
 
244
    JSObject *obj;
 
245
    JSExnPrivate *privateData;
 
246
    jsval privateValue;
 
247
 
 
248
    if (JSVAL_IS_PRIMITIVE(exn))
 
249
        return NULL;
 
250
    obj = JSVAL_TO_OBJECT(exn);
 
251
    if (OBJ_GET_CLASS(cx, obj) != &ExceptionClass)
 
252
        return NULL;
 
253
    privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
 
254
    if (JSVAL_IS_VOID(privateValue))
 
255
        return NULL;
 
256
    privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue);
 
257
    if (!privateData)
 
258
        return NULL;
 
259
 
 
260
    JS_ASSERT(privateData->errorReport);
 
261
    return privateData->errorReport;
 
262
}
 
263
 
 
264
/*
 
265
 * This must be kept in synch with the exceptions array below.
 
266
 * XXX use a jsexn.tbl file a la jsopcode.tbl
 
267
 */
 
268
typedef enum JSExnType {
 
269
    JSEXN_NONE = -1,
 
270
      JSEXN_ERR,
 
271
        JSEXN_INTERNALERR,
 
272
        JSEXN_EVALERR,
 
273
        JSEXN_RANGEERR,
 
274
        JSEXN_REFERENCEERR,
 
275
        JSEXN_SYNTAXERR,
 
276
        JSEXN_TYPEERR,
 
277
        JSEXN_URIERR,
 
278
        JSEXN_LIMIT
 
279
} JSExnType;
 
280
 
 
281
struct JSExnSpec {
 
282
    int protoIndex;
 
283
    const char *name;
 
284
    JSNative native;
 
285
};
 
286
 
 
287
/*
 
288
 * All *Error constructors share the same JSClass, ExceptionClass.  But each
 
289
 * constructor function for an *Error class must have a distinct native 'call'
 
290
 * function pointer, in order for instanceof to work properly across multiple
 
291
 * standard class sets.  See jsfun.c:fun_hasInstance.
 
292
 */
 
293
#define MAKE_EXCEPTION_CTOR(name)                                             \
 
294
const char js_##name##_str[] = #name;                                         \
 
295
static JSBool                                                                 \
 
296
name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)      \
 
297
{                                                                             \
 
298
    return Exception(cx, obj, argc, argv, rval);                              \
 
299
}
 
300
 
 
301
MAKE_EXCEPTION_CTOR(Error)
 
302
MAKE_EXCEPTION_CTOR(InternalError)
 
303
MAKE_EXCEPTION_CTOR(EvalError)
 
304
MAKE_EXCEPTION_CTOR(RangeError)
 
305
MAKE_EXCEPTION_CTOR(ReferenceError)
 
306
MAKE_EXCEPTION_CTOR(SyntaxError)
 
307
MAKE_EXCEPTION_CTOR(TypeError)
 
308
MAKE_EXCEPTION_CTOR(URIError)
 
309
 
 
310
#undef MAKE_EXCEPTION_CTOR
 
311
 
 
312
static struct JSExnSpec exceptions[] = {
 
313
    { JSEXN_NONE,       js_Error_str,           Error },
 
314
    { JSEXN_ERR,        js_InternalError_str,   InternalError },
 
315
    { JSEXN_ERR,        js_EvalError_str,       EvalError },
 
316
    { JSEXN_ERR,        js_RangeError_str,      RangeError },
 
317
    { JSEXN_ERR,        js_ReferenceError_str,  ReferenceError },
 
318
    { JSEXN_ERR,        js_SyntaxError_str,     SyntaxError },
 
319
    { JSEXN_ERR,        js_TypeError_str,       TypeError },
 
320
    { JSEXN_ERR,        js_URIError_str,        URIError },
 
321
    {0,NULL,NULL}
 
322
};
 
323
 
 
324
static JSBool
 
325
InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message,
 
326
                    JSString *filename, uintN lineno)
 
327
{
 
328
    JSCheckAccessOp checkAccess;
 
329
    JSErrorReporter older;
 
330
    JSExceptionState *state;
 
331
    jschar *stackbuf;
 
332
    size_t stacklen, stackmax;
 
333
    JSStackFrame *fp;
 
334
    jsval callerid, v;
 
335
    JSBool ok;
 
336
    JSString *argsrc, *stack;
 
337
    uintN i, ulineno;
 
338
    const char *cp;
 
339
    char ulnbuf[11];
 
340
 
 
341
    if (!JS_DefineProperty(cx, obj, js_message_str, STRING_TO_JSVAL(message),
 
342
                           NULL, NULL, JSPROP_ENUMERATE)) {
 
343
        return JS_FALSE;
 
344
    }
 
345
 
 
346
    if (!JS_DefineProperty(cx, obj, js_filename_str,
 
347
                           STRING_TO_JSVAL(filename),
 
348
                           NULL, NULL, JSPROP_ENUMERATE)) {
 
349
        return JS_FALSE;
 
350
    }
 
351
 
 
352
    if (!JS_DefineProperty(cx, obj, js_lineno_str,
 
353
                           INT_TO_JSVAL(lineno),
 
354
                           NULL, NULL, JSPROP_ENUMERATE)) {
 
355
        return JS_FALSE;
 
356
    }
 
357
 
 
358
    /*
 
359
     * Set the 'stack' property.
 
360
     *
 
361
     * First, set aside any error reporter for cx and save its exception state
 
362
     * so we can suppress any checkAccess failures.  Such failures should stop
 
363
     * the backtrace procedure, not result in a failure of this constructor.
 
364
     */
 
365
    checkAccess = cx->runtime->checkObjectAccess;
 
366
    if (checkAccess) {
 
367
        older = JS_SetErrorReporter(cx, NULL);
 
368
        state = JS_SaveExceptionState(cx);
 
369
    }
 
370
#ifdef __GNUC__         /* suppress bogus gcc warnings */
 
371
    else {
 
372
        older = NULL;
 
373
        state = NULL;
 
374
    }
 
375
#endif
 
376
    callerid = ATOM_KEY(cx->runtime->atomState.callerAtom);
 
377
 
 
378
    /*
 
379
     * Prepare to allocate a jschar buffer at stackbuf, where stacklen indexes
 
380
     * the next free jschar slot, and with room for at most stackmax non-null
 
381
     * jschars.  If stackbuf is non-null, it always contains an extra slot for
 
382
     * the null terminator we'll store at the end, as a backstop.
 
383
     *
 
384
     * All early returns must goto done after this point, till the after-loop
 
385
     * cleanup code has run!
 
386
     */
 
387
    stackbuf = NULL;
 
388
    stacklen = stackmax = 0;
 
389
    ok = JS_TRUE;
 
390
 
 
391
#define APPEND_CHAR_TO_STACK(c)                                               \
 
392
    JS_BEGIN_MACRO                                                            \
 
393
        if (stacklen == stackmax) {                                           \
 
394
            void *ptr_;                                                       \
 
395
            stackmax = stackmax ? 2 * stackmax : 64;                          \
 
396
            ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar));   \
 
397
            if (!ptr_) {                                                      \
 
398
                ok = JS_FALSE;                                                \
 
399
                goto done;                                                    \
 
400
            }                                                                 \
 
401
            stackbuf = ptr_;                                                  \
 
402
        }                                                                     \
 
403
        stackbuf[stacklen++] = (c);                                           \
 
404
    JS_END_MACRO
 
405
 
 
406
#define APPEND_STRING_TO_STACK(str)                                           \
 
407
    JS_BEGIN_MACRO                                                            \
 
408
        JSString *str_ = str;                                                 \
 
409
        size_t length_ = JSSTRING_LENGTH(str_);                               \
 
410
        if (stacklen + length_ > stackmax) {                                  \
 
411
            void *ptr_;                                                       \
 
412
            stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_));            \
 
413
            ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar));   \
 
414
            if (!ptr_) {                                                      \
 
415
                ok = JS_FALSE;                                                \
 
416
                goto done;                                                    \
 
417
            }                                                                 \
 
418
            stackbuf = ptr_;                                                  \
 
419
        }                                                                     \
 
420
        js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_);       \
 
421
        stacklen += length_;                                                  \
 
422
    JS_END_MACRO
 
423
 
 
424
    for (fp = cx->fp; fp; fp = fp->down) {
 
425
        if (checkAccess) {
 
426
            v = (fp->fun && fp->argv) ? fp->argv[-2] : JSVAL_NULL;
 
427
            if (!JSVAL_IS_PRIMITIVE(v)) {
 
428
                ok = checkAccess(cx, fp->fun->object, callerid, JSACC_READ, &v);
 
429
                if (!ok) {
 
430
                    ok = JS_TRUE;
 
431
                    break;
 
432
                }
 
433
            }
 
434
        }
 
435
 
 
436
        if (fp->fun) {
 
437
            if (fp->fun->atom)
 
438
                APPEND_STRING_TO_STACK(ATOM_TO_STRING(fp->fun->atom));
 
439
 
 
440
            APPEND_CHAR_TO_STACK('(');
 
441
            for (i = 0; i < fp->argc; i++) {
 
442
                /* Avoid toSource bloat and fallibility for object types. */
 
443
                v = fp->argv[i];
 
444
                if (JSVAL_IS_PRIMITIVE(v)) {
 
445
                    argsrc = js_ValueToSource(cx, v);
 
446
                } else if (JSVAL_IS_FUNCTION(cx, v)) {
 
447
                    /* XXX Avoid function decompilation bloat for now. */
 
448
                    argsrc = JS_GetFunctionId(JS_ValueToFunction(cx, v));
 
449
                    if (!argsrc)
 
450
                        argsrc = js_ValueToSource(cx, v);
 
451
                } else {
 
452
                    /* XXX Avoid toString on objects, it takes too long and
 
453
                           uses too much memory, for too many classes (see
 
454
                           Mozilla bug 166743). */
 
455
                    char buf[100];
 
456
                    JS_snprintf(buf, sizeof buf, "[object %s]",
 
457
                                OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name);
 
458
                    argsrc = JS_NewStringCopyZ(cx, buf);
 
459
                }
 
460
                if (!argsrc) {
 
461
                    ok = JS_FALSE;
 
462
                    goto done;
 
463
                }
 
464
                if (i > 0)
 
465
                    APPEND_CHAR_TO_STACK(',');
 
466
                APPEND_STRING_TO_STACK(argsrc);
 
467
            }
 
468
            APPEND_CHAR_TO_STACK(')');
 
469
        }
 
470
 
 
471
        APPEND_CHAR_TO_STACK('@');
 
472
        if (fp->script && fp->script->filename) {
 
473
            for (cp = fp->script->filename; *cp; cp++)
 
474
                APPEND_CHAR_TO_STACK(*cp);
 
475
        }
 
476
        APPEND_CHAR_TO_STACK(':');
 
477
        if (fp->script && fp->pc) {
 
478
            ulineno = js_PCToLineNumber(cx, fp->script, fp->pc);
 
479
            JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", ulineno);
 
480
            for (cp = ulnbuf; *cp; cp++)
 
481
                APPEND_CHAR_TO_STACK(*cp);
 
482
        } else {
 
483
            APPEND_CHAR_TO_STACK('0');
 
484
        }
 
485
        APPEND_CHAR_TO_STACK('\n');
 
486
    }
 
487
 
 
488
#undef APPEND_CHAR_TO_STACK
 
489
#undef APPEND_STRING_TO_STACK
 
490
 
 
491
done:
 
492
    if (checkAccess) {
 
493
        if (ok)
 
494
            JS_RestoreExceptionState(cx, state);
 
495
        else
 
496
            JS_DropExceptionState(cx, state);
 
497
        JS_SetErrorReporter(cx, older);
 
498
    }
 
499
    if (!ok) {
 
500
        JS_free(cx, stackbuf);
 
501
        return JS_FALSE;
 
502
    }
 
503
 
 
504
    if (!stackbuf) {
 
505
        stack = cx->runtime->emptyString;
 
506
    } else {
 
507
        /* NB: if stackbuf was allocated, it has room for the terminator. */
 
508
        JS_ASSERT(stacklen <= stackmax);
 
509
        if (stacklen < stackmax) {
 
510
            /*
 
511
             * Realloc can fail when shrinking on some FreeBSD versions, so
 
512
             * don't use JS_realloc here; simply let the oversized allocation
 
513
             * be owned by the string in that rare case.
 
514
             */
 
515
            void *shrunk = realloc(stackbuf, (stacklen+1) * sizeof(jschar));
 
516
            if (shrunk)
 
517
                stackbuf = shrunk;
 
518
        }
 
519
        stackbuf[stacklen] = 0;
 
520
        stack = js_NewString(cx, stackbuf, stacklen, 0);
 
521
        if (!stack) {
 
522
            JS_free(cx, stackbuf);
 
523
            return JS_FALSE;
 
524
        }
 
525
    }
 
526
    return JS_DefineProperty(cx, obj, js_stack_str,
 
527
                             STRING_TO_JSVAL(stack),
 
528
                             NULL, NULL, JSPROP_ENUMERATE);
 
529
}
 
530
 
 
531
static JSBool
 
532
Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
533
{
 
534
    JSBool ok;
 
535
    jsval pval;
 
536
    int32 lineno;
 
537
    JSString *message, *filename;
 
538
 
 
539
    if (cx->creatingException)
 
540
        return JS_FALSE;
 
541
    cx->creatingException = JS_TRUE;
 
542
 
 
543
    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
 
544
        /*
 
545
         * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
 
546
         * called as functions, without operator new.  But as we do not give
 
547
         * each constructor a distinct JSClass, whose .name member is used by
 
548
         * js_NewObject to find the class prototype, we must get the class
 
549
         * prototype ourselves.
 
550
         */
 
551
        ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]),
 
552
                              (jsid)cx->runtime->atomState.classPrototypeAtom,
 
553
                              &pval);
 
554
        if (!ok)
 
555
            goto out;
 
556
        obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(pval), NULL);
 
557
        if (!obj) {
 
558
            ok = JS_FALSE;
 
559
            goto out;
 
560
        }
 
561
        *rval = OBJECT_TO_JSVAL(obj);
 
562
    }
 
563
 
 
564
    /*
 
565
     * If it's a new object of class Exception, then null out the private
 
566
     * data so that the finalizer doesn't attempt to free it.
 
567
     */
 
568
    if (OBJ_GET_CLASS(cx, obj) == &ExceptionClass)
 
569
        OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID);
 
570
 
 
571
    /* Set the 'message' property. */
 
572
    if (argc != 0) {
 
573
        message = js_ValueToString(cx, argv[0]);
 
574
        if (!message) {
 
575
            ok = JS_FALSE;
 
576
            goto out;
 
577
        }
 
578
    } else {
 
579
        message = cx->runtime->emptyString;
 
580
    }
 
581
 
 
582
    /* Set the 'fileName' property. */
 
583
    if (argc > 1) {
 
584
        filename = js_ValueToString(cx, argv[1]);
 
585
        if (!filename) {
 
586
            ok = JS_FALSE;
 
587
            goto out;
 
588
        }
 
589
    } else {
 
590
        filename = cx->runtime->emptyString;
 
591
    }
 
592
 
 
593
    /* Set the 'lineNumber' property. */
 
594
    if (argc > 2) {
 
595
        ok = js_ValueToInt32(cx, argv[2], &lineno);
 
596
        if (!ok)
 
597
            goto out;
 
598
    } else {
 
599
        lineno = 0;
 
600
    }
 
601
 
 
602
    ok = InitExceptionObject(cx, obj, message, filename, lineno);
 
603
 
 
604
out:
 
605
    cx->creatingException = JS_FALSE;
 
606
    return ok;
 
607
}
 
608
 
 
609
/*
 
610
 * Convert to string.
 
611
 *
 
612
 * This method only uses JavaScript-modifiable properties name, message.  It
 
613
 * is left to the host to check for private data and report filename and line
 
614
 * number information along with this message.
 
615
 */
 
616
static JSBool
 
617
exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
618
{
 
619
    jsval v;
 
620
    JSString *name, *message, *result;
 
621
    jschar *chars, *cp;
 
622
    size_t name_length, message_length, length;
 
623
 
 
624
    if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v))
 
625
        return JS_FALSE;
 
626
    name = js_ValueToString(cx, v);
 
627
    if (!name)
 
628
        return JS_FALSE;
 
629
 
 
630
    if (!JS_GetProperty(cx, obj, js_message_str, &v) ||
 
631
        !(message = js_ValueToString(cx, v))) {
 
632
        return JS_FALSE;
 
633
    }
 
634
 
 
635
    if (JSSTRING_LENGTH(message) != 0) {
 
636
        name_length = JSSTRING_LENGTH(name);
 
637
        message_length = JSSTRING_LENGTH(message);
 
638
        length = name_length + message_length + 2;
 
639
        cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar));
 
640
        if (!chars)
 
641
            return JS_FALSE;
 
642
 
 
643
        js_strncpy(cp, JSSTRING_CHARS(name), name_length);
 
644
        cp += name_length;
 
645
        *cp++ = ':'; *cp++ = ' ';
 
646
        js_strncpy(cp, JSSTRING_CHARS(message), message_length);
 
647
        cp += message_length;
 
648
        *cp = 0;
 
649
 
 
650
        result = js_NewString(cx, chars, length, 0);
 
651
        if (!result) {
 
652
            JS_free(cx, chars);
 
653
            return JS_FALSE;
 
654
        }
 
655
    } else {
 
656
        result = name;
 
657
    }
 
658
 
 
659
    *rval = STRING_TO_JSVAL(result);
 
660
    return JS_TRUE;
 
661
}
 
662
 
 
663
#if JS_HAS_TOSOURCE
 
664
/*
 
665
 * Return a string that may eval to something similar to the original object.
 
666
 */
 
667
static JSBool
 
668
exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
669
{
 
670
    jsval v;
 
671
    JSString *name, *message, *filename, *lineno_as_str, *result;
 
672
    int32 lineno;
 
673
    size_t lineno_length, name_length, message_length, filename_length, length;
 
674
    jschar *chars, *cp;
 
675
 
 
676
    if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v))
 
677
        return JS_FALSE;
 
678
    name = js_ValueToString(cx, v);
 
679
    if (!name)
 
680
        return JS_FALSE;
 
681
 
 
682
    if (!JS_GetProperty(cx, obj, js_message_str, &v) ||
 
683
        !(message = js_ValueToSource(cx, v))) {
 
684
        return JS_FALSE;
 
685
    }
 
686
 
 
687
    if (!JS_GetProperty(cx, obj, js_filename_str, &v) ||
 
688
        !(filename = js_ValueToSource(cx, v))) {
 
689
        return JS_FALSE;
 
690
    }
 
691
 
 
692
    if (!JS_GetProperty(cx, obj, js_lineno_str, &v) ||
 
693
        !js_ValueToInt32 (cx, v, &lineno)) {
 
694
        return JS_FALSE;
 
695
    }
 
696
 
 
697
    if (lineno != 0) {
 
698
        if (!(lineno_as_str = js_ValueToString(cx, v))) {
 
699
            return JS_FALSE;
 
700
        }
 
701
        lineno_length = JSSTRING_LENGTH(lineno_as_str);
 
702
    } else {
 
703
        lineno_as_str = NULL;
 
704
        lineno_length = 0;
 
705
    }
 
706
 
 
707
    /* Magic 8, for the characters in ``(new ())''. */
 
708
    name_length = JSSTRING_LENGTH(name);
 
709
    message_length = JSSTRING_LENGTH(message);
 
710
    length = 8 + name_length + message_length;
 
711
 
 
712
    filename_length = JSSTRING_LENGTH(filename);
 
713
    if (filename_length != 0) {
 
714
        /* append filename as ``, {filename}'' */
 
715
        length += 2 + filename_length;
 
716
        if (lineno_as_str) {
 
717
            /* append lineno as ``, {lineno_as_str}'' */
 
718
            length += 2 + lineno_length;
 
719
        }
 
720
    } else {
 
721
        if (lineno_as_str) {
 
722
            /*
 
723
             * no filename, but have line number,
 
724
             * need to append ``, "", {lineno_as_str}''
 
725
             */
 
726
            length += 6 + lineno_length;
 
727
        }
 
728
    }
 
729
 
 
730
    cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar));
 
731
    if (!chars)
 
732
        return JS_FALSE;
 
733
 
 
734
    *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
 
735
    js_strncpy(cp, JSSTRING_CHARS(name), name_length);
 
736
    cp += name_length;
 
737
    *cp++ = '(';
 
738
    if (message_length != 0) {
 
739
        js_strncpy(cp, JSSTRING_CHARS(message), message_length);
 
740
        cp += message_length;
 
741
    }
 
742
 
 
743
    if (filename_length != 0) {
 
744
        /* append filename as ``, {filename}'' */
 
745
        *cp++ = ','; *cp++ = ' ';
 
746
        js_strncpy(cp, JSSTRING_CHARS(filename), filename_length);
 
747
        cp += filename_length;
 
748
    } else {
 
749
        if (lineno_as_str) {
 
750
            /*
 
751
             * no filename, but have line number,
 
752
             * need to append ``, "", {lineno_as_str}''
 
753
             */
 
754
            *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
 
755
        }
 
756
    }
 
757
    if (lineno_as_str) {
 
758
        /* append lineno as ``, {lineno_as_str}'' */
 
759
        *cp++ = ','; *cp++ = ' ';
 
760
        js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length);
 
761
        cp += lineno_length;
 
762
    }
 
763
 
 
764
    *cp++ = ')'; *cp++ = ')'; *cp = 0;
 
765
 
 
766
    result = js_NewString(cx, chars, length, 0);
 
767
    if (!result) {
 
768
        JS_free(cx, chars);
 
769
        return JS_FALSE;
 
770
    }
 
771
    *rval = STRING_TO_JSVAL(result);
 
772
    return JS_TRUE;
 
773
}
 
774
#endif
 
775
 
 
776
static JSFunctionSpec exception_methods[] = {
 
777
#if JS_HAS_TOSOURCE
 
778
    {js_toSource_str,   exn_toSource,           0,0,0},
 
779
#endif
 
780
    {js_toString_str,   exn_toString,           0,0,0},
 
781
    {0,0,0,0,0}
 
782
};
 
783
 
 
784
JSObject *
 
785
js_InitExceptionClasses(JSContext *cx, JSObject *obj)
 
786
{
 
787
    int i;
 
788
    JSObject *protos[JSEXN_LIMIT];
 
789
 
 
790
    /* Initialize the prototypes first. */
 
791
    for (i = 0; exceptions[i].name != 0; i++) {
 
792
        JSAtom *atom;
 
793
        JSFunction *fun;
 
794
        JSString *nameString;
 
795
        int protoIndex = exceptions[i].protoIndex;
 
796
 
 
797
        /* Make the prototype for the current constructor name. */
 
798
        protos[i] = js_NewObject(cx, &ExceptionClass,
 
799
                                 (protoIndex != JSEXN_NONE)
 
800
                                 ? protos[protoIndex]
 
801
                                 : NULL,
 
802
                                 obj);
 
803
        if (!protos[i])
 
804
            return NULL;
 
805
 
 
806
        /* So exn_finalize knows whether to destroy private data. */
 
807
        OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_VOID);
 
808
 
 
809
        atom = js_Atomize(cx, exceptions[i].name, strlen(exceptions[i].name), 0);
 
810
        if (!atom)
 
811
            return NULL;
 
812
 
 
813
        /* Make a constructor function for the current name. */
 
814
        fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0);
 
815
        if (!fun)
 
816
            return NULL;
 
817
 
 
818
        /* Make this constructor make objects of class Exception. */
 
819
        fun->clasp = &ExceptionClass;
 
820
 
 
821
        /* Make the prototype and constructor links. */
 
822
        if (!js_SetClassPrototype(cx, fun->object, protos[i],
 
823
                                  JSPROP_READONLY | JSPROP_PERMANENT)) {
 
824
            return NULL;
 
825
        }
 
826
 
 
827
        /* proto bootstrap bit from JS_InitClass omitted. */
 
828
        nameString = JS_NewStringCopyZ(cx, exceptions[i].name);
 
829
        if (!nameString)
 
830
            return NULL;
 
831
 
 
832
        /* Add the name property to the prototype. */
 
833
        if (!JS_DefineProperty(cx, protos[i], js_name_str,
 
834
                               STRING_TO_JSVAL(nameString),
 
835
                               NULL, NULL,
 
836
                               JSPROP_ENUMERATE)) {
 
837
            return NULL;
 
838
        }
 
839
    }
 
840
 
 
841
    /*
 
842
     * Add an empty message property.  (To Exception.prototype only,
 
843
     * because this property will be the same for all the exception
 
844
     * protos.)
 
845
     */
 
846
    if (!JS_DefineProperty(cx, protos[0], js_message_str,
 
847
                           STRING_TO_JSVAL(cx->runtime->emptyString),
 
848
                           NULL, NULL, JSPROP_ENUMERATE)) {
 
849
        return NULL;
 
850
    }
 
851
    if (!JS_DefineProperty(cx, protos[0], js_filename_str,
 
852
                           STRING_TO_JSVAL(cx->runtime->emptyString),
 
853
                           NULL, NULL, JSPROP_ENUMERATE)) {
 
854
        return NULL;
 
855
    }
 
856
    if (!JS_DefineProperty(cx, protos[0], js_lineno_str,
 
857
                           INT_TO_JSVAL(0),
 
858
                           NULL, NULL, JSPROP_ENUMERATE)) {
 
859
        return NULL;
 
860
    }
 
861
 
 
862
    /*
 
863
     * Add methods only to Exception.prototype, because ostensibly all
 
864
     * exception types delegate to that.
 
865
     */
 
866
    if (!JS_DefineFunctions(cx, protos[0], exception_methods))
 
867
        return NULL;
 
868
 
 
869
    return protos[0];
 
870
}
 
871
 
 
872
static JSExnType errorToExceptionNum[] = {
 
873
#define MSG_DEF(name, number, count, exception, format) \
 
874
    exception,
 
875
#include "js.msg"
 
876
#undef MSG_DEF
 
877
};
 
878
 
 
879
#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
 
880
/* For use below... get character strings for error name and exception name */
 
881
static struct exnname { char *name; char *exception; } errortoexnname[] = {
 
882
#define MSG_DEF(name, number, count, exception, format) \
 
883
    {#name, #exception},
 
884
#include "js.msg"
 
885
#undef MSG_DEF
 
886
};
 
887
#endif /* DEBUG */
 
888
 
 
889
JSBool
 
890
js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
 
891
{
 
892
    JSErrNum errorNumber;
 
893
    JSExnType exn;
 
894
    JSBool ok;
 
895
    JSObject *errProto, *errObject;
 
896
    JSString *messageStr, *filenameStr;
 
897
    uintN lineno;
 
898
    JSExnPrivate *privateData;
 
899
 
 
900
    /*
 
901
     * Tell our caller to report immediately if cx has no active frames, or if
 
902
     * this report is just a warning.
 
903
     */
 
904
    JS_ASSERT(reportp);
 
905
    if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags))
 
906
        return JS_FALSE;
 
907
 
 
908
    /* Find the exception index associated with this error. */
 
909
    errorNumber = (JSErrNum) reportp->errorNumber;
 
910
    exn = errorToExceptionNum[errorNumber];
 
911
    JS_ASSERT(exn < JSEXN_LIMIT);
 
912
 
 
913
#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
 
914
    /* Print the error name and the associated exception name to stderr */
 
915
    fprintf(stderr, "%s\t%s\n",
 
916
            errortoexnname[errorNumber].name,
 
917
            errortoexnname[errorNumber].exception);
 
918
#endif
 
919
 
 
920
    /*
 
921
     * Return false (no exception raised) if no exception is associated
 
922
     * with the given error number.
 
923
     */
 
924
    if (exn == JSEXN_NONE)
 
925
        return JS_FALSE;
 
926
 
 
927
    /*
 
928
     * Prevent runaway recursion, just as the Exception native constructor
 
929
     * must do, via cx->creatingException.  If an out-of-memory error occurs,
 
930
     * no exception object will be created, but we don't assume that OOM is
 
931
     * the only kind of error that subroutines of this function called below
 
932
     * might raise.
 
933
     */
 
934
    if (cx->creatingException)
 
935
        return JS_FALSE;
 
936
    cx->creatingException = JS_TRUE;
 
937
 
 
938
    /*
 
939
     * Try to get an appropriate prototype by looking up the corresponding
 
940
     * exception constructor name in the scope chain of the current context's
 
941
     * top stack frame, or in the global object if no frame is active.
 
942
     *
 
943
     * XXXbe hack around JSCLASS_NEW_RESOLVE code in js_LookupProperty that
 
944
     *       checks cx->fp, cx->fp->pc, and js_CodeSpec[*cx->fp->pc] in order
 
945
     *       to compute resolve flags such as JSRESOLVE_ASSIGNING.  The bug
 
946
     *       is that this "internal" js_GetClassPrototype call may trigger a
 
947
     *       resolve of exceptions[exn].name if the global object uses a lazy
 
948
     *       standard class resolver (see JS_ResolveStandardClass), but the
 
949
     *       current frame and bytecode end up affecting the resolve flags.
 
950
     */
 
951
    {
 
952
        JSStackFrame *fp = cx->fp;
 
953
        jsbytecode *pc = NULL;
 
954
 
 
955
        if (fp) {
 
956
            pc = fp->pc;
 
957
            fp->pc = NULL;
 
958
        }
 
959
        ok = js_GetClassPrototype(cx, exceptions[exn].name, &errProto);
 
960
        if (pc)
 
961
            fp->pc = pc;
 
962
        if (!ok)
 
963
            goto out;
 
964
    }
 
965
 
 
966
    errObject = js_NewObject(cx, &ExceptionClass, errProto, NULL);
 
967
    if (!errObject) {
 
968
        ok = JS_FALSE;
 
969
        goto out;
 
970
    }
 
971
 
 
972
    /*
 
973
     * Set the generated Exception object early, so it won't be GC'd by a last
 
974
     * ditch attempt to collect garbage, or a GC that otherwise nests or races
 
975
     * under any of the following calls.  If one of the following calls fails,
 
976
     * it will overwrite this exception object with one of its own (except in
 
977
     * case of OOM errors, of course).
 
978
     */
 
979
    JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
 
980
 
 
981
    messageStr = JS_NewStringCopyZ(cx, message);
 
982
    if (!messageStr) {
 
983
        ok = JS_FALSE;
 
984
        goto out;
 
985
    }
 
986
 
 
987
    if (reportp) {
 
988
        filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
 
989
        if (!filenameStr) {
 
990
            ok = JS_FALSE;
 
991
            goto out;
 
992
        }
 
993
        lineno = reportp->lineno;
 
994
    } else {
 
995
        filenameStr = cx->runtime->emptyString;
 
996
        lineno = 0;
 
997
    }
 
998
    ok = InitExceptionObject(cx, errObject, messageStr, filenameStr, lineno);
 
999
    if (!ok)
 
1000
        goto out;
 
1001
 
 
1002
    /*
 
1003
     * Construct a new copy of the error report struct, and store it in the
 
1004
     * exception object's private data.  We can't use the error report struct
 
1005
     * that was passed in, because it's stack-allocated, and also because it
 
1006
     * may point to transient data in the JSTokenStream.
 
1007
     */
 
1008
    privateData = exn_newPrivate(cx, reportp);
 
1009
    if (!privateData) {
 
1010
        ok = JS_FALSE;
 
1011
        goto out;
 
1012
    }
 
1013
    OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData));
 
1014
 
 
1015
    /* Flag the error report passed in to indicate an exception was raised. */
 
1016
    reportp->flags |= JSREPORT_EXCEPTION;
 
1017
 
 
1018
out:
 
1019
    cx->creatingException = JS_FALSE;
 
1020
    return ok;
 
1021
}
 
1022
#endif /* JS_HAS_ERROR_EXCEPTIONS */
 
1023
 
 
1024
#if JS_HAS_EXCEPTIONS
 
1025
 
 
1026
JSBool
 
1027
js_ReportUncaughtException(JSContext *cx)
 
1028
{
 
1029
    JSObject *exnObject;
 
1030
    JSString *str;
 
1031
    jsval exn;
 
1032
    JSErrorReport *reportp;
 
1033
    const char *bytes;
 
1034
 
 
1035
    if (!JS_IsExceptionPending(cx))
 
1036
        return JS_FALSE;
 
1037
 
 
1038
    if (!JS_GetPendingException(cx, &exn))
 
1039
        return JS_FALSE;
 
1040
 
 
1041
    /*
 
1042
     * Because js_ValueToString below could error and an exception object
 
1043
     * could become unrooted, we root it here.
 
1044
     */
 
1045
    if (JSVAL_IS_OBJECT(exn) && exn != JSVAL_NULL) {
 
1046
        exnObject = JSVAL_TO_OBJECT(exn);
 
1047
        if (!js_AddRoot(cx, &exnObject, "exn.report.root"))
 
1048
            return JS_FALSE;
 
1049
    } else {
 
1050
        exnObject = NULL;
 
1051
    }
 
1052
 
 
1053
#if JS_HAS_ERROR_EXCEPTIONS
 
1054
    reportp = js_ErrorFromException(cx, exn);
 
1055
#else
 
1056
    reportp = NULL;
 
1057
#endif
 
1058
 
 
1059
    str = js_ValueToString(cx, exn);
 
1060
    bytes = str ? js_GetStringBytes(str) : "null";
 
1061
 
 
1062
    if (reportp == NULL) {
 
1063
        /*
 
1064
         * XXXmccabe todo: Instead of doing this, synthesize an error report
 
1065
         * struct that includes the filename, lineno where the exception was
 
1066
         * originally thrown.
 
1067
         */
 
1068
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
1069
                             JSMSG_UNCAUGHT_EXCEPTION, bytes);
 
1070
    } else {
 
1071
        /* Flag the error as an exception. */
 
1072
        reportp->flags |= JSREPORT_EXCEPTION;
 
1073
        js_ReportErrorAgain(cx, bytes, reportp);
 
1074
    }
 
1075
 
 
1076
    if (exnObject != NULL)
 
1077
        js_RemoveRoot(cx->runtime, &exnObject);
 
1078
    return JS_TRUE;
 
1079
}
 
1080
 
 
1081
#endif /* JS_HAS_EXCEPTIONS */