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/codegen/code-factory.h"
8#include "src/logging/counters.h"
9#include "src/numbers/conversions.h"
10#include "src/objects/objects-inl.h"
11#ifdef V8_INTL_SUPPORT
12#include "src/objects/intl-objects.h"
13#endif
14
15namespace v8 {
16namespace internal {
17
18// -----------------------------------------------------------------------------
19// ES6 section 20.1 Number Objects
20
21// ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits )
22BUILTIN(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->IsJSPrimitiveWrapper()) {
29    value = handle(Handle<JSPrimitiveWrapper>::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 )
67BUILTIN(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->IsJSPrimitiveWrapper()) {
74    value = handle(Handle<JSPrimitiveWrapper>::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 ] ] )
112BUILTIN(NumberPrototypeToLocaleString) {
113  HandleScope scope(isolate);
114  const char* method_name = "Number.prototype.toLocaleString";
115
116  isolate->CountUsage(v8::Isolate::UseCounterFeature::kNumberToLocaleString);
117
118  Handle<Object> value = args.at(0);
119
120  // Unwrap the receiver {value}.
121  if (value->IsJSPrimitiveWrapper()) {
122    value = handle(Handle<JSPrimitiveWrapper>::cast(value)->value(), isolate);
123  }
124  // 1. Let x be ? thisNumberValue(this value)
125  if (!value->IsNumber()) {
126    THROW_NEW_ERROR_RETURN_FAILURE(
127        isolate,
128        NewTypeError(MessageTemplate::kNotGeneric,
129                     isolate->factory()->NewStringFromAsciiChecked(method_name),
130                     isolate->factory()->Number_string()));
131  }
132
133#ifdef V8_INTL_SUPPORT
134  RETURN_RESULT_OR_FAILURE(
135      isolate,
136      Intl::NumberToLocaleString(isolate, value, args.atOrUndefined(isolate, 1),
137                                 args.atOrUndefined(isolate, 2), method_name));
138#else
139  // Turn the {value} into a String.
140  return *isolate->factory()->NumberToString(value);
141#endif  // V8_INTL_SUPPORT
142}
143
144// ES6 section 20.1.3.5 Number.prototype.toPrecision ( precision )
145BUILTIN(NumberPrototypeToPrecision) {
146  HandleScope scope(isolate);
147  Handle<Object> value = args.at(0);
148  Handle<Object> precision = args.atOrUndefined(isolate, 1);
149
150  // Unwrap the receiver {value}.
151  if (value->IsJSPrimitiveWrapper()) {
152    value = handle(Handle<JSPrimitiveWrapper>::cast(value)->value(), isolate);
153  }
154  if (!value->IsNumber()) {
155    THROW_NEW_ERROR_RETURN_FAILURE(
156        isolate, NewTypeError(MessageTemplate::kNotGeneric,
157                              isolate->factory()->NewStringFromAsciiChecked(
158                                  "Number.prototype.toPrecision"),
159                              isolate->factory()->Number_string()));
160  }
161  double const value_number = value->Number();
162
163  // If no {precision} was specified, just return ToString of {value}.
164  if (precision->IsUndefined(isolate)) {
165    return *isolate->factory()->NumberToString(value);
166  }
167
168  // Convert the {precision} to an integer first.
169  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, precision,
170                                     Object::ToInteger(isolate, precision));
171  double const precision_number = precision->Number();
172
173  if (std::isnan(value_number)) return ReadOnlyRoots(isolate).NaN_string();
174  if (std::isinf(value_number)) {
175    return (value_number < 0.0) ? ReadOnlyRoots(isolate).minus_Infinity_string()
176                                : ReadOnlyRoots(isolate).Infinity_string();
177  }
178  if (precision_number < 1.0 || precision_number > kMaxFractionDigits) {
179    THROW_NEW_ERROR_RETURN_FAILURE(
180        isolate, NewRangeError(MessageTemplate::kToPrecisionFormatRange));
181  }
182  char* const str = DoubleToPrecisionCString(
183      value_number, static_cast<int>(precision_number));
184  Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
185  DeleteArray(str);
186  return *result;
187}
188
189}  // namespace internal
190}  // namespace v8
191