Bug 1847469 - Part 1: Add JS::ColumnNumberZeroOrigin, JS::ColumnNumberOneOrigin, JS::WasmFunctionIndex, JS::ColumnNumberOffset and compound types. r=iain

Differential Revision: https://phabricator.services.mozilla.com/D185739
This commit is contained in:
Tooru Fujisawa 2023-08-16 17:31:36 +00:00
parent 263a227ec9
commit ecb285cbe7
12 changed files with 569 additions and 64 deletions

523
js/public/ColumnNumber.h Normal file
View File

@ -0,0 +1,523 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
// [SMDOC] Column numbers
//
// Inside SpiderMonkey, column numbers are represented as 32-bit unsigned
// integers, either with 0-origin or 1-origin. Also, some parts of the engine
// use the highest bit of a column number as a tag to indicate Wasm frame.
//
// In a 0-origin context, column 0 is the first character of the line.
// In a 1-origin context, column 1 is the first character of the line,
// for example:
//
// function foo() { ... }
// ^ ^
// 0-origin: 0 15
// 1-origin: 1 16
//
// The column 0 in 1-origin is an invalid sentinel value used in some places,
// such as differential testing (bug 1848467).
//
// These classes help figuring out which origin and tag the column number
// uses, and also help converting between them.
//
// Eventually all SpiderMonkey API and internal use should switch to 1-origin
// (bug 1144340).
#ifndef js_ColumnNumber_h
#define js_ColumnNumber_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/Attributes.h" // MOZ_IMPLICIT
#include <limits> // std::numeric_limits
#include <stdint.h> // uint32_t
namespace JS {
// Wasm function index.
//
// This class is used as parameter or return type of
// TaggedColumnNumberWithOrigin class below.
struct WasmFunctionIndex {
// TaggedColumnNumberWithOrigin uses the highest bit as a tag.
static constexpr uint32_t Limit = std::numeric_limits<int32_t>::max() / 2;
// For wasm frames, the function index is returned as the column with the
// high bit set. In paths that format error stacks into strings, this
// information can be used to synthesize a proper wasm frame. But when raw
// column numbers are handed out, we just fix them to the first column to
// avoid confusion.
static constexpr uint32_t DefaultBinarySourceColumnNumberZeroOrigin = 0;
static constexpr uint32_t DefaultBinarySourceColumnNumberOneOrigin = 1;
private:
uint32_t value_ = 0;
public:
constexpr WasmFunctionIndex() = default;
constexpr WasmFunctionIndex(const WasmFunctionIndex& other) = default;
inline explicit WasmFunctionIndex(uint32_t value) : value_(value) {
MOZ_ASSERT(valid());
}
uint32_t value() const { return value_; }
bool valid() const { return value_ <= Limit; }
};
// The offset between 2 column numbers.
struct ColumnNumberOffset {
private:
int32_t value_ = 0;
public:
constexpr ColumnNumberOffset() = default;
constexpr ColumnNumberOffset(const ColumnNumberOffset& other) = default;
inline explicit ColumnNumberOffset(int32_t value) : value_(value) {}
static constexpr ColumnNumberOffset zero() { return ColumnNumberOffset(); }
bool operator==(const ColumnNumberOffset& rhs) const {
return value_ == rhs.value_;
}
bool operator!=(const ColumnNumberOffset& rhs) const {
return !(*this == rhs);
}
int32_t value() const { return value_; }
};
namespace detail {
template <typename T>
struct TaggedColumnNumberWithOrigin;
// Shared implementation of {,Limited}ColumnNumber{Zero,One}Origin classes.
//
// Origin can be either 0 or 1.
// LimitValue being 0 means there's no limit.
template <uint32_t Origin, uint32_t LimitValue = 0>
struct ColumnNumberWithOrigin {
static_assert(Origin == 0 || Origin == 1);
protected:
uint32_t value_ = Origin;
template <typename T>
friend struct TaggedColumnNumberWithOrigin;
public:
constexpr ColumnNumberWithOrigin() = default;
ColumnNumberWithOrigin(
const ColumnNumberWithOrigin<Origin, LimitValue>& other) = default;
explicit ColumnNumberWithOrigin(uint32_t value) : value_(value) {
MOZ_ASSERT(valid());
}
static constexpr ColumnNumberWithOrigin<Origin, LimitValue> zero() {
return ColumnNumberWithOrigin<Origin, LimitValue>();
}
bool operator==(const ColumnNumberWithOrigin<Origin, LimitValue>& rhs) const {
return value_ == rhs.value_;
}
bool operator!=(const ColumnNumberWithOrigin<Origin, LimitValue>& rhs) const {
return !(*this == rhs);
}
ColumnNumberWithOrigin<Origin, LimitValue> operator+(
const ColumnNumberOffset& offset) const {
MOZ_ASSERT(valid());
MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
return ColumnNumberWithOrigin<Origin, LimitValue>(value_ + offset.value());
}
ColumnNumberWithOrigin<Origin, LimitValue> operator-(
const ColumnNumberOffset& offset) const {
MOZ_ASSERT(valid());
MOZ_ASSERT(ptrdiff_t(value_) - offset.value() >= 0);
return ColumnNumberWithOrigin<Origin, LimitValue>(value_ - offset.value());
}
ColumnNumberOffset operator-(
const ColumnNumberWithOrigin<Origin, LimitValue>& other) const {
MOZ_ASSERT(valid());
return ColumnNumberOffset(int32_t(value_) -
int32_t(other.zeroOriginValue()));
}
ColumnNumberWithOrigin<Origin, LimitValue>& operator+=(
const ColumnNumberOffset& offset) {
MOZ_ASSERT(valid());
MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
value_ += offset.value();
MOZ_ASSERT(valid());
return *this;
}
ColumnNumberWithOrigin<Origin, LimitValue>& operator-=(
const ColumnNumberOffset& offset) {
MOZ_ASSERT(valid());
MOZ_ASSERT(ptrdiff_t(value_) - offset.value() >= 0);
value_ -= offset.value();
MOZ_ASSERT(valid());
return *this;
}
bool operator<(const ColumnNumberWithOrigin<Origin, LimitValue>& rhs) const {
MOZ_ASSERT(valid());
MOZ_ASSERT(rhs.valid());
return value_ < rhs.value_;
}
bool operator<=(const ColumnNumberWithOrigin<Origin, LimitValue>& rhs) const {
MOZ_ASSERT(valid());
MOZ_ASSERT(rhs.valid());
return value_ <= rhs.value_;
}
bool operator>(const ColumnNumberWithOrigin<Origin, LimitValue>& rhs) const {
MOZ_ASSERT(valid());
MOZ_ASSERT(rhs.valid());
return value_ > rhs.value_;
}
bool operator>=(const ColumnNumberWithOrigin<Origin, LimitValue>& rhs) const {
MOZ_ASSERT(valid());
MOZ_ASSERT(rhs.valid());
return value_ >= rhs.value_;
}
// Convert between origins.
uint32_t zeroOriginValue() const {
MOZ_ASSERT(valid());
if constexpr (Origin == 0) {
return value_;
}
if (value_ == 0) {
// 1-origin uses 0 as special value, but 0-origin doesn't have it.
return 0;
}
return value_ - 1;
}
uint32_t oneOriginValue() const {
MOZ_ASSERT(valid());
if constexpr (Origin == 0) {
return value_ + 1;
}
return value_;
}
uint32_t* addressOfValueForTranscode() { return &value_; }
bool valid() const {
if constexpr (LimitValue == 0) {
return true;
}
return value_ <= LimitValue;
}
};
// See the comment for LimitedColumnNumberZeroOrigin below
static constexpr uint32_t ColumnNumberZeroOriginLimit =
std::numeric_limits<int32_t>::max() / 2 - 1;
static constexpr uint32_t ColumnNumberOneOriginLimit =
std::numeric_limits<int32_t>::max() / 2;
} // namespace detail
// Column number in 0-origin with 31-bit limit.
//
// Various parts of the engine requires the column number be represented in
// 31 bits.
//
// See:
// * TaggedColumnNumberWithOrigin
// * TokenStreamAnyChars::checkOptions
// * SourceNotes::isRepresentable
// * WasmFrameIter::computeLine
struct LimitedColumnNumberZeroOrigin
: public detail::ColumnNumberWithOrigin<
0, detail::ColumnNumberZeroOriginLimit> {
private:
using Base =
detail::ColumnNumberWithOrigin<0, detail::ColumnNumberZeroOriginLimit>;
public:
static constexpr uint32_t Limit = detail::ColumnNumberZeroOriginLimit;
static_assert(uint32_t(Limit + Limit) > Limit,
"Adding Limit should not overflow");
using Base::Base;
LimitedColumnNumberZeroOrigin() = default;
LimitedColumnNumberZeroOrigin(const LimitedColumnNumberZeroOrigin& other) =
default;
MOZ_IMPLICIT LimitedColumnNumberZeroOrigin(const Base& other) : Base(other) {}
explicit LimitedColumnNumberZeroOrigin(
const detail::ColumnNumberWithOrigin<
1, detail::ColumnNumberOneOriginLimit>& other)
: Base(other.zeroOriginValue()) {}
static LimitedColumnNumberZeroOrigin limit() {
return LimitedColumnNumberZeroOrigin(Limit);
}
// Convert from column number without limit.
// Column number above the limit is saturated to the limit.
static LimitedColumnNumberZeroOrigin fromUnlimited(uint32_t value) {
if (value > Limit) {
return LimitedColumnNumberZeroOrigin(Limit);
}
return LimitedColumnNumberZeroOrigin(value);
}
static LimitedColumnNumberZeroOrigin fromUnlimited(
const ColumnNumberWithOrigin<0, 0>& value) {
return fromUnlimited(value.zeroOriginValue());
}
};
// Column number in 1-origin with 31-bit limit.
struct LimitedColumnNumberOneOrigin
: public detail::ColumnNumberWithOrigin<
1, detail::ColumnNumberOneOriginLimit> {
private:
using Base =
detail::ColumnNumberWithOrigin<1, detail::ColumnNumberOneOriginLimit>;
public:
static constexpr uint32_t Limit = detail::ColumnNumberOneOriginLimit;
static_assert(uint32_t(Limit + Limit) > Limit,
"Adding Limit should not overflow");
using Base::Base;
LimitedColumnNumberOneOrigin() = default;
LimitedColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other) =
default;
MOZ_IMPLICIT LimitedColumnNumberOneOrigin(const Base& other) : Base(other) {}
explicit LimitedColumnNumberOneOrigin(
const detail::ColumnNumberWithOrigin<
0, detail::ColumnNumberZeroOriginLimit>& other)
: Base(other.oneOriginValue()) {}
static LimitedColumnNumberOneOrigin limit() {
return LimitedColumnNumberOneOrigin(Limit);
}
static LimitedColumnNumberOneOrigin fromUnlimited(uint32_t value) {
if (value > Limit) {
return LimitedColumnNumberOneOrigin(Limit);
}
return LimitedColumnNumberOneOrigin(value);
}
static LimitedColumnNumberOneOrigin fromUnlimited(
const ColumnNumberWithOrigin<1, 0>& value) {
return fromUnlimited(value.oneOriginValue());
}
};
// Column number in 0-origin.
struct ColumnNumberZeroOrigin : public detail::ColumnNumberWithOrigin<0> {
private:
using Base = detail::ColumnNumberWithOrigin<0>;
public:
using Base::Base;
ColumnNumberZeroOrigin() = default;
ColumnNumberZeroOrigin(const ColumnNumberZeroOrigin& other) = default;
MOZ_IMPLICIT ColumnNumberZeroOrigin(const Base& other) : Base(other) {}
explicit ColumnNumberZeroOrigin(
const detail::ColumnNumberWithOrigin<1>& other)
: Base(other.zeroOriginValue()) {}
explicit ColumnNumberZeroOrigin(const LimitedColumnNumberZeroOrigin& other)
: Base(other.zeroOriginValue()) {}
explicit ColumnNumberZeroOrigin(const LimitedColumnNumberOneOrigin& other)
: Base(other.zeroOriginValue()) {}
};
// Column number in 1-origin.
struct ColumnNumberOneOrigin : public detail::ColumnNumberWithOrigin<1> {
private:
using Base = detail::ColumnNumberWithOrigin<1>;
public:
using Base::Base;
ColumnNumberOneOrigin() = default;
ColumnNumberOneOrigin(const ColumnNumberOneOrigin& other) = default;
MOZ_IMPLICIT ColumnNumberOneOrigin(const Base& other) : Base(other) {}
explicit ColumnNumberOneOrigin(const detail::ColumnNumberWithOrigin<0>& other)
: Base(other.oneOriginValue()) {}
explicit ColumnNumberOneOrigin(const LimitedColumnNumberZeroOrigin& other)
: Base(other.oneOriginValue()) {}
explicit ColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other)
: Base(other.oneOriginValue()) {}
};
namespace detail {
// Either column number with limit, or Wasm function index.
//
// In order to pass the Wasm frame's (url, bytecode-offset, func-index) tuple
// through the existing (url, line, column) tuple, it tags the highest bit of
// the column to indicate "this is a wasm frame".
//
// When knowing clients see this bit, they shall render the tuple
// (url, line, column|bit) as "url:wasm-function[column]:0xline" according
// to the WebAssembly Web API's Developer-Facing Display Conventions.
// https://webassembly.github.io/spec/web-api/index.html#conventions
// The wasm bytecode offset continues to be passed as the JS line to avoid
// breaking existing devtools code written when this used to be the case.
//
// 0b0YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY LimitedColumnNumberT
// 0b1YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY WasmFunctionIndex
//
// The tagged colum number shouldn't escape the JS engine except for the
// following places:
// * SavedFrame API which can directly access WASM frame's info
// * ubi::Node API which can also directly access WASM frame's info
template <typename LimitedColumnNumberT>
struct TaggedColumnNumberWithOrigin {
static constexpr uint32_t WasmFunctionTag = 1u << 31;
static_assert((WasmFunctionIndex::Limit & WasmFunctionTag) == 0);
static_assert((LimitedColumnNumberT::Limit & WasmFunctionTag) == 0);
protected:
uint32_t value_ = 0;
explicit TaggedColumnNumberWithOrigin(uint32_t value) : value_(value) {}
public:
constexpr TaggedColumnNumberWithOrigin() = default;
TaggedColumnNumberWithOrigin(
const TaggedColumnNumberWithOrigin<LimitedColumnNumberT>& other) =
default;
explicit TaggedColumnNumberWithOrigin(const LimitedColumnNumberT& other)
: value_(other.value_) {
MOZ_ASSERT(isLimitedColumnNumber());
}
explicit TaggedColumnNumberWithOrigin(const WasmFunctionIndex& other)
: value_(other.value() | WasmFunctionTag) {
MOZ_ASSERT(isWasmFunctionIndex());
}
static TaggedColumnNumberWithOrigin<LimitedColumnNumberT> fromRaw(
uint32_t value) {
return TaggedColumnNumberWithOrigin<LimitedColumnNumberT>(value);
}
bool operator==(
const TaggedColumnNumberWithOrigin<LimitedColumnNumberT>& rhs) const {
return value_ == rhs.value_;
}
bool operator!=(
const TaggedColumnNumberWithOrigin<LimitedColumnNumberT>& rhs) const {
return !(*this == rhs);
}
bool isLimitedColumnNumber() const { return !isWasmFunctionIndex(); }
bool isWasmFunctionIndex() const { return !!(value_ & WasmFunctionTag); }
LimitedColumnNumberT toLimitedColumnNumber() const {
MOZ_ASSERT(isLimitedColumnNumber());
return LimitedColumnNumberT(value_);
}
WasmFunctionIndex toWasmFunctionIndex() const {
MOZ_ASSERT(isWasmFunctionIndex());
return WasmFunctionIndex(value_ & ~WasmFunctionTag);
}
uint32_t zeroOriginValue() const {
return isWasmFunctionIndex()
? WasmFunctionIndex::DefaultBinarySourceColumnNumberZeroOrigin
: toLimitedColumnNumber().zeroOriginValue();
}
uint32_t oneOriginValue() const {
return isWasmFunctionIndex()
? WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin
: toLimitedColumnNumber().oneOriginValue();
}
uint32_t rawValue() const { return value_; }
uint32_t* addressOfValueForTranscode() { return &value_; }
};
} // namespace detail
class TaggedColumnNumberZeroOrigin
: public detail::TaggedColumnNumberWithOrigin<
LimitedColumnNumberZeroOrigin> {
private:
using Base =
detail::TaggedColumnNumberWithOrigin<LimitedColumnNumberZeroOrigin>;
public:
using Base::Base;
using Base::WasmFunctionTag;
TaggedColumnNumberZeroOrigin() = default;
TaggedColumnNumberZeroOrigin(const TaggedColumnNumberZeroOrigin& other) =
default;
MOZ_IMPLICIT TaggedColumnNumberZeroOrigin(const Base& other) : Base(other) {}
explicit TaggedColumnNumberZeroOrigin(
const detail::TaggedColumnNumberWithOrigin<LimitedColumnNumberOneOrigin>&
other)
: Base(other.isWasmFunctionIndex() ? other.rawValue()
: other.zeroOriginValue()) {}
};
class TaggedColumnNumberOneOrigin : public detail::TaggedColumnNumberWithOrigin<
LimitedColumnNumberOneOrigin> {
private:
using Base =
detail::TaggedColumnNumberWithOrigin<LimitedColumnNumberOneOrigin>;
public:
using Base::Base;
using Base::WasmFunctionTag;
TaggedColumnNumberOneOrigin() = default;
TaggedColumnNumberOneOrigin(const TaggedColumnNumberOneOrigin& other) =
default;
MOZ_IMPLICIT TaggedColumnNumberOneOrigin(const Base& other) : Base(other) {}
explicit TaggedColumnNumberOneOrigin(
const detail::TaggedColumnNumberWithOrigin<LimitedColumnNumberZeroOrigin>&
other)
: Base(other.isWasmFunctionIndex() ? other.rawValue()
: other.oneOriginValue()) {}
static TaggedColumnNumberOneOrigin forDifferentialTesting() {
return TaggedColumnNumberOneOrigin(LimitedColumnNumberOneOrigin(0));
}
};
} // namespace JS
#endif /* js_ColumnNumber_h */

View File

@ -25,6 +25,7 @@
#include "gc/Zone.h" // for Zone
#include "gc/ZoneAllocator.h" // for AddCellMemory
#include "js/CallArgs.h" // for CallArgs, CallArgsFromVp
#include "js/ColumnNumber.h" // JS::WasmFunctionIndex
#include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_*
#include "js/GCVariant.h" // for GCVariant
#include "js/HeapAPI.h" // for GCCellPtr
@ -372,9 +373,11 @@ bool DebuggerScript::CallData::getStartLine() {
}
bool DebuggerScript::CallData::getStartColumn() {
args.rval().setNumber(
referent.get().match([](BaseScript*& s) { return s->column(); },
[](WasmInstanceObject*&) { return (uint32_t)0; }));
args.rval().setNumber(referent.get().match(
[](BaseScript*& s) { return s->column(); },
[](WasmInstanceObject*&) {
return JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberZeroOrigin;
}));
return true;
}

View File

@ -17,7 +17,8 @@
#include "debugger/Debugger.h" // for DebuggerSourceReferent, Debugger
#include "debugger/Script.h" // for DebuggerScript
#include "frontend/FrontendContext.h" // for AutoReportFrontendContext
#include "gc/Tracer.h" // for TraceManuallyBarrieredCrossCompartmentEdge
#include "gc/Tracer.h" // for TraceManuallyBarrieredCrossCompartmentEdge
#include "js/ColumnNumber.h" // JS::WasmFunctionIndex
#include "js/CompilationAndEvaluation.h" // for Compile
#include "js/ErrorReport.h" // for JS_ReportErrorASCII, JS_ReportErrorNumberASCII
#include "js/experimental/TypedData.h" // for JS_NewUint8Array
@ -353,7 +354,9 @@ class DebuggerSourceGetStartColumnMatcher {
ScriptSource* ss = sourceObject->source();
return ss->startColumn();
}
ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
return JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberZeroOrigin;
}
};
bool DebuggerSource::CallData::getStartColumn() {

View File

@ -58,9 +58,10 @@
#include "frontend/TDZCheckCache.h" // TDZCheckCache
#include "frontend/TryEmitter.h" // TryEmitter
#include "frontend/WhileEmitter.h" // WhileEmitter
#include "js/friend/ErrorMessages.h" // JSMSG_*
#include "js/friend/StackLimits.h" // AutoCheckRecursionLimit
#include "util/StringBuffer.h" // StringBuffer
#include "js/ColumnNumber.h" // JS::LimitedColumnNumberZeroOrigin
#include "js/friend/ErrorMessages.h" // JSMSG_*
#include "js/friend/StackLimits.h" // AutoCheckRecursionLimit
#include "util/StringBuffer.h" // StringBuffer
#include "vm/BytecodeUtil.h" // JOF_*, IsArgOp, IsLocalOp, SET_UINT24, SET_ICINDEX, BytecodeFallsThrough, BytecodeIsJumpTarget
#include "vm/CompletionKind.h" // CompletionKind
#include "vm/FunctionPrefixKind.h" // FunctionPrefixKind
@ -609,11 +610,13 @@ bool BytecodeEmitter::updateSourceCoordNotes(uint32_t offset) {
}
uint32_t columnIndex = errorReporter().columnAt(offset);
MOZ_ASSERT(columnIndex <= ColumnLimit);
MOZ_ASSERT(columnIndex <= JS::LimitedColumnNumberZeroOrigin::Limit);
// Assert colspan is always representable.
static_assert((0 - ptrdiff_t(ColumnLimit)) >= SrcNote::ColSpan::MinColSpan);
static_assert((ptrdiff_t(ColumnLimit) - 0) <= SrcNote::ColSpan::MaxColSpan);
static_assert((0 - ptrdiff_t(JS::LimitedColumnNumberZeroOrigin::Limit)) >=
SrcNote::ColSpan::MinColSpan);
static_assert((ptrdiff_t(JS::LimitedColumnNumberZeroOrigin::Limit) - 0) <=
SrcNote::ColSpan::MaxColSpan);
ptrdiff_t colspan =
ptrdiff_t(columnIndex) - ptrdiff_t(bytecodeSection().lastColumn());

View File

@ -35,6 +35,7 @@
#include "frontend/ParserAtom.h"
#include "frontend/ReservedWords.h"
#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
#include "js/ColumnNumber.h" // JS::LimitedColumnNumberZeroOrigin
#include "js/ErrorReport.h" // JSErrorBase
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/Printf.h" // JS_smprintf
@ -453,7 +454,7 @@ TokenStreamSpecific<Unit, AnyCharsAccess>::TokenStreamSpecific(
bool TokenStreamAnyChars::checkOptions() {
// Constrain starting columns to where they will saturate.
if (options().column > ColumnLimit) {
if (options().column > JS::LimitedColumnNumberZeroOrigin::Limit) {
reportErrorNoOffset(JSMSG_BAD_COLUMN_NUMBER);
return false;
}
@ -832,19 +833,16 @@ uint32_t GeneralTokenStreamChars<Unit, AnyCharsAccess>::computeColumn(
anyChars.computePartialColumn(lineToken, offset, this->sourceUnits);
if (lineToken.isFirstLine()) {
if (column > ColumnLimit) {
return ColumnLimit;
if (column > JS::LimitedColumnNumberZeroOrigin::Limit) {
return JS::LimitedColumnNumberZeroOrigin::Limit;
}
static_assert(uint32_t(ColumnLimit + ColumnLimit) > ColumnLimit,
"Adding ColumnLimit should not overflow");
uint32_t firstLineOffset = anyChars.options_.column;
column += firstLineOffset;
}
if (column > ColumnLimit) {
return ColumnLimit;
if (column > JS::LimitedColumnNumberZeroOrigin::Limit) {
return JS::LimitedColumnNumberZeroOrigin::Limit;
}
return column;

View File

@ -222,16 +222,6 @@ class FrontendContext;
namespace frontend {
// Saturate column number at a limit that can be represented in various parts of
// the engine. Source locations beyond this point will report at the limit
// column instead.
//
// See:
// - TokenStreamAnyChars::checkOptions
// - ColSpan::isRepresentable
// - WasmFrameIter::computeLine
static constexpr uint32_t ColumnLimit = std::numeric_limits<int32_t>::max() / 2;
// True if str is a keyword.
bool IsKeyword(TaggedParserAtomIndex atom);

View File

@ -35,7 +35,7 @@ if (helperThreadCount() > 0) {
// Check handling of columns near the limit of our ability to represent them.
// (This is hardly thorough, but since web content can't set column numbers,
// it's probably not worth it to be thorough.)
const maxColumn = Math.pow(2, 30) - 1;
const maxColumn = Math.pow(2, 30) - 2;
assertEq(evaluate("saveStack().column", { columnNumber: maxColumn }),
maxColumn + 1);

View File

@ -132,6 +132,7 @@ EXPORTS.js += [
"../public/CallNonGenericMethod.h",
"../public/CharacterEncoding.h",
"../public/Class.h",
"../public/ColumnNumber.h",
"../public/ComparisonOperators.h",
"../public/CompilationAndEvaluation.h",
"../public/CompileOptions.h",

View File

@ -19,7 +19,8 @@
#include "gc/GCContext.h"
#include "gc/HashUtil.h"
#include "js/CharacterEncoding.h"
#include "js/ErrorReport.h" // JSErrorBase
#include "js/ColumnNumber.h" // JS::WasmFunctionIndex, JS::TaggedColumnNumberZeroOrigin, JS::TaggedColumnNumberOneOrigin
#include "js/ErrorReport.h" // JSErrorBase
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty
#include "js/PropertySpec.h"
@ -591,18 +592,15 @@ bool SavedFrame::isSelfHosted(JSContext* cx) {
}
bool SavedFrame::isWasm() {
// See WasmFrameIter::computeLine() comment.
return bool(getColumn() & wasm::WasmFrameIter::ColumnBit);
return bool(getColumn() & JS::TaggedColumnNumberOneOrigin::WasmFunctionTag);
}
uint32_t SavedFrame::wasmFuncIndex() {
// See WasmFrameIter::computeLine() comment.
MOZ_ASSERT(isWasm());
return getColumn() & ~wasm::WasmFrameIter::ColumnBit;
return getColumn() & ~JS::TaggedColumnNumberOneOrigin::WasmFunctionTag;
}
uint32_t SavedFrame::wasmBytecodeOffset() {
// See WasmFrameIter::computeLine() comment.
MOZ_ASSERT(isWasm());
return getLine();
}
@ -1992,13 +1990,8 @@ UniqueChars BuildUTF8StackString(JSContext* cx, JSPrincipals* principals,
}
uint32_t FixupMaybeWASMColumnForDisplay(uint32_t column) {
// As described in WasmFrameIter::computeLine(), for wasm frames, the
// function index is returned as the column with the high bit set. In paths
// that format error stacks into strings, this information can be used to
// synthesize a proper wasm frame. But when raw column numbers are handed
// out, we just fix them to 1 to avoid confusion.
if (column & wasm::WasmFrameIter::ColumnBit) {
return 1;
if (column & JS::TaggedColumnNumberZeroOrigin::WasmFunctionTag) {
return JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin;
}
return JSErrorBase::fromZeroOriginToOneOrigin(column);

View File

@ -23,6 +23,7 @@
#include "debugger/Debugger.h"
#include "ds/Sort.h"
#include "jit/MacroAssembler.h"
#include "js/ColumnNumber.h" // JS::WasmFunctionIndex
#include "wasm/WasmJS.h"
#include "wasm/WasmStubs.h"
#include "wasm/WasmValidate.h"
@ -59,8 +60,6 @@ void DebugState::finalize(JS::GCContext* gcx) {
}
}
static const uint32_t DefaultBinarySourceColumnNumber = 1;
static const CallSite* SlowCallSiteSearchByOffset(const MetadataTier& metadata,
uint32_t offset) {
for (const CallSite& callSite : metadata.callSites) {
@ -84,8 +83,10 @@ bool DebugState::getAllColumnOffsets(Vector<ExprLoc>* offsets) {
continue;
}
uint32_t offset = callSite.lineOrBytecode();
if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber,
offset)) {
if (!offsets->emplaceBack(
offset,
JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin,
offset)) {
return false;
}
}
@ -98,7 +99,7 @@ bool DebugState::getOffsetLocation(uint32_t offset, size_t* lineno,
return false;
}
*lineno = offset;
*column = DefaultBinarySourceColumnNumber;
*column = JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin;
return true;
}

View File

@ -19,6 +19,7 @@
#include "wasm/WasmFrameIter.h"
#include "jit/JitFrames.h"
#include "js/ColumnNumber.h" // JS::WasmFunctionIndex, JS::TaggedColumnNumberOneOrigin
#include "vm/JitActivation.h" // js::jit::JitActivation
#include "vm/JSContext.h"
#include "wasm/WasmDebugFrame.h"
@ -286,26 +287,16 @@ uint32_t WasmFrameIter::funcIndex() const {
unsigned WasmFrameIter::computeLine(uint32_t* column) const {
if (instance()->isAsmJS()) {
if (column) {
*column = 1;
*column = JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin;
}
return lineOrBytecode_;
}
// As a terrible hack to avoid changing the tons of places that pass around
// (url, line, column) tuples to instead passing around a Variant that
// stores a (url, func-index, bytecode-offset) tuple for wasm frames,
// wasm stuffs its tuple into the existing (url, line, column) tuple,
// tagging the high bit of the column to indicate "this is a wasm frame".
// When knowing clients see this bit, they shall render the tuple
// (url, line, column|bit) as "url:wasm-function[column]:0xline" according
// to the WebAssembly Web API's Developer-Facing Display Conventions.
// https://webassembly.github.io/spec/web-api/index.html#conventions
// The wasm bytecode offset continues to be passed as the JS line to avoid
// breaking existing devtools code written when this used to be the case.
MOZ_ASSERT(!(codeRange_->funcIndex() & ColumnBit));
MOZ_ASSERT(!(codeRange_->funcIndex() &
JS::TaggedColumnNumberOneOrigin::WasmFunctionTag));
if (column) {
*column = codeRange_->funcIndex() | ColumnBit;
*column = codeRange_->funcIndex() |
JS::TaggedColumnNumberOneOrigin::WasmFunctionTag;
}
return lineOrBytecode_;
}

View File

@ -57,7 +57,6 @@ using RegisterState = JS::ProfilingFrameIterator::RegisterState;
class WasmFrameIter {
public:
enum class Unwind { True, False };
static constexpr uint32_t ColumnBit = 1u << 31;
private:
jit::JitActivation* activation_;