diff --git a/dom/base/ScriptSettings.cpp b/dom/base/ScriptSettings.cpp index d67f2167ae041..8d9b67f70e690 100644 --- a/dom/base/ScriptSettings.cpp +++ b/dom/base/ScriptSettings.cpp @@ -332,7 +332,8 @@ AutoJSAPI::~AutoJSAPI() } void -WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep); +WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, + JSErrorReport* aRep); void AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal, @@ -519,7 +520,7 @@ AutoJSAPI::Init(nsGlobalWindow* aWindow) // Eventually, SpiderMonkey will have a special-purpose callback for warnings // only. void -WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep) +WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep) { MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags)); if (!NS_IsMainThread()) { @@ -533,7 +534,7 @@ WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep) workers::WorkerPrivate* worker = workers::GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(worker); - worker->ReportError(aCx, JS::ConstUTF8CharsZ(), aRep); + worker->ReportError(aCx, aMessage, aRep); return; } @@ -545,7 +546,7 @@ WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep) // DOM Window. win = xpc::AddonWindowOrNull(JS::CurrentGlobalOrNull(aCx)); } - xpcReport->Init(aRep, nullptr, nsContentUtils::IsCallerChrome(), + xpcReport->Init(aRep, aMessage, nsContentUtils::IsCallerChrome(), win ? win->AsInner()->WindowID() : 0); xpcReport->LogToConsole(); } @@ -585,7 +586,7 @@ AutoJSAPI::ReportException() win = xpc::AddonWindowOrNull(errorGlobal); } nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr; - xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(), + xpcReport->Init(jsReport.report(), jsReport.message(), nsContentUtils::IsCallerChrome(), inner ? inner->WindowID() : 0); if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) { @@ -609,7 +610,7 @@ AutoJSAPI::ReportException() // to get hold of it. After we invoke ReportError, clear the exception on // cx(), just in case ReportError didn't. JS_SetPendingException(cx(), exn); - worker->ReportError(cx(), jsReport.toStringResult(), jsReport.report()); + worker->ReportError(cx(), jsReport.message(), jsReport.report()); ClearException(); } } else { diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index fc9ae8ea4dcdc..54575b9cf4ef7 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -1022,8 +1022,7 @@ Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise) bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(aPromise)) : GetCurrentThreadWorkerPrivate()->IsChromeWorker(); nsGlobalWindow* win = isMainThread ? xpc::WindowGlobalOrNull(aPromise) : nullptr; - xpcReport->Init(report.report(), report.toStringResult().c_str(), isChrome, - win ? win->AsInner()->WindowID() : 0); + xpcReport->Init(report.report(), report.message(), isChrome, win ? win->AsInner()->WindowID() : 0); // Now post an event to do the real reporting async NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport)); @@ -2645,8 +2644,7 @@ Promise::MaybeReportRejected() if (exp) { xpcReport->Init(cx, exp, isChrome, windowID); } else { - xpcReport->Init(report.report(), report.toStringResult(), - isChrome, windowID); + xpcReport->Init(report.report(), report.message(), isChrome, windowID); } // Now post an event to do the real reporting async diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 2327d505dec4d..ac4f709b258d1 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -2094,7 +2094,7 @@ ScriptExecutorRunnable::LogExceptionToConsole(JSContext* aCx, } RefPtr xpcReport = new xpc::ErrorReport(); - xpcReport->Init(report.report(), report.toStringResult().c_str(), + xpcReport->Init(report.report(), report.message(), aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID()); RefPtr r = new AsyncErrorReporter(xpcReport); diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 233cb5c4bcdd4..de6e415a3c8e6 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -426,7 +426,7 @@ ExtractErrorValues(JSContext* aCx, JS::Handle aValue, // this report anywhere. RefPtr report = new xpc::ErrorReport(); report->Init(err, - "", // toString result + "", // fallback message false, // chrome 0); // window ID diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 7a89b6fdc06aa..ac73004dd1a43 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -86,7 +86,6 @@ #include "nsProxyRelease.h" #include "nsQueryObject.h" #include "nsSandboxFlags.h" -#include "nsUTF8Utils.h" #include "prthread.h" #include "xpcpublic.h" @@ -5812,7 +5811,7 @@ WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus) } void -WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult, +WorkerPrivate::ReportError(JSContext* aCx, const char* aFallbackMessage, JSErrorReport* aReport) { AssertIsOnWorkerThread(); @@ -5856,19 +5855,13 @@ WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult, flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag; } - if (message.IsEmpty() && aToStringResult) { - nsDependentCString toStringResult(aToStringResult.c_str()); - if (!AppendUTF8toUTF16(toStringResult, message, mozilla::fallible)) { + if (message.IsEmpty()) { + nsDependentCString fallbackMessage(aFallbackMessage); + if (!AppendUTF8toUTF16(fallbackMessage, message, mozilla::fallible)) { // Try again, with only a 1 KB string. Do this infallibly this time. // If the user doesn't have 1 KB to spare we're done anyways. - uint32_t index = std::min(uint32_t(1024), toStringResult.Length()); - - // Drop the last code point that may be cropped. - index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index); - - nsDependentCString truncatedToStringResult(aToStringResult.c_str(), - index); - AppendUTF8toUTF16(truncatedToStringResult, message); + nsDependentCString truncatedFallbackMessage(aFallbackMessage, 1024); + AppendUTF8toUTF16(truncatedFallbackMessage, message); } } diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 5e57167ee04ad..385fd9f81677d 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -9,7 +9,6 @@ #include "Workers.h" -#include "js/CharacterEncoding.h" #include "nsIContentPolicy.h" #include "nsIContentSecurityPolicy.h" #include "nsILoadGroup.h" @@ -1165,8 +1164,7 @@ class WorkerPrivate : public WorkerPrivateParent NotifyInternal(JSContext* aCx, Status aStatus); void - ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult, - JSErrorReport* aReport); + ReportError(JSContext* aCx, const char* aMessage, JSErrorReport* aReport); static void ReportErrorToConsole(const char* aMessage); diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 4957405aed27b..1f48c5fa1c8ca 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -567,8 +567,8 @@ TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, uns void CompileError::throwError(JSContext* cx) { - if (JSREPORT_IS_WARNING(flags)) { - CallWarningReporter(cx, this); + if (JSREPORT_IS_WARNING(report.flags)) { + CallWarningReporter(cx, message, &report); return; } @@ -583,7 +583,17 @@ CompileError::throwError(JSContext* cx) // as the non-top-level "load", "eval", or "compile" native function // returns false, the top-level reporter will eventually receive the // uncaught exception report. - ErrorToException(cx, this, nullptr, nullptr); + ErrorToException(cx, message, &report, nullptr, nullptr); +} + +CompileError::~CompileError() +{ + js_free((void*)report.linebuf()); + js_free((void*)report.ucmessage); + js_free(message); + message = nullptr; + + PodZero(&report); } bool @@ -605,33 +615,33 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne return false; CompileError& err = *tempErrPtr; - err.flags = flags; - err.errorNumber = errorNumber; - err.filename = filename; - err.isMuted = mutedErrors; + err.report.flags = flags; + err.report.errorNumber = errorNumber; + err.report.filename = filename; + err.report.isMuted = mutedErrors; if (offset == NoOffset) { - err.lineno = 0; - err.column = 0; + err.report.lineno = 0; + err.report.column = 0; } else { - err.lineno = srcCoords.lineNum(offset); - err.column = srcCoords.columnIndex(offset); + err.report.lineno = srcCoords.lineNum(offset); + err.report.column = srcCoords.columnIndex(offset); } // If we have no location information, try to get one from the caller. bool callerFilename = false; - if (offset != NoOffset && !err.filename && cx->isJSContext()) { + if (offset != NoOffset && !err.report.filename && cx->isJSContext()) { NonBuiltinFrameIter iter(cx->asJSContext(), FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK, cx->compartment()->principals()); if (!iter.done() && iter.filename()) { callerFilename = true; - err.filename = iter.filename(); - err.lineno = iter.computeLine(&err.column); + err.report.filename = iter.filename(); + err.report.lineno = iter.computeLine(&err.report.column); } } - if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber, - nullptr, ArgumentsAreLatin1, &err, args)) + if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber, &err.message, + nullptr, ArgumentsAreLatin1, &err.report, args)) { return false; } @@ -644,7 +654,7 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne // So we don't even try, leaving report.linebuf and friends zeroed. This // means that any error involving a multi-line token (e.g. an unterminated // multi-line string literal) won't have a context printed. - if (offset != NoOffset && err.lineno == lineno && !callerFilename) { + if (offset != NoOffset && err.report.lineno == lineno && !callerFilename) { // We show only a portion (a "window") of the line around the erroneous // token -- the first char in the token, plus |windowRadius| chars // before it and |windowRadius - 1| chars after it. This is because @@ -682,7 +692,7 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne if (!linebuf) return false; - err.initOwnedLinebuf(linebuf.release(), windowLength, offset - windowStart); + err.report.initLinebuf(linebuf.release(), windowLength, offset - windowStart); } if (cx->isJSContext()) diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 118f254da3261..a36512c7e3321 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -237,9 +237,18 @@ struct Token } }; -class CompileError : public JSErrorReport { -public: +struct CompileError { + JSErrorReport report; + char* message; + CompileError() : message(nullptr) {} + ~CompileError(); void throwError(JSContext* cx); + + private: + // CompileError owns raw allocated memory, so disable assignment and copying + // for safety. + void operator=(const CompileError&) = delete; + CompileError(const CompileError&) = delete; }; // Ideally, tokenizing would be entirely independent of context. But the diff --git a/js/src/gdb/gdb-tests.cpp b/js/src/gdb/gdb-tests.cpp index 5523222b9511a..05e34ee30abae 100644 --- a/js/src/gdb/gdb-tests.cpp +++ b/js/src/gdb/gdb-tests.cpp @@ -43,12 +43,12 @@ checkBool(bool success) } /* The warning reporter callback. */ -void reportWarning(JSContext* cx, JSErrorReport* report) +void reportWarning(JSContext* cx, const char* message, JSErrorReport* report) { fprintf(stderr, "%s:%u: %s\n", report->filename ? report->filename : "", (unsigned int) report->lineno, - report->message().c_str()); + message); } // prologue.py sets a breakpoint on this function; test functions can call it diff --git a/js/src/jsapi-tests/testParseJSON.cpp b/js/src/jsapi-tests/testParseJSON.cpp index e82c6791143c9..15d9f024d47ea 100644 --- a/js/src/jsapi-tests/testParseJSON.cpp +++ b/js/src/jsapi-tests/testParseJSON.cpp @@ -307,7 +307,7 @@ Error(JSContext* cx, const char (&input)[N], uint32_t expectedLine, CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE); const char* lineAndColumnASCII = JS_smprintf("line %d column %d", expectedLine, expectedColumn); - CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII) != nullptr); + CHECK(strstr(report.message(), lineAndColumnASCII) != nullptr); js_free((void*)lineAndColumnASCII); /* We do not execute JS, so there should be no exception thrown. */ diff --git a/js/src/jsapi-tests/testUncaughtSymbol.cpp b/js/src/jsapi-tests/testUncaughtSymbol.cpp index a916b23d38350..7c3927f81eb22 100644 --- a/js/src/jsapi-tests/testUncaughtSymbol.cpp +++ b/js/src/jsapi-tests/testUncaughtSymbol.cpp @@ -41,11 +41,11 @@ GetSymbolExceptionType(JSContext* cx) js::ErrorReport report(cx); MOZ_RELEASE_ASSERT(report.init(cx, exn, js::ErrorReport::WithSideEffects)); - if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol(Symbol.iterator)") == 0) + if (strcmp(report.message(), "uncaught exception: Symbol(Symbol.iterator)") == 0) return SYMBOL_ITERATOR; - if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol(foo)") == 0) + if (strcmp(report.message(), "uncaught exception: Symbol(foo)") == 0) return SYMBOL_FOO; - if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol()") == 0) + if (strcmp(report.message(), "uncaught exception: Symbol()") == 0) return SYMBOL_EMPTY; MOZ_CRASH("Unexpected symbol"); } diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index 9955621ef1ab1..d734c744c539c 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -298,14 +298,14 @@ class JSAPITest cx = nullptr; } - static void reportWarning(JSContext* cx, JSErrorReport* report) { + static void reportWarning(JSContext* cx, const char* message, JSErrorReport* report) { MOZ_RELEASE_ASSERT(report); MOZ_RELEASE_ASSERT(JSREPORT_IS_WARNING(report->flags)); fprintf(stderr, "%s:%u:%s\n", report->filename ? report->filename : "", (unsigned int) report->lineno, - report->message().c_str()); + message); } virtual const JSClass * getGlobalClass() { diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index d9ebd83e69a4a..9c07b20b37d61 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -70,7 +70,6 @@ #include "js/SliceBudget.h" #include "js/StructuredClone.h" #include "js/UniquePtr.h" -#include "js/Utility.h" #include "vm/DateObject.h" #include "vm/Debugger.h" #include "vm/EnvironmentObject.h" @@ -6130,45 +6129,15 @@ JS_ErrorFromException(JSContext* cx, HandleObject obj) } void -JSErrorReport::initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, - size_t tokenOffsetArg) +JSErrorReport::initLinebuf(const char16_t* linebuf, size_t linebufLength, size_t tokenOffset) { - MOZ_ASSERT(linebufArg); - MOZ_ASSERT(tokenOffsetArg <= linebufLengthArg); - MOZ_ASSERT(linebufArg[linebufLengthArg] == '\0'); + MOZ_ASSERT(linebuf); + MOZ_ASSERT(tokenOffset <= linebufLength); + MOZ_ASSERT(linebuf[linebufLength] == '\0'); - linebuf_ = linebufArg; - linebufLength_ = linebufLengthArg; - tokenOffset_ = tokenOffsetArg; -} - -void -JSErrorReport::freeLinebuf() -{ - if (ownsLinebuf_ && linebuf_) { - js_free((void*)linebuf_); - ownsLinebuf_ = false; - } - linebuf_ = nullptr; -} - -JSString* -JSErrorReport::newMessageString(JSContext* cx) -{ - if (!message_) - return cx->runtime()->emptyString; - - return JS_NewStringCopyUTF8Z(cx, message_); -} - -void -JSErrorReport::freeMessage() -{ - if (ownsMessage_) { - js_free((void*)message_.get()); - ownsMessage_ = false; - } - message_ = JS::ConstUTF8CharsZ(); + linebuf_ = linebuf; + linebufLength_ = linebufLength; + tokenOffset_ = tokenOffset; } JS_PUBLIC_API(bool) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c6c46295514a9..5a84657edaf54 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5299,12 +5299,7 @@ JS_ReportAllocationOverflow(JSContext* cx); class JSErrorReport { - // The (default) error message. - // If ownsMessage_ is true, the it is freed in destructor. - JS::ConstUTF8CharsZ message_; - // Offending source line without final '\n'. - // If ownsLinebuf__ is true, the buffer is freed in destructor. const char16_t* linebuf_; // Number of chars in linebuf_. Does not include trailing '\0'. @@ -5316,30 +5311,20 @@ class JSErrorReport public: JSErrorReport() : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0), - filename(nullptr), lineno(0), column(0), - flags(0), errorNumber(0), - exnType(0), isMuted(false), - ownsLinebuf_(false), ownsMessage_(false) + filename(nullptr), lineno(0), column(0), isMuted(false), + flags(0), errorNumber(0), ucmessage(nullptr), + exnType(0) {} - ~JSErrorReport() { - freeLinebuf(); - freeMessage(); - } - const char* filename; /* source file name, URL, etc., or null */ unsigned lineno; /* source line number */ unsigned column; /* zero-based column index in line */ + bool isMuted; /* See the comment in ReadOnlyCompileOptions. */ unsigned flags; /* error/warning, etc. */ unsigned errorNumber; /* the error number, e.g. see js.msg */ + const char16_t* ucmessage; /* the (default) error message */ int16_t exnType; /* One of the JSExnType constants */ - bool isMuted : 1; /* See the comment in ReadOnlyCompileOptions. */ - - private: - bool ownsLinebuf_ : 1; - bool ownsMessage_ : 1; - public: const char16_t* linebuf() const { return linebuf_; } @@ -5349,29 +5334,7 @@ class JSErrorReport size_t tokenOffset() const { return tokenOffset_; } - void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg) { - initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg); - ownsLinebuf_ = true; - } - void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg); - void freeLinebuf(); - - const JS::ConstUTF8CharsZ message() const { - return message_; - } - - void initOwnedMessage(const char* messageArg) { - initBorrowedMessage(messageArg); - ownsMessage_ = true; - } - void initBorrowedMessage(const char* messageArg) { - MOZ_ASSERT(!message_); - message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg)); - } - - JSString* newMessageString(JSContext* cx); - - void freeMessage(); + void initLinebuf(const char16_t* linebuf, size_t linebufLength, size_t tokenOffset); }; /* @@ -5397,7 +5360,8 @@ class JSErrorReport namespace JS { -using WarningReporter = void (*)(JSContext* cx, JSErrorReport* report); +typedef void +(* WarningReporter)(JSContext* cx, const char* message, JSErrorReport* report); extern JS_PUBLIC_API(WarningReporter) SetWarningReporter(JSContext* cx, WarningReporter reporter); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index be5d51aa7f05a..782e8febf4e2d 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -159,8 +159,8 @@ AutoResolving::alreadyStartedSlow() const } static void -ReportError(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback, - void* userRef) +ReportError(JSContext* cx, const char* message, JSErrorReport* reportp, + JSErrorCallback callback, void* userRef) { /* * Check the error report, and set a JavaScript-catchable exception @@ -176,11 +176,11 @@ ReportError(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback, } if (JSREPORT_IS_WARNING(reportp->flags)) { - CallWarningReporter(cx, reportp); + CallWarningReporter(cx, message, reportp); return; } - ErrorToException(cx, reportp, callback, userRef); + ErrorToException(cx, message, reportp, callback, userRef); } /* @@ -321,36 +321,45 @@ bool js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format, ErrorArgumentsType argumentsType, va_list ap) { + char* message; + char16_t* ucmessage; + size_t messagelen; JSErrorReport report; + bool warning; if (checkReportFlags(cx, &flags)) return true; - UniqueChars message(JS_vsmprintf(format, ap)); + message = JS_vsmprintf(format, ap); if (!message) { ReportOutOfMemory(cx); return false; } + messagelen = strlen(message); - MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(message.get())); + MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(message)); report.flags = flags; report.errorNumber = JSMSG_USER_DEFINED_ERROR; - if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreUTF8) { - report.initOwnedMessage(message.release()); + if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreLatin1) { + ucmessage = InflateString(cx, message, &messagelen); } else { - MOZ_ASSERT(argumentsType == ArgumentsAreLatin1); - Latin1Chars latin1(message.get(), strlen(message.get())); - UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1)); - if (!utf8) - return false; - report.initOwnedMessage(reinterpret_cast(utf8.get())); + JS::UTF8Chars utf8(message, messagelen); + size_t unused; + ucmessage = LossyUTF8CharsToNewTwoByteCharsZ(cx, utf8, &unused).get(); } + if (!ucmessage) { + js_free(message); + return false; + } + report.ucmessage = ucmessage; PopulateReportBlame(cx, &report); - bool warning = JSREPORT_IS_WARNING(report.flags); + warning = JSREPORT_IS_WARNING(report.flags); - ReportError(cx, &report, nullptr, nullptr); + ReportError(cx, message, &report, nullptr, nullptr); + js_free(message); + js_free(ucmessage); return warning; } @@ -382,10 +391,14 @@ js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg) } bool -js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, - JSErrorReport* report, bool reportWarnings) +js::PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report, + bool reportWarnings) { - MOZ_ASSERT(report); + if (!report) { + fprintf(file, "%s\n", message); + fflush(file); + return false; + } /* Conditionally ignore reported warnings. */ if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings) @@ -407,8 +420,6 @@ js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, JS_free(cx, tmp); } - const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str(); - /* embedded newlines -- argh! */ const char* ctmp; while ((ctmp = strchr(message, '\n')) != 0) { @@ -461,34 +472,38 @@ js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, class MOZ_RAII AutoMessageArgs { + const char16_t** args_; size_t totalLength_; /* only {0} thru {9} supported */ - mozilla::Array args_; mozilla::Array lengths_; uint16_t count_; + bool passed_ : 1; bool allocatedElements_ : 1; public: AutoMessageArgs() - : totalLength_(0), count_(0), allocatedElements_(false) - { - PodArrayZero(args_); - } + : args_(nullptr), totalLength_(0), count_(0), + passed_(false), allocatedElements_(false) + {} ~AutoMessageArgs() { + if (passed_) + return; + + if (!args_) + return; + /* free the arguments only if we allocated them */ if (allocatedElements_) { uint16_t i = 0; - while (i < count_) { - if (args_[i]) - js_free((void*)args_[i]); - i++; - } + while (args_[i]) + js_free((void*)args_[i++]); } + js_free(args_); } - const char* args(size_t i) const { + const char16_t* args(size_t i) const { MOZ_ASSERT(i < count_); return args_[i]; } @@ -506,50 +521,56 @@ class MOZ_RAII AutoMessageArgs return count_; } - /* Gather the arguments into an array, and accumulate their sizes. */ + bool passed() const { + return passed_; + } + + /* + * Gather the arguments into an array, and accumulate their sizes. We + * allocate 1 more than necessary and null it out to act as the sentinel + * value when we free the pointers later. + */ bool init(ExclusiveContext* cx, const char16_t** argsArg, uint16_t countArg, ErrorArgumentsType typeArg, va_list ap) { + MOZ_ASSERT(!args_); MOZ_ASSERT(countArg > 0); + args_ = argsArg; count_ = countArg; - + passed_ = !!args_; + if (passed_) { + MOZ_ASSERT(!args_[count_]); + } else { + args_ = cx->pod_malloc(count_ + 1); + if (!args_) + return false; + args_[count_] = nullptr; + } for (uint16_t i = 0; i < count_; i++) { - switch (typeArg) { - case ArgumentsAreASCII: - case ArgumentsAreUTF8: { - MOZ_ASSERT(!argsArg); - args_[i] = va_arg(ap, char*); - MOZ_ASSERT_IF(typeArg == ArgumentsAreASCII, JS::StringIsASCII(args_[i])); - lengths_[i] = strlen(args_[i]); - break; - } - case ArgumentsAreLatin1: { - MOZ_ASSERT(!argsArg); - const Latin1Char* latin1 = va_arg(ap, Latin1Char*); - size_t len = strlen(reinterpret_cast(latin1)); - mozilla::Range range(latin1, len); - char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str(); - if (!utf8) - return false; + if (passed_) { + lengths_[i] = js_strlen(args_[i]); + } else if (typeArg == ArgumentsAreASCII || typeArg == ArgumentsAreLatin1) { + const char* charArg = va_arg(ap, char*); + size_t charArgLength = strlen(charArg); + + MOZ_ASSERT_IF(typeArg == ArgumentsAreASCII, JS::StringIsASCII(charArg)); - args_[i] = utf8; - lengths_[i] = strlen(utf8); + args_[i] = InflateString(cx, charArg, &charArgLength); + if (!args_[i]) + return false; allocatedElements_ = true; - break; - } - case ArgumentsAreUnicode: { - const char16_t* uc = argsArg ? argsArg[i] : va_arg(ap, char16_t*); - size_t len = js_strlen(uc); - mozilla::Range range(uc, len); - char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str(); - if (!utf8) + MOZ_ASSERT(charArgLength == js_strlen(args_[i])); + lengths_[i] = charArgLength; + } else if (typeArg == ArgumentsAreUTF8) { + const char* charArg = va_arg(ap, char*); + JS::UTF8Chars utf8(charArg, strlen(charArg)); + args_[i] = LossyUTF8CharsToNewTwoByteCharsZ(cx, utf8, &lengths_[i]).get(); + if (!args_[i]) return false; - - args_[i] = utf8; - lengths_[i] = strlen(utf8); allocatedElements_ = true; - break; - } + } else { + args_[i] = va_arg(ap, char16_t*); + lengths_[i] = js_strlen(args_[i]); } totalLength_ += lengths_[i]; } @@ -564,19 +585,21 @@ class MOZ_RAII AutoMessageArgs * The format string addressed by the error number may contain operands * identified by the format {N}, where N is a decimal digit. Each of these * is to be replaced by the Nth argument from the va_list. The complete - * message is placed into reportp->message_. + * message is placed into reportp->ucmessage converted to a JSString. * * Returns true if the expansion succeeds (can fail if out of memory). */ bool js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, void* userRef, const unsigned errorNumber, - const char16_t** messageArgs, + char** messagep, const char16_t** messageArgs, ErrorArgumentsType argumentsType, JSErrorReport* reportp, va_list ap) { const JSErrorFormatString* efs; + *messagep = nullptr; + if (!callback) callback = GetErrorMessage; @@ -598,8 +621,9 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, * for {X} in the format. */ if (efs->format) { - const char* fmt; - char* out; + char16_t* buffer; + char16_t* fmt; + char16_t* out; #ifdef DEBUG int expandedArgs = 0; #endif @@ -610,6 +634,9 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, if (!args.init(cx, messageArgs, argCount, argumentsType, ap)) return false; + buffer = fmt = InflateString(cx, efs->format, &len); + if (!buffer) + goto error; expandedLength = len - (3 * args.count()) /* exclude the {n} */ + args.totalLength(); @@ -618,17 +645,17 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, * Note - the above calculation assumes that each argument * is used once and only once in the expansion !!! */ - char* utf8 = out = cx->pod_malloc(expandedLength + 1); - if (!out) - return false; - - fmt = efs->format; + reportp->ucmessage = out = cx->pod_malloc(expandedLength + 1); + if (!out) { + js_free(buffer); + goto error; + } while (*fmt) { if (*fmt == '{') { if (isdigit(fmt[1])) { int d = JS7_UNDEC(fmt[1]); MOZ_RELEASE_ASSERT(d < args.count()); - strncpy(out, args.args(d), args.lengths(d)); + js_strncpy(out, args.args(d), args.lengths(d)); out += args.lengths(d); fmt += 3; #ifdef DEBUG @@ -641,8 +668,13 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, } MOZ_ASSERT(expandedArgs == args.count()); *out = 0; - - reportp->initOwnedMessage(utf8); + js_free(buffer); + size_t msgLen = PointerRangeSize(static_cast(reportp->ucmessage), + static_cast(out)); + mozilla::Range ucmsg(reportp->ucmessage, msgLen); + *messagep = JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str(); + if (!*messagep) + goto error; } } else { /* Non-null messageArgs should have at least one non-null arg. */ @@ -651,22 +683,40 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, * Zero arguments: the format string (if it exists) is the * entire message. */ - if (efs->format) - reportp->initBorrowedMessage(efs->format); + if (efs->format) { + size_t len; + *messagep = DuplicateString(cx, efs->format).release(); + if (!*messagep) + goto error; + len = strlen(*messagep); + reportp->ucmessage = InflateString(cx, *messagep, &len); + if (!reportp->ucmessage) + goto error; + } } } - if (!reportp->message()) { + if (*messagep == nullptr) { /* where's the right place for this ??? */ const char* defaultErrorMessage = "No error message available for error number %d"; size_t nbytes = strlen(defaultErrorMessage) + 16; - char* message = cx->pod_malloc(nbytes); - if (!message) - return false; - snprintf(message, nbytes, defaultErrorMessage, errorNumber); - reportp->initOwnedMessage(message); + *messagep = cx->pod_malloc(nbytes); + if (!*messagep) + goto error; + snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber); } return true; + +error: + if (reportp->ucmessage) { + js_free((void*)reportp->ucmessage); + reportp->ucmessage = nullptr; + } + if (*messagep) { + js_free((void*)*messagep); + *messagep = nullptr; + } + return false; } bool @@ -675,6 +725,7 @@ js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback, ErrorArgumentsType argumentsType, va_list ap) { JSErrorReport report; + char* message; bool warning; if (checkReportFlags(cx, &flags)) @@ -686,11 +737,14 @@ js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback, PopulateReportBlame(cx, &report); if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber, - nullptr, argumentsType, &report, ap)) { + &message, nullptr, argumentsType, &report, ap)) { return false; } - ReportError(cx, &report, callback, userRef); + ReportError(cx, message, &report, callback, userRef); + + js_free(message); + js_free((void*)report.ucmessage); return warning; } @@ -698,14 +752,14 @@ js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback, static bool ExpandErrorArguments(ExclusiveContext* cx, JSErrorCallback callback, void* userRef, const unsigned errorNumber, - const char16_t** messageArgs, + char** messagep, const char16_t** messageArgs, ErrorArgumentsType argumentsType, JSErrorReport* reportp, ...) { va_list ap; va_start(ap, reportp); bool expanded = js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber, - messageArgs, argumentsType, reportp, ap); + messagep, messageArgs, argumentsType, reportp, ap); va_end(ap); return expanded; } @@ -724,25 +778,30 @@ js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback call report.errorNumber = errorNumber; PopulateReportBlame(cx, &report); + char* message; if (!ExpandErrorArguments(cx, callback, userRef, errorNumber, - args, ArgumentsAreUnicode, &report)) + &message, args, ArgumentsAreUnicode, &report)) { return false; } - ReportError(cx, &report, callback, userRef); + ReportError(cx, message, &report, callback, userRef); + + js_free(message); + js_free((void*)report.ucmessage); return warning; } void -js::CallWarningReporter(JSContext* cx, JSErrorReport* reportp) +js::CallWarningReporter(JSContext* cx, const char* message, JSErrorReport* reportp) { + MOZ_ASSERT(message); MOZ_ASSERT(reportp); MOZ_ASSERT(JSREPORT_IS_WARNING(reportp->flags)); if (JS::WarningReporter warningReporter = cx->runtime()->warningReporter) - warningReporter(cx, reportp); + warningReporter(cx, message, reportp); } bool diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 54f2ce1e0d0ec..15d60d9f1c707 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -67,7 +67,7 @@ TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set); struct AutoResolving; -namespace frontend { class CompileError; } +namespace frontend { struct CompileError; } /* * Execution Context Overview: @@ -616,7 +616,7 @@ ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback extern bool ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback, void* userRef, const unsigned errorNumber, - const char16_t** messageArgs, + char** message, const char16_t** messageArgs, ErrorArgumentsType argumentsType, JSErrorReport* reportp, va_list ap); @@ -628,17 +628,17 @@ ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg); * Prints a full report and returns true if the given report is non-nullptr * and the report doesn't have the JSREPORT_WARNING flag set or reportWarnings * is true. - * Returns false otherwise. + * Returns false otherwise, printing just the message if the report is nullptr. */ extern bool -PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult, - JSErrorReport* report, bool reportWarnings); +PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report, + bool reportWarnings); /* * Send a JSErrorReport to the warningReporter callback. */ void -CallWarningReporter(JSContext* cx, JSErrorReport* report); +CallWarningReporter(JSContext* cx, const char* message, JSErrorReport* report); extern bool ReportIsNotDefined(JSContext* cx, HandlePropertyName name); diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 8089f631a0104..92a3dd4569771 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -28,7 +28,6 @@ #include "jswrapper.h" #include "gc/Marking.h" -#include "js/CharacterEncoding.h" #include "vm/ErrorObject.h" #include "vm/GlobalObject.h" #include "vm/SavedStacks.h" @@ -152,7 +151,7 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report) * We use a single malloc block to make a deep copy of JSErrorReport with * the following layout: * JSErrorReport - * char array with characters for message_ + * char16_t array with characters for ucmessage * char16_t array with characters for linebuf * char array with characters for filename * Such layout together with the properties enforced by the following @@ -167,15 +166,15 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report) size_t linebufSize = 0; if (report->linebuf()) linebufSize = (report->linebufLength() + 1) * sizeof(char16_t); - size_t messageSize = 0; - if (report->message()) - messageSize = strlen(report->message().c_str()) + 1; + size_t ucmessageSize = 0; + if (report->ucmessage) + ucmessageSize = JS_CHARS_SIZE(report->ucmessage); /* * The mallocSize can not overflow since it represents the sum of the * sizes of already allocated objects. */ - size_t mallocSize = sizeof(JSErrorReport) + messageSize + linebufSize + filenameSize; + size_t mallocSize = sizeof(JSErrorReport) + ucmessageSize + linebufSize + filenameSize; uint8_t* cursor = cx->pod_calloc(mallocSize); if (!cursor) return nullptr; @@ -183,17 +182,17 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report) JSErrorReport* copy = (JSErrorReport*)cursor; cursor += sizeof(JSErrorReport); - if (report->message()) { - copy->initBorrowedMessage((const char*)cursor); - js_memcpy(cursor, report->message().c_str(), messageSize); - cursor += messageSize; + if (report->ucmessage) { + copy->ucmessage = (const char16_t*)cursor; + js_memcpy(cursor, report->ucmessage, ucmessageSize); + cursor += ucmessageSize; } if (report->linebuf()) { const char16_t* linebufCopy = (const char16_t*)cursor; js_memcpy(cursor, report->linebuf(), linebufSize); cursor += linebufSize; - copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset()); + copy->initLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset()); } if (report->filename) { @@ -503,7 +502,7 @@ js::GetErrorTypeName(JSContext* cx, int16_t exnType) } void -js::ErrorToException(JSContext* cx, JSErrorReport* reportp, +js::ErrorToException(JSContext* cx, const char* message, JSErrorReport* reportp, JSErrorCallback callback, void* userRef) { MOZ_ASSERT(reportp); @@ -513,7 +512,7 @@ js::ErrorToException(JSContext* cx, JSErrorReport* reportp, // we cannot construct the Error constructor without self-hosted code. Just // print the error to stderr to help debugging. if (cx->runtime()->isSelfHostingCompartment(cx->compartment())) { - PrintError(cx, stderr, JS::ConstUTF8CharsZ(), reportp, true); + PrintError(cx, stderr, message, reportp, true); return; } @@ -537,7 +536,8 @@ js::ErrorToException(JSContext* cx, JSErrorReport* reportp, AutoScopedAssign asa(&cx->generatingError, true); // Create an exception object. - RootedString messageStr(cx, reportp->newMessageString(cx)); + RootedString messageStr(cx, reportp->ucmessage ? JS_NewUCStringCopyZ(cx, reportp->ucmessage) + : JS_NewStringCopyZ(cx, message)); if (!messageStr) return; @@ -612,7 +612,7 @@ ErrorReportToString(JSContext* cx, JSErrorReport* reportp) /* * If "str" is null at this point, that means we just want to use - * message without prefixing it with anything. + * reportp->ucmessage without prefixing it with anything. */ if (str) { RootedString separator(cx, JS_NewUCStringCopyN(cx, u": ", 2)); @@ -623,7 +623,7 @@ ErrorReportToString(JSContext* cx, JSErrorReport* reportp) return nullptr; } - RootedString message(cx, reportp->newMessageString(cx)); + RootedString message(cx, JS_NewUCStringCopyZ(cx, reportp->ucmessage)); if (!message) return nullptr; @@ -635,6 +635,8 @@ ErrorReportToString(JSContext* cx, JSErrorReport* reportp) ErrorReport::ErrorReport(JSContext* cx) : reportp(nullptr), + message_(nullptr), + ownedMessage(nullptr), str(cx), strChars(cx), exnObject(cx) @@ -643,6 +645,11 @@ ErrorReport::ErrorReport(JSContext* cx) ErrorReport::~ErrorReport() { + if (!ownedMessage) + return; + + js_free(ownedMessage); + js_free(const_cast(ownedReport.ucmessage)); } void @@ -831,47 +838,37 @@ ErrorReport::init(JSContext* cx, HandleValue exn, ownedReport.exnType = JSEXN_INTERNALERR; ownedReport.column = column; if (str) { - // Note that using |str| for |message_| here is kind of wrong, + // Note that using |str| for |ucmessage| here is kind of wrong, // because |str| is supposed to be of the format - // |ErrorName: ErrorMessage|, and |message_| is supposed to - // correspond to |ErrorMessage|. But this is what we've - // historically done for duck-typed error objects. + // |ErrorName: ErrorMessage|, and |ucmessage| is supposed to + // correspond to |ErrorMessage|. But this is what we've historically + // done for duck-typed error objects. // // If only this stuff could get specced one day... - char* utf8; - if (str->ensureFlat(cx) && - strChars.initTwoByte(cx, str) && - (utf8 = JS::CharsToNewUTF8CharsZ(cx, strChars.twoByteRange()).c_str())) - { - ownedReport.initOwnedMessage(utf8); - } else { - cx->clearPendingException(); - str = nullptr; - } + if (str->ensureFlat(cx) && strChars.initTwoByte(cx, str)) + ownedReport.ucmessage = strChars.twoByteChars(); } } - const char* utf8Message = nullptr; if (str) - utf8Message = toStringResultBytesStorage.encodeUtf8(cx, str); - if (!utf8Message) - utf8Message = "unknown (can't convert to string)"; + message_ = bytesStorage.encodeUtf8(cx, str); + if (!message_) + message_ = "unknown (can't convert to string)"; if (!reportp) { // This is basically an inlined version of // // JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - // JSMSG_UNCAUGHT_EXCEPTION, utf8Message); + // JSMSG_UNCAUGHT_EXCEPTION, message_); // // but without the reporting bits. Instead it just puts all // the stuff we care about in our ownedReport and message_. - if (!populateUncaughtExceptionReportUTF8(cx, utf8Message)) { + if (!populateUncaughtExceptionReportUTF8(cx, message_)) { // Just give up. We're out of memory or something; not much we can // do here. return false; } } else { - toStringResult_ = JS::ConstUTF8CharsZ(utf8Message, strlen(utf8Message)); /* Flag the error as an exception. */ reportp->flags |= JSREPORT_EXCEPTION; } @@ -910,13 +907,14 @@ ErrorReport::populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap) } if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, - JSMSG_UNCAUGHT_EXCEPTION, + JSMSG_UNCAUGHT_EXCEPTION, &ownedMessage, nullptr, ArgumentsAreUTF8, &ownedReport, ap)) { return false; } - toStringResult_ = ownedReport.message(); reportp = &ownedReport; + message_ = ownedMessage; + ownsMessageAndReport = true; return true; } diff --git a/js/src/jsexn.h b/js/src/jsexn.h index a63c70909ef28..0bae18f4c6bb6 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -39,7 +39,7 @@ ComputeStackString(JSContext* cx); * the rug. */ extern void -ErrorToException(JSContext* cx, JSErrorReport* reportp, +ErrorToException(JSContext* cx, const char* message, JSErrorReport* reportp, JSErrorCallback callback, void* userRef); extern JSErrorReport* diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 931e067ec4aae..b1e8ec25e3ebb 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1452,9 +1452,9 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport) return reportp; } - const JS::ConstUTF8CharsZ toStringResult() + const char* message() { - return toStringResult_; + return message_; } private: @@ -1473,9 +1473,16 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport) // We may have a provided JSErrorReport, so need a way to represent that. JSErrorReport* reportp; + // And we may have a message. + const char* message_; + // Or we may need to synthesize a JSErrorReport one of our own. JSErrorReport ownedReport; + // Or a message of our own. If this is non-null, we need to clean up both + // it and ownedReport. + char* ownedMessage; + // And we have a string to maybe keep alive that has pointers into // it from ownedReport. JS::RootedString str; @@ -1486,14 +1493,14 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(ErrorReport) // And we need to root our exception value. JS::RootedObject exnObject; + // And possibly some byte storage for our message_. + JSAutoByteString bytesStorage; + // And for our filename. JSAutoByteString filename; - // We may have a result of error.toString(). - // FIXME: We should not call error.toString(), since it could have side - // effect (see bug 633623). - JS::ConstUTF8CharsZ toStringResult_; - JSAutoByteString toStringResultBytesStorage; + // True if we need to free message_ and the stuff in ownedReport + bool ownsMessageAndReport; }; /* Implemented in vm/StructuredClone.cpp. */ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 6d695cacbd99c..b53af7be407e8 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -6314,7 +6314,7 @@ CreateLastWarningObject(JSContext* cx, JSErrorReport* report) if (!DefineProperty(cx, warningObj, cx->names().name, nameVal)) return false; - RootedString messageStr(cx, report->newMessageString(cx)); + RootedString messageStr(cx, JS_NewUCStringCopyZ(cx, report->ucmessage)); if (!messageStr) return false; RootedValue messageVal(cx, StringValue(messageStr)); @@ -6394,8 +6394,7 @@ js::shell::AutoReportException::~AutoReportException() ShellContext* sc = GetShellContext(cx); js::ErrorReport report(cx); if (!report.init(cx, exn, js::ErrorReport::WithSideEffects)) { - fprintf(stderr, "out of memory initializing ErrorReport\n"); - fflush(stderr); + PrintError(cx, stderr, "out of memory initializing ErrorReport", nullptr, reportWarnings); JS_ClearPendingException(cx); return; } @@ -6403,7 +6402,7 @@ js::shell::AutoReportException::~AutoReportException() MOZ_ASSERT(!JSREPORT_IS_WARNING(report.report()->flags)); FILE* fp = ErrorFilePointer(); - PrintError(cx, fp, report.toStringResult(), report.report(), reportWarnings); + PrintError(cx, fp, report.message(), report.report(), reportWarnings); { JS::AutoSaveExceptionState savedExc(cx); @@ -6421,7 +6420,7 @@ js::shell::AutoReportException::~AutoReportException() } void -js::shell::WarningReporter(JSContext* cx, JSErrorReport* report) +js::shell::WarningReporter(JSContext* cx, const char* message, JSErrorReport* report) { ShellContext* sc = GetShellContext(cx); FILE* fp = ErrorFilePointer(); @@ -6439,7 +6438,7 @@ js::shell::WarningReporter(JSContext* cx, JSErrorReport* report) } // Print the warning. - PrintError(cx, fp, JS::ConstUTF8CharsZ(), report, reportWarnings); + PrintError(cx, fp, message, report, reportWarnings); } static bool diff --git a/js/src/shell/jsshell.h b/js/src/shell/jsshell.h index a3ecbef999954..a9012122a4571 100644 --- a/js/src/shell/jsshell.h +++ b/js/src/shell/jsshell.h @@ -24,7 +24,7 @@ const JSErrorFormatString* my_GetErrorMessage(void* userRef, const unsigned errorNumber); void -WarningReporter(JSContext* cx, JSErrorReport* report); +WarningReporter(JSContext* cx, const char* message, JSErrorReport* report); class MOZ_STACK_CLASS AutoReportException { diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp index 47b61b57b0777..fd6ad407a7fe1 100644 --- a/js/src/vm/ErrorObject.cpp +++ b/js/src/vm/ErrorObject.cpp @@ -7,14 +7,10 @@ #include "vm/ErrorObject-inl.h" -#include "mozilla/Range.h" - #include "jsexn.h" #include "js/CallArgs.h" -#include "js/CharacterEncoding.h" #include "vm/GlobalObject.h" -#include "vm/String.h" #include "jsobjinlines.h" @@ -149,11 +145,10 @@ js::ErrorObject::getOrCreateErrorReport(JSContext* cx) message = cx->runtime()->emptyString; if (!message->ensureFlat(cx)) return nullptr; - - UniquePtr utf8 = StringToNewUTF8CharsZ(cx, *message); - if (!utf8) + AutoStableStringChars chars(cx); + if (!chars.initTwoByte(cx, message)) return nullptr; - report.initOwnedMessage(utf8.release()); + report.ucmessage = chars.twoByteRange().start().get(); // Cache and return. JSErrorReport* copy = CopyErrorReport(cx, &report); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 2d25ce7bb7911..87aeedc050956 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -37,7 +37,6 @@ #include "gc/Policy.h" #include "jit/AtomicOperations.h" #include "jit/InlinableNatives.h" -#include "js/CharacterEncoding.h" #include "js/Date.h" #include "vm/Compression.h" #include "vm/GeneratorObject.h" @@ -67,12 +66,12 @@ using mozilla::PodMove; using mozilla::Maybe; static void -selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report) +selfHosting_WarningReporter(JSContext* cx, const char* message, JSErrorReport* report) { MOZ_ASSERT(report); MOZ_ASSERT(JSREPORT_IS_WARNING(report->flags)); - PrintError(cx, stderr, JS::ConstUTF8CharsZ(), report, true); + PrintError(cx, stderr, message, report, true); } static bool @@ -2683,7 +2682,7 @@ MaybePrintAndClearPendingException(JSContext* cx, FILE* file) } MOZ_ASSERT(!JSREPORT_IS_WARNING(report.report()->flags)); - PrintError(cx, file, report.toStringResult(), report.report(), true); + PrintError(cx, file, report.message(), report.report(), true); } class MOZ_STACK_CLASS AutoSelfHostingErrorReporter diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 7e549337a10d6..ca38fa9073368 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2343,11 +2343,11 @@ nsXPCComponents_Utils::ReportError(HandleValue error, JSContext* cx) uint32_t column = err->tokenOffset(); + const char16_t* ucmessage = err->ucmessage; const char16_t* linebuf = err->linebuf(); nsresult rv = scripterr->InitWithWindowID( - err->message() ? NS_ConvertUTF8toUTF16(err->message().c_str()) - : EmptyString(), + ucmessage ? nsDependentString(ucmessage) : EmptyString(), fileUni, linebuf ? nsDependentString(linebuf, err->linebufLength()) : EmptyString(), err->lineno, diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index 0719ec819e9d6..9c63a14722b8c 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -1047,56 +1047,6 @@ class MOZ_STACK_CLASS AutoExceptionRestorer RootedValue tvr; }; -static nsresult -JSErrorToXPCException(const char* toStringResult, - const char* ifaceName, - const char* methodName, - const JSErrorReport* report, - nsIException** exceptn) -{ - AutoJSContext cx; - nsresult rv = NS_ERROR_FAILURE; - RefPtr data; - if (report) { - nsAutoString bestMessage; - if (report && report->message()) { - CopyUTF8toUTF16(report->message().c_str(), bestMessage); - } else if (toStringResult) { - CopyUTF8toUTF16(toStringResult, bestMessage); - } else { - bestMessage.AssignLiteral("JavaScript Error"); - } - - const char16_t* linebuf = report->linebuf(); - - data = new nsScriptError(); - data->InitWithWindowID( - bestMessage, - NS_ConvertASCIItoUTF16(report->filename), - linebuf ? nsDependentString(linebuf, report->linebufLength()) : EmptyString(), - report->lineno, - report->tokenOffset(), report->flags, - NS_LITERAL_CSTRING("XPConnect JavaScript"), - nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx)); - } - - if (data) { - nsAutoCString formattedMsg; - data->ToString(formattedMsg); - - rv = XPCConvert::ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS, - formattedMsg.get(), ifaceName, - methodName, - static_cast(data.get()), - exceptn, nullptr, nullptr); - } else { - rv = XPCConvert::ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR, - nullptr, ifaceName, methodName, - nullptr, exceptn, nullptr, nullptr); - } - return rv; -} - // static nsresult XPCConvert::JSValToXPCException(MutableHandleValue s, @@ -1140,11 +1090,11 @@ XPCConvert::JSValToXPCException(MutableHandleValue s, // extract the report and build an xpcexception from that const JSErrorReport* report; if (nullptr != (report = JS_ErrorFromException(cx, obj))) { - JSAutoByteString toStringResult; - RootedString str(cx, ToString(cx, s)); - if (str) - toStringResult.encodeUtf8(cx, str); - return JSErrorToXPCException(toStringResult.ptr(), ifaceName, + JSAutoByteString message; + JSString* str; + if (nullptr != (str = ToString(cx, s))) + message.encodeLatin1(cx, str); + return JSErrorToXPCException(message.ptr(), ifaceName, methodName, report, exceptn); } @@ -1237,6 +1187,58 @@ XPCConvert::JSValToXPCException(MutableHandleValue s, return NS_ERROR_FAILURE; } +/********************************/ + +// static +nsresult +XPCConvert::JSErrorToXPCException(const char* message, + const char* ifaceName, + const char* methodName, + const JSErrorReport* report, + nsIException** exceptn) +{ + AutoJSContext cx; + nsresult rv = NS_ERROR_FAILURE; + RefPtr data; + if (report) { + nsAutoString bestMessage; + if (report && report->ucmessage) { + bestMessage = static_cast(report->ucmessage); + } else if (message) { + CopyASCIItoUTF16(message, bestMessage); + } else { + bestMessage.AssignLiteral("JavaScript Error"); + } + + const char16_t* linebuf = report->linebuf(); + + data = new nsScriptError(); + data->InitWithWindowID( + bestMessage, + NS_ConvertASCIItoUTF16(report->filename), + linebuf ? nsDependentString(linebuf, report->linebufLength()) : EmptyString(), + report->lineno, + report->tokenOffset(), report->flags, + NS_LITERAL_CSTRING("XPConnect JavaScript"), + nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx)); + } + + if (data) { + nsAutoCString formattedMsg; + data->ToString(formattedMsg); + + rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS, + formattedMsg.get(), ifaceName, methodName, + static_cast(data.get()), + exceptn, nullptr, nullptr); + } else { + rv = ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR, + nullptr, ifaceName, methodName, nullptr, + exceptn, nullptr, nullptr); + } + return rv; +} + /***************************************************************************/ // array fun... diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 53cf649bc0ecb..5a49e0fe0210a 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -169,7 +169,7 @@ nsXPConnect::IsISupportsDescendant(nsIInterfaceInfo* info) } void -xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult, +xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aFallbackMessage, bool aIsChrome, uint64_t aWindowID) { mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript") @@ -177,8 +177,8 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult, mWindowID = aWindowID; ErrorReportToMessageString(aReport, mErrorMsg); - if (mErrorMsg.IsEmpty() && aToStringResult) { - AppendUTF8toUTF16(aToStringResult, mErrorMsg); + if (mErrorMsg.IsEmpty() && aFallbackMessage) { + mErrorMsg.AssignWithConversion(aFallbackMessage); } if (!aReport->filename) { @@ -290,13 +290,14 @@ xpc::ErrorReport::ErrorReportToMessageString(JSErrorReport* aReport, nsAString& aString) { aString.Truncate(); - if (aReport->message()) { + const char16_t* m = aReport->ucmessage; + if (m) { JSFlatString* name = js::GetErrorTypeName(CycleCollectedJSContext::Get()->Context(), aReport->exnType); if (name) { AssignJSFlatString(aString, name); aString.AppendLiteral(": "); } - aString.Append(NS_ConvertUTF8toUTF16(aReport->message().c_str())); + aString.Append(m); } } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index a0197d3c8a5b9..8d2b747546b86 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2376,6 +2376,12 @@ class XPCConvert const char* methodName, nsIException** exception); + static nsresult JSErrorToXPCException(const char* message, + const char* ifaceName, + const char* methodName, + const JSErrorReport* report, + nsIException** exception); + static nsresult ConstructException(nsresult rv, const char* message, const char* ifaceName, const char* methodName, diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 78ebfea7c69f0..06ffd27413d68 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -516,7 +516,7 @@ class ErrorReport { , mIsMuted(false) {} - void Init(JSErrorReport* aReport, const char* aToStringResult, + void Init(JSErrorReport* aReport, const char* aFallbackMessage, bool aIsChrome, uint64_t aWindowID); void Init(JSContext* aCx, mozilla::dom::Exception* aException, bool aIsChrome, uint64_t aWindowID); diff --git a/netwerk/base/ProxyAutoConfig.cpp b/netwerk/base/ProxyAutoConfig.cpp index 61c327c0269bb..f16217f9d2665 100644 --- a/netwerk/base/ProxyAutoConfig.cpp +++ b/netwerk/base/ProxyAutoConfig.cpp @@ -327,8 +327,7 @@ PACLogErrorOrWarning(const nsAString& aKind, JSErrorReport* aReport) nsString formattedMessage(NS_LITERAL_STRING("PAC Execution ")); formattedMessage += aKind; formattedMessage += NS_LITERAL_STRING(": "); - if (aReport->message()) - formattedMessage.Append(NS_ConvertUTF8toUTF16(aReport->message().c_str())); + formattedMessage += aReport->ucmessage; formattedMessage += NS_LITERAL_STRING(" ["); formattedMessage.Append(aReport->linebuf(), aReport->linebufLength()); formattedMessage += NS_LITERAL_STRING("]"); @@ -336,7 +335,7 @@ PACLogErrorOrWarning(const nsAString& aKind, JSErrorReport* aReport) } static void -PACWarningReporter(JSContext* aCx, JSErrorReport* aReport) +PACWarningReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aReport) { MOZ_ASSERT(aReport); MOZ_ASSERT(JSREPORT_IS_WARNING(aReport->flags)); diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index a4b777bf87776..93b0dc3d839f5 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -486,7 +486,7 @@ CycleCollectedJSContext::~CycleCollectedJSContext() } static void -MozCrashWarningReporter(JSContext*, JSErrorReport*) +MozCrashWarningReporter(JSContext*, const char*, JSErrorReport*) { MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?"); } diff --git a/xpcom/string/nsUTF8Utils.h b/xpcom/string/nsUTF8Utils.h index 9f38fa555551e..0c72876fe5021 100644 --- a/xpcom/string/nsUTF8Utils.h +++ b/xpcom/string/nsUTF8Utils.h @@ -13,7 +13,6 @@ #include "nscore.h" #include "mozilla/Assertions.h" #include "mozilla/SSE.h" -#include "mozilla/TypeTraits.h" #include "nsCharTraits.h" @@ -723,20 +722,4 @@ class LossyConvertEncoding16to8 }; #endif // MOZILLA_INTERNAL_API - -template -inline UnsignedT -RewindToPriorUTF8Codepoint(const Char* utf8Chars, UnsignedT index) -{ - static_assert(mozilla::IsSame::value || - mozilla::IsSame::value || - mozilla::IsSame::value, - "UTF-8 data must be in 8-bit units"); - static_assert(mozilla::IsUnsigned::value, "index type must be unsigned"); - while (index > 0 && (utf8Chars[index] & 0xC0) == 0x80) - --index; - - return index; -} - #endif /* !defined(nsUTF8Utils_h_) */