Commit 658daa65 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Implement BigInt.parseInt

based on the existing Number.parseInt.

Bug: v8:6791
Change-Id: I9169a4695807a3e435e343d239431ae7f6ccf2a1
Reviewed-on: https://chromium-review.googlesource.com/685990Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48241}
parent 1f99c66b
......@@ -4,6 +4,7 @@
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/conversions.h"
#include "src/counters.h"
#include "src/objects-inl.h"
......@@ -44,14 +45,33 @@ BUILTIN(BigIntConstructor_ConstructStub) {
BUILTIN(BigIntParseInt) {
HandleScope scope(isolate);
Handle<Object> string_obj = args.atOrUndefined(isolate, 1);
Handle<Object> radix_obj = args.atOrUndefined(isolate, 2);
// TODO(jkummerow): Implement.
USE(string_obj);
USE(radix_obj);
UNIMPLEMENTED();
Handle<Object> string = args.atOrUndefined(isolate, 1);
Handle<Object> radix = args.atOrUndefined(isolate, 2);
// Convert {string} to a String and flatten it.
// Fast path: avoid back-and-forth conversion for Smi inputs.
if (string->IsSmi() && radix->IsUndefined(isolate)) {
int number = Smi::ToInt(*string);
if (number == 0) return *isolate->factory()->NewBigInt(0);
Handle<BigInt> result = isolate->factory()->NewBigIntRaw(1);
result->set_value(number);
return *result;
}
Handle<String> subject;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, subject,
Object::ToString(isolate, string));
subject = String::Flatten(subject);
// Convert {radix} to Int32.
if (!radix->IsNumber()) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix, Object::ToNumber(radix));
}
int radix32 = DoubleToInt32(radix->Number());
if (radix32 != 0 && (radix32 < 2 || radix32 > 36)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(MessageTemplate::kToRadixFormatRange));
}
RETURN_RESULT_OR_FAILURE(isolate, StringToBigInt(isolate, subject, radix32));
}
BUILTIN(BigIntAsUintN) {
......
......@@ -16,6 +16,7 @@
#include "src/factory.h"
#include "src/handles.h"
#include "src/objects-inl.h"
#include "src/objects/bigint.h"
#include "src/strtod.h"
#include "src/unicode-cache-inl.h"
#include "src/utils.h"
......@@ -799,7 +800,60 @@ double StringToInt(Isolate* isolate, Handle<String> string, int radix) {
return helper.GetResult();
}
class BigIntParseIntHelper : public StringToIntHelper {
public:
BigIntParseIntHelper(Isolate* isolate, Handle<String> string, int radix)
: StringToIntHelper(isolate, string, radix) {}
MaybeHandle<BigInt> GetResult() {
ParseInt();
switch (state()) {
case kJunk:
THROW_NEW_ERROR(isolate(),
NewSyntaxError(MessageTemplate::kBigIntInvalidString),
BigInt);
case kZero:
return isolate()->factory()->NewBigInt(0);
case kError:
return MaybeHandle<BigInt>();
case kDone:
result_->set_sign(negative());
result_->RightTrim();
return result_;
case kRunning:
break;
}
UNREACHABLE();
}
protected:
virtual void AllocateResult() {
// 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 = length() - cursor();
MaybeHandle<BigInt> maybe =
BigInt::AllocateFor(isolate(), radix(), charcount);
if (!maybe.ToHandle(&result_)) {
set_state(kError);
}
}
virtual void ResultMultiplyAdd(uint32_t multiplier, uint32_t part) {
result_->InplaceMultiplyAdd(static_cast<uintptr_t>(multiplier),
static_cast<uintptr_t>(part));
}
private:
Handle<BigInt> result_;
};
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string,
int radix) {
BigIntParseIntHelper helper(isolate, string, radix);
return helper.GetResult();
}
const char* DoubleToCString(double v, Vector<char> buffer) {
switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) {
......
......@@ -13,6 +13,7 @@
namespace v8 {
namespace internal {
class BigInt;
template <typename T>
class Handle;
class UnicodeCache;
......@@ -104,6 +105,9 @@ double StringToDouble(UnicodeCache* unicode_cache,
double StringToInt(Isolate* isolate, Handle<String> string, int radix);
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string,
int radix);
const int kDoubleToCStringMinBufferSize = 100;
// Converts a double to a string value according to ECMA-262 9.8.1.
......
......@@ -506,6 +506,7 @@ class ErrorUtils : public AllStatic {
T(UnsupportedSuper, "Unsupported reference to 'super'") \
/* RangeError */ \
T(BigIntDivZero, "Division by zero") \
T(BigIntTooBig, "Maximum BigInt size exceeded") \
T(DateRange, "Provided date is not in valid range.") \
T(ExpectedTimezoneID, \
"Expected Area/Location(/Location)* for time zone, got %") \
......@@ -550,6 +551,7 @@ class ErrorUtils : public AllStatic {
"The requested module contains conflicting star exports for name '%'") \
T(BadGetterArity, "Getter must not have any formal parameters.") \
T(BadSetterArity, "Setter must have exactly one formal parameter.") \
T(BigIntInvalidString, "Invalid BigInt string") \
T(ConstructorIsAccessor, "Class constructor may not be an accessor") \
T(ConstructorIsGenerator, "Class constructor may not be a generator") \
T(ConstructorIsAsync, "Class constructor may not be an async method") \
......
......@@ -345,6 +345,13 @@ void BigInt::InternalMultiplyAdd(BigInt* source, digit_t factor,
}
}
// Multiplies {this} with {factor} and adds {summand} to the result.
void BigInt::InplaceMultiplyAdd(uintptr_t factor, uintptr_t summand) {
STATIC_ASSERT(sizeof(factor) == sizeof(digit_t));
STATIC_ASSERT(sizeof(summand) == sizeof(digit_t));
InternalMultiplyAdd(this, factor, summand, length(), this);
}
// Divides {x} by {divisor}, returning the result in {quotient} and {remainder}.
// Mathematically, the contract is:
// quotient = (x - remainder) / divisor, with 0 <= remainder < divisor.
......@@ -570,6 +577,57 @@ Handle<BigInt> BigInt::Copy(Handle<BigInt> source) {
return result;
}
// 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) + ","); }
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;
MaybeHandle<BigInt> BigInt::AllocateFor(Isolate* isolate, int radix,
int charcount) {
DCHECK(2 <= radix && radix <= 36);
DCHECK(charcount >= 0);
size_t bits_min;
size_t bits_per_char = kMaxBitsPerChar[radix];
size_t chars = static_cast<size_t>(charcount);
const int roundup = kBitsPerCharTableMultiplier - 1;
if (chars <= 1000000) {
// More precise path: multiply first, then divide.
bits_min = bits_per_char * chars;
// Divide by 32 (see table), rounding up.
bits_min = (bits_min + roundup) >> kBitsPerCharTableShift;
} else {
// Overflow avoidance path: divide first, then multiply.
// The addition can't overflow because of the int -> size_t cast.
bits_min = ((chars + roundup) >> kBitsPerCharTableShift) * bits_per_char;
// Check if overflow happened.
if (bits_min < chars) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt);
}
}
if (bits_min > static_cast<size_t>(kMaxInt)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt);
}
// Divide by kDigitsBits, rounding up.
int length = (static_cast<int>(bits_min) + kDigitBits - 1) / kDigitBits;
if (length > BigInt::kMaxLength) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt);
}
return isolate->factory()->NewBigInt(length);
}
void BigInt::RightTrim() {
int old_length = length();
int new_length = old_length;
......
......@@ -82,6 +82,8 @@ class BigInt : public HeapObject {
class BodyDescriptor;
private:
friend class BigIntParseIntHelper;
typedef uintptr_t digit_t;
static const int kDigitSize = sizeof(digit_t);
static const int kDigitBits = kDigitSize * kBitsPerByte;
......@@ -90,6 +92,8 @@ class BigInt : public HeapObject {
// Private helpers for public methods.
static Handle<BigInt> Copy(Handle<BigInt> source);
static MaybeHandle<BigInt> AllocateFor(Isolate* isolate, int radix,
int charcount);
void RightTrim();
static Handle<BigInt> AbsoluteAdd(Handle<BigInt> x, Handle<BigInt> y,
......@@ -105,6 +109,7 @@ class BigInt : public HeapObject {
int accumulator_index);
static void InternalMultiplyAdd(BigInt* source, digit_t factor,
digit_t summand, int n, BigInt* result);
void InplaceMultiplyAdd(uintptr_t factor, uintptr_t summand);
// Specialized helpers for Divide/Remainder.
static void AbsoluteDivSmall(Handle<BigInt> x, digit_t divisor,
......
......@@ -56,6 +56,22 @@ const six = BigInt(6);
assertEquals("-1011001110001", BigInt(-5745).toString(2));
}
// .parseInt
{
assertEquals("hellobigint", BigInt.parseInt("hellobigint", 32).toString(32));
assertEquals("abc", BigInt.parseInt("101010111100", 2).toString(16));
// Detect "0x" prefix.
assertEquals("f00dcafe", BigInt.parseInt("0xf00dcafe").toString(16));
// Default base is 10, trailing junk is skipped.
assertEquals("abc", BigInt.parseInt("2748junk").toString(16));
// Objects are converted to string.
let obj = {toString: () => "0x12345"};
assertEquals("12345", BigInt.parseInt(obj).toString(16));
// Empty and invalid strings throw.
assertThrows("BigInt.parseInt('')", SyntaxError);
assertThrows("BigInt.parseInt('nope', 2)", SyntaxError);
}
// .valueOf
{
assertEquals(Object(zero).valueOf(), another_zero);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment