1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/builtins/builtins-utils-inl.h"
6 #include "src/builtins/builtins.h"
7 #include "src/code-factory.h"
8 #include "src/conversions.h"
9 #include "src/counters.h"
10 #include "src/objects-inl.h"
11 #ifdef V8_INTL_SUPPORT
12 #include "src/objects/intl-objects.h"
13 #endif
14
15 namespace v8 {
16 namespace internal {
17
18 // -----------------------------------------------------------------------------
19 // ES6 section 20.1 Number Objects
20
21 // ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits )
BUILTIN(NumberPrototypeToExponential)22 BUILTIN(NumberPrototypeToExponential) {
23 HandleScope scope(isolate);
24 Handle<Object> value = args.at(0);
25 Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1);
26
27 // Unwrap the receiver {value}.
28 if (value->IsJSValue()) {
29 value = handle(Handle<JSValue>::cast(value)->value(), isolate);
30 }
31 if (!value->IsNumber()) {
32 THROW_NEW_ERROR_RETURN_FAILURE(
33 isolate, NewTypeError(MessageTemplate::kNotGeneric,
34 isolate->factory()->NewStringFromAsciiChecked(
35 "Number.prototype.toExponential"),
36 isolate->factory()->Number_string()));
37 }
38 double const value_number = value->Number();
39
40 // Convert the {fraction_digits} to an integer first.
41 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
42 isolate, fraction_digits, Object::ToInteger(isolate, fraction_digits));
43 double const fraction_digits_number = fraction_digits->Number();
44
45 if (std::isnan(value_number)) return ReadOnlyRoots(isolate).NaN_string();
46 if (std::isinf(value_number)) {
47 return (value_number < 0.0) ? ReadOnlyRoots(isolate).minus_Infinity_string()
48 : ReadOnlyRoots(isolate).Infinity_string();
49 }
50 if (fraction_digits_number < 0.0 ||
51 fraction_digits_number > kMaxFractionDigits) {
52 THROW_NEW_ERROR_RETURN_FAILURE(
53 isolate, NewRangeError(MessageTemplate::kNumberFormatRange,
54 isolate->factory()->NewStringFromAsciiChecked(
55 "toExponential()")));
56 }
57 int const f = args.atOrUndefined(isolate, 1)->IsUndefined(isolate)
58 ? -1
59 : static_cast<int>(fraction_digits_number);
60 char* const str = DoubleToExponentialCString(value_number, f);
61 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
62 DeleteArray(str);
63 return *result;
64 }
65
66 // ES6 section 20.1.3.3 Number.prototype.toFixed ( fractionDigits )
BUILTIN(NumberPrototypeToFixed)67 BUILTIN(NumberPrototypeToFixed) {
68 HandleScope scope(isolate);
69 Handle<Object> value = args.at(0);
70 Handle<Object> fraction_digits = args.atOrUndefined(isolate, 1);
71
72 // Unwrap the receiver {value}.
73 if (value->IsJSValue()) {
74 value = handle(Handle<JSValue>::cast(value)->value(), isolate);
75 }
76 if (!value->IsNumber()) {
77 THROW_NEW_ERROR_RETURN_FAILURE(
78 isolate, NewTypeError(MessageTemplate::kNotGeneric,
79 isolate->factory()->NewStringFromAsciiChecked(
80 "Number.prototype.toFixed"),
81 isolate->factory()->Number_string()));
82 }
83 double const value_number = value->Number();
84
85 // Convert the {fraction_digits} to an integer first.
86 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
87 isolate, fraction_digits, Object::ToInteger(isolate, fraction_digits));
88 double const fraction_digits_number = fraction_digits->Number();
89
90 // Check if the {fraction_digits} are in the supported range.
91 if (fraction_digits_number < 0.0 ||
92 fraction_digits_number > kMaxFractionDigits) {
93 THROW_NEW_ERROR_RETURN_FAILURE(
94 isolate, NewRangeError(MessageTemplate::kNumberFormatRange,
95 isolate->factory()->NewStringFromAsciiChecked(
96 "toFixed() digits")));
97 }
98
99 if (std::isnan(value_number)) return ReadOnlyRoots(isolate).NaN_string();
100 if (std::isinf(value_number)) {
101 return (value_number < 0.0) ? ReadOnlyRoots(isolate).minus_Infinity_string()
102 : ReadOnlyRoots(isolate).Infinity_string();
103 }
104 char* const str = DoubleToFixedCString(
105 value_number, static_cast<int>(fraction_digits_number));
106 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
107 DeleteArray(str);
108 return *result;
109 }
110
111 // ES6 section 20.1.3.4 Number.prototype.toLocaleString ( [ r1 [ , r2 ] ] )
BUILTIN(NumberPrototypeToLocaleString)112 BUILTIN(NumberPrototypeToLocaleString) {
113 HandleScope scope(isolate);
114 Handle<Object> value = args.at(0);
115
116 // Unwrap the receiver {value}.
117 if (value->IsJSValue()) {
118 value = handle(Handle<JSValue>::cast(value)->value(), isolate);
119 }
120 // 1. Let x be ? thisNumberValue(this value)
121 if (!value->IsNumber()) {
122 THROW_NEW_ERROR_RETURN_FAILURE(
123 isolate, NewTypeError(MessageTemplate::kNotGeneric,
124 isolate->factory()->NewStringFromAsciiChecked(
125 "Number.prototype.toLocaleString"),
126 isolate->factory()->Number_string()));
127 }
128
129 #ifdef V8_INTL_SUPPORT
130 RETURN_RESULT_OR_FAILURE(
131 isolate,
132 Intl::NumberToLocaleString(isolate, value, args.atOrUndefined(isolate, 1),
133 args.atOrUndefined(isolate, 2)));
134 #else
135 // Turn the {value} into a String.
136 return *isolate->factory()->NumberToString(value);
137 #endif // V8_INTL_SUPPORT
138 }
139
140 // ES6 section 20.1.3.5 Number.prototype.toPrecision ( precision )
BUILTIN(NumberPrototypeToPrecision)141 BUILTIN(NumberPrototypeToPrecision) {
142 HandleScope scope(isolate);
143 Handle<Object> value = args.at(0);
144 Handle<Object> precision = args.atOrUndefined(isolate, 1);
145
146 // Unwrap the receiver {value}.
147 if (value->IsJSValue()) {
148 value = handle(Handle<JSValue>::cast(value)->value(), isolate);
149 }
150 if (!value->IsNumber()) {
151 THROW_NEW_ERROR_RETURN_FAILURE(
152 isolate, NewTypeError(MessageTemplate::kNotGeneric,
153 isolate->factory()->NewStringFromAsciiChecked(
154 "Number.prototype.toPrecision"),
155 isolate->factory()->Number_string()));
156 }
157 double const value_number = value->Number();
158
159 // If no {precision} was specified, just return ToString of {value}.
160 if (precision->IsUndefined(isolate)) {
161 return *isolate->factory()->NumberToString(value);
162 }
163
164 // Convert the {precision} to an integer first.
165 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, precision,
166 Object::ToInteger(isolate, precision));
167 double const precision_number = precision->Number();
168
169 if (std::isnan(value_number)) return ReadOnlyRoots(isolate).NaN_string();
170 if (std::isinf(value_number)) {
171 return (value_number < 0.0) ? ReadOnlyRoots(isolate).minus_Infinity_string()
172 : ReadOnlyRoots(isolate).Infinity_string();
173 }
174 if (precision_number < 1.0 || precision_number > kMaxFractionDigits) {
175 THROW_NEW_ERROR_RETURN_FAILURE(
176 isolate, NewRangeError(MessageTemplate::kToPrecisionFormatRange));
177 }
178 char* const str = DoubleToPrecisionCString(
179 value_number, static_cast<int>(precision_number));
180 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
181 DeleteArray(str);
182 return *result;
183 }
184
185 // ES6 section 20.1.3.6 Number.prototype.toString ( [ radix ] )
BUILTIN(NumberPrototypeToString)186 BUILTIN(NumberPrototypeToString) {
187 HandleScope scope(isolate);
188 Handle<Object> value = args.at(0);
189 Handle<Object> radix = args.atOrUndefined(isolate, 1);
190
191 // Unwrap the receiver {value}.
192 if (value->IsJSValue()) {
193 value = handle(Handle<JSValue>::cast(value)->value(), isolate);
194 }
195 if (!value->IsNumber()) {
196 THROW_NEW_ERROR_RETURN_FAILURE(
197 isolate, NewTypeError(MessageTemplate::kNotGeneric,
198 isolate->factory()->NewStringFromAsciiChecked(
199 "Number.prototype.toString"),
200 isolate->factory()->Number_string()));
201 }
202 double const value_number = value->Number();
203
204 // If no {radix} was specified, just return ToString of {value}.
205 if (radix->IsUndefined(isolate)) {
206 return *isolate->factory()->NumberToString(value);
207 }
208
209 // Convert the {radix} to an integer first.
210 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix,
211 Object::ToInteger(isolate, radix));
212 double const radix_number = radix->Number();
213
214 // If {radix} is 10, just return ToString of {value}.
215 if (radix_number == 10.0) return *isolate->factory()->NumberToString(value);
216
217 // Make sure the {radix} is within the valid range.
218 if (radix_number < 2.0 || radix_number > 36.0) {
219 THROW_NEW_ERROR_RETURN_FAILURE(
220 isolate, NewRangeError(MessageTemplate::kToRadixFormatRange));
221 }
222
223 // Fast case where the result is a one character string.
224 if ((IsUint32Double(value_number) && value_number < radix_number) ||
225 value_number == -0.0) {
226 // Character array used for conversion.
227 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
228 return *isolate->factory()->LookupSingleCharacterStringFromCode(
229 kCharTable[static_cast<uint32_t>(value_number)]);
230 }
231
232 // Slow case.
233 if (std::isnan(value_number)) return ReadOnlyRoots(isolate).NaN_string();
234 if (std::isinf(value_number)) {
235 return (value_number < 0.0) ? ReadOnlyRoots(isolate).minus_Infinity_string()
236 : ReadOnlyRoots(isolate).Infinity_string();
237 }
238 char* const str =
239 DoubleToRadixCString(value_number, static_cast<int>(radix_number));
240 Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
241 DeleteArray(str);
242 return *result;
243 }
244
245 } // namespace internal
246 } // namespace v8
247