summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/src/jsapi.cpp131
-rw-r--r--js/src/jsapi.h174
-rw-r--r--js/src/jscntxt.cpp41
-rw-r--r--js/src/jscntxt.h7
-rw-r--r--js/src/jsexn.cpp114
-rw-r--r--js/src/jsexn.h3
6 files changed, 401 insertions, 69 deletions
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 6d6eacec24..8cb3e58029 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -69,7 +69,6 @@
#include "js/Proxy.h"
#include "js/SliceBudget.h"
#include "js/StructuredClone.h"
-#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "vm/AsyncFunction.h"
#include "vm/DateObject.h"
@@ -6300,7 +6299,7 @@ JSErrorReport::freeLinebuf()
}
JSString*
-JSErrorReport::newMessageString(JSContext* cx)
+JSErrorBase::newMessageString(JSContext* cx)
{
if (!message_)
return cx->runtime()->emptyString;
@@ -6309,7 +6308,7 @@ JSErrorReport::newMessageString(JSContext* cx)
}
void
-JSErrorReport::freeMessage()
+JSErrorBase::freeMessage()
{
if (ownsMessage_) {
js_free((void*)message_.get());
@@ -6318,6 +6317,132 @@ JSErrorReport::freeMessage()
message_ = JS::ConstUTF8CharsZ();
}
+JSErrorNotes::JSErrorNotes()
+ : notes_()
+{}
+
+JSErrorNotes::~JSErrorNotes()
+{
+}
+
+static UniquePtr<JSErrorNotes::Note>
+CreateErrorNoteVA(JSContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber,
+ ErrorArgumentsType argumentsType, va_list ap)
+{
+ auto note = MakeUnique<JSErrorNotes::Note>();
+ if (!note)
+ return nullptr;
+
+ note->errorNumber = errorNumber;
+ note->filename = filename;
+ note->lineno = lineno;
+ note->column = column;
+
+ if (!ExpandErrorArgumentsVA(cx, errorCallback, userRef, errorNumber,
+ nullptr, argumentsType, note.get(), ap)) {
+ return nullptr;
+ }
+
+ return note;
+}
+
+bool
+JSErrorNotes::addNoteASCII(JSContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, errorNumber);
+ auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef,
+ errorNumber, ArgumentsAreASCII, ap);
+ va_end(ap);
+
+ if (!note)
+ return false;
+ if (!notes_.append(Move(note)))
+ return false;
+ return true;
+}
+
+bool
+JSErrorNotes::addNoteLatin1(JSContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, errorNumber);
+ auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef,
+ errorNumber, ArgumentsAreLatin1, ap);
+ va_end(ap);
+
+ if (!note)
+ return false;
+ if (!notes_.append(Move(note)))
+ return false;
+ return true;
+}
+
+bool
+JSErrorNotes::addNoteUTF8(JSContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...)
+{
+ va_list ap;
+ va_start(ap, errorNumber);
+ auto note = CreateErrorNoteVA(cx, filename, lineno, column, errorCallback, userRef,
+ errorNumber, ArgumentsAreUTF8, ap);
+ va_end(ap);
+
+ if (!note)
+ return false;
+ if (!notes_.append(Move(note)))
+ return false;
+ return true;
+}
+
+size_t
+JSErrorNotes::length()
+{
+ return notes_.length();
+}
+
+UniquePtr<JSErrorNotes>
+JSErrorNotes::copy(JSContext* cx)
+{
+ auto copiedNotes = MakeUnique<JSErrorNotes>();
+ if (!copiedNotes)
+ return nullptr;
+
+ for (auto&& note : *this) {
+ js::UniquePtr<JSErrorNotes::Note> copied(CopyErrorNote(cx, note.get()));
+ if (!copied)
+ return nullptr;
+
+ if (!copiedNotes->notes_.append(Move(copied)))
+ return nullptr;
+ }
+
+ return copiedNotes;
+}
+
+JSErrorNotes::iterator
+JSErrorNotes::begin()
+{
+ return iterator(notes_.begin());
+}
+
+JSErrorNotes::iterator
+JSErrorNotes::end()
+{
+ return iterator(notes_.end());
+}
+
JS_PUBLIC_API(bool)
JS_ThrowStopIteration(JSContext* cx)
{
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 3f65dad346..99100620af 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -18,6 +18,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/Variant.h"
+#include <iterator>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
@@ -36,6 +37,7 @@
#include "js/Realm.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
+#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "js/Value.h"
#include "js/Vector.h"
@@ -5362,14 +5364,130 @@ JS_ReportOutOfMemory(JSContext* cx);
extern JS_PUBLIC_API(void)
JS_ReportAllocationOverflow(JSContext* cx);
-class JSErrorReport
+/**
+ * Base class that implements parts shared by JSErrorReport and
+ * JSErrorNotes::Note.
+ */
+class JSErrorBase
{
// The (default) error message.
// If ownsMessage_ is true, the it is freed in destructor.
JS::ConstUTF8CharsZ message_;
+ public:
+ JSErrorBase()
+ : filename(nullptr), lineno(0), column(0),
+ errorNumber(0),
+ ownsMessage_(false)
+ {}
+
+ ~JSErrorBase() {
+ freeMessage();
+ }
+
+ // Source file name, URL, etc., or null.
+ const char* filename;
+
+ // Source line number.
+ unsigned lineno;
+
+ // Zero-based column index in line.
+ unsigned column;
+
+ // the error number, e.g. see js.msg.
+ unsigned errorNumber;
+
+ private:
+ bool ownsMessage_ : 1;
+
+ public:
+ 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);
+
+ private:
+ void freeMessage();
+};
+
+/**
+ * Notes associated with JSErrorReport.
+ */
+class JSErrorNotes
+{
+ public:
+ class Note : public JSErrorBase
+ {};
+
+ private:
+ // Stores pointers to each note.
+ js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
+
+ public:
+ JSErrorNotes();
+ ~JSErrorNotes();
+
+ // Add an note to the given position.
+ bool addNoteASCII(JSContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteLatin1(JSContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+ bool addNoteUTF8(JSContext* cx,
+ const char* filename, unsigned lineno, unsigned column,
+ JSErrorCallback errorCallback, void* userRef,
+ const unsigned errorNumber, ...);
+
+ size_t length();
+
+ // Create a deep copy of notes.
+ js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
+
+ class iterator : public std::iterator<std::input_iterator_tag, js::UniquePtr<Note>>
+ {
+ js::UniquePtr<Note>* note_;
+ public:
+ explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note)
+ {}
+
+ bool operator==(iterator other) const {
+ return note_ == other.note_;
+ }
+ bool operator!=(iterator other) const {
+ return !(*this == other);
+ }
+ iterator& operator++() {
+ note_++;
+ return *this;
+ }
+ reference operator*() {
+ return *note_;
+ }
+ };
+ iterator begin();
+ iterator end();
+};
+
+/**
+ * Describes a single error or warning that occurs in the execution of script.
+ */
+class JSErrorReport : public JSErrorBase
+{
// Offending source line without final '\n'.
- // If ownsLinebuf__ is true, the buffer is freed in destructor.
+ // If ownsLinebuf_ is true, the buffer is freed in destructor.
const char16_t* linebuf_;
// Number of chars in linebuf_. Does not include trailing '\0'.
@@ -5381,28 +5499,29 @@ 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)
+ notes(nullptr),
+ flags(0), exnType(0), isMuted(false),
+ ownsLinebuf_(false)
{}
~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 */
- unsigned flags; /* error/warning, etc. */
- unsigned errorNumber; /* the error number, e.g. see js.msg */
- int16_t exnType; /* One of the JSExnType constants */
- bool isMuted : 1; /* See the comment in ReadOnlyCompileOptions. */
+ // Associated notes, or nullptr if there's no note.
+ js::UniquePtr<JSErrorNotes> notes;
+
+ // error/warning, etc.
+ unsigned flags;
+
+ // One of the JSExnType constants.
+ int16_t exnType;
+
+ // See the comment in ReadOnlyCompileOptions.
+ bool isMuted : 1;
private:
bool ownsLinebuf_ : 1;
- bool ownsMessage_ : 1;
public:
const char16_t* linebuf() const {
@@ -5414,29 +5533,16 @@ class JSErrorReport
size_t tokenOffset() const {
return tokenOffset_;
}
- void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg, size_t tokenOffsetArg) {
+ 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));
- }
+ void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
+ size_t tokenOffsetArg);
- JSString* newMessageString(JSContext* cx);
-
- void freeMessage();
+ private:
+ void freeLinebuf();
};
/*
diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp
index 31d62332d8..4e34c0e488 100644
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -557,6 +557,18 @@ class MOZ_RAII AutoMessageArgs
}
};
+static void
+SetExnType(JSErrorReport* reportp, int16_t exnType)
+{
+ reportp->exnType = exnType;
+}
+
+static void
+SetExnType(JSErrorNotes::Note* notep, int16_t exnType)
+{
+ // Do nothing for JSErrorNotes::Note.
+}
+
/*
* The arguments from ap need to be packaged up into an array and stored
* into the report struct.
@@ -568,12 +580,13 @@ class MOZ_RAII AutoMessageArgs
*
* Returns true if the expansion succeeds (can fail if out of memory).
*/
+template <typename T>
bool
-js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
+ExpandErrorArgumentsHelper(ExclusiveContext* cx, JSErrorCallback callback,
void* userRef, const unsigned errorNumber,
const char16_t** messageArgs,
ErrorArgumentsType argumentsType,
- JSErrorReport* reportp, va_list ap)
+ T* reportp, va_list ap)
{
const JSErrorFormatString* efs;
@@ -586,7 +599,7 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
}
if (efs) {
- reportp->exnType = efs->exnType;
+ SetExnType(reportp, efs->exnType);
MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(efs->format));
@@ -670,6 +683,28 @@ js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
}
bool
+js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
+ void* userRef, const unsigned errorNumber,
+ const char16_t** messageArgs,
+ ErrorArgumentsType argumentsType,
+ JSErrorReport* reportp, va_list ap)
+{
+ return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
+ messageArgs, argumentsType, reportp, ap);
+}
+
+bool
+js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
+ void* userRef, const unsigned errorNumber,
+ const char16_t** messageArgs,
+ ErrorArgumentsType argumentsType,
+ JSErrorNotes::Note* notep, va_list ap)
+{
+ return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
+ messageArgs, argumentsType, notep, ap);
+}
+
+bool
js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
void* userRef, const unsigned errorNumber,
ErrorArgumentsType argumentsType, va_list ap)
diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h
index 0a28412425..c1e676f685 100644
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -621,6 +621,13 @@ ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
ErrorArgumentsType argumentsType,
JSErrorReport* reportp, va_list ap);
+extern bool
+ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
+ void* userRef, const unsigned errorNumber,
+ const char16_t** messageArgs,
+ ErrorArgumentsType argumentsType,
+ JSErrorNotes::Note* notep, va_list ap);
+
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
extern void
ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg);
diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp
index 102e6fb34e..b07d118b9e 100644
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -201,28 +201,77 @@ ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
IMPLEMENT_ERROR_CLASS(RuntimeError)
};
-JSErrorReport*
-js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
+size_t
+ExtraMallocSize(JSErrorReport* report)
+{
+ if (report->linebuf())
+ return (report->linebufLength() + 1) * sizeof(char16_t);
+
+ return 0;
+}
+
+size_t
+ExtraMallocSize(JSErrorNotes::Note* note)
+{
+ return 0;
+}
+
+bool
+CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy, JSErrorReport* report)
+{
+ if (report->linebuf()) {
+ size_t linebufSize = (report->linebufLength() + 1) * sizeof(char16_t);
+ const char16_t* linebufCopy = (const char16_t*)(*cursor);
+ js_memcpy(*cursor, report->linebuf(), linebufSize);
+ *cursor += linebufSize;
+ copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset());
+ }
+
+ /* Copy non-pointer members. */
+ copy->isMuted = report->isMuted;
+ copy->exnType = report->exnType;
+
+ /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
+ copy->flags = report->flags;
+
+ /* Deep copy notes. */
+ if (report->notes) {
+ auto copiedNotes = report->notes->copy(cx);
+ if (!copiedNotes)
+ return false;
+ copy->notes = Move(copiedNotes);
+ } else {
+ copy->notes.reset(nullptr);
+ }
+
+ return true;
+}
+
+bool
+CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorNotes::Note* copy, JSErrorNotes::Note* report)
+{
+ return true;
+}
+
+template <typename T>
+static T*
+CopyErrorHelper(JSContext* cx, T* report)
{
/*
- * We use a single malloc block to make a deep copy of JSErrorReport with
+ * We use a single malloc block to make a deep copy of JSErrorReport or
+ * JSErrorNotes::Note, except JSErrorNotes linked from JSErrorReport with
* the following layout:
- * JSErrorReport
+ * JSErrorReport or JSErrorNotes::Note
* char array with characters for message_
- * char16_t array with characters for linebuf
* char array with characters for filename
+ * char16_t array with characters for linebuf (only for JSErrorReport)
* Such layout together with the properties enforced by the following
* asserts does not need any extra alignment padding.
*/
- JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char*) == 0);
+ JS_STATIC_ASSERT(sizeof(T) % sizeof(const char*) == 0);
JS_STATIC_ASSERT(sizeof(const char*) % sizeof(char16_t) == 0);
-#define JS_CHARS_SIZE(chars) ((js_strlen(chars) + 1) * sizeof(char16_t))
-
size_t filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
- 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;
@@ -231,13 +280,13 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
* 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(T) + messageSize + filenameSize + ExtraMallocSize(report);
uint8_t* cursor = cx->pod_calloc<uint8_t>(mallocSize);
if (!cursor)
return nullptr;
- JSErrorReport* copy = (JSErrorReport*)cursor;
- cursor += sizeof(JSErrorReport);
+ T* copy = new (cursor) T();
+ cursor += sizeof(T);
if (report->message()) {
copy->initBorrowedMessage((const char*)cursor);
@@ -245,33 +294,40 @@ js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
cursor += messageSize;
}
- 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());
- }
-
if (report->filename) {
copy->filename = (const char*)cursor;
js_memcpy(cursor, report->filename, filenameSize);
+ cursor += filenameSize;
+ }
+
+ if (!CopyExtraData(cx, &cursor, copy, report)) {
+ /* js_delete calls destructor for T and js_free for pod_calloc. */
+ js_delete(copy);
+ return nullptr;
}
- MOZ_ASSERT(cursor + filenameSize == (uint8_t*)copy + mallocSize);
+
+ MOZ_ASSERT(cursor == (uint8_t*)copy + mallocSize);
/* Copy non-pointer members. */
- copy->isMuted = report->isMuted;
copy->lineno = report->lineno;
copy->column = report->column;
copy->errorNumber = report->errorNumber;
- copy->exnType = report->exnType;
- /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
- copy->flags = report->flags;
-
-#undef JS_CHARS_SIZE
return copy;
}
+JSErrorNotes::Note*
+js::CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note)
+{
+ return CopyErrorHelper(cx, note);
+}
+
+JSErrorReport*
+js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
+{
+ return CopyErrorHelper(cx, report);
+}
+
struct SuppressErrorsGuard
{
JSContext* cx;
@@ -322,7 +378,7 @@ exn_finalize(FreeOp* fop, JSObject* obj)
{
MOZ_ASSERT(fop->maybeOffMainThread());
if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
- fop->free_(report);
+ fop->delete_(report);
}
JSErrorReport*
diff --git a/js/src/jsexn.h b/js/src/jsexn.h
index ae6335209b..1698bb2e97 100644
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -18,6 +18,9 @@
namespace js {
class ErrorObject;
+JSErrorNotes::Note*
+CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
+
JSErrorReport*
CopyErrorReport(JSContext* cx, JSErrorReport* report);