summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects/bigint.h
blob: a5ca5148679abe3be7dcaa184bbd712ced45b5fe (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
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_BIGINT_H_
#define V8_OBJECTS_BIGINT_H_

#include "src/common/globals.h"
#include "src/objects/heap-object.h"
#include "src/objects/objects.h"
#include "src/utils/utils.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

void MutableBigInt_AbsoluteAddAndCanonicalize(Address result_addr,
                                              Address x_addr, Address y_addr);
int32_t MutableBigInt_AbsoluteCompare(Address x_addr, Address y_addr);
void MutableBigInt_AbsoluteSubAndCanonicalize(Address result_addr,
                                              Address x_addr, Address y_addr);

class BigInt;
class ValueDeserializer;
class ValueSerializer;

// BigIntBase is just the raw data object underlying a BigInt. Use with care!
// Most code should be using BigInts instead.
class BigIntBase : public HeapObject {
 public:
  inline int length() const {
    int32_t bitfield = RELAXED_READ_INT32_FIELD(*this, kBitfieldOffset);
    return LengthBits::decode(static_cast<uint32_t>(bitfield));
  }

  // For use by the GC.
  inline int synchronized_length() const {
    int32_t bitfield = ACQUIRE_READ_INT32_FIELD(*this, kBitfieldOffset);
    return LengthBits::decode(static_cast<uint32_t>(bitfield));
  }

  static inline BigIntBase unchecked_cast(Object o) {
    return bit_cast<BigIntBase>(o);
  }

  // The maximum kMaxLengthBits that the current implementation supports
  // would be kMaxInt - kSystemPointerSize * kBitsPerByte - 1.
  // Since we want a platform independent limit, choose a nice round number
  // somewhere below that maximum.
  static const int kMaxLengthBits = 1 << 30;  // ~1 billion.
  static const int kMaxLength =
      kMaxLengthBits / (kSystemPointerSize * kBitsPerByte);

  // Sign and length are stored in the same bitfield.  Since the GC needs to be
  // able to read the length concurrently, the getters and setters are atomic.
  static const int kLengthFieldBits = 30;
  STATIC_ASSERT(kMaxLength <= ((1 << kLengthFieldBits) - 1));
  class SignBits : public BitField<bool, 0, 1> {};
  class LengthBits : public BitField<int, SignBits::kNext, kLengthFieldBits> {};
  STATIC_ASSERT(LengthBits::kNext <= 32);

  // Layout description.
#define BIGINT_FIELDS(V)                                                  \
  V(kBitfieldOffset, kInt32Size)                                          \
  V(kOptionalPaddingOffset, POINTER_SIZE_PADDING(kOptionalPaddingOffset)) \
  /* Header size. */                                                      \
  V(kHeaderSize, 0)                                                       \
  V(kDigitsOffset, 0)

  DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize, BIGINT_FIELDS)
#undef BIGINT_FIELDS

  static constexpr bool HasOptionalPadding() {
    return FIELD_SIZE(kOptionalPaddingOffset) > 0;
  }

 private:
  friend class ::v8::internal::BigInt;  // MSVC wants full namespace.
  friend class MutableBigInt;

  using digit_t = uintptr_t;
  static const int kDigitSize = sizeof(digit_t);
  // kMaxLength definition assumes this:
  STATIC_ASSERT(kDigitSize == kSystemPointerSize);

  static const int kDigitBits = kDigitSize * kBitsPerByte;
  static const int kHalfDigitBits = kDigitBits / 2;
  static const digit_t kHalfDigitMask = (1ull << kHalfDigitBits) - 1;

  // sign() == true means negative.
  inline bool sign() const {
    int32_t bitfield = RELAXED_READ_INT32_FIELD(*this, kBitfieldOffset);
    return SignBits::decode(static_cast<uint32_t>(bitfield));
  }

  inline digit_t digit(int n) const {
    SLOW_DCHECK(0 <= n && n < length());
    return ReadField<digit_t>(kDigitsOffset + n * kDigitSize);
  }

  bool is_zero() const { return length() == 0; }

  // Only serves to make macros happy; other code should use IsBigInt.
  bool IsBigIntBase() const { return true; }

  OBJECT_CONSTRUCTORS(BigIntBase, HeapObject);
};

class FreshlyAllocatedBigInt : public BigIntBase {
  // This class is essentially the publicly accessible abstract version of
  // MutableBigInt (which is a hidden implementation detail). It serves as
  // the return type of Factory::NewBigInt, and makes it possible to enforce
  // casting restrictions:
  // - FreshlyAllocatedBigInt can be cast explicitly to MutableBigInt
  //   (with MutableBigInt::Cast) for initialization.
  // - MutableBigInt can be cast/converted explicitly to BigInt
  //   (with MutableBigInt::MakeImmutable); is afterwards treated as readonly.
  // - No accidental implicit casting is possible from BigInt to MutableBigInt
  //   (and no explicit operator is provided either).

 public:
  inline static FreshlyAllocatedBigInt cast(Object object);
  inline static FreshlyAllocatedBigInt unchecked_cast(Object o) {
    return bit_cast<FreshlyAllocatedBigInt>(o);
  }

  // Clear uninitialized padding space.
  inline void clear_padding() {
    if (FIELD_SIZE(kOptionalPaddingOffset) != 0) {
      DCHECK_EQ(4, FIELD_SIZE(kOptionalPaddingOffset));
      memset(reinterpret_cast<void*>(address() + kOptionalPaddingOffset), 0,
             FIELD_SIZE(kOptionalPaddingOffset));
    }
  }

 private:
  // Only serves to make macros happy; other code should use IsBigInt.
  bool IsFreshlyAllocatedBigInt() const { return true; }

  OBJECT_CONSTRUCTORS(FreshlyAllocatedBigInt, BigIntBase);
};

// Arbitrary precision integers in JavaScript.
class BigInt : public BigIntBase {
 public:
  // Implementation of the Spec methods, see:
  // https://tc39.github.io/proposal-bigint/#sec-numeric-types
  // Sections 1.1.1 through 1.1.19.
  static Handle<BigInt> UnaryMinus(Isolate* isolate, Handle<BigInt> x);
  static MaybeHandle<BigInt> BitwiseNot(Isolate* isolate, Handle<BigInt> x);
  static MaybeHandle<BigInt> Exponentiate(Isolate* isolate, Handle<BigInt> base,
                                          Handle<BigInt> exponent);
  static MaybeHandle<BigInt> Multiply(Isolate* isolate, Handle<BigInt> x,
                                      Handle<BigInt> y);
  static MaybeHandle<BigInt> Divide(Isolate* isolate, Handle<BigInt> x,
                                    Handle<BigInt> y);
  static MaybeHandle<BigInt> Remainder(Isolate* isolate, Handle<BigInt> x,
                                       Handle<BigInt> y);
  static MaybeHandle<BigInt> Add(Isolate* isolate, Handle<BigInt> x,
                                 Handle<BigInt> y);
  static MaybeHandle<BigInt> Subtract(Isolate* isolate, Handle<BigInt> x,
                                      Handle<BigInt> y);
  static MaybeHandle<BigInt> LeftShift(Isolate* isolate, Handle<BigInt> x,
                                       Handle<BigInt> y);
  static MaybeHandle<BigInt> SignedRightShift(Isolate* isolate,
                                              Handle<BigInt> x,
                                              Handle<BigInt> y);
  static MaybeHandle<BigInt> UnsignedRightShift(Isolate* isolate,
                                                Handle<BigInt> x,
                                                Handle<BigInt> y);
  // More convenient version of "bool LessThan(x, y)".
  static ComparisonResult CompareToBigInt(Handle<BigInt> x, Handle<BigInt> y);
  static bool EqualToBigInt(BigInt x, BigInt y);
  static MaybeHandle<BigInt> BitwiseAnd(Isolate* isolate, Handle<BigInt> x,
                                        Handle<BigInt> y);
  static MaybeHandle<BigInt> BitwiseXor(Isolate* isolate, Handle<BigInt> x,
                                        Handle<BigInt> y);
  static MaybeHandle<BigInt> BitwiseOr(Isolate* isolate, Handle<BigInt> x,
                                       Handle<BigInt> y);

  // Other parts of the public interface.
  static MaybeHandle<BigInt> Increment(Isolate* isolate, Handle<BigInt> x);
  static MaybeHandle<BigInt> Decrement(Isolate* isolate, Handle<BigInt> x);

  bool ToBoolean() { return !is_zero(); }
  uint32_t Hash() {
    // TODO(jkummerow): Improve this. At least use length and sign.
    return is_zero() ? 0 : ComputeLongHash(static_cast<uint64_t>(digit(0)));
  }

  bool IsNegative() const { return sign(); }

  static bool EqualToString(Isolate* isolate, Handle<BigInt> x,
                            Handle<String> y);
  static bool EqualToNumber(Handle<BigInt> x, Handle<Object> y);
  static ComparisonResult CompareToString(Isolate* isolate, Handle<BigInt> x,
                                          Handle<String> y);
  static ComparisonResult CompareToNumber(Handle<BigInt> x, Handle<Object> y);
  // Exposed for tests, do not call directly. Use CompareToNumber() instead.
  V8_EXPORT_PRIVATE static ComparisonResult CompareToDouble(Handle<BigInt> x,
                                                            double y);

  static Handle<BigInt> AsIntN(Isolate* isolate, uint64_t n, Handle<BigInt> x);
  static MaybeHandle<BigInt> AsUintN(Isolate* isolate, uint64_t n,
                                     Handle<BigInt> x);

  static Handle<BigInt> FromInt64(Isolate* isolate, int64_t n);
  static Handle<BigInt> FromUint64(Isolate* isolate, uint64_t n);
  static MaybeHandle<BigInt> FromWords64(Isolate* isolate, int sign_bit,
                                         int words64_count,
                                         const uint64_t* words);
  int64_t AsInt64(bool* lossless = nullptr);
  uint64_t AsUint64(bool* lossless = nullptr);
  int Words64Count();
  void ToWordsArray64(int* sign_bit, int* words64_count, uint64_t* words);

  DECL_CAST(BigInt)
  DECL_VERIFIER(BigInt)
  DECL_PRINTER(BigInt)
  void BigIntShortPrint(std::ostream& os);

  inline static int SizeFor(int length) {
    return kHeaderSize + length * kDigitSize;
  }

  static MaybeHandle<String> ToString(Isolate* isolate, Handle<BigInt> bigint,
                                      int radix = 10,
                                      ShouldThrow should_throw = kThrowOnError);
  // "The Number value for x", see:
  // https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type
  // Returns a Smi or HeapNumber.
  static Handle<Object> ToNumber(Isolate* isolate, Handle<BigInt> x);

  // ECMAScript's NumberToBigInt
  V8_EXPORT_PRIVATE static MaybeHandle<BigInt> FromNumber(
      Isolate* isolate, Handle<Object> number);

  // ECMAScript's ToBigInt (throws for Number input)
  static MaybeHandle<BigInt> FromObject(Isolate* isolate, Handle<Object> obj);

  class BodyDescriptor;

 private:
  friend class StringToBigIntHelper;
  friend class ValueDeserializer;
  friend class ValueSerializer;

  // Special functions for StringToBigIntHelper:
  static Handle<BigInt> Zero(Isolate* isolate);
  static MaybeHandle<FreshlyAllocatedBigInt> AllocateFor(
      Isolate* isolate, int radix, int charcount, ShouldThrow should_throw,
      AllocationType allocation);
  static void InplaceMultiplyAdd(Handle<FreshlyAllocatedBigInt> x,
                                 uintptr_t factor, uintptr_t summand);
  static Handle<BigInt> Finalize(Handle<FreshlyAllocatedBigInt> x, bool sign);

  // Special functions for ValueSerializer/ValueDeserializer:
  uint32_t GetBitfieldForSerialization() const;
  static int DigitsByteLengthForBitfield(uint32_t bitfield);
  // Expects {storage} to have a length of at least
  // {DigitsByteLengthForBitfield(GetBitfieldForSerialization())}.
  void SerializeDigits(uint8_t* storage);
  V8_WARN_UNUSED_RESULT static MaybeHandle<BigInt> FromSerializedDigits(
      Isolate* isolate, uint32_t bitfield, Vector<const uint8_t> digits_storage,
      AllocationType allocation);

  OBJECT_CONSTRUCTORS(BigInt, BigIntBase);
};

}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_BIGINT_H_