[bigint] Expose BigInt on the global object

Along with BigInt.prototype. Their functions only have skeleton
implementations. The purpose of this change is to make it easier
to gradually increase test coverage (e.g. for toString(radix)).

Of course this is still behind the --harmony-bigint flag.

Bug: v8:6791
Change-Id: Ic307fd9165c56ac782fba18d648ce893daaa718f
Reviewed-on: https://chromium-review.googlesource.com/671209
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48094}
This commit is contained in:
Jakob Kummerow 2017-09-19 22:32:15 -07:00 committed by Commit Bot
parent ce76dd60c8
commit b361ed5135
11 changed files with 249 additions and 34 deletions

View File

@ -1234,6 +1234,7 @@ v8_source_set("v8_base") {
"src/builtins/builtins-api.cc",
"src/builtins/builtins-array.cc",
"src/builtins/builtins-arraybuffer.cc",
"src/builtins/builtins-bigint.cc",
"src/builtins/builtins-boolean.cc",
"src/builtins/builtins-call.cc",
"src/builtins/builtins-callsite.cc",

View File

@ -4220,7 +4220,6 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_template_escapes)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrict_constructor_return)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_strict_legacy_accessor_builtins)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_bigint)
void InstallPublicSymbol(Factory* factory, Handle<Context> native_context,
const char* name, Handle<Symbol> value) {
@ -4345,6 +4344,50 @@ void Genesis::InitializeGlobal_harmony_regexp_dotall() {
native_context()->set_regexp_prototype_map(*prototype_map);
}
void Genesis::InitializeGlobal_harmony_bigint() {
if (!FLAG_harmony_bigint) return;
Handle<JSGlobalObject> global(native_context()->global_object());
Handle<JSFunction> bigint_fun = InstallFunction(
global, "BigInt", JS_VALUE_TYPE, JSValue::kSize,
isolate()->factory()->the_hole_value(), Builtins::kBigIntConstructor);
bigint_fun->shared()->DontAdaptArguments();
bigint_fun->shared()->SetConstructStub(
*BUILTIN_CODE(isolate(), BigIntConstructor_ConstructStub));
bigint_fun->shared()->set_length(1);
InstallWithIntrinsicDefaultProto(isolate(), bigint_fun,
Context::BIGINT_FUNCTION_INDEX);
heap()->bigint_map()->SetConstructorFunctionIndex(
Context::BIGINT_FUNCTION_INDEX);
// Install the properties of the BigInt constructor.
// parseInt(string, radix)
SimpleInstallFunction(bigint_fun, "parseInt", Builtins::kBigIntParseInt, 2,
false);
// asUintN(bits, bigint)
SimpleInstallFunction(bigint_fun, "asUintN", Builtins::kBigIntAsUintN, 2,
false);
// asIntN(bits, bigint)
SimpleInstallFunction(bigint_fun, "asIntN", Builtins::kBigIntAsIntN, 2,
false);
// Set up the %BigIntPrototype%.
Handle<JSObject> prototype(JSObject::cast(bigint_fun->instance_prototype()));
JSFunction::SetPrototype(bigint_fun, prototype);
// Install the properties of the BigInt.prototype.
// "constructor" is created implicitly by InstallFunction() above.
// toLocaleString([reserved1 [, reserved2]])
SimpleInstallFunction(prototype, "toLocaleString",
Builtins::kBigIntPrototypeToLocaleString, 0, false);
// toString([radix])
SimpleInstallFunction(prototype, "toString",
Builtins::kBigIntPrototypeToString, 0, false);
// valueOf()
SimpleInstallFunction(prototype, "valueOf", Builtins::kBigIntPrototypeValueOf,
0, false);
}
#ifdef V8_INTL_SUPPORT
void Genesis::InitializeGlobal_harmony_number_format_to_parts() {

View File

@ -0,0 +1,150 @@
// 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.
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/counters.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
BUILTIN(BigIntConstructor) {
HandleScope scope(isolate);
Handle<Object> value = args.atOrUndefined(isolate, 1);
// TODO(jkummerow): Implement properly.
// Dummy implementation only takes Smi args.
if (!value->IsSmi()) return isolate->heap()->undefined_value();
int num = Smi::ToInt(*value);
if (num == 0) return *isolate->factory()->NewBigInt(0);
Handle<BigInt> result = isolate->factory()->NewBigIntRaw(1);
result->set_value(num);
return *result;
}
BUILTIN(BigIntConstructor_ConstructStub) {
HandleScope scope(isolate);
Handle<Object> value = args.atOrUndefined(isolate, 1);
Handle<JSFunction> target = args.target();
Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
DCHECK(*target == target->native_context()->bigint_function());
Handle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
JSObject::New(target, new_target));
// TODO(jkummerow): Implement.
USE(value);
USE(result);
UNIMPLEMENTED();
}
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();
}
BUILTIN(BigIntAsUintN) {
HandleScope scope(isolate);
Handle<Object> bits_obj = args.atOrUndefined(isolate, 1);
Handle<Object> bigint_obj = args.atOrUndefined(isolate, 2);
// TODO(jkummerow): Implement.
USE(bits_obj);
USE(bigint_obj);
UNIMPLEMENTED();
}
BUILTIN(BigIntAsIntN) {
HandleScope scope(isolate);
Handle<Object> bits_obj = args.atOrUndefined(isolate, 1);
Handle<Object> bigint_obj = args.atOrUndefined(isolate, 2);
// TODO(jkummerow): Implement.
USE(bits_obj);
USE(bigint_obj);
UNIMPLEMENTED();
}
BUILTIN(BigIntPrototypeToLocaleString) {
HandleScope scope(isolate);
// TODO(jkummerow): Implement.
UNIMPLEMENTED();
}
namespace {
MaybeHandle<BigInt> ThisBigIntValue(Isolate* isolate, Handle<Object> value,
const char* caller) {
// 1. If Type(value) is BigInt, return value.
if (value->IsBigInt()) return Handle<BigInt>::cast(value);
// 2. If Type(value) is Object and value has a [[BigIntData]] internal slot:
if (value->IsJSValue()) {
// 2a. Assert: value.[[BigIntData]] is a BigInt value.
// 2b. Return value.[[BigIntData]].
Object* data = JSValue::cast(*value)->value();
if (data->IsBigInt()) return handle(BigInt::cast(data), isolate);
}
// 3. Throw a TypeError exception.
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kNotGeneric,
isolate->factory()->NewStringFromAsciiChecked(caller),
isolate->factory()->NewStringFromStaticChars("BigInt")),
BigInt);
}
} // namespace
BUILTIN(BigIntPrototypeToString) {
HandleScope scope(isolate);
// 1. Let x be ? thisBigIntValue(this value).
Handle<BigInt> x;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, x,
ThisBigIntValue(isolate, args.receiver(), "BigInt.prototype.toString"));
// 2. If radix is not present, let radixNumber be 10.
// 3. Else if radix is undefined, let radixNumber be 10.
Handle<Object> radix = args.atOrUndefined(isolate, 1);
int radix_number;
if (radix->IsUndefined(isolate)) {
radix_number = 10;
} else {
// 4. Else, let radixNumber be ? ToInteger(radix).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix,
Object::ToInteger(isolate, radix));
radix_number = static_cast<int>(radix->Number());
}
// 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
if (radix_number < 2 || radix_number > 36) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kToRadixFormatRange));
}
// Return the String representation of this Number value using the radix
// specified by radixNumber.
RETURN_RESULT_OR_FAILURE(isolate, BigInt::ToString(x, radix_number));
}
BUILTIN(BigIntPrototypeValueOf) {
HandleScope scope(isolate);
RETURN_RESULT_OR_FAILURE(
isolate,
ThisBigIntValue(isolate, args.receiver(), "BigInt.prototype.valueOf"));
}
} // namespace internal
} // namespace v8

View File

@ -328,6 +328,16 @@ namespace internal {
TFJ(AsyncFunctionPromiseCreate, 0) \
TFJ(AsyncFunctionPromiseRelease, 1, kPromise) \
\
/* BigInt */ \
CPP(BigIntConstructor) \
CPP(BigIntConstructor_ConstructStub) \
CPP(BigIntParseInt) \
CPP(BigIntAsUintN) \
CPP(BigIntAsIntN) \
CPP(BigIntPrototypeToLocaleString) \
CPP(BigIntPrototypeToString) \
CPP(BigIntPrototypeValueOf) \
\
/* Boolean */ \
CPP(BooleanConstructor) \
CPP(BooleanConstructor_ConstructStub) \

View File

@ -226,6 +226,7 @@ enum ContextLookupFlags {
V(ASYNC_GENERATOR_RETURN_CLOSED_REJECT_SHARED_FUN, SharedFunctionInfo, \
async_generator_return_closed_reject_shared_fun) \
V(ATOMICS_OBJECT, JSObject, atomics_object) \
V(BIGINT_FUNCTION_INDEX, JSFunction, bigint_function) \
V(BOOLEAN_FUNCTION_INDEX, JSFunction, boolean_function) \
V(BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX, Map, \
bound_function_with_constructor_map) \

View File

@ -123,7 +123,7 @@ Handle<BigInt> BigInt::BitwiseOr(Handle<BigInt> x, Handle<BigInt> y) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
}
Handle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
// TODO(jkummerow): Support non-power-of-two radixes.
if (!base::bits::IsPowerOfTwo(radix)) radix = 16;
return ToStringBasePowerOfTwo(bigint, radix);
@ -248,9 +248,10 @@ void BigInt::RightTrim() {
static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
// TODO(jkummerow): Add more tests for this when it is exposed on
// BigInt.prototype.
Handle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x, int radix) {
// TODO(jkummerow): Add more tests for this when we have a way to construct
// multi-digit BigInts.
MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x,
int radix) {
STATIC_ASSERT(base::bits::IsPowerOfTwo(kDigitBits));
DCHECK(base::bits::IsPowerOfTwo(radix));
DCHECK(radix >= 2 && radix <= 32);

View File

@ -60,7 +60,7 @@ class BigInt : public HeapObject {
}
void Initialize(int length, bool zero_initialize);
static Handle<String> ToString(Handle<BigInt> bigint, int radix);
static MaybeHandle<String> ToString(Handle<BigInt> bigint, int radix);
// Temporarily exposed helper, pending proper initialization.
void set_value(int value) {
@ -100,7 +100,8 @@ class BigInt : public HeapObject {
// abs(x) < abs(y), or zero if abs(x) == abs(y).
static int AbsoluteCompare(Handle<BigInt> x, Handle<BigInt> y);
static Handle<String> ToStringBasePowerOfTwo(Handle<BigInt> x, int radix);
static MaybeHandle<String> ToStringBasePowerOfTwo(Handle<BigInt> x,
int radix);
// Digit arithmetic helpers.
static inline digit_t digit_add(digit_t a, digit_t b, digit_t* carry);

View File

@ -12,28 +12,6 @@
namespace v8 {
namespace internal {
RUNTIME_FUNCTION(Runtime_BigInt) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(value, 0);
// For the moment, this is the only way to create a BigInt.
// Since we currently don't want ClusterFuzz to generate BigInts, we always
// throw here if the --harmony-bigint flag is disabled. (All --harmony-* flags
// are blacklisted for ClusterFuzz.)
if (!FLAG_harmony_bigint) {
THROW_NEW_ERROR_RETURN_FAILURE(isolate,
NewTypeError(MessageTemplate::kUnsupported));
}
if (value == 0) return *isolate->factory()->NewBigInt(0);
Handle<BigInt> result = isolate->factory()->NewBigInt(1);
result->set_value(value);
return *result;
}
RUNTIME_FUNCTION(Runtime_BigIntEqual) {
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());

View File

@ -69,7 +69,6 @@ namespace internal {
F(SetAllowAtomicsWait, 1, 1)
#define FOR_EACH_INTRINSIC_BIGINT(F) \
F(BigInt, 1, 1) \
F(BigIntEqual, 2, 1) \
F(BigIntToBoolean, 1, 1)

View File

@ -622,6 +622,7 @@
'builtins/builtins-api.cc',
'builtins/builtins-arraybuffer.cc',
'builtins/builtins-array.cc',
'builtins/builtins-bigint.cc',
'builtins/builtins-boolean.cc',
'builtins/builtins-call.cc',
'builtins/builtins-callsite.cc',

View File

@ -6,10 +6,15 @@
'use strict'
const zero = %BigInt(0);
const another_zero = %BigInt(0);
const one = %BigInt(1);
const another_one = %BigInt(1);
const zero = BigInt(0);
const another_zero = BigInt(0);
const one = BigInt(1);
const another_one = BigInt(1);
// BigInt
{
assertSame(BigInt, BigInt.prototype.constructor)
}
// typeof
{
@ -32,6 +37,31 @@ const another_one = %BigInt(1);
assertEquals(String(one), "1");
}
// .toString(radix)
{
// assertEquals(expected, BigInt(input).toString(n)) is generated by:
// input = $(python -c "print(int('expected', n))")
assertEquals("hello", BigInt(18306744).toString(32));
assertEquals("-hello", BigInt(-18306744).toString(32));
assertEquals("abcde", BigInt(0xabcde).toString(16));
assertEquals("-abcde", BigInt(-0xabcde).toString(16));
assertEquals("1234567", BigInt(342391).toString(8));
assertEquals("-1234567", BigInt(-342391).toString(8));
assertEquals("1230123", BigInt(6939).toString(4));
assertEquals("-1230123", BigInt(-6939).toString(4));
assertEquals("1011001110001", BigInt(5745).toString(2));
assertEquals("-1011001110001", BigInt(-5745).toString(2));
}
// .valueOf
{
assertEquals(Object(zero).valueOf(), another_zero);
assertThrows(() => { return BigInt.prototype.valueOf.call("string"); },
TypeError);
// TODO(jkummerow): Add tests for (new BigInt(...)).valueOf() when we
// can construct BigInt wrappers.
}
// ToBoolean
{
assertTrue(!zero);