Bug 1874684 - Part 25: Editorial updates. r=dminor
[gecko.git] / js / public / ColumnNumber.h
blob9fd007f4bb4aaa82add894c91daab4e3d897d9ce
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/. */
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.
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.
16 // Also these classes support converting from 0-origin column number.
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:
22 // function foo() { ... }
23 // ^ ^
24 // 0-origin: 0 15
25 // 1-origin: 1 16
27 #ifndef js_ColumnNumber_h
28 #define js_ColumnNumber_h
30 #include "mozilla/Assertions.h" // MOZ_ASSERT
31 #include "mozilla/Attributes.h" // MOZ_IMPLICIT
33 #include <limits> // std::numeric_limits
34 #include <stdint.h> // uint32_t
36 namespace JS {
38 // Wasm function index.
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;
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;
53 private:
54 uint32_t value_ = 0;
56 public:
57 constexpr WasmFunctionIndex() = default;
58 constexpr WasmFunctionIndex(const WasmFunctionIndex& other) = default;
60 inline explicit WasmFunctionIndex(uint32_t value) : value_(value) {
61 MOZ_ASSERT(valid());
64 uint32_t value() const { return value_; }
66 bool valid() const { return value_ <= Limit; }
69 // The offset between 2 column numbers.
70 struct ColumnNumberOffset {
71 private:
72 int32_t value_ = 0;
74 public:
75 constexpr ColumnNumberOffset() = default;
76 constexpr ColumnNumberOffset(const ColumnNumberOffset& other) = default;
78 inline explicit ColumnNumberOffset(int32_t value) : value_(value) {}
80 static constexpr ColumnNumberOffset zero() { return ColumnNumberOffset(); }
82 bool operator==(const ColumnNumberOffset& rhs) const {
83 return value_ == rhs.value_;
86 bool operator!=(const ColumnNumberOffset& rhs) const {
87 return !(*this == rhs);
90 int32_t value() const { return value_; }
93 // The positive offset from certain column number.
94 struct ColumnNumberUnsignedOffset {
95 private:
96 uint32_t value_ = 0;
98 public:
99 constexpr ColumnNumberUnsignedOffset() = default;
100 constexpr ColumnNumberUnsignedOffset(
101 const ColumnNumberUnsignedOffset& other) = default;
103 inline explicit ColumnNumberUnsignedOffset(uint32_t value) : value_(value) {}
105 static constexpr ColumnNumberUnsignedOffset zero() {
106 return ColumnNumberUnsignedOffset();
109 ColumnNumberUnsignedOffset operator+(
110 const ColumnNumberUnsignedOffset& offset) const {
111 return ColumnNumberUnsignedOffset(value_ + offset.value());
114 ColumnNumberUnsignedOffset& operator+=(
115 const ColumnNumberUnsignedOffset& offset) {
116 value_ += offset.value();
117 return *this;
120 bool operator==(const ColumnNumberUnsignedOffset& rhs) const {
121 return value_ == rhs.value_;
124 bool operator!=(const ColumnNumberUnsignedOffset& rhs) const {
125 return !(*this == rhs);
128 uint32_t value() const { return value_; }
130 uint32_t* addressOfValueForTranscode() { return &value_; }
133 struct TaggedColumnNumberOneOrigin;
135 namespace detail {
137 // Shared implementation of {,Limited}ColumnNumberOneOrigin classes.
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;
145 protected:
146 uint32_t value_ = OriginValue;
148 friend struct ::JS::TaggedColumnNumberOneOrigin;
150 public:
151 constexpr MaybeLimitedColumnNumber() = default;
152 MaybeLimitedColumnNumber(const MaybeLimitedColumnNumber& other) = default;
153 MaybeLimitedColumnNumber& operator=(const MaybeLimitedColumnNumber& other) =
154 default;
156 explicit MaybeLimitedColumnNumber(uint32_t value) : value_(value) {
157 MOZ_ASSERT(valid());
160 bool operator==(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
161 return value_ == rhs.value_;
164 bool operator!=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
165 return !(*this == rhs);
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());
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());
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());
188 ColumnNumberOffset operator-(
189 const MaybeLimitedColumnNumber<LimitValue>& other) const {
190 MOZ_ASSERT(valid());
191 return ColumnNumberOffset(int32_t(value_) - int32_t(other.value_));
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;
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;
211 bool operator<(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
212 MOZ_ASSERT(valid());
213 MOZ_ASSERT(rhs.valid());
214 return value_ < rhs.value_;
216 bool operator<=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
217 MOZ_ASSERT(valid());
218 MOZ_ASSERT(rhs.valid());
219 return value_ <= rhs.value_;
221 bool operator>(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
222 MOZ_ASSERT(valid());
223 MOZ_ASSERT(rhs.valid());
224 return value_ > rhs.value_;
226 bool operator>=(const MaybeLimitedColumnNumber<LimitValue>& rhs) const {
227 MOZ_ASSERT(valid());
228 MOZ_ASSERT(rhs.valid());
229 return value_ >= rhs.value_;
232 uint32_t oneOriginValue() const {
233 MOZ_ASSERT(valid());
235 return value_;
238 uint32_t* addressOfValueForTranscode() { return &value_; }
240 bool valid() const {
241 if constexpr (LimitValue == 0) {
242 return true;
245 MOZ_ASSERT(value_ != 0);
247 return value_ <= LimitValue;
251 // See the comment for LimitedColumnNumberOneOrigin below
252 static constexpr uint32_t ColumnNumberOneOriginLimit =
253 std::numeric_limits<int32_t>::max() / 2;
255 } // namespace detail
257 // Column number in 1-origin with 31-bit limit.
259 // Various parts of the engine requires the column number be represented in
260 // 31 bits.
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>;
273 public:
274 static constexpr uint32_t Limit = detail::ColumnNumberOneOriginLimit;
276 static_assert(uint32_t(Limit + Limit) > Limit,
277 "Adding Limit should not overflow");
279 using Base::Base;
281 LimitedColumnNumberOneOrigin() = default;
282 LimitedColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other) =
283 default;
284 MOZ_IMPLICIT LimitedColumnNumberOneOrigin(const Base& other) : Base(other) {}
286 static LimitedColumnNumberOneOrigin limit() {
287 return LimitedColumnNumberOneOrigin(Limit);
290 static LimitedColumnNumberOneOrigin fromUnlimited(uint32_t value) {
291 if (value > Limit) {
292 return LimitedColumnNumberOneOrigin(Limit);
294 return LimitedColumnNumberOneOrigin(value);
296 static LimitedColumnNumberOneOrigin fromUnlimited(
297 const MaybeLimitedColumnNumber<0>& value) {
298 return fromUnlimited(value.oneOriginValue());
301 static LimitedColumnNumberOneOrigin fromZeroOrigin(uint32_t value) {
302 return LimitedColumnNumberOneOrigin(value + 1);
306 // Column number in 1-origin.
307 struct ColumnNumberOneOrigin : public detail::MaybeLimitedColumnNumber<0> {
308 private:
309 using Base = detail::MaybeLimitedColumnNumber<0>;
311 public:
312 using Base::Base;
313 using Base::operator=;
315 ColumnNumberOneOrigin() = default;
316 ColumnNumberOneOrigin(const ColumnNumberOneOrigin& other) = default;
317 ColumnNumberOneOrigin& operator=(ColumnNumberOneOrigin&) = default;
319 MOZ_IMPLICIT ColumnNumberOneOrigin(const Base& other) : Base(other) {}
321 explicit ColumnNumberOneOrigin(const LimitedColumnNumberOneOrigin& other)
322 : Base(other.oneOriginValue()) {}
324 static ColumnNumberOneOrigin fromZeroOrigin(uint32_t value) {
325 return ColumnNumberOneOrigin(value + 1);
329 // Either LimitedColumnNumberOneOrigin, or WasmFunctionIndex.
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".
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.
342 // 0b0YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY LimitedColumnNumberOneOrigin
343 // 0b1YYYYYYY_YYYYYYYY_YYYYYYYY_YYYYYYYY WasmFunctionIndex
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;
352 static_assert((WasmFunctionIndex::Limit & WasmFunctionTag) == 0);
353 static_assert((LimitedColumnNumberOneOrigin::Limit & WasmFunctionTag) == 0);
355 protected:
356 uint32_t value_ = LimitedColumnNumberOneOrigin::OriginValue;
358 explicit TaggedColumnNumberOneOrigin(uint32_t value) : value_(value) {}
360 public:
361 constexpr TaggedColumnNumberOneOrigin() = default;
362 TaggedColumnNumberOneOrigin(const TaggedColumnNumberOneOrigin& other) =
363 default;
365 explicit TaggedColumnNumberOneOrigin(
366 const LimitedColumnNumberOneOrigin& other)
367 : value_(other.value_) {
368 MOZ_ASSERT(isLimitedColumnNumber());
370 explicit TaggedColumnNumberOneOrigin(const WasmFunctionIndex& other)
371 : value_(other.value() | WasmFunctionTag) {
372 MOZ_ASSERT(isWasmFunctionIndex());
375 static TaggedColumnNumberOneOrigin fromRaw(uint32_t value) {
376 return TaggedColumnNumberOneOrigin(value);
379 static TaggedColumnNumberOneOrigin forDifferentialTesting() {
380 return TaggedColumnNumberOneOrigin(LimitedColumnNumberOneOrigin());
383 bool operator==(const TaggedColumnNumberOneOrigin& rhs) const {
384 return value_ == rhs.value_;
387 bool operator!=(const TaggedColumnNumberOneOrigin& rhs) const {
388 return !(*this == rhs);
391 bool isLimitedColumnNumber() const { return !isWasmFunctionIndex(); }
393 bool isWasmFunctionIndex() const { return !!(value_ & WasmFunctionTag); }
395 LimitedColumnNumberOneOrigin toLimitedColumnNumber() const {
396 MOZ_ASSERT(isLimitedColumnNumber());
397 return LimitedColumnNumberOneOrigin(value_);
400 WasmFunctionIndex toWasmFunctionIndex() const {
401 MOZ_ASSERT(isWasmFunctionIndex());
402 return WasmFunctionIndex(value_ & ~WasmFunctionTag);
405 uint32_t oneOriginValue() const {
406 return isWasmFunctionIndex()
407 ? WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin
408 : toLimitedColumnNumber().oneOriginValue();
411 uint32_t rawValue() const { return value_; }
413 uint32_t* addressOfValueForTranscode() { return &value_; }
416 } // namespace JS
418 #endif /* js_ColumnNumber_h */