summaryrefslogtreecommitdiffstats
path: root/js/public/ColumnNumber.h
blob: 9fd007f4bb4aaa82add894c91daab4e3d897d9ce (plain)
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 */