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
13 namespace v8 {
14 namespace internal {
15
RUNTIME_FUNCTION(Runtime_NumberToRadixString)16 RUNTIME_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
RUNTIME_FUNCTION(Runtime_NumberToFixed)51 RUNTIME_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
RUNTIME_FUNCTION(Runtime_NumberToExponential)68 RUNTIME_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
RUNTIME_FUNCTION(Runtime_NumberToPrecision)84 RUNTIME_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
RUNTIME_FUNCTION(Runtime_IsValidSmi)100 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StringToNumber)109 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StringParseInt)118 RUNTIME_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)
RUNTIME_FUNCTION(Runtime_StringParseFloat)146 RUNTIME_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
RUNTIME_FUNCTION(Runtime_NumberToString)159 RUNTIME_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
RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache)168 RUNTIME_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.
RUNTIME_FUNCTION(Runtime_NumberToIntegerMapMinusZero)178 RUNTIME_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.
RUNTIME_FUNCTION(Runtime_NumberToSmi)193 RUNTIME_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
RUNTIME_FUNCTION(Runtime_NumberImul)211 RUNTIME_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.
RUNTIME_FUNCTION(Runtime_SmiLexicographicCompare)226 RUNTIME_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
RUNTIME_FUNCTION(Runtime_MaxSmi)300 RUNTIME_FUNCTION(Runtime_MaxSmi) {
301 SealHandleScope shs(isolate);
302 DCHECK(args.length() == 0);
303 return Smi::FromInt(Smi::kMaxValue);
304 }
305
306
RUNTIME_FUNCTION(Runtime_IsSmi)307 RUNTIME_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
RUNTIME_FUNCTION(Runtime_GetRootNaN)315 RUNTIME_FUNCTION(Runtime_GetRootNaN) {
316 SealHandleScope shs(isolate);
317 DCHECK(args.length() == 0);
318 return isolate->heap()->nan_value();
319 }
320
321
RUNTIME_FUNCTION(Runtime_GetHoleNaNUpper)322 RUNTIME_FUNCTION(Runtime_GetHoleNaNUpper) {
323 HandleScope scope(isolate);
324 DCHECK(args.length() == 0);
325 return *isolate->factory()->NewNumberFromUint(kHoleNanUpper32);
326 }
327
328
RUNTIME_FUNCTION(Runtime_GetHoleNaNLower)329 RUNTIME_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