--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -10,16 +10,17 @@
#include "frontend/BytecodeEmitter.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h"
+#include "mozilla/Unused.h"
#include <string.h>
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsfun.h"
#include "jsnum.h"
@@ -2488,17 +2489,17 @@ LengthOfSetLine(unsigned line)
/* Updates line number notes, not column notes. */
bool
BytecodeEmitter::updateLineNumberNotes(uint32_t offset)
{
TokenStream* ts = &parser->tokenStream;
bool onThisLine;
if (!ts->srcCoords.isOnThisLine(offset, currentLine(), &onThisLine))
- return ts->reportError(JSMSG_OUT_OF_MEMORY);
+ return ReportTokenError(ts, JSMSG_OUT_OF_MEMORY);
if (!onThisLine) {
unsigned line = ts->srcCoords.lineNum(offset);
unsigned delta = line - currentLine();
/*
* Encode any change in the current source line number by using
* either several SRC_NEWLINE notes or just one SRC_SETLINE note,
* whichever consumes less space.
@@ -3561,48 +3562,54 @@ inline TokenStream&
BytecodeEmitter::tokenStream()
{
return parser->tokenStream;
}
bool
BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
{
- TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
-
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream().reportCompileErrorNumberVA(nullptr, pos.begin, JSREPORT_ERROR,
- errorNumber, args);
+
+ OffsetErrorContext context = pn
+ ? tokenStream().atOffset(pn->pn_pos.begin)
+ : tokenStream().asOffsetErrorContext();
+ bool result = ReportCompileErrorNumberVA(&context, nullptr, JSREPORT_ERROR,
+ errorNumber, args);
va_end(args);
return result;
}
bool
BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
{
- TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
-
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream().reportExtraWarningErrorNumberVA(nullptr, pos.begin,
- errorNumber, args);
+
+ OffsetErrorContext context = pn
+ ? tokenStream().atOffset(pn->pn_pos.begin)
+ : tokenStream().asOffsetErrorContext();
+ bool result = ReportExtraWarningErrorNumberVA(&context, nullptr,
+ errorNumber, args);
va_end(args);
return result;
}
bool
BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...)
{
- TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
-
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream().reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(),
- errorNumber, args);
+
+ OffsetErrorContext context = pn
+ ? tokenStream().atOffset(pn->pn_pos.begin)
+ : tokenStream().asOffsetErrorContext();
+ bool result = ReportStrictModeErrorNumberVA(&context, nullptr, sc->strict(),
+ errorNumber, args);
va_end(args);
return result;
}
bool
BytecodeEmitter::emitNewInit(JSProtoKey key)
{
const size_t len = 1 + UINT32_INDEX_LEN;
@@ -4459,17 +4466,17 @@ BytecodeEmitter::emitSwitch(ParseNode* p
// After entering the scope, push the switch control.
BreakableControl controlInfo(this, StatementKind::Switch);
ptrdiff_t top = offset();
// Switch bytecodes run from here till end of final case.
uint32_t caseCount = cases->pn_count;
if (caseCount > JS_BIT(16)) {
- parser->tokenStream.reportError(JSMSG_TOO_MANY_CASES);
+ mozilla::Unused << ReportTokenError(&parser->tokenStream, JSMSG_TOO_MANY_CASES);
return false;
}
// Try for most optimal, fall back if not dense ints.
JSOp switchOp = JSOP_TABLESWITCH;
uint32_t tableLength = 0;
int32_t low, high;
bool hasDefault = false;
@@ -9081,19 +9088,19 @@ BytecodeEmitter::emitCallOrNew(ParseNode
* Then (or in a call case that has no explicit reference-base
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
* value required for calls (which non-strict mode functions
* will box into the global object).
*/
uint32_t argc = pn->pn_count - 1;
if (argc >= ARGC_LIMIT) {
- parser->tokenStream.reportError(callop
- ? JSMSG_TOO_MANY_FUN_ARGS
- : JSMSG_TOO_MANY_CON_ARGS);
+ mozilla::Unused << ReportTokenError(&parser->tokenStream, callop
+ ? JSMSG_TOO_MANY_FUN_ARGS
+ : JSMSG_TOO_MANY_CON_ARGS);
return false;
}
ParseNode* pn2 = pn->pn_head;
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
switch (pn2->getKind()) {
case PNK_NAME:
if (emitterMode == BytecodeEmitter::SelfHosting && !spread) {
@@ -10855,17 +10862,17 @@ BytecodeEmitter::addToSrcNoteDelta(jssrc
}
return true;
}
bool
BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offset)
{
if (!SN_REPRESENTABLE_OFFSET(offset)) {
- parser->tokenStream.reportError(JSMSG_NEED_DIET, js_script_str);
+ mozilla::Unused << ReportTokenError(&parser->tokenStream, JSMSG_NEED_DIET, js_script_str);
return false;
}
SrcNotesVector& notes = this->notes();
/* Find the offset numbered which (i.e., skip exactly which offsets). */
jssrcnote* sn = ¬es[index];
MOZ_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/ErrorReport.cpp
@@ -0,0 +1,266 @@
+#include "frontend/ErrorReport.h"
+#include "vm/StringBuffer.h"
+
+namespace js {
+namespace frontend {
+
+namespace {
+
+// By convention, if the offset is `NoOffset`, the error doesn't have an offset.
+const uint32_t NoOffset = UINT32_MAX;
+
+}
+
+//----- Implementation of ErrorContext and OffsetErrorContext
+
+OffsetErrorContext::OffsetErrorContext(ErrorContext* original, uint32_t offset):
+ wrapped_(original),
+ offset_(offset)
+{ }
+
+uint32_t
+OffsetErrorContext::currentPosition() const {
+ return offset_;
+}
+
+const ReadOnlyCompileOptions&
+OffsetErrorContext::options() const {
+ return wrapped_->options();
+}
+
+JSContext*
+OffsetErrorContext::context() const {
+ return wrapped_->context();
+}
+
+bool
+OffsetErrorContext::strictMode() const {
+ return wrapped_->strictMode();
+}
+
+const char*
+OffsetErrorContext::getFilename() const {
+ return wrapped_->getFilename();
+}
+
+const class SourceCoords&
+OffsetErrorContext::sourceCoords() const {
+ return wrapped_->sourceCoords();
+}
+
+unsigned
+OffsetErrorContext::lineNumber() const {
+ return wrapped_->lineNumber();
+}
+
+bool
+OffsetErrorContext::fillComplaint(JSErrorReport& err) {
+ return wrapped_->fillComplaint(err);
+}
+
+bool
+OffsetErrorContext::hasMutedErrors() const {
+ return wrapped_->hasMutedErrors();
+}
+
+OffsetErrorContext ErrorContext::asOffsetErrorContext() {
+ return OffsetErrorContext(this, currentPosition());
+}
+
+OffsetErrorContext ErrorContext::noOffset() {
+ return OffsetErrorContext(this, UINT32_MAX);
+}
+
+OffsetErrorContext ErrorContext::atOffset(uint32_t offset) {
+ return OffsetErrorContext(this, offset);
+}
+
+// ----- Actual error reporting
+
+void
+ReportInvalidEscapeError(ErrorContext* cx, InvalidEscapeType type) {
+ switch (type) {
+ case InvalidEscapeType::None:
+ MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType");
+ return;
+ case InvalidEscapeType::Hexadecimal:
+ IgnoreReportTokenError(cx, JSMSG_MALFORMED_ESCAPE, "hexadecimal");
+ return;
+ case InvalidEscapeType::Unicode:
+ IgnoreReportTokenError(cx, JSMSG_MALFORMED_ESCAPE, "Unicode");
+ return;
+ case InvalidEscapeType::UnicodeOverflow:
+ IgnoreReportTokenError(cx, JSMSG_UNICODE_OVERFLOW, "escape sequence");
+ return;
+ case InvalidEscapeType::Octal:
+ IgnoreReportTokenError(cx, JSMSG_DEPRECATED_OCTAL);
+ return;
+ }
+}
+
+bool
+ReportCompileErrorNumberVA(ErrorContext* context,
+ UniquePtr<JSErrorNotes> notes,
+ unsigned flags, unsigned errorNumber, va_list args)
+{
+ bool warning = JSREPORT_IS_WARNING(flags);
+ const uint32_t offset = context->currentPosition();
+
+ if (warning && context->options().werrorOption) {
+ flags &= ~JSREPORT_WARNING;
+ warning = false;
+ }
+
+ // On the active thread, report the error immediately. When compiling off
+ // thread, save the error so that the thread finishing the parse can report
+ // it later.
+ CompileError tempErr;
+ CompileError* tempErrPtr = &tempErr;
+ JSContext* cx = context->context();
+ if (cx->helperThread() && !cx->addPendingCompileError(&tempErrPtr))
+ return false;
+ CompileError& err = *tempErrPtr;
+
+ err.notes = Move(notes);
+ err.flags = flags;
+ err.errorNumber = errorNumber;
+ err.filename = context->getFilename();
+ err.isMuted = context->hasMutedErrors();
+ const SourceCoords& srcCoords = context->sourceCoords();
+ if (offset == NoOffset) {
+ err.lineno = 0;
+ err.column = 0;
+ } else {
+ err.lineno = srcCoords.lineNum(offset);
+ err.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->helperThread()) {
+ NonBuiltinFrameIter iter(cx,
+ 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);
+ }
+ }
+
+ if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
+ nullptr, ArgumentsAreLatin1, &err, args))
+ {
+ return false;
+ }
+
+ // Given a token, T, that we want to complain about: if T's (starting)
+ // lineno doesn't match TokenStream's lineno, that means we've scanned past
+ // the line that T starts on, which makes it hard to print some or all of
+ // T's (starting) line for context.
+ //
+ // 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 == context->lineNumber() && !callerFilename) {
+ if (!context->fillComplaint(err)) {
+ return false;
+ }
+ }
+
+ if (!cx->helperThread())
+ err.throwError(cx);
+
+ return warning;
+}
+
+bool
+ReportStrictModeTokenError(ErrorContext* context, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ bool result = ReportStrictModeErrorNumberVA(context, nullptr,
+ context->strictMode(), errorNumber, args);
+ va_end(args);
+ return result;
+}
+
+bool
+ReportTokenError(ErrorContext* context, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ bool result = ReportCompileErrorNumberVA(context, nullptr,
+ JSREPORT_ERROR, errorNumber, args);
+ va_end(args);
+ return result;
+}
+
+void
+IgnoreReportTokenError(ErrorContext* context, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+#ifdef DEBUG
+ bool result =
+#endif
+ ReportCompileErrorNumberVA(context, nullptr, JSREPORT_ERROR,
+ errorNumber, args);
+ MOZ_ASSERT(!result, "reporting an error returned true?");
+ va_end(args);
+}
+
+bool
+ReportWarning(ErrorContext* context, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ bool result = ReportCompileErrorNumberVA(context, nullptr,
+ JSREPORT_WARNING, errorNumber, args);
+ va_end(args);
+ return result;
+}
+
+bool
+ReportExtraWarningErrorNumberVA(ErrorContext* context,
+ UniquePtr<JSErrorNotes> notes, unsigned errorNumber, va_list args)
+{
+ if (!context->options().extraWarningsOption)
+ return true;
+
+ return ReportCompileErrorNumberVA(context, Move(notes),
+ JSREPORT_STRICT|JSREPORT_WARNING, errorNumber, args);
+}
+
+void
+ReportAsmJSError(ErrorContext* context, unsigned errorNumber, ...)
+{
+ va_list args;
+ va_start(args, errorNumber);
+ unsigned flags = context->options().throwOnAsmJSValidationFailureOption
+ ? JSREPORT_ERROR
+ : JSREPORT_WARNING;
+ ReportCompileErrorNumberVA(context, nullptr, flags, errorNumber, args);
+ va_end(args);
+}
+
+bool
+ReportStrictModeErrorNumberVA(ErrorContext* context,
+ UniquePtr<JSErrorNotes> notes,
+ bool strictMode, unsigned errorNumber, va_list args)
+{
+ // In strict mode code, this is an error, not merely a warning.
+ unsigned flags;
+ if (strictMode)
+ flags = JSREPORT_ERROR;
+ else if (context->options().extraWarningsOption)
+ flags = JSREPORT_WARNING | JSREPORT_STRICT;
+ else
+ return true;
+
+ return ReportCompileErrorNumberVA(context, Move(notes), flags, errorNumber, args);
+}
+
+} // namespace frontend
+} // namespace js
+
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/ErrorReport.h
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef frontend_ErrorReport_h
+#define frontend_ErrorReport_h
+
+// Error reporting for the tokenizer
+
+#include "jsapi.h"
+#include "NamespaceImports.h"
+#include "frontend/SourceCoords.h"
+
+namespace js {
+namespace frontend {
+
+class OffsetErrorContext;
+
+// Additional data on an error.
+//
+// This (almost) purely virtual class provides signatures to access the
+// context data maintained by TokenStream, without having to depend on
+// the full implementation of TokenStream.
+class ErrorContext
+{
+ public:
+ virtual JSContext* context() const = 0;
+
+ // The current position in characters, or UINT32_MAX if
+ // there is no position.
+ virtual uint32_t currentPosition() const = 0;
+
+ // The number of the current line.
+ virtual unsigned lineNumber() const = 0;
+
+ // The options with which the source is compiled.
+ virtual const JS::ReadOnlyCompileOptions& options() const = 0;
+
+ // `true` if we are operating in strict mode, `false` otherwise.
+ virtual bool strictMode() const = 0;
+
+ // The name of the file being parsed.
+ virtual const char* getFilename() const = 0;
+
+ // `true` if errors should be sanitized ("muted"), as specified
+ // by HTML5 security.
+ virtual bool hasMutedErrors() const = 0;
+
+ // Access to the source line-to-offset mappings.
+ virtual const class SourceCoords& sourceCoords() const = 0;
+
+ // Add an error message displaying the context of a syntax error.
+ virtual bool fillComplaint(JSErrorReport& err) = 0;
+
+ // Return an OffsetErrorContext based on `this`, but without an offset.
+ OffsetErrorContext noOffset();
+
+ // Return an OffsetErrorContext based on `this`, but with a different offset.
+ OffsetErrorContext atOffset(uint32_t offset);
+
+ // Return an OffsetErrorContext with the same behavior as `this`.
+ OffsetErrorContext asOffsetErrorContext();
+};
+
+// Utility class providing the ability to use an `ErrorContext` but to specify
+// an error that happened at a different offset or without any offset.
+class OffsetErrorContext: public ErrorContext MOZ_STACK_CLASS {
+public:
+ OffsetErrorContext(ErrorContext*, uint32_t offset);
+
+public:
+ virtual uint32_t currentPosition() const override final;
+ virtual const ReadOnlyCompileOptions& options() const override final;
+ virtual JSContext* context() const override final;
+ virtual bool strictMode() const override final;
+ virtual const char* getFilename() const override final;
+ virtual bool hasMutedErrors() const override final;
+ virtual const class SourceCoords& sourceCoords() const override final;
+ virtual unsigned lineNumber() const override final;
+ virtual bool fillComplaint(JSErrorReport& err) override final;
+
+private:
+ // The ErrorContext providing all the data.
+ ErrorContext* wrapped_;
+
+ // An offset for the error. By convention, UINT32_MAX for "no offset".
+ const uint32_t offset_;
+};
+
+enum class InvalidEscapeType {
+ // No invalid character escapes.
+ None,
+ // A malformed \x escape.
+ Hexadecimal,
+ // A malformed \u escape.
+ Unicode,
+ // An otherwise well-formed \u escape which represents a
+ // codepoint > 10FFFF.
+ UnicodeOverflow,
+ // An octal escape in a template token.
+ Octal
+};
+
+//---- Specialized error reporters
+
+void ReportInvalidEscapeError(ErrorContext* context, InvalidEscapeType type);
+
+MOZ_MUST_USE bool ReportTokenError(ErrorContext* context, unsigned errorNumber, ...);
+// As `reportTokenError`, but asserts that the error was reported instead of returning a `bool`.
+void IgnoreReportTokenError(ErrorContext* context, unsigned errorNumber, ...);
+
+MOZ_MUST_USE bool ReportStrictModeTokenError(ErrorContext* context, unsigned errorNumber, ...);
+
+MOZ_MUST_USE bool ReportWarning(ErrorContext* context, unsigned errorNumber, ...);
+
+// asm.js reporter
+void ReportAsmJSError(ErrorContext* context, unsigned errorNumber, ...);
+
+
+
+//--- General-purpose error reporters
+
+// General-purpose error reporters. You should avoid calling these
+// directly, and instead use the more succinct alternatives (reportTokenError(),
+// reportWarning(), &c.) in TokenStream, Parser, and BytecodeEmitter.
+bool ReportCompileErrorNumberVA(ErrorContext* context, UniquePtr<JSErrorNotes> notes, unsigned flags,
+ unsigned errorNumber, va_list args);
+bool ReportStrictModeErrorNumberVA(ErrorContext* context, UniquePtr<JSErrorNotes> notes,
+ bool strictMode, unsigned errorNumber, va_list args);
+bool ReportExtraWarningErrorNumberVA(ErrorContext* context, UniquePtr<JSErrorNotes> notes,
+ unsigned errorNumber, va_list args);
+
+
+}
+}
+
+#endif // frontend_ErrorReport_h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -324,17 +324,17 @@ EvalSharedContext::EvalSharedContext(JSC
}
}
}
bool
ParseContext::init()
{
if (scriptId_ == UINT32_MAX) {
- tokenStream_.reportError(JSMSG_NEED_DIET, js_script_str);
+ mozilla::Unused << ReportTokenError(&tokenStream_, JSMSG_NEED_DIET, js_script_str);
return false;
}
JSContext* cx = sc()->context;
if (isFunctionBox()) {
// Named lambdas always need a binding for their own name. If this
// binding is closed over when we finish parsing the function in
@@ -588,150 +588,165 @@ FunctionBox::initWithEnclosingScope(Scop
computeInWith(enclosingScope);
}
void
ParserBase::error(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+ OffsetErrorContext context = tokenStream.atOffset(pos().begin);
#ifdef DEBUG
bool result =
#endif
- tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_ERROR,
- errorNumber, args);
+ ReportCompileErrorNumberVA(&context, nullptr, JSREPORT_ERROR,
+ errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
va_end(args);
}
void
ParserBase::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+ OffsetErrorContext context = tokenStream.atOffset(pos().begin);
#ifdef DEBUG
bool result =
#endif
- tokenStream.reportCompileErrorNumberVA(Move(notes), pos().begin, JSREPORT_ERROR,
- errorNumber, args);
+ ReportCompileErrorNumberVA(&context, Move(notes), JSREPORT_ERROR,
+ errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
va_end(args);
}
void
ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+ OffsetErrorContext context = tokenStream.atOffset(offset);
#ifdef DEBUG
bool result =
#endif
- tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args);
+ ReportCompileErrorNumberVA(&context, nullptr, JSREPORT_ERROR, errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
va_end(args);
}
void
ParserBase::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+
+ OffsetErrorContext context = tokenStream.atOffset(offset);
#ifdef DEBUG
bool result =
#endif
- tokenStream.reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_ERROR,
+ ReportCompileErrorNumberVA(&context, Move(notes), JSREPORT_ERROR,
errorNumber, args);
MOZ_ASSERT(!result, "reporting an error returned true?");
va_end(args);
}
bool
ParserBase::warning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+
+ OffsetErrorContext context = tokenStream.atOffset(pos().begin);
bool result =
- tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_WARNING,
- errorNumber, args);
+ ReportCompileErrorNumberVA(&context, nullptr, JSREPORT_WARNING,
+ errorNumber, args);
va_end(args);
return result;
}
bool
ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+
+ OffsetErrorContext context = tokenStream.atOffset(offset);
bool result =
- tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
- errorNumber, args);
+ ReportCompileErrorNumberVA(&context, nullptr, JSREPORT_WARNING,
+ errorNumber, args);
va_end(args);
return result;
}
bool
ParserBase::extraWarning(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
- bool result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin,
- errorNumber, args);
+
+ OffsetErrorContext context = tokenStream.atOffset(pos().begin);
+ bool result = ReportExtraWarningErrorNumberVA(&context, nullptr,
+ errorNumber, args);
va_end(args);
return result;
}
bool
ParserBase::strictModeError(unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+
+ OffsetErrorContext context = tokenStream.atOffset(pos().begin);
bool res =
- tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(),
- errorNumber, args);
+ ReportStrictModeErrorNumberVA(&context, nullptr, pc->sc()->strict(),
+ errorNumber, args);
va_end(args);
return res;
}
bool
ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
+
+ OffsetErrorContext context = tokenStream.atOffset(offset);
bool res =
- tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(),
- errorNumber, args);
+ ReportStrictModeErrorNumberVA(&context, nullptr, pc->sc()->strict(),
+ errorNumber, args);
va_end(args);
return res;
}
bool
ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
{
va_list args;
va_start(args, errorNumber);
bool result = false;
- uint32_t offset = TokenStream::NoOffset;
+ OffsetErrorContext context = tokenStream.noOffset();
switch (kind) {
case ParseError:
- result = tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR,
- errorNumber, args);
+ result = ReportCompileErrorNumberVA(&context, nullptr, JSREPORT_ERROR,
+ errorNumber, args);
break;
case ParseWarning:
result =
- tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING,
- errorNumber, args);
+ ReportCompileErrorNumberVA(&context, nullptr, JSREPORT_WARNING,
+ errorNumber, args);
break;
case ParseExtraWarning:
- result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset,
- errorNumber, args);
+ result = ReportExtraWarningErrorNumberVA(&context, nullptr,
+ errorNumber, args);
break;
case ParseStrictError:
- result = tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, strict,
- errorNumber, args);
+ result = ReportStrictModeErrorNumberVA(&context, nullptr, strict,
+ errorNumber, args);
break;
}
va_end(args);
return result;
}
template <>
bool
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -7,16 +7,17 @@
/* JS parser. */
#ifndef frontend_Parser_h
#define frontend_Parser_h
#include "mozilla/Array.h"
#include "mozilla/Maybe.h"
#include "mozilla/TypeTraits.h"
+#include "mozilla/Unused.h"
#include "jspubtd.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/FullParseHandler.h"
#include "frontend/NameAnalysisTypes.h"
#include "frontend/NameCollections.h"
#include "frontend/SharedContext.h"
@@ -126,17 +127,18 @@ class ParseContext : public Nestable<Par
void dump(ParseContext* pc);
uint32_t id() const {
return id_;
}
MOZ_MUST_USE bool init(ParseContext* pc) {
if (id_ == UINT32_MAX) {
- pc->tokenStream_.reportError(JSMSG_NEED_DIET, js_script_str);
+ mozilla::Unused << ReportTokenError(&pc->tokenStream_, JSMSG_NEED_DIET,
+ js_script_str);
return false;
}
return declared_.acquire(pc->sc()->context);
}
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
return declared_->lookup(name);
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/SourceCoords.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef frontend_SourceCoords_h
+#define frontend_SourceCoords_h
+
+#include "jsapi.h"
+
+namespace js {
+namespace frontend {
+
+// This class maps a userbuf offset (which is 0-indexed) to a line number
+// (which is 1-indexed) and a column index (which is 0-indexed).
+class SourceCoords
+{
+ // For a given buffer holding source code, |lineStartOffsets_| has one
+ // element per line of source code, plus one sentinel element. Each
+ // non-sentinel element holds the buffer offset for the start of the
+ // corresponding line of source code. For this example script:
+ //
+ // 1 // xyz [line starts at offset 0]
+ // 2 var x; [line starts at offset 7]
+ // 3 [line starts at offset 14]
+ // 4 var y; [line starts at offset 15]
+ //
+ // |lineStartOffsets_| is:
+ //
+ // [0, 7, 14, 15, MAX_PTR]
+ //
+ // To convert a "line number" to a "line index" (i.e. an index into
+ // |lineStartOffsets_|), subtract |initialLineNum_|. E.g. line 3's
+ // line index is (3 - initialLineNum_), which is 2. Therefore
+ // lineStartOffsets_[2] holds the buffer offset for the start of line 3,
+ // which is 14. (Note that |initialLineNum_| is often 1, but not
+ // always.)
+ //
+ // The first element is always 0, and the last element is always the
+ // MAX_PTR sentinel.
+ //
+ // offset-to-line/column lookups are O(log n) in the worst case (binary
+ // search), but in practice they're heavily clustered and we do better
+ // than that by using the previous lookup's result (lastLineIndex_) as
+ // a starting point.
+ //
+ // Checking if an offset lies within a particular line number
+ // (isOnThisLine()) is O(1).
+ //
+ Vector<uint32_t, 128> lineStartOffsets_;
+ uint32_t initialLineNum_;
+
+ // This is mutable because it's modified on every search, but that fact
+ // isn't visible outside this class.
+ mutable uint32_t lastLineIndex_;
+
+ uint32_t lineIndexOf(uint32_t offset) const;
+
+ static const uint32_t MAX_PTR = UINT32_MAX;
+
+ uint32_t lineIndexToNum(uint32_t lineIndex) const { return lineIndex + initialLineNum_; }
+ uint32_t lineNumToIndex(uint32_t lineNum) const { return lineNum - initialLineNum_; }
+
+ public:
+ SourceCoords(JSContext* cx, uint32_t ln);
+
+ MOZ_MUST_USE bool add(uint32_t lineNum, uint32_t lineStartOffset);
+ MOZ_MUST_USE bool fill(const SourceCoords& other);
+
+ bool isOnThisLine(uint32_t offset, uint32_t lineNum, bool* onThisLine) const {
+ uint32_t lineIndex = lineNumToIndex(lineNum);
+ if (lineIndex + 1 >= lineStartOffsets_.length()) // +1 due to sentinel
+ return false;
+ *onThisLine = lineStartOffsets_[lineIndex] <= offset &&
+ offset < lineStartOffsets_[lineIndex + 1];
+ return true;
+ }
+
+ uint32_t lineNum(uint32_t offset) const;
+ uint32_t columnIndex(uint32_t offset) const;
+ void lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum, uint32_t* columnIndex) const;
+};
+
+} // namespace frontend
+} // namespace js
+
+#endif // frontend_SourceCoords_h
\ No newline at end of file
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1,16 +1,17 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// JS lexical scanner.
+#include "frontend/ErrorReport.h"
#include "frontend/TokenStream.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/IntegerTypeTraits.h"
#include "mozilla/PodOperations.h"
#include <ctype.h>
#include <stdarg.h>
@@ -247,39 +248,39 @@ TokenStream::reservedWordToPropertyName(
FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
#undef EMIT_CASE
default:
MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind.");
}
return nullptr;
}
-TokenStream::SourceCoords::SourceCoords(JSContext* cx, uint32_t ln)
+SourceCoords::SourceCoords(JSContext* cx, uint32_t ln)
: lineStartOffsets_(cx), initialLineNum_(ln), lastLineIndex_(0)
{
// This is actually necessary! Removing it causes compile errors on
// GCC and clang. You could try declaring this:
//
- // const uint32_t TokenStream::SourceCoords::MAX_PTR;
+ // const uint32_t SourceCoords::MAX_PTR;
//
// which fixes the GCC/clang error, but causes bustage on Windows. Sigh.
//
uint32_t maxPtr = MAX_PTR;
// The first line begins at buffer offset 0. MAX_PTR is the sentinel. The
// appends cannot fail because |lineStartOffsets_| has statically-allocated
// elements.
MOZ_ASSERT(lineStartOffsets_.capacity() >= 2);
MOZ_ALWAYS_TRUE(lineStartOffsets_.reserve(2));
lineStartOffsets_.infallibleAppend(0);
lineStartOffsets_.infallibleAppend(maxPtr);
}
MOZ_ALWAYS_INLINE bool
-TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset)
+SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset)
{
uint32_t lineIndex = lineNumToIndex(lineNum);
uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
MOZ_ASSERT(lineStartOffsets_[0] == 0 && lineStartOffsets_[sentinelIndex] == MAX_PTR);
if (lineIndex == sentinelIndex) {
// We haven't seen this newline before. Update lineStartOffsets_
@@ -300,17 +301,17 @@ TokenStream::SourceCoords::add(uint32_t
// than checking it hasn't mysteriously changed).
// This path can be executed after hitting OOM, so check lineIndex.
MOZ_ASSERT_IF(lineIndex < sentinelIndex, lineStartOffsets_[lineIndex] == lineStartOffset);
}
return true;
}
MOZ_ALWAYS_INLINE bool
-TokenStream::SourceCoords::fill(const TokenStream::SourceCoords& other)
+SourceCoords::fill(const SourceCoords& other)
{
MOZ_ASSERT(lineStartOffsets_.back() == MAX_PTR);
MOZ_ASSERT(other.lineStartOffsets_.back() == MAX_PTR);
if (lineStartOffsets_.length() >= other.lineStartOffsets_.length())
return true;
uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
@@ -319,17 +320,17 @@ TokenStream::SourceCoords::fill(const To
for (size_t i = sentinelIndex + 1; i < other.lineStartOffsets_.length(); i++) {
if (!lineStartOffsets_.append(other.lineStartOffsets_[i]))
return false;
}
return true;
}
MOZ_ALWAYS_INLINE uint32_t
-TokenStream::SourceCoords::lineIndexOf(uint32_t offset) const
+SourceCoords::lineIndexOf(uint32_t offset) const
{
uint32_t iMin, iMax, iMid;
if (lineStartOffsets_[lastLineIndex_] <= offset) {
// If we reach here, offset is on a line the same as or higher than
// last time. Check first for the +0, +1, +2 cases, because they
// typically cover 85--98% of cases.
if (offset < lineStartOffsets_[lastLineIndex_ + 1])
@@ -370,33 +371,33 @@ TokenStream::SourceCoords::lineIndexOf(u
}
MOZ_ASSERT(iMax == iMin);
MOZ_ASSERT(lineStartOffsets_[iMin] <= offset && offset < lineStartOffsets_[iMin + 1]);
lastLineIndex_ = iMin;
return iMin;
}
uint32_t
-TokenStream::SourceCoords::lineNum(uint32_t offset) const
+SourceCoords::lineNum(uint32_t offset) const
{
uint32_t lineIndex = lineIndexOf(offset);
return lineIndexToNum(lineIndex);
}
uint32_t
-TokenStream::SourceCoords::columnIndex(uint32_t offset) const
+SourceCoords::columnIndex(uint32_t offset) const
{
uint32_t lineIndex = lineIndexOf(offset);
uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
MOZ_ASSERT(offset >= lineStartOffset);
return offset - lineStartOffset;
}
void
-TokenStream::SourceCoords::lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum,
+SourceCoords::lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum,
uint32_t* columnIndex) const
{
uint32_t lineIndex = lineIndexOf(offset);
*lineNum = lineIndexToNum(lineIndex);
uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
MOZ_ASSERT(offset >= lineStartOffset);
*columnIndex = offset - lineStartOffset;
}
@@ -445,17 +446,18 @@ TokenStream::TokenStream(JSContext* cx,
#endif
bool
TokenStream::checkOptions()
{
// Constrain starting columns to half of the range of a signed 32-bit value,
// to avoid overflow.
if (options().column >= mozilla::MaxValue<int32_t>::value / 2 + 1) {
- reportErrorNoOffset(JSMSG_BAD_COLUMN_NUMBER);
+ OffsetErrorContext context = noOffset();
+ mozilla::Unused << ReportTokenError(&context, JSMSG_BAD_COLUMN_NUMBER);
return false;
}
return true;
}
TokenStream::~TokenStream()
{
@@ -661,32 +663,16 @@ bool
TokenStream::seek(const Position& pos, const TokenStream& other)
{
if (!srcCoords.fill(other.srcCoords))
return false;
seek(pos);
return true;
}
-bool
-TokenStream::reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
- bool strictMode, unsigned errorNumber, va_list args)
-{
- // In strict mode code, this is an error, not merely a warning.
- unsigned flags;
- if (strictMode)
- flags = JSREPORT_ERROR;
- else if (options().extraWarningsOption)
- flags = JSREPORT_WARNING | JSREPORT_STRICT;
- else
- return true;
-
- return reportCompileErrorNumberVA(Move(notes), offset, flags, errorNumber, args);
-}
-
void
CompileError::throwError(JSContext* cx)
{
if (JSREPORT_IS_WARNING(flags)) {
CallWarningReporter(cx, this);
return;
}
@@ -699,217 +685,16 @@ CompileError::throwError(JSContext* cx)
// reporter is to ignore a report with this flag for all but top-level
// compilation errors. The exception will remain pending, and so long
// 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);
}
-bool
-TokenStream::reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
- unsigned flags, unsigned errorNumber, va_list args)
-{
- bool warning = JSREPORT_IS_WARNING(flags);
-
- if (warning && options().werrorOption) {
- flags &= ~JSREPORT_WARNING;
- warning = false;
- }
-
- // On the active thread, report the error immediately. When compiling off
- // thread, save the error so that the thread finishing the parse can report
- // it later.
- CompileError tempErr;
- CompileError* tempErrPtr = &tempErr;
- if (cx->helperThread() && !cx->addPendingCompileError(&tempErrPtr))
- return false;
- CompileError& err = *tempErrPtr;
-
- err.notes = Move(notes);
- err.flags = flags;
- err.errorNumber = errorNumber;
- err.filename = filename;
- err.isMuted = mutedErrors;
- if (offset == NoOffset) {
- err.lineno = 0;
- err.column = 0;
- } else {
- err.lineno = srcCoords.lineNum(offset);
- err.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->helperThread()) {
- NonBuiltinFrameIter iter(cx,
- 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);
- }
- }
-
- if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
- nullptr, ArgumentsAreLatin1, &err, args))
- {
- return false;
- }
-
- // Given a token, T, that we want to complain about: if T's (starting)
- // lineno doesn't match TokenStream's lineno, that means we've scanned past
- // the line that T starts on, which makes it hard to print some or all of
- // T's (starting) line for context.
- //
- // 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) {
- // 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
- // lines can be very long and printing the whole line is (a) not that
- // helpful, and (b) can waste a lot of memory. See bug 634444.
- static const size_t windowRadius = 60;
-
- // The window must start within the current line, no earlier than
- // windowRadius characters before offset.
- size_t windowStart = (offset - linebase > windowRadius) ?
- offset - windowRadius :
- linebase;
-
- // The window must start within the portion of the current line
- // that we actually have in our buffer.
- if (windowStart < userbuf.startOffset())
- windowStart = userbuf.startOffset();
-
- // The window must end within the current line, no later than
- // windowRadius after offset.
- size_t windowEnd = userbuf.findEOLMax(offset, windowRadius);
- size_t windowLength = windowEnd - windowStart;
- MOZ_ASSERT(windowLength <= windowRadius * 2);
-
- // Create the windowed strings.
- StringBuffer windowBuf(cx);
- if (!windowBuf.append(userbuf.rawCharPtrAt(windowStart), windowLength) ||
- !windowBuf.append('\0'))
- {
- return false;
- }
-
- // The window into the offending source line, without final \n.
- UniqueTwoByteChars linebuf(windowBuf.stealChars());
- if (!linebuf)
- return false;
-
- err.initOwnedLinebuf(linebuf.release(), windowLength, offset - windowStart);
- }
-
- if (!cx->helperThread())
- err.throwError(cx);
-
- return warning;
-}
-
-bool
-TokenStream::reportStrictModeError(unsigned errorNumber, ...)
-{
- va_list args;
- va_start(args, errorNumber);
- bool result = reportStrictModeErrorNumberVA(nullptr, currentToken().pos.begin, strictMode(),
- errorNumber, args);
- va_end(args);
- return result;
-}
-
-bool
-TokenStream::reportError(unsigned errorNumber, ...)
-{
- va_list args;
- va_start(args, errorNumber);
- bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR,
- errorNumber, args);
- va_end(args);
- return result;
-}
-
-bool
-TokenStream::reportErrorNoOffset(unsigned errorNumber, ...)
-{
- va_list args;
- va_start(args, errorNumber);
- bool result = reportCompileErrorNumberVA(nullptr, NoOffset, JSREPORT_ERROR,
- errorNumber, args);
- va_end(args);
- return result;
-}
-
-bool
-TokenStream::warning(unsigned errorNumber, ...)
-{
- va_list args;
- va_start(args, errorNumber);
- bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_WARNING,
- errorNumber, args);
- va_end(args);
- return result;
-}
-
-bool
-TokenStream::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
- unsigned errorNumber, va_list args)
-{
- if (!options().extraWarningsOption)
- return true;
-
- return reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_STRICT|JSREPORT_WARNING,
- errorNumber, args);
-}
-
-void
-TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...)
-{
- va_list args;
- va_start(args, errorNumber);
- unsigned flags = options().throwOnAsmJSValidationFailureOption
- ? JSREPORT_ERROR
- : JSREPORT_WARNING;
- reportCompileErrorNumberVA(nullptr, offset, flags, errorNumber, args);
- va_end(args);
-}
-
-void
-TokenStream::error(unsigned errorNumber, ...)
-{
- va_list args;
- va_start(args, errorNumber);
-#ifdef DEBUG
- bool result =
-#endif
- reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR,
- errorNumber, args);
- MOZ_ASSERT(!result, "reporting an error returned true?");
- va_end(args);
-}
-
-void
-TokenStream::errorAt(uint32_t offset, unsigned errorNumber, ...)
-{
- va_list args;
- va_start(args, errorNumber);
-#ifdef DEBUG
- bool result =
-#endif
- reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args);
- MOZ_ASSERT(!result, "reporting an error returned true?");
- va_end(args);
-}
-
// We have encountered a '\': check for a Unicode escape sequence after it.
// Return the length of the escape sequence and the character code point (by
// value) if we found a Unicode escape sequence. Otherwise, return 0. In both
// cases, do not advance along the buffer.
uint32_t
TokenStream::peekUnicodeEscape(uint32_t* codePoint)
{
int32_t c = getCharIgnoreEOL();
@@ -1037,17 +822,17 @@ TokenStream::getDirective(bool isMultili
const char* errorMsgPragma,
UniqueTwoByteChars* destination)
{
MOZ_ASSERT(directiveLength <= 18);
char16_t peeked[18];
if (peekChars(directiveLength, peeked) && CharsMatch(peeked, directive)) {
if (shouldWarnDeprecated) {
- if (!warning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma))
+ if (!ReportWarning(this, JSMSG_DEPRECATED_PRAGMA, errorMsgPragma))
return false;
}
skipChars(directiveLength);
tokenbuf.clear();
do {
int32_t c;
@@ -1490,37 +1275,37 @@ TokenStream::getTokenInternal(TokenKind*
}
if (c == 'e' || c == 'E') {
hasExp = true;
c = getCharIgnoreEOL();
if (c == '+' || c == '-')
c = getCharIgnoreEOL();
if (!JS7_ISDEC(c)) {
ungetCharIgnoreEOL(c);
- reportError(JSMSG_MISSING_EXPONENT);
+ mozilla::Unused << ReportTokenError(this, JSMSG_MISSING_EXPONENT);
goto error;
}
do {
c = getCharIgnoreEOL();
} while (JS7_ISDEC(c));
}
ungetCharIgnoreEOL(c);
if (c != EOF) {
if (unicode::IsIdentifierStart(char16_t(c))) {
- reportError(JSMSG_IDSTART_AFTER_NUMBER);
+ mozilla::Unused << ReportTokenError(this, JSMSG_IDSTART_AFTER_NUMBER);
goto error;
}
if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) {
uint32_t codePoint;
if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) &&
unicode::IsIdentifierStart(codePoint))
{
- reportError(JSMSG_IDSTART_AFTER_NUMBER);
+ mozilla::Unused << ReportTokenError(this, JSMSG_IDSTART_AFTER_NUMBER);
goto error;
}
}
}
// Unlike identifiers and strings, numbers cannot contain escaped
// chars, so we don't need to use tokenbuf. Instead we can just
// convert the char16_t characters in userbuf to the numeric value.
@@ -1563,84 +1348,84 @@ TokenStream::getTokenInternal(TokenKind*
tp = newToken(-1);
int radix;
c = getCharIgnoreEOL();
if (c == 'x' || c == 'X') {
radix = 16;
c = getCharIgnoreEOL();
if (!JS7_ISHEX(c)) {
ungetCharIgnoreEOL(c);
- reportError(JSMSG_MISSING_HEXDIGITS);
+ mozilla::Unused << ReportTokenError(this, JSMSG_MISSING_HEXDIGITS);
goto error;
}
numStart = userbuf.addressOfNextRawChar() - 1; // one past the '0x'
while (JS7_ISHEX(c))
c = getCharIgnoreEOL();
} else if (c == 'b' || c == 'B') {
radix = 2;
c = getCharIgnoreEOL();
if (c != '0' && c != '1') {
ungetCharIgnoreEOL(c);
- reportError(JSMSG_MISSING_BINARY_DIGITS);
+ mozilla::Unused << ReportTokenError(this, JSMSG_MISSING_BINARY_DIGITS);
goto error;
}
numStart = userbuf.addressOfNextRawChar() - 1; // one past the '0b'
while (c == '0' || c == '1')
c = getCharIgnoreEOL();
} else if (c == 'o' || c == 'O') {
radix = 8;
c = getCharIgnoreEOL();
if (c < '0' || c > '7') {
ungetCharIgnoreEOL(c);
- reportError(JSMSG_MISSING_OCTAL_DIGITS);
+ mozilla::Unused << ReportTokenError(this, JSMSG_MISSING_OCTAL_DIGITS);
goto error;
}
numStart = userbuf.addressOfNextRawChar() - 1; // one past the '0o'
while ('0' <= c && c <= '7')
c = getCharIgnoreEOL();
} else if (JS7_ISDEC(c)) {
radix = 8;
numStart = userbuf.addressOfNextRawChar() - 1; // one past the '0'
while (JS7_ISDEC(c)) {
// Octal integer literals are not permitted in strict mode code.
- if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
+ if (!ReportStrictModeTokenError(this, JSMSG_DEPRECATED_OCTAL))
goto error;
// Outside strict mode, we permit 08 and 09 as decimal numbers,
// which makes our behaviour a superset of the ECMA numeric
// grammar. We might not always be so permissive, so we warn
// about it.
if (c >= '8') {
- if (!warning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09"))
+ if (!ReportWarning(this, JSMSG_BAD_OCTAL, c == '8' ? "08" : "09"))
goto error;
// Use the decimal scanner for the rest of the number.
goto decimal;
}
c = getCharIgnoreEOL();
}
} else {
// '0' not followed by 'x', 'X' or a digit; scan as a decimal number.
numStart = userbuf.addressOfNextRawChar() - 1;
goto decimal;
}
ungetCharIgnoreEOL(c);
if (c != EOF) {
if (unicode::IsIdentifierStart(char16_t(c))) {
- reportError(JSMSG_IDSTART_AFTER_NUMBER);
+ mozilla::Unused << ReportTokenError(this, JSMSG_IDSTART_AFTER_NUMBER);
goto error;
}
if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(c))) {
uint32_t codePoint;
if (matchTrailForLeadSurrogate(c, nullptr, &codePoint) &&
unicode::IsIdentifierStart(codePoint))
{
- reportError(JSMSG_IDSTART_AFTER_NUMBER);
+ mozilla::Unused << ReportTokenError(this, JSMSG_IDSTART_AFTER_NUMBER);
goto error;
}
}
}
double dval;
const char16_t* dummy;
if (!GetPrefixInteger(cx, numStart, userbuf.addressOfNextRawChar(), radix, &dummy, &dval))
@@ -1787,17 +1572,17 @@ TokenStream::getTokenInternal(TokenKind*
!(c == '*' && matchChar('/'))) {
if (c == '@' || c == '#') {
bool shouldWarn = c == '@';
if (!getDirectives(true, shouldWarn))
goto error;
}
}
if (c == EOF) {
- reportError(JSMSG_UNTERMINATED_COMMENT);
+ mozilla::Unused << ReportTokenError(this, JSMSG_UNTERMINATED_COMMENT);
goto error;
}
if (linenoBefore != lineno)
updateFlagsForEOL();
cursor = (cursor - 1) & ntokensMask;
goto retry;
}
@@ -1817,17 +1602,17 @@ TokenStream::getTokenInternal(TokenKind*
} else if (c == ']') {
inCharClass = false;
} else if (c == '/' && !inCharClass) {
// For compat with IE, allow unescaped / in char classes.
break;
}
if (c == '\n' || c == EOF) {
ungetChar(c);
- reportError(JSMSG_UNTERMINATED_REGEXP);
+ mozilla::Unused << ReportTokenError(this, JSMSG_UNTERMINATED_REGEXP);
goto error;
}
if (!tokenbuf.append(c))
goto error;
}
RegExpFlag reflags = NoFlags;
unsigned length = tokenbuf.length() + 1;
@@ -1851,17 +1636,17 @@ TokenStream::getTokenInternal(TokenKind*
}
if (!peekChar(&c))
goto error;
if (JS7_ISLET(c)) {
char buf[2] = { '\0', '\0' };
tp->pos.begin += length + 1;
buf[0] = char(c);
- reportError(JSMSG_BAD_REGEXP_FLAG, buf);
+ mozilla::Unused << ReportTokenError(this, JSMSG_BAD_REGEXP_FLAG, buf);
(void) getChar();
goto error;
}
tp->type = TOK_REGEXP;
tp->setRegExpFlags(reflags);
goto out;
}
@@ -1886,17 +1671,17 @@ TokenStream::getTokenInternal(TokenKind*
tp->type = TOK_DEC;
} else {
tp->type = matchChar('=') ? TOK_SUBASSIGN : TOK_SUB;
}
goto out;
badchar:
default:
- reportError(JSMSG_ILLEGAL_CHARACTER);
+ mozilla::Unused << ReportTokenError(this, JSMSG_ILLEGAL_CHARACTER);
goto error;
}
MOZ_CRASH("should have jumped to |out| or |error|");
out:
if (flags.hitOOM)
return false;
@@ -1928,16 +1713,26 @@ TokenStream::getTokenInternal(TokenKind*
// because the parser will deal with the illegal token by aborting parsing
// immediately.
userbuf.poison();
#endif
MOZ_MAKE_MEM_UNDEFINED(ttp, sizeof(*ttp));
return false;
}
+bool TokenStream::checkForInvalidTemplateEscapeError()
+{
+ if (invalidTemplateEscapeType == InvalidEscapeType::None)
+ return true;
+
+ OffsetErrorContext context = atOffset(invalidTemplateEscapeOffset);
+ ReportInvalidEscapeError(&context, invalidTemplateEscapeType);
+ return false;
+}
+
bool
TokenStream::getStringOrTemplateToken(int untilChar, Token** tp)
{
int c;
int nc = -1;
bool parsingTemplate = (untilChar == '`');
@@ -1945,17 +1740,17 @@ TokenStream::getStringOrTemplateToken(in
tokenbuf.clear();
// We need to detect any of these chars: " or ', \n (or its
// equivalents), \\, EOF. Because we detect EOL sequences here and
// put them back immediately, we can use getCharIgnoreEOL().
while ((c = getCharIgnoreEOL()) != untilChar) {
if (c == EOF) {
ungetCharIgnoreEOL(c);
- error(JSMSG_UNTERMINATED_STRING);
+ IgnoreReportTokenError(this, JSMSG_UNTERMINATED_STRING);
return false;
}
if (c == '\\') {
// When parsing templates, we don't immediately report errors for
// invalid escapes; these are handled by the parser.
// In those cases we don't append to tokenbuf, since it won't be
// read.
@@ -1990,54 +1785,58 @@ TokenStream::getStringOrTemplateToken(in
do {
int32_t c = getCharIgnoreEOL();
if (c == EOF) {
if (parsingTemplate) {
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
valid = false;
break;
}
- reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+ OffsetErrorContext context = atOffset(start);
+ ReportInvalidEscapeError(&context, InvalidEscapeType::Unicode);
return false;
}
if (c == '}') {
if (first) {
if (parsingTemplate) {
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
valid = false;
break;
}
- reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+ OffsetErrorContext context = atOffset(start);
+ ReportInvalidEscapeError(&context, InvalidEscapeType::Unicode);
return false;
}
break;
}
if (!JS7_ISHEX(c)) {
if (parsingTemplate) {
// We put the character back so that we read
// it on the next pass, which matters if it
// was '`' or '\'.
ungetCharIgnoreEOL(c);
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
valid = false;
break;
}
- reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+ OffsetErrorContext context = atOffset(start);
+ ReportInvalidEscapeError(&context, InvalidEscapeType::Unicode);
return false;
}
code = (code << 4) | JS7_UNHEX(c);
if (code > unicode::NonBMPMax) {
if (parsingTemplate) {
setInvalidTemplateEscape(start + 3, InvalidEscapeType::UnicodeOverflow);
valid = false;
break;
}
- reportInvalidEscapeError(start + 3, InvalidEscapeType::UnicodeOverflow);
+ OffsetErrorContext context = atOffset(start + 3);
+ ReportInvalidEscapeError(&context, InvalidEscapeType::UnicodeOverflow);
return false;
}
first = false;
} while (true);
if (!valid)
continue;
@@ -2062,17 +1861,18 @@ TokenStream::getStringOrTemplateToken(in
c = (c << 4) + JS7_UNHEX(cp[2]);
c = (c << 4) + JS7_UNHEX(cp[3]);
skipChars(4);
} else {
if (parsingTemplate) {
setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
continue;
}
- reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
+ OffsetErrorContext context = atOffset(start);
+ ReportInvalidEscapeError(&context, InvalidEscapeType::Unicode);
return false;
}
break;
}
// Hexadecimal character specification.
case 'x': {
char16_t cp[2];
@@ -2080,17 +1880,18 @@ TokenStream::getStringOrTemplateToken(in
c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);
skipChars(2);
} else {
uint32_t start = userbuf.offset() - 2;
if (parsingTemplate) {
setInvalidTemplateEscape(start, InvalidEscapeType::Hexadecimal);
continue;
}
- reportInvalidEscapeError(start, InvalidEscapeType::Hexadecimal);
+ OffsetErrorContext context = atOffset(start);
+ ReportInvalidEscapeError(&context, InvalidEscapeType::Hexadecimal);
return false;
}
break;
}
default:
// Octal character specification.
if (JS7_ISOCT(c)) {
@@ -2100,17 +1901,17 @@ TokenStream::getStringOrTemplateToken(in
return false;
// Strict mode code allows only \0, then a non-digit.
if (val != 0 || JS7_ISDEC(c)) {
if (parsingTemplate) {
setInvalidTemplateEscape(userbuf.offset() - 2, InvalidEscapeType::Octal);
continue;
}
- if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
+ if (!ReportStrictModeTokenError(this, JSMSG_DEPRECATED_OCTAL))
return false;
flags.sawOctalEscape = true;
}
if (JS7_ISOCT(c)) {
val = 8 * val + JS7_UNOCT(c);
getChar();
if (!peekChar(&c))
@@ -2127,17 +1928,17 @@ TokenStream::getStringOrTemplateToken(in
c = char16_t(val);
}
break;
}
} else if (TokenBuf::isRawEOLChar(c)) {
if (!parsingTemplate) {
ungetCharIgnoreEOL(c);
- error(JSMSG_UNTERMINATED_STRING);
+ IgnoreReportTokenError(this, JSMSG_UNTERMINATED_STRING);
return false;
}
if (c == '\r') {
c = '\n';
if (userbuf.peekRawChar() == '\n')
skipCharsIgnoreEOL(1);
}
updateLineInfoForEOL();
@@ -2166,16 +1967,62 @@ TokenStream::getStringOrTemplateToken(in
else
(*tp)->type = TOK_NO_SUBS_TEMPLATE;
}
(*tp)->setAtom(atom);
return true;
}
+bool
+TokenStream::fillComplaint(JSErrorReport& err) {
+ const uint32_t offset = currentPosition();
+
+ // 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
+ // lines can be very long and printing the whole line is (a) not that
+ // helpful, and (b) can waste a lot of memory. See bug 634444.
+ static const size_t windowRadius = 60;
+
+
+ // The window must start within the current line, no earlier than
+ // windowRadius characters before offset.
+ size_t windowStart = (offset - linebase > windowRadius) ?
+ offset - windowRadius :
+ linebase;
+
+ // The window must start within the portion of the current line
+ // that we actually have in our buffer.
+ if (windowStart < userbuf.startOffset())
+ windowStart = userbuf.startOffset();
+
+ // The window must end within the current line, no later than
+ // windowRadius after offset.
+ size_t windowEnd = userbuf.findEOLMax(offset, windowRadius);
+ size_t windowLength = windowEnd - windowStart;
+ MOZ_ASSERT(windowLength <= windowRadius * 2);
+
+ // Create the windowed strings.
+ StringBuffer windowBuf(cx);
+ if (!windowBuf.append(userbuf.rawCharPtrAt(windowStart), windowLength)
+ || !windowBuf.append('\0'))
+ {
+ return false;
+ }
+
+ // The window into the offending source line, without final \n.
+ UniqueTwoByteChars linebuf(windowBuf.stealChars());
+ if (!linebuf)
+ return false;
+
+ err.initOwnedLinebuf(linebuf.release(), windowLength, offset - windowStart);
+ return true;
+}
+
JS_FRIEND_API(int)
js_fgets(char* buf, int size, FILE* file)
{
int n, i, c;
bool crflag;
n = size - 1;
if (n < 0)
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -17,16 +17,18 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include "jscntxt.h"
#include "jspubtd.h"
+#include "frontend/ErrorReport.h"
+#include "frontend/SourceCoords.h"
#include "frontend/TokenKind.h"
#include "js/UniquePtr.h"
#include "js/Vector.h"
#include "vm/RegExpObject.h"
#include "vm/String.h"
struct KeywordInfo;
@@ -74,30 +76,16 @@ struct TokenPos {
bool encloses(const TokenPos& pos) const {
return begin <= pos.begin && pos.end <= end;
}
};
enum DecimalPoint { NoDecimal = false, HasDecimal = true };
-enum class InvalidEscapeType {
- // No invalid character escapes.
- None,
- // A malformed \x escape.
- Hexadecimal,
- // A malformed \u escape.
- Unicode,
- // An otherwise well-formed \u escape which represents a
- // codepoint > 10FFFF.
- UnicodeOverflow,
- // An octal escape in a template token.
- Octal
-};
-
class TokenStream;
struct Token
{
private:
// Sometimes the parser needs to inform the tokenizer to interpret
// subsequent text in a particular manner: for example, to tokenize a
// keyword as an identifier, not as the actual keyword, on the right-hand
@@ -304,17 +292,17 @@ class StrictModeGetter {
// this turns out not to be a problem in practice. See the
// mozilla.dev.tech.js-engine.internals thread entitled 'Bug in the scanner?'
// for more details:
// https://groups.google.com/forum/?fromgroups=#!topic/mozilla.dev.tech.js-engine.internals/2JLH5jRcr7E).
//
// The methods seek() and tell() allow to rescan from a previous visited
// location of the buffer.
//
-class MOZ_STACK_CLASS TokenStream
+class MOZ_STACK_CLASS TokenStream: public ErrorContext
{
// Unicode separators that are treated as line terminators, in addition to \n, \r.
enum {
LINE_SEPARATOR = 0x2028,
PARA_SEPARATOR = 0x2029
};
static const size_t ntokens = 4; // 1 current + 2 lookahead, rounded
@@ -333,18 +321,19 @@ class MOZ_STACK_CLASS TokenStream
MOZ_MUST_USE bool checkOptions();
// Accessors.
const Token& currentToken() const { return tokens[cursor]; }
bool isCurrentTokenType(TokenKind type) const {
return currentToken().type == type;
}
const CharBuffer& getTokenbuf() const { return tokenbuf; }
- const char* getFilename() const { return filename; }
+ const char* getFilename() const override final { return filename; }
bool getMutedErrors() const { return mutedErrors; }
+ virtual bool hasMutedErrors() const override final { return getMutedErrors(); }
JSVersion versionNumber() const { return VersionNumber(options().version); }
JSVersion versionWithFlags() const { return options().version; }
private:
PropertyName* reservedWordToPropertyName(TokenKind tt) const;
public:
PropertyName* currentName() const {
@@ -377,51 +366,21 @@ class MOZ_STACK_CLASS TokenStream
return invalidTemplateEscapeType != InvalidEscapeType::None;
}
void clearInvalidTemplateEscape() {
invalidTemplateEscapeType = InvalidEscapeType::None;
}
// If there is an invalid escape in a template, report it and return false,
// otherwise return true.
- bool checkForInvalidTemplateEscapeError() {
- if (invalidTemplateEscapeType == InvalidEscapeType::None)
- return true;
-
- reportInvalidEscapeError(invalidTemplateEscapeOffset, invalidTemplateEscapeType);
- return false;
- }
-
- // TokenStream-specific error reporters.
- bool reportError(unsigned errorNumber, ...);
- bool reportErrorNoOffset(unsigned errorNumber, ...);
-
- // Report the given error at the current offset.
- void error(unsigned errorNumber, ...);
-
- // Report the given error at the given offset.
- void errorAt(uint32_t offset, unsigned errorNumber, ...);
+ bool checkForInvalidTemplateEscapeError();
- // Warn at the current offset.
- MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
-
- static const uint32_t NoOffset = UINT32_MAX;
-
- // General-purpose error reporters. You should avoid calling these
- // directly, and instead use the more succinct alternatives (error(),
- // warning(), &c.) in TokenStream, Parser, and BytecodeEmitter.
- bool reportCompileErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned flags,
- unsigned errorNumber, va_list args);
- bool reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
- bool strictMode, unsigned errorNumber, va_list args);
- bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
- unsigned errorNumber, va_list args);
-
- // asm.js reporter
- void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...);
+ uint32_t currentPosition() const override final {
+ return currentToken().pos.begin;
+ }
JSAtom* getRawTemplateStringAtom() {
MOZ_ASSERT(currentToken().type == TOK_TEMPLATE_HEAD ||
currentToken().type == TOK_NO_SUBS_TEMPLATE);
const char16_t* cur = userbuf.rawCharPtrAt(currentToken().pos.begin + 1);
const char16_t* end;
if (currentToken().type == TOK_TEMPLATE_HEAD) {
// Of the form |`...${| or |}...${|
@@ -441,48 +400,28 @@ class MOZ_STACK_CLASS TokenStream
}
if (!charbuf.append(ch))
return nullptr;
cur++;
}
return AtomizeChars(cx, charbuf.begin(), charbuf.length());
}
+ bool strictMode() const override final {
+ return strictModeGetter && strictModeGetter->strictMode();
+ }
+
private:
- // These are private because they should only be called by the tokenizer
- // while tokenizing not by, for example, BytecodeEmitter.
- bool reportStrictModeError(unsigned errorNumber, ...);
- bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
-
void setInvalidTemplateEscape(uint32_t offset, InvalidEscapeType type) {
MOZ_ASSERT(type != InvalidEscapeType::None);
if (invalidTemplateEscapeType != InvalidEscapeType::None)
return;
invalidTemplateEscapeOffset = offset;
invalidTemplateEscapeType = type;
}
- void reportInvalidEscapeError(uint32_t offset, InvalidEscapeType type) {
- switch (type) {
- case InvalidEscapeType::None:
- MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType");
- return;
- case InvalidEscapeType::Hexadecimal:
- errorAt(offset, JSMSG_MALFORMED_ESCAPE, "hexadecimal");
- return;
- case InvalidEscapeType::Unicode:
- errorAt(offset, JSMSG_MALFORMED_ESCAPE, "Unicode");
- return;
- case InvalidEscapeType::UnicodeOverflow:
- errorAt(offset, JSMSG_UNICODE_OVERFLOW, "escape sequence");
- return;
- case InvalidEscapeType::Octal:
- errorAt(offset, JSMSG_DEPRECATED_OCTAL);
- return;
- }
- }
static JSAtom* atomize(JSContext* cx, CharBuffer& cb);
MOZ_MUST_USE bool putIdentInTokenbuf(const char16_t* identStart);
struct Flags
{
bool isEOF:1; // Hit end of file.
bool isDirtyLine:1; // Non-whitespace since start of line.
@@ -643,17 +582,17 @@ class MOZ_STACK_CLASS TokenStream
// If lookahead != 0, we have scanned ahead at least one token, and
// |lineno| is the line that the furthest-scanned token ends on. If
// it's the same as the line that the current token ends on, that's a
// stronger condition than what we are looking for, and we don't need
// to return TOK_EOL.
if (lookahead != 0) {
bool onThisLine;
if (!srcCoords.isOnThisLine(curr.pos.end, lineno, &onThisLine))
- return reportError(JSMSG_OUT_OF_MEMORY);
+ return ReportTokenError(this, JSMSG_OUT_OF_MEMORY);
if (onThisLine) {
MOZ_ASSERT(!flags.hadError);
verifyConsistentModifier(modifier, nextToken());
*ttp = nextToken().type;
return true;
}
}
@@ -757,97 +696,36 @@ class MOZ_STACK_CLASS TokenStream
bool hasSourceMapURL() const {
return sourceMapURL_ != nullptr;
}
char16_t* sourceMapURL() {
return sourceMapURL_.get();
}
- // This class maps a userbuf offset (which is 0-indexed) to a line number
- // (which is 1-indexed) and a column index (which is 0-indexed).
- class SourceCoords
- {
- // For a given buffer holding source code, |lineStartOffsets_| has one
- // element per line of source code, plus one sentinel element. Each
- // non-sentinel element holds the buffer offset for the start of the
- // corresponding line of source code. For this example script:
- //
- // 1 // xyz [line starts at offset 0]
- // 2 var x; [line starts at offset 7]
- // 3 [line starts at offset 14]
- // 4 var y; [line starts at offset 15]
- //
- // |lineStartOffsets_| is:
- //
- // [0, 7, 14, 15, MAX_PTR]
- //
- // To convert a "line number" to a "line index" (i.e. an index into
- // |lineStartOffsets_|), subtract |initialLineNum_|. E.g. line 3's
- // line index is (3 - initialLineNum_), which is 2. Therefore
- // lineStartOffsets_[2] holds the buffer offset for the start of line 3,
- // which is 14. (Note that |initialLineNum_| is often 1, but not
- // always.)
- //
- // The first element is always 0, and the last element is always the
- // MAX_PTR sentinel.
- //
- // offset-to-line/column lookups are O(log n) in the worst case (binary
- // search), but in practice they're heavily clustered and we do better
- // than that by using the previous lookup's result (lastLineIndex_) as
- // a starting point.
- //
- // Checking if an offset lies within a particular line number
- // (isOnThisLine()) is O(1).
- //
- Vector<uint32_t, 128> lineStartOffsets_;
- uint32_t initialLineNum_;
+ SourceCoords srcCoords;
+ const SourceCoords& sourceCoords() const override final {
+ return srcCoords;
+ }
+ unsigned lineNumber() const override final {
+ return lineno;
+ }
- // This is mutable because it's modified on every search, but that fact
- // isn't visible outside this class.
- mutable uint32_t lastLineIndex_;
-
- uint32_t lineIndexOf(uint32_t offset) const;
-
- static const uint32_t MAX_PTR = UINT32_MAX;
-
- uint32_t lineIndexToNum(uint32_t lineIndex) const { return lineIndex + initialLineNum_; }
- uint32_t lineNumToIndex(uint32_t lineNum) const { return lineNum - initialLineNum_; }
-
- public:
- SourceCoords(JSContext* cx, uint32_t ln);
-
- MOZ_MUST_USE bool add(uint32_t lineNum, uint32_t lineStartOffset);
- MOZ_MUST_USE bool fill(const SourceCoords& other);
-
- bool isOnThisLine(uint32_t offset, uint32_t lineNum, bool* onThisLine) const {
- uint32_t lineIndex = lineNumToIndex(lineNum);
- if (lineIndex + 1 >= lineStartOffsets_.length()) // +1 due to sentinel
- return false;
- *onThisLine = lineStartOffsets_[lineIndex] <= offset &&
- offset < lineStartOffsets_[lineIndex + 1];
- return true;
- }
-
- uint32_t lineNum(uint32_t offset) const;
- uint32_t columnIndex(uint32_t offset) const;
- void lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum, uint32_t* columnIndex) const;
- };
-
- SourceCoords srcCoords;
+ // Add an error message displaying the context of a syntax error.
+ bool fillComplaint(JSErrorReport& err) override final;
JSAtomState& names() const {
return cx->names();
}
- JSContext* context() const {
+ virtual JSContext* context() const override final {
return cx;
}
- const ReadOnlyCompileOptions& options() const {
+ virtual const ReadOnlyCompileOptions& options() const override final {
return options_;
}
private:
// This is the low-level interface to the JS source code buffer. It just
// gets raw chars, basically. TokenStreams functions are layered on top
// and do some extra stuff like converting all EOL sequences to '\n',
// tracking the line number, and setting |flags.isEOF|. (The "raw" in "raw
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -241,17 +241,17 @@ RegExpParser<CharT>::RegExpParser(fronte
Advance();
}
template <typename CharT>
RegExpTree*
RegExpParser<CharT>::ReportError(unsigned errorNumber, const char* param /* = nullptr */)
{
gc::AutoSuppressGC suppressGC(ts.context());
- ts.reportError(errorNumber, param);
+ mozilla::Unused << ReportTokenError(&ts, errorNumber, param);
return nullptr;
}
template <typename CharT>
void
RegExpParser<CharT>::Advance()
{
if (next_pos_ < end_) {
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -176,16 +176,17 @@ UNIFIED_SOURCES += [
'builtin/WeakMapObject.cpp',
'builtin/WeakSetObject.cpp',
'devtools/sharkctl.cpp',
'ds/Bitmap.cpp',
'ds/LifoAlloc.cpp',
'ds/MemoryProtectionExceptionHandler.cpp',
'frontend/BytecodeCompiler.cpp',
'frontend/BytecodeEmitter.cpp',
+ 'frontend/ErrorReport.cpp',
'frontend/FoldConstants.cpp',
'frontend/NameFunctions.cpp',
'frontend/ParseNode.cpp',
'frontend/TokenStream.cpp',
'gc/Allocator.cpp',
'gc/AtomMarking.cpp',
'gc/Barrier.cpp',
'gc/GCTrace.cpp',
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -43,16 +43,17 @@
#include "wasm/WasmGenerator.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmJS.h"
#include "wasm/WasmSerialize.h"
#include "wasm/WasmValidate.h"
#include "jsobjinlines.h"
+#include "frontend/ErrorReport.h"
#include "frontend/ParseNode-inl.h"
#include "vm/ArrayBufferObject-inl.h"
using namespace js;
using namespace js::frontend;
using namespace js::jit;
using namespace js::wasm;
@@ -1731,19 +1732,20 @@ class MOZ_STACK_CLASS ModuleValidator
errorString_(nullptr),
errorOffset_(UINT32_MAX),
errorOverRecursed_(false)
{}
~ModuleValidator() {
if (errorString_) {
MOZ_ASSERT(errorOffset_ != UINT32_MAX);
- tokenStream().reportAsmJSError(errorOffset_,
- JSMSG_USE_ASM_TYPE_FAIL,
- errorString_.get());
+ OffsetErrorContext context = tokenStream().atOffset(errorOffset_);
+ ReportAsmJSError(&context,
+ JSMSG_USE_ASM_TYPE_FAIL,
+ errorString_.get());
}
if (errorOverRecursed_)
ReportOverRecursed(cx_);
}
bool init() {
asmJSMetadata_ = cx_->new_<AsmJSMetadata>();
if (!asmJSMetadata_)