1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsexn.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,930 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * JS standard exception implementation. 1.12 + */ 1.13 + 1.14 +#include "jsexn.h" 1.15 + 1.16 +#include "mozilla/ArrayUtils.h" 1.17 +#include "mozilla/PodOperations.h" 1.18 + 1.19 +#include <string.h> 1.20 + 1.21 +#include "jsapi.h" 1.22 +#include "jscntxt.h" 1.23 +#include "jsfun.h" 1.24 +#include "jsnum.h" 1.25 +#include "jsobj.h" 1.26 +#include "jsscript.h" 1.27 +#include "jstypes.h" 1.28 +#include "jsutil.h" 1.29 +#include "jswrapper.h" 1.30 + 1.31 +#include "gc/Marking.h" 1.32 +#include "vm/ErrorObject.h" 1.33 +#include "vm/GlobalObject.h" 1.34 +#include "vm/StringBuffer.h" 1.35 + 1.36 +#include "jsobjinlines.h" 1.37 + 1.38 +#include "vm/ErrorObject-inl.h" 1.39 + 1.40 +using namespace js; 1.41 +using namespace js::gc; 1.42 +using namespace js::types; 1.43 + 1.44 +using mozilla::ArrayLength; 1.45 +using mozilla::PodArrayZero; 1.46 +using mozilla::PodZero; 1.47 + 1.48 +static void 1.49 +exn_finalize(FreeOp *fop, JSObject *obj); 1.50 + 1.51 +const Class ErrorObject::class_ = { 1.52 + js_Error_str, 1.53 + JSCLASS_IMPLEMENTS_BARRIERS | 1.54 + JSCLASS_HAS_CACHED_PROTO(JSProto_Error) | 1.55 + JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), 1.56 + JS_PropertyStub, /* addProperty */ 1.57 + JS_DeletePropertyStub, /* delProperty */ 1.58 + JS_PropertyStub, /* getProperty */ 1.59 + JS_StrictPropertyStub, /* setProperty */ 1.60 + JS_EnumerateStub, 1.61 + JS_ResolveStub, 1.62 + JS_ConvertStub, 1.63 + exn_finalize, 1.64 + nullptr, /* call */ 1.65 + nullptr, /* hasInstance */ 1.66 + nullptr /* construct */ 1.67 +}; 1.68 + 1.69 +JSErrorReport * 1.70 +js::CopyErrorReport(JSContext *cx, JSErrorReport *report) 1.71 +{ 1.72 + /* 1.73 + * We use a single malloc block to make a deep copy of JSErrorReport with 1.74 + * the following layout: 1.75 + * JSErrorReport 1.76 + * array of copies of report->messageArgs 1.77 + * jschar array with characters for all messageArgs 1.78 + * jschar array with characters for ucmessage 1.79 + * jschar array with characters for uclinebuf and uctokenptr 1.80 + * char array with characters for linebuf and tokenptr 1.81 + * char array with characters for filename 1.82 + * Such layout together with the properties enforced by the following 1.83 + * asserts does not need any extra alignment padding. 1.84 + */ 1.85 + JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0); 1.86 + JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); 1.87 + 1.88 + size_t filenameSize; 1.89 + size_t linebufSize; 1.90 + size_t uclinebufSize; 1.91 + size_t ucmessageSize; 1.92 + size_t i, argsArraySize, argsCopySize, argSize; 1.93 + size_t mallocSize; 1.94 + JSErrorReport *copy; 1.95 + uint8_t *cursor; 1.96 + 1.97 +#define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) 1.98 + 1.99 + filenameSize = report->filename ? strlen(report->filename) + 1 : 0; 1.100 + linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; 1.101 + uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; 1.102 + ucmessageSize = 0; 1.103 + argsArraySize = 0; 1.104 + argsCopySize = 0; 1.105 + if (report->ucmessage) { 1.106 + ucmessageSize = JS_CHARS_SIZE(report->ucmessage); 1.107 + if (report->messageArgs) { 1.108 + for (i = 0; report->messageArgs[i]; ++i) 1.109 + argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); 1.110 + 1.111 + /* Non-null messageArgs should have at least one non-null arg. */ 1.112 + JS_ASSERT(i != 0); 1.113 + argsArraySize = (i + 1) * sizeof(const jschar *); 1.114 + } 1.115 + } 1.116 + 1.117 + /* 1.118 + * The mallocSize can not overflow since it represents the sum of the 1.119 + * sizes of already allocated objects. 1.120 + */ 1.121 + mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + 1.122 + ucmessageSize + uclinebufSize + linebufSize + filenameSize; 1.123 + cursor = cx->pod_malloc<uint8_t>(mallocSize); 1.124 + if (!cursor) 1.125 + return nullptr; 1.126 + 1.127 + copy = (JSErrorReport *)cursor; 1.128 + memset(cursor, 0, sizeof(JSErrorReport)); 1.129 + cursor += sizeof(JSErrorReport); 1.130 + 1.131 + if (argsArraySize != 0) { 1.132 + copy->messageArgs = (const jschar **)cursor; 1.133 + cursor += argsArraySize; 1.134 + for (i = 0; report->messageArgs[i]; ++i) { 1.135 + copy->messageArgs[i] = (const jschar *)cursor; 1.136 + argSize = JS_CHARS_SIZE(report->messageArgs[i]); 1.137 + js_memcpy(cursor, report->messageArgs[i], argSize); 1.138 + cursor += argSize; 1.139 + } 1.140 + copy->messageArgs[i] = nullptr; 1.141 + JS_ASSERT(cursor == (uint8_t *)copy->messageArgs[0] + argsCopySize); 1.142 + } 1.143 + 1.144 + if (report->ucmessage) { 1.145 + copy->ucmessage = (const jschar *)cursor; 1.146 + js_memcpy(cursor, report->ucmessage, ucmessageSize); 1.147 + cursor += ucmessageSize; 1.148 + } 1.149 + 1.150 + if (report->uclinebuf) { 1.151 + copy->uclinebuf = (const jschar *)cursor; 1.152 + js_memcpy(cursor, report->uclinebuf, uclinebufSize); 1.153 + cursor += uclinebufSize; 1.154 + if (report->uctokenptr) { 1.155 + copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - 1.156 + report->uclinebuf); 1.157 + } 1.158 + } 1.159 + 1.160 + if (report->linebuf) { 1.161 + copy->linebuf = (const char *)cursor; 1.162 + js_memcpy(cursor, report->linebuf, linebufSize); 1.163 + cursor += linebufSize; 1.164 + if (report->tokenptr) { 1.165 + copy->tokenptr = copy->linebuf + (report->tokenptr - 1.166 + report->linebuf); 1.167 + } 1.168 + } 1.169 + 1.170 + if (report->filename) { 1.171 + copy->filename = (const char *)cursor; 1.172 + js_memcpy(cursor, report->filename, filenameSize); 1.173 + } 1.174 + JS_ASSERT(cursor + filenameSize == (uint8_t *)copy + mallocSize); 1.175 + 1.176 + /* HOLD called by the destination error object. */ 1.177 + copy->originPrincipals = report->originPrincipals; 1.178 + 1.179 + /* Copy non-pointer members. */ 1.180 + copy->lineno = report->lineno; 1.181 + copy->column = report->column; 1.182 + copy->errorNumber = report->errorNumber; 1.183 + copy->exnType = report->exnType; 1.184 + 1.185 + /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ 1.186 + copy->flags = report->flags; 1.187 + 1.188 +#undef JS_CHARS_SIZE 1.189 + return copy; 1.190 +} 1.191 + 1.192 +struct SuppressErrorsGuard 1.193 +{ 1.194 + JSContext *cx; 1.195 + JSErrorReporter prevReporter; 1.196 + JS::AutoSaveExceptionState prevState; 1.197 + 1.198 + SuppressErrorsGuard(JSContext *cx) 1.199 + : cx(cx), 1.200 + prevReporter(JS_SetErrorReporter(cx, nullptr)), 1.201 + prevState(cx) 1.202 + {} 1.203 + 1.204 + ~SuppressErrorsGuard() 1.205 + { 1.206 + JS_SetErrorReporter(cx, prevReporter); 1.207 + } 1.208 +}; 1.209 + 1.210 +JSString * 1.211 +js::ComputeStackString(JSContext *cx) 1.212 +{ 1.213 + StringBuffer sb(cx); 1.214 + 1.215 + { 1.216 + RootedAtom atom(cx); 1.217 + SuppressErrorsGuard seg(cx); 1.218 + for (NonBuiltinFrameIter i(cx, FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED, 1.219 + cx->compartment()->principals); 1.220 + !i.done(); 1.221 + ++i) 1.222 + { 1.223 + /* First append the function name, if any. */ 1.224 + if (i.isNonEvalFunctionFrame()) 1.225 + atom = i.functionDisplayAtom(); 1.226 + else 1.227 + atom = nullptr; 1.228 + if (atom && !sb.append(atom)) 1.229 + return nullptr; 1.230 + 1.231 + /* Next a @ separating function name from source location. */ 1.232 + if (!sb.append('@')) 1.233 + return nullptr; 1.234 + 1.235 + /* Now the filename. */ 1.236 + const char *cfilename = i.scriptFilename(); 1.237 + if (!cfilename) 1.238 + cfilename = ""; 1.239 + if (!sb.appendInflated(cfilename, strlen(cfilename))) 1.240 + return nullptr; 1.241 + 1.242 + uint32_t column = 0; 1.243 + uint32_t line = i.computeLine(&column); 1.244 + // Now the line number 1.245 + if (!sb.append(':') || !NumberValueToStringBuffer(cx, NumberValue(line), sb)) 1.246 + return nullptr; 1.247 + 1.248 + // Finally, : followed by the column number (1-based, as in other browsers) 1.249 + // and a newline. 1.250 + if (!sb.append(':') || !NumberValueToStringBuffer(cx, NumberValue(column + 1), sb) || 1.251 + !sb.append('\n')) 1.252 + { 1.253 + return nullptr; 1.254 + } 1.255 + 1.256 + /* 1.257 + * Cut off the stack if it gets too deep (most commonly for 1.258 + * infinite recursion errors). 1.259 + */ 1.260 + const size_t MaxReportedStackDepth = 1u << 20; 1.261 + if (sb.length() > MaxReportedStackDepth) 1.262 + break; 1.263 + } 1.264 + } 1.265 + 1.266 + return sb.finishString(); 1.267 +} 1.268 + 1.269 +static void 1.270 +exn_finalize(FreeOp *fop, JSObject *obj) 1.271 +{ 1.272 + if (JSErrorReport *report = obj->as<ErrorObject>().getErrorReport()) { 1.273 + /* These were held by ErrorObject::init. */ 1.274 + if (JSPrincipals *prin = report->originPrincipals) 1.275 + JS_DropPrincipals(fop->runtime(), prin); 1.276 + fop->free_(report); 1.277 + } 1.278 +} 1.279 + 1.280 +JSErrorReport * 1.281 +js_ErrorFromException(JSContext *cx, HandleObject objArg) 1.282 +{ 1.283 + // It's ok to UncheckedUnwrap here, since all we do is get the 1.284 + // JSErrorReport, and consumers are careful with the information they get 1.285 + // from that anyway. Anyone doing things that would expose anything in the 1.286 + // JSErrorReport to page script either does a security check on the 1.287 + // JSErrorReport's principal or also tries to do toString on our object and 1.288 + // will fail if they can't unwrap it. 1.289 + RootedObject obj(cx, UncheckedUnwrap(objArg)); 1.290 + if (!obj->is<ErrorObject>()) 1.291 + return nullptr; 1.292 + 1.293 + return obj->as<ErrorObject>().getOrCreateErrorReport(cx); 1.294 +} 1.295 + 1.296 +static bool 1.297 +Error(JSContext *cx, unsigned argc, Value *vp) 1.298 +{ 1.299 + CallArgs args = CallArgsFromVp(argc, vp); 1.300 + 1.301 + /* Compute the error message, if any. */ 1.302 + RootedString message(cx, nullptr); 1.303 + if (args.hasDefined(0)) { 1.304 + message = ToString<CanGC>(cx, args[0]); 1.305 + if (!message) 1.306 + return false; 1.307 + } 1.308 + 1.309 + /* Find the scripted caller. */ 1.310 + NonBuiltinFrameIter iter(cx); 1.311 + 1.312 + /* Set the 'fileName' property. */ 1.313 + RootedString fileName(cx); 1.314 + if (args.length() > 1) { 1.315 + fileName = ToString<CanGC>(cx, args[1]); 1.316 + } else { 1.317 + fileName = cx->runtime()->emptyString; 1.318 + if (!iter.done()) { 1.319 + if (const char *cfilename = iter.scriptFilename()) 1.320 + fileName = JS_NewStringCopyZ(cx, cfilename); 1.321 + } 1.322 + } 1.323 + if (!fileName) 1.324 + return false; 1.325 + 1.326 + /* Set the 'lineNumber' property. */ 1.327 + uint32_t lineNumber, columnNumber = 0; 1.328 + if (args.length() > 2) { 1.329 + if (!ToUint32(cx, args[2], &lineNumber)) 1.330 + return false; 1.331 + } else { 1.332 + lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber); 1.333 + } 1.334 + 1.335 + Rooted<JSString*> stack(cx, ComputeStackString(cx)); 1.336 + if (!stack) 1.337 + return false; 1.338 + 1.339 + /* 1.340 + * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when 1.341 + * called as functions, without operator new. But as we do not give 1.342 + * each constructor a distinct JSClass, we must get the exception type 1.343 + * ourselves. 1.344 + */ 1.345 + JSExnType exnType = JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32()); 1.346 + 1.347 + RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName, 1.348 + lineNumber, columnNumber, nullptr, message)); 1.349 + if (!obj) 1.350 + return false; 1.351 + 1.352 + args.rval().setObject(*obj); 1.353 + return true; 1.354 +} 1.355 + 1.356 +/* ES5 15.11.4.4 (NB: with subsequent errata). */ 1.357 +static bool 1.358 +exn_toString(JSContext *cx, unsigned argc, Value *vp) 1.359 +{ 1.360 + JS_CHECK_RECURSION(cx, return false); 1.361 + CallArgs args = CallArgsFromVp(argc, vp); 1.362 + 1.363 + /* Step 2. */ 1.364 + if (!args.thisv().isObject()) { 1.365 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE, "Error"); 1.366 + return false; 1.367 + } 1.368 + 1.369 + /* Step 1. */ 1.370 + RootedObject obj(cx, &args.thisv().toObject()); 1.371 + 1.372 + /* Step 3. */ 1.373 + RootedValue nameVal(cx); 1.374 + if (!JSObject::getProperty(cx, obj, obj, cx->names().name, &nameVal)) 1.375 + return false; 1.376 + 1.377 + /* Step 4. */ 1.378 + RootedString name(cx); 1.379 + if (nameVal.isUndefined()) { 1.380 + name = cx->names().Error; 1.381 + } else { 1.382 + name = ToString<CanGC>(cx, nameVal); 1.383 + if (!name) 1.384 + return false; 1.385 + } 1.386 + 1.387 + /* Step 5. */ 1.388 + RootedValue msgVal(cx); 1.389 + if (!JSObject::getProperty(cx, obj, obj, cx->names().message, &msgVal)) 1.390 + return false; 1.391 + 1.392 + /* Step 6. */ 1.393 + RootedString message(cx); 1.394 + if (msgVal.isUndefined()) { 1.395 + message = cx->runtime()->emptyString; 1.396 + } else { 1.397 + message = ToString<CanGC>(cx, msgVal); 1.398 + if (!message) 1.399 + return false; 1.400 + } 1.401 + 1.402 + /* Step 7. */ 1.403 + if (name->empty() && message->empty()) { 1.404 + args.rval().setString(cx->names().Error); 1.405 + return true; 1.406 + } 1.407 + 1.408 + /* Step 8. */ 1.409 + if (name->empty()) { 1.410 + args.rval().setString(message); 1.411 + return true; 1.412 + } 1.413 + 1.414 + /* Step 9. */ 1.415 + if (message->empty()) { 1.416 + args.rval().setString(name); 1.417 + return true; 1.418 + } 1.419 + 1.420 + /* Step 10. */ 1.421 + StringBuffer sb(cx); 1.422 + if (!sb.append(name) || !sb.append(": ") || !sb.append(message)) 1.423 + return false; 1.424 + 1.425 + JSString *str = sb.finishString(); 1.426 + if (!str) 1.427 + return false; 1.428 + args.rval().setString(str); 1.429 + return true; 1.430 +} 1.431 + 1.432 +#if JS_HAS_TOSOURCE 1.433 +/* 1.434 + * Return a string that may eval to something similar to the original object. 1.435 + */ 1.436 +static bool 1.437 +exn_toSource(JSContext *cx, unsigned argc, Value *vp) 1.438 +{ 1.439 + JS_CHECK_RECURSION(cx, return false); 1.440 + CallArgs args = CallArgsFromVp(argc, vp); 1.441 + 1.442 + RootedObject obj(cx, ToObject(cx, args.thisv())); 1.443 + if (!obj) 1.444 + return false; 1.445 + 1.446 + RootedValue nameVal(cx); 1.447 + RootedString name(cx); 1.448 + if (!JSObject::getProperty(cx, obj, obj, cx->names().name, &nameVal) || 1.449 + !(name = ToString<CanGC>(cx, nameVal))) 1.450 + { 1.451 + return false; 1.452 + } 1.453 + 1.454 + RootedValue messageVal(cx); 1.455 + RootedString message(cx); 1.456 + if (!JSObject::getProperty(cx, obj, obj, cx->names().message, &messageVal) || 1.457 + !(message = ValueToSource(cx, messageVal))) 1.458 + { 1.459 + return false; 1.460 + } 1.461 + 1.462 + RootedValue filenameVal(cx); 1.463 + RootedString filename(cx); 1.464 + if (!JSObject::getProperty(cx, obj, obj, cx->names().fileName, &filenameVal) || 1.465 + !(filename = ValueToSource(cx, filenameVal))) 1.466 + { 1.467 + return false; 1.468 + } 1.469 + 1.470 + RootedValue linenoVal(cx); 1.471 + uint32_t lineno; 1.472 + if (!JSObject::getProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) || 1.473 + !ToUint32(cx, linenoVal, &lineno)) 1.474 + { 1.475 + return false; 1.476 + } 1.477 + 1.478 + StringBuffer sb(cx); 1.479 + if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) 1.480 + return false; 1.481 + 1.482 + if (!sb.append(message)) 1.483 + return false; 1.484 + 1.485 + if (!filename->empty()) { 1.486 + if (!sb.append(", ") || !sb.append(filename)) 1.487 + return false; 1.488 + } 1.489 + if (lineno != 0) { 1.490 + /* We have a line, but no filename, add empty string */ 1.491 + if (filename->empty() && !sb.append(", \"\"")) 1.492 + return false; 1.493 + 1.494 + JSString *linenumber = ToString<CanGC>(cx, linenoVal); 1.495 + if (!linenumber) 1.496 + return false; 1.497 + if (!sb.append(", ") || !sb.append(linenumber)) 1.498 + return false; 1.499 + } 1.500 + 1.501 + if (!sb.append("))")) 1.502 + return false; 1.503 + 1.504 + JSString *str = sb.finishString(); 1.505 + if (!str) 1.506 + return false; 1.507 + args.rval().setString(str); 1.508 + return true; 1.509 +} 1.510 +#endif 1.511 + 1.512 +static const JSFunctionSpec exception_methods[] = { 1.513 +#if JS_HAS_TOSOURCE 1.514 + JS_FN(js_toSource_str, exn_toSource, 0,0), 1.515 +#endif 1.516 + JS_FN(js_toString_str, exn_toString, 0,0), 1.517 + JS_FS_END 1.518 +}; 1.519 + 1.520 +/* JSProto_ ordering for exceptions shall match JSEXN_ constants. */ 1.521 +JS_STATIC_ASSERT(JSEXN_ERR == 0); 1.522 +JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR == JSProto_InternalError); 1.523 +JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR == JSProto_EvalError); 1.524 +JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR == JSProto_RangeError); 1.525 +JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError); 1.526 +JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError); 1.527 +JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError); 1.528 +JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError); 1.529 + 1.530 +/* static */ ErrorObject * 1.531 +ErrorObject::createProto(JSContext *cx, JS::Handle<GlobalObject*> global, JSExnType type, 1.532 + JS::HandleObject proto) 1.533 +{ 1.534 + RootedObject errorProto(cx); 1.535 + errorProto = global->createBlankPrototypeInheriting(cx, &ErrorObject::class_, *proto); 1.536 + if (!errorProto) 1.537 + return nullptr; 1.538 + 1.539 + Rooted<ErrorObject*> err(cx, &errorProto->as<ErrorObject>()); 1.540 + RootedString emptyStr(cx, cx->names().empty); 1.541 + if (!ErrorObject::init(cx, err, type, nullptr, emptyStr, emptyStr, 0, 0, emptyStr)) 1.542 + return nullptr; 1.543 + 1.544 + // The various prototypes also have .name in addition to the normal error 1.545 + // instance properties. 1.546 + JSProtoKey key = GetExceptionProtoKey(type); 1.547 + RootedPropertyName name(cx, ClassName(key, cx)); 1.548 + RootedValue nameValue(cx, StringValue(name)); 1.549 + if (!JSObject::defineProperty(cx, err, cx->names().name, nameValue, 1.550 + JS_PropertyStub, JS_StrictPropertyStub, 0)) 1.551 + { 1.552 + return nullptr; 1.553 + } 1.554 + 1.555 + // Create the corresponding constructor. 1.556 + RootedFunction ctor(cx, global->createConstructor(cx, Error, name, 1, 1.557 + JSFunction::ExtendedFinalizeKind)); 1.558 + if (!ctor) 1.559 + return nullptr; 1.560 + ctor->setExtendedSlot(0, Int32Value(int32_t(type))); 1.561 + 1.562 + if (!LinkConstructorAndPrototype(cx, ctor, err)) 1.563 + return nullptr; 1.564 + 1.565 + if (!GlobalObject::initBuiltinConstructor(cx, global, key, ctor, err)) 1.566 + return nullptr; 1.567 + 1.568 + return err; 1.569 +} 1.570 + 1.571 +JSObject * 1.572 +js_InitExceptionClasses(JSContext *cx, HandleObject obj) 1.573 +{ 1.574 + JS_ASSERT(obj->is<GlobalObject>()); 1.575 + JS_ASSERT(obj->isNative()); 1.576 + 1.577 + Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); 1.578 + 1.579 + RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); 1.580 + if (!objProto) 1.581 + return nullptr; 1.582 + 1.583 + /* Initialize the base Error class first. */ 1.584 + RootedObject errorProto(cx, ErrorObject::createProto(cx, global, JSEXN_ERR, objProto)); 1.585 + if (!errorProto) 1.586 + return nullptr; 1.587 + 1.588 + /* |Error.prototype| alone has method properties. */ 1.589 + if (!DefinePropertiesAndBrand(cx, errorProto, nullptr, exception_methods)) 1.590 + return nullptr; 1.591 + 1.592 + /* Define all remaining *Error constructors. */ 1.593 + for (int i = JSEXN_ERR + 1; i < JSEXN_LIMIT; i++) { 1.594 + if (!ErrorObject::createProto(cx, global, JSExnType(i), errorProto)) 1.595 + return nullptr; 1.596 + } 1.597 + 1.598 + return errorProto; 1.599 +} 1.600 + 1.601 +const JSErrorFormatString* 1.602 +js_GetLocalizedErrorMessage(ExclusiveContext *cx, void *userRef, const char *locale, 1.603 + const unsigned errorNumber) 1.604 +{ 1.605 + const JSErrorFormatString *errorString = nullptr; 1.606 + 1.607 + // The locale callbacks might not be thread safe, so don't call them if 1.608 + // we're not on the main thread. When used with XPConnect, 1.609 + // |localeGetErrorMessage| will be nullptr anyways. 1.610 + if (cx->isJSContext() && 1.611 + cx->asJSContext()->runtime()->localeCallbacks && 1.612 + cx->asJSContext()->runtime()->localeCallbacks->localeGetErrorMessage) 1.613 + { 1.614 + JSLocaleCallbacks *callbacks = cx->asJSContext()->runtime()->localeCallbacks; 1.615 + errorString = callbacks->localeGetErrorMessage(userRef, locale, errorNumber); 1.616 + } 1.617 + 1.618 + if (!errorString) 1.619 + errorString = js_GetErrorMessage(userRef, locale, errorNumber); 1.620 + return errorString; 1.621 +} 1.622 + 1.623 +JS_FRIEND_API(const jschar*) 1.624 +js::GetErrorTypeName(JSRuntime* rt, int16_t exnType) 1.625 +{ 1.626 + /* 1.627 + * JSEXN_INTERNALERR returns null to prevent that "InternalError: " 1.628 + * is prepended before "uncaught exception: " 1.629 + */ 1.630 + if (exnType <= JSEXN_NONE || exnType >= JSEXN_LIMIT || 1.631 + exnType == JSEXN_INTERNALERR) 1.632 + { 1.633 + return nullptr; 1.634 + } 1.635 + JSProtoKey key = GetExceptionProtoKey(JSExnType(exnType)); 1.636 + return ClassName(key, rt)->chars(); 1.637 +} 1.638 + 1.639 +bool 1.640 +js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, 1.641 + JSErrorCallback callback, void *userRef) 1.642 +{ 1.643 + // Tell our caller to report immediately if this report is just a warning. 1.644 + JS_ASSERT(reportp); 1.645 + if (JSREPORT_IS_WARNING(reportp->flags)) 1.646 + return false; 1.647 + 1.648 + // Find the exception index associated with this error. 1.649 + JSErrNum errorNumber = static_cast<JSErrNum>(reportp->errorNumber); 1.650 + const JSErrorFormatString *errorString; 1.651 + if (!callback || callback == js_GetErrorMessage) 1.652 + errorString = js_GetLocalizedErrorMessage(cx, nullptr, nullptr, errorNumber); 1.653 + else 1.654 + errorString = callback(userRef, nullptr, errorNumber); 1.655 + JSExnType exnType = errorString ? static_cast<JSExnType>(errorString->exnType) : JSEXN_NONE; 1.656 + MOZ_ASSERT(exnType < JSEXN_LIMIT); 1.657 + 1.658 + // Return false (no exception raised) if no exception is associated 1.659 + // with the given error number. 1.660 + if (exnType == JSEXN_NONE) 1.661 + return false; 1.662 + 1.663 + // Prevent infinite recursion. 1.664 + if (cx->generatingError) 1.665 + return false; 1.666 + AutoScopedAssign<bool> asa(&cx->generatingError, true); 1.667 + 1.668 + // Create an exception object. 1.669 + RootedString messageStr(cx, reportp->ucmessage ? JS_NewUCStringCopyZ(cx, reportp->ucmessage) 1.670 + : JS_NewStringCopyZ(cx, message)); 1.671 + if (!messageStr) 1.672 + return cx->isExceptionPending(); 1.673 + 1.674 + RootedString fileName(cx, JS_NewStringCopyZ(cx, reportp->filename)); 1.675 + if (!fileName) 1.676 + return cx->isExceptionPending(); 1.677 + 1.678 + uint32_t lineNumber = reportp->lineno; 1.679 + uint32_t columnNumber = reportp->column; 1.680 + 1.681 + RootedString stack(cx, ComputeStackString(cx)); 1.682 + if (!stack) 1.683 + return cx->isExceptionPending(); 1.684 + 1.685 + js::ScopedJSFreePtr<JSErrorReport> report(CopyErrorReport(cx, reportp)); 1.686 + if (!report) 1.687 + return cx->isExceptionPending(); 1.688 + 1.689 + RootedObject errObject(cx, ErrorObject::create(cx, exnType, stack, fileName, 1.690 + lineNumber, columnNumber, &report, messageStr)); 1.691 + if (!errObject) 1.692 + return cx->isExceptionPending(); 1.693 + 1.694 + // Throw it. 1.695 + RootedValue errValue(cx, ObjectValue(*errObject)); 1.696 + JS_SetPendingException(cx, errValue); 1.697 + 1.698 + // Flag the error report passed in to indicate an exception was raised. 1.699 + reportp->flags |= JSREPORT_EXCEPTION; 1.700 + return true; 1.701 +} 1.702 + 1.703 +static bool 1.704 +IsDuckTypedErrorObject(JSContext *cx, HandleObject exnObject, const char **filename_strp) 1.705 +{ 1.706 + bool found; 1.707 + if (!JS_HasProperty(cx, exnObject, js_message_str, &found) || !found) 1.708 + return false; 1.709 + 1.710 + const char *filename_str = *filename_strp; 1.711 + if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found) { 1.712 + /* DOMException duck quacks "filename" (all lowercase) */ 1.713 + filename_str = "filename"; 1.714 + if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found) 1.715 + return false; 1.716 + } 1.717 + 1.718 + if (!JS_HasProperty(cx, exnObject, js_lineNumber_str, &found) || !found) 1.719 + return false; 1.720 + 1.721 + *filename_strp = filename_str; 1.722 + return true; 1.723 +} 1.724 + 1.725 +JS_FRIEND_API(JSString *) 1.726 +js::ErrorReportToString(JSContext *cx, JSErrorReport *reportp) 1.727 +{ 1.728 + JSExnType type = static_cast<JSExnType>(reportp->exnType); 1.729 + RootedString str(cx, cx->runtime()->emptyString); 1.730 + if (type != JSEXN_NONE) 1.731 + str = ClassName(GetExceptionProtoKey(type), cx); 1.732 + RootedString toAppend(cx, JS_NewUCStringCopyN(cx, MOZ_UTF16(": "), 2)); 1.733 + if (!str || !toAppend) 1.734 + return nullptr; 1.735 + str = ConcatStrings<CanGC>(cx, str, toAppend); 1.736 + if (!str) 1.737 + return nullptr; 1.738 + toAppend = JS_NewUCStringCopyZ(cx, reportp->ucmessage); 1.739 + if (toAppend) 1.740 + str = ConcatStrings<CanGC>(cx, str, toAppend); 1.741 + return str; 1.742 +} 1.743 + 1.744 +bool 1.745 +js_ReportUncaughtException(JSContext *cx) 1.746 +{ 1.747 + if (!cx->isExceptionPending()) 1.748 + return true; 1.749 + 1.750 + RootedValue exn(cx); 1.751 + if (!cx->getPendingException(&exn)) 1.752 + return false; 1.753 + 1.754 + /* 1.755 + * Because ToString below could error and an exception object could become 1.756 + * unrooted, we must root exnObject. Later, if exnObject is non-null, we 1.757 + * need to root other intermediates, so allocate an operand stack segment 1.758 + * to protect all of these values. 1.759 + */ 1.760 + RootedObject exnObject(cx); 1.761 + if (JSVAL_IS_PRIMITIVE(exn)) { 1.762 + exnObject = nullptr; 1.763 + } else { 1.764 + exnObject = JSVAL_TO_OBJECT(exn); 1.765 + } 1.766 + 1.767 + JS_ClearPendingException(cx); 1.768 + JSErrorReport *reportp = exnObject ? js_ErrorFromException(cx, exnObject) 1.769 + : nullptr; 1.770 + 1.771 + // Be careful not to invoke ToString if we've already successfully extracted 1.772 + // an error report, since the exception might be wrapped in a security 1.773 + // wrapper, and ToString-ing it might throw. 1.774 + RootedString str(cx); 1.775 + if (reportp) 1.776 + str = ErrorReportToString(cx, reportp); 1.777 + else 1.778 + str = ToString<CanGC>(cx, exn); 1.779 + 1.780 + JSErrorReport report; 1.781 + 1.782 + // If js_ErrorFromException didn't get us a JSErrorReport, then the object 1.783 + // was not an ErrorObject, security-wrapped or otherwise. However, it might 1.784 + // still quack like one. Give duck-typing a chance. 1.785 + const char *filename_str = js_fileName_str; 1.786 + JSAutoByteString filename; 1.787 + if (!reportp && exnObject && IsDuckTypedErrorObject(cx, exnObject, &filename_str)) 1.788 + { 1.789 + // Temporary value for pulling properties off of duck-typed objects. 1.790 + RootedValue val(cx); 1.791 + 1.792 + RootedString name(cx); 1.793 + if (JS_GetProperty(cx, exnObject, js_name_str, &val) && val.isString()) 1.794 + name = val.toString(); 1.795 + 1.796 + RootedString msg(cx); 1.797 + if (JS_GetProperty(cx, exnObject, js_message_str, &val) && val.isString()) 1.798 + msg = val.toString(); 1.799 + 1.800 + // If we have the right fields, override the ToString we performed on 1.801 + // the exception object above with something built out of its quacks 1.802 + // (i.e. as much of |NameQuack: MessageQuack| as we can make). 1.803 + // 1.804 + // It would be nice to use ErrorReportToString here, but we can't quite 1.805 + // do it - mostly because we'd need to figure out what JSExnType |name| 1.806 + // corresponds to, which may not be any JSExnType at all. 1.807 + if (name && msg) { 1.808 + RootedString colon(cx, JS_NewStringCopyZ(cx, ": ")); 1.809 + if (!colon) 1.810 + return false; 1.811 + RootedString nameColon(cx, ConcatStrings<CanGC>(cx, name, colon)); 1.812 + if (!nameColon) 1.813 + return false; 1.814 + str = ConcatStrings<CanGC>(cx, nameColon, msg); 1.815 + if (!str) 1.816 + return false; 1.817 + } else if (name) { 1.818 + str = name; 1.819 + } else if (msg) { 1.820 + str = msg; 1.821 + } 1.822 + 1.823 + if (JS_GetProperty(cx, exnObject, filename_str, &val)) { 1.824 + JSString *tmp = ToString<CanGC>(cx, val); 1.825 + if (tmp) 1.826 + filename.encodeLatin1(cx, tmp); 1.827 + } 1.828 + 1.829 + uint32_t lineno; 1.830 + if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &val) || 1.831 + !ToUint32(cx, val, &lineno)) 1.832 + { 1.833 + lineno = 0; 1.834 + } 1.835 + 1.836 + uint32_t column; 1.837 + if (!JS_GetProperty(cx, exnObject, js_columnNumber_str, &val) || 1.838 + !ToUint32(cx, val, &column)) 1.839 + { 1.840 + column = 0; 1.841 + } 1.842 + 1.843 + reportp = &report; 1.844 + PodZero(&report); 1.845 + report.filename = filename.ptr(); 1.846 + report.lineno = (unsigned) lineno; 1.847 + report.exnType = int16_t(JSEXN_NONE); 1.848 + report.column = (unsigned) column; 1.849 + if (str) { 1.850 + // Note that using |str| for |ucmessage| here is kind of wrong, 1.851 + // because |str| is supposed to be of the format 1.852 + // |ErrorName: ErrorMessage|, and |ucmessage| is supposed to 1.853 + // correspond to |ErrorMessage|. But this is what we've historically 1.854 + // done for duck-typed error objects. 1.855 + // 1.856 + // If only this stuff could get specced one day... 1.857 + if (JSFlatString *flat = str->ensureFlat(cx)) 1.858 + report.ucmessage = flat->chars(); 1.859 + } 1.860 + } 1.861 + 1.862 + JSAutoByteString bytesStorage; 1.863 + const char *bytes = nullptr; 1.864 + if (str) 1.865 + bytes = bytesStorage.encodeLatin1(cx, str); 1.866 + if (!bytes) 1.867 + bytes = "unknown (can't convert to string)"; 1.868 + 1.869 + if (!reportp) { 1.870 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.871 + JSMSG_UNCAUGHT_EXCEPTION, bytes); 1.872 + } else { 1.873 + /* Flag the error as an exception. */ 1.874 + reportp->flags |= JSREPORT_EXCEPTION; 1.875 + 1.876 + /* Pass the exception object. */ 1.877 + JS_SetPendingException(cx, exn); 1.878 + CallErrorReporter(cx, bytes, reportp); 1.879 + } 1.880 + 1.881 + JS_ClearPendingException(cx); 1.882 + return true; 1.883 +} 1.884 + 1.885 +JSObject * 1.886 +js_CopyErrorObject(JSContext *cx, Handle<ErrorObject*> err, HandleObject scope) 1.887 +{ 1.888 + assertSameCompartment(cx, scope); 1.889 + 1.890 + js::ScopedJSFreePtr<JSErrorReport> copyReport; 1.891 + if (JSErrorReport *errorReport = err->getErrorReport()) { 1.892 + copyReport = CopyErrorReport(cx, errorReport); 1.893 + if (!copyReport) 1.894 + return nullptr; 1.895 + } 1.896 + 1.897 + RootedString message(cx, err->getMessage()); 1.898 + if (message && !cx->compartment()->wrap(cx, message.address())) 1.899 + return nullptr; 1.900 + RootedString fileName(cx, err->fileName(cx)); 1.901 + if (!cx->compartment()->wrap(cx, fileName.address())) 1.902 + return nullptr; 1.903 + RootedString stack(cx, err->stack(cx)); 1.904 + if (!cx->compartment()->wrap(cx, stack.address())) 1.905 + return nullptr; 1.906 + uint32_t lineNumber = err->lineNumber(); 1.907 + uint32_t columnNumber = err->columnNumber(); 1.908 + JSExnType errorType = err->type(); 1.909 + 1.910 + // Create the Error object. 1.911 + return ErrorObject::create(cx, errorType, stack, fileName, 1.912 + lineNumber, columnNumber, ©Report, message); 1.913 +} 1.914 + 1.915 +JS_PUBLIC_API(bool) 1.916 +JS::CreateTypeError(JSContext *cx, HandleString stack, HandleString fileName, 1.917 + uint32_t lineNumber, uint32_t columnNumber, JSErrorReport *report, 1.918 + HandleString message, MutableHandleValue rval) 1.919 +{ 1.920 + assertSameCompartment(cx, stack, fileName, message); 1.921 + js::ScopedJSFreePtr<JSErrorReport> rep; 1.922 + if (report) 1.923 + rep = CopyErrorReport(cx, report); 1.924 + 1.925 + RootedObject obj(cx, 1.926 + js::ErrorObject::create(cx, JSEXN_TYPEERR, stack, fileName, 1.927 + lineNumber, columnNumber, &rep, message)); 1.928 + if (!obj) 1.929 + return false; 1.930 + 1.931 + rval.setObject(*obj); 1.932 + return true; 1.933 +}