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