mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 16:22:00 +00:00
48d2408789
The "OneOrigin" name should be kept in the class names and method names until the remaining part of Gecko and DevTools are also migrated to 1-origin, in order to clarify where the conversion is necessary. Differential Revision: https://phabricator.services.mozilla.com/D193379
419 lines
13 KiB
C++
419 lines
13 KiB
C++
/* -*- 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 1-origin 32-bit
|
|
// unsigned integers. Some parts of the engine use the highest bit of a column
|
|
// number as a tag to indicate Wasm frame.
|
|
//
|
|
// These classes help clarifying the origin of the column number, and also
|
|
// figuring out whether the column number uses the wasm's tag or not, and also
|
|
// help converting between them.
|
|
//
|
|
// Also these classes support converting from 0-origin column number.
|
|
//
|
|
// 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
|
|
|
|
#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
|
|
// TaggedColumnNumberOneOrigin class below.
|
|
struct WasmFunctionIndex {
|
|
// TaggedColumnNumberOneOrigin 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 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_; }
|
|
};
|
|
|
|
// The positive offset from certain column number.
|
|
struct ColumnNumberUnsignedOffset {
|
|
private:
|
|
uint32_t value_ = 0;
|
|
|
|
public:
|
|
constexpr ColumnNumberUnsignedOffset() = default;
|
|
constexpr ColumnNumberUnsignedOffset(
|
|
const ColumnNumberUnsignedOffset& other) = default;
|
|
|
|
inline explicit ColumnNumberUnsignedOffset(uint32_t value) : value_(value) {}
|
|
|
|
static constexpr ColumnNumberUnsignedOffset zero() {
|
|
return ColumnNumberUnsignedOffset();
|
|
}
|
|
|
|
ColumnNumberUnsignedOffset operator+(
|
|
const ColumnNumberUnsignedOffset& offset) const {
|
|
return ColumnNumberUnsignedOffset(value_ + offset.value());
|
|
}
|
|
|
|
ColumnNumberUnsignedOffset& operator+=(
|
|
const ColumnNumberUnsignedOffset& offset) {
|
|
value_ += offset.value();
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const ColumnNumberUnsignedOffset& rhs) const {
|
|
return value_ == rhs.value_;
|
|
}
|
|
|
|
bool operator!=(const ColumnNumberUnsignedOffset& rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
uint32_t value() const { return value_; }
|
|
|
|
uint32_t* addressOfValueForTranscode() { return &value_; }
|
|
};
|
|
|
|
struct TaggedColumnNumberOneOrigin;
|
|
|
|
namespace detail {
|
|
|
|
// Shared implementation of {,Limited}ColumnNumberOneOrigin classes.
|
|
//
|
|
// LimitValue being 0 means there's no limit.
|
|
template <uint32_t LimitValue = 0>
|
|
struct MaybeLimitedColumnNumber {
|
|
public:
|
|
static constexpr uint32_t OriginValue = 1;
|
|
|
|
protected:
|
|
uint32_t value_ = OriginValue;
|
|
|
|
friend struct ::JS::TaggedColumnNumberOneOrigin;
|
|
|
|
public:
|
|
constexpr MaybeLimitedColumnNumber() = default;
|
|
MaybeLimitedColumnNumber(const MaybeLimitedColumnNumber& other) = default;
|
|
MaybeLimitedColumnNumber& operator=(const MaybeLimitedColumnNumber& other) =
|
|
default;
|
|
|
|
explicit MaybeLimitedColumnNumber(uint32_t value) : value_(value) {
|
|
MOZ_ASSERT(valid());
|
|
}
|
|
|
|
bool operator==(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
|
|
return value_ == rhs.value_;
|
|
}
|
|
|
|
bool operator!=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
MaybeLimitedColumnNumber<LimitValue> operator+(
|
|
const ColumnNumberOffset& offset) const {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
|
|
return MaybeLimitedColumnNumber<LimitValue>(value_ + offset.value());
|
|
}
|
|
|
|
MaybeLimitedColumnNumber<LimitValue> operator+(
|
|
const ColumnNumberUnsignedOffset& offset) const {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
|
|
return MaybeLimitedColumnNumber<LimitValue>(value_ + offset.value());
|
|
}
|
|
|
|
MaybeLimitedColumnNumber<LimitValue> operator-(
|
|
const ColumnNumberOffset& offset) const {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(ptrdiff_t(value_) - offset.value() >= 0);
|
|
return MaybeLimitedColumnNumber<LimitValue>(value_ - offset.value());
|
|
}
|
|
ColumnNumberOffset operator-(
|
|
const MaybeLimitedColumnNumber<LimitValue>& other) const {
|
|
MOZ_ASSERT(valid());
|
|
return ColumnNumberOffset(int32_t(value_) - int32_t(other.value_));
|
|
}
|
|
|
|
MaybeLimitedColumnNumber<LimitValue>& operator+=(
|
|
const ColumnNumberOffset& offset) {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0);
|
|
value_ += offset.value();
|
|
MOZ_ASSERT(valid());
|
|
return *this;
|
|
}
|
|
MaybeLimitedColumnNumber<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 MaybeLimitedColumnNumber<LimitValue>& rhs) const {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(rhs.valid());
|
|
return value_ < rhs.value_;
|
|
}
|
|
bool operator<=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(rhs.valid());
|
|
return value_ <= rhs.value_;
|
|
}
|
|
bool operator>(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(rhs.valid());
|
|
return value_ > rhs.value_;
|
|
}
|
|
bool operator>=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
|
|
MOZ_ASSERT(valid());
|
|
MOZ_ASSERT(rhs.valid());
|
|
return value_ >= rhs.value_;
|
|
}
|
|
|
|
uint32_t oneOriginValue() const {
|
|
MOZ_ASSERT(valid());
|
|
|
|
return value_;
|
|
}
|
|
|
|
uint32_t* addressOfValueForTranscode() { return &value_; }
|
|
|
|
bool valid() const {
|
|
if constexpr (LimitValue == 0) {
|
|
return true;
|
|
}
|
|
|
|
MOZ_ASSERT(value_ != 0);
|
|
|
|
return value_ <= LimitValue;
|
|
}
|
|
};
|
|
|
|
// See the comment for LimitedColumnNumberOneOrigin below
|
|
static constexpr uint32_t ColumnNumberOneOriginLimit =
|
|
std::numeric_limits<int32_t>::max() / 2;
|
|
|
|
} // namespace detail
|
|
|
|
// Column number in 1-origin with 31-bit limit.
|
|
//
|
|
// Various parts of the engine requires the column number be represented in
|
|
// 31 bits.
|
|
//
|
|
// See:
|
|
// * TaggedColumnNumberOneOrigin
|
|
// * TokenStreamAnyChars::checkOptions
|
|
// * SourceNotes::isRepresentable
|
|
// * WasmFrameIter::computeLine
|
|
struct LimitedColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<
|
|
detail::ColumnNumberOneOriginLimit> {
|
|
private:
|
|
using Base =
|
|
detail::MaybeLimitedColumnNumber<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) {}
|
|
|
|
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 MaybeLimitedColumnNumber<0>& value) {
|
|
return fromUnlimited(value.oneOriginValue());
|
|
}
|
|
|
|
static LimitedColumnNumberOneOrigin fromZeroOrigin(uint32_t value) {
|
|
return LimitedColumnNumberOneOrigin(value + 1);
|
|
}
|
|
};
|
|
|
|
// Column number in 1-origin.
|
|
struct ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> {
|
|
private:
|
|
using Base = detail::MaybeLimitedColumnNumber<0>;
|
|
|
|
public:
|
|
using Base::Base;
|
|
using Base::operator=;
|
|
|
|
ColumnNumberOneOrigin() = default;
|
|
ColumnNumberOneOrigin(const ColumnNumberOneOrigin& other) = default;
|
|
ColumnNumberOneOrigin& operator=(ColumnNumberOneOrigin&) = default;
|
|
|
|
MOZ_IMPLICIT ColumnNumberOneOrigin(const Base& other) : Base(other) {}
|
|
|
|
explicit ColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other)
|
|
: Base(other.oneOriginValue()) {}
|
|
|
|
static ColumnNumberOneOrigin fromZeroOrigin(uint32_t value) {
|
|
return ColumnNumberOneOrigin(value + 1);
|
|
}
|
|
};
|
|
|
|
// Either LimitedColumnNumberOneOrigin, or WasmFunctionIndex.
|
|
//
|
|
// 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 LimitedColumnNumberOneOrigin
|
|
// 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
|
|
struct TaggedColumnNumberOneOrigin {
|
|
static constexpr uint32_t WasmFunctionTag = 1u << 31;
|
|
|
|
static_assert((WasmFunctionIndex::Limit & WasmFunctionTag) == 0);
|
|
static_assert((LimitedColumnNumberOneOrigin::Limit & WasmFunctionTag) == 0);
|
|
|
|
protected:
|
|
uint32_t value_ = LimitedColumnNumberOneOrigin::OriginValue;
|
|
|
|
explicit TaggedColumnNumberOneOrigin(uint32_t value) : value_(value) {}
|
|
|
|
public:
|
|
constexpr TaggedColumnNumberOneOrigin() = default;
|
|
TaggedColumnNumberOneOrigin(const TaggedColumnNumberOneOrigin& other) =
|
|
default;
|
|
|
|
explicit TaggedColumnNumberOneOrigin(
|
|
const LimitedColumnNumberOneOrigin& other)
|
|
: value_(other.value_) {
|
|
MOZ_ASSERT(isLimitedColumnNumber());
|
|
}
|
|
explicit TaggedColumnNumberOneOrigin(const WasmFunctionIndex& other)
|
|
: value_(other.value() | WasmFunctionTag) {
|
|
MOZ_ASSERT(isWasmFunctionIndex());
|
|
}
|
|
|
|
static TaggedColumnNumberOneOrigin fromRaw(uint32_t value) {
|
|
return TaggedColumnNumberOneOrigin(value);
|
|
}
|
|
|
|
static TaggedColumnNumberOneOrigin forDifferentialTesting() {
|
|
return TaggedColumnNumberOneOrigin(LimitedColumnNumberOneOrigin());
|
|
}
|
|
|
|
bool operator==(const TaggedColumnNumberOneOrigin& rhs) const {
|
|
return value_ == rhs.value_;
|
|
}
|
|
|
|
bool operator!=(const TaggedColumnNumberOneOrigin& rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
bool isLimitedColumnNumber() const { return !isWasmFunctionIndex(); }
|
|
|
|
bool isWasmFunctionIndex() const { return !!(value_ & WasmFunctionTag); }
|
|
|
|
LimitedColumnNumberOneOrigin toLimitedColumnNumber() const {
|
|
MOZ_ASSERT(isLimitedColumnNumber());
|
|
return LimitedColumnNumberOneOrigin(value_);
|
|
}
|
|
|
|
WasmFunctionIndex toWasmFunctionIndex() const {
|
|
MOZ_ASSERT(isWasmFunctionIndex());
|
|
return WasmFunctionIndex(value_ & ~WasmFunctionTag);
|
|
}
|
|
|
|
uint32_t oneOriginValue() const {
|
|
return isWasmFunctionIndex()
|
|
? WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin
|
|
: toLimitedColumnNumber().oneOriginValue();
|
|
}
|
|
|
|
uint32_t rawValue() const { return value_; }
|
|
|
|
uint32_t* addressOfValueForTranscode() { return &value_; }
|
|
};
|
|
|
|
} // namespace JS
|
|
|
|
#endif /* js_ColumnNumber_h */
|