PageRenderTime 305ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsexn.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1376 lines | 1005 code | 157 blank | 214 comment | 202 complexity | 6a55f62b7471d33996ea5349db8fde33 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  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. * JS standard exception implementation.
  42. */
  43. #include "jsstddef.h"
  44. #include <stdlib.h>
  45. #include <string.h>
  46. #include "jstypes.h"
  47. #include "jsbit.h"
  48. #include "jsutil.h" /* Added by JSIFY */
  49. #include "jsprf.h"
  50. #include "jsapi.h"
  51. #include "jscntxt.h"
  52. #include "jsversion.h"
  53. #include "jsdbgapi.h"
  54. #include "jsexn.h"
  55. #include "jsfun.h"
  56. #include "jsinterp.h"
  57. #include "jsnum.h"
  58. #include "jsobj.h"
  59. #include "jsopcode.h"
  60. #include "jsscope.h"
  61. #include "jsscript.h"
  62. #include "jsstaticcheck.h"
  63. /* Forward declarations for js_ErrorClass's initializer. */
  64. static JSBool
  65. Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
  66. static void
  67. exn_finalize(JSContext *cx, JSObject *obj);
  68. static void
  69. exn_trace(JSTracer *trc, JSObject *obj);
  70. static void
  71. exn_finalize(JSContext *cx, JSObject *obj);
  72. static JSBool
  73. exn_enumerate(JSContext *cx, JSObject *obj);
  74. static JSBool
  75. exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
  76. JSObject **objp);
  77. JSClass js_ErrorClass = {
  78. js_Error_str,
  79. JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_MARK_IS_TRACE |
  80. JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
  81. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  82. exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize,
  83. NULL, NULL, NULL, Exception,
  84. NULL, NULL, JS_CLASS_TRACE(exn_trace), NULL
  85. };
  86. typedef struct JSStackTraceElem {
  87. JSString *funName;
  88. size_t argc;
  89. const char *filename;
  90. uintN ulineno;
  91. } JSStackTraceElem;
  92. typedef struct JSExnPrivate {
  93. /* A copy of the JSErrorReport originally generated. */
  94. JSErrorReport *errorReport;
  95. JSString *message;
  96. JSString *filename;
  97. uintN lineno;
  98. size_t stackDepth;
  99. JSStackTraceElem stackElems[1];
  100. } JSExnPrivate;
  101. static JSString *
  102. StackTraceToString(JSContext *cx, JSExnPrivate *priv);
  103. static JSErrorReport *
  104. CopyErrorReport(JSContext *cx, JSErrorReport *report)
  105. {
  106. /*
  107. * We use a single malloc block to make a deep copy of JSErrorReport with
  108. * the following layout:
  109. * JSErrorReport
  110. * array of copies of report->messageArgs
  111. * jschar array with characters for all messageArgs
  112. * jschar array with characters for ucmessage
  113. * jschar array with characters for uclinebuf and uctokenptr
  114. * char array with characters for linebuf and tokenptr
  115. * char array with characters for filename
  116. * Such layout together with the properties enforced by the following
  117. * asserts does not need any extra alignment padding.
  118. */
  119. JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);
  120. JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0);
  121. size_t filenameSize;
  122. size_t linebufSize;
  123. size_t uclinebufSize;
  124. size_t ucmessageSize;
  125. size_t i, argsArraySize, argsCopySize, argSize;
  126. size_t mallocSize;
  127. JSErrorReport *copy;
  128. uint8 *cursor;
  129. #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
  130. filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
  131. linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0;
  132. uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0;
  133. ucmessageSize = 0;
  134. argsArraySize = 0;
  135. argsCopySize = 0;
  136. if (report->ucmessage) {
  137. ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
  138. if (report->messageArgs) {
  139. for (i = 0; report->messageArgs[i]; ++i)
  140. argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
  141. /* Non-null messageArgs should have at least one non-null arg. */
  142. JS_ASSERT(i != 0);
  143. argsArraySize = (i + 1) * sizeof(const jschar *);
  144. }
  145. }
  146. /*
  147. * The mallocSize can not overflow since it represents the sum of the
  148. * sizes of already allocated objects.
  149. */
  150. mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize +
  151. ucmessageSize + uclinebufSize + linebufSize + filenameSize;
  152. cursor = (uint8 *)JS_malloc(cx, mallocSize);
  153. if (!cursor)
  154. return NULL;
  155. copy = (JSErrorReport *)cursor;
  156. memset(cursor, 0, sizeof(JSErrorReport));
  157. cursor += sizeof(JSErrorReport);
  158. if (argsArraySize != 0) {
  159. copy->messageArgs = (const jschar **)cursor;
  160. cursor += argsArraySize;
  161. for (i = 0; report->messageArgs[i]; ++i) {
  162. copy->messageArgs[i] = (const jschar *)cursor;
  163. argSize = JS_CHARS_SIZE(report->messageArgs[i]);
  164. memcpy(cursor, report->messageArgs[i], argSize);
  165. cursor += argSize;
  166. }
  167. copy->messageArgs[i] = NULL;
  168. JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize);
  169. }
  170. if (report->ucmessage) {
  171. copy->ucmessage = (const jschar *)cursor;
  172. memcpy(cursor, report->ucmessage, ucmessageSize);
  173. cursor += ucmessageSize;
  174. }
  175. if (report->uclinebuf) {
  176. copy->uclinebuf = (const jschar *)cursor;
  177. memcpy(cursor, report->uclinebuf, uclinebufSize);
  178. cursor += uclinebufSize;
  179. if (report->uctokenptr) {
  180. copy->uctokenptr = copy->uclinebuf + (report->uctokenptr -
  181. report->uclinebuf);
  182. }
  183. }
  184. if (report->linebuf) {
  185. copy->linebuf = (const char *)cursor;
  186. memcpy(cursor, report->linebuf, linebufSize);
  187. cursor += linebufSize;
  188. if (report->tokenptr) {
  189. copy->tokenptr = copy->linebuf + (report->tokenptr -
  190. report->linebuf);
  191. }
  192. }
  193. if (report->filename) {
  194. copy->filename = (const char *)cursor;
  195. memcpy(cursor, report->filename, filenameSize);
  196. }
  197. JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize);
  198. /* Copy non-pointer members. */
  199. copy->lineno = report->lineno;
  200. copy->errorNumber = report->errorNumber;
  201. /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
  202. copy->flags = report->flags;
  203. #undef JS_CHARS_SIZE
  204. return copy;
  205. }
  206. static jsval *
  207. GetStackTraceValueBuffer(JSExnPrivate *priv)
  208. {
  209. /*
  210. * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals
  211. * that helps to produce more informative stack traces. The following
  212. * assert allows us to assume that no gap after stackElems is necessary to
  213. * align the buffer properly.
  214. */
  215. JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0);
  216. return (jsval *)(priv->stackElems + priv->stackDepth);
  217. }
  218. static JSBool
  219. InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
  220. JSString *filename, uintN lineno, JSErrorReport *report)
  221. {
  222. JSSecurityCallbacks *callbacks;
  223. JSCheckAccessOp checkAccess;
  224. JSErrorReporter older;
  225. JSExceptionState *state;
  226. jsval callerid, v;
  227. JSStackFrame *fp, *fpstop;
  228. size_t stackDepth, valueCount, size;
  229. JSBool overflow;
  230. JSExnPrivate *priv;
  231. JSStackTraceElem *elem;
  232. jsval *values;
  233. JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass);
  234. /*
  235. * Prepare stack trace data.
  236. *
  237. * Set aside any error reporter for cx and save its exception state
  238. * so we can suppress any checkAccess failures. Such failures should stop
  239. * the backtrace procedure, not result in a failure of this constructor.
  240. */
  241. callbacks = JS_GetSecurityCallbacks(cx);
  242. checkAccess = callbacks
  243. ? callbacks->checkObjectAccess
  244. : NULL;
  245. older = JS_SetErrorReporter(cx, NULL);
  246. state = JS_SaveExceptionState(cx);
  247. callerid = ATOM_KEY(cx->runtime->atomState.callerAtom);
  248. stackDepth = 0;
  249. valueCount = 0;
  250. for (fp = cx->fp; fp; fp = fp->down) {
  251. if (fp->fun && fp->argv) {
  252. v = JSVAL_NULL;
  253. if (checkAccess &&
  254. !checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) {
  255. break;
  256. }
  257. valueCount += fp->argc;
  258. }
  259. ++stackDepth;
  260. }
  261. JS_RestoreExceptionState(cx, state);
  262. JS_SetErrorReporter(cx, older);
  263. fpstop = fp;
  264. size = offsetof(JSExnPrivate, stackElems);
  265. overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem));
  266. size += stackDepth * sizeof(JSStackTraceElem);
  267. overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval));
  268. size += valueCount * sizeof(jsval);
  269. if (overflow) {
  270. js_ReportAllocationOverflow(cx);
  271. return JS_FALSE;
  272. }
  273. priv = (JSExnPrivate *)JS_malloc(cx, size);
  274. if (!priv)
  275. return JS_FALSE;
  276. /*
  277. * We initialize errorReport with a copy of report after setting the
  278. * private slot, to prevent GC accessing a junk value we clear the field
  279. * here.
  280. */
  281. priv->errorReport = NULL;
  282. priv->message = message;
  283. priv->filename = filename;
  284. priv->lineno = lineno;
  285. priv->stackDepth = stackDepth;
  286. values = GetStackTraceValueBuffer(priv);
  287. elem = priv->stackElems;
  288. for (fp = cx->fp; fp != fpstop; fp = fp->down) {
  289. if (!fp->fun) {
  290. elem->funName = NULL;
  291. elem->argc = 0;
  292. } else {
  293. elem->funName = fp->fun->atom
  294. ? ATOM_TO_STRING(fp->fun->atom)
  295. : cx->runtime->emptyString;
  296. elem->argc = fp->argc;
  297. memcpy(values, fp->argv, fp->argc * sizeof(jsval));
  298. values += fp->argc;
  299. }
  300. elem->ulineno = 0;
  301. elem->filename = NULL;
  302. if (fp->script) {
  303. elem->filename = fp->script->filename;
  304. if (fp->regs)
  305. elem->ulineno = js_FramePCToLineNumber(cx, fp);
  306. }
  307. ++elem;
  308. }
  309. JS_ASSERT(priv->stackElems + stackDepth == elem);
  310. JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
  311. STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv));
  312. if (report) {
  313. /*
  314. * Construct a new copy of the error report struct. We can't use the
  315. * error report struct that was passed in, because it's allocated on
  316. * the stack, and also because it may point to transient data in the
  317. * JSTokenStream.
  318. */
  319. priv->errorReport = CopyErrorReport(cx, report);
  320. if (!priv->errorReport) {
  321. /* The finalizer realeases priv since it is in the private slot. */
  322. return JS_FALSE;
  323. }
  324. }
  325. return JS_TRUE;
  326. }
  327. static JSExnPrivate *
  328. GetExnPrivate(JSContext *cx, JSObject *obj)
  329. {
  330. jsval privateValue;
  331. JSExnPrivate *priv;
  332. JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass);
  333. privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
  334. if (JSVAL_IS_VOID(privateValue))
  335. return NULL;
  336. priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue);
  337. JS_ASSERT(priv);
  338. return priv;
  339. }
  340. static void
  341. exn_trace(JSTracer *trc, JSObject *obj)
  342. {
  343. JSExnPrivate *priv;
  344. JSStackTraceElem *elem;
  345. size_t vcount, i;
  346. jsval *vp, v;
  347. priv = GetExnPrivate(trc->context, obj);
  348. if (priv) {
  349. if (priv->message)
  350. JS_CALL_STRING_TRACER(trc, priv->message, "exception message");
  351. if (priv->filename)
  352. JS_CALL_STRING_TRACER(trc, priv->filename, "exception filename");
  353. elem = priv->stackElems;
  354. for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
  355. if (elem->funName) {
  356. JS_CALL_STRING_TRACER(trc, elem->funName,
  357. "stack trace function name");
  358. }
  359. if (IS_GC_MARKING_TRACER(trc) && elem->filename)
  360. js_MarkScriptFilename(elem->filename);
  361. vcount += elem->argc;
  362. }
  363. vp = GetStackTraceValueBuffer(priv);
  364. for (i = 0; i != vcount; ++i, ++vp) {
  365. v = *vp;
  366. JS_CALL_VALUE_TRACER(trc, v, "stack trace argument");
  367. }
  368. }
  369. }
  370. static void
  371. exn_finalize(JSContext *cx, JSObject *obj)
  372. {
  373. JSExnPrivate *priv;
  374. priv = GetExnPrivate(cx, obj);
  375. if (priv) {
  376. if (priv->errorReport)
  377. JS_free(cx, priv->errorReport);
  378. JS_free(cx, priv);
  379. }
  380. }
  381. static JSBool
  382. exn_enumerate(JSContext *cx, JSObject *obj)
  383. {
  384. JSAtomState *atomState;
  385. uintN i;
  386. JSAtom *atom;
  387. JSObject *pobj;
  388. JSProperty *prop;
  389. JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1);
  390. static const uint16 offsets[] = {
  391. (uint16)offsetof(JSAtomState, messageAtom),
  392. (uint16)offsetof(JSAtomState, fileNameAtom),
  393. (uint16)offsetof(JSAtomState, lineNumberAtom),
  394. (uint16)offsetof(JSAtomState, stackAtom),
  395. };
  396. atomState = &cx->runtime->atomState;
  397. for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) {
  398. atom = *(JSAtom **)((uint8 *)atomState + offsets[i]);
  399. if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
  400. return JS_FALSE;
  401. if (prop)
  402. OBJ_DROP_PROPERTY(cx, pobj, prop);
  403. }
  404. return JS_TRUE;
  405. }
  406. static JSBool
  407. exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
  408. JSObject **objp)
  409. {
  410. JSExnPrivate *priv;
  411. JSString *str;
  412. JSAtom *atom;
  413. JSString *stack;
  414. const char *prop;
  415. jsval v;
  416. *objp = NULL;
  417. priv = GetExnPrivate(cx, obj);
  418. if (priv && JSVAL_IS_STRING(id)) {
  419. str = JSVAL_TO_STRING(id);
  420. atom = cx->runtime->atomState.messageAtom;
  421. if (str == ATOM_TO_STRING(atom)) {
  422. prop = js_message_str;
  423. v = STRING_TO_JSVAL(priv->message);
  424. goto define;
  425. }
  426. atom = cx->runtime->atomState.fileNameAtom;
  427. if (str == ATOM_TO_STRING(atom)) {
  428. prop = js_fileName_str;
  429. v = STRING_TO_JSVAL(priv->filename);
  430. goto define;
  431. }
  432. atom = cx->runtime->atomState.lineNumberAtom;
  433. if (str == ATOM_TO_STRING(atom)) {
  434. prop = js_lineNumber_str;
  435. v = INT_TO_JSVAL(priv->lineno);
  436. goto define;
  437. }
  438. atom = cx->runtime->atomState.stackAtom;
  439. if (str == ATOM_TO_STRING(atom)) {
  440. stack = StackTraceToString(cx, priv);
  441. if (!stack)
  442. return JS_FALSE;
  443. /* Allow to GC all things that were used to build stack trace. */
  444. priv->stackDepth = 0;
  445. prop = js_stack_str;
  446. v = STRING_TO_JSVAL(stack);
  447. goto define;
  448. }
  449. }
  450. return JS_TRUE;
  451. define:
  452. if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE))
  453. return JS_FALSE;
  454. *objp = obj;
  455. return JS_TRUE;
  456. }
  457. JSErrorReport *
  458. js_ErrorFromException(JSContext *cx, jsval exn)
  459. {
  460. JSObject *obj;
  461. JSExnPrivate *priv;
  462. if (JSVAL_IS_PRIMITIVE(exn))
  463. return NULL;
  464. obj = JSVAL_TO_OBJECT(exn);
  465. if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass)
  466. return NULL;
  467. priv = GetExnPrivate(cx, obj);
  468. if (!priv)
  469. return NULL;
  470. return priv->errorReport;
  471. }
  472. struct JSExnSpec {
  473. int protoIndex;
  474. const char *name;
  475. JSProtoKey key;
  476. JSNative native;
  477. };
  478. /*
  479. * All *Error constructors share the same JSClass, js_ErrorClass. But each
  480. * constructor function for an *Error class must have a distinct native 'call'
  481. * function pointer, in order for instanceof to work properly across multiple
  482. * standard class sets. See jsfun.c:fun_hasInstance.
  483. */
  484. #define MAKE_EXCEPTION_CTOR(name) \
  485. static JSBool \
  486. name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \
  487. { \
  488. return Exception(cx, obj, argc, argv, rval); \
  489. }
  490. MAKE_EXCEPTION_CTOR(Error)
  491. MAKE_EXCEPTION_CTOR(InternalError)
  492. MAKE_EXCEPTION_CTOR(EvalError)
  493. MAKE_EXCEPTION_CTOR(RangeError)
  494. MAKE_EXCEPTION_CTOR(ReferenceError)
  495. MAKE_EXCEPTION_CTOR(SyntaxError)
  496. MAKE_EXCEPTION_CTOR(TypeError)
  497. MAKE_EXCEPTION_CTOR(URIError)
  498. #undef MAKE_EXCEPTION_CTOR
  499. static struct JSExnSpec exceptions[] = {
  500. {JSEXN_NONE, js_Error_str, JSProto_Error, Error},
  501. {JSEXN_ERR, js_InternalError_str, JSProto_InternalError, InternalError},
  502. {JSEXN_ERR, js_EvalError_str, JSProto_EvalError, EvalError},
  503. {JSEXN_ERR, js_RangeError_str, JSProto_RangeError, RangeError},
  504. {JSEXN_ERR, js_ReferenceError_str, JSProto_ReferenceError, ReferenceError},
  505. {JSEXN_ERR, js_SyntaxError_str, JSProto_SyntaxError, SyntaxError},
  506. {JSEXN_ERR, js_TypeError_str, JSProto_TypeError, TypeError},
  507. {JSEXN_ERR, js_URIError_str, JSProto_URIError, URIError},
  508. {0, NULL, JSProto_Null, NULL}
  509. };
  510. static JSString *
  511. ValueToShortSource(JSContext *cx, jsval v)
  512. {
  513. JSString *str;
  514. /* Avoid toSource bloat and fallibility for object types. */
  515. if (JSVAL_IS_PRIMITIVE(v)) {
  516. str = js_ValueToSource(cx, v);
  517. } else if (VALUE_IS_FUNCTION(cx, v)) {
  518. /*
  519. * XXX Avoid function decompilation bloat for now.
  520. */
  521. str = JS_GetFunctionId(JS_ValueToFunction(cx, v));
  522. if (!str && !(str = js_ValueToSource(cx, v))) {
  523. /*
  524. * Continue to soldier on if the function couldn't be
  525. * converted into a string.
  526. */
  527. JS_ClearPendingException(cx);
  528. str = JS_NewStringCopyZ(cx, "[unknown function]");
  529. }
  530. } else {
  531. /*
  532. * XXX Avoid toString on objects, it takes too long and uses too much
  533. * memory, for too many classes (see Mozilla bug 166743).
  534. */
  535. char buf[100];
  536. JS_snprintf(buf, sizeof buf, "[object %s]",
  537. OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name);
  538. str = JS_NewStringCopyZ(cx, buf);
  539. }
  540. return str;
  541. }
  542. static JSString *
  543. StackTraceToString(JSContext *cx, JSExnPrivate *priv)
  544. {
  545. jschar *stackbuf;
  546. size_t stacklen, stackmax;
  547. JSStackTraceElem *elem, *endElem;
  548. jsval *values;
  549. size_t i;
  550. JSString *str;
  551. const char *cp;
  552. char ulnbuf[11];
  553. /* After this point, failing control flow must goto bad. */
  554. stackbuf = NULL;
  555. stacklen = stackmax = 0;
  556. /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
  557. #define STACK_LENGTH_LIMIT JS_BIT(20)
  558. #define APPEND_CHAR_TO_STACK(c) \
  559. JS_BEGIN_MACRO \
  560. if (stacklen == stackmax) { \
  561. void *ptr_; \
  562. if (stackmax >= STACK_LENGTH_LIMIT) \
  563. goto done; \
  564. stackmax = stackmax ? 2 * stackmax : 64; \
  565. ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
  566. if (!ptr_) \
  567. goto bad; \
  568. stackbuf = (jschar *) ptr_; \
  569. } \
  570. stackbuf[stacklen++] = (c); \
  571. JS_END_MACRO
  572. #define APPEND_STRING_TO_STACK(str) \
  573. JS_BEGIN_MACRO \
  574. JSString *str_ = str; \
  575. jschar *chars_; \
  576. size_t length_; \
  577. \
  578. JSSTRING_CHARS_AND_LENGTH(str_, chars_, length_); \
  579. if (length_ > stackmax - stacklen) { \
  580. void *ptr_; \
  581. if (stackmax >= STACK_LENGTH_LIMIT || \
  582. length_ >= STACK_LENGTH_LIMIT - stacklen) { \
  583. goto done; \
  584. } \
  585. stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \
  586. ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \
  587. if (!ptr_) \
  588. goto bad; \
  589. stackbuf = (jschar *) ptr_; \
  590. } \
  591. js_strncpy(stackbuf + stacklen, chars_, length_); \
  592. stacklen += length_; \
  593. JS_END_MACRO
  594. values = GetStackTraceValueBuffer(priv);
  595. elem = priv->stackElems;
  596. for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
  597. if (elem->funName) {
  598. APPEND_STRING_TO_STACK(elem->funName);
  599. APPEND_CHAR_TO_STACK('(');
  600. for (i = 0; i != elem->argc; i++, values++) {
  601. if (i > 0)
  602. APPEND_CHAR_TO_STACK(',');
  603. str = ValueToShortSource(cx, *values);
  604. if (!str)
  605. goto bad;
  606. APPEND_STRING_TO_STACK(str);
  607. }
  608. APPEND_CHAR_TO_STACK(')');
  609. }
  610. APPEND_CHAR_TO_STACK('@');
  611. if (elem->filename) {
  612. for (cp = elem->filename; *cp; cp++)
  613. APPEND_CHAR_TO_STACK(*cp);
  614. }
  615. APPEND_CHAR_TO_STACK(':');
  616. JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno);
  617. for (cp = ulnbuf; *cp; cp++)
  618. APPEND_CHAR_TO_STACK(*cp);
  619. APPEND_CHAR_TO_STACK('\n');
  620. }
  621. #undef APPEND_CHAR_TO_STACK
  622. #undef APPEND_STRING_TO_STACK
  623. #undef STACK_LENGTH_LIMIT
  624. done:
  625. if (stacklen == 0) {
  626. JS_ASSERT(!stackbuf);
  627. return cx->runtime->emptyString;
  628. }
  629. if (stacklen < stackmax) {
  630. /*
  631. * Realloc can fail when shrinking on some FreeBSD versions, so
  632. * don't use JS_realloc here; simply let the oversized allocation
  633. * be owned by the string in that rare case.
  634. */
  635. void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar));
  636. if (shrunk)
  637. stackbuf = (jschar *) shrunk;
  638. }
  639. stackbuf[stacklen] = 0;
  640. str = js_NewString(cx, stackbuf, stacklen);
  641. if (str)
  642. return str;
  643. bad:
  644. if (stackbuf)
  645. JS_free(cx, stackbuf);
  646. return NULL;
  647. }
  648. /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
  649. with these two functions. */
  650. static JSString *
  651. FilenameToString(JSContext *cx, const char *filename)
  652. {
  653. return JS_NewStringCopyZ(cx, filename);
  654. }
  655. static const char *
  656. StringToFilename(JSContext *cx, JSString *str)
  657. {
  658. return js_GetStringBytes(cx, str);
  659. }
  660. static JSBool
  661. Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  662. {
  663. uint32 lineno;
  664. JSString *message, *filename;
  665. JSStackFrame *fp;
  666. if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
  667. /*
  668. * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
  669. * called as functions, without operator new. But as we do not give
  670. * each constructor a distinct JSClass, whose .name member is used by
  671. * js_NewObject to find the class prototype, we must get the class
  672. * prototype ourselves.
  673. */
  674. if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]),
  675. ATOM_TO_JSID(cx->runtime->atomState
  676. .classPrototypeAtom),
  677. rval))
  678. return JS_FALSE;
  679. obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL, 0);
  680. if (!obj)
  681. return JS_FALSE;
  682. *rval = OBJECT_TO_JSVAL(obj);
  683. }
  684. /*
  685. * If it's a new object of class Exception, then null out the private
  686. * data so that the finalizer doesn't attempt to free it.
  687. */
  688. if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass)
  689. STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, JSVAL_VOID);
  690. /* Set the 'message' property. */
  691. if (argc != 0) {
  692. message = js_ValueToString(cx, argv[0]);
  693. if (!message)
  694. return JS_FALSE;
  695. argv[0] = STRING_TO_JSVAL(message);
  696. } else {
  697. message = cx->runtime->emptyString;
  698. }
  699. /* Set the 'fileName' property. */
  700. if (argc > 1) {
  701. filename = js_ValueToString(cx, argv[1]);
  702. if (!filename)
  703. return JS_FALSE;
  704. argv[1] = STRING_TO_JSVAL(filename);
  705. fp = NULL;
  706. } else {
  707. fp = JS_GetScriptedCaller(cx, NULL);
  708. if (fp) {
  709. filename = FilenameToString(cx, fp->script->filename);
  710. if (!filename)
  711. return JS_FALSE;
  712. } else {
  713. filename = cx->runtime->emptyString;
  714. }
  715. }
  716. /* Set the 'lineNumber' property. */
  717. if (argc > 2) {
  718. lineno = js_ValueToECMAUint32(cx, &argv[2]);
  719. if (JSVAL_IS_NULL(argv[2]))
  720. return JS_FALSE;
  721. } else {
  722. if (!fp)
  723. fp = JS_GetScriptedCaller(cx, NULL);
  724. lineno = (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0;
  725. }
  726. return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) ||
  727. InitExnPrivate(cx, obj, message, filename, lineno, NULL);
  728. }
  729. /*
  730. * Convert to string.
  731. *
  732. * This method only uses JavaScript-modifiable properties name, message. It
  733. * is left to the host to check for private data and report filename and line
  734. * number information along with this message.
  735. */
  736. static JSBool
  737. exn_toString(JSContext *cx, uintN argc, jsval *vp)
  738. {
  739. JSObject *obj;
  740. jsval v;
  741. JSString *name, *message, *result;
  742. jschar *chars, *cp;
  743. size_t name_length, message_length, length;
  744. obj = JS_THIS_OBJECT(cx, vp);
  745. if (!obj ||
  746. !OBJ_GET_PROPERTY(cx, obj,
  747. ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
  748. &v)) {
  749. return JS_FALSE;
  750. }
  751. name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString;
  752. *vp = STRING_TO_JSVAL(name);
  753. if (!JS_GetProperty(cx, obj, js_message_str, &v))
  754. return JS_FALSE;
  755. message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v)
  756. : cx->runtime->emptyString;
  757. if (JSSTRING_LENGTH(message) != 0) {
  758. name_length = JSSTRING_LENGTH(name);
  759. message_length = JSSTRING_LENGTH(message);
  760. length = (name_length ? name_length + 2 : 0) + message_length;
  761. cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
  762. if (!chars)
  763. return JS_FALSE;
  764. if (name_length) {
  765. js_strncpy(cp, JSSTRING_CHARS(name), name_length);
  766. cp += name_length;
  767. *cp++ = ':'; *cp++ = ' ';
  768. }
  769. js_strncpy(cp, JSSTRING_CHARS(message), message_length);
  770. cp += message_length;
  771. *cp = 0;
  772. result = js_NewString(cx, chars, length);
  773. if (!result) {
  774. JS_free(cx, chars);
  775. return JS_FALSE;
  776. }
  777. } else {
  778. result = name;
  779. }
  780. *vp = STRING_TO_JSVAL(result);
  781. return JS_TRUE;
  782. }
  783. #if JS_HAS_TOSOURCE
  784. /*
  785. * Return a string that may eval to something similar to the original object.
  786. */
  787. static JSBool
  788. exn_toSource(JSContext *cx, uintN argc, jsval *vp)
  789. {
  790. JSObject *obj;
  791. JSString *name, *message, *filename, *lineno_as_str, *result;
  792. jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
  793. JSTempValueRooter tvr;
  794. JSBool ok;
  795. uint32 lineno;
  796. size_t lineno_length, name_length, message_length, filename_length, length;
  797. jschar *chars, *cp;
  798. obj = JS_THIS_OBJECT(cx, vp);
  799. if (!obj ||
  800. !OBJ_GET_PROPERTY(cx, obj,
  801. ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
  802. vp)) {
  803. return JS_FALSE;
  804. }
  805. name = js_ValueToString(cx, *vp);
  806. if (!name)
  807. return JS_FALSE;
  808. *vp = STRING_TO_JSVAL(name);
  809. MUST_FLOW_THROUGH("out");
  810. JS_PUSH_TEMP_ROOT(cx, 3, localroots, &tvr);
  811. #ifdef __GNUC__
  812. message = filename = NULL;
  813. #endif
  814. ok = JS_GetProperty(cx, obj, js_message_str, &localroots[0]) &&
  815. (message = js_ValueToSource(cx, localroots[0]));
  816. if (!ok)
  817. goto out;
  818. localroots[0] = STRING_TO_JSVAL(message);
  819. ok = JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) &&
  820. (filename = js_ValueToSource(cx, localroots[1]));
  821. if (!ok)
  822. goto out;
  823. localroots[1] = STRING_TO_JSVAL(filename);
  824. ok = JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]);
  825. if (!ok)
  826. goto out;
  827. lineno = js_ValueToECMAUint32 (cx, &localroots[2]);
  828. ok = !JSVAL_IS_NULL(localroots[2]);
  829. if (!ok)
  830. goto out;
  831. if (lineno != 0) {
  832. lineno_as_str = js_ValueToString(cx, localroots[2]);
  833. if (!lineno_as_str) {
  834. ok = JS_FALSE;
  835. goto out;
  836. }
  837. lineno_length = JSSTRING_LENGTH(lineno_as_str);
  838. } else {
  839. lineno_as_str = NULL;
  840. lineno_length = 0;
  841. }
  842. /* Magic 8, for the characters in ``(new ())''. */
  843. name_length = JSSTRING_LENGTH(name);
  844. message_length = JSSTRING_LENGTH(message);
  845. length = 8 + name_length + message_length;
  846. filename_length = JSSTRING_LENGTH(filename);
  847. if (filename_length != 0) {
  848. /* append filename as ``, {filename}'' */
  849. length += 2 + filename_length;
  850. if (lineno_as_str) {
  851. /* append lineno as ``, {lineno_as_str}'' */
  852. length += 2 + lineno_length;
  853. }
  854. } else {
  855. if (lineno_as_str) {
  856. /*
  857. * no filename, but have line number,
  858. * need to append ``, "", {lineno_as_str}''
  859. */
  860. length += 6 + lineno_length;
  861. }
  862. }
  863. cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
  864. if (!chars) {
  865. ok = JS_FALSE;
  866. goto out;
  867. }
  868. *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
  869. js_strncpy(cp, JSSTRING_CHARS(name), name_length);
  870. cp += name_length;
  871. *cp++ = '(';
  872. if (message_length != 0) {
  873. js_strncpy(cp, JSSTRING_CHARS(message), message_length);
  874. cp += message_length;
  875. }
  876. if (filename_length != 0) {
  877. /* append filename as ``, {filename}'' */
  878. *cp++ = ','; *cp++ = ' ';
  879. js_strncpy(cp, JSSTRING_CHARS(filename), filename_length);
  880. cp += filename_length;
  881. } else {
  882. if (lineno_as_str) {
  883. /*
  884. * no filename, but have line number,
  885. * need to append ``, "", {lineno_as_str}''
  886. */
  887. *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
  888. }
  889. }
  890. if (lineno_as_str) {
  891. /* append lineno as ``, {lineno_as_str}'' */
  892. *cp++ = ','; *cp++ = ' ';
  893. js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length);
  894. cp += lineno_length;
  895. }
  896. *cp++ = ')'; *cp++ = ')'; *cp = 0;
  897. result = js_NewString(cx, chars, length);
  898. if (!result) {
  899. JS_free(cx, chars);
  900. ok = JS_FALSE;
  901. goto out;
  902. }
  903. *vp = STRING_TO_JSVAL(result);
  904. ok = JS_TRUE;
  905. out:
  906. JS_POP_TEMP_ROOT(cx, &tvr);
  907. return ok;
  908. }
  909. #endif
  910. static JSFunctionSpec exception_methods[] = {
  911. #if JS_HAS_TOSOURCE
  912. JS_FN(js_toSource_str, exn_toSource, 0,0),
  913. #endif
  914. JS_FN(js_toString_str, exn_toString, 0,0),
  915. JS_FS_END
  916. };
  917. JSObject *
  918. js_InitExceptionClasses(JSContext *cx, JSObject *obj)
  919. {
  920. JSObject *obj_proto, *protos[JSEXN_LIMIT];
  921. int i;
  922. /*
  923. * If lazy class initialization occurs for any Error subclass, then all
  924. * classes are initialized, starting with Error. To avoid reentry and
  925. * redundant initialization, we must not pass a null proto parameter to
  926. * js_NewObject below, when called for the Error superclass. We need to
  927. * ensure that Object.prototype is the proto of Error.prototype.
  928. *
  929. * See the equivalent code to ensure that parent_proto is non-null when
  930. * JS_InitClass calls js_NewObject, in jsapi.c.
  931. */
  932. if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
  933. &obj_proto)) {
  934. return NULL;
  935. }
  936. if (!js_EnterLocalRootScope(cx))
  937. return NULL;
  938. /* Initialize the prototypes first. */
  939. for (i = 0; exceptions[i].name != 0; i++) {
  940. JSAtom *atom;
  941. JSFunction *fun;
  942. JSString *nameString;
  943. int protoIndex = exceptions[i].protoIndex;
  944. /* Make the prototype for the current constructor name. */
  945. protos[i] = js_NewObject(cx, &js_ErrorClass,
  946. (protoIndex != JSEXN_NONE)
  947. ? protos[protoIndex]
  948. : obj_proto,
  949. obj, 0);
  950. if (!protos[i])
  951. break;
  952. /* So exn_finalize knows whether to destroy private data. */
  953. STOBJ_SET_SLOT(protos[i], JSSLOT_PRIVATE, JSVAL_VOID);
  954. /* Make a constructor function for the current name. */
  955. atom = cx->runtime->atomState.classAtoms[exceptions[i].key];
  956. fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0);
  957. if (!fun)
  958. break;
  959. /* Make this constructor make objects of class Exception. */
  960. FUN_CLASP(fun) = &js_ErrorClass;
  961. /* Make the prototype and constructor links. */
  962. if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), protos[i],
  963. JSPROP_READONLY | JSPROP_PERMANENT)) {
  964. break;
  965. }
  966. /* proto bootstrap bit from JS_InitClass omitted. */
  967. nameString = JS_NewStringCopyZ(cx, exceptions[i].name);
  968. if (!nameString)
  969. break;
  970. /* Add the name property to the prototype. */
  971. if (!JS_DefineProperty(cx, protos[i], js_name_str,
  972. STRING_TO_JSVAL(nameString),
  973. NULL, NULL,
  974. JSPROP_ENUMERATE)) {
  975. break;
  976. }
  977. /* Finally, stash the constructor for later uses. */
  978. if (!js_SetClassObject(cx, obj, exceptions[i].key, FUN_OBJECT(fun)))
  979. break;
  980. }
  981. js_LeaveLocalRootScope(cx);
  982. if (exceptions[i].name)
  983. return NULL;
  984. /*
  985. * Add an empty message property. (To Exception.prototype only,
  986. * because this property will be the same for all the exception
  987. * protos.)
  988. */
  989. if (!JS_DefineProperty(cx, protos[0], js_message_str,
  990. STRING_TO_JSVAL(cx->runtime->emptyString),
  991. NULL, NULL, JSPROP_ENUMERATE)) {
  992. return NULL;
  993. }
  994. if (!JS_DefineProperty(cx, protos[0], js_fileName_str,
  995. STRING_TO_JSVAL(cx->runtime->emptyString),
  996. NULL, NULL, JSPROP_ENUMERATE)) {
  997. return NULL;
  998. }
  999. if (!JS_DefineProperty(cx, protos[0], js_lineNumber_str,
  1000. INT_TO_JSVAL(0),
  1001. NULL, NULL, JSPROP_ENUMERATE)) {
  1002. return NULL;
  1003. }
  1004. /*
  1005. * Add methods only to Exception.prototype, because ostensibly all
  1006. * exception types delegate to that.
  1007. */
  1008. if (!JS_DefineFunctions(cx, protos[0], exception_methods))
  1009. return NULL;
  1010. return protos[0];
  1011. }
  1012. const JSErrorFormatString*
  1013. js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
  1014. const uintN errorNumber)
  1015. {
  1016. const JSErrorFormatString *errorString = NULL;
  1017. if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
  1018. errorString = cx->localeCallbacks
  1019. ->localeGetErrorMessage(userRef, locale, errorNumber);
  1020. }
  1021. if (!errorString)
  1022. errorString = js_GetErrorMessage(userRef, locale, errorNumber);
  1023. return errorString;
  1024. }
  1025. #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
  1026. /* For use below... get character strings for error name and exception name */
  1027. static struct exnname { char *name; char *exception; } errortoexnname[] = {
  1028. #define MSG_DEF(name, number, count, exception, format) \
  1029. {#name, #exception},
  1030. #include "js.msg"
  1031. #undef MSG_DEF
  1032. };
  1033. #endif /* DEBUG */
  1034. JSBool
  1035. js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
  1036. {
  1037. JSErrNum errorNumber;
  1038. const JSErrorFormatString *errorString;
  1039. JSExnType exn;
  1040. jsval tv[4];
  1041. JSTempValueRooter tvr;
  1042. JSBool ok;
  1043. JSObject *errProto, *errObject;
  1044. JSString *messageStr, *filenameStr;
  1045. /*
  1046. * Tell our caller to report immediately if this report is just a warning.
  1047. */
  1048. JS_ASSERT(reportp);
  1049. if (JSREPORT_IS_WARNING(reportp->flags))
  1050. return JS_FALSE;
  1051. /* Find the exception index associated with this error. */
  1052. errorNumber = (JSErrNum) reportp->errorNumber;
  1053. errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
  1054. exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
  1055. JS_ASSERT(exn < JSEXN_LIMIT);
  1056. #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
  1057. /* Print the error name and the associated exception name to stderr */
  1058. fprintf(stderr, "%s\t%s\n",
  1059. errortoexnname[errorNumber].name,
  1060. errortoexnname[errorNumber].exception);
  1061. #endif
  1062. /*
  1063. * Return false (no exception raised) if no exception is associated
  1064. * with the given error number.
  1065. */
  1066. if (exn == JSEXN_NONE)
  1067. return JS_FALSE;
  1068. /*
  1069. * Prevent runaway recursion, via cx->generatingError. If an out-of-memory
  1070. * error occurs, no exception object will be created, but we don't assume
  1071. * that OOM is the only kind of error that subroutines of this function
  1072. * called below might raise.
  1073. */
  1074. if (cx->generatingError)
  1075. return JS_FALSE;
  1076. MUST_FLOW_THROUGH("out");
  1077. cx->generatingError = JS_TRUE;
  1078. /* Protect the newly-created strings below from nesting GCs. */
  1079. memset(tv, 0, sizeof tv);
  1080. JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(tv), tv, &tvr);
  1081. /*
  1082. * Try to get an appropriate prototype by looking up the corresponding
  1083. * exception constructor name in the scope chain of the current context's
  1084. * top stack frame, or in the global object if no frame is active.
  1085. */
  1086. ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key),
  1087. &errProto);
  1088. if (!ok)
  1089. goto out;
  1090. tv[0] = OBJECT_TO_JSVAL(errProto);
  1091. errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0);
  1092. if (!errObject) {
  1093. ok = JS_FALSE;
  1094. goto out;
  1095. }
  1096. tv[1] = OBJECT_TO_JSVAL(errObject);
  1097. messageStr = JS_NewStringCopyZ(cx, message);
  1098. if (!messageStr) {
  1099. ok = JS_FALSE;
  1100. goto out;
  1101. }
  1102. tv[2] = STRING_TO_JSVAL(messageStr);
  1103. filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
  1104. if (!filenameStr) {
  1105. ok = JS_FALSE;
  1106. goto out;
  1107. }
  1108. tv[3] = STRING_TO_JSVAL(filenameStr);
  1109. ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
  1110. reportp->lineno, reportp);
  1111. if (!ok)
  1112. goto out;
  1113. JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
  1114. /* Flag the error report passed in to indicate an exception was raised. */
  1115. reportp->flags |= JSREPORT_EXCEPTION;
  1116. out:
  1117. JS_POP_TEMP_ROOT(cx, &tvr);
  1118. cx->generatingError = JS_FALSE;
  1119. return ok;
  1120. }
  1121. JSBool
  1122. js_ReportUncaughtException(JSContext *cx)
  1123. {
  1124. jsval exn;
  1125. JSObject *exnObject;
  1126. jsval roots[5];
  1127. JSTempValueRooter tvr;
  1128. JSErrorReport *reportp, report;
  1129. JSString *str;
  1130. const char *bytes;
  1131. JSBool ok;
  1132. if (!JS_IsExceptionPending(cx))
  1133. return JS_TRUE;
  1134. if (!JS_GetPendingException(cx, &exn))
  1135. return JS_FALSE;
  1136. memset(roots, 0, sizeof roots);
  1137. JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);
  1138. /*
  1139. * Because js_ValueToString below could error and an exception object
  1140. * could become unrooted, we must root exnObject. Later, if exnObject is
  1141. * non-null, we need to root other intermediates, so allocate an operand
  1142. * stack segment to protect all of these values.
  1143. */
  1144. if (JSVAL_IS_PRIMITIVE(exn)) {
  1145. exnObject = NULL;
  1146. } else {
  1147. exnObject = JSVAL_TO_OBJECT(exn);
  1148. roots[0] = exn;
  1149. }
  1150. JS_ClearPendingException(cx);
  1151. reportp = js_ErrorFromException(cx, exn);
  1152. /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */
  1153. str = js_ValueToString(cx, exn);
  1154. if (!str) {
  1155. bytes = "unknown (can't convert to string)";
  1156. } else {
  1157. roots[1] = STRING_TO_JSVAL(str);
  1158. bytes = js_GetStringBytes(cx, str);
  1159. if (!bytes) {
  1160. ok = JS_FALSE;
  1161. goto out;
  1162. }
  1163. }
  1164. ok = JS_TRUE;
  1165. if (!reportp &&
  1166. exnObject &&
  1167. OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) {
  1168. const char *filename;
  1169. uint32 lineno;
  1170. ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]);
  1171. if (!ok)
  1172. goto out;
  1173. if (JSVAL_IS_STRING(roots[2])) {
  1174. bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2]));
  1175. if (!bytes) {
  1176. ok = JS_FALSE;
  1177. goto out;
  1178. }
  1179. }
  1180. ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]);
  1181. if (!ok)
  1182. goto out;
  1183. str = js_ValueToString(cx, roots[3]);
  1184. if (!str) {
  1185. ok = JS_FALSE;
  1186. goto out;
  1187. }
  1188. filename = StringToFilename(cx, str);
  1189. if (!filename) {
  1190. ok = JS_FALSE;
  1191. goto out;
  1192. }
  1193. ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]);
  1194. if (!ok)
  1195. goto out;
  1196. lineno = js_ValueToECMAUint32 (cx, &roots[4]);
  1197. ok = !JSVAL_IS_NULL(roots[4]);
  1198. if (!ok)
  1199. goto out;
  1200. reportp = &report;
  1201. memset(&report, 0, sizeof report);
  1202. report.filename = filename;
  1203. report.lineno = (uintN) lineno;
  1204. }
  1205. if (!reportp) {
  1206. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  1207. JSMSG_UNCAUGHT_EXCEPTION, bytes);
  1208. } else {
  1209. /* Flag the error as an exception. */
  1210. reportp->flags |= JSREPORT_EXCEPTION;
  1211. /* Pass the exception object. */
  1212. JS_SetPendingException(cx, exn);
  1213. js_ReportErrorAgain(cx, bytes, reportp);
  1214. JS_ClearPendingException(cx);
  1215. }
  1216. out:
  1217. JS_POP_TEMP_ROOT(cx, &tvr);
  1218. return ok;
  1219. }