Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 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 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 5 | #include "src/runtime/runtime-utils.h" |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 6 | |
| 7 | #include "src/arguments.h" |
| 8 | #include "src/base/bits.h" |
| 9 | #include "src/bootstrapper.h" |
| 10 | #include "src/codegen.h" |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 11 | #include "src/isolate-inl.h" |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 12 | |
| 13 | namespace v8 { |
| 14 | namespace internal { |
| 15 | |
| 16 | RUNTIME_FUNCTION(Runtime_NumberToRadixString) { |
| 17 | HandleScope scope(isolate); |
| 18 | DCHECK(args.length() == 2); |
| 19 | CONVERT_SMI_ARG_CHECKED(radix, 1); |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 20 | CHECK(2 <= radix && radix <= 36); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 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 | |
| 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: |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 59 | CHECK(f >= 0 && f <= 20); |
| 60 | CHECK(!Double(value).IsSpecial()); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 61 | char* str = DoubleToFixedCString(value, f); |
| 62 | Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); |
| 63 | DeleteArray(str); |
| 64 | return *result; |
| 65 | } |
| 66 | |
| 67 | |
| 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); |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 75 | CHECK(f >= -1 && f <= 20); |
| 76 | CHECK(!Double(value).IsSpecial()); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 77 | char* str = DoubleToExponentialCString(value, f); |
| 78 | Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); |
| 79 | DeleteArray(str); |
| 80 | return *result; |
| 81 | } |
| 82 | |
| 83 | |
| 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); |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 91 | CHECK(f >= 1 && f <= 21); |
| 92 | CHECK(!Double(value).IsSpecial()); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 93 | char* str = DoubleToPrecisionCString(value, f); |
| 94 | Handle<String> result = isolate->factory()->NewStringFromAsciiChecked(str); |
| 95 | DeleteArray(str); |
| 96 | return *result; |
| 97 | } |
| 98 | |
| 99 | |
| 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 | |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 109 | RUNTIME_FUNCTION(Runtime_StringToNumber) { |
| 110 | HandleScope handle_scope(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 111 | DCHECK_EQ(1, args.length()); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 112 | CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 113 | return *String::ToNumber(subject); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 117 | // ES6 18.2.5 parseInt(string, radix) slow path |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 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]); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 123 | // Step 8.a. is already handled in the JS function. |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 124 | CHECK(radix == 0 || (2 <= radix && radix <= 36)); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 125 | |
| 126 | subject = String::Flatten(subject); |
| 127 | double value; |
| 128 | |
| 129 | { |
| 130 | DisallowHeapAllocation no_gc; |
| 131 | String::FlatContent flat = subject->GetFlatContent(); |
| 132 | |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 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 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 145 | // ES6 18.2.4 parseFloat(string) |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 146 | RUNTIME_FUNCTION(Runtime_StringParseFloat) { |
| 147 | HandleScope shs(isolate); |
| 148 | DCHECK(args.length() == 1); |
| 149 | CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); |
| 150 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 151 | double value = |
| 152 | StringToDouble(isolate->unicode_cache(), subject, ALLOW_TRAILING_JUNK, |
| 153 | std::numeric_limits<double>::quiet_NaN()); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 154 | |
| 155 | return *isolate->factory()->NewNumber(value); |
| 156 | } |
| 157 | |
| 158 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 159 | RUNTIME_FUNCTION(Runtime_NumberToString) { |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 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 | |
| 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 | |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 177 | // Converts a Number to a Smi, if possible. Returns NaN if the number is not |
| 178 | // a small integer. |
| 179 | RUNTIME_FUNCTION(Runtime_NumberToSmi) { |
| 180 | SealHandleScope shs(isolate); |
| 181 | DCHECK(args.length() == 1); |
| 182 | CONVERT_ARG_CHECKED(Object, obj, 0); |
| 183 | if (obj->IsSmi()) { |
| 184 | return obj; |
| 185 | } |
| 186 | if (obj->IsHeapNumber()) { |
| 187 | double value = HeapNumber::cast(obj)->value(); |
| 188 | int int_value = FastD2I(value); |
| 189 | if (value == FastI2D(int_value) && Smi::IsValid(int_value)) { |
| 190 | return Smi::FromInt(int_value); |
| 191 | } |
| 192 | } |
| 193 | return isolate->heap()->nan_value(); |
| 194 | } |
| 195 | |
| 196 | |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 197 | // Compare two Smis as if they were converted to strings and then |
| 198 | // compared lexicographically. |
| 199 | RUNTIME_FUNCTION(Runtime_SmiLexicographicCompare) { |
| 200 | SealHandleScope shs(isolate); |
| 201 | DCHECK(args.length() == 2); |
| 202 | CONVERT_SMI_ARG_CHECKED(x_value, 0); |
| 203 | CONVERT_SMI_ARG_CHECKED(y_value, 1); |
| 204 | |
| 205 | // If the integers are equal so are the string representations. |
| 206 | if (x_value == y_value) return Smi::FromInt(EQUAL); |
| 207 | |
| 208 | // If one of the integers is zero the normal integer order is the |
| 209 | // same as the lexicographic order of the string representations. |
| 210 | if (x_value == 0 || y_value == 0) |
| 211 | return Smi::FromInt(x_value < y_value ? LESS : GREATER); |
| 212 | |
| 213 | // If only one of the integers is negative the negative number is |
| 214 | // smallest because the char code of '-' is less than the char code |
| 215 | // of any digit. Otherwise, we make both values positive. |
| 216 | |
| 217 | // Use unsigned values otherwise the logic is incorrect for -MIN_INT on |
| 218 | // architectures using 32-bit Smis. |
| 219 | uint32_t x_scaled = x_value; |
| 220 | uint32_t y_scaled = y_value; |
| 221 | if (x_value < 0 || y_value < 0) { |
| 222 | if (y_value >= 0) return Smi::FromInt(LESS); |
| 223 | if (x_value >= 0) return Smi::FromInt(GREATER); |
| 224 | x_scaled = -x_value; |
| 225 | y_scaled = -y_value; |
| 226 | } |
| 227 | |
| 228 | static const uint32_t kPowersOf10[] = { |
| 229 | 1, 10, 100, 1000, |
| 230 | 10 * 1000, 100 * 1000, 1000 * 1000, 10 * 1000 * 1000, |
| 231 | 100 * 1000 * 1000, 1000 * 1000 * 1000}; |
| 232 | |
| 233 | // If the integers have the same number of decimal digits they can be |
| 234 | // compared directly as the numeric order is the same as the |
| 235 | // lexicographic order. If one integer has fewer digits, it is scaled |
| 236 | // by some power of 10 to have the same number of digits as the longer |
| 237 | // integer. If the scaled integers are equal it means the shorter |
| 238 | // integer comes first in the lexicographic order. |
| 239 | |
| 240 | // From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 |
| 241 | int x_log2 = 31 - base::bits::CountLeadingZeros32(x_scaled); |
| 242 | int x_log10 = ((x_log2 + 1) * 1233) >> 12; |
| 243 | x_log10 -= x_scaled < kPowersOf10[x_log10]; |
| 244 | |
| 245 | int y_log2 = 31 - base::bits::CountLeadingZeros32(y_scaled); |
| 246 | int y_log10 = ((y_log2 + 1) * 1233) >> 12; |
| 247 | y_log10 -= y_scaled < kPowersOf10[y_log10]; |
| 248 | |
| 249 | int tie = EQUAL; |
| 250 | |
| 251 | if (x_log10 < y_log10) { |
| 252 | // X has fewer digits. We would like to simply scale up X but that |
| 253 | // might overflow, e.g when comparing 9 with 1_000_000_000, 9 would |
| 254 | // be scaled up to 9_000_000_000. So we scale up by the next |
| 255 | // smallest power and scale down Y to drop one digit. It is OK to |
| 256 | // drop one digit from the longer integer since the final digit is |
| 257 | // past the length of the shorter integer. |
| 258 | x_scaled *= kPowersOf10[y_log10 - x_log10 - 1]; |
| 259 | y_scaled /= 10; |
| 260 | tie = LESS; |
| 261 | } else if (y_log10 < x_log10) { |
| 262 | y_scaled *= kPowersOf10[x_log10 - y_log10 - 1]; |
| 263 | x_scaled /= 10; |
| 264 | tie = GREATER; |
| 265 | } |
| 266 | |
| 267 | if (x_scaled < y_scaled) return Smi::FromInt(LESS); |
| 268 | if (x_scaled > y_scaled) return Smi::FromInt(GREATER); |
| 269 | return Smi::FromInt(tie); |
| 270 | } |
| 271 | |
| 272 | |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 273 | RUNTIME_FUNCTION(Runtime_MaxSmi) { |
| 274 | SealHandleScope shs(isolate); |
| 275 | DCHECK(args.length() == 0); |
| 276 | return Smi::FromInt(Smi::kMaxValue); |
| 277 | } |
| 278 | |
| 279 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 280 | RUNTIME_FUNCTION(Runtime_IsSmi) { |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 281 | SealHandleScope shs(isolate); |
| 282 | DCHECK(args.length() == 1); |
| 283 | CONVERT_ARG_CHECKED(Object, obj, 0); |
| 284 | return isolate->heap()->ToBoolean(obj->IsSmi()); |
| 285 | } |
| 286 | |
| 287 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 288 | RUNTIME_FUNCTION(Runtime_GetRootNaN) { |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 289 | SealHandleScope shs(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 290 | DCHECK(args.length() == 0); |
| 291 | return isolate->heap()->nan_value(); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 292 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 293 | |
| 294 | |
| 295 | RUNTIME_FUNCTION(Runtime_GetHoleNaNUpper) { |
| 296 | HandleScope scope(isolate); |
| 297 | DCHECK(args.length() == 0); |
| 298 | return *isolate->factory()->NewNumberFromUint(kHoleNanUpper32); |
Emily Bernier | d0a1eb7 | 2015-03-24 16:35:39 -0400 | [diff] [blame] | 299 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 300 | |
| 301 | |
| 302 | RUNTIME_FUNCTION(Runtime_GetHoleNaNLower) { |
| 303 | HandleScope scope(isolate); |
| 304 | DCHECK(args.length() == 0); |
| 305 | return *isolate->factory()->NewNumberFromUint(kHoleNanLower32); |
| 306 | } |
| 307 | |
| 308 | |
| 309 | } // namespace internal |
| 310 | } // namespace v8 |