Bug 1283712 - Part 1: Add JSErrorBase, JSErrorNotes, JSErrorNotes::Note, and JSErrorReport.{notes,freeNotes}. r=jwalden
authorTooru Fujisawa <arai_a@mac.com>
Wed, 15 Feb 2017 23:53:05 +0900
changeset 343085 cbcd8c6fe6bb6ddbb38bbdf72f6365a0faeb56f2
parent 343084 761227e357b95f345ec43df0b2be4cba88393ef4
child 343086 c7a11d17916df6fad0884230d1010621f0cd1311
push id31369
push userkwierso@gmail.com
push dateThu, 16 Feb 2017 00:18:40 +0000
treeherdermozilla-central@e9b926463f9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1283712
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1283712 - Part 1: Add JSErrorBase, JSErrorNotes, JSErrorNotes::Note, and JSErrorReport.{notes,freeNotes}. r=jwalden
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsexn.cpp
js/src/jsexn.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -64,17 +64,16 @@
 #include "jit/JitCommon.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #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"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/ErrorObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
@@ -6257,34 +6256,160 @@ JSErrorReport::freeLinebuf()
     if (ownsLinebuf_ && linebuf_) {
         js_free((void*)linebuf_);
         ownsLinebuf_ = false;
     }
     linebuf_ = nullptr;
 }
 
 JSString*
-JSErrorReport::newMessageString(JSContext* cx)
+JSErrorBase::newMessageString(JSContext* cx)
 {
     if (!message_)
         return cx->runtime()->emptyString;
 
     return JS_NewStringCopyUTF8Z(cx, message_);
 }
 
 void
-JSErrorReport::freeMessage()
+JSErrorBase::freeMessage()
 {
     if (ownsMessage_) {
         js_free((void*)message_.get());
         ownsMessage_ = false;
     }
     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)
 {
     AssertHeapIsIdle();
     return ThrowStopIteration(cx);
 }
 
 JS_PUBLIC_API(bool)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -12,16 +12,17 @@
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Range.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Variant.h"
 
+#include <iterator>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include "jsalloc.h"
 #include "jspubtd.h"
 
@@ -31,16 +32,17 @@
 #include "js/GCVector.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
 #include "js/Principals.h"
 #include "js/Realm.h"
 #include "js/RefCounted.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"
 
 /************************************************************************/
 
 namespace JS {
 
@@ -5384,93 +5386,197 @@ extern JS_PUBLIC_API(void)
 JS_ReportOutOfMemory(JSContext* cx);
 
 /**
  * Complain when an allocation size overflows the maximum supported limit.
  */
 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_;
 
-    // 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'.
-    size_t linebufLength_;
-
-    // The 0-based offset of error token in linebuf_.
-    size_t tokenOffset_;
-
   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)
+    JSErrorBase()
+      : filename(nullptr), lineno(0), column(0),
+        errorNumber(0),
+        ownsMessage_(false)
     {}
 
-    ~JSErrorReport() {
-        freeLinebuf();
+    ~JSErrorBase() {
         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. */
+    // 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 ownsLinebuf_ : 1;
     bool ownsMessage_ : 1;
 
   public:
-    const char16_t* linebuf() const {
-        return linebuf_;
-    }
-    size_t linebufLength() const {
-        return linebufLength_;
-    }
-    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);
 
+  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.
+    const char16_t* linebuf_;
+
+    // Number of chars in linebuf_. Does not include trailing '\0'.
+    size_t linebufLength_;
+
+    // The 0-based offset of error token in linebuf_.
+    size_t tokenOffset_;
+
+  public:
+    JSErrorReport()
+      : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0),
+        notes(nullptr),
+        flags(0), exnType(0), isMuted(false),
+        ownsLinebuf_(false)
+    {}
+
+    ~JSErrorReport() {
+        freeLinebuf();
+    }
+
+    // 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;
+
+  public:
+    const char16_t* linebuf() const {
+        return linebuf_;
+    }
+    size_t linebufLength() const {
+        return linebufLength_;
+    }
+    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);
+
+  private:
+    void freeLinebuf();
+};
+
 /*
  * JSErrorReport flag values.  These may be freely composed.
  */
 #define JSREPORT_ERROR      0x0     /* pseudo-flag for default case */
 #define JSREPORT_WARNING    0x1     /* reported via JS_ReportWarning */
 #define JSREPORT_EXCEPTION  0x2     /* exception was thrown */
 #define JSREPORT_STRICT     0x4     /* error or warning due to strict option */
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -617,46 +617,59 @@ class MOZ_RAII AutoMessageArgs
               }
             }
             totalLength_ += lengths_[i];
         }
         return true;
     }
 };
 
+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.
  *
  * 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_.
  *
  * Returns true if the expansion succeeds (can fail if out of memory).
  */
+template <typename T>
 bool
-js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
+ExpandErrorArgumentsHelper(JSContext* 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;
 
     if (!callback)
         callback = GetErrorMessage;
 
     {
         AutoSuppressGC suppressGC(cx);
         efs = callback(userRef, errorNumber);
     }
 
     if (efs) {
-        reportp->exnType = efs->exnType;
+        SetExnType(reportp, efs->exnType);
 
         MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(efs->format));
 
         uint16_t argCount = efs->argCount;
         MOZ_RELEASE_ASSERT(argCount <= JS::MaxNumErrorArguments);
         if (argCount > 0) {
             /*
              * Parse the error format, substituting the argument X
@@ -730,16 +743,38 @@ js::ExpandErrorArgumentsVA(JSContext* cx
             return false;
         snprintf(message, nbytes, defaultErrorMessage, errorNumber);
         reportp->initOwnedMessage(message);
     }
     return true;
 }
 
 bool
+js::ExpandErrorArgumentsVA(JSContext* 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)
 {
     JSErrorReport report;
     bool warning;
 
     if (checkReportFlags(cx, &flags))
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -988,16 +988,23 @@ ReportErrorNumberUCArray(JSContext* cx, 
 
 extern bool
 ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
                        void* userRef, const unsigned errorNumber,
                        const char16_t** messageArgs,
                        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);
 
 /*
  * 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.
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -200,85 +200,141 @@ ErrorObject::classes[JSEXN_ERROR_LIMIT] 
     IMPLEMENT_ERROR_CLASS(URIError),
     // These Error subclasses are not accessible via the global object:
     IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
     IMPLEMENT_ERROR_CLASS(CompileError),
     IMPLEMENT_ERROR_CLASS(LinkError),
     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;
 
     /*
      * 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);
         js_memcpy(cursor, report->message().c_str(), messageSize);
         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;
     }
-    MOZ_ASSERT(cursor + filenameSize == (uint8_t*)copy + mallocSize);
+
+    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 == (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;
+
+    return copy;
+}
 
-    /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
-    copy->flags = report->flags;
+JSErrorNotes::Note*
+js::CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note)
+{
+    return CopyErrorHelper(cx, note);
+}
 
-#undef JS_CHARS_SIZE
-    return copy;
+JSErrorReport*
+js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
+{
+    return CopyErrorHelper(cx, report);
 }
 
 struct SuppressErrorsGuard
 {
     JSContext* cx;
     JS::WarningReporter prevReporter;
     JS::AutoSaveExceptionState prevState;
 
@@ -321,17 +377,17 @@ js::ComputeStackString(JSContext* cx)
     return str.get();
 }
 
 static void
 exn_finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->maybeOnHelperThread());
     if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
-        fop->free_(report);
+        fop->delete_(report);
 }
 
 JSErrorReport*
 js::ErrorFromException(JSContext* cx, HandleObject objArg)
 {
     // It's ok to UncheckedUnwrap here, since all we do is get the
     // JSErrorReport, and consumers are careful with the information they get
     // from that anyway.  Anyone doing things that would expose anything in the
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -13,16 +13,19 @@
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "NamespaceImports.h"
 
 namespace js {
 class ErrorObject;
 
+JSErrorNotes::Note*
+CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
+
 JSErrorReport*
 CopyErrorReport(JSContext* cx, JSErrorReport* report);
 
 JSString*
 ComputeStackString(JSContext* cx);
 
 /*
  * Given a JSErrorReport, check to see if there is an exception associated with