[bigint] Move String-to-BigInt parsing to src/bigint/
No changes to the algorithm, approximately 4x performance improvement thanks to reduced overhead. Bug: v8:11515 Change-Id: Id3f6c91bd650f6ae47ac8f169dc780420091998e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3046185 Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#76022}
This commit is contained in:
parent
835a8b7de5
commit
152ecad8cd
@ -2685,6 +2685,7 @@ filegroup(
|
||||
"src/bigint/div-helpers.cc",
|
||||
"src/bigint/div-helpers.h",
|
||||
"src/bigint/div-schoolbook.cc",
|
||||
"src/bigint/fromstring.cc",
|
||||
"src/bigint/mul-fft.cc",
|
||||
"src/bigint/mul-karatsuba.cc",
|
||||
"src/bigint/mul-schoolbook.cc",
|
||||
|
1
BUILD.gn
1
BUILD.gn
@ -5005,6 +5005,7 @@ v8_source_set("v8_bigint") {
|
||||
"src/bigint/div-helpers.cc",
|
||||
"src/bigint/div-helpers.h",
|
||||
"src/bigint/div-schoolbook.cc",
|
||||
"src/bigint/fromstring.cc",
|
||||
"src/bigint/mul-karatsuba.cc",
|
||||
"src/bigint/mul-schoolbook.cc",
|
||||
"src/bigint/tostring.cc",
|
||||
|
@ -67,6 +67,9 @@ class ProcessorImpl : public Processor {
|
||||
void ToStringImpl(char* out, int* out_length, Digits X, int radix, bool sign,
|
||||
bool use_fast_algorithm);
|
||||
|
||||
void FromString(RWDigits Z, FromStringAccumulator* accumulator);
|
||||
void FromStringClassic(RWDigits Z, FromStringAccumulator* accumulator);
|
||||
|
||||
bool should_terminate() { return status_ == Status::kInterrupted; }
|
||||
|
||||
// Each unit is supposed to represent approximately one CPU {mul} instruction.
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace v8 {
|
||||
namespace bigint {
|
||||
@ -235,6 +236,8 @@ bool SubtractSigned(RWDigits Z, Digits X, bool x_negative, Digits Y,
|
||||
|
||||
enum class Status { kOk, kInterrupted };
|
||||
|
||||
class FromStringAccumulator;
|
||||
|
||||
class Processor {
|
||||
public:
|
||||
// Takes ownership of {platform}.
|
||||
@ -258,6 +261,8 @@ class Processor {
|
||||
// {out_length} initially contains the allocated capacity of {out}, and
|
||||
// upon return will be set to the actual length of the result string.
|
||||
Status ToString(char* out, int* out_length, Digits X, int radix, bool sign);
|
||||
|
||||
Status FromString(RWDigits Z, FromStringAccumulator* accumulator);
|
||||
};
|
||||
|
||||
inline int AddResultLength(int x_length, int y_length) {
|
||||
@ -296,9 +301,130 @@ int ToStringResultLength(Digits X, int radix, bool sign);
|
||||
// In DEBUG builds, the result of {ToString} will be initialized to this value.
|
||||
constexpr char kStringZapValue = '?';
|
||||
|
||||
// Support for parsing BigInts from Strings, using an Accumulator object
|
||||
// for intermediate state.
|
||||
|
||||
class ProcessorImpl;
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
// Clang supports this since 3.9, GCC since 5.x.
|
||||
#define HAVE_BUILTIN_MUL_OVERFLOW 1
|
||||
#else
|
||||
#define HAVE_BUILTIN_MUL_OVERFLOW 0
|
||||
#endif
|
||||
|
||||
// A container object for all metadata required for parsing a BigInt from
|
||||
// a string.
|
||||
// Aggressively optimized not to waste instructions for small cases, while
|
||||
// also scaling transparently to huge cases.
|
||||
// Defined here in the header so that {ConsumeChar} can be inlined.
|
||||
class FromStringAccumulator {
|
||||
public:
|
||||
// {max_digits} is only used for refusing to grow beyond a given size
|
||||
// (see "Step 1" below). Does not cause pre-allocation, so feel free to
|
||||
// specify a large maximum.
|
||||
// TODO(jkummerow): The limit applies to the number of intermediate chunks,
|
||||
// whereas the final result will be slightly smaller (depending on {radix}).
|
||||
// So setting max_digits=N here will, for sufficiently large N, not actually
|
||||
// allow parsing BigInts with N digits. We can fix that if/when anyone cares.
|
||||
FromStringAccumulator(int radix, int max_digits)
|
||||
: radix_(radix),
|
||||
#if !HAVE_BUILTIN_MUL_OVERFLOW
|
||||
max_multiplier_((~digit_t{0}) / radix),
|
||||
#endif
|
||||
max_digits_(max_digits),
|
||||
limit_digit_(radix < 10 ? radix : 10),
|
||||
limit_alpha_(radix > 10 ? radix - 10 : 0) {
|
||||
}
|
||||
|
||||
~FromStringAccumulator() {
|
||||
delete parts_;
|
||||
delete multipliers_;
|
||||
}
|
||||
|
||||
// Step 1: Call this method repeatedly to read all characters.
|
||||
// This method will return quickly; it does not perform heavy processing.
|
||||
enum class Result { kOk, kInvalidChar, kMaxSizeExceeded };
|
||||
Result ConsumeChar(uint32_t c) {
|
||||
digit_t d;
|
||||
if (c - '0' < limit_digit_) {
|
||||
d = c - '0';
|
||||
} else if ((c | 32u) - 'a' < limit_alpha_) {
|
||||
d = (c | 32u) - 'a' + 10;
|
||||
} else {
|
||||
return Result::kInvalidChar;
|
||||
}
|
||||
#if HAVE_BUILTIN_MUL_OVERFLOW
|
||||
digit_t m;
|
||||
if (!__builtin_mul_overflow(multiplier_, radix_, &m)) {
|
||||
multiplier_ = m;
|
||||
part_ = part_ * radix_ + d;
|
||||
}
|
||||
#else
|
||||
if (multiplier_ <= max_multiplier_) {
|
||||
multiplier_ *= radix_;
|
||||
part_ = part_ * radix_ + d;
|
||||
}
|
||||
#endif
|
||||
else { // NOLINT(readability/braces)
|
||||
if (!AddPart(multiplier_, part_)) return Result::kMaxSizeExceeded;
|
||||
multiplier_ = radix_;
|
||||
part_ = d;
|
||||
}
|
||||
return Result::kOk;
|
||||
}
|
||||
|
||||
// Step 2: Call this method to determine the required size for the result.
|
||||
int ResultLength() {
|
||||
if (!parts_) return part_ > 0 ? 1 : 0;
|
||||
if (multiplier_ > 1) {
|
||||
multipliers_->push_back(multiplier_);
|
||||
parts_->push_back(part_);
|
||||
// {ResultLength} should be idempotent.
|
||||
multiplier_ = 1;
|
||||
part_ = 0;
|
||||
}
|
||||
return parts_size();
|
||||
}
|
||||
|
||||
// Step 3: Use BigIntProcessor::FromString() to retrieve the result into an
|
||||
// {RWDigits} struct allocated for the size returned by step 2.
|
||||
|
||||
private:
|
||||
friend class ProcessorImpl;
|
||||
int parts_size() { return static_cast<int>(parts_->size()); }
|
||||
|
||||
bool AddPart(digit_t multiplier, digit_t part) {
|
||||
if (!parts_) {
|
||||
parts_ = new std::vector<digit_t>;
|
||||
multipliers_ = new std::vector<digit_t>;
|
||||
} else if (parts_size() == max_digits_) {
|
||||
return false;
|
||||
}
|
||||
multipliers_->push_back(multiplier);
|
||||
parts_->push_back(part);
|
||||
return true;
|
||||
}
|
||||
|
||||
const digit_t radix_;
|
||||
#if !HAVE_BUILTIN_MUL_OVERFLOW
|
||||
const digit_t max_multiplier_;
|
||||
#endif
|
||||
// The next part to be added to {parts_}, or the only part when sufficient.
|
||||
digit_t part_{0};
|
||||
digit_t multiplier_{1};
|
||||
const int max_digits_;
|
||||
const uint32_t limit_digit_;
|
||||
const uint32_t limit_alpha_;
|
||||
// Avoid allocating these unless we actually need them.
|
||||
std::vector<digit_t>* parts_{nullptr};
|
||||
std::vector<digit_t>* multipliers_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
||||
|
||||
#undef BIGINT_H_DCHECK
|
||||
#undef HAVE_BUILTIN_MUL_OVERFLOW
|
||||
|
||||
#endif // V8_BIGINT_BIGINT_H_
|
||||
|
42
src/bigint/fromstring.cc
Normal file
42
src/bigint/fromstring.cc
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#include "src/bigint/bigint-internal.h"
|
||||
#include "src/bigint/vector-arithmetic.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace bigint {
|
||||
|
||||
// The classic algorithm: for every part, multiply the accumulator with
|
||||
// the appropriate multiplier, and add the part. O(n²) overall.
|
||||
void ProcessorImpl::FromStringClassic(RWDigits Z,
|
||||
FromStringAccumulator* accumulator) {
|
||||
Z[0] = (*accumulator->parts_)[0];
|
||||
RWDigits already_set(Z, 0, 1);
|
||||
for (int i = 1; i < Z.len(); i++) Z[i] = 0;
|
||||
for (int i = 1; i < accumulator->parts_size(); i++) {
|
||||
MultiplySingle(Z, already_set, (*accumulator->multipliers_)[i]);
|
||||
if (should_terminate()) return;
|
||||
Add(Z, (*accumulator->parts_)[i]);
|
||||
already_set.set_len(already_set.len() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessorImpl::FromString(RWDigits Z, FromStringAccumulator* accumulator) {
|
||||
if (!accumulator->parts_) {
|
||||
if (Z.len() > 0) Z[0] = accumulator->part_;
|
||||
for (int i = 1; i < Z.len(); i++) Z[i] = 0;
|
||||
} else {
|
||||
FromStringClassic(Z, accumulator);
|
||||
}
|
||||
}
|
||||
|
||||
Status Processor::FromString(RWDigits Z, FromStringAccumulator* accumulator) {
|
||||
ProcessorImpl* impl = static_cast<ProcessorImpl*>(this);
|
||||
impl->FromString(Z, accumulator);
|
||||
return impl->get_and_clear_status();
|
||||
}
|
||||
|
||||
} // namespace bigint
|
||||
} // namespace v8
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "src/execution/local-isolate.h"
|
||||
|
||||
#include "src/bigint/bigint.h"
|
||||
#include "src/execution/isolate.h"
|
||||
#include "src/execution/thread-id.h"
|
||||
#include "src/handles/handles-inl.h"
|
||||
@ -24,7 +25,9 @@ LocalIsolate::LocalIsolate(Isolate* isolate, ThreadKind kind,
|
||||
: GetCurrentStackPosition() - FLAG_stack_size * KB),
|
||||
runtime_call_stats_(runtime_call_stats) {}
|
||||
|
||||
LocalIsolate::~LocalIsolate() = default;
|
||||
LocalIsolate::~LocalIsolate() {
|
||||
if (bigint_processor_) bigint_processor_->Destroy();
|
||||
}
|
||||
|
||||
void LocalIsolate::RegisterDeserializerStarted() {
|
||||
return isolate_->RegisterDeserializerStarted();
|
||||
@ -46,6 +49,12 @@ bool LocalIsolate::is_collecting_type_profile() const {
|
||||
return isolate_->is_collecting_type_profile();
|
||||
}
|
||||
|
||||
// Used for lazy initialization, based on an assumption that most
|
||||
// LocalIsolates won't be used to parse any BigInt literals.
|
||||
void LocalIsolate::InitializeBigIntProcessor() {
|
||||
bigint_processor_ = bigint::Processor::New(new bigint::Platform());
|
||||
}
|
||||
|
||||
// static
|
||||
bool StackLimitCheck::HasOverflowed(LocalIsolate* local_isolate) {
|
||||
return GetCurrentStackPosition() < local_isolate->stack_limit();
|
||||
|
@ -15,6 +15,11 @@
|
||||
#include "src/heap/local-heap.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
namespace bigint {
|
||||
class Processor;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
class Isolate;
|
||||
@ -92,6 +97,10 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
|
||||
ThreadId thread_id() const { return thread_id_; }
|
||||
Address stack_limit() const { return stack_limit_; }
|
||||
RuntimeCallStats* runtime_call_stats() const { return runtime_call_stats_; }
|
||||
bigint::Processor* bigint_processor() {
|
||||
if (!bigint_processor_) InitializeBigIntProcessor();
|
||||
return bigint_processor_;
|
||||
}
|
||||
|
||||
bool is_main_thread() const { return heap_.is_main_thread(); }
|
||||
|
||||
@ -106,6 +115,8 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
|
||||
private:
|
||||
friend class v8::internal::LocalFactory;
|
||||
|
||||
void InitializeBigIntProcessor();
|
||||
|
||||
LocalHeap heap_;
|
||||
|
||||
// TODO(leszeks): Extract out the fields of the Isolate we want and store
|
||||
@ -117,6 +128,7 @@ class V8_EXPORT_PRIVATE LocalIsolate final : private HiddenLocalFactory {
|
||||
Address const stack_limit_;
|
||||
|
||||
RuntimeCallStats* runtime_call_stats_;
|
||||
bigint::Processor* bigint_processor_{nullptr};
|
||||
};
|
||||
|
||||
template <base::MutexSharedType kIsShared>
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "src/base/numbers/dtoa.h"
|
||||
#include "src/base/numbers/strtod.h"
|
||||
#include "src/base/platform/wrappers.h"
|
||||
#include "src/bigint/bigint.h"
|
||||
#include "src/common/assert-scope.h"
|
||||
#include "src/handles/handles.h"
|
||||
#include "src/heap/factory.h"
|
||||
@ -310,38 +311,35 @@ class StringToIntHelper {
|
||||
|
||||
protected:
|
||||
// Subclasses must implement these:
|
||||
virtual void AllocateResult() = 0;
|
||||
virtual void ResultMultiplyAdd(uint32_t multiplier, uint32_t part) = 0;
|
||||
virtual void ParseOneByte(const uint8_t* start) = 0;
|
||||
virtual void ParseTwoByte(const base::uc16* start) = 0;
|
||||
|
||||
// Subclasses must call this to do all the work.
|
||||
void ParseInt();
|
||||
|
||||
// Subclasses may override this.
|
||||
virtual bool CheckTermination() { return false; }
|
||||
virtual void HandleSpecialCases() {}
|
||||
|
||||
// Subclass constructors should call these for configuration before calling
|
||||
// ParseInt().
|
||||
void set_allow_binary_and_octal_prefixes() {
|
||||
allow_binary_and_octal_prefixes_ = true;
|
||||
}
|
||||
void set_disallow_trailing_junk() { allow_trailing_junk_ = false; }
|
||||
bool allow_trailing_junk() { return allow_trailing_junk_; }
|
||||
|
||||
bool IsOneByte() const {
|
||||
return raw_one_byte_subject_ != nullptr ||
|
||||
String::IsOneByteRepresentationUnderneath(*subject_);
|
||||
}
|
||||
|
||||
base::Vector<const uint8_t> GetOneByteVector() {
|
||||
base::Vector<const uint8_t> GetOneByteVector(
|
||||
const DisallowGarbageCollection& no_gc) {
|
||||
if (raw_one_byte_subject_ != nullptr) {
|
||||
return base::Vector<const uint8_t>(raw_one_byte_subject_, length_);
|
||||
}
|
||||
DisallowGarbageCollection no_gc;
|
||||
return subject_->GetFlatContent(no_gc).ToOneByteVector();
|
||||
}
|
||||
|
||||
base::Vector<const base::uc16> GetTwoByteVector() {
|
||||
DisallowGarbageCollection no_gc;
|
||||
base::Vector<const base::uc16> GetTwoByteVector(
|
||||
const DisallowGarbageCollection& no_gc) {
|
||||
return subject_->GetFlatContent(no_gc).ToUC16Vector();
|
||||
}
|
||||
|
||||
@ -357,8 +355,6 @@ class StringToIntHelper {
|
||||
private:
|
||||
template <class Char>
|
||||
void DetectRadixInternal(Char current, int length);
|
||||
template <class Char>
|
||||
bool ParseChunkInternal(Char start);
|
||||
|
||||
IsolateT* isolate_;
|
||||
Handle<String> subject_;
|
||||
@ -375,46 +371,18 @@ class StringToIntHelper {
|
||||
|
||||
template <typename IsolateT>
|
||||
void StringToIntHelper<IsolateT>::ParseInt() {
|
||||
{
|
||||
DisallowGarbageCollection no_gc;
|
||||
if (IsOneByte()) {
|
||||
base::Vector<const uint8_t> vector = GetOneByteVector();
|
||||
DetectRadixInternal(vector.begin(), vector.length());
|
||||
} else {
|
||||
base::Vector<const base::uc16> vector = GetTwoByteVector();
|
||||
DetectRadixInternal(vector.begin(), vector.length());
|
||||
}
|
||||
DisallowGarbageCollection no_gc;
|
||||
if (IsOneByte()) {
|
||||
base::Vector<const uint8_t> vector = GetOneByteVector(no_gc);
|
||||
DetectRadixInternal(vector.begin(), vector.length());
|
||||
if (state_ != State::kRunning) return;
|
||||
ParseOneByte(vector.begin());
|
||||
} else {
|
||||
base::Vector<const base::uc16> vector = GetTwoByteVector(no_gc);
|
||||
DetectRadixInternal(vector.begin(), vector.length());
|
||||
if (state_ != State::kRunning) return;
|
||||
ParseTwoByte(vector.begin());
|
||||
}
|
||||
if (state_ != State::kRunning) return;
|
||||
AllocateResult();
|
||||
HandleSpecialCases();
|
||||
if (state_ != State::kRunning) return;
|
||||
do {
|
||||
{
|
||||
DisallowGarbageCollection no_gc;
|
||||
if (IsOneByte()) {
|
||||
base::Vector<const uint8_t> vector = GetOneByteVector();
|
||||
DCHECK_EQ(length_, vector.length());
|
||||
if (ParseChunkInternal(vector.begin())) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
base::Vector<const base::uc16> vector = GetTwoByteVector();
|
||||
DCHECK_EQ(length_, vector.length());
|
||||
if (ParseChunkInternal(vector.begin())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The flat vector handle is temporarily released after parsing 10kb
|
||||
// in order to invoke interrupts which may in turn invoke GC.
|
||||
if (CheckTermination()) {
|
||||
set_state(State::kError);
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
DCHECK_NE(state_, State::kRunning);
|
||||
}
|
||||
|
||||
template <typename IsolateT>
|
||||
@ -497,22 +465,118 @@ void StringToIntHelper<IsolateT>::DetectRadixInternal(Char current,
|
||||
cursor_ = static_cast<int>(current - start);
|
||||
}
|
||||
|
||||
template <typename IsolateT>
|
||||
template <class Char>
|
||||
bool StringToIntHelper<IsolateT>::ParseChunkInternal(Char start) {
|
||||
const int kChunkSize = 10240;
|
||||
Char current = start + cursor_;
|
||||
Char end = start + length_;
|
||||
Char break_pos = current + kChunkSize;
|
||||
class NumberParseIntHelper : public StringToIntHelper<Isolate> {
|
||||
public:
|
||||
NumberParseIntHelper(Isolate* isolate, Handle<String> string, int radix)
|
||||
: StringToIntHelper(isolate, string, radix) {}
|
||||
|
||||
template <class Char>
|
||||
void ParseInternal(Char start) {
|
||||
Char current = start + cursor();
|
||||
Char end = start + length();
|
||||
|
||||
if (radix() == 10) return HandleBaseTenCase(current, end);
|
||||
if (base::bits::IsPowerOfTwo(radix())) {
|
||||
result_ = HandlePowerOfTwoCase(current, end);
|
||||
set_state(State::kDone);
|
||||
return;
|
||||
}
|
||||
return HandleGenericCase(current, end);
|
||||
}
|
||||
void ParseOneByte(const uint8_t* start) final { return ParseInternal(start); }
|
||||
void ParseTwoByte(const base::uc16* start) final {
|
||||
return ParseInternal(start);
|
||||
}
|
||||
|
||||
double GetResult() {
|
||||
ParseInt();
|
||||
switch (state()) {
|
||||
case State::kJunk:
|
||||
case State::kEmpty:
|
||||
return JunkStringValue();
|
||||
case State::kZero:
|
||||
return SignedZero(negative());
|
||||
case State::kDone:
|
||||
return negative() ? -result_ : result_;
|
||||
case State::kError:
|
||||
case State::kRunning:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Char>
|
||||
void HandleGenericCase(Char current, Char end);
|
||||
|
||||
template <class Char>
|
||||
double HandlePowerOfTwoCase(Char current, Char end) {
|
||||
const bool allow_trailing_junk = true;
|
||||
// GetResult() will take care of the sign bit, so ignore it for now.
|
||||
const bool negative = false;
|
||||
switch (radix()) {
|
||||
case 2:
|
||||
return InternalStringToIntDouble<1>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
case 4:
|
||||
return InternalStringToIntDouble<2>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
case 8:
|
||||
return InternalStringToIntDouble<3>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
|
||||
case 16:
|
||||
return InternalStringToIntDouble<4>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
|
||||
case 32:
|
||||
return InternalStringToIntDouble<5>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
template <class Char>
|
||||
void HandleBaseTenCase(Char current, Char end) {
|
||||
// Parsing with strtod.
|
||||
const int kMaxSignificantDigits = 309; // Doubles are less than 1.8e308.
|
||||
// The buffer may contain up to kMaxSignificantDigits + 1 digits and a zero
|
||||
// end.
|
||||
const int kBufferSize = kMaxSignificantDigits + 2;
|
||||
char buffer[kBufferSize];
|
||||
int buffer_pos = 0;
|
||||
while (*current >= '0' && *current <= '9') {
|
||||
if (buffer_pos <= kMaxSignificantDigits) {
|
||||
// If the number has more than kMaxSignificantDigits it will be parsed
|
||||
// as infinity.
|
||||
DCHECK_LT(buffer_pos, kBufferSize);
|
||||
buffer[buffer_pos++] = static_cast<char>(*current);
|
||||
}
|
||||
++current;
|
||||
if (current == end) break;
|
||||
}
|
||||
|
||||
SLOW_DCHECK(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos] = '\0';
|
||||
base::Vector<const char> buffer_vector(buffer, buffer_pos);
|
||||
result_ = Strtod(buffer_vector, 0);
|
||||
set_state(State::kDone);
|
||||
}
|
||||
|
||||
double result_ = 0;
|
||||
};
|
||||
|
||||
template <class Char>
|
||||
void NumberParseIntHelper::HandleGenericCase(Char current, Char end) {
|
||||
// The following code causes accumulating rounding error for numbers greater
|
||||
// than ~2^56. It's explicitly allowed in the spec: "if R is not 2, 4, 8, 10,
|
||||
// 16, or 32, then mathInt may be an implementation-dependent approximation to
|
||||
// the mathematical integer value" (15.1.2.2).
|
||||
|
||||
int lim_0 = '0' + (radix_ < 10 ? radix_ : 10);
|
||||
int lim_a = 'a' + (radix_ - 10);
|
||||
int lim_A = 'A' + (radix_ - 10);
|
||||
int lim_0 = '0' + (radix() < 10 ? radix() : 10);
|
||||
int lim_a = 'a' + (radix() - 10);
|
||||
int lim_A = 'A' + (radix() - 10);
|
||||
|
||||
// NOTE: The code for computing the value may seem a bit complex at
|
||||
// first glance. It is structured to use 32-bit multiply-and-add
|
||||
@ -542,9 +606,9 @@ bool StringToIntHelper<IsolateT>::ParseChunkInternal(Char start) {
|
||||
// will not overflow the multiplier, we stop parsing the part
|
||||
// by leaving the loop.
|
||||
const uint32_t kMaximumMultiplier = 0xFFFFFFFFU / 36;
|
||||
uint32_t m = multiplier * static_cast<uint32_t>(radix_);
|
||||
uint32_t m = multiplier * static_cast<uint32_t>(radix());
|
||||
if (m > kMaximumMultiplier) break;
|
||||
part = part * radix_ + d;
|
||||
part = part * radix() + d;
|
||||
multiplier = m;
|
||||
DCHECK(multiplier > part);
|
||||
|
||||
@ -554,132 +618,14 @@ bool StringToIntHelper<IsolateT>::ParseChunkInternal(Char start) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the value and skip the part in the string.
|
||||
ResultMultiplyAdd(multiplier, part);
|
||||
|
||||
// Set final state
|
||||
if (done) {
|
||||
if (!allow_trailing_junk_ && AdvanceToNonspace(¤t, end)) {
|
||||
set_state(State::kJunk);
|
||||
} else {
|
||||
set_state(State::kDone);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} while (current < break_pos);
|
||||
|
||||
cursor_ = static_cast<int>(current - start);
|
||||
return false;
|
||||
}
|
||||
|
||||
class NumberParseIntHelper : public StringToIntHelper<Isolate> {
|
||||
public:
|
||||
NumberParseIntHelper(Isolate* isolate, Handle<String> string, int radix)
|
||||
: StringToIntHelper(isolate, string, radix) {}
|
||||
|
||||
double GetResult() {
|
||||
ParseInt();
|
||||
switch (state()) {
|
||||
case State::kJunk:
|
||||
case State::kEmpty:
|
||||
return JunkStringValue();
|
||||
case State::kZero:
|
||||
return SignedZero(negative());
|
||||
case State::kDone:
|
||||
return negative() ? -result_ : result_;
|
||||
case State::kError:
|
||||
case State::kRunning:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
protected:
|
||||
void AllocateResult() override {}
|
||||
void ResultMultiplyAdd(uint32_t multiplier, uint32_t part) override {
|
||||
result_ = result_ * multiplier + part;
|
||||
} while (!done);
|
||||
|
||||
if (!allow_trailing_junk() && AdvanceToNonspace(¤t, end)) {
|
||||
return set_state(State::kJunk);
|
||||
}
|
||||
|
||||
private:
|
||||
void HandleSpecialCases() override {
|
||||
bool is_power_of_two = base::bits::IsPowerOfTwo(radix());
|
||||
if (!is_power_of_two && radix() != 10) return;
|
||||
DisallowGarbageCollection no_gc;
|
||||
if (IsOneByte()) {
|
||||
base::Vector<const uint8_t> vector = GetOneByteVector();
|
||||
DCHECK_EQ(length(), vector.length());
|
||||
result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.begin())
|
||||
: HandleBaseTenCase(vector.begin());
|
||||
} else {
|
||||
base::Vector<const base::uc16> vector = GetTwoByteVector();
|
||||
DCHECK_EQ(length(), vector.length());
|
||||
result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.begin())
|
||||
: HandleBaseTenCase(vector.begin());
|
||||
}
|
||||
set_state(State::kDone);
|
||||
}
|
||||
|
||||
template <class Char>
|
||||
double HandlePowerOfTwoCase(Char start) {
|
||||
Char current = start + cursor();
|
||||
Char end = start + length();
|
||||
const bool allow_trailing_junk = true;
|
||||
// GetResult() will take care of the sign bit, so ignore it for now.
|
||||
const bool negative = false;
|
||||
switch (radix()) {
|
||||
case 2:
|
||||
return InternalStringToIntDouble<1>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
case 4:
|
||||
return InternalStringToIntDouble<2>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
case 8:
|
||||
return InternalStringToIntDouble<3>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
|
||||
case 16:
|
||||
return InternalStringToIntDouble<4>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
|
||||
case 32:
|
||||
return InternalStringToIntDouble<5>(current, end, negative,
|
||||
allow_trailing_junk);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
template <class Char>
|
||||
double HandleBaseTenCase(Char start) {
|
||||
// Parsing with strtod.
|
||||
Char current = start + cursor();
|
||||
Char end = start + length();
|
||||
const int kMaxSignificantDigits = 309; // Doubles are less than 1.8e308.
|
||||
// The buffer may contain up to kMaxSignificantDigits + 1 digits and a zero
|
||||
// end.
|
||||
const int kBufferSize = kMaxSignificantDigits + 2;
|
||||
char buffer[kBufferSize];
|
||||
int buffer_pos = 0;
|
||||
while (*current >= '0' && *current <= '9') {
|
||||
if (buffer_pos <= kMaxSignificantDigits) {
|
||||
// If the number has more than kMaxSignificantDigits it will be parsed
|
||||
// as infinity.
|
||||
DCHECK_LT(buffer_pos, kBufferSize);
|
||||
buffer[buffer_pos++] = static_cast<char>(*current);
|
||||
}
|
||||
++current;
|
||||
if (current == end) break;
|
||||
}
|
||||
|
||||
SLOW_DCHECK(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos] = '\0';
|
||||
base::Vector<const char> buffer_vector(buffer, buffer_pos);
|
||||
return Strtod(buffer_vector, 0);
|
||||
}
|
||||
|
||||
double result_ = 0;
|
||||
};
|
||||
return set_state(State::kDone);
|
||||
}
|
||||
|
||||
// Converts a string to a double value. Assumes the Iterator supports
|
||||
// the following operations:
|
||||
@ -989,6 +935,11 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
|
||||
this->set_allow_binary_and_octal_prefixes();
|
||||
}
|
||||
|
||||
void ParseOneByte(const uint8_t* start) final { return ParseInternal(start); }
|
||||
void ParseTwoByte(const base::uc16* start) final {
|
||||
return ParseInternal(start);
|
||||
}
|
||||
|
||||
MaybeHandle<BigInt> GetResult() {
|
||||
this->ParseInt();
|
||||
if (behavior_ == Behavior::kStringToBigInt && this->sign() != Sign::kNone &&
|
||||
@ -1009,7 +960,8 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
|
||||
case State::kZero:
|
||||
return BigInt::Zero(this->isolate(), allocation_type());
|
||||
case State::kDone:
|
||||
return BigInt::Finalize<Isolate>(result_, this->negative());
|
||||
return BigInt::Allocate(this->isolate(), accumulator_.get(),
|
||||
this->negative(), allocation_type());
|
||||
case State::kEmpty:
|
||||
case State::kRunning:
|
||||
break;
|
||||
@ -1017,28 +969,35 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
protected:
|
||||
void AllocateResult() override {
|
||||
// We have to allocate a BigInt that's big enough to fit the result.
|
||||
// Conseratively assume that all remaining digits are significant.
|
||||
// Optimization opportunity: Would it makes sense to scan for trailing
|
||||
// junk before allocating the result?
|
||||
int charcount = this->length() - this->cursor();
|
||||
MaybeHandle<FreshlyAllocatedBigInt> maybe =
|
||||
BigInt::AllocateFor(this->isolate(), this->radix(), charcount,
|
||||
kDontThrow, allocation_type());
|
||||
if (!maybe.ToHandle(&result_)) {
|
||||
this->set_state(State::kError);
|
||||
private:
|
||||
template <class Char>
|
||||
void ParseInternal(Char start) {
|
||||
accumulator_.reset(
|
||||
new bigint::FromStringAccumulator(this->radix(), BigInt::kMaxLength));
|
||||
|
||||
Char current = start + this->cursor();
|
||||
Char end = start + this->length();
|
||||
|
||||
do {
|
||||
using Result = bigint::FromStringAccumulator::Result;
|
||||
Result result = accumulator_->ConsumeChar(*current);
|
||||
if (result != Result::kOk) {
|
||||
if (result == Result::kMaxSizeExceeded) {
|
||||
this->set_state(State::kError);
|
||||
} else {
|
||||
DCHECK(result == Result::kInvalidChar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
++current;
|
||||
} while (current < end);
|
||||
|
||||
if (!this->allow_trailing_junk() && AdvanceToNonspace(¤t, end)) {
|
||||
return this->set_state(State::kJunk);
|
||||
}
|
||||
return this->set_state(State::kDone);
|
||||
}
|
||||
|
||||
void ResultMultiplyAdd(uint32_t multiplier, uint32_t part) override {
|
||||
BigInt::InplaceMultiplyAdd(*result_, static_cast<uintptr_t>(multiplier),
|
||||
static_cast<uintptr_t>(part));
|
||||
}
|
||||
|
||||
bool CheckTermination() override;
|
||||
|
||||
AllocationType allocation_type() {
|
||||
// For literals, we pretenure the allocated BigInt, since it's about
|
||||
// to be stored in the interpreter's constants array.
|
||||
@ -1046,23 +1005,10 @@ class StringToBigIntHelper : public StringToIntHelper<IsolateT> {
|
||||
: AllocationType::kYoung;
|
||||
}
|
||||
|
||||
private:
|
||||
Handle<FreshlyAllocatedBigInt> result_;
|
||||
std::unique_ptr<bigint::FromStringAccumulator> accumulator_;
|
||||
Behavior behavior_;
|
||||
};
|
||||
|
||||
template <typename IsolateT>
|
||||
bool StringToBigIntHelper<IsolateT>::CheckTermination() {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool StringToBigIntHelper<Isolate>::CheckTermination() {
|
||||
StackLimitCheck interrupt_check(isolate());
|
||||
return interrupt_check.InterruptRequested() &&
|
||||
isolate()->stack_guard()->HandleInterrupts().IsException(isolate());
|
||||
}
|
||||
|
||||
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string) {
|
||||
string = String::Flatten(isolate, string);
|
||||
StringToBigIntHelper<Isolate> helper(isolate, string);
|
||||
|
@ -126,10 +126,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
|
||||
Isolate* isolate, Handle<BigIntBase> x, Handle<BigIntBase> y,
|
||||
MutableBigInt result_storage = MutableBigInt());
|
||||
|
||||
static void InternalMultiplyAdd(BigIntBase source, digit_t factor,
|
||||
digit_t summand, int n, MutableBigInt result);
|
||||
void InplaceMultiplyAdd(uintptr_t factor, uintptr_t summand);
|
||||
|
||||
// Specialized helpers for shift operations.
|
||||
static MaybeHandle<BigInt> LeftShiftByAbsolute(Isolate* isolate,
|
||||
Handle<BigIntBase> x,
|
||||
@ -152,7 +148,6 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
|
||||
// Digit arithmetic helpers.
|
||||
static inline digit_t digit_add(digit_t a, digit_t b, digit_t* carry);
|
||||
static inline digit_t digit_sub(digit_t a, digit_t b, digit_t* borrow);
|
||||
static inline digit_t digit_mul(digit_t a, digit_t b, digit_t* high);
|
||||
static inline bool digit_ismax(digit_t x) {
|
||||
return static_cast<digit_t>(~x) == 0;
|
||||
}
|
||||
@ -1411,50 +1406,6 @@ Handle<MutableBigInt> MutableBigInt::AbsoluteXor(Isolate* isolate,
|
||||
[](digit_t a, digit_t b) { return a ^ b; });
|
||||
}
|
||||
|
||||
// Multiplies {source} with {factor} and adds {summand} to the result.
|
||||
// {result} and {source} may be the same BigInt for inplace modification.
|
||||
void MutableBigInt::InternalMultiplyAdd(BigIntBase source, digit_t factor,
|
||||
digit_t summand, int n,
|
||||
MutableBigInt result) {
|
||||
DCHECK(source.length() >= n);
|
||||
DCHECK(result.length() >= n);
|
||||
digit_t carry = summand;
|
||||
digit_t high = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
digit_t current = source.digit(i);
|
||||
digit_t new_carry = 0;
|
||||
// Compute this round's multiplication.
|
||||
digit_t new_high = 0;
|
||||
current = digit_mul(current, factor, &new_high);
|
||||
// Add last round's carryovers.
|
||||
current = digit_add(current, high, &new_carry);
|
||||
current = digit_add(current, carry, &new_carry);
|
||||
// Store result and prepare for next round.
|
||||
result.set_digit(i, current);
|
||||
carry = new_carry;
|
||||
high = new_high;
|
||||
}
|
||||
if (result.length() > n) {
|
||||
result.set_digit(n++, carry + high);
|
||||
// Current callers don't pass in such large results, but let's be robust.
|
||||
while (n < result.length()) {
|
||||
result.set_digit(n++, 0);
|
||||
}
|
||||
} else {
|
||||
CHECK_EQ(carry + high, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Multiplies {x} with {factor} and then adds {summand} to it.
|
||||
void BigInt::InplaceMultiplyAdd(FreshlyAllocatedBigInt x, uintptr_t factor,
|
||||
uintptr_t summand) {
|
||||
STATIC_ASSERT(sizeof(factor) == sizeof(digit_t));
|
||||
STATIC_ASSERT(sizeof(summand) == sizeof(digit_t));
|
||||
MutableBigInt bigint = MutableBigInt::cast(x);
|
||||
MutableBigInt::InternalMultiplyAdd(bigint, factor, summand, bigint.length(),
|
||||
bigint);
|
||||
}
|
||||
|
||||
MaybeHandle<BigInt> MutableBigInt::LeftShiftByAbsolute(Isolate* isolate,
|
||||
Handle<BigIntBase> x,
|
||||
Handle<BigIntBase> y) {
|
||||
@ -1591,71 +1542,33 @@ Maybe<BigInt::digit_t> MutableBigInt::ToShiftAmount(Handle<BigIntBase> x) {
|
||||
return Just(value);
|
||||
}
|
||||
|
||||
// Lookup table for the maximum number of bits required per character of a
|
||||
// base-N string representation of a number. To increase accuracy, the array
|
||||
// value is the actual value multiplied by 32. To generate this table:
|
||||
// for (var i = 0; i <= 36; i++) { print(Math.ceil(Math.log2(i) * 32) + ","); }
|
||||
constexpr uint8_t kMaxBitsPerChar[] = {
|
||||
0, 0, 32, 51, 64, 75, 83, 90, 96, // 0..8
|
||||
102, 107, 111, 115, 119, 122, 126, 128, // 9..16
|
||||
131, 134, 136, 139, 141, 143, 145, 147, // 17..24
|
||||
149, 151, 153, 154, 156, 158, 159, 160, // 25..32
|
||||
162, 163, 165, 166, // 33..36
|
||||
};
|
||||
|
||||
static const int kBitsPerCharTableShift = 5;
|
||||
static const size_t kBitsPerCharTableMultiplier = 1u << kBitsPerCharTableShift;
|
||||
void Terminate(Isolate* isolate) { isolate->TerminateExecution(); }
|
||||
// {LocalIsolate} doesn't support interruption or termination.
|
||||
void Terminate(LocalIsolate* isolate) { UNREACHABLE(); }
|
||||
|
||||
template <typename IsolateT>
|
||||
MaybeHandle<FreshlyAllocatedBigInt> BigInt::AllocateFor(
|
||||
IsolateT* isolate, int radix, int charcount, ShouldThrow should_throw,
|
||||
AllocationType allocation) {
|
||||
DCHECK(2 <= radix && radix <= 36);
|
||||
DCHECK_GE(charcount, 0);
|
||||
size_t bits_per_char = kMaxBitsPerChar[radix];
|
||||
uint64_t chars = static_cast<uint64_t>(charcount);
|
||||
const int roundup = kBitsPerCharTableMultiplier - 1;
|
||||
if (chars <=
|
||||
(std::numeric_limits<uint64_t>::max() - roundup) / bits_per_char) {
|
||||
uint64_t bits_min = bits_per_char * chars;
|
||||
// Divide by 32 (see table), rounding up.
|
||||
bits_min = (bits_min + roundup) >> kBitsPerCharTableShift;
|
||||
if (bits_min <= static_cast<uint64_t>(kMaxInt)) {
|
||||
// Divide by kDigitsBits, rounding up.
|
||||
int length = static_cast<int>((bits_min + kDigitBits - 1) / kDigitBits);
|
||||
if (length <= kMaxLength) {
|
||||
Handle<MutableBigInt> result =
|
||||
MutableBigInt::New(isolate, length, allocation).ToHandleChecked();
|
||||
result->InitializeDigits(length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
// All the overflow/maximum checks above fall through to here.
|
||||
if (should_throw == kThrowOnError) {
|
||||
return ThrowBigIntTooBig<FreshlyAllocatedBigInt>(isolate);
|
||||
} else {
|
||||
return MaybeHandle<FreshlyAllocatedBigInt>();
|
||||
MaybeHandle<BigInt> BigInt::Allocate(IsolateT* isolate,
|
||||
bigint::FromStringAccumulator* accumulator,
|
||||
bool negative, AllocationType allocation) {
|
||||
int digits = accumulator->ResultLength();
|
||||
DCHECK_LE(digits, kMaxLength);
|
||||
Handle<MutableBigInt> result =
|
||||
MutableBigInt::New(isolate, digits, allocation).ToHandleChecked();
|
||||
bigint::Status status =
|
||||
isolate->bigint_processor()->FromString(GetRWDigits(result), accumulator);
|
||||
if (status == bigint::Status::kInterrupted) {
|
||||
Terminate(isolate);
|
||||
return {};
|
||||
}
|
||||
result->set_sign(negative);
|
||||
return MutableBigInt::MakeImmutable(result);
|
||||
}
|
||||
template MaybeHandle<FreshlyAllocatedBigInt> BigInt::AllocateFor(
|
||||
Isolate* isolate, int radix, int charcount, ShouldThrow should_throw,
|
||||
AllocationType allocation);
|
||||
template MaybeHandle<FreshlyAllocatedBigInt> BigInt::AllocateFor(
|
||||
LocalIsolate* isolate, int radix, int charcount, ShouldThrow should_throw,
|
||||
AllocationType allocation);
|
||||
|
||||
template <typename IsolateT>
|
||||
Handle<BigInt> BigInt::Finalize(Handle<FreshlyAllocatedBigInt> x, bool sign) {
|
||||
Handle<MutableBigInt> bigint = Handle<MutableBigInt>::cast(x);
|
||||
bigint->set_sign(sign);
|
||||
return MutableBigInt::MakeImmutable<Isolate>(bigint);
|
||||
}
|
||||
|
||||
template Handle<BigInt> BigInt::Finalize<Isolate>(
|
||||
Handle<FreshlyAllocatedBigInt>, bool);
|
||||
template Handle<BigInt> BigInt::Finalize<LocalIsolate>(
|
||||
Handle<FreshlyAllocatedBigInt>, bool);
|
||||
template MaybeHandle<BigInt> BigInt::Allocate(Isolate*,
|
||||
bigint::FromStringAccumulator*,
|
||||
bool, AllocationType);
|
||||
template MaybeHandle<BigInt> BigInt::Allocate(LocalIsolate*,
|
||||
bigint::FromStringAccumulator*,
|
||||
bool, AllocationType);
|
||||
|
||||
// The serialization format MUST NOT CHANGE without updating the format
|
||||
// version in value-serializer.cc!
|
||||
@ -2056,43 +1969,6 @@ inline BigInt::digit_t MutableBigInt::digit_sub(digit_t a, digit_t b,
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the low half of the result. High half is in {high}.
|
||||
inline BigInt::digit_t MutableBigInt::digit_mul(digit_t a, digit_t b,
|
||||
digit_t* high) {
|
||||
#if HAVE_TWODIGIT_T
|
||||
twodigit_t result = static_cast<twodigit_t>(a) * static_cast<twodigit_t>(b);
|
||||
*high = result >> kDigitBits;
|
||||
return static_cast<digit_t>(result);
|
||||
#else
|
||||
// Multiply in half-pointer-sized chunks.
|
||||
// For inputs [AH AL]*[BH BL], the result is:
|
||||
//
|
||||
// [AL*BL] // r_low
|
||||
// + [AL*BH] // r_mid1
|
||||
// + [AH*BL] // r_mid2
|
||||
// + [AH*BH] // r_high
|
||||
// = [R4 R3 R2 R1] // high = [R4 R3], low = [R2 R1]
|
||||
//
|
||||
// Where of course we must be careful with carries between the columns.
|
||||
digit_t a_low = a & kHalfDigitMask;
|
||||
digit_t a_high = a >> kHalfDigitBits;
|
||||
digit_t b_low = b & kHalfDigitMask;
|
||||
digit_t b_high = b >> kHalfDigitBits;
|
||||
|
||||
digit_t r_low = a_low * b_low;
|
||||
digit_t r_mid1 = a_low * b_high;
|
||||
digit_t r_mid2 = a_high * b_low;
|
||||
digit_t r_high = a_high * b_high;
|
||||
|
||||
digit_t carry = 0;
|
||||
digit_t low = digit_add(r_low, r_mid1 << kHalfDigitBits, &carry);
|
||||
low = digit_add(low, r_mid2 << kHalfDigitBits, &carry);
|
||||
*high =
|
||||
(r_mid1 >> kHalfDigitBits) + (r_mid2 >> kHalfDigitBits) + r_high + carry;
|
||||
return low;
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef HAVE_TWODIGIT_T
|
||||
|
||||
void MutableBigInt::set_64_bits(uint64_t bits) {
|
||||
|
@ -14,6 +14,11 @@
|
||||
#include "src/objects/object-macros.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
namespace bigint {
|
||||
class FromStringAccumulator;
|
||||
} // namespace bigint
|
||||
|
||||
namespace internal {
|
||||
|
||||
void MutableBigInt_AbsoluteAddAndCanonicalize(Address result_addr,
|
||||
@ -252,13 +257,9 @@ class BigInt : public BigIntBase {
|
||||
static Handle<BigInt> Zero(
|
||||
IsolateT* isolate, AllocationType allocation = AllocationType::kYoung);
|
||||
template <typename IsolateT>
|
||||
static MaybeHandle<FreshlyAllocatedBigInt> AllocateFor(
|
||||
IsolateT* isolate, int radix, int charcount, ShouldThrow should_throw,
|
||||
AllocationType allocation);
|
||||
static void InplaceMultiplyAdd(FreshlyAllocatedBigInt x, uintptr_t factor,
|
||||
uintptr_t summand);
|
||||
template <typename IsolateT>
|
||||
static Handle<BigInt> Finalize(Handle<FreshlyAllocatedBigInt> x, bool sign);
|
||||
static MaybeHandle<BigInt> Allocate(
|
||||
IsolateT* isolate, bigint::FromStringAccumulator* accumulator,
|
||||
bool negative, AllocationType allocation);
|
||||
|
||||
// Special functions for ValueSerializer/ValueDeserializer:
|
||||
uint32_t GetBitfieldForSerialization() const;
|
||||
|
@ -239,6 +239,14 @@ TEST(TerminateBigIntToString) {
|
||||
"fail();");
|
||||
}
|
||||
|
||||
TEST(TerminateBigIntFromString) {
|
||||
TestTerminatingSlowOperation(
|
||||
"var a = '12344567890'.repeat(10000);\n"
|
||||
"terminate();\n"
|
||||
"BigInt(a);\n"
|
||||
"fail();\n");
|
||||
}
|
||||
|
||||
int call_count = 0;
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user