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