diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 2529224e6a..078f1e7c24 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1349,6 +1349,13 @@ void Genesis::InitializeGlobal(Handle global_object, SimpleInstallFunction(number_fun, "isNaN", Builtins::kNumberIsNaN, 1, true); SimpleInstallFunction(number_fun, "isSafeInteger", Builtins::kNumberIsSafeInteger, 1, true); + + // Install Number.parseFloat and Global.parseFloat. + Handle parse_float_fun = SimpleInstallFunction( + number_fun, "parseFloat", Builtins::kNumberParseFloat, 1, true); + JSObject::AddProperty(global_object, + factory->NewStringFromAsciiChecked("parseFloat"), + parse_float_fun, DONT_ENUM); } { // --- B o o l e a n --- diff --git a/src/builtins/builtins-number.cc b/src/builtins/builtins-number.cc index 6b894d3e47..0d2f5e0cf4 100644 --- a/src/builtins/builtins-number.cc +++ b/src/builtins/builtins-number.cc @@ -150,6 +150,112 @@ void Builtins::Generate_NumberIsSafeInteger(CodeStubAssembler* assembler) { assembler->Return(assembler->BooleanConstant(false)); } +// ES6 section 20.1.2.12 Number.parseFloat ( string ) +void Builtins::Generate_NumberParseFloat(CodeStubAssembler* assembler) { + typedef CodeStubAssembler::Label Label; + typedef compiler::Node Node; + typedef CodeStubAssembler::Variable Variable; + + Node* context = assembler->Parameter(4); + + // We might need to loop once for ToString conversion. + Variable var_input(assembler, MachineRepresentation::kTagged); + Label loop(assembler, &var_input); + var_input.Bind(assembler->Parameter(1)); + assembler->Goto(&loop); + assembler->Bind(&loop); + { + // Load the current {input} value. + Node* input = var_input.value(); + + // Check if the {input} is a HeapObject or a Smi. + Label if_inputissmi(assembler), if_inputisnotsmi(assembler); + assembler->Branch(assembler->WordIsSmi(input), &if_inputissmi, + &if_inputisnotsmi); + + assembler->Bind(&if_inputissmi); + { + // The {input} is already a Number, no need to do anything. + assembler->Return(input); + } + + assembler->Bind(&if_inputisnotsmi); + { + // The {input} is a HeapObject, check if it's already a String. + Label if_inputisstring(assembler), if_inputisnotstring(assembler); + Node* input_map = assembler->LoadMap(input); + Node* input_instance_type = assembler->LoadMapInstanceType(input_map); + assembler->Branch(assembler->IsStringInstanceType(input_instance_type), + &if_inputisstring, &if_inputisnotstring); + + assembler->Bind(&if_inputisstring); + { + // The {input} is already a String, check if {input} contains + // a cached array index. + Label if_inputcached(assembler), if_inputnotcached(assembler); + Node* input_hash = assembler->LoadNameHashField(input); + Node* input_bit = assembler->Word32And( + input_hash, + assembler->Int32Constant(String::kContainsCachedArrayIndexMask)); + assembler->Branch( + assembler->Word32Equal(input_bit, assembler->Int32Constant(0)), + &if_inputcached, &if_inputnotcached); + + assembler->Bind(&if_inputcached); + { + // Just return the {input}s cached array index. + Node* input_array_index = + assembler->BitFieldDecodeWord( + input_hash); + assembler->Return(assembler->SmiTag(input_array_index)); + } + + assembler->Bind(&if_inputnotcached); + { + // Need to fall back to the runtime to convert {input} to double. + assembler->Return(assembler->CallRuntime(Runtime::kStringParseFloat, + context, input)); + } + } + + assembler->Bind(&if_inputisnotstring); + { + // The {input} is neither a String nor a Smi, check for HeapNumber. + Label if_inputisnumber(assembler), + if_inputisnotnumber(assembler, Label::kDeferred); + assembler->Branch( + assembler->WordEqual(input_map, assembler->HeapNumberMapConstant()), + &if_inputisnumber, &if_inputisnotnumber); + + assembler->Bind(&if_inputisnumber); + { + // The {input} is already a Number, take care of -0. + Label if_inputiszero(assembler), if_inputisnotzero(assembler); + Node* input_value = assembler->LoadHeapNumberValue(input); + assembler->Branch(assembler->Float64Equal( + input_value, assembler->Float64Constant(0.0)), + &if_inputiszero, &if_inputisnotzero); + + assembler->Bind(&if_inputiszero); + assembler->Return(assembler->SmiConstant(0)); + + assembler->Bind(&if_inputisnotzero); + assembler->Return(input); + } + + assembler->Bind(&if_inputisnotnumber); + { + // Need to convert the {input} to String first. + // TODO(bmeurer): This could be more efficient if necessary. + Callable callable = CodeFactory::ToString(assembler->isolate()); + var_input.Bind(assembler->CallStub(callable, context, input)); + assembler->Goto(&loop); + } + } + } + } +} + // ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits ) BUILTIN(NumberPrototypeToExponential) { HandleScope scope(isolate); diff --git a/src/builtins/builtins.h b/src/builtins/builtins.h index 83f49e6803..081fb18a66 100644 --- a/src/builtins/builtins.h +++ b/src/builtins/builtins.h @@ -480,6 +480,8 @@ namespace internal { TFJ(NumberIsNaN, 2) \ /* ES6 section 20.1.2.5 Number.isSafeInteger ( number ) */ \ TFJ(NumberIsSafeInteger, 2) \ + /* ES6 section 20.1.2.12 Number.parseFloat ( string ) */ \ + TFJ(NumberParseFloat, 2) \ CPP(NumberPrototypeToExponential) \ CPP(NumberPrototypeToFixed) \ CPP(NumberPrototypeToLocaleString) \ diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index ac6fda4877..8fae69aac6 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1362,6 +1362,8 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) { case kNumberIsNaN: case kNumberIsSafeInteger: return Type::Boolean(); + case kNumberParseFloat: + return Type::Number(); case kNumberParseInt: return t->cache_.kIntegerOrMinusZeroOrNaN; case kNumberToString: diff --git a/src/js/v8natives.js b/src/js/v8natives.js index 93636a036b..0a05881bdf 100644 --- a/src/js/v8natives.js +++ b/src/js/v8natives.js @@ -52,15 +52,6 @@ function GlobalParseInt(string, radix) { } -// ES6 18.2.4 parseFloat(string) -function GlobalParseFloat(string) { - // 1. Let inputString be ? ToString(string). - string = TO_STRING(string); - if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string); - return %StringParseFloat(string); -} - - // ---------------------------------------------------------------------------- // Set up global object. @@ -78,7 +69,6 @@ utils.InstallConstants(global, [ // Set up non-enumerable function on the global object. utils.InstallFunctions(global, DONT_ENUM, [ "parseInt", GlobalParseInt, - "parseFloat", GlobalParseFloat, ]); @@ -213,7 +203,6 @@ utils.InstallConstants(GlobalNumber, [ // Harmony Number constructor additions utils.InstallFunctions(GlobalNumber, DONT_ENUM, [ "parseInt", GlobalParseInt, - "parseFloat", GlobalParseFloat ]); diff --git a/src/objects.h b/src/objects.h index ad69e6e92a..123d1c229c 100644 --- a/src/objects.h +++ b/src/objects.h @@ -7071,6 +7071,7 @@ class Script: public Struct { V(Number, isInteger, NumberIsInteger) \ V(Number, isNaN, NumberIsNaN) \ V(Number, isSafeInteger, NumberIsSafeInteger) \ + V(Number, parseFloat, NumberParseFloat) \ V(Number, parseInt, NumberParseInt) \ V(Number.prototype, toString, NumberToString) diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc index 37992fa7e7..5e0b7b3c7a 100644 --- a/test/cctest/test-serialize.cc +++ b/test/cctest/test-serialize.cc @@ -826,7 +826,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) { const char* source = "function f() { return Math.abs(1); }\n" "function g() { return Number.parseInt(1); }\n" - "Number.parseFloat(1);" + "Object.valueOf(1);" "var a = 5"; const char* warmup = "a = f()"; @@ -851,7 +851,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) { CHECK(IsCompiled("Math.abs")); CHECK(!IsCompiled("g")); CHECK(!IsCompiled("Number.parseInt")); - CHECK(!IsCompiled("Number.parseFloat")); + CHECK(!IsCompiled("Object.valueOf")); CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust()); } isolate->Dispose();