1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
|
/* -*- 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 */
|