Commit a2d61597 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

Reland "[turbofan] Inline Number constructor in certain cases"

This is a reland of 9eca23e9

Adds a deopt continuation, which fixes JavaScript stack traces
to contain the number constructor after inlining.

Original change's description:
> [turbofan] Inline Number constructor in certain cases
>
> This CL adds inlining for the Number constructor if new.target is not
> present. The lowering is BigInt compatible, i.e. it converts BigInts to
> numbers.
>
> Bug: v8:7904
> Change-Id: If03b9f872d82e50b6ded7709069181c33dc44e82
> Reviewed-on: https://chromium-review.googlesource.com/1118557
> Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#54454}

Bug: v8:7904
Change-Id: Ic416e5ba81fa3a0f59ae4afa80df83c46a759487
Reviewed-on: https://chromium-review.googlesource.com/1146581
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54609}
parent 108bef16
......@@ -730,6 +730,12 @@ TF_BUILTIN(NumberConstructor, ConstructorBuiltinsAssembler) {
}
}
TF_BUILTIN(GenericConstructorLazyDeoptContinuation,
ConstructorBuiltinsAssembler) {
Node* result = Parameter(Descriptor::kResult);
Return(result);
}
// https://tc39.github.io/ecma262/#sec-string-constructor
TF_BUILTIN(StringConstructor, ConstructorBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
......
......@@ -142,6 +142,14 @@ TF_BUILTIN(ToNumber, CodeStubAssembler) {
Return(ToNumber(context, input));
}
// Like ToNumber, but also converts BigInts.
TF_BUILTIN(ToNumberConvertBigInt, CodeStubAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* input = Parameter(Descriptor::kArgument);
Return(ToNumber(context, input, BigIntHandling::kConvertToNumber));
}
// ES section #sec-tostring-applied-to-the-number-type
TF_BUILTIN(NumberToString, CodeStubAssembler) {
TNode<Number> input = CAST(Parameter(Descriptor::kArgument));
......
......@@ -186,6 +186,7 @@ namespace internal {
TFC(NonNumberToNumber, TypeConversion, 1) \
TFC(NonNumberToNumeric, TypeConversion, 1) \
TFC(ToNumber, TypeConversion, 1) \
TFC(ToNumberConvertBigInt, TypeConversion, 1) \
TFC(ToNumeric, TypeConversion, 1) \
TFC(NumberToString, TypeConversion, 1) \
TFC(ToString, TypeConversion, 1) \
......@@ -1132,7 +1133,7 @@ namespace internal {
/* ES #sec-typedarray-constructors */ \
TFS(CreateTypedArray, kTarget, kNewTarget, kArg1, kArg2, kArg3) \
TFJ(TypedArrayBaseConstructor, 0, kReceiver) \
TFJ(TypedArrayConstructorLazyDeoptContinuation, 1, kReceiver, kResult) \
TFJ(GenericConstructorLazyDeoptContinuation, 1, kReceiver, kResult) \
TFJ(TypedArrayConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
CPP(TypedArrayPrototypeBuffer) \
/* ES6 #sec-get-%typedarray%.prototype.bytelength */ \
......
......@@ -750,12 +750,6 @@ TF_BUILTIN(CreateTypedArray, TypedArrayBuiltinsAssembler) {
Return(result);
}
TF_BUILTIN(TypedArrayConstructorLazyDeoptContinuation,
TypedArrayBuiltinsAssembler) {
Node* result = Parameter(Descriptor::kResult);
Return(result);
}
// ES #sec-typedarray-constructors
TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
......
......@@ -294,7 +294,7 @@ bool Builtins::IsLazy(int index) {
case kThrowWasmTrapUnreachable: // Required by wasm.
case kToBooleanLazyDeoptContinuation:
case kToNumber: // Required by wasm.
case kTypedArrayConstructorLazyDeoptContinuation:
case kGenericConstructorLazyDeoptContinuation:
case kWasmCompileLazy: // Required by wasm.
case kWasmStackGuard: // Required by wasm.
return false;
......
......@@ -3675,6 +3675,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceDatePrototypeGetTime(node);
case Builtins::kDateNow:
return ReduceDateNow(node);
case Builtins::kNumberConstructor:
return ReduceNumberConstructor(node);
default:
break;
}
......@@ -6034,7 +6036,7 @@ Reduction JSCallReducer::ReduceTypedArrayConstructor(
Node* const parameters[] = {jsgraph()->TheHoleConstant()};
int const num_parameters = static_cast<int>(arraysize(parameters));
frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), shared, Builtins::kTypedArrayConstructorLazyDeoptContinuation,
jsgraph(), shared, Builtins::kGenericConstructorLazyDeoptContinuation,
target, context, parameters, num_parameters, frame_state,
ContinuationFrameStateMode::LAZY);
......@@ -7182,6 +7184,45 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) {
return Changed(node);
}
// ES section #sec-number-constructor
Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
if (p.arity() <= 2) {
ReplaceWithValue(node, jsgraph()->ZeroConstant());
}
// We don't have a new.target argument, so we can convert to number,
// but must also convert BigInts.
if (p.arity() == 3) {
Node* target = NodeProperties::GetValueInput(node, 0);
Node* context = NodeProperties::GetContextInput(node);
Node* value = NodeProperties::GetValueInput(node, 2);
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
Handle<SharedFunctionInfo> number_constructor(
handle(native_context()->number_function()->shared(), isolate()));
const std::vector<Node*> checkpoint_parameters({
jsgraph()->UndefinedConstant(), /* receiver */
});
int checkpoint_parameters_size =
static_cast<int>(checkpoint_parameters.size());
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), number_constructor,
Builtins::kGenericConstructorLazyDeoptContinuation, target, context,
checkpoint_parameters.data(), checkpoint_parameters_size,
outer_frame_state, ContinuationFrameStateMode::LAZY);
NodeProperties::ReplaceValueInputs(node, value);
NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt());
NodeProperties::ReplaceFrameStateInput(node, frame_state);
return Changed(node);
}
return NoChange();
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
......
......@@ -190,6 +190,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceDateNow(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceNumberConstructor(Node* node);
// Returns the updated {to} node, and updates control and effect along the
// way.
Node* DoFilterPostCallbackWork(ElementsKind kind, Node** control,
......
......@@ -82,6 +82,7 @@ REPLACE_STUB_CALL(Equal)
REPLACE_STUB_CALL(ToInteger)
REPLACE_STUB_CALL(ToLength)
REPLACE_STUB_CALL(ToNumber)
REPLACE_STUB_CALL(ToNumberConvertBigInt)
REPLACE_STUB_CALL(ToNumeric)
REPLACE_STUB_CALL(ToName)
REPLACE_STUB_CALL(ToObject)
......
......@@ -612,6 +612,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(ToLength, Operator::kNoProperties, 1, 1) \
V(ToName, Operator::kNoProperties, 1, 1) \
V(ToNumber, Operator::kNoProperties, 1, 1) \
V(ToNumberConvertBigInt, Operator::kNoProperties, 1, 1) \
V(ToNumeric, Operator::kNoProperties, 1, 1) \
V(ToObject, Operator::kFoldable, 1, 1) \
V(ToString, Operator::kNoProperties, 1, 1) \
......
......@@ -707,6 +707,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* ToLength();
const Operator* ToName();
const Operator* ToNumber();
const Operator* ToNumberConvertBigInt();
const Operator* ToNumeric();
const Operator* ToObject();
const Operator* ToString();
......
......@@ -2305,6 +2305,7 @@ Reduction JSTypedLowering::Reduce(Node* node) {
case IrOpcode::kJSToName:
return ReduceJSToName(node);
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric:
return ReduceJSToNumberOrNumeric(node);
case IrOpcode::kJSToString:
......
......@@ -240,7 +240,8 @@ InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
// TODO(jarin) Support both sides.
Node* input = arith->InputAt(0);
if (input->opcode() == IrOpcode::kSpeculativeToNumber ||
input->opcode() == IrOpcode::kJSToNumber) {
input->opcode() == IrOpcode::kJSToNumber ||
input->opcode() == IrOpcode::kJSToNumberConvertBigInt) {
input = input->InputAt(0);
}
if (input != phi) return nullptr;
......
......@@ -602,6 +602,7 @@ bool NodeProperties::CanBeNullOrUndefined(Isolate* isolate, Node* receiver,
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric:
case IrOpcode::kJSToString:
case IrOpcode::kToBoolean:
......
......@@ -121,6 +121,7 @@
V(JSToLength) \
V(JSToName) \
V(JSToNumber) \
V(JSToNumberConvertBigInt) \
V(JSToNumeric) \
V(JSToObject) \
V(JSToString) \
......
......@@ -265,7 +265,9 @@ Type OperationTyper::ConvertReceiver(Type type) {
return type;
}
Type OperationTyper::ToNumberOrNumeric(Object::Conversion mode, Type type) {
// Returns the result type of converting {type} to number, if the
// result does not depend on conversion options.
base::Optional<Type> OperationTyper::ToNumberCommon(Type type) {
if (type.Is(Type::Number())) return type;
if (type.Is(Type::NullOrUndefined())) {
if (type.Is(Type::Null())) return cache_.kSingletonZero;
......@@ -289,6 +291,13 @@ Type OperationTyper::ToNumberOrNumeric(Object::Conversion mode, Type type) {
}
return Type::Intersect(type, Type::Number(), zone());
}
return base::Optional<Type>();
}
Type OperationTyper::ToNumberOrNumeric(Object::Conversion mode, Type type) {
if (base::Optional<Type> maybe_result_type = ToNumberCommon(type)) {
return *maybe_result_type;
}
if (type.Is(Type::BigInt())) {
return mode == Object::Conversion::kToNumber ? Type::None() : type;
}
......@@ -300,6 +309,13 @@ Type OperationTyper::ToNumber(Type type) {
return ToNumberOrNumeric(Object::Conversion::kToNumber, type);
}
Type OperationTyper::ToNumberConvertBigInt(Type type) {
if (base::Optional<Type> maybe_result_type = ToNumberCommon(type)) {
return *maybe_result_type;
}
return Type::Number();
}
Type OperationTyper::ToNumeric(Type type) {
return ToNumberOrNumeric(Object::Conversion::kToNumeric, type);
}
......
......@@ -35,6 +35,7 @@ class V8_EXPORT_PRIVATE OperationTyper {
Type ToPrimitive(Type type);
Type ToNumber(Type type);
Type ToNumberConvertBigInt(Type type);
Type ToNumeric(Type type);
Type ToBoolean(Type type);
......@@ -78,6 +79,7 @@ class V8_EXPORT_PRIVATE OperationTyper {
typedef base::Flags<ComparisonOutcomeFlags> ComparisonOutcome;
Type ToNumberOrNumeric(Object::Conversion mode, Type type);
base::Optional<Type> ToNumberCommon(Type type);
ComparisonOutcome Invert(ComparisonOutcome);
Type Invert(Type);
......
......@@ -94,6 +94,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric:
case IrOpcode::kJSToObject:
case IrOpcode::kJSToString:
......
......@@ -1563,6 +1563,7 @@ class RepresentationSelector {
return;
}
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
case IrOpcode::kJSToNumeric: {
VisitInputs(node);
// TODO(bmeurer): Optimize somewhat based on input type?
......@@ -3297,6 +3298,7 @@ void SimplifiedLowering::LowerAllNodes() {
void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
Node* node, RepresentationSelector* selector) {
DCHECK(node->opcode() == IrOpcode::kJSToNumber ||
node->opcode() == IrOpcode::kJSToNumberConvertBigInt ||
node->opcode() == IrOpcode::kJSToNumeric);
Node* value = node->InputAt(0);
Node* context = node->InputAt(1);
......@@ -3320,11 +3322,17 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
Node* efalse0 = effect;
Node* vfalse0;
{
Operator const* op = node->opcode() == IrOpcode::kJSToNumber
? ToNumberOperator()
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber ? ToNumberCode()
: ToNumericCode();
Operator const* op =
node->opcode() == IrOpcode::kJSToNumber
? (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntOperator()
: ToNumberOperator())
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber
? ToNumberCode()
: (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntCode()
: ToNumericCode());
vfalse0 = efalse0 = if_false0 = graph()->NewNode(
op, code, value, context, frame_state, efalse0, if_false0);
......@@ -3392,6 +3400,7 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToFloat64(
void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToWord32(
Node* node, RepresentationSelector* selector) {
DCHECK(node->opcode() == IrOpcode::kJSToNumber ||
node->opcode() == IrOpcode::kJSToNumberConvertBigInt ||
node->opcode() == IrOpcode::kJSToNumeric);
Node* value = node->InputAt(0);
Node* context = node->InputAt(1);
......@@ -3412,11 +3421,17 @@ void SimplifiedLowering::DoJSToNumberOrNumericTruncatesToWord32(
Node* efalse0 = effect;
Node* vfalse0;
{
Operator const* op = node->opcode() == IrOpcode::kJSToNumber
? ToNumberOperator()
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber ? ToNumberCode()
: ToNumericCode();
Operator const* op =
node->opcode() == IrOpcode::kJSToNumber
? (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntOperator()
: ToNumberOperator())
: ToNumericOperator();
Node* code = node->opcode() == IrOpcode::kJSToNumber
? ToNumberCode()
: (node->opcode() == IrOpcode::kJSToNumberConvertBigInt
? ToNumberConvertBigIntCode()
: ToNumericCode());
vfalse0 = efalse0 = if_false0 = graph()->NewNode(
op, code, value, context, frame_state, efalse0, if_false0);
......@@ -3922,6 +3937,16 @@ Node* SimplifiedLowering::ToNumberCode() {
return to_number_code_.get();
}
Node* SimplifiedLowering::ToNumberConvertBigIntCode() {
if (!to_number_convert_big_int_code_.is_set()) {
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kToNumberConvertBigInt);
to_number_convert_big_int_code_.set(
jsgraph()->HeapConstant(callable.code()));
}
return to_number_convert_big_int_code_.get();
}
Node* SimplifiedLowering::ToNumericCode() {
if (!to_numeric_code_.is_set()) {
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumeric);
......@@ -3942,6 +3967,19 @@ Operator const* SimplifiedLowering::ToNumberOperator() {
return to_number_operator_.get();
}
Operator const* SimplifiedLowering::ToNumberConvertBigIntOperator() {
if (!to_number_convert_big_int_operator_.is_set()) {
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kToNumberConvertBigInt);
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
auto call_descriptor =
Linkage::GetStubCallDescriptor(graph()->zone(), callable.descriptor(),
0, flags, Operator::kNoProperties);
to_number_convert_big_int_operator_.set(common()->Call(call_descriptor));
}
return to_number_convert_big_int_operator_.get();
}
Operator const* SimplifiedLowering::ToNumericOperator() {
if (!to_numeric_operator_.is_set()) {
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumeric);
......
......@@ -52,8 +52,10 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
Zone* const zone_;
TypeCache const& type_cache_;
SetOncePointer<Node> to_number_code_;
SetOncePointer<Node> to_number_convert_big_int_code_;
SetOncePointer<Node> to_numeric_code_;
SetOncePointer<Operator const> to_number_operator_;
SetOncePointer<Operator const> to_number_convert_big_int_operator_;
SetOncePointer<Operator const> to_numeric_operator_;
// TODO(danno): SimplifiedLowering shouldn't know anything about the source
......@@ -76,8 +78,10 @@ class V8_EXPORT_PRIVATE SimplifiedLowering final {
Node* Uint32Mod(Node* const node);
Node* ToNumberCode();
Node* ToNumberConvertBigIntCode();
Node* ToNumericCode();
Operator const* ToNumberOperator();
Operator const* ToNumberConvertBigIntOperator();
Operator const* ToNumericOperator();
friend class RepresentationSelector;
......
......@@ -266,6 +266,7 @@ class Typer::Visitor : public Reducer {
static Type ToLength(Type, Typer*);
static Type ToName(Type, Typer*);
static Type ToNumber(Type, Typer*);
static Type ToNumberConvertBigInt(Type, Typer*);
static Type ToNumeric(Type, Typer*);
static Type ToObject(Type, Typer*);
static Type ToString(Type, Typer*);
......@@ -529,6 +530,11 @@ Type Typer::Visitor::ToNumber(Type type, Typer* t) {
return t->operation_typer_.ToNumber(type);
}
// static
Type Typer::Visitor::ToNumberConvertBigInt(Type type, Typer* t) {
return t->operation_typer_.ToNumberConvertBigInt(type);
}
// static
Type Typer::Visitor::ToNumeric(Type type, Typer* t) {
return t->operation_typer_.ToNumeric(type);
......@@ -1125,6 +1131,10 @@ Type Typer::Visitor::TypeJSToNumber(Node* node) {
return TypeUnaryOp(node, ToNumber);
}
Type Typer::Visitor::TypeJSToNumberConvertBigInt(Node* node) {
return TypeUnaryOp(node, ToNumberConvertBigInt);
}
Type Typer::Visitor::TypeJSToNumeric(Node* node) {
return TypeUnaryOp(node, ToNumeric);
}
......
......@@ -629,6 +629,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckTypeIs(node, Type::Name());
break;
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToNumberConvertBigInt:
// Type is Number.
CheckTypeIs(node, Type::Number());
break;
......
......@@ -605,7 +605,8 @@ static void CheckIsConvertedToNumber(Node* val, Node* converted) {
CHECK_EQ(val, converted);
} else {
if (converted->opcode() == IrOpcode::kNumberConstant) return;
CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode());
CHECK(IrOpcode::kJSToNumber == converted->opcode() ||
IrOpcode::kJSToNumberConvertBigInt == converted->opcode());
CHECK_EQ(val, converted->InputAt(0));
}
}
......
......@@ -1039,6 +1039,19 @@
{"name": "OneLineComments"},
{"name": "MultiLineComment"}
]
},
{
"name": "Numbers",
"path": ["Numbers"],
"main": "run.js",
"flags": ["--allow-natives-syntax"],
"resources": [ "toNumber.js"],
"results_regexp": "^%s\\-Numbers\\(Score\\): (.+)$",
"tests": [
{"name": "Constructor"},
{"name": "UnaryPlus"},
{"name": "ParseFloat"}
]
}
]
}
// Copyright 2018 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.
load('../base.js');
load('toNumber.js');
function PrintResult(name, result) {
console.log(name);
console.log(name + '-Numbers(Score): ' + result);
}
function PrintError(name, error) {
PrintResult(name, error);
}
BenchmarkSuite.config.doWarmup = undefined;
BenchmarkSuite.config.doDeterministic = undefined;
BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
NotifyError: PrintError });
// Copyright 2018 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.
const A = [undefined, 12, "123"];
function NumberConstructor() {
Number.isNaN(Number(A[0]))
Number.isNaN(Number(A[1]))
Number.isNaN(Number(A[2]))
}
createSuite('Constructor', 1000, NumberConstructor, ()=>{});
function NumberPlus() {
Number.isNaN(+(A[0]))
Number.isNaN(+(A[1]))
Number.isNaN(+(A[2]))
}
createSuite('UnaryPlus', 1000, NumberPlus, ()=>{});
function NumberParseFloat() {
Number.isNaN(parseFloat(A[0]))
Number.isNaN(parseFloat(A[1]))
Number.isNaN(parseFloat(A[2]))
}
createSuite('ParseFloat', 1000, NumberParseFloat, ()=>{});
// Copyright 2018 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
// This test writes {} to x to trigger lazy deopt
// from inside the number constructor.
var x = "5";
var b = false;
check = function() {
if (b) x = {};
return 0;
}
var obj = {};
obj.valueOf = check;
function f() {
try {
return x + Number(obj);
} catch(e) {
console.log(e.stack);
}
}
f();
f();
%OptimizeFunctionOnNextCall(f);
b = true;
f();
// Copyright 2018 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 --harmony-bigint
function f(x, b) {
if (b) return Math.trunc(+(x))
else return Math.trunc(Number(x))
}
f("1", true);
f("2", true);
f("2", false);
%OptimizeFunctionOnNextCall(f);
f(3n);
......@@ -31,7 +31,7 @@ function Test(f, ...cases) {
function V(input, expected_value) {
function check(result) {
assertFalse(result.exception, input);
assertFalse(result.exception, `unexpected exception ${result.value} on input ${input}`);
assertEquals(expected_value, result.value);
}
return {input, check};
......@@ -39,7 +39,7 @@ function V(input, expected_value) {
function E(input, expected_exception) {
function check(result) {
assertTrue(result.exception, input);
assertTrue(result.exception, `expected exception ${expected_exception.name} on input ${input}`);
assertInstanceof(result.value, expected_exception);
}
return {input, check};
......@@ -56,10 +56,15 @@ const six = {[Symbol.toPrimitive]() {return 6n}};
// inputs.
////////////////////////////////////////////////////////////////////////////////
Test(x => Number(x),
V(1n, 1), V(1, 1), V("", 0), V(1.4, 1.4), V(null, 0), V(six, 6));
Test(x => Math.trunc(+x),
E(1n, TypeError), V(1, 1), V("", 0), V(1.4, 1), V(null, 0), E(six, TypeError));
Test(x => Math.trunc(Number(x)),
V(1n, 1), V(1, 1), V("", 0), V(1.4, 1), V(null, 0), V(six, 6));
Test(x => String(x),
V(1n, "1"), V(1, "1"), V(1.4, "1.4"), V(null, "null"), V(six, "6"));
......
// Copyright 2018 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
check = function() {
assertEquals(null, check.caller);
}
var obj = {};
obj.valueOf = check;
function f() {
Number(obj);
}
f();
%OptimizeFunctionOnNextCall(f);
f();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment