From e678a0f9a961165ab43cd1a6080fcb11995b701a Mon Sep 17 00:00:00 2001 From: bmeurer Date: Mon, 19 Oct 2015 01:35:00 -0700 Subject: [PATCH] [runtime] Implement %_ToLength via ToLengthStub. Use %_ToLength for TO_LENGTH, implemented via a ToLengthStub that supports a fast path for small integers. Everything else is still handled in the runtime. CQ_INCLUDE_TRYBOTS=tryserver.v8:v8_linux_nosnap_rel BUG=v8:4494 LOG=n Review URL: https://codereview.chromium.org/1412963002 Cr-Commit-Position: refs/heads/master@{#31358} --- src/arm/code-stubs-arm.cc | 15 ++++ src/arm/interface-descriptors-arm.cc | 4 + src/arm64/code-stubs-arm64.cc | 15 ++++ src/arm64/interface-descriptors-arm64.cc | 4 + src/code-factory.cc | 7 ++ src/code-factory.h | 1 + src/code-stubs.h | 10 +++ src/full-codegen/arm/full-codegen-arm.cc | 13 ++++ src/full-codegen/arm64/full-codegen-arm64.cc | 13 ++++ src/full-codegen/full-codegen.h | 1 + src/full-codegen/ia32/full-codegen-ia32.cc | 13 ++++ src/full-codegen/mips/full-codegen-mips.cc | 14 ++++ .../mips64/full-codegen-mips64.cc | 14 ++++ src/full-codegen/x64/full-codegen-x64.cc | 13 ++++ src/hydrogen.cc | 14 ++++ src/hydrogen.h | 1 + src/ia32/code-stubs-ia32.cc | 19 +++++ src/ia32/interface-descriptors-ia32.cc | 4 + src/interface-descriptors.cc | 7 ++ src/interface-descriptors.h | 11 +++ src/js/macros.py | 2 +- src/mips/code-stubs-mips.cc | 17 +++++ src/mips/interface-descriptors-mips.cc | 4 + src/mips64/code-stubs-mips64.cc | 17 +++++ src/mips64/interface-descriptors-mips64.cc | 4 + src/x64/code-stubs-x64.cc | 19 +++++ src/x64/interface-descriptors-x64.cc | 4 + test/mjsunit/harmony/to-length.js | 76 +++++++++++++++++++ 28 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 test/mjsunit/harmony/to-length.js diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 35928107f2..d85ebdb114 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3168,6 +3168,21 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToLengthStub::Generate(MacroAssembler* masm) { + // The ToLength stub takes one argument in r0. + Label not_smi; + __ JumpIfNotSmi(r0, ¬_smi); + STATIC_ASSERT(kSmiTag == 0); + __ tst(r0, r0); + __ mov(r0, Operand(0), LeaveCC, lt); + __ Ret(); + __ bind(¬_smi); + + __ push(r0); // Push argument. + __ TailCallRuntime(Runtime::kToLength, 1, 1); +} + + void ToStringStub::Generate(MacroAssembler* masm) { // The ToString stub takes one argument in r0. Label is_number; diff --git a/src/arm/interface-descriptors-arm.cc b/src/arm/interface-descriptors-arm.cc index 274bbb102f..963b77782a 100644 --- a/src/arm/interface-descriptors-arm.cc +++ b/src/arm/interface-descriptors-arm.cc @@ -101,6 +101,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToLengthDescriptor::ReceiverRegister() { return r0; } + + // static const Register ToStringDescriptor::ReceiverRegister() { return r0; } diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc index fc75e082ba..0a7b475265 100644 --- a/src/arm64/code-stubs-arm64.cc +++ b/src/arm64/code-stubs-arm64.cc @@ -3914,6 +3914,21 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToLengthStub::Generate(MacroAssembler* masm) { + // The ToLength stub takes one argument in x0. + Label not_smi; + __ JumpIfNotSmi(x0, ¬_smi); + STATIC_ASSERT(kSmiTag == 0); + __ Tst(x0, x0); + __ Csel(x0, x0, Operand(0), ge); + __ Ret(); + __ Bind(¬_smi); + + __ Push(x0); // Push argument. + __ TailCallRuntime(Runtime::kToLength, 1, 1); +} + + void ToStringStub::Generate(MacroAssembler* masm) { // The ToString stub takes one argument in x0. Label is_number; diff --git a/src/arm64/interface-descriptors-arm64.cc b/src/arm64/interface-descriptors-arm64.cc index 0373d97672..4e1b818065 100644 --- a/src/arm64/interface-descriptors-arm64.cc +++ b/src/arm64/interface-descriptors-arm64.cc @@ -102,6 +102,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToLengthDescriptor::ReceiverRegister() { return x0; } + + // static const Register ToStringDescriptor::ReceiverRegister() { return x0; } diff --git a/src/code-factory.cc b/src/code-factory.cc index 2398192785..037a866681 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -182,6 +182,13 @@ Callable CodeFactory::ToString(Isolate* isolate) { } +// static +Callable CodeFactory::ToLength(Isolate* isolate) { + ToLengthStub stub(isolate); + return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor()); +} + + // static Callable CodeFactory::ToObject(Isolate* isolate) { ToObjectStub stub(isolate); diff --git a/src/code-factory.h b/src/code-factory.h index ec1435c199..f93f46e748 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -74,6 +74,7 @@ class CodeFactory final { static Callable ToNumber(Isolate* isolate); static Callable ToString(Isolate* isolate); + static Callable ToLength(Isolate* isolate); static Callable ToObject(Isolate* isolate); static Callable StringAdd(Isolate* isolate, StringAddFlags flags, diff --git a/src/code-stubs.h b/src/code-stubs.h index 51ea6aa715..b53d52a3b3 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -52,6 +52,7 @@ namespace internal { V(StubFailureTrampoline) \ V(SubString) \ V(ToNumber) \ + V(ToLength) \ V(ToString) \ V(ToObject) \ V(VectorStoreICTrampoline) \ @@ -3076,6 +3077,15 @@ class ToNumberStub final : public PlatformCodeStub { }; +class ToLengthStub final : public PlatformCodeStub { + public: + explicit ToLengthStub(Isolate* isolate) : PlatformCodeStub(isolate) {} + + DEFINE_CALL_INTERFACE_DESCRIPTOR(ToLength); + DEFINE_PLATFORM_CODE_STUB(ToLength, PlatformCodeStub); +}; + + class ToStringStub final : public PlatformCodeStub { public: explicit ToStringStub(Isolate* isolate) : PlatformCodeStub(isolate) {} diff --git a/src/full-codegen/arm/full-codegen-arm.cc b/src/full-codegen/arm/full-codegen-arm.cc index efe055d1d6..178672ebca 100644 --- a/src/full-codegen/arm/full-codegen-arm.cc +++ b/src/full-codegen/arm/full-codegen-arm.cc @@ -3797,6 +3797,19 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToLength(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into r0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToLengthStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(r0); +} + + void FullCodeGenerator::EmitToString(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/arm64/full-codegen-arm64.cc b/src/full-codegen/arm64/full-codegen-arm64.cc index c4a4136f63..074982e83e 100644 --- a/src/full-codegen/arm64/full-codegen-arm64.cc +++ b/src/full-codegen/arm64/full-codegen-arm64.cc @@ -3507,6 +3507,19 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToLength(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into x0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToLengthStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(x0); +} + + void FullCodeGenerator::EmitToString(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/full-codegen.h b/src/full-codegen/full-codegen.h index 96250c3047..d118aea889 100644 --- a/src/full-codegen/full-codegen.h +++ b/src/full-codegen/full-codegen.h @@ -518,6 +518,7 @@ class FullCodeGenerator: public AstVisitor { F(ToInteger) \ F(NumberToString) \ F(ToString) \ + F(ToLength) \ F(ToNumber) \ F(ToName) \ F(ToObject) \ diff --git a/src/full-codegen/ia32/full-codegen-ia32.cc b/src/full-codegen/ia32/full-codegen-ia32.cc index a0852b0d21..6f0414d341 100644 --- a/src/full-codegen/ia32/full-codegen-ia32.cc +++ b/src/full-codegen/ia32/full-codegen-ia32.cc @@ -3695,6 +3695,19 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToLength(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into eax and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToLengthStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(eax); +} + + void FullCodeGenerator::EmitToString(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/mips/full-codegen-mips.cc b/src/full-codegen/mips/full-codegen-mips.cc index a1468b9cbe..cd5ee34ea8 100644 --- a/src/full-codegen/mips/full-codegen-mips.cc +++ b/src/full-codegen/mips/full-codegen-mips.cc @@ -3811,6 +3811,20 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToLength(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into a0 and convert it. + VisitForAccumulatorValue(args->at(0)); + __ mov(a0, result_register()); + + ToLengthStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(v0); +} + + void FullCodeGenerator::EmitToString(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/mips64/full-codegen-mips64.cc b/src/full-codegen/mips64/full-codegen-mips64.cc index ceb0926eb7..020d385161 100644 --- a/src/full-codegen/mips64/full-codegen-mips64.cc +++ b/src/full-codegen/mips64/full-codegen-mips64.cc @@ -3814,6 +3814,20 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToLength(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into a0 and convert it. + VisitForAccumulatorValue(args->at(0)); + __ mov(a0, result_register()); + + ToLengthStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(v0); +} + + void FullCodeGenerator::EmitToString(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/x64/full-codegen-x64.cc b/src/full-codegen/x64/full-codegen-x64.cc index c620e4863d..d75cc0ff2d 100644 --- a/src/full-codegen/x64/full-codegen-x64.cc +++ b/src/full-codegen/x64/full-codegen-x64.cc @@ -3704,6 +3704,19 @@ void FullCodeGenerator::EmitToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToLength(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into rax and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToLengthStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(rax); +} + + void FullCodeGenerator::EmitToNumber(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 615ea71d8a..56ccabb750 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -12285,6 +12285,20 @@ void HOptimizedGraphBuilder::GenerateToString(CallRuntime* call) { } +void HOptimizedGraphBuilder::GenerateToLength(CallRuntime* call) { + DCHECK_EQ(1, call->arguments()->length()); + CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); + Callable callable = CodeFactory::ToLength(isolate()); + HValue* input = Pop(); + HValue* stub = Add(callable.code()); + HValue* values[] = {context(), input}; + HInstruction* result = + New(stub, 0, callable.descriptor(), + Vector(values, arraysize(values))); + return ast_context()->ReturnInstruction(result, call->id()); +} + + void HOptimizedGraphBuilder::GenerateToNumber(CallRuntime* call) { DCHECK_EQ(1, call->arguments()->length()); CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); diff --git a/src/hydrogen.h b/src/hydrogen.h index 464d857a46..4d0d2b6715 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -2226,6 +2226,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { F(ToInteger) \ F(ToObject) \ F(ToString) \ + F(ToLength) \ F(ToNumber) \ F(IsFunction) \ F(IsSpecObject) \ diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 4431944c1f..2f2929f5cf 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -3192,6 +3192,25 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToLengthStub::Generate(MacroAssembler* masm) { + // The ToLength stub takes on argument in eax. + Label not_smi, positive_smi; + __ JumpIfNotSmi(eax, ¬_smi, Label::kNear); + STATIC_ASSERT(kSmiTag == 0); + __ test(eax, eax); + __ j(greater_equal, &positive_smi, Label::kNear); + __ xor_(eax, eax); + __ bind(&positive_smi); + __ Ret(); + __ bind(¬_smi); + + __ pop(ecx); // Pop return address. + __ push(eax); // Push argument. + __ push(ecx); // Push return address. + __ TailCallRuntime(Runtime::kToLength, 1, 1); +} + + void ToStringStub::Generate(MacroAssembler* masm) { // The ToString stub takes one argument in eax. Label is_number; diff --git a/src/ia32/interface-descriptors-ia32.cc b/src/ia32/interface-descriptors-ia32.cc index f6ad72c6d7..2077dd76e6 100644 --- a/src/ia32/interface-descriptors-ia32.cc +++ b/src/ia32/interface-descriptors-ia32.cc @@ -105,6 +105,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToLengthDescriptor::ReceiverRegister() { return eax; } + + // static const Register ToStringDescriptor::ReceiverRegister() { return eax; } diff --git a/src/interface-descriptors.cc b/src/interface-descriptors.cc index 95729095f4..c81ea8b7d2 100644 --- a/src/interface-descriptors.cc +++ b/src/interface-descriptors.cc @@ -189,6 +189,13 @@ void StringCompareDescriptor::InitializePlatformSpecific( } +void ToLengthDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {ReceiverRegister()}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + void ToStringDescriptor::InitializePlatformSpecific( CallInterfaceDescriptorData* data) { Register registers[] = {ReceiverRegister()}; diff --git a/src/interface-descriptors.h b/src/interface-descriptors.h index 481ffb9f8f..42dca2eba7 100644 --- a/src/interface-descriptors.h +++ b/src/interface-descriptors.h @@ -25,6 +25,7 @@ class PlatformInterfaceDescriptor; V(FastNewClosure) \ V(FastNewContext) \ V(ToNumber) \ + V(ToLength) \ V(ToString) \ V(ToObject) \ V(NumberToString) \ @@ -373,6 +374,16 @@ class ToNumberDescriptor : public CallInterfaceDescriptor { }; +class ToLengthDescriptor : public CallInterfaceDescriptor { + public: + enum ParameterIndices { kReceiverIndex }; + + DECLARE_DESCRIPTOR(ToLengthDescriptor, CallInterfaceDescriptor) + + static const Register ReceiverRegister(); +}; + + class ToStringDescriptor : public CallInterfaceDescriptor { public: enum ParameterIndices { kReceiverIndex }; diff --git a/src/js/macros.py b/src/js/macros.py index 8259ee8da5..8f8856b4f8 100644 --- a/src/js/macros.py +++ b/src/js/macros.py @@ -146,7 +146,7 @@ macro TO_INTEGER(arg) = (%_ToInteger(arg)); macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(arg)); macro TO_INT32(arg) = ((arg) | 0); macro TO_UINT32(arg) = ((arg) >>> 0); -macro TO_LENGTH(arg) = (%ToLength(arg)); +macro TO_LENGTH(arg) = (%_ToLength(arg)); macro TO_LENGTH_OR_UINT32(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_UINT32(arg)); macro TO_LENGTH_OR_INTEGER(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_INTEGER(arg)); macro TO_STRING(arg) = (%_ToString(arg)); diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index 60d96c28d9..3cf1145a19 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -3314,6 +3314,23 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToLengthStub::Generate(MacroAssembler* masm) { + // The ToLength stub takes on argument in a0. + Label not_smi, positive_smi; + __ JumpIfNotSmi(a0, ¬_smi); + STATIC_ASSERT(kSmiTag == 0); + __ Branch(&positive_smi, ge, a0, Operand(zero_reg)); + __ mov(a0, zero_reg); + __ bind(&positive_smi); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); + __ bind(¬_smi); + + __ push(a0); // Push argument. + __ TailCallRuntime(Runtime::kToLength, 1, 1); +} + + void ToStringStub::Generate(MacroAssembler* masm) { // The ToString stub takes on argument in a0. Label is_number; diff --git a/src/mips/interface-descriptors-mips.cc b/src/mips/interface-descriptors-mips.cc index 30fcaddb60..2fe3554b68 100644 --- a/src/mips/interface-descriptors-mips.cc +++ b/src/mips/interface-descriptors-mips.cc @@ -99,6 +99,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToLengthDescriptor::ReceiverRegister() { return a0; } + + // static const Register ToStringDescriptor::ReceiverRegister() { return a0; } diff --git a/src/mips64/code-stubs-mips64.cc b/src/mips64/code-stubs-mips64.cc index d1bdd7d901..bb6deb92c2 100644 --- a/src/mips64/code-stubs-mips64.cc +++ b/src/mips64/code-stubs-mips64.cc @@ -3348,6 +3348,23 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToLengthStub::Generate(MacroAssembler* masm) { + // The ToLength stub takes on argument in a0. + Label not_smi, positive_smi; + __ JumpIfNotSmi(a0, ¬_smi); + STATIC_ASSERT(kSmiTag == 0); + __ Branch(&positive_smi, ge, a0, Operand(zero_reg)); + __ mov(a0, zero_reg); + __ bind(&positive_smi); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); + __ bind(¬_smi); + + __ push(a0); // Push argument. + __ TailCallRuntime(Runtime::kToLength, 1, 1); +} + + void ToStringStub::Generate(MacroAssembler* masm) { // The ToString stub takes on argument in a0. Label is_number; diff --git a/src/mips64/interface-descriptors-mips64.cc b/src/mips64/interface-descriptors-mips64.cc index 6648684857..cf8a4456f3 100644 --- a/src/mips64/interface-descriptors-mips64.cc +++ b/src/mips64/interface-descriptors-mips64.cc @@ -99,6 +99,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToLengthDescriptor::ReceiverRegister() { return a0; } + + // static const Register ToStringDescriptor::ReceiverRegister() { return a0; } diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 2df677fad1..481e100c77 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -3122,6 +3122,25 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToLengthStub::Generate(MacroAssembler* masm) { + // The ToLength stub takes on argument in rax. + Label not_smi, positive_smi; + __ JumpIfNotSmi(rax, ¬_smi, Label::kNear); + STATIC_ASSERT(kSmiTag == 0); + __ testp(rax, rax); + __ j(greater_equal, &positive_smi, Label::kNear); + __ xorl(rax, rax); + __ bind(&positive_smi); + __ Ret(); + __ bind(¬_smi); + + __ PopReturnAddressTo(rcx); // Pop return address. + __ Push(rax); // Push argument. + __ PushReturnAddressFrom(rcx); // Push return address. + __ TailCallRuntime(Runtime::kToLength, 1, 1); +} + + void ToStringStub::Generate(MacroAssembler* masm) { // The ToString stub takes one argument in rax. Label is_number; diff --git a/src/x64/interface-descriptors-x64.cc b/src/x64/interface-descriptors-x64.cc index c8426c54e3..e69d38d1f3 100644 --- a/src/x64/interface-descriptors-x64.cc +++ b/src/x64/interface-descriptors-x64.cc @@ -107,6 +107,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToLengthDescriptor::ReceiverRegister() { return rax; } + + // static const Register ToStringDescriptor::ReceiverRegister() { return rax; } diff --git a/test/mjsunit/harmony/to-length.js b/test/mjsunit/harmony/to-length.js new file mode 100644 index 0000000000..b805e5cffe --- /dev/null +++ b/test/mjsunit/harmony/to-length.js @@ -0,0 +1,76 @@ +// 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(0, %ToLength(NaN)); +assertEquals(0, %_ToLength(NaN)); + +assertEquals(0, %ToLength(-Infinity)); +assertEquals(0, %_ToLength(-Infinity)); + +assertEquals(0, %ToLength(0)); +assertEquals(0, %_ToLength(0)); + +assertEquals(0, %ToLength(.5)); +assertEquals(0, %_ToLength(.5)); + +assertEquals(42, %ToLength(42.99999)); +assertEquals(42, %_ToLength(42.99999)); + +assertEquals(9007199254740991, %ToLength(9007199254740991)); +assertEquals(9007199254740991, %_ToLength(9007199254740991)); + +assertEquals(9007199254740991, %ToLength(Infinity)); +assertEquals(9007199254740991, %_ToLength(Infinity)); + +assertEquals(0, %ToLength(null)); +assertEquals(0, %_ToLength(null)); + +assertEquals(1, %ToLength(true)); +assertEquals(1, %_ToLength(true)); + +assertEquals(0, %ToLength(false)); +assertEquals(0, %_ToLength(false)); + +assertEquals(0, %ToLength(undefined)); +assertEquals(0, %_ToLength(undefined)); + +assertEquals(0, %ToLength("-1")); +assertEquals(0, %_ToLength("-1")); +assertEquals(123, %ToLength("123")); +assertEquals(123, %_ToLength("123")); +assertEquals(0, %ToLength("random text")); +assertEquals(0, %_ToLength("random text")); + +assertThrows(function() { %ToLength(Symbol.toPrimitive) }, TypeError); +assertThrows(function() { %_ToLength(Symbol.toPrimitive) }, TypeError); + +var a = { toString: function() { return 54321 }}; +assertEquals(54321, %ToLength(a)); +assertEquals(54321, %_ToLength(a)); + +var b = { valueOf: function() { return 42 }}; +assertEquals(42, %ToLength(b)); +assertEquals(42, %_ToLength(b)); + +var c = { + toString: function() { return "x"}, + valueOf: function() { return 123 } +}; +assertEquals(123, %ToLength(c)); +assertEquals(123, %_ToLength(c)); + +var d = { + [Symbol.toPrimitive]: function(hint) { + assertEquals("number", hint); + return 987654321; + } +}; +assertEquals(987654321, %ToLength(d)); +assertEquals(987654321, %_ToLength(d)); + +var e = new Date(0); +assertEquals(0, %ToLength(e)); +assertEquals(0, %_ToLength(e));