From: bmeurer Date: Fri, 28 Aug 2015 09:21:23 +0000 (-0700) Subject: [es6] Implement spec compliant ToPrimitive in the runtime. X-Git-Tag: upstream/4.7.83~606 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f6c6d713b4a1bb94457571f9e627ce2dea531b68;p=platform%2Fupstream%2Fv8.git [es6] Implement spec compliant ToPrimitive in the runtime. This is the first step towards a spec compliant ToPrimitive implementation (and therefore spec compliant ToNumber, ToString, ToName, and friends). It adds support for the @@toPrimitive symbol that was introduced with ES2015, and also adds the new Symbol.prototype[@@toPrimitive] and Date.prototype[@@toPrimitive] initial properties. There are now runtime functions for %ToPrimitive, %ToNumber and %ToString, which do the right thing and should be used as fallbacks instead of the hairy runtime.js implementations. I will do the same for the other conversion operations mentioned by the spec in follow up CLs. Once everything is in place we can look into optimizing things further, so that we don't always call into the runtime. Also fixed Date.prototype.toJSON to be spec compliant. R=mstarzinger@chromium.org, yangguo@chromium.org BUG=v8:4307 LOG=y Review URL: https://codereview.chromium.org/1306303003 Cr-Commit-Position: refs/heads/master@{#30434} --- diff --git a/src/accessors.cc b/src/accessors.cc index b18cd9967..c1951b962 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -216,7 +216,7 @@ void Accessors::ArrayLengthSetter( } Handle number_v; - if (!Execution::ToNumber(isolate, length_obj).ToHandle(&number_v)) { + if (!Object::ToNumber(isolate, length_obj).ToHandle(&number_v)) { isolate->OptionalRescheduleException(false); return; } diff --git a/src/api.cc b/src/api.cc index 0fb4154b6..42a887582 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2854,7 +2854,7 @@ MaybeLocal Value::ToString(Local context) const { PREPARE_FOR_EXECUTION(context, "ToString", String); Local result; has_pending_exception = - !ToLocal(i::Execution::ToString(isolate, obj), &result); + !ToLocal(i::Object::ToString(isolate, obj), &result); RETURN_ON_FAILED_EXECUTION(String); RETURN_ESCAPED(result); } @@ -2920,7 +2920,7 @@ MaybeLocal Value::ToNumber(Local context) const { PREPARE_FOR_EXECUTION(context, "ToNumber", Number); Local result; has_pending_exception = - !ToLocal(i::Execution::ToNumber(isolate, obj), &result); + !ToLocal(i::Object::ToNumber(isolate, obj), &result); RETURN_ON_FAILED_EXECUTION(Number); RETURN_ESCAPED(result); } @@ -3244,7 +3244,7 @@ Maybe Value::NumberValue(Local context) const { if (obj->IsNumber()) return Just(obj->Number()); PREPARE_FOR_EXECUTION_PRIMITIVE(context, "NumberValue", double); i::Handle num; - has_pending_exception = !i::Execution::ToNumber(isolate, obj).ToHandle(&num); + has_pending_exception = !i::Object::ToNumber(isolate, obj).ToHandle(&num); RETURN_ON_FAILED_EXECUTION_PRIMITIVE(double); return Just(num->Number()); } @@ -3334,7 +3334,7 @@ MaybeLocal Value::ToArrayIndex(Local context) const { PREPARE_FOR_EXECUTION(context, "ToArrayIndex", Uint32); i::Handle string_obj; has_pending_exception = - !i::Execution::ToString(isolate, self).ToHandle(&string_obj); + !i::Object::ToString(isolate, self).ToHandle(&string_obj); RETURN_ON_FAILED_EXECUTION(Uint32); i::Handle str = i::Handle::cast(string_obj); uint32_t index; @@ -3614,8 +3614,8 @@ Maybe v8::Object::GetPropertyAttributes( auto self = Utils::OpenHandle(this); auto key_obj = Utils::OpenHandle(*key); if (!key_obj->IsName()) { - has_pending_exception = !i::Execution::ToString( - isolate, key_obj).ToHandle(&key_obj); + has_pending_exception = + !i::Object::ToString(isolate, key_obj).ToHandle(&key_obj); RETURN_ON_FAILED_EXECUTION_PRIMITIVE(PropertyAttribute); } auto key_name = i::Handle::cast(key_obj); diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 5bd400848..695bf33dc 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3264,7 +3264,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ bind(¬_oddball); __ push(r0); // Push argument. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc index 4bd582ff2..4555a81c5 100644 --- a/src/arm64/code-stubs-arm64.cc +++ b/src/arm64/code-stubs-arm64.cc @@ -3995,7 +3995,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ Bind(¬_oddball); __ Push(x0); // Push argument. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index 93112be1a..078e5adf9 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -245,6 +245,11 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) { case Runtime::kInlineGetPrototype: case Runtime::kInlineRegExpExec: case Runtime::kInlineToObject: + case Runtime::kInlineToPrimitive: + case Runtime::kInlineToPrimitive_Number: + case Runtime::kInlineToPrimitive_String: + case Runtime::kInlineOrdinaryToPrimitive: + case Runtime::kInlineToNumber: return 1; case Runtime::kInlineDeoptimizeNow: case Runtime::kInlineThrowNotDateError: diff --git a/src/contexts.h b/src/contexts.h index 7c4ebc0c2..1abd6f2df 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -136,7 +136,6 @@ enum BindingFlags { V(SUB_BUILTIN_INDEX, JSFunction, sub_builtin) \ V(SUB_STRONG_BUILTIN_INDEX, JSFunction, sub_strong_builtin) \ V(TO_NAME_BUILTIN_INDEX, JSFunction, to_name_builtin) \ - V(TO_NUMBER_BUILTIN_INDEX, JSFunction, to_number_builtin) \ V(TO_STRING_BUILTIN_INDEX, JSFunction, to_string_builtin) diff --git a/src/date.js b/src/date.js index 2be8bcb22..44d2f4df1 100644 --- a/src/date.js +++ b/src/date.js @@ -364,6 +364,21 @@ function DateToLocaleTimeString() { } +// 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint ) +function DateToPrimitive(hint) { + if (!IS_SPEC_OBJECT(this)) { + throw MakeTypeError(kIncompatibleMethodReceiver, + "Date.prototype [ @@toPrimitive ]", this); + } + if (hint === "default") { + hint = "string"; + } else if (hint !== "number" && hint !== "string") { + throw MakeTypeError(kInvalidHint, hint); + } + return %OrdinaryToPrimitive(this, hint); +} + + // ECMA 262 - 15.9.5.8 function DateValueOf() { CHECK_DATE(this); @@ -777,9 +792,10 @@ function DateToISOString() { } +// 20.3.4.37 Date.prototype.toJSON ( key ) function DateToJSON(key) { var o = TO_OBJECT(this); - var tv = $defaultNumber(o); + var tv = TO_PRIMITIVE_NUMBER(o); if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) { return null; } @@ -831,6 +847,9 @@ utils.InstallFunctions(GlobalDate, DONT_ENUM, [ // Set up non-enumerable constructor property of the Date prototype object. %AddNamedProperty(GlobalDate.prototype, "constructor", GlobalDate, DONT_ENUM); +utils.SetFunctionName(DateToPrimitive, symbolToPrimitive); +%AddNamedProperty(GlobalDate.prototype, symbolToPrimitive, DateToPrimitive, + DONT_ENUM | READ_ONLY); // Set up non-enumerable functions of the Date prototype object and // set their names. diff --git a/src/debug/debug.cc b/src/debug/debug.cc index 99036d71e..de7cc2315 100644 --- a/src/debug/debug.cc +++ b/src/debug/debug.cc @@ -2148,7 +2148,7 @@ void Debug::NotifyMessageHandler(v8::DebugEvent event, Handle exception; if (!maybe_exception.ToHandle(&exception)) break; Handle result; - if (!Execution::ToString(isolate_, exception).ToHandle(&result)) break; + if (!Object::ToString(isolate_, exception).ToHandle(&result)) break; answer = Handle::cast(result); } diff --git a/src/execution.cc b/src/execution.cc index 4ab2403e5..64e5e39f9 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -544,18 +544,6 @@ void StackGuard::InitThread(const ExecutionAccess& lock) { } while (false) -MaybeHandle Execution::ToNumber( - Isolate* isolate, Handle obj) { - RETURN_NATIVE_CALL(to_number, { obj }); -} - - -MaybeHandle Execution::ToString( - Isolate* isolate, Handle obj) { - RETURN_NATIVE_CALL(to_string, { obj }); -} - - MaybeHandle Execution::ToDetailString( Isolate* isolate, Handle obj) { RETURN_NATIVE_CALL(to_detail_string, { obj }); @@ -584,7 +572,7 @@ MaybeHandle Execution::NewDate(Isolate* isolate, double time) { MaybeHandle Execution::ToInt32(Isolate* isolate, Handle obj) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, Execution::ToNumber(isolate, obj), + ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, Object::ToNumber(isolate, obj), Object); return isolate->factory()->NewNumberFromInt(DoubleToInt32(obj->Number())); } @@ -601,7 +589,7 @@ MaybeHandle Execution::ToObject(Isolate* isolate, Handle obj) { MaybeHandle Execution::ToUint32(Isolate* isolate, Handle obj) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, Execution::ToNumber(isolate, obj), + ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, Object::ToNumber(isolate, obj), Object); return isolate->factory()->NewNumberFromUint(DoubleToUint32(obj->Number())); } diff --git a/src/execution.h b/src/execution.h index 2c07a64aa..d7996d190 100644 --- a/src/execution.h +++ b/src/execution.h @@ -59,10 +59,6 @@ class Execution final : public AllStatic { Handle argv[], MaybeHandle* exception_out = NULL); - // ECMA-262 9.3 - MUST_USE_RESULT static MaybeHandle ToNumber( - Isolate* isolate, Handle obj); - // ECMA-262 9.4 MUST_USE_RESULT static MaybeHandle ToInteger( Isolate* isolate, Handle obj); @@ -80,10 +76,6 @@ class Execution final : public AllStatic { MUST_USE_RESULT static MaybeHandle ToLength( Isolate* isolate, Handle obj); - // ECMA-262 9.8 - MUST_USE_RESULT static MaybeHandle ToString( - Isolate* isolate, Handle obj); - // ECMA-262 9.8 MUST_USE_RESULT static MaybeHandle ToDetailString( Isolate* isolate, Handle obj); diff --git a/src/full-codegen/arm/full-codegen-arm.cc b/src/full-codegen/arm/full-codegen-arm.cc index ef65b31c5..ca25d6ea5 100644 --- a/src/full-codegen/arm/full-codegen-arm.cc +++ b/src/full-codegen/arm/full-codegen-arm.cc @@ -3468,7 +3468,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( // string "valueOf" the result is false. // The use of ip to store the valueOf string assumes that it is not otherwise // used in the loop below. - __ mov(ip, Operand(isolate()->factory()->value_of_string())); + __ LoadRoot(ip, Heap::kvalueOf_stringRootIndex); __ jmp(&entry); __ bind(&loop); __ ldr(r3, MemOperand(r4, 0)); diff --git a/src/full-codegen/arm64/full-codegen-arm64.cc b/src/full-codegen/arm64/full-codegen-arm64.cc index a9ec2aa3b..55ac3f5f9 100644 --- a/src/full-codegen/arm64/full-codegen-arm64.cc +++ b/src/full-codegen/arm64/full-codegen-arm64.cc @@ -3169,7 +3169,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( // string "valueOf" the result is false. Register valueof_string = x1; int descriptor_size = DescriptorArray::kDescriptorSize * kPointerSize; - __ Mov(valueof_string, Operand(isolate()->factory()->value_of_string())); + __ LoadRoot(valueof_string, Heap::kvalueOf_stringRootIndex); __ Bind(&loop); __ Ldr(x15, MemOperand(descriptors, descriptor_size, PostIndex)); __ Cmp(x15, valueof_string); diff --git a/src/full-codegen/ia32/full-codegen-ia32.cc b/src/full-codegen/ia32/full-codegen-ia32.cc index 8bdd8d456..1cf1416bd 100644 --- a/src/full-codegen/ia32/full-codegen-ia32.cc +++ b/src/full-codegen/ia32/full-codegen-ia32.cc @@ -3363,7 +3363,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ jmp(&entry); __ bind(&loop); __ mov(edx, FieldOperand(ebx, 0)); - __ cmp(edx, isolate()->factory()->value_of_string()); + __ cmp(edx, isolate()->factory()->valueOf_string()); __ j(equal, if_false); __ add(ebx, Immediate(DescriptorArray::kDescriptorSize * kPointerSize)); __ bind(&entry); diff --git a/src/full-codegen/mips/full-codegen-mips.cc b/src/full-codegen/mips/full-codegen-mips.cc index 748689624..9baeff034 100644 --- a/src/full-codegen/mips/full-codegen-mips.cc +++ b/src/full-codegen/mips/full-codegen-mips.cc @@ -3462,7 +3462,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( // string "valueOf" the result is false. // The use of t2 to store the valueOf string assumes that it is not otherwise // used in the loop below. - __ li(t2, Operand(isolate()->factory()->value_of_string())); + __ LoadRoot(t2, Heap::kvalueOf_stringRootIndex); __ jmp(&entry); __ bind(&loop); __ lw(a3, MemOperand(t0, 0)); diff --git a/src/full-codegen/mips64/full-codegen-mips64.cc b/src/full-codegen/mips64/full-codegen-mips64.cc index c4b924bf1..10e458041 100644 --- a/src/full-codegen/mips64/full-codegen-mips64.cc +++ b/src/full-codegen/mips64/full-codegen-mips64.cc @@ -3464,7 +3464,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( // string "valueOf" the result is false. // The use of a6 to store the valueOf string assumes that it is not otherwise // used in the loop below. - __ li(a6, Operand(isolate()->factory()->value_of_string())); + __ LoadRoot(a6, Heap::kvalueOf_stringRootIndex); __ jmp(&entry); __ bind(&loop); __ ld(a3, MemOperand(a4, 0)); diff --git a/src/full-codegen/ppc/full-codegen-ppc.cc b/src/full-codegen/ppc/full-codegen-ppc.cc index 634ccdbd2..c9b11ba4c 100644 --- a/src/full-codegen/ppc/full-codegen-ppc.cc +++ b/src/full-codegen/ppc/full-codegen-ppc.cc @@ -3461,7 +3461,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( // string "valueOf" the result is false. // The use of ip to store the valueOf string assumes that it is not otherwise // used in the loop below. - __ mov(ip, Operand(isolate()->factory()->value_of_string())); + __ LoadRoot(ip, Heap::kvalueOf_stringRootIndex); __ b(&entry); __ bind(&loop); __ LoadP(r6, MemOperand(r7, 0)); diff --git a/src/full-codegen/x64/full-codegen-x64.cc b/src/full-codegen/x64/full-codegen-x64.cc index 9ef1faea3..d521fcd97 100644 --- a/src/full-codegen/x64/full-codegen-x64.cc +++ b/src/full-codegen/x64/full-codegen-x64.cc @@ -3353,7 +3353,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ jmp(&entry); __ bind(&loop); __ movp(rdx, FieldOperand(r8, 0)); - __ Cmp(rdx, isolate()->factory()->value_of_string()); + __ CompareRoot(rdx, Heap::kvalueOf_stringRootIndex); __ j(equal, if_false); __ addp(r8, Immediate(DescriptorArray::kDescriptorSize * kPointerSize)); __ bind(&entry); diff --git a/src/full-codegen/x87/full-codegen-x87.cc b/src/full-codegen/x87/full-codegen-x87.cc index 9e2d95cbf..82e754e6a 100644 --- a/src/full-codegen/x87/full-codegen-x87.cc +++ b/src/full-codegen/x87/full-codegen-x87.cc @@ -3354,7 +3354,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ jmp(&entry); __ bind(&loop); __ mov(edx, FieldOperand(ebx, 0)); - __ cmp(edx, isolate()->factory()->value_of_string()); + __ cmp(edx, isolate()->factory()->valueOf_string()); __ j(equal, if_false); __ add(ebx, Immediate(DescriptorArray::kDescriptorSize * kPointerSize)); __ bind(&entry); diff --git a/src/heap/heap.h b/src/heap/heap.h index 97d32bbe9..826933dd9 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -218,6 +218,7 @@ namespace internal { V(Boolean_string, "Boolean") \ V(callee_string, "callee") \ V(constructor_string, "constructor") \ + V(default_string, "default") \ V(dot_result_string, ".result") \ V(eval_string, "eval") \ V(float32x4_string, "float32x4") \ @@ -278,8 +279,9 @@ namespace internal { V(Date_string, "Date") \ V(char_at_string, "CharAt") \ V(undefined_string, "undefined") \ - V(value_of_string, "valueOf") \ + V(valueOf_string, "valueOf") \ V(stack_string, "stack") \ + V(toString_string, "toString") \ V(toJSON_string, "toJSON") \ V(KeyedLoadMonomorphic_string, "KeyedLoadMonomorphic") \ V(KeyedStoreMonomorphic_string, "KeyedStoreMonomorphic") \ @@ -353,6 +355,7 @@ namespace internal { Symbol.isConcatSpreadable) \ V(is_regexp_symbol, symbolIsRegExp, Symbol.isRegExp) \ V(iterator_symbol, symbolIterator, Symbol.iterator) \ + V(to_primitive_symbol, symbolToPrimitive, Symbol.toPrimitive) \ V(to_string_tag_symbol, symbolToStringTag, Symbol.toStringTag) \ V(unscopables_symbol, symbolUnscopables, Symbol.unscopables) diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 0590ab8f5..bfabb1f78 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -3271,7 +3271,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ pop(ecx); // Pop return address. __ push(eax); // Push argument. __ push(ecx); // Push return address. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/src/json-stringifier.h b/src/json-stringifier.h index fb6b80dde..3744ea78d 100644 --- a/src/json-stringifier.h +++ b/src/json-stringifier.h @@ -382,12 +382,12 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSValue( if (class_name == isolate_->heap()->String_string()) { Handle value; ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate_, value, Execution::ToString(isolate_, object), EXCEPTION); + isolate_, value, Object::ToString(isolate_, object), EXCEPTION); SerializeString(Handle::cast(value)); } else if (class_name == isolate_->heap()->Number_string()) { Handle value; ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate_, value, Execution::ToNumber(isolate_, object), EXCEPTION); + isolate_, value, Object::ToNumber(isolate_, object), EXCEPTION); if (value->IsSmi()) return SerializeSmi(Smi::cast(*value)); SerializeHeapNumber(Handle::cast(value)); } else if (class_name == isolate_->heap()->Boolean_string()) { diff --git a/src/macros.py b/src/macros.py index b0228da9a..03c201259 100644 --- a/src/macros.py +++ b/src/macros.py @@ -153,6 +153,9 @@ macro TO_UINT32(arg) = (arg >>> 0); macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : $nonStringToString(arg)); macro TO_NUMBER_INLINE(arg) = (IS_NUMBER(%IS_VAR(arg)) ? arg : $nonNumberToNumber(arg)); macro TO_OBJECT(arg) = (%_ToObject(arg)); +macro TO_PRIMITIVE(arg) = (%_ToPrimitive(arg)); +macro TO_PRIMITIVE_NUMBER(arg) = (%_ToPrimitive_Number(arg)); +macro TO_PRIMITIVE_STRING(arg) = (%_ToPrimitive_String(arg)); macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null"); macro HAS_OWN_PROPERTY(arg, index) = (%_CallFunction(arg, index, ObjectHasOwnProperty)); macro SHOULD_CREATE_WRAPPER(functionName, receiver) = (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(functionName)); diff --git a/src/messages.cc b/src/messages.cc index a8afa3f5a..4dcac50bf 100644 --- a/src/messages.cc +++ b/src/messages.cc @@ -471,7 +471,7 @@ MaybeHandle ErrorToStringHelper::GetStringifiedProperty( String); if (obj->IsUndefined()) return default_value; if (!obj->IsString()) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, Execution::ToString(isolate, obj), + ASSIGN_RETURN_ON_EXCEPTION(isolate, obj, Object::ToString(isolate, obj), String); } return Handle::cast(obj); diff --git a/src/messages.h b/src/messages.h index f08135bb1..532db9247 100644 --- a/src/messages.h +++ b/src/messages.h @@ -257,6 +257,7 @@ class CallSite { "Offset is outside the bounds of the DataView") \ T(InvalidDataViewLength, "Invalid data view length") \ T(InvalidDataViewOffset, "Start offset is outside the bounds of the buffer") \ + T(InvalidHint, "Invalid hint: %") \ T(InvalidLanguageTag, "Invalid language tag: %") \ T(InvalidWeakMapKey, "Invalid value used as weak map key") \ T(InvalidWeakSetValue, "Invalid value used in weak set") \ diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index b34cbf1d8..039268dcb 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -3413,7 +3413,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ bind(¬_oddball); __ push(a0); // Push argument. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/src/mips64/code-stubs-mips64.cc b/src/mips64/code-stubs-mips64.cc index 3c53d3651..bff8717d4 100644 --- a/src/mips64/code-stubs-mips64.cc +++ b/src/mips64/code-stubs-mips64.cc @@ -3445,7 +3445,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ bind(¬_oddball); __ push(a0); // Push argument. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 57750c345..a37fcd583 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -1132,6 +1132,7 @@ bool Object::FitsRepresentation(Representation representation) { } +// static MaybeHandle Object::ToObject(Isolate* isolate, Handle object) { return ToObject( @@ -1139,6 +1140,21 @@ MaybeHandle Object::ToObject(Isolate* isolate, } +// static +MaybeHandle Object::ToName(Isolate* isolate, Handle input) { + if (input->IsName()) return Handle::cast(input); + return ToString(isolate, input); +} + + +// static +MaybeHandle Object::ToPrimitive(Handle input, + ToPrimitiveHint hint) { + if (input->IsPrimitive()) return input; + return JSReceiver::ToPrimitive(Handle::cast(input), hint); +} + + bool Object::HasSpecificClassOf(String* name) { return this->IsJSObject() && (JSObject::cast(this)->class_name() == name); } diff --git a/src/objects.cc b/src/objects.cc index fc461887c..260a64b91 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -90,14 +90,57 @@ MaybeHandle Object::ToObject(Isolate* isolate, } -MaybeHandle Object::ToName(Isolate* isolate, Handle object) { - if (object->IsName()) { - return Handle::cast(object); - } else { - Handle converted; - ASSIGN_RETURN_ON_EXCEPTION(isolate, converted, - Execution::ToString(isolate, object), Name); - return Handle::cast(converted); +// static +MaybeHandle Object::ToNumber(Isolate* isolate, Handle input) { + while (true) { + if (input->IsNumber()) { + return input; + } + if (input->IsOddball()) { + return handle(Handle::cast(input)->to_number(), isolate); + } + if (input->IsString()) { + return String::ToNumber(Handle::cast(input)); + } + if (input->IsSymbol()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber), + Object); + } + if (input->IsSimd128Value()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSimdToNumber), + Object); + } + ASSIGN_RETURN_ON_EXCEPTION( + isolate, input, JSReceiver::ToPrimitive(Handle::cast(input), + ToPrimitiveHint::kNumber), + Object); + } +} + + +// static +MaybeHandle Object::ToString(Isolate* isolate, Handle input) { + while (true) { + if (input->IsString()) { + return Handle::cast(input); + } + if (input->IsOddball()) { + return handle(Handle::cast(input)->to_string(), isolate); + } + if (input->IsNumber()) { + return isolate->factory()->NumberToString(input); + } + if (input->IsSymbol()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString), + String); + } + if (input->IsSimd128Value()) { + return Simd128Value::ToString(Handle::cast(input)); + } + ASSIGN_RETURN_ON_EXCEPTION( + isolate, input, JSReceiver::ToPrimitive(Handle::cast(input), + ToPrimitiveHint::kString), + String); } } @@ -109,7 +152,6 @@ bool Object::BooleanValue() { if (IsUndetectableObject()) return false; // Undetectable object is false. if (IsString()) return String::cast(this)->length() != 0; if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); - if (IsSimd128Value()) return true; // Simd value types evaluate to true. return true; } @@ -157,6 +199,26 @@ bool Object::IsPromise(Handle object) { } +// static +MaybeHandle Object::GetMethod(Handle receiver, + Handle name) { + Handle func; + Isolate* isolate = receiver->GetIsolate(); + ASSIGN_RETURN_ON_EXCEPTION(isolate, func, + JSReceiver::GetProperty(receiver, name), Object); + if (func->IsNull() || func->IsUndefined()) { + return isolate->factory()->undefined_value(); + } + if (!func->IsCallable()) { + // TODO(bmeurer): Better error message here? + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCalledNonCallable, func), + Object); + } + return func; +} + + MaybeHandle Object::GetProperty(LookupIterator* it, LanguageMode language_mode) { for (; it->IsFound(); it->Next()) { @@ -1485,6 +1547,73 @@ void HeapNumber::HeapNumberPrint(std::ostream& os) { // NOLINT (*reinterpret_cast(FIELD_ADDR_CONST(p, offset))) +// static +Handle Simd128Value::ToString(Handle input) { +#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ + if (input->Is##Type()) return Type::ToString(Handle::cast(input)); + SIMD128_TYPES(SIMD128_TYPE) +#undef SIMD128_TYPE + UNREACHABLE(); + return Handle::null(); +} + + +// static +Handle Float32x4::ToString(Handle input) { + Isolate* const isolate = input->GetIsolate(); + char arr[100]; + Vector buffer(arr, arraysize(arr)); + std::ostringstream os; + os << "SIMD.Float32x4(" + << std::string(DoubleToCString(input->get_lane(0), buffer)) << ", " + << std::string(DoubleToCString(input->get_lane(1), buffer)) << ", " + << std::string(DoubleToCString(input->get_lane(2), buffer)) << ", " + << std::string(DoubleToCString(input->get_lane(3), buffer)) << ")"; + return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); +} + + +#define SIMD128_BOOL_TO_STRING(Type, lane_count) \ + Handle Type::ToString(Handle input) { \ + Isolate* const isolate = input->GetIsolate(); \ + std::ostringstream os; \ + os << "SIMD." #Type "("; \ + os << (input->get_lane(0) ? "true" : "false"); \ + for (int i = 1; i < lane_count; i++) { \ + os << ", " << (input->get_lane(i) ? "true" : "false"); \ + } \ + os << ")"; \ + return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); \ + } +SIMD128_BOOL_TO_STRING(Bool32x4, 4) +SIMD128_BOOL_TO_STRING(Bool16x8, 8) +SIMD128_BOOL_TO_STRING(Bool8x16, 16) +#undef SIMD128_BOOL_TO_STRING + + +#define SIMD128_INT_TO_STRING(Type, lane_count) \ + Handle Type::ToString(Handle input) { \ + Isolate* const isolate = input->GetIsolate(); \ + char arr[100]; \ + Vector buffer(arr, arraysize(arr)); \ + std::ostringstream os; \ + os << "SIMD." #Type "("; \ + os << IntToCString(input->get_lane(0), buffer); \ + for (int i = 1; i < lane_count; i++) { \ + os << ", " << IntToCString(input->get_lane(i), buffer); \ + } \ + os << ")"; \ + return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); \ + } +SIMD128_INT_TO_STRING(Int32x4, 4) +SIMD128_INT_TO_STRING(Uint32x4, 4) +SIMD128_INT_TO_STRING(Int16x8, 8) +SIMD128_INT_TO_STRING(Uint16x8, 8) +SIMD128_INT_TO_STRING(Int8x16, 16) +SIMD128_INT_TO_STRING(Uint8x16, 16) +#undef SIMD128_INT_TO_STRING + + bool Simd128Value::BitwiseEquals(const Simd128Value* other) const { return READ_INT64_FIELD(this, kValueOffset) == READ_INT64_FIELD(other, kValueOffset) && @@ -3279,7 +3408,7 @@ MaybeHandle Object::SetDataProperty(LookupIterator* it, if (it->IsElement() && receiver->HasFixedTypedArrayElements()) { if (!value->IsNumber() && !value->IsUndefined()) { ASSIGN_RETURN_ON_EXCEPTION(it->isolate(), to_assign, - Execution::ToNumber(it->isolate(), value), + Object::ToNumber(it->isolate(), value), Object); // ToNumber above might modify the receiver, causing the cached // holder_map to mismatch the actual holder->map() after this point. @@ -5881,6 +6010,75 @@ MaybeHandle JSObject::DeepCopy( } +// static +MaybeHandle JSReceiver::ToPrimitive(Handle receiver, + ToPrimitiveHint hint) { + Isolate* const isolate = receiver->GetIsolate(); + Handle exotic_to_prim; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, exotic_to_prim, + GetMethod(receiver, isolate->factory()->to_primitive_symbol()), Object); + if (!exotic_to_prim->IsUndefined()) { + Handle hint_string; + switch (hint) { + case ToPrimitiveHint::kDefault: + hint_string = isolate->factory()->default_string(); + break; + case ToPrimitiveHint::kNumber: + hint_string = isolate->factory()->number_string(); + break; + case ToPrimitiveHint::kString: + hint_string = isolate->factory()->string_string(); + break; + } + Handle result; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, result, + Execution::Call(isolate, exotic_to_prim, receiver, 1, &hint_string), + Object); + if (result->IsPrimitive()) return result; + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCannotConvertToPrimitive), + Object); + } + return OrdinaryToPrimitive(receiver, + (hint == ToPrimitiveHint::kString) + ? isolate->factory()->string_string() + : isolate->factory()->number_string()); +} + + +// static +MaybeHandle JSReceiver::OrdinaryToPrimitive(Handle receiver, + Handle hint) { + Isolate* const isolate = receiver->GetIsolate(); + Handle method_names[2]; + if (hint.is_identical_to(isolate->factory()->number_string())) { + method_names[0] = isolate->factory()->valueOf_string(); + method_names[1] = isolate->factory()->toString_string(); + } else { + DCHECK(hint.is_identical_to(isolate->factory()->string_string())); + method_names[0] = isolate->factory()->toString_string(); + method_names[1] = isolate->factory()->valueOf_string(); + } + for (Handle name : method_names) { + Handle method; + ASSIGN_RETURN_ON_EXCEPTION(isolate, method, + JSReceiver::GetProperty(receiver, name), Object); + if (method->IsCallable()) { + Handle result; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, result, Execution::Call(isolate, method, receiver, 0, NULL), + Object); + if (result->IsPrimitive()) return result; + } + } + THROW_NEW_ERROR(isolate, + NewTypeError(MessageTemplate::kCannotConvertToPrimitive), + Object); +} + + // Tests for the fast common case for property enumeration: // - This object and all prototypes has an enum cache (which means that // it is no proxy, has no interceptors and needs no access checks). @@ -8152,6 +8350,95 @@ bool String::LooksValid() { } +namespace { + +bool AreDigits(const uint8_t* s, int from, int to) { + for (int i = from; i < to; i++) { + if (s[i] < '0' || s[i] > '9') return false; + } + + return true; +} + + +int ParseDecimalInteger(const uint8_t* s, int from, int to) { + DCHECK(to - from < 10); // Overflow is not possible. + DCHECK(from < to); + int d = s[from] - '0'; + + for (int i = from + 1; i < to; i++) { + d = 10 * d + (s[i] - '0'); + } + + return d; +} + +} // namespace + + +// static +Handle String::ToNumber(Handle subject) { + Isolate* const isolate = subject->GetIsolate(); + + // Flatten {subject} string first. + subject = String::Flatten(subject); + + // Fast array index case. + uint32_t index; + if (subject->AsArrayIndex(&index)) { + return isolate->factory()->NewNumberFromUint(index); + } + + // Fast case: short integer or some sorts of junk values. + if (subject->IsSeqOneByteString()) { + int len = subject->length(); + if (len == 0) return handle(Smi::FromInt(0), isolate); + + DisallowHeapAllocation no_gc; + uint8_t const* data = Handle::cast(subject)->GetChars(); + bool minus = (data[0] == '-'); + int start_pos = (minus ? 1 : 0); + + if (start_pos == len) { + return isolate->factory()->nan_value(); + } else if (data[start_pos] > '9') { + // Fast check for a junk value. A valid string may start from a + // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit + // or the 'I' character ('Infinity'). All of that have codes not greater + // than '9' except 'I' and  . + if (data[start_pos] != 'I' && data[start_pos] != 0xa0) { + return isolate->factory()->nan_value(); + } + } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) { + // The maximal/minimal smi has 10 digits. If the string has less digits + // we know it will fit into the smi-data type. + int d = ParseDecimalInteger(data, start_pos, len); + if (minus) { + if (d == 0) return isolate->factory()->minus_zero_value(); + d = -d; + } else if (!subject->HasHashCode() && len <= String::kMaxArrayIndexSize && + (len == 1 || data[0] != '0')) { + // String hash is not calculated yet but all the data are present. + // Update the hash field to speed up sequential convertions. + uint32_t hash = StringHasher::MakeArrayIndexHash(d, len); +#ifdef DEBUG + subject->Hash(); // Force hash calculation. + DCHECK_EQ(static_cast(subject->hash_field()), + static_cast(hash)); +#endif + subject->set_hash_field(hash); + } + return handle(Smi::FromInt(d), isolate); + } + } + + // Slower case. + int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY; + return isolate->factory()->NewNumber( + StringToDouble(isolate->unicode_cache(), subject, flags)); +} + + String::FlatContent String::GetFlatContent() { DCHECK(!AllowHeapAllocation::IsAllowed()); int length = this->length(); diff --git a/src/objects.h b/src/objects.h index f7492ebb0..f7aea06f9 100644 --- a/src/objects.h +++ b/src/objects.h @@ -168,6 +168,11 @@ enum KeyedAccessStoreMode { }; +// Valid hints for the abstract operation ToPrimitive, +// implemented according to ES6, section 7.1.1. +enum class ToPrimitiveHint { kDefault, kNumber, kString }; + + enum TypeofMode { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; @@ -1077,9 +1082,25 @@ class Object { MUST_USE_RESULT static MaybeHandle ToObject( Isolate* isolate, Handle object, Handle context); - // Convert to a Name if needed. - MUST_USE_RESULT static MaybeHandle ToName(Isolate* isolate, - Handle object); + // ES6 section 7.1.14 ToPropertyKey + MUST_USE_RESULT static inline MaybeHandle ToName(Isolate* isolate, + Handle input); + + // ES6 section 7.1.1 ToPrimitive + MUST_USE_RESULT static inline MaybeHandle ToPrimitive( + Handle input, ToPrimitiveHint hint = ToPrimitiveHint::kDefault); + + // ES6 section 7.1.3 ToNumber + MUST_USE_RESULT static MaybeHandle ToNumber(Isolate* isolate, + Handle input); + + // ES6 section 7.1.12 ToString + MUST_USE_RESULT static MaybeHandle ToString(Isolate* isolate, + Handle input); + + // ES6 section 7.3.9 GetMethod + MUST_USE_RESULT static MaybeHandle GetMethod( + Handle receiver, Handle name); MUST_USE_RESULT static MaybeHandle GetProperty( LookupIterator* it, LanguageMode language_mode = SLOPPY); @@ -1578,6 +1599,8 @@ class Simd128Value : public HeapObject { DECLARE_PRINTER(Simd128Value) DECLARE_VERIFIER(Simd128Value) + static Handle ToString(Handle input); + // Equality operations. inline bool Equals(Simd128Value* that); @@ -1620,6 +1643,8 @@ class Simd128Value : public HeapObject { \ DECLARE_PRINTER(Type) \ \ + static Handle ToString(Handle input); \ + \ inline bool Equals(Type* that); \ \ private: \ @@ -1649,6 +1674,13 @@ class JSReceiver: public HeapObject { public: DECLARE_CAST(JSReceiver) + // ES6 section 7.1.1 ToPrimitive + MUST_USE_RESULT static MaybeHandle ToPrimitive( + Handle receiver, + ToPrimitiveHint hint = ToPrimitiveHint::kDefault); + MUST_USE_RESULT static MaybeHandle OrdinaryToPrimitive( + Handle receiver, Handle hint); + // Implementation of [[HasProperty]], ECMA-262 5th edition, section 8.12.6. MUST_USE_RESULT static inline Maybe HasProperty( Handle object, Handle name); @@ -8346,6 +8378,9 @@ class String: public Name { // to this method are not efficient unless the string is flat. INLINE(uint16_t Get(int index)); + // ES6 section 7.1.3.1 ToNumber Applied to the String Type + static Handle ToNumber(Handle subject); + // Flattens the string. Checks first inline to see if it is // necessary. Does nothing if the string is not a cons string. // Flattening allocates a sequential string with the same data as diff --git a/src/ppc/code-stubs-ppc.cc b/src/ppc/code-stubs-ppc.cc index 896dd6c1d..50e8b7f6f 100644 --- a/src/ppc/code-stubs-ppc.cc +++ b/src/ppc/code-stubs-ppc.cc @@ -3433,7 +3433,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ bind(¬_oddball); __ push(r3); // Push argument. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/src/runtime.js b/src/runtime.js index 1503373d4..1ef4e973f 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -11,7 +11,6 @@ // The following declarations are shared with other native JS files. // They are all declared at this one spot to avoid redeclaration errors. -var $defaultNumber; var $defaultString; var $NaN; var $nonNumberToNumber; @@ -826,7 +825,6 @@ function ToPositiveInteger(x, rangeErrorIndex) { // ---------------------------------------------------------------------------- // Exports -$defaultNumber = DefaultNumber; $defaultString = DefaultString; $NaN = %GetRootNaN(); $nonNumberToNumber = NonNumberToNumber; @@ -880,7 +878,6 @@ $toString = ToString; "sub_builtin", SUB, "sub_strong_builtin", SUB_STRONG, "to_name_builtin", TO_NAME, - "to_number_builtin", TO_NUMBER, "to_string_builtin", TO_STRING, ]); diff --git a/src/runtime/runtime-i18n.cc b/src/runtime/runtime-i18n.cc index 73d511074..290c228c2 100644 --- a/src/runtime/runtime-i18n.cc +++ b/src/runtime/runtime-i18n.cc @@ -352,7 +352,7 @@ RUNTIME_FUNCTION(Runtime_InternalDateFormat) { Handle value; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, - Execution::ToNumber(isolate, date)); + Object::ToNumber(isolate, date)); icu::SimpleDateFormat* date_format = DateFormat::UnpackDateFormat(isolate, date_format_holder); @@ -446,7 +446,7 @@ RUNTIME_FUNCTION(Runtime_InternalNumberFormat) { Handle value; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, - Execution::ToNumber(isolate, number)); + Object::ToNumber(isolate, number)); icu::DecimalFormat* number_format = NumberFormat::UnpackNumberFormat(isolate, number_format_holder); diff --git a/src/runtime/runtime-numbers.cc b/src/runtime/runtime-numbers.cc index 49734ba8d..d99d5fc54 100644 --- a/src/runtime/runtime-numbers.cc +++ b/src/runtime/runtime-numbers.cc @@ -112,81 +112,11 @@ RUNTIME_FUNCTION(Runtime_IsValidSmi) { } -static bool AreDigits(const uint8_t* s, int from, int to) { - for (int i = from; i < to; i++) { - if (s[i] < '0' || s[i] > '9') return false; - } - - return true; -} - - -static int ParseDecimalInteger(const uint8_t* s, int from, int to) { - DCHECK(to - from < 10); // Overflow is not possible. - DCHECK(from < to); - int d = s[from] - '0'; - - for (int i = from + 1; i < to; i++) { - d = 10 * d + (s[i] - '0'); - } - - return d; -} - - RUNTIME_FUNCTION(Runtime_StringToNumber) { HandleScope handle_scope(isolate); - DCHECK(args.length() == 1); + DCHECK_EQ(1, args.length()); CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); - subject = String::Flatten(subject); - - // Fast case: short integer or some sorts of junk values. - if (subject->IsSeqOneByteString()) { - int len = subject->length(); - if (len == 0) return Smi::FromInt(0); - - DisallowHeapAllocation no_gc; - uint8_t const* data = Handle::cast(subject)->GetChars(); - bool minus = (data[0] == '-'); - int start_pos = (minus ? 1 : 0); - - if (start_pos == len) { - return isolate->heap()->nan_value(); - } else if (data[start_pos] > '9') { - // Fast check for a junk value. A valid string may start from a - // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit - // or the 'I' character ('Infinity'). All of that have codes not greater - // than '9' except 'I' and  . - if (data[start_pos] != 'I' && data[start_pos] != 0xa0) { - return isolate->heap()->nan_value(); - } - } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) { - // The maximal/minimal smi has 10 digits. If the string has less digits - // we know it will fit into the smi-data type. - int d = ParseDecimalInteger(data, start_pos, len); - if (minus) { - if (d == 0) return isolate->heap()->minus_zero_value(); - d = -d; - } else if (!subject->HasHashCode() && len <= String::kMaxArrayIndexSize && - (len == 1 || data[0] != '0')) { - // String hash is not calculated yet but all the data are present. - // Update the hash field to speed up sequential convertions. - uint32_t hash = StringHasher::MakeArrayIndexHash(d, len); -#ifdef DEBUG - subject->Hash(); // Force hash calculation. - DCHECK_EQ(static_cast(subject->hash_field()), - static_cast(hash)); -#endif - subject->set_hash_field(hash); - } - return Smi::FromInt(d); - } - } - - // Slower case. - int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY; - return *isolate->factory()->NewNumber( - StringToDouble(isolate->unicode_cache(), subject, flags)); + return *String::ToNumber(subject); } diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index 2dd5db86e..e493118ae 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc @@ -1425,6 +1425,62 @@ RUNTIME_FUNCTION(Runtime_ToObject) { } +RUNTIME_FUNCTION(Runtime_ToPrimitive) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(Object, input, 0); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, + Object::ToPrimitive(input)); + return *result; +} + + +RUNTIME_FUNCTION(Runtime_ToPrimitive_Number) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(Object, input, 0); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, Object::ToPrimitive(input, ToPrimitiveHint::kNumber)); + return *result; +} + + +RUNTIME_FUNCTION(Runtime_ToPrimitive_String) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(Object, input, 0); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, Object::ToPrimitive(input, ToPrimitiveHint::kString)); + return *result; +} + + +RUNTIME_FUNCTION(Runtime_OrdinaryToPrimitive) { + HandleScope scope(isolate); + DCHECK_EQ(2, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0); + CONVERT_ARG_HANDLE_CHECKED(String, hint, 1); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, JSReceiver::OrdinaryToPrimitive(receiver, hint)); + return *result; +} + + +RUNTIME_FUNCTION(Runtime_ToNumber) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(Object, input, 0); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, + Object::ToNumber(isolate, input)); + return *result; +} + + RUNTIME_FUNCTION(Runtime_StrictEquals) { SealHandleScope scope(isolate); DCHECK_EQ(2, args.length()); diff --git a/src/runtime/runtime-scopes.cc b/src/runtime/runtime-scopes.cc index fa8f7a811..3839a2724 100644 --- a/src/runtime/runtime-scopes.cc +++ b/src/runtime/runtime-scopes.cc @@ -1139,7 +1139,7 @@ RUNTIME_FUNCTION(Runtime_Arguments) { // Convert the key to a string. Handle converted; ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, converted, - Execution::ToString(isolate, raw_key)); + Object::ToString(isolate, raw_key)); Handle key = Handle::cast(converted); // Try to convert the string key into an array index. diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 18faeb874..f04325442 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -491,6 +491,11 @@ namespace internal { F(DefineGetterPropertyUnchecked, 4, 1) \ F(DefineSetterPropertyUnchecked, 4, 1) \ F(ToObject, 1, 1) \ + F(ToPrimitive, 1, 1) \ + F(ToPrimitive_Number, 1, 1) \ + F(ToPrimitive_String, 1, 1) \ + F(OrdinaryToPrimitive, 2, 1) \ + F(ToNumber, 1, 1) \ F(StrictEquals, 2, 1) \ F(InstanceOf, 2, 1) \ F(HasInPrototypeChain, 2, 1) diff --git a/src/symbol.js b/src/symbol.js index 8cac2c56a..3f6e28177 100644 --- a/src/symbol.js +++ b/src/symbol.js @@ -7,6 +7,7 @@ // - symbolIsConcatSpreadable // - symbolIsRegExp // - symbolIterator +// - symbolToPrimitive // - symbolToStringTag // - symbolUnscopables @@ -40,6 +41,16 @@ function SymbolConstructor(x) { } +// 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint ) +function SymbolToPrimitive(hint) { + if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) { + throw MakeTypeError(kIncompatibleMethodReceiver, + "Symbol.prototype [ @@toPrimitive ]", this); + } + return %_ValueOf(this); +} + + function SymbolToString() { if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) { throw MakeTypeError(kIncompatibleMethodReceiver, @@ -97,6 +108,7 @@ utils.InstallConstants(GlobalSymbol, [ // "isConcatSpreadable", symbolIsConcatSpreadable, // "isRegExp", symbolIsRegExp, "iterator", symbolIterator, + "toPrimitive", symbolToPrimitive, // TODO(dslomov, caitp): Currently defined in harmony-tostring.js --- // Move here when shipping // "toStringTag", symbolToStringTag, @@ -110,6 +122,10 @@ utils.InstallFunctions(GlobalSymbol, DONT_ENUM, [ %AddNamedProperty( GlobalSymbol.prototype, "constructor", GlobalSymbol, DONT_ENUM); +utils.SetFunctionName(SymbolToPrimitive, symbolToPrimitive); +%AddNamedProperty( + GlobalSymbol.prototype, symbolToPrimitive, SymbolToPrimitive, + DONT_ENUM | READ_ONLY); %AddNamedProperty( GlobalSymbol.prototype, symbolToStringTag, "Symbol", DONT_ENUM | READ_ONLY); diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index a3db77740..aca21a127 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -3224,7 +3224,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ PopReturnAddressTo(rcx); // Pop return address. __ Push(rax); // Push argument. __ PushReturnAddressFrom(rcx); // Push return address. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/src/x87/code-stubs-x87.cc b/src/x87/code-stubs-x87.cc index aa5753484..ff05f4d76 100644 --- a/src/x87/code-stubs-x87.cc +++ b/src/x87/code-stubs-x87.cc @@ -2981,7 +2981,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ pop(ecx); // Pop return address. __ push(eax); // Push argument. __ push(ecx); // Push return address. - __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION); + __ TailCallRuntime(Runtime::kToNumber, 1, 1); } diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 3dff7efaf..2c8559609 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -74,16 +74,14 @@ TEST(HeapMaps) { static void CheckOddball(Isolate* isolate, Object* obj, const char* string) { CHECK(obj->IsOddball()); Handle handle(obj, isolate); - Object* print_string = - *Execution::ToString(isolate, handle).ToHandleChecked(); + Object* print_string = *Object::ToString(isolate, handle).ToHandleChecked(); CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string))); } static void CheckSmi(Isolate* isolate, int value, const char* string) { Handle handle(Smi::FromInt(value), isolate); - Object* print_string = - *Execution::ToString(isolate, handle).ToHandleChecked(); + Object* print_string = *Object::ToString(isolate, handle).ToHandleChecked(); CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string))); } @@ -92,7 +90,7 @@ static void CheckNumber(Isolate* isolate, double value, const char* string) { Handle number = isolate->factory()->NewNumber(value); CHECK(number->IsNumber()); Handle print_string = - Execution::ToString(isolate, number).ToHandleChecked(); + Object::ToString(isolate, number).ToHandleChecked(); CHECK(String::cast(*print_string)->IsUtf8EqualTo(CStrVector(string))); } diff --git a/test/mjsunit/harmony/to-number.js b/test/mjsunit/harmony/to-number.js new file mode 100644 index 000000000..712576684 --- /dev/null +++ b/test/mjsunit/harmony/to-number.js @@ -0,0 +1,57 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +assertEquals(1, %ToNumber(1)); +assertEquals(1, %_ToNumber(1)); + +assertEquals(.5, %ToNumber(.5)); +assertEquals(.5, %_ToNumber(.5)); + +assertEquals(0, %ToNumber(null)); +assertEquals(0, %_ToNumber(null)); + +assertEquals(1, %ToNumber(true)); +assertEquals(1, %_ToNumber(true)); + +assertEquals(0, %ToNumber(false)); +assertEquals(0, %_ToNumber(false)); + +assertEquals(NaN, %ToNumber(undefined)); +assertEquals(NaN, %_ToNumber(undefined)); + +assertEquals(-1, %ToNumber("-1")); +assertEquals(-1, %_ToNumber("-1")); +assertEquals(123, %ToNumber("123")); +assertEquals(123, %_ToNumber("123")); +assertEquals(NaN, %ToNumber("random text")); +assertEquals(NaN, %_ToNumber("random text")); + +assertThrows(function() { %ToNumber(Symbol.toPrimitive) }, TypeError); +assertThrows(function() { %_ToNumber(Symbol.toPrimitive) }, TypeError); + +var a = { toString: function() { return 54321 }}; +assertEquals(54321, %ToNumber(a)); +assertEquals(54321, %_ToNumber(a)); + +var b = { valueOf: function() { return 42 }}; +assertEquals(42, %ToNumber(b)); +assertEquals(42, %_ToNumber(b)); + +var c = { + toString: function() { return "x"}, + valueOf: function() { return 123 } +}; +assertEquals(123, %ToNumber(c)); +assertEquals(123, %_ToNumber(c)); + +var d = { + [Symbol.toPrimitive]: function(hint) { + assertEquals("number", hint); + return 987654321; + } +}; +assertEquals(987654321, %ToNumber(d)); +assertEquals(987654321, %_ToNumber(d)); diff --git a/test/mjsunit/harmony/to-primitive.js b/test/mjsunit/harmony/to-primitive.js new file mode 100644 index 000000000..755742519 --- /dev/null +++ b/test/mjsunit/harmony/to-primitive.js @@ -0,0 +1,110 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +assertEquals(1, %ToPrimitive(1)); +assertEquals(1, %ToPrimitive_Number(1)); +assertEquals(1, %ToPrimitive_String(1)); +assertEquals(1, %_ToPrimitive(1)); +assertEquals(1, %_ToPrimitive_Number(1)); +assertEquals(1, %_ToPrimitive_String(1)); + +assertEquals(.5, %ToPrimitive(.5)); +assertEquals(.5, %ToPrimitive_Number(.5)); +assertEquals(.5, %ToPrimitive_String(.5)); +assertEquals(.5, %_ToPrimitive(.5)); +assertEquals(.5, %_ToPrimitive_Number(.5)); +assertEquals(.5, %_ToPrimitive_String(.5)); + +assertEquals(null, %ToPrimitive(null)); +assertEquals(null, %ToPrimitive_Number(null)); +assertEquals(null, %ToPrimitive_String(null)); +assertEquals(null, %_ToPrimitive(null)); +assertEquals(null, %_ToPrimitive_Number(null)); +assertEquals(null, %_ToPrimitive_String(null)); + +assertEquals(true, %ToPrimitive(true)); +assertEquals(true, %ToPrimitive_Number(true)); +assertEquals(true, %ToPrimitive_String(true)); +assertEquals(true, %_ToPrimitive(true)); +assertEquals(true, %_ToPrimitive_Number(true)); +assertEquals(true, %_ToPrimitive_String(true)); + +assertEquals(false, %ToPrimitive(false)); +assertEquals(false, %ToPrimitive_Number(false)); +assertEquals(false, %ToPrimitive_String(false)); +assertEquals(false, %_ToPrimitive(false)); +assertEquals(false, %_ToPrimitive_Number(false)); +assertEquals(false, %_ToPrimitive_String(false)); + +assertEquals(undefined, %ToPrimitive(undefined)); +assertEquals(undefined, %ToPrimitive_Number(undefined)); +assertEquals(undefined, %ToPrimitive_String(undefined)); +assertEquals(undefined, %_ToPrimitive(undefined)); +assertEquals(undefined, %_ToPrimitive_Number(undefined)); +assertEquals(undefined, %_ToPrimitive_String(undefined)); + +assertEquals("random text", %ToPrimitive("random text")); +assertEquals("random text", %ToPrimitive_Number("random text")); +assertEquals("random text", %ToPrimitive_String("random text")); +assertEquals("random text", %_ToPrimitive("random text")); +assertEquals("random text", %_ToPrimitive_Number("random text")); +assertEquals("random text", %_ToPrimitive_String("random text")); + +assertEquals(Symbol.toPrimitive, %ToPrimitive(Symbol.toPrimitive)); +assertEquals(Symbol.toPrimitive, %ToPrimitive_Number(Symbol.toPrimitive)); +assertEquals(Symbol.toPrimitive, %ToPrimitive_String(Symbol.toPrimitive)); +assertEquals(Symbol.toPrimitive, %_ToPrimitive(Symbol.toPrimitive)); +assertEquals(Symbol.toPrimitive, %_ToPrimitive_Number(Symbol.toPrimitive)); +assertEquals(Symbol.toPrimitive, %_ToPrimitive_String(Symbol.toPrimitive)); + +var a = { toString: function() { return "xyz" }}; +assertEquals("xyz", %ToPrimitive(a)); +assertEquals("xyz", %ToPrimitive_Number(a)); +assertEquals("xyz", %ToPrimitive_String(a)); +assertEquals("xyz", %_ToPrimitive(a)); +assertEquals("xyz", %_ToPrimitive_Number(a)); +assertEquals("xyz", %_ToPrimitive_String(a)); +assertEquals("xyz", %OrdinaryToPrimitive(a, "number")); +assertEquals("xyz", %OrdinaryToPrimitive(a, "string")); +assertEquals("xyz", %_OrdinaryToPrimitive(a, "number")); +assertEquals("xyz", %_OrdinaryToPrimitive(a, "string")); + +var b = { valueOf: function() { return 42 }}; +assertEquals(42, %ToPrimitive(b)); +assertEquals(42, %ToPrimitive_Number(b)); +assertEquals("[object Object]", %ToPrimitive_String(b)); +assertEquals(42, %_ToPrimitive(b)); +assertEquals(42, %_ToPrimitive_Number(b)); +assertEquals("[object Object]", %_ToPrimitive_String(b)); +assertEquals(42, %OrdinaryToPrimitive(b, "number")); +assertEquals("[object Object]", %OrdinaryToPrimitive(b, "string")); +assertEquals(42, %_OrdinaryToPrimitive(b, "number")); +assertEquals("[object Object]", %_OrdinaryToPrimitive(b, "string")); + +var c = { + toString: function() { return "x"}, + valueOf: function() { return 123 } +}; +assertEquals(123, %ToPrimitive(c)); +assertEquals(123, %ToPrimitive_Number(c)); +assertEquals("x", %ToPrimitive_String(c)); +assertEquals(123, %_ToPrimitive(c)); +assertEquals(123, %_ToPrimitive_Number(c)); +assertEquals("x", %_ToPrimitive_String(c)); +assertEquals(123, %OrdinaryToPrimitive(c, "number")); +assertEquals("x", %OrdinaryToPrimitive(c, "string")); +assertEquals(123, %_OrdinaryToPrimitive(c, "number")); +assertEquals("x", %_OrdinaryToPrimitive(c, "string")); + +var d = { + [Symbol.toPrimitive]: function(hint) { return hint } +}; +assertEquals("default", %ToPrimitive(d)); +assertEquals("number", %ToPrimitive_Number(d)); +assertEquals("string", %ToPrimitive_String(d)); +assertEquals("default", %_ToPrimitive(d)); +assertEquals("number", %_ToPrimitive_Number(d)); +assertEquals("string", %_ToPrimitive_String(d));