runtime-numbers.cc revision 014dc512cdd3e367bee49a713fdc5ed92584a3e5
1// Copyright 2014 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/runtime/runtime-utils.h"
6
7#include "src/arguments.h"
8#include "src/base/bits.h"
9#include "src/bootstrapper.h"
10#include "src/codegen.h"
11#include "src/isolate-inl.h"
12
13namespace v8 {
14namespace internal {
15
16RUNTIME_FUNCTION(Runtime_NumberToRadixString) {
17  HandleScope scope(isolate);
18  DCHECK(args.length() == 2);
19  CONVERT_SMI_ARG_CHECKED(radix, 1);
20  RUNTIME_ASSERT(2 <= radix && radix <= 36);
21
22  // Fast case where the result is a one character string.
23  if (args[0]->IsSmi()) {
24    int value = args.smi_at(0);
25    if (value >= 0 && value < radix) {
26      // Character array used for conversion.
27      static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
28      return *isolate->factory()->LookupSingleCharacterStringFromCode(
29          kCharTable[value]);
30    }
31  }
32
33  // Slow case.
34  CONVERT_DOUBLE_ARG_CHECKED(value, 0);
35  if (std::isnan(value)) {
36    return isolate->heap()->nan_string();
37  }
38  if (std::isinf(value)) {
39    if (value < 0) {
40      return isolate->heap()->minus_infinity_string();
41    }
42    return isolate->heap()->infinity_string();
43  }
44  char* str = DoubleToRadixCString(value, radix);
45  Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
46  DeleteArray(str);
47  return *result;
48}
49
50
51RUNTIME_FUNCTION(Runtime_NumberToFixed) {
52  HandleScope scope(isolate);
53  DCHECK(args.length() == 2);
54
55  CONVERT_DOUBLE_ARG_CHECKED(value, 0);
56  CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
57  int f = FastD2IChecked(f_number);
58  // See DoubleToFixedCString for these constants:
59  RUNTIME_ASSERT(f >= 0 && f <= 20);
60  RUNTIME_ASSERT(!Double(value).IsSpecial());
61  char* str = DoubleToFixedCString(value, f);
62  Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
63  DeleteArray(str);
64  return *result;
65}
66
67
68RUNTIME_FUNCTION(Runtime_NumberToExponential) {
69  HandleScope scope(isolate);
70  DCHECK(args.length() == 2);
71
72  CONVERT_DOUBLE_ARG_CHECKED(value, 0);
73  CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
74  int f = FastD2IChecked(f_number);
75  RUNTIME_ASSERT(f >= -1 && f <= 20);
76  RUNTIME_ASSERT(!Double(value).IsSpecial());
77  char* str = DoubleToExponentialCString(value, f);
78  Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
79  DeleteArray(str);
80  return *result;
81}
82
83
84RUNTIME_FUNCTION(Runtime_NumberToPrecision) {
85  HandleScope scope(isolate);
86  DCHECK(args.length() == 2);
87
88  CONVERT_DOUBLE_ARG_CHECKED(value, 0);
89  CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
90  int f = FastD2IChecked(f_number);
91  RUNTIME_ASSERT(f >= 1 && f <= 21);
92  RUNTIME_ASSERT(!Double(value).IsSpecial());
93  char* str = DoubleToPrecisionCString(value, f);
94  Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str);
95  DeleteArray(str);
96  return *result;
97}
98
99
100RUNTIME_FUNCTION(Runtime_IsValidSmi) {
101  SealHandleScope shs(isolate);
102  DCHECK(args.length() == 1);
103
104  CONVERT_NUMBER_CHECKED(int32_t, number, Int32, args[0]);
105  return isolate->heap()->ToBoolean(Smi::IsValid(number));
106}
107
108
109RUNTIME_FUNCTION(Runtime_StringToNumber) {
110  HandleScope handle_scope(isolate);
111  DCHECK_EQ(1, args.length());
112  CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
113  return *String::ToNumber(subject);
114}
115
116
117// ES6 18.2.5 parseInt(string, radix) slow path
118RUNTIME_FUNCTION(Runtime_StringParseInt) {
119  HandleScope handle_scope(isolate);
120  DCHECK(args.length() == 2);
121  CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
122  CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]);
123  // Step 8.a. is already handled in the JS function.
124  RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
125
126  subject = String::Flatten(subject);
127  double value;
128
129  {
130    DisallowHeapAllocation no_gc;
131    String::FlatContent flat = subject->GetFlatContent();
132
133    if (flat.IsOneByte()) {
134      value =
135          StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(), radix);
136    } else {
137      value = StringToInt(isolate->unicode_cache(), flat.ToUC16Vector(), radix);
138    }
139  }
140
141  return *isolate->factory()->NewNumber(value);
142}
143
144
145// ES6 18.2.4 parseFloat(string)
146RUNTIME_FUNCTION(Runtime_StringParseFloat) {
147  HandleScope shs(isolate);
148  DCHECK(args.length() == 1);
149  CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
150
151  double value =
152      StringToDouble(isolate->unicode_cache(), subject, ALLOW_TRAILING_JUNK,
153                     std::numeric_limits<double>::quiet_NaN());
154
155  return *isolate->factory()->NewNumber(value);
156}
157
158
159RUNTIME_FUNCTION(Runtime_NumberToString) {
160  HandleScope scope(isolate);
161  DCHECK(args.length() == 1);
162  CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
163
164  return *isolate->factory()->NumberToString(number);
165}
166
167
168RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache) {
169  HandleScope scope(isolate);
170  DCHECK(args.length() == 1);
171  CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0);
172
173  return *isolate->factory()->NumberToString(number, false);
174}
175
176
177// TODO(bmeurer): Kill this runtime entry. Uses in date.js are wrong anyway.
178RUNTIME_FUNCTION(Runtime_NumberToIntegerMapMinusZero) {
179  HandleScope scope(isolate);
180  DCHECK(args.length() == 1);
181  CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
182  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, input, Object::ToNumber(input));
183  double double_value = DoubleToInteger(input->Number());
184  // Map both -0 and +0 to +0.
185  if (double_value == 0) double_value = 0;
186
187  return *isolate->factory()->NewNumber(double_value);
188}
189
190
191// Converts a Number to a Smi, if possible. Returns NaN if the number is not
192// a small integer.
193RUNTIME_FUNCTION(Runtime_NumberToSmi) {
194  SealHandleScope shs(isolate);
195  DCHECK(args.length() == 1);
196  CONVERT_ARG_CHECKED(Object, obj, 0);
197  if (obj->IsSmi()) {
198    return obj;
199  }
200  if (obj->IsHeapNumber()) {
201    double value = HeapNumber::cast(obj)->value();
202    int int_value = FastD2I(value);
203    if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
204      return Smi::FromInt(int_value);
205    }
206  }
207  return isolate->heap()->nan_value();
208}
209
210
211RUNTIME_FUNCTION(Runtime_NumberImul) {
212  HandleScope scope(isolate);
213  DCHECK(args.length() == 2);
214
215  // We rely on implementation-defined behavior below, but at least not on
216  // undefined behavior.
217  CONVERT_NUMBER_CHECKED(uint32_t, x, Int32, args[0]);
218  CONVERT_NUMBER_CHECKED(uint32_t, y, Int32, args[1]);
219  int32_t product = static_cast<int32_t>(x * y);
220  return *isolate->factory()->NewNumberFromInt(product);
221}
222
223
224// Compare two Smis as if they were converted to strings and then
225// compared lexicographically.
226RUNTIME_FUNCTION(Runtime_SmiLexicographicCompare) {
227  SealHandleScope shs(isolate);
228  DCHECK(args.length() == 2);
229  CONVERT_SMI_ARG_CHECKED(x_value, 0);
230  CONVERT_SMI_ARG_CHECKED(y_value, 1);
231
232  // If the integers are equal so are the string representations.
233  if (x_value == y_value) return Smi::FromInt(EQUAL);
234
235  // If one of the integers is zero the normal integer order is the
236  // same as the lexicographic order of the string representations.
237  if (x_value == 0 || y_value == 0)
238    return Smi::FromInt(x_value < y_value ? LESS : GREATER);
239
240  // If only one of the integers is negative the negative number is
241  // smallest because the char code of '-' is less than the char code
242  // of any digit.  Otherwise, we make both values positive.
243
244  // Use unsigned values otherwise the logic is incorrect for -MIN_INT on
245  // architectures using 32-bit Smis.
246  uint32_t x_scaled = x_value;
247  uint32_t y_scaled = y_value;
248  if (x_value < 0 || y_value < 0) {
249    if (y_value >= 0) return Smi::FromInt(LESS);
250    if (x_value >= 0) return Smi::FromInt(GREATER);
251    x_scaled = -x_value;
252    y_scaled = -y_value;
253  }
254
255  static const uint32_t kPowersOf10[] = {
256      1,                 10,                100,         1000,
257      10 * 1000,         100 * 1000,        1000 * 1000, 10 * 1000 * 1000,
258      100 * 1000 * 1000, 1000 * 1000 * 1000};
259
260  // If the integers have the same number of decimal digits they can be
261  // compared directly as the numeric order is the same as the
262  // lexicographic order.  If one integer has fewer digits, it is scaled
263  // by some power of 10 to have the same number of digits as the longer
264  // integer.  If the scaled integers are equal it means the shorter
265  // integer comes first in the lexicographic order.
266
267  // From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
268  int x_log2 = 31 - base::bits::CountLeadingZeros32(x_scaled);
269  int x_log10 = ((x_log2 + 1) * 1233) >> 12;
270  x_log10 -= x_scaled < kPowersOf10[x_log10];
271
272  int y_log2 = 31 - base::bits::CountLeadingZeros32(y_scaled);
273  int y_log10 = ((y_log2 + 1) * 1233) >> 12;
274  y_log10 -= y_scaled < kPowersOf10[y_log10];
275
276  int tie = EQUAL;
277
278  if (x_log10 < y_log10) {
279    // X has fewer digits.  We would like to simply scale up X but that
280    // might overflow, e.g when comparing 9 with 1_000_000_000, 9 would
281    // be scaled up to 9_000_000_000. So we scale up by the next
282    // smallest power and scale down Y to drop one digit. It is OK to
283    // drop one digit from the longer integer since the final digit is
284    // past the length of the shorter integer.
285    x_scaled *= kPowersOf10[y_log10 - x_log10 - 1];
286    y_scaled /= 10;
287    tie = LESS;
288  } else if (y_log10 < x_log10) {
289    y_scaled *= kPowersOf10[x_log10 - y_log10 - 1];
290    x_scaled /= 10;
291    tie = GREATER;
292  }
293
294  if (x_scaled < y_scaled) return Smi::FromInt(LESS);
295  if (x_scaled > y_scaled) return Smi::FromInt(GREATER);
296  return Smi::FromInt(tie);
297}
298
299
300RUNTIME_FUNCTION(Runtime_MaxSmi) {
301  SealHandleScope shs(isolate);
302  DCHECK(args.length() == 0);
303  return Smi::FromInt(Smi::kMaxValue);
304}
305
306
307RUNTIME_FUNCTION(Runtime_IsSmi) {
308  SealHandleScope shs(isolate);
309  DCHECK(args.length() == 1);
310  CONVERT_ARG_CHECKED(Object, obj, 0);
311  return isolate->heap()->ToBoolean(obj->IsSmi());
312}
313
314
315RUNTIME_FUNCTION(Runtime_GetRootNaN) {
316  SealHandleScope shs(isolate);
317  DCHECK(args.length() == 0);
318  return isolate->heap()->nan_value();
319}
320
321
322RUNTIME_FUNCTION(Runtime_GetHoleNaNUpper) {
323  HandleScope scope(isolate);
324  DCHECK(args.length() == 0);
325  return *isolate->factory()->NewNumberFromUint(kHoleNanUpper32);
326}
327
328
329RUNTIME_FUNCTION(Runtime_GetHoleNaNLower) {
330  HandleScope scope(isolate);
331  DCHECK(args.length() == 0);
332  return *isolate->factory()->NewNumberFromUint(kHoleNanLower32);
333}
334
335
336}  // namespace internal
337}  // namespace v8
338