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