ColumnNumber.h (13161B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 // [SMDOC] Column numbers 7 // 8 // Inside SpiderMonkey, column numbers are represented as 1-origin 32-bit 9 // unsigned integers. Some parts of the engine use the highest bit of a column 10 // number as a tag to indicate Wasm frame. 11 // 12 // These classes help clarifying the origin of the column number, and also 13 // figuring out whether the column number uses the wasm's tag or not, and also 14 // help converting between them. 15 // 16 // Also these classes support converting from 0-origin column number. 17 // 18 // In a 0-origin context, column 0 is the first character of the line. 19 // In a 1-origin context, column 1 is the first character of the line, 20 // for example: 21 // 22 // function foo() { ... } 23 // ^ ^ 24 // 0-origin: 0 15 25 // 1-origin: 1 16 26 27 #ifndef js_ColumnNumber_h 28 #define js_ColumnNumber_h 29 30 #include "mozilla/Assertions.h" // MOZ_ASSERT 31 #include "mozilla/Attributes.h" // MOZ_IMPLICIT 32 33 #include <limits> // std::numeric_limits 34 #include <stdint.h> // uint32_t 35 36 namespace JS { 37 38 // Wasm function index. 39 // 40 // This class is used as parameter or return type of 41 // TaggedColumnNumberOneOrigin class below. 42 struct WasmFunctionIndex { 43 // TaggedColumnNumberOneOrigin uses the highest bit as a tag. 44 static constexpr uint32_t Limit = std::numeric_limits<int32_t>::max() / 2; 45 46 // For wasm frames, the function index is returned as the column with the 47 // high bit set. In paths that format error stacks into strings, this 48 // information can be used to synthesize a proper wasm frame. But when raw 49 // column numbers are handed out, we just fix them to the first column to 50 // avoid confusion. 51 static constexpr uint32_t DefaultBinarySourceColumnNumberOneOrigin = 1; 52 53 private: 54 uint32_t value_ = 0; 55 56 public: 57 constexpr WasmFunctionIndex() = default; 58 constexpr WasmFunctionIndex(const WasmFunctionIndex& other) = default; 59 60 inline explicit WasmFunctionIndex(uint32_t value) : value_(value) { 61 MOZ_ASSERT(valid()); 62 } 63 64 uint32_t value() const { return value_; } 65 66 bool valid() const { return value_ <= Limit; } 67 }; 68 69 // The offset between 2 column numbers. 70 struct ColumnNumberOffset { 71 private: 72 int32_t value_ = 0; 73 74 public: 75 constexpr ColumnNumberOffset() = default; 76 constexpr ColumnNumberOffset(const ColumnNumberOffset& other) = default; 77 78 inline explicit ColumnNumberOffset(int32_t value) : value_(value) {} 79 80 static constexpr ColumnNumberOffset zero() { return ColumnNumberOffset(); } 81 82 bool operator==(const ColumnNumberOffset& rhs) const { 83 return value_ == rhs.value_; 84 } 85 86 bool operator!=(const ColumnNumberOffset& rhs) const { 87 return !(*this == rhs); 88 } 89 90 int32_t value() const { return value_; } 91 }; 92 93 // The positive offset from certain column number. 94 struct ColumnNumberUnsignedOffset { 95 private: 96 uint32_t value_ = 0; 97 98 public: 99 constexpr ColumnNumberUnsignedOffset() = default; 100 constexpr ColumnNumberUnsignedOffset( 101 const ColumnNumberUnsignedOffset& other) = default; 102 103 inline explicit ColumnNumberUnsignedOffset(uint32_t value) : value_(value) {} 104 105 static constexpr ColumnNumberUnsignedOffset zero() { 106 return ColumnNumberUnsignedOffset(); 107 } 108 109 ColumnNumberUnsignedOffset operator+( 110 const ColumnNumberUnsignedOffset& offset) const { 111 return ColumnNumberUnsignedOffset(value_ + offset.value()); 112 } 113 114 ColumnNumberUnsignedOffset& operator+=( 115 const ColumnNumberUnsignedOffset& offset) { 116 value_ += offset.value(); 117 return *this; 118 } 119 120 bool operator==(const ColumnNumberUnsignedOffset& rhs) const { 121 return value_ == rhs.value_; 122 } 123 124 bool operator!=(const ColumnNumberUnsignedOffset& rhs) const { 125 return !(*this == rhs); 126 } 127 128 uint32_t value() const { return value_; } 129 130 uint32_t* addressOfValueForTranscode() { return &value_; } 131 }; 132 133 struct TaggedColumnNumberOneOrigin; 134 135 namespace detail { 136 137 // Shared implementation of {,Limited}ColumnNumberOneOrigin classes. 138 // 139 // LimitValue being 0 means there's no limit. 140 template <uint32_t LimitValue = 0> 141 struct MaybeLimitedColumnNumber { 142 public: 143 static constexpr uint32_t OriginValue = 1; 144 145 protected: 146 uint32_t value_ = OriginValue; 147 148 friend struct ::JS::TaggedColumnNumberOneOrigin; 149 150 public: 151 constexpr MaybeLimitedColumnNumber() = default; 152 MaybeLimitedColumnNumber(const MaybeLimitedColumnNumber& other) = default; 153 MaybeLimitedColumnNumber& operator=(const MaybeLimitedColumnNumber& other) = 154 default; 155 156 explicit MaybeLimitedColumnNumber(uint32_t value) : value_(value) { 157 MOZ_ASSERT(valid()); 158 } 159 160 bool operator==(const MaybeLimitedColumnNumber<LimitValue>& rhs) const { 161 return value_ == rhs.value_; 162 } 163 164 bool operator!=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const { 165 return !(*this == rhs); 166 } 167 168 MaybeLimitedColumnNumber<LimitValue> operator+( 169 const ColumnNumberOffset& offset) const { 170 MOZ_ASSERT(valid()); 171 MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0); 172 return MaybeLimitedColumnNumber<LimitValue>(value_ + offset.value()); 173 } 174 175 MaybeLimitedColumnNumber<LimitValue> operator+( 176 const ColumnNumberUnsignedOffset& offset) const { 177 MOZ_ASSERT(valid()); 178 MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0); 179 return MaybeLimitedColumnNumber<LimitValue>(value_ + offset.value()); 180 } 181 182 MaybeLimitedColumnNumber<LimitValue> operator-( 183 const ColumnNumberOffset& offset) const { 184 MOZ_ASSERT(valid()); 185 MOZ_ASSERT(ptrdiff_t(value_) - offset.value() >= 0); 186 return MaybeLimitedColumnNumber<LimitValue>(value_ - offset.value()); 187 } 188 ColumnNumberOffset operator-( 189 const MaybeLimitedColumnNumber<LimitValue>& other) const { 190 MOZ_ASSERT(valid()); 191 return ColumnNumberOffset(int32_t(value_) - int32_t(other.value_)); 192 } 193 194 MaybeLimitedColumnNumber<LimitValue>& operator+=( 195 const ColumnNumberOffset& offset) { 196 MOZ_ASSERT(valid()); 197 MOZ_ASSERT(ptrdiff_t(value_) + offset.value() >= 0); 198 value_ += offset.value(); 199 MOZ_ASSERT(valid()); 200 return *this; 201 } 202 MaybeLimitedColumnNumber<LimitValue>& operator-=( 203 const ColumnNumberOffset& offset) { 204 MOZ_ASSERT(valid()); 205 MOZ_ASSERT(ptrdiff_t(value_) - offset.value() >= 0); 206 value_ -= offset.value(); 207 MOZ_ASSERT(valid()); 208 return *this; 209 } 210 211 bool operator<(const MaybeLimitedColumnNumber<LimitValue>& rhs) const { 212 MOZ_ASSERT(valid()); 213 MOZ_ASSERT(rhs.valid()); 214 return value_ < rhs.value_; 215 } 216 bool operator<=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const { 217 MOZ_ASSERT(valid()); 218 MOZ_ASSERT(rhs.valid()); 219 return value_ <= rhs.value_; 220 } 221 bool operator>(const MaybeLimitedColumnNumber<LimitValue>& rhs) const { 222 MOZ_ASSERT(valid()); 223 MOZ_ASSERT(rhs.valid()); 224 return value_ > rhs.value_; 225 } 226 bool operator>=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const { 227 MOZ_ASSERT(valid()); 228 MOZ_ASSERT(rhs.valid()); 229 return value_ >= rhs.value_; 230 } 231 232 uint32_t oneOriginValue() const { 233 MOZ_ASSERT(valid()); 234 235 return value_; 236 } 237 238 uint32_t* addressOfValueForTranscode() { return &value_; } 239 240 bool valid() const { 241 if constexpr (LimitValue == 0) { 242 return true; 243 } 244 245 MOZ_ASSERT(value_ != 0); 246 247 return value_ <= LimitValue; 248 } 249 }; 250 251 // See the comment for LimitedColumnNumberOneOrigin below 252 static constexpr uint32_t ColumnNumberOneOriginLimit = 253 std::numeric_limits<int32_t>::max() / 2; 254 255 } // namespace detail 256 257 // Column number in 1-origin with 31-bit limit. 258 // 259 // Various parts of the engine requires the column number be represented in 260 // 31 bits. 261 // 262 // See: 263 // * TaggedColumnNumberOneOrigin 264 // * TokenStreamAnyChars::checkOptions 265 // * SourceNotes::isRepresentable 266 // * WasmFrameIter::computeLine 267 struct LimitedColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber< 268 detail::ColumnNumberOneOriginLimit> { 269 private: 270 using Base = 271 detail::MaybeLimitedColumnNumber<detail::ColumnNumberOneOriginLimit>; 272 273 public: 274 static constexpr uint32_t Limit = detail::ColumnNumberOneOriginLimit; 275 276 static_assert(uint32_t(Limit + Limit) > Limit, 277 "Adding Limit should not overflow"); 278 279 using Base::Base; 280 281 LimitedColumnNumberOneOrigin() = default; 282 LimitedColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other) = 283 default; 284 MOZ_IMPLICIT LimitedColumnNumberOneOrigin(const Base& other) : Base(other) {} 285 286 static LimitedColumnNumberOneOrigin limit() { 287 return LimitedColumnNumberOneOrigin(Limit); 288 } 289 290 static LimitedColumnNumberOneOrigin fromUnlimited(uint32_t value) { 291 if (value > Limit) { 292 return LimitedColumnNumberOneOrigin(Limit); 293 } 294 return LimitedColumnNumberOneOrigin(value); 295 } 296 static LimitedColumnNumberOneOrigin fromUnlimited( 297 const MaybeLimitedColumnNumber<0>& value) { 298 return fromUnlimited(value.oneOriginValue()); 299 } 300 301 static LimitedColumnNumberOneOrigin fromZeroOrigin(uint32_t value) { 302 return LimitedColumnNumberOneOrigin(value + 1); 303 } 304 }; 305 306 // Column number in 1-origin. 307 struct ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> { 308 private: 309 using Base = detail::MaybeLimitedColumnNumber<0>; 310 311 public: 312 using Base::Base; 313 using Base::operator=; 314 315 ColumnNumberOneOrigin() = default; 316 ColumnNumberOneOrigin(const ColumnNumberOneOrigin& other) = default; 317 ColumnNumberOneOrigin& operator=(ColumnNumberOneOrigin&) = default; 318 319 MOZ_IMPLICIT ColumnNumberOneOrigin(const Base& other) : Base(other) {} 320 321 explicit ColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other) 322 : Base(other.oneOriginValue()) {} 323 324 static ColumnNumberOneOrigin fromZeroOrigin(uint32_t value) { 325 return ColumnNumberOneOrigin(value + 1); 326 } 327 }; 328 329 // Either LimitedColumnNumberOneOrigin, or WasmFunctionIndex. 330 // 331 // In order to pass the Wasm frame's (url, bytecode-offset, func-index) tuple 332 // through the existing (url, line, column) tuple, it tags the highest bit of 333 // the column to indicate "this is a wasm frame". 334 // 335 // When knowing clients see this bit, they shall render the tuple 336 // (url, line, column|bit) as "url:wasm-function[column]:0xline" according 337 // to the WebAssembly Web API's Developer-Facing Display Conventions. 338 // https://webassembly.github.io/spec/web-api/index.html#conventions 339 // The wasm bytecode offset continues to be passed as the JS line to avoid 340 // breaking existing devtools code written when this used to be the case. 341 // 342 // 0b0YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY LimitedColumnNumberOneOrigin 343 // 0b1YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY WasmFunctionIndex 344 // 345 // The tagged colum number shouldn't escape the JS engine except for the 346 // following places: 347 // * SavedFrame API which can directly access WASM frame's info 348 // * ubi::Node API which can also directly access WASM frame's info 349 struct TaggedColumnNumberOneOrigin { 350 static constexpr uint32_t WasmFunctionTag = 1u << 31; 351 352 static_assert((WasmFunctionIndex::Limit & WasmFunctionTag) == 0); 353 static_assert((LimitedColumnNumberOneOrigin::Limit & WasmFunctionTag) == 0); 354 355 protected: 356 uint32_t value_ = LimitedColumnNumberOneOrigin::OriginValue; 357 358 explicit TaggedColumnNumberOneOrigin(uint32_t value) : value_(value) {} 359 360 public: 361 constexpr TaggedColumnNumberOneOrigin() = default; 362 TaggedColumnNumberOneOrigin(const TaggedColumnNumberOneOrigin& other) = 363 default; 364 365 explicit TaggedColumnNumberOneOrigin( 366 const LimitedColumnNumberOneOrigin& other) 367 : value_(other.value_) { 368 MOZ_ASSERT(isLimitedColumnNumber()); 369 } 370 explicit TaggedColumnNumberOneOrigin(const WasmFunctionIndex& other) 371 : value_(other.value() | WasmFunctionTag) { 372 MOZ_ASSERT(isWasmFunctionIndex()); 373 } 374 375 static TaggedColumnNumberOneOrigin fromRaw(uint32_t value) { 376 return TaggedColumnNumberOneOrigin(value); 377 } 378 379 static TaggedColumnNumberOneOrigin forDifferentialTesting() { 380 return TaggedColumnNumberOneOrigin(LimitedColumnNumberOneOrigin()); 381 } 382 383 bool operator==(const TaggedColumnNumberOneOrigin& rhs) const { 384 return value_ == rhs.value_; 385 } 386 387 bool operator!=(const TaggedColumnNumberOneOrigin& rhs) const { 388 return !(*this == rhs); 389 } 390 391 bool isLimitedColumnNumber() const { return !isWasmFunctionIndex(); } 392 393 bool isWasmFunctionIndex() const { return !!(value_ & WasmFunctionTag); } 394 395 LimitedColumnNumberOneOrigin toLimitedColumnNumber() const { 396 MOZ_ASSERT(isLimitedColumnNumber()); 397 return LimitedColumnNumberOneOrigin(value_); 398 } 399 400 WasmFunctionIndex toWasmFunctionIndex() const { 401 MOZ_ASSERT(isWasmFunctionIndex()); 402 return WasmFunctionIndex(value_ & ~WasmFunctionTag); 403 } 404 405 uint32_t oneOriginValue() const { 406 return isWasmFunctionIndex() 407 ? WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin 408 : toLimitedColumnNumber().oneOriginValue(); 409 } 410 411 uint32_t rawValue() const { return value_; } 412 413 uint32_t* addressOfValueForTranscode() { return &value_; } 414 }; 415 416 } // namespace JS 417 418 #endif /* js_ColumnNumber_h */