diff --git a/AUTHORS b/AUTHORS index f6e48c7c77..79569706c7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -196,6 +196,7 @@ Wiktor Garbacz Xiaoyin Liu Yannic Bonenberger Yong Wang +Youfeng Hao Yu Yin Zac Hansen Zhao Jiazhong diff --git a/BUILD.gn b/BUILD.gn index a99622db01..fcb5adf41a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -952,6 +952,7 @@ torque_files = [ "src/builtins/internal-coverage.tq", "src/builtins/iterator.tq", "src/builtins/math.tq", + "src/builtins/number.tq", "src/builtins/object-fromentries.tq", "src/builtins/object.tq", "src/builtins/promise-abstract-operations.tq", diff --git a/src/builtins/base.tq b/src/builtins/base.tq index 7246dbc507..b6321b5ee1 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.tq @@ -1469,6 +1469,8 @@ const kBoolean: constexpr PrimitiveType generates 'PrimitiveType::kBoolean'; const kSymbol: constexpr PrimitiveType generates 'PrimitiveType::kSymbol'; +const kNumber: constexpr PrimitiveType + generates 'PrimitiveType::kNumber'; const kNameDictionaryInitialCapacity: constexpr int32 generates 'NameDictionary::kInitialCapacity'; diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 79aab193cf..03f5e01fe8 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -670,7 +670,6 @@ namespace internal { CPP(NumberPrototypeToFixed) \ CPP(NumberPrototypeToLocaleString) \ CPP(NumberPrototypeToPrecision) \ - CPP(NumberPrototypeToString) \ /* ES6 #sec-number.prototype.valueof */ \ TFJ(NumberPrototypeValueOf, 0, kReceiver) \ TFC(Add, BinaryOp) \ diff --git a/src/builtins/builtins-number.cc b/src/builtins/builtins-number.cc index 49e7ff27b8..f6ff61a704 100644 --- a/src/builtins/builtins-number.cc +++ b/src/builtins/builtins-number.cc @@ -186,65 +186,5 @@ BUILTIN(NumberPrototypeToPrecision) { return *result; } -// ES6 section 20.1.3.6 Number.prototype.toString ( [ radix ] ) -BUILTIN(NumberPrototypeToString) { - HandleScope scope(isolate); - Handle value = args.at(0); - Handle radix = args.atOrUndefined(isolate, 1); - - // Unwrap the receiver {value}. - if (value->IsJSPrimitiveWrapper()) { - value = handle(Handle::cast(value)->value(), isolate); - } - if (!value->IsNumber()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kNotGeneric, - isolate->factory()->NewStringFromAsciiChecked( - "Number.prototype.toString"), - isolate->factory()->Number_string())); - } - double const value_number = value->Number(); - - // If no {radix} was specified, just return ToString of {value}. - if (radix->IsUndefined(isolate)) { - return *isolate->factory()->NumberToString(value); - } - - // Convert the {radix} to an integer first. - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix, - Object::ToInteger(isolate, radix)); - double const radix_number = radix->Number(); - - // If {radix} is 10, just return ToString of {value}. - if (radix_number == 10.0) return *isolate->factory()->NumberToString(value); - - // Make sure the {radix} is within the valid range. - if (radix_number < 2.0 || radix_number > 36.0) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError(MessageTemplate::kToRadixFormatRange)); - } - - // Fast case where the result is a one character string. - if ((IsUint32Double(value_number) && value_number < radix_number) || - IsMinusZero(value_number)) { - // Character array used for conversion. - static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - return *isolate->factory()->LookupSingleCharacterStringFromCode( - kCharTable[static_cast(value_number)]); - } - - // Slow case. - if (std::isnan(value_number)) return ReadOnlyRoots(isolate).NaN_string(); - if (std::isinf(value_number)) { - return (value_number < 0.0) ? ReadOnlyRoots(isolate).minus_Infinity_string() - : ReadOnlyRoots(isolate).Infinity_string(); - } - char* const str = - DoubleToRadixCString(value_number, static_cast(radix_number)); - Handle result = isolate->factory()->NewStringFromAsciiChecked(str); - DeleteArray(str); - return *result; -} - } // namespace internal } // namespace v8 diff --git a/src/builtins/number.tq b/src/builtins/number.tq new file mode 100644 index 0000000000..e319401570 --- /dev/null +++ b/src/builtins/number.tq @@ -0,0 +1,79 @@ +// Copyright 2019 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. + +namespace runtime { + extern transitioning runtime + DoubleToStringWithRadix(implicit context: Context)(Number, Number): String; +} // namespace runtime + +namespace number { + const kToRadixFormatRange: constexpr MessageTemplate + generates 'MessageTemplate::kToRadixFormatRange'; + + extern macro NaNStringConstant(): String; + extern macro ZeroStringConstant(): String; + extern macro InfinityStringConstant(): String; + extern macro MinusInfinityStringConstant(): String; + + const kAsciiZero: constexpr int32 = 48; // '0' (ascii) + const kAsciiLowerCaseA: constexpr int32 = 97; // 'a' (ascii) + + const MINUS_V8_INFINITY: constexpr float64 generates '-V8_INFINITY'; + + transitioning macro ThisNumberValue(implicit context: Context)( + receiver: JSAny, method: constexpr string): Number { + return UnsafeCast(ToThisValue(receiver, kNumber, method)); + } + + // https://tc39.github.io/ecma262/#sec-number.prototype.tostring + transitioning javascript builtin NumberPrototypeToString( + js-implicit context: Context, receiver: JSAny)(...arguments): String { + // 1. Let x be ? thisNumberValue(this value). + const x = ThisNumberValue(receiver, 'Number.prototype.toString'); + + // 2. If radix is not present, let radixNumber be 10. + // 3. Else if radix is undefined, let radixNumber be 10. + // 4. Else, let radixNumber be ? ToInteger(radix). + const radix: JSAny = arguments[0]; + const radixNumber: Number = radix == Undefined ? + 10 : + ToInteger_Inline(context, radix, kTruncateMinusZero); + + // 5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. + if (radixNumber < 2 || radixNumber > 36) { + ThrowRangeError(kToRadixFormatRange); + } + + // 6. If radixNumber = 10, return ! ToString(x). + if (radixNumber == 10) { + return NumberToString(x); + } + + // 7. Return the String representation of this Number + // value using the radix specified by radixNumber. + + // Fast case where the result is a one character string. + if (TaggedIsPositiveSmi(x) && x < radixNumber) { + let charCode = Convert(UnsafeCast(x)); + if (charCode < 10) { + charCode += kAsciiZero; + } else { + charCode = charCode - 10 + kAsciiLowerCaseA; + } + return StringFromSingleCharCode(charCode); + } + + if (x == -0) { + return ZeroStringConstant(); + } else if (NumberIsNaN(x)) { + return NaNStringConstant(); + } else if (x == V8_INFINITY) { + return InfinityStringConstant(); + } else if (x == MINUS_V8_INFINITY) { + return MinusInfinityStringConstant(); + } + + return runtime::DoubleToStringWithRadix(x, radixNumber); + } +} diff --git a/src/codegen/code-stub-assembler.h b/src/codegen/code-stub-assembler.h index c5041e0d2d..7b72a95803 100644 --- a/src/codegen/code-stub-assembler.h +++ b/src/codegen/code-stub-assembler.h @@ -93,6 +93,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol }; V(GlobalPropertyCellMap, global_property_cell_map, PropertyCellMap) \ V(has_instance_symbol, has_instance_symbol, HasInstanceSymbol) \ V(HeapNumberMap, heap_number_map, HeapNumberMap) \ + V(Infinity_string, Infinity_string, InfinityString) \ V(is_concat_spreadable_symbol, is_concat_spreadable_symbol, \ IsConcatSpreadableSymbol) \ V(iterator_symbol, iterator_symbol, IteratorSymbol) \ @@ -101,9 +102,11 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol }; V(match_symbol, match_symbol, MatchSymbol) \ V(megamorphic_symbol, megamorphic_symbol, MegamorphicSymbol) \ V(MetaMap, meta_map, MetaMap) \ + V(minus_Infinity_string, minus_Infinity_string, MinusInfinityString) \ V(MinusZeroValue, minus_zero_value, MinusZero) \ V(name_string, name_string, NameString) \ V(NanValue, nan_value, Nan) \ + V(NaN_string, NaN_string, NaNString) \ V(next_string, next_string, NextString) \ V(NoClosuresCellMap, no_closures_cell_map, NoClosuresCellMap) \ V(null_to_string, null_to_string, NullToString) \ @@ -149,7 +152,8 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol }; V(undefined_to_string, undefined_to_string, UndefinedToString) \ V(UndefinedValue, undefined_value, Undefined) \ V(uninitialized_symbol, uninitialized_symbol, UninitializedSymbol) \ - V(WeakFixedArrayMap, weak_fixed_array_map, WeakFixedArrayMap) + V(WeakFixedArrayMap, weak_fixed_array_map, WeakFixedArrayMap) \ + V(zero_string, zero_string, ZeroString) #define HEAP_IMMOVABLE_OBJECT_LIST(V) \ HEAP_MUTABLE_IMMOVABLE_OBJECT_LIST(V) \ diff --git a/src/runtime/runtime-internal.cc b/src/runtime/runtime-internal.cc index ed5fd4bcc7..e253a19d63 100644 --- a/src/runtime/runtime-internal.cc +++ b/src/runtime/runtime-internal.cc @@ -601,5 +601,18 @@ RUNTIME_FUNCTION(Runtime_GetInitializerFunction) { Handle initializer = JSReceiver::GetDataProperty(constructor, key); return *initializer; } + +RUNTIME_FUNCTION(Runtime_DoubleToStringWithRadix) { + HandleScope scope(isolate); + DCHECK_EQ(2, args.length()); + CONVERT_DOUBLE_ARG_CHECKED(number, 0); + CONVERT_INT32_ARG_CHECKED(radix, 1); + + char* const str = DoubleToRadixCString(number, radix); + Handle result = isolate->factory()->NewStringFromAsciiChecked(str); + DeleteArray(str); + return *result; +} + } // namespace internal } // namespace v8 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 75583cac70..7c27d39101 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -210,6 +210,7 @@ namespace internal { F(AllowDynamicFunction, 1, 1) \ I(CreateAsyncFromSyncIterator, 1, 1) \ F(CreateListFromArrayLike, 1, 1) \ + F(DoubleToStringWithRadix, 2, 1) \ F(FatalProcessOutOfMemoryInAllocateRaw, 0, 1) \ F(FatalProcessOutOfMemoryInvalidArrayLength, 0, 1) \ F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \