1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
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/
11 : *
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.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
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.
24 : *
25 : * Contributor(s):
26 : *
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.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS standard exception implementation.
43 : */
44 : #include <stdlib.h>
45 : #include <string.h>
46 :
47 : #include "mozilla/Util.h"
48 :
49 : #include "jstypes.h"
50 : #include "jsutil.h"
51 : #include "jsprf.h"
52 : #include "jsapi.h"
53 : #include "jscntxt.h"
54 : #include "jsversion.h"
55 : #include "jsexn.h"
56 : #include "jsfun.h"
57 : #include "jsgc.h"
58 : #include "jsgcmark.h"
59 : #include "jsinterp.h"
60 : #include "jsnum.h"
61 : #include "jsobj.h"
62 : #include "jsopcode.h"
63 : #include "jsscope.h"
64 : #include "jsscript.h"
65 : #include "jswrapper.h"
66 :
67 : #include "vm/GlobalObject.h"
68 :
69 : #include "jsinferinlines.h"
70 : #include "jsobjinlines.h"
71 :
72 : #include "vm/Stack-inl.h"
73 : #include "vm/String-inl.h"
74 : #include "vm/StringBuffer-inl.h"
75 :
76 : using namespace mozilla;
77 : using namespace js;
78 : using namespace js::gc;
79 : using namespace js::types;
80 :
81 : /* Forward declarations for ErrorClass's initializer. */
82 : static JSBool
83 : Exception(JSContext *cx, unsigned argc, Value *vp);
84 :
85 : static void
86 : exn_trace(JSTracer *trc, JSObject *obj);
87 :
88 : static void
89 : exn_finalize(JSContext *cx, JSObject *obj);
90 :
91 : static JSBool
92 : exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
93 : JSObject **objp);
94 :
95 : Class js::ErrorClass = {
96 : js_Error_str,
97 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE |
98 : JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
99 : JS_PropertyStub, /* addProperty */
100 : JS_PropertyStub, /* delProperty */
101 : JS_PropertyStub, /* getProperty */
102 : JS_StrictPropertyStub, /* setProperty */
103 : JS_EnumerateStub,
104 : (JSResolveOp)exn_resolve,
105 : JS_ConvertStub,
106 : exn_finalize,
107 : NULL, /* checkAccess */
108 : NULL, /* call */
109 : NULL, /* construct */
110 : NULL, /* hasInstance */
111 : exn_trace
112 : };
113 :
114 19922066 : typedef struct JSStackTraceElem {
115 : js::HeapPtrString funName;
116 : size_t argc;
117 : const char *filename;
118 : unsigned ulineno;
119 : } JSStackTraceElem;
120 :
121 : typedef struct JSExnPrivate {
122 : /* A copy of the JSErrorReport originally generated. */
123 : JSErrorReport *errorReport;
124 : js::HeapPtrString message;
125 : js::HeapPtrString filename;
126 : unsigned lineno;
127 : size_t stackDepth;
128 : int exnType;
129 : JSStackTraceElem stackElems[1];
130 : } JSExnPrivate;
131 :
132 : static JSString *
133 : StackTraceToString(JSContext *cx, JSExnPrivate *priv);
134 :
135 : static JSErrorReport *
136 11604 : CopyErrorReport(JSContext *cx, JSErrorReport *report)
137 : {
138 : /*
139 : * We use a single malloc block to make a deep copy of JSErrorReport with
140 : * the following layout:
141 : * JSErrorReport
142 : * array of copies of report->messageArgs
143 : * jschar array with characters for all messageArgs
144 : * jschar array with characters for ucmessage
145 : * jschar array with characters for uclinebuf and uctokenptr
146 : * char array with characters for linebuf and tokenptr
147 : * char array with characters for filename
148 : * Such layout together with the properties enforced by the following
149 : * asserts does not need any extra alignment padding.
150 : */
151 : JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);
152 : JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0);
153 :
154 : size_t filenameSize;
155 : size_t linebufSize;
156 : size_t uclinebufSize;
157 : size_t ucmessageSize;
158 : size_t i, argsArraySize, argsCopySize, argSize;
159 : size_t mallocSize;
160 : JSErrorReport *copy;
161 : uint8_t *cursor;
162 :
163 : #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
164 :
165 11604 : filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
166 11604 : linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0;
167 11604 : uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0;
168 11604 : ucmessageSize = 0;
169 11604 : argsArraySize = 0;
170 11604 : argsCopySize = 0;
171 11604 : if (report->ucmessage) {
172 11604 : ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
173 11604 : if (report->messageArgs) {
174 26381 : for (i = 0; report->messageArgs[i]; ++i)
175 16525 : argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
176 :
177 : /* Non-null messageArgs should have at least one non-null arg. */
178 9856 : JS_ASSERT(i != 0);
179 9856 : argsArraySize = (i + 1) * sizeof(const jschar *);
180 : }
181 : }
182 :
183 : /*
184 : * The mallocSize can not overflow since it represents the sum of the
185 : * sizes of already allocated objects.
186 : */
187 : mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize +
188 11604 : ucmessageSize + uclinebufSize + linebufSize + filenameSize;
189 11604 : cursor = (uint8_t *)cx->malloc_(mallocSize);
190 11604 : if (!cursor)
191 0 : return NULL;
192 :
193 11604 : copy = (JSErrorReport *)cursor;
194 11604 : memset(cursor, 0, sizeof(JSErrorReport));
195 11604 : cursor += sizeof(JSErrorReport);
196 :
197 11604 : if (argsArraySize != 0) {
198 9856 : copy->messageArgs = (const jschar **)cursor;
199 9856 : cursor += argsArraySize;
200 26381 : for (i = 0; report->messageArgs[i]; ++i) {
201 16525 : copy->messageArgs[i] = (const jschar *)cursor;
202 16525 : argSize = JS_CHARS_SIZE(report->messageArgs[i]);
203 16525 : js_memcpy(cursor, report->messageArgs[i], argSize);
204 16525 : cursor += argSize;
205 : }
206 9856 : copy->messageArgs[i] = NULL;
207 9856 : JS_ASSERT(cursor == (uint8_t *)copy->messageArgs[0] + argsCopySize);
208 : }
209 :
210 11604 : if (report->ucmessage) {
211 11604 : copy->ucmessage = (const jschar *)cursor;
212 11604 : js_memcpy(cursor, report->ucmessage, ucmessageSize);
213 11604 : cursor += ucmessageSize;
214 : }
215 :
216 11604 : if (report->uclinebuf) {
217 602 : copy->uclinebuf = (const jschar *)cursor;
218 602 : js_memcpy(cursor, report->uclinebuf, uclinebufSize);
219 602 : cursor += uclinebufSize;
220 602 : if (report->uctokenptr) {
221 : copy->uctokenptr = copy->uclinebuf + (report->uctokenptr -
222 602 : report->uclinebuf);
223 : }
224 : }
225 :
226 11604 : if (report->linebuf) {
227 602 : copy->linebuf = (const char *)cursor;
228 602 : js_memcpy(cursor, report->linebuf, linebufSize);
229 602 : cursor += linebufSize;
230 602 : if (report->tokenptr) {
231 : copy->tokenptr = copy->linebuf + (report->tokenptr -
232 602 : report->linebuf);
233 : }
234 : }
235 :
236 11604 : if (report->filename) {
237 11604 : copy->filename = (const char *)cursor;
238 11604 : js_memcpy(cursor, report->filename, filenameSize);
239 : }
240 11604 : JS_ASSERT(cursor + filenameSize == (uint8_t *)copy + mallocSize);
241 :
242 : /* HOLD called by the destination error object. */
243 11604 : copy->originPrincipals = report->originPrincipals;
244 :
245 : /* Copy non-pointer members. */
246 11604 : copy->lineno = report->lineno;
247 11604 : copy->errorNumber = report->errorNumber;
248 :
249 : /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
250 11604 : copy->flags = report->flags;
251 :
252 : #undef JS_CHARS_SIZE
253 11604 : return copy;
254 : }
255 :
256 : static HeapValue *
257 14998 : GetStackTraceValueBuffer(JSExnPrivate *priv)
258 : {
259 : /*
260 : * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals
261 : * that helps to produce more informative stack traces. The following
262 : * assert allows us to assume that no gap after stackElems is necessary to
263 : * align the buffer properly.
264 : */
265 : JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0);
266 :
267 14998 : return reinterpret_cast<HeapValue *>(priv->stackElems + priv->stackDepth);
268 : }
269 :
270 : struct SuppressErrorsGuard
271 : {
272 : JSContext *cx;
273 : JSErrorReporter prevReporter;
274 : JSExceptionState *prevState;
275 :
276 13029 : SuppressErrorsGuard(JSContext *cx)
277 : : cx(cx),
278 13029 : prevReporter(JS_SetErrorReporter(cx, NULL)),
279 26058 : prevState(JS_SaveExceptionState(cx))
280 13029 : {}
281 :
282 13029 : ~SuppressErrorsGuard()
283 : {
284 13029 : JS_RestoreExceptionState(cx, prevState);
285 13029 : JS_SetErrorReporter(cx, prevReporter);
286 13029 : }
287 : };
288 :
289 : struct AppendArg {
290 : Vector<Value> &values;
291 4361204 : AppendArg(Vector<Value> &values) : values(values) {}
292 5322670 : bool operator()(unsigned, Value *vp) {
293 5322670 : return values.append(*vp);
294 : }
295 : };
296 :
297 : static void
298 : SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv);
299 :
300 : static bool
301 13029 : InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
302 : JSString *filename, unsigned lineno, JSErrorReport *report, int exnType)
303 : {
304 13029 : JS_ASSERT(exnObject->isError());
305 13029 : JS_ASSERT(!exnObject->getPrivate());
306 :
307 13029 : JSCheckAccessOp checkAccess = cx->runtime->securityCallbacks->checkObjectAccess;
308 :
309 26058 : Vector<JSStackTraceElem> frames(cx);
310 26058 : Vector<Value> values(cx);
311 : {
312 26058 : SuppressErrorsGuard seg(cx);
313 4383688 : for (FrameRegsIter i(cx); !i.done(); ++i) {
314 : /*
315 : * An exception object stores stack values from 'fp' which may be
316 : * in a different compartment from 'exnObject'. Engine compartment
317 : * invariants require such values to be wrapped. A simpler solution
318 : * is to just cut off the backtrace at compartment boundaries.
319 : * Also, avoid exposing values from different security principals.
320 : */
321 4374042 : StackFrame *fp = i.fp();
322 4374042 : if (fp->compartment() != cx->compartment)
323 3383 : break;
324 4370659 : if (checkAccess && fp->isNonEvalFunctionFrame()) {
325 4268897 : Value v = NullValue();
326 4268897 : jsid callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
327 4268897 : if (!checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v))
328 0 : break;
329 : }
330 :
331 4370659 : if (!frames.growBy(1))
332 0 : return false;
333 4370659 : JSStackTraceElem &frame = frames.back();
334 4370659 : if (fp->isNonEvalFunctionFrame()) {
335 4361204 : frame.funName.init(fp->fun()->atom ? fp->fun()->atom : cx->runtime->emptyString);
336 4361204 : frame.argc = fp->numActualArgs();
337 4361204 : if (!fp->forEachCanonicalActualArg(AppendArg(values)))
338 0 : return false;
339 : } else {
340 9455 : frame.funName.init(NULL);
341 9455 : frame.argc = 0;
342 : }
343 4370659 : if (fp->isScriptFrame()) {
344 4370659 : frame.filename = fp->script()->filename;
345 4370659 : frame.ulineno = PCToLineNumber(fp->script(), i.pc());
346 : } else {
347 0 : frame.ulineno = 0;
348 0 : frame.filename = NULL;
349 : }
350 : }
351 : }
352 :
353 : /* Do not need overflow check: the vm stack is already bigger. */
354 : JS_STATIC_ASSERT(sizeof(JSStackTraceElem) <= sizeof(StackFrame));
355 :
356 : size_t nbytes = offsetof(JSExnPrivate, stackElems) +
357 13029 : frames.length() * sizeof(JSStackTraceElem) +
358 13029 : values.length() * sizeof(HeapValue);
359 :
360 13029 : JSExnPrivate *priv = (JSExnPrivate *)cx->malloc_(nbytes);
361 13029 : if (!priv)
362 0 : return false;
363 :
364 : /* Initialize to zero so that write barriers don't witness undefined values. */
365 13029 : memset(priv, 0, nbytes);
366 :
367 13029 : if (report) {
368 : /*
369 : * Construct a new copy of the error report struct. We can't use the
370 : * error report struct that was passed in, because it's allocated on
371 : * the stack, and also because it may point to transient data in the
372 : * TokenStream.
373 : */
374 11586 : priv->errorReport = CopyErrorReport(cx, report);
375 11586 : if (!priv->errorReport) {
376 0 : cx->free_(priv);
377 0 : return false;
378 : }
379 : } else {
380 1443 : priv->errorReport = NULL;
381 : }
382 :
383 13029 : priv->message.init(message);
384 13029 : priv->filename.init(filename);
385 13029 : priv->lineno = lineno;
386 13029 : priv->stackDepth = frames.length();
387 13029 : priv->exnType = exnType;
388 :
389 13029 : JSStackTraceElem *framesDest = priv->stackElems;
390 13029 : HeapValue *valuesDest = reinterpret_cast<HeapValue *>(framesDest + frames.length());
391 13029 : JS_ASSERT(valuesDest == GetStackTraceValueBuffer(priv));
392 :
393 13029 : PodCopy(framesDest, frames.begin(), frames.length());
394 5335699 : for (size_t i = 0; i < values.length(); ++i)
395 5322670 : valuesDest[i].init(cx->compartment, values[i]);
396 :
397 13029 : SetExnPrivate(cx, exnObject, priv);
398 13029 : return true;
399 : }
400 :
401 : static inline JSExnPrivate *
402 157029 : GetExnPrivate(JSObject *obj)
403 : {
404 157029 : JS_ASSERT(obj->isError());
405 157029 : return (JSExnPrivate *) obj->getPrivate();
406 : }
407 :
408 : static void
409 87351 : exn_trace(JSTracer *trc, JSObject *obj)
410 : {
411 : JSExnPrivate *priv;
412 : JSStackTraceElem *elem;
413 : size_t vcount, i;
414 : HeapValue *vp;
415 :
416 87351 : priv = GetExnPrivate(obj);
417 87351 : if (priv) {
418 1914 : if (priv->message)
419 1912 : MarkString(trc, &priv->message, "exception message");
420 1914 : if (priv->filename)
421 1914 : MarkString(trc, &priv->filename, "exception filename");
422 :
423 1914 : elem = priv->stackElems;
424 5787 : for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
425 3873 : if (elem->funName)
426 1899 : MarkString(trc, &elem->funName, "stack trace function name");
427 3873 : if (IS_GC_MARKING_TRACER(trc) && elem->filename)
428 3305 : js_MarkScriptFilename(elem->filename);
429 3873 : vcount += elem->argc;
430 : }
431 1914 : vp = GetStackTraceValueBuffer(priv);
432 2444 : for (i = 0; i != vcount; ++i, ++vp)
433 530 : MarkValue(trc, vp, "stack trace argument");
434 : }
435 87351 : }
436 :
437 : /* NB: An error object's private must be set through this function. */
438 : static void
439 13047 : SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv)
440 : {
441 13047 : JS_ASSERT(!exnObject->getPrivate());
442 13047 : JS_ASSERT(exnObject->isError());
443 13047 : if (JSErrorReport *report = priv->errorReport) {
444 11604 : if (JSPrincipals *prin = report->originPrincipals)
445 1857 : JS_HoldPrincipals(prin);
446 : }
447 13047 : exnObject->setPrivate(priv);
448 13047 : }
449 :
450 : static void
451 48783 : exn_finalize(JSContext *cx, JSObject *obj)
452 : {
453 48783 : if (JSExnPrivate *priv = GetExnPrivate(obj)) {
454 13047 : if (JSErrorReport *report = priv->errorReport) {
455 : /* HOLD called by SetExnPrivate. */
456 11604 : if (JSPrincipals *prin = report->originPrincipals)
457 1857 : JS_DropPrincipals(cx->runtime, prin);
458 11604 : cx->free_(report);
459 : }
460 13047 : cx->free_(priv);
461 : }
462 48783 : }
463 :
464 : static JSBool
465 19635 : exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
466 : JSObject **objp)
467 : {
468 : JSExnPrivate *priv;
469 : JSString *str;
470 : JSAtom *atom;
471 : JSString *stack;
472 : const char *prop;
473 : jsval v;
474 : unsigned attrs;
475 :
476 19635 : *objp = NULL;
477 19635 : priv = GetExnPrivate(obj);
478 19635 : if (priv && JSID_IS_ATOM(id)) {
479 15127 : str = JSID_TO_STRING(id);
480 :
481 15127 : atom = cx->runtime->atomState.messageAtom;
482 15127 : if (str == atom) {
483 2336 : prop = js_message_str;
484 :
485 : /*
486 : * Per ES5 15.11.1.1, if Error is called with no argument or with
487 : * undefined as the argument, it returns an Error object with no
488 : * own message property.
489 : */
490 2336 : if (!priv->message)
491 0 : return true;
492 :
493 2336 : v = STRING_TO_JSVAL(priv->message);
494 2336 : attrs = 0;
495 2336 : goto define;
496 : }
497 :
498 12791 : atom = cx->runtime->atomState.fileNameAtom;
499 12791 : if (str == atom) {
500 57 : prop = js_fileName_str;
501 57 : v = STRING_TO_JSVAL(priv->filename);
502 57 : attrs = JSPROP_ENUMERATE;
503 57 : goto define;
504 : }
505 :
506 12734 : atom = cx->runtime->atomState.lineNumberAtom;
507 12734 : if (str == atom) {
508 1231 : prop = js_lineNumber_str;
509 1231 : v = INT_TO_JSVAL(priv->lineno);
510 1231 : attrs = JSPROP_ENUMERATE;
511 1231 : goto define;
512 : }
513 :
514 11503 : atom = cx->runtime->atomState.stackAtom;
515 11503 : if (str == atom) {
516 55 : stack = StackTraceToString(cx, priv);
517 55 : if (!stack)
518 0 : return false;
519 :
520 55 : prop = js_stack_str;
521 55 : v = STRING_TO_JSVAL(stack);
522 55 : attrs = JSPROP_ENUMERATE;
523 55 : goto define;
524 : }
525 : }
526 15956 : return true;
527 :
528 : define:
529 3679 : if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, attrs))
530 0 : return false;
531 3679 : *objp = obj;
532 3679 : return true;
533 : }
534 :
535 : JSErrorReport *
536 1864 : js_ErrorFromException(JSContext *cx, jsval exn)
537 : {
538 : JSObject *obj;
539 : JSExnPrivate *priv;
540 :
541 1864 : if (JSVAL_IS_PRIMITIVE(exn))
542 520 : return NULL;
543 1344 : obj = JSVAL_TO_OBJECT(exn);
544 1344 : if (!obj->isError())
545 102 : return NULL;
546 1242 : priv = GetExnPrivate(obj);
547 1242 : if (!priv)
548 0 : return NULL;
549 1242 : return priv->errorReport;
550 : }
551 :
552 : static JSString *
553 106 : ValueToShortSource(JSContext *cx, const Value &v)
554 : {
555 : JSString *str;
556 :
557 : /* Avoid toSource bloat and fallibility for object types. */
558 106 : if (!v.isObject())
559 80 : return js_ValueToSource(cx, v);
560 :
561 26 : JSObject *obj = &v.toObject();
562 52 : AutoCompartment ac(cx, obj);
563 26 : if (!ac.enter())
564 0 : return NULL;
565 :
566 26 : if (obj->isFunction()) {
567 : /*
568 : * XXX Avoid function decompilation bloat for now.
569 : */
570 1 : str = JS_GetFunctionId(obj->toFunction());
571 1 : if (!str && !(str = js_ValueToSource(cx, v))) {
572 : /*
573 : * Continue to soldier on if the function couldn't be
574 : * converted into a string.
575 : */
576 0 : JS_ClearPendingException(cx);
577 0 : str = JS_NewStringCopyZ(cx, "[unknown function]");
578 : }
579 : } else {
580 : /*
581 : * XXX Avoid toString on objects, it takes too long and uses too much
582 : * memory, for too many classes (see Mozilla bug 166743).
583 : */
584 : char buf[100];
585 25 : JS_snprintf(buf, sizeof buf, "[object %s]", obj->getClass()->name);
586 25 : str = JS_NewStringCopyZ(cx, buf);
587 : }
588 :
589 26 : ac.leave();
590 :
591 26 : if (!str || !cx->compartment->wrap(cx, &str))
592 0 : return NULL;
593 26 : return str;
594 : }
595 :
596 : static JSString *
597 55 : StackTraceToString(JSContext *cx, JSExnPrivate *priv)
598 : {
599 : jschar *stackbuf;
600 : size_t stacklen, stackmax;
601 : JSStackTraceElem *elem, *endElem;
602 : HeapValue *values;
603 : size_t i;
604 : JSString *str;
605 : const char *cp;
606 : char ulnbuf[11];
607 :
608 : /* After this point, failing control flow must goto bad. */
609 55 : stackbuf = NULL;
610 55 : stacklen = stackmax = 0;
611 :
612 : /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
613 : #define STACK_LENGTH_LIMIT JS_BIT(20)
614 :
615 : #define APPEND_CHAR_TO_STACK(c) \
616 : JS_BEGIN_MACRO \
617 : if (stacklen == stackmax) { \
618 : void *ptr_; \
619 : if (stackmax >= STACK_LENGTH_LIMIT) \
620 : goto done; \
621 : stackmax = stackmax ? 2 * stackmax : 64; \
622 : ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar)); \
623 : if (!ptr_) \
624 : goto bad; \
625 : stackbuf = (jschar *) ptr_; \
626 : } \
627 : stackbuf[stacklen++] = (c); \
628 : JS_END_MACRO
629 :
630 : #define APPEND_STRING_TO_STACK(str) \
631 : JS_BEGIN_MACRO \
632 : JSString *str_ = str; \
633 : size_t length_ = str_->length(); \
634 : const jschar *chars_ = str_->getChars(cx); \
635 : if (!chars_) \
636 : goto bad; \
637 : \
638 : if (length_ > stackmax - stacklen) { \
639 : void *ptr_; \
640 : if (stackmax >= STACK_LENGTH_LIMIT || \
641 : length_ >= STACK_LENGTH_LIMIT - stacklen) { \
642 : goto done; \
643 : } \
644 : stackmax = RoundUpPow2(stacklen + length_); \
645 : ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar)); \
646 : if (!ptr_) \
647 : goto bad; \
648 : stackbuf = (jschar *) ptr_; \
649 : } \
650 : js_strncpy(stackbuf + stacklen, chars_, length_); \
651 : stacklen += length_; \
652 : JS_END_MACRO
653 :
654 55 : values = GetStackTraceValueBuffer(priv);
655 55 : elem = priv->stackElems;
656 365 : for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
657 310 : if (elem->funName) {
658 301 : APPEND_STRING_TO_STACK(elem->funName);
659 301 : APPEND_CHAR_TO_STACK('(');
660 407 : for (i = 0; i != elem->argc; i++, values++) {
661 106 : if (i > 0)
662 42 : APPEND_CHAR_TO_STACK(',');
663 106 : str = ValueToShortSource(cx, *values);
664 106 : if (!str)
665 0 : goto bad;
666 106 : APPEND_STRING_TO_STACK(str);
667 : }
668 301 : APPEND_CHAR_TO_STACK(')');
669 : }
670 310 : APPEND_CHAR_TO_STACK('@');
671 310 : if (elem->filename) {
672 15184 : for (cp = elem->filename; *cp; cp++)
673 14874 : APPEND_CHAR_TO_STACK(*cp);
674 : }
675 310 : APPEND_CHAR_TO_STACK(':');
676 310 : JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno);
677 1306 : for (cp = ulnbuf; *cp; cp++)
678 996 : APPEND_CHAR_TO_STACK(*cp);
679 310 : APPEND_CHAR_TO_STACK('\n');
680 : }
681 : #undef APPEND_CHAR_TO_STACK
682 : #undef APPEND_STRING_TO_STACK
683 : #undef STACK_LENGTH_LIMIT
684 :
685 : done:
686 55 : if (stacklen == 0) {
687 18 : JS_ASSERT(!stackbuf);
688 18 : return cx->runtime->emptyString;
689 : }
690 37 : if (stacklen < stackmax) {
691 : /*
692 : * Realloc can fail when shrinking on some FreeBSD versions, so
693 : * don't use JS_realloc here; simply let the oversized allocation
694 : * be owned by the string in that rare case.
695 : */
696 37 : void *shrunk = cx->realloc_(stackbuf, (stacklen+1) * sizeof(jschar));
697 37 : if (shrunk)
698 37 : stackbuf = (jschar *) shrunk;
699 : }
700 :
701 37 : stackbuf[stacklen] = 0;
702 37 : str = js_NewString(cx, stackbuf, stacklen);
703 37 : if (str)
704 37 : return str;
705 :
706 : bad:
707 0 : if (stackbuf)
708 0 : cx->free_(stackbuf);
709 0 : return NULL;
710 : }
711 :
712 : /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
713 : with these two functions. */
714 : static JSString *
715 1443 : FilenameToString(JSContext *cx, const char *filename)
716 : {
717 1443 : return JS_NewStringCopyZ(cx, filename);
718 : }
719 :
720 : static JSBool
721 1443 : Exception(JSContext *cx, unsigned argc, Value *vp)
722 : {
723 1443 : CallArgs args = CallArgsFromVp(argc, vp);
724 :
725 : /*
726 : * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
727 : * called as functions, without operator new. But as we do not give
728 : * each constructor a distinct JSClass, whose .name member is used by
729 : * NewNativeClassInstance to find the class prototype, we must get the
730 : * class prototype ourselves.
731 : */
732 : Value protov;
733 1443 : if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
734 0 : return false;
735 :
736 1443 : if (!protov.isObject()) {
737 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error");
738 0 : return false;
739 : }
740 :
741 1443 : JSObject *errProto = &protov.toObject();
742 1443 : JSObject *obj = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
743 1443 : if (!obj)
744 0 : return false;
745 :
746 : /* Set the 'message' property. */
747 : JSString *message;
748 1443 : if (args.hasDefined(0)) {
749 464 : message = ToString(cx, args[0]);
750 464 : if (!message)
751 0 : return false;
752 464 : args[0].setString(message);
753 : } else {
754 979 : message = NULL;
755 : }
756 :
757 : /* Find the scripted caller. */
758 1443 : FrameRegsIter iter(cx);
759 2886 : while (!iter.done() && !iter.fp()->isScriptFrame())
760 0 : ++iter;
761 :
762 : /* Set the 'fileName' property. */
763 : JSString *filename;
764 1443 : if (args.length() > 1) {
765 0 : filename = ToString(cx, args[1]);
766 0 : if (!filename)
767 0 : return false;
768 0 : args[1].setString(filename);
769 : } else {
770 1443 : if (!iter.done()) {
771 1443 : filename = FilenameToString(cx, iter.fp()->script()->filename);
772 1443 : if (!filename)
773 0 : return false;
774 : } else {
775 0 : filename = cx->runtime->emptyString;
776 : }
777 : }
778 :
779 : /* Set the 'lineNumber' property. */
780 : uint32_t lineno;
781 1443 : if (args.length() > 2) {
782 0 : if (!ToUint32(cx, args[2], &lineno))
783 0 : return false;
784 : } else {
785 1443 : lineno = iter.done() ? 0 : PCToLineNumber(iter.fp()->script(), iter.pc());
786 : }
787 :
788 1443 : int exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32();
789 1443 : if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType))
790 0 : return false;
791 :
792 1443 : args.rval().setObject(*obj);
793 1443 : return true;
794 : }
795 :
796 : /* ES5 15.11.4.4 (NB: with subsequent errata). */
797 : static JSBool
798 7833 : exn_toString(JSContext *cx, unsigned argc, Value *vp)
799 : {
800 7833 : JS_CHECK_RECURSION(cx, return false);
801 7815 : CallArgs args = CallArgsFromVp(argc, vp);
802 :
803 : /* Step 2. */
804 7815 : if (!args.thisv().isObject()) {
805 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error");
806 0 : return false;
807 : }
808 :
809 : /* Step 1. */
810 7815 : JSObject &obj = args.thisv().toObject();
811 :
812 : /* Step 3. */
813 : Value nameVal;
814 7815 : if (!obj.getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal))
815 0 : return false;
816 :
817 : /* Step 4. */
818 : JSString *name;
819 7815 : if (nameVal.isUndefined()) {
820 9 : name = CLASS_ATOM(cx, Error);
821 : } else {
822 7806 : name = ToString(cx, nameVal);
823 7806 : if (!name)
824 5286 : return false;
825 : }
826 :
827 : /* Step 5. */
828 : Value msgVal;
829 2529 : if (!obj.getProperty(cx, cx->runtime->atomState.messageAtom, &msgVal))
830 0 : return false;
831 :
832 : /* Step 6. */
833 : JSString *message;
834 2529 : if (msgVal.isUndefined()) {
835 9 : message = cx->runtime->emptyString;
836 : } else {
837 2520 : message = ToString(cx, msgVal);
838 2520 : if (!message)
839 0 : return false;
840 : }
841 :
842 : /* Step 7. */
843 2529 : if (name->empty() && message->empty()) {
844 9 : args.rval().setString(CLASS_ATOM(cx, Error));
845 9 : return true;
846 : }
847 :
848 : /* Step 8. */
849 2520 : if (name->empty()) {
850 9 : args.rval().setString(message);
851 9 : return true;
852 : }
853 :
854 : /* Step 9. */
855 2511 : if (message->empty()) {
856 27 : args.rval().setString(name);
857 27 : return true;
858 : }
859 :
860 : /* Step 10. */
861 4968 : StringBuffer sb(cx);
862 2484 : if (!sb.append(name) || !sb.append(": ") || !sb.append(message))
863 0 : return false;
864 :
865 2484 : JSString *str = sb.finishString();
866 2484 : if (!str)
867 0 : return false;
868 2484 : args.rval().setString(str);
869 2484 : return true;
870 : }
871 :
872 : #if JS_HAS_TOSOURCE
873 : /*
874 : * Return a string that may eval to something similar to the original object.
875 : */
876 : static JSBool
877 0 : exn_toSource(JSContext *cx, unsigned argc, Value *vp)
878 : {
879 0 : JS_CHECK_RECURSION(cx, return false);
880 0 : CallArgs args = CallArgsFromVp(argc, vp);
881 :
882 0 : JSObject *obj = ToObject(cx, &args.thisv());
883 0 : if (!obj)
884 0 : return false;
885 :
886 : Value nameVal;
887 : JSString *name;
888 0 : if (!obj->getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal) ||
889 : !(name = ToString(cx, nameVal)))
890 : {
891 0 : return false;
892 : }
893 :
894 : Value messageVal;
895 : JSString *message;
896 0 : if (!obj->getProperty(cx, cx->runtime->atomState.messageAtom, &messageVal) ||
897 : !(message = js_ValueToSource(cx, messageVal)))
898 : {
899 0 : return false;
900 : }
901 :
902 : Value filenameVal;
903 : JSString *filename;
904 0 : if (!obj->getProperty(cx, cx->runtime->atomState.fileNameAtom, &filenameVal) ||
905 : !(filename = js_ValueToSource(cx, filenameVal)))
906 : {
907 0 : return false;
908 : }
909 :
910 : Value linenoVal;
911 : uint32_t lineno;
912 0 : if (!obj->getProperty(cx, cx->runtime->atomState.lineNumberAtom, &linenoVal) ||
913 0 : !ToUint32(cx, linenoVal, &lineno))
914 : {
915 0 : return false;
916 : }
917 :
918 0 : StringBuffer sb(cx);
919 0 : if (!sb.append("(new ") || !sb.append(name) || !sb.append("("))
920 0 : return false;
921 :
922 0 : if (!sb.append(message))
923 0 : return false;
924 :
925 0 : if (!filename->empty()) {
926 0 : if (!sb.append(", ") || !sb.append(filename))
927 0 : return false;
928 : }
929 0 : if (lineno != 0) {
930 : /* We have a line, but no filename, add empty string */
931 0 : if (filename->empty() && !sb.append(", \"\""))
932 0 : return false;
933 :
934 0 : JSString *linenumber = ToString(cx, linenoVal);
935 0 : if (!linenumber)
936 0 : return false;
937 0 : if (!sb.append(", ") || !sb.append(linenumber))
938 0 : return false;
939 : }
940 :
941 0 : if (!sb.append("))"))
942 0 : return false;
943 :
944 0 : JSString *str = sb.finishString();
945 0 : if (!str)
946 0 : return false;
947 0 : args.rval().setString(str);
948 0 : return true;
949 : }
950 : #endif
951 :
952 : static JSFunctionSpec exception_methods[] = {
953 : #if JS_HAS_TOSOURCE
954 : JS_FN(js_toSource_str, exn_toSource, 0,0),
955 : #endif
956 : JS_FN(js_toString_str, exn_toString, 0,0),
957 : JS_FS_END
958 : };
959 :
960 : /* JSProto_ ordering for exceptions shall match JSEXN_ constants. */
961 : JS_STATIC_ASSERT(JSEXN_ERR == 0);
962 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR == JSProto_InternalError);
963 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR == JSProto_EvalError);
964 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR == JSProto_RangeError);
965 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError);
966 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError);
967 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError);
968 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError);
969 :
970 : static JSObject *
971 35752 : InitErrorClass(JSContext *cx, GlobalObject *global, int type, JSObject &proto)
972 : {
973 35752 : JSProtoKey key = GetExceptionProtoKey(type);
974 35752 : JSAtom *name = cx->runtime->atomState.classAtoms[key];
975 35752 : JSObject *errorProto = global->createBlankPrototypeInheriting(cx, &ErrorClass, proto);
976 35752 : if (!errorProto)
977 0 : return NULL;
978 :
979 35752 : Value empty = StringValue(cx->runtime->emptyString);
980 35752 : jsid nameId = ATOM_TO_JSID(cx->runtime->atomState.nameAtom);
981 35752 : jsid messageId = ATOM_TO_JSID(cx->runtime->atomState.messageAtom);
982 35752 : jsid fileNameId = ATOM_TO_JSID(cx->runtime->atomState.fileNameAtom);
983 35752 : jsid lineNumberId = ATOM_TO_JSID(cx->runtime->atomState.lineNumberAtom);
984 178760 : if (!DefineNativeProperty(cx, errorProto, nameId, StringValue(name),
985 35752 : JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
986 : !DefineNativeProperty(cx, errorProto, messageId, empty,
987 35752 : JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
988 : !DefineNativeProperty(cx, errorProto, fileNameId, empty,
989 35752 : JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
990 : !DefineNativeProperty(cx, errorProto, lineNumberId, Int32Value(0),
991 35752 : JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0))
992 : {
993 0 : return NULL;
994 : }
995 :
996 : /* Create the corresponding constructor. */
997 : JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1,
998 35752 : JSFunction::ExtendedFinalizeKind);
999 35752 : if (!ctor)
1000 0 : return NULL;
1001 35752 : ctor->setExtendedSlot(0, Int32Value(int32_t(type)));
1002 :
1003 35752 : if (!LinkConstructorAndPrototype(cx, ctor, errorProto))
1004 0 : return NULL;
1005 :
1006 35752 : if (!DefineConstructorAndPrototype(cx, global, key, ctor, errorProto))
1007 0 : return NULL;
1008 :
1009 35752 : JS_ASSERT(!errorProto->getPrivate());
1010 :
1011 35752 : return errorProto;
1012 : }
1013 :
1014 : JSObject *
1015 4469 : js_InitExceptionClasses(JSContext *cx, JSObject *obj)
1016 : {
1017 4469 : JS_ASSERT(obj->isGlobal());
1018 4469 : JS_ASSERT(obj->isNative());
1019 :
1020 4469 : GlobalObject *global = &obj->asGlobal();
1021 :
1022 4469 : JSObject *objectProto = global->getOrCreateObjectPrototype(cx);
1023 4469 : if (!objectProto)
1024 0 : return NULL;
1025 :
1026 : /* Initialize the base Error class first. */
1027 4469 : JSObject *errorProto = InitErrorClass(cx, global, JSEXN_ERR, *objectProto);
1028 4469 : if (!errorProto)
1029 0 : return NULL;
1030 :
1031 : /* |Error.prototype| alone has method properties. */
1032 4469 : if (!DefinePropertiesAndBrand(cx, errorProto, NULL, exception_methods))
1033 0 : return NULL;
1034 :
1035 : /* Define all remaining *Error constructors. */
1036 35752 : for (int i = JSEXN_ERR + 1; i < JSEXN_LIMIT; i++) {
1037 31283 : if (!InitErrorClass(cx, global, i, *errorProto))
1038 0 : return NULL;
1039 : }
1040 :
1041 4469 : return errorProto;
1042 : }
1043 :
1044 : const JSErrorFormatString*
1045 29243 : js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
1046 : const unsigned errorNumber)
1047 : {
1048 29243 : const JSErrorFormatString *errorString = NULL;
1049 :
1050 29243 : if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
1051 : errorString = cx->localeCallbacks
1052 0 : ->localeGetErrorMessage(userRef, locale, errorNumber);
1053 : }
1054 29243 : if (!errorString)
1055 29243 : errorString = js_GetErrorMessage(userRef, locale, errorNumber);
1056 29243 : return errorString;
1057 : }
1058 :
1059 : #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
1060 : /* For use below... get character strings for error name and exception name */
1061 : static struct exnname { char *name; char *exception; } errortoexnname[] = {
1062 : #define MSG_DEF(name, number, count, exception, format) \
1063 : {#name, #exception},
1064 : #include "js.msg"
1065 : #undef MSG_DEF
1066 : };
1067 : #endif /* DEBUG */
1068 :
1069 : JSBool
1070 19020 : js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
1071 : JSErrorCallback callback, void *userRef)
1072 : {
1073 : JSErrNum errorNumber;
1074 : const JSErrorFormatString *errorString;
1075 : JSExnType exn;
1076 : jsval tv[4];
1077 : JSBool ok;
1078 : JSObject *errProto, *errObject;
1079 : JSString *messageStr, *filenameStr;
1080 :
1081 : /*
1082 : * Tell our caller to report immediately if this report is just a warning.
1083 : */
1084 19020 : JS_ASSERT(reportp);
1085 19020 : if (JSREPORT_IS_WARNING(reportp->flags))
1086 7337 : return JS_FALSE;
1087 :
1088 : /* Find the exception index associated with this error. */
1089 11683 : errorNumber = (JSErrNum) reportp->errorNumber;
1090 11683 : if (!callback || callback == js_GetErrorMessage)
1091 10870 : errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
1092 : else
1093 813 : errorString = callback(userRef, NULL, errorNumber);
1094 11683 : exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
1095 11683 : JS_ASSERT(exn < JSEXN_LIMIT);
1096 :
1097 : #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
1098 : /* Print the error name and the associated exception name to stderr */
1099 : fprintf(stderr, "%s\t%s\n",
1100 : errortoexnname[errorNumber].name,
1101 : errortoexnname[errorNumber].exception);
1102 : #endif
1103 :
1104 : /*
1105 : * Return false (no exception raised) if no exception is associated
1106 : * with the given error number.
1107 : */
1108 11683 : if (exn == JSEXN_NONE)
1109 0 : return JS_FALSE;
1110 :
1111 : /*
1112 : * Prevent runaway recursion, via cx->generatingError. If an out-of-memory
1113 : * error occurs, no exception object will be created, but we don't assume
1114 : * that OOM is the only kind of error that subroutines of this function
1115 : * called below might raise.
1116 : */
1117 11683 : if (cx->generatingError)
1118 97 : return JS_FALSE;
1119 :
1120 : MUST_FLOW_THROUGH("out");
1121 11586 : cx->generatingError = JS_TRUE;
1122 :
1123 : /* Protect the newly-created strings below from nesting GCs. */
1124 11586 : PodArrayZero(tv);
1125 23172 : AutoArrayRooter tvr(cx, ArrayLength(tv), tv);
1126 :
1127 : /*
1128 : * Try to get an appropriate prototype by looking up the corresponding
1129 : * exception constructor name in the scope chain of the current context's
1130 : * top stack frame, or in the global object if no frame is active.
1131 : */
1132 11586 : ok = js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto);
1133 11586 : if (!ok)
1134 0 : goto out;
1135 11586 : tv[0] = OBJECT_TO_JSVAL(errProto);
1136 :
1137 11586 : errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
1138 11586 : if (!errObject) {
1139 0 : ok = JS_FALSE;
1140 0 : goto out;
1141 : }
1142 11586 : tv[1] = OBJECT_TO_JSVAL(errObject);
1143 :
1144 11586 : messageStr = JS_NewStringCopyZ(cx, message);
1145 11586 : if (!messageStr) {
1146 0 : ok = JS_FALSE;
1147 0 : goto out;
1148 : }
1149 11586 : tv[2] = STRING_TO_JSVAL(messageStr);
1150 :
1151 11586 : filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
1152 11586 : if (!filenameStr) {
1153 0 : ok = JS_FALSE;
1154 0 : goto out;
1155 : }
1156 11586 : tv[3] = STRING_TO_JSVAL(filenameStr);
1157 :
1158 : ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
1159 11586 : reportp->lineno, reportp, exn);
1160 11586 : if (!ok)
1161 0 : goto out;
1162 :
1163 11586 : JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
1164 :
1165 : /* Flag the error report passed in to indicate an exception was raised. */
1166 11586 : reportp->flags |= JSREPORT_EXCEPTION;
1167 :
1168 : out:
1169 11586 : cx->generatingError = JS_FALSE;
1170 11586 : return ok;
1171 : }
1172 :
1173 : JSBool
1174 12971 : js_ReportUncaughtException(JSContext *cx)
1175 : {
1176 : jsval exn;
1177 : JSObject *exnObject;
1178 : jsval roots[5];
1179 : JSErrorReport *reportp, report;
1180 : JSString *str;
1181 : const char *bytes;
1182 :
1183 12971 : if (!JS_IsExceptionPending(cx))
1184 11412 : return true;
1185 :
1186 1559 : if (!JS_GetPendingException(cx, &exn))
1187 0 : return false;
1188 :
1189 1559 : PodArrayZero(roots);
1190 3118 : AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
1191 :
1192 : /*
1193 : * Because ToString below could error and an exception object could become
1194 : * unrooted, we must root exnObject. Later, if exnObject is non-null, we
1195 : * need to root other intermediates, so allocate an operand stack segment
1196 : * to protect all of these values.
1197 : */
1198 1559 : if (JSVAL_IS_PRIMITIVE(exn)) {
1199 475 : exnObject = NULL;
1200 : } else {
1201 1084 : exnObject = JSVAL_TO_OBJECT(exn);
1202 1084 : roots[0] = exn;
1203 : }
1204 :
1205 1559 : JS_ClearPendingException(cx);
1206 1559 : reportp = js_ErrorFromException(cx, exn);
1207 :
1208 : /* XXX L10N angels cry once again. see also everywhere else */
1209 1559 : str = ToString(cx, exn);
1210 3118 : JSAutoByteString bytesStorage;
1211 1559 : if (!str) {
1212 9 : bytes = "unknown (can't convert to string)";
1213 : } else {
1214 1550 : roots[1] = StringValue(str);
1215 1550 : if (!bytesStorage.encode(cx, str))
1216 0 : return false;
1217 1550 : bytes = bytesStorage.ptr();
1218 : }
1219 :
1220 3118 : JSAutoByteString filename;
1221 1559 : if (!reportp && exnObject && exnObject->isError()) {
1222 11 : if (!JS_GetProperty(cx, exnObject, js_message_str, &roots[2]))
1223 0 : return false;
1224 11 : if (JSVAL_IS_STRING(roots[2])) {
1225 11 : bytesStorage.clear();
1226 11 : if (!bytesStorage.encode(cx, str))
1227 0 : return false;
1228 11 : bytes = bytesStorage.ptr();
1229 : }
1230 :
1231 11 : if (!JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]))
1232 0 : return false;
1233 11 : str = ToString(cx, roots[3]);
1234 11 : if (!str || !filename.encode(cx, str))
1235 0 : return false;
1236 :
1237 11 : if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]))
1238 0 : return false;
1239 : uint32_t lineno;
1240 11 : if (!ToUint32(cx, roots[4], &lineno))
1241 0 : return false;
1242 :
1243 11 : reportp = &report;
1244 11 : PodZero(&report);
1245 11 : report.filename = filename.ptr();
1246 11 : report.lineno = (unsigned) lineno;
1247 11 : if (JSVAL_IS_STRING(roots[2])) {
1248 11 : JSFixedString *fixed = JSVAL_TO_STRING(roots[2])->ensureFixed(cx);
1249 11 : if (!fixed)
1250 0 : return false;
1251 11 : report.ucmessage = fixed->chars();
1252 : }
1253 : }
1254 :
1255 1559 : if (!reportp) {
1256 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1257 575 : JSMSG_UNCAUGHT_EXCEPTION, bytes);
1258 : } else {
1259 : /* Flag the error as an exception. */
1260 984 : reportp->flags |= JSREPORT_EXCEPTION;
1261 :
1262 : /* Pass the exception object. */
1263 984 : JS_SetPendingException(cx, exn);
1264 984 : js_ReportErrorAgain(cx, bytes, reportp);
1265 984 : JS_ClearPendingException(cx);
1266 : }
1267 :
1268 1559 : return true;
1269 : }
1270 :
1271 : extern JSObject *
1272 18 : js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope)
1273 : {
1274 18 : assertSameCompartment(cx, scope);
1275 18 : JSExnPrivate *priv = GetExnPrivate(errobj);
1276 :
1277 18 : uint32_t stackDepth = priv->stackDepth;
1278 18 : size_t valueCount = 0;
1279 18 : for (uint32_t i = 0; i < stackDepth; i++)
1280 0 : valueCount += priv->stackElems[i].argc;
1281 :
1282 : size_t size = offsetof(JSExnPrivate, stackElems) +
1283 : stackDepth * sizeof(JSStackTraceElem) +
1284 18 : valueCount * sizeof(jsval);
1285 :
1286 18 : JSExnPrivate *copy = (JSExnPrivate *)cx->malloc_(size);
1287 18 : if (!copy)
1288 0 : return NULL;
1289 :
1290 : struct AutoFree {
1291 : JSContext *cx;
1292 : JSExnPrivate *p;
1293 18 : ~AutoFree() {
1294 18 : if (p) {
1295 0 : cx->free_(p->errorReport);
1296 0 : cx->free_(p);
1297 : }
1298 18 : }
1299 36 : } autoFree = {cx, copy};
1300 :
1301 : // Copy each field. Don't bother copying the stack elements.
1302 18 : if (priv->errorReport) {
1303 18 : copy->errorReport = CopyErrorReport(cx, priv->errorReport);
1304 18 : if (!copy->errorReport)
1305 0 : return NULL;
1306 : } else {
1307 0 : copy->errorReport = NULL;
1308 : }
1309 18 : copy->message.init(priv->message);
1310 18 : if (!cx->compartment->wrap(cx, ©->message))
1311 0 : return NULL;
1312 36 : JS::Anchor<JSString *> messageAnchor(copy->message);
1313 18 : copy->filename.init(priv->filename);
1314 18 : if (!cx->compartment->wrap(cx, ©->filename))
1315 0 : return NULL;
1316 36 : JS::Anchor<JSString *> filenameAnchor(copy->filename);
1317 18 : copy->lineno = priv->lineno;
1318 18 : copy->stackDepth = 0;
1319 18 : copy->exnType = priv->exnType;
1320 :
1321 : // Create the Error object.
1322 18 : JSObject *proto = scope->global().getOrCreateCustomErrorPrototype(cx, copy->exnType);
1323 18 : if (!proto)
1324 0 : return NULL;
1325 18 : JSObject *copyobj = NewObjectWithGivenProto(cx, &ErrorClass, proto, NULL);
1326 18 : SetExnPrivate(cx, copyobj, copy);
1327 18 : autoFree.p = NULL;
1328 18 : return copyobj;
1329 : }
|