[es6] Implement spec compliant ToPrimitive in the runtime.
authorbmeurer <bmeurer@chromium.org>
Fri, 28 Aug 2015 09:21:23 +0000 (02:21 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 28 Aug 2015 09:21:43 +0000 (09:21 +0000)
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}

42 files changed:
src/accessors.cc
src/api.cc
src/arm/code-stubs-arm.cc
src/arm64/code-stubs-arm64.cc
src/compiler/linkage.cc
src/contexts.h
src/date.js
src/debug/debug.cc
src/execution.cc
src/execution.h
src/full-codegen/arm/full-codegen-arm.cc
src/full-codegen/arm64/full-codegen-arm64.cc
src/full-codegen/ia32/full-codegen-ia32.cc
src/full-codegen/mips/full-codegen-mips.cc
src/full-codegen/mips64/full-codegen-mips64.cc
src/full-codegen/ppc/full-codegen-ppc.cc
src/full-codegen/x64/full-codegen-x64.cc
src/full-codegen/x87/full-codegen-x87.cc
src/heap/heap.h
src/ia32/code-stubs-ia32.cc
src/json-stringifier.h
src/macros.py
src/messages.cc
src/messages.h
src/mips/code-stubs-mips.cc
src/mips64/code-stubs-mips64.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/ppc/code-stubs-ppc.cc
src/runtime.js
src/runtime/runtime-i18n.cc
src/runtime/runtime-numbers.cc
src/runtime/runtime-object.cc
src/runtime/runtime-scopes.cc
src/runtime/runtime.h
src/symbol.js
src/x64/code-stubs-x64.cc
src/x87/code-stubs-x87.cc
test/cctest/test-heap.cc
test/mjsunit/harmony/to-number.js [new file with mode: 0644]
test/mjsunit/harmony/to-primitive.js [new file with mode: 0644]

index b18cd99..c1951b9 100644 (file)
@@ -216,7 +216,7 @@ void Accessors::ArrayLengthSetter(
     }
 
     Handle<Object> number_v;
-    if (!Execution::ToNumber(isolate, length_obj).ToHandle(&number_v)) {
+    if (!Object::ToNumber(isolate, length_obj).ToHandle(&number_v)) {
       isolate->OptionalRescheduleException(false);
       return;
     }
index 0fb4154..42a8875 100644 (file)
@@ -2854,7 +2854,7 @@ MaybeLocal<String> Value::ToString(Local<Context> context) const {
   PREPARE_FOR_EXECUTION(context, "ToString", String);
   Local<String> result;
   has_pending_exception =
-      !ToLocal<String>(i::Execution::ToString(isolate, obj), &result);
+      !ToLocal<String>(i::Object::ToString(isolate, obj), &result);
   RETURN_ON_FAILED_EXECUTION(String);
   RETURN_ESCAPED(result);
 }
@@ -2920,7 +2920,7 @@ MaybeLocal<Number> Value::ToNumber(Local<Context> context) const {
   PREPARE_FOR_EXECUTION(context, "ToNumber", Number);
   Local<Number> result;
   has_pending_exception =
-      !ToLocal<Number>(i::Execution::ToNumber(isolate, obj), &result);
+      !ToLocal<Number>(i::Object::ToNumber(isolate, obj), &result);
   RETURN_ON_FAILED_EXECUTION(Number);
   RETURN_ESCAPED(result);
 }
@@ -3244,7 +3244,7 @@ Maybe<double> Value::NumberValue(Local<Context> context) const {
   if (obj->IsNumber()) return Just(obj->Number());
   PREPARE_FOR_EXECUTION_PRIMITIVE(context, "NumberValue", double);
   i::Handle<i::Object> 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<Uint32> Value::ToArrayIndex(Local<Context> context) const {
   PREPARE_FOR_EXECUTION(context, "ToArrayIndex", Uint32);
   i::Handle<i::Object> 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<i::String> str = i::Handle<i::String>::cast(string_obj);
   uint32_t index;
@@ -3614,8 +3614,8 @@ Maybe<PropertyAttribute> 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<i::Name>::cast(key_obj);
index 5bd4008..695bf33 100644 (file)
@@ -3264,7 +3264,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
   __ bind(&not_oddball);
 
   __ push(r0);  // Push argument.
-  __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION);
+  __ TailCallRuntime(Runtime::kToNumber, 1, 1);
 }
 
 
index 4bd582f..4555a81 100644 (file)
@@ -3995,7 +3995,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
   __ Bind(&not_oddball);
 
   __ Push(x0);  // Push argument.
-  __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION);
+  __ TailCallRuntime(Runtime::kToNumber, 1, 1);
 }
 
 
index 93112be..078e5ad 100644 (file)
@@ -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:
index 7c4ebc0..1abd6f2 100644 (file)
@@ -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)
 
 
index 2be8bcb..44d2f4d 100644 (file)
@@ -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.
index 99036d7..de7cc23 100644 (file)
@@ -2148,7 +2148,7 @@ void Debug::NotifyMessageHandler(v8::DebugEvent event,
       Handle<Object> exception;
       if (!maybe_exception.ToHandle(&exception)) break;
       Handle<Object> result;
-      if (!Execution::ToString(isolate_, exception).ToHandle(&result)) break;
+      if (!Object::ToString(isolate_, exception).ToHandle(&result)) break;
       answer = Handle<String>::cast(result);
     }
 
index 4ab2403..64e5e39 100644 (file)
@@ -544,18 +544,6 @@ void StackGuard::InitThread(const ExecutionAccess& lock) {
   } while (false)
 
 
-MaybeHandle<Object> Execution::ToNumber(
-    Isolate* isolate, Handle<Object> obj) {
-  RETURN_NATIVE_CALL(to_number, { obj });
-}
-
-
-MaybeHandle<Object> Execution::ToString(
-    Isolate* isolate, Handle<Object> obj) {
-  RETURN_NATIVE_CALL(to_string, { obj });
-}
-
-
 MaybeHandle<Object> Execution::ToDetailString(
     Isolate* isolate, Handle<Object> obj) {
   RETURN_NATIVE_CALL(to_detail_string, { obj });
@@ -584,7 +572,7 @@ MaybeHandle<Object> Execution::NewDate(Isolate* isolate, double time) {
 
 
 MaybeHandle<Object> Execution::ToInt32(Isolate* isolate, Handle<Object> 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<Object> Execution::ToObject(Isolate* isolate, Handle<Object> obj) {
 
 
 MaybeHandle<Object> Execution::ToUint32(Isolate* isolate, Handle<Object> 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()));
 }
index 2c07a64..d7996d1 100644 (file)
@@ -59,10 +59,6 @@ class Execution final : public AllStatic {
                                      Handle<Object> argv[],
                                      MaybeHandle<Object>* exception_out = NULL);
 
-  // ECMA-262 9.3
-  MUST_USE_RESULT static MaybeHandle<Object> ToNumber(
-      Isolate* isolate, Handle<Object> obj);
-
   // ECMA-262 9.4
   MUST_USE_RESULT static MaybeHandle<Object> ToInteger(
       Isolate* isolate, Handle<Object> obj);
@@ -81,10 +77,6 @@ class Execution final : public AllStatic {
       Isolate* isolate, Handle<Object> obj);
 
   // ECMA-262 9.8
-  MUST_USE_RESULT static MaybeHandle<Object> ToString(
-      Isolate* isolate, Handle<Object> obj);
-
-  // ECMA-262 9.8
   MUST_USE_RESULT static MaybeHandle<Object> ToDetailString(
       Isolate* isolate, Handle<Object> obj);
 
index ef65b31..ca25d6e 100644 (file)
@@ -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));
index a9ec2aa..55ac3f5 100644 (file)
@@ -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);
index 8bdd8d4..1cf1416 100644 (file)
@@ -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);
index 7486896..9baeff0 100644 (file)
@@ -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));
index c4b924b..10e4580 100644 (file)
@@ -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));
index 634ccdb..c9b11ba 100644 (file)
@@ -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));
index 9ef1fae..d521fcd 100644 (file)
@@ -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);
index 9e2d95c..82e754e 100644 (file)
@@ -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);
index 97d32bb..826933d 100644 (file)
@@ -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)
 
index 0590ab8..bfabb1f 100644 (file)
@@ -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);
 }
 
 
index fb6b80d..3744ea7 100644 (file)
@@ -382,12 +382,12 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSValue(
   if (class_name == isolate_->heap()->String_string()) {
     Handle<Object> value;
     ASSIGN_RETURN_ON_EXCEPTION_VALUE(
-        isolate_, value, Execution::ToString(isolate_, object), EXCEPTION);
+        isolate_, value, Object::ToString(isolate_, object), EXCEPTION);
     SerializeString(Handle<String>::cast(value));
   } else if (class_name == isolate_->heap()->Number_string()) {
     Handle<Object> 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<HeapNumber>::cast(value));
   } else if (class_name == isolate_->heap()->Boolean_string()) {
index b0228da..03c2012 100644 (file)
@@ -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));
index a8afa3f..4dcac50 100644 (file)
@@ -471,7 +471,7 @@ MaybeHandle<String> 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<String>::cast(obj);
index f08135b..532db92 100644 (file)
@@ -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")                     \
index b34cbf1..039268d 100644 (file)
@@ -3413,7 +3413,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
   __ bind(&not_oddball);
 
   __ push(a0);  // Push argument.
-  __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION);
+  __ TailCallRuntime(Runtime::kToNumber, 1, 1);
 }
 
 
index 3c53d36..bff8717 100644 (file)
@@ -3445,7 +3445,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
   __ bind(&not_oddball);
 
   __ push(a0);  // Push argument.
-  __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION);
+  __ TailCallRuntime(Runtime::kToNumber, 1, 1);
 }
 
 
index 57750c3..a37fcd5 100644 (file)
@@ -1132,6 +1132,7 @@ bool Object::FitsRepresentation(Representation representation) {
 }
 
 
+// static
 MaybeHandle<JSReceiver> Object::ToObject(Isolate* isolate,
                                          Handle<Object> object) {
   return ToObject(
@@ -1139,6 +1140,21 @@ MaybeHandle<JSReceiver> Object::ToObject(Isolate* isolate,
 }
 
 
+// static
+MaybeHandle<Name> Object::ToName(Isolate* isolate, Handle<Object> input) {
+  if (input->IsName()) return Handle<Name>::cast(input);
+  return ToString(isolate, input);
+}
+
+
+// static
+MaybeHandle<Object> Object::ToPrimitive(Handle<Object> input,
+                                        ToPrimitiveHint hint) {
+  if (input->IsPrimitive()) return input;
+  return JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input), hint);
+}
+
+
 bool Object::HasSpecificClassOf(String* name) {
   return this->IsJSObject() && (JSObject::cast(this)->class_name() == name);
 }
index fc46188..260a64b 100644 (file)
@@ -90,14 +90,57 @@ MaybeHandle<JSReceiver> Object::ToObject(Isolate* isolate,
 }
 
 
-MaybeHandle<Name> Object::ToName(Isolate* isolate, Handle<Object> object) {
-  if (object->IsName()) {
-    return Handle<Name>::cast(object);
-  } else {
-    Handle<Object> converted;
-    ASSIGN_RETURN_ON_EXCEPTION(isolate, converted,
-                               Execution::ToString(isolate, object), Name);
-    return Handle<Name>::cast(converted);
+// static
+MaybeHandle<Object> Object::ToNumber(Isolate* isolate, Handle<Object> input) {
+  while (true) {
+    if (input->IsNumber()) {
+      return input;
+    }
+    if (input->IsOddball()) {
+      return handle(Handle<Oddball>::cast(input)->to_number(), isolate);
+    }
+    if (input->IsString()) {
+      return String::ToNumber(Handle<String>::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<JSReceiver>::cast(input),
+                                                ToPrimitiveHint::kNumber),
+        Object);
+  }
+}
+
+
+// static
+MaybeHandle<String> Object::ToString(Isolate* isolate, Handle<Object> input) {
+  while (true) {
+    if (input->IsString()) {
+      return Handle<String>::cast(input);
+    }
+    if (input->IsOddball()) {
+      return handle(Handle<Oddball>::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<Simd128Value>::cast(input));
+    }
+    ASSIGN_RETURN_ON_EXCEPTION(
+        isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::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> object) {
 }
 
 
+// static
+MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver,
+                                      Handle<Name> name) {
+  Handle<Object> 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> 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<const byte*>(FIELD_ADDR_CONST(p, offset)))
 
 
+// static
+Handle<String> Simd128Value::ToString(Handle<Simd128Value> input) {
+#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \
+  if (input->Is##Type()) return Type::ToString(Handle<Type>::cast(input));
+  SIMD128_TYPES(SIMD128_TYPE)
+#undef SIMD128_TYPE
+  UNREACHABLE();
+  return Handle<String>::null();
+}
+
+
+// static
+Handle<String> Float32x4::ToString(Handle<Float32x4> input) {
+  Isolate* const isolate = input->GetIsolate();
+  char arr[100];
+  Vector<char> 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<String> Type::ToString(Handle<Type> 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<String> Type::ToString(Handle<Type> input) {                       \
+    Isolate* const isolate = input->GetIsolate();                           \
+    char arr[100];                                                          \
+    Vector<char> 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> 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> JSObject::DeepCopy(
 }
 
 
+// static
+MaybeHandle<Object> JSReceiver::ToPrimitive(Handle<JSReceiver> receiver,
+                                            ToPrimitiveHint hint) {
+  Isolate* const isolate = receiver->GetIsolate();
+  Handle<Object> 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<Object> 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<Object> 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<Object> JSReceiver::OrdinaryToPrimitive(Handle<JSReceiver> receiver,
+                                                    Handle<String> hint) {
+  Isolate* const isolate = receiver->GetIsolate();
+  Handle<String> 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<String> name : method_names) {
+    Handle<Object> method;
+    ASSIGN_RETURN_ON_EXCEPTION(isolate, method,
+                               JSReceiver::GetProperty(receiver, name), Object);
+    if (method->IsCallable()) {
+      Handle<Object> 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<Object> String::ToNumber(Handle<String> 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<SeqOneByteString>::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 &nbsp;.
+      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<int>(subject->hash_field()),
+                  static_cast<int>(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();
index f7492eb..f7aea06 100644 (file)
@@ -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<JSReceiver> ToObject(
       Isolate* isolate, Handle<Object> object, Handle<Context> context);
 
-  // Convert to a Name if needed.
-  MUST_USE_RESULT static MaybeHandle<Name> ToName(Isolate* isolate,
-                                                  Handle<Object> object);
+  // ES6 section 7.1.14 ToPropertyKey
+  MUST_USE_RESULT static inline MaybeHandle<Name> ToName(Isolate* isolate,
+                                                         Handle<Object> input);
+
+  // ES6 section 7.1.1 ToPrimitive
+  MUST_USE_RESULT static inline MaybeHandle<Object> ToPrimitive(
+      Handle<Object> input, ToPrimitiveHint hint = ToPrimitiveHint::kDefault);
+
+  // ES6 section 7.1.3 ToNumber
+  MUST_USE_RESULT static MaybeHandle<Object> ToNumber(Isolate* isolate,
+                                                      Handle<Object> input);
+
+  // ES6 section 7.1.12 ToString
+  MUST_USE_RESULT static MaybeHandle<String> ToString(Isolate* isolate,
+                                                      Handle<Object> input);
+
+  // ES6 section 7.3.9 GetMethod
+  MUST_USE_RESULT static MaybeHandle<Object> GetMethod(
+      Handle<JSReceiver> receiver, Handle<Name> name);
 
   MUST_USE_RESULT static MaybeHandle<Object> GetProperty(
       LookupIterator* it, LanguageMode language_mode = SLOPPY);
@@ -1578,6 +1599,8 @@ class Simd128Value : public HeapObject {
   DECLARE_PRINTER(Simd128Value)
   DECLARE_VERIFIER(Simd128Value)
 
+  static Handle<String> ToString(Handle<Simd128Value> input);
+
   // Equality operations.
   inline bool Equals(Simd128Value* that);
 
@@ -1620,6 +1643,8 @@ class Simd128Value : public HeapObject {
                                                                      \
     DECLARE_PRINTER(Type)                                            \
                                                                      \
+    static Handle<String> ToString(Handle<Type> 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<Object> ToPrimitive(
+      Handle<JSReceiver> receiver,
+      ToPrimitiveHint hint = ToPrimitiveHint::kDefault);
+  MUST_USE_RESULT static MaybeHandle<Object> OrdinaryToPrimitive(
+      Handle<JSReceiver> receiver, Handle<String> hint);
+
   // Implementation of [[HasProperty]], ECMA-262 5th edition, section 8.12.6.
   MUST_USE_RESULT static inline Maybe<bool> HasProperty(
       Handle<JSReceiver> object, Handle<Name> 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<Object> ToNumber(Handle<String> 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
index 896dd6c..50e8b7f 100644 (file)
@@ -3433,7 +3433,7 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
   __ bind(&not_oddball);
 
   __ push(r3);  // Push argument.
-  __ InvokeBuiltin(Context::TO_NUMBER_BUILTIN_INDEX, JUMP_FUNCTION);
+  __ TailCallRuntime(Runtime::kToNumber, 1, 1);
 }
 
 
index 1503373..1ef4e97 100644 (file)
@@ -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,
 ]);
 
index 73d5110..290c228 100644 (file)
@@ -352,7 +352,7 @@ RUNTIME_FUNCTION(Runtime_InternalDateFormat) {
 
   Handle<Object> 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<Object> 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);
index 49734ba..d99d5fc 100644 (file)
@@ -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<SeqOneByteString>::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 &nbsp;.
-      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<int>(subject->hash_field()),
-                  static_cast<int>(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);
 }
 
 
index 2dd5db8..e493118 100644 (file)
@@ -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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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());
index fa8f7a8..3839a27 100644 (file)
@@ -1139,7 +1139,7 @@ RUNTIME_FUNCTION(Runtime_Arguments) {
   // Convert the key to a string.
   Handle<Object> converted;
   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, converted,
-                                     Execution::ToString(isolate, raw_key));
+                                     Object::ToString(isolate, raw_key));
   Handle<String> key = Handle<String>::cast(converted);
 
   // Try to convert the string key into an array index.
index 18faeb8..f043254 100644 (file)
@@ -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)
index 8cac2c5..3f6e281 100644 (file)
@@ -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);
 
index a3db777..aca21a1 100644 (file)
@@ -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);
 }
 
 
index aa57534..ff05f4d 100644 (file)
@@ -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);
 }
 
 
index 3dff7ef..2c85596 100644 (file)
@@ -74,16 +74,14 @@ TEST(HeapMaps) {
 static void CheckOddball(Isolate* isolate, Object* obj, const char* string) {
   CHECK(obj->IsOddball());
   Handle<Object> 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<Object> 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<Object> number = isolate->factory()->NewNumber(value);
   CHECK(number->IsNumber());
   Handle<Object> 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 (file)
index 0000000..7125766
--- /dev/null
@@ -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 (file)
index 0000000..7557425
--- /dev/null
@@ -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));