Commit 5bed1516 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Avoid introducing machine operators during typed lowering.

Introducing machine operators early causes trouble for the typing,
truncation analysis and representation selection, so we should rather
stick to simplified operators instead. Now there's only the for-in case
left, which is not clear how we can handle this in a better way.

Drive-by-fix: Also don't introduce Int32Constant and Word32Shl in
JSTypedLowering, but use NumberConstant and proper NumberShiftLeft
operators instead.

R=jarin@chromium.org
BUG=chromium:630951

Review-Url: https://codereview.chromium.org/2182453002
Cr-Commit-Position: refs/heads/master@{#38008}
parent 37ba8f96
...@@ -173,8 +173,8 @@ Reduction JSIntrinsicLowering::ReduceIsInstanceType( ...@@ -173,8 +173,8 @@ Reduction JSIntrinsicLowering::ReduceIsInstanceType(
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value, graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value,
effect, if_false), effect, if_false),
effect, if_false); effect, if_false);
Node* vfalse = graph()->NewNode(machine()->Word32Equal(), efalse, Node* vfalse = graph()->NewNode(simplified()->NumberEqual(), efalse,
jsgraph()->Int32Constant(instance_type)); jsgraph()->Constant(instance_type));
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
...@@ -386,12 +386,6 @@ JSOperatorBuilder* JSIntrinsicLowering::javascript() const { ...@@ -386,12 +386,6 @@ JSOperatorBuilder* JSIntrinsicLowering::javascript() const {
return jsgraph_->javascript(); return jsgraph_->javascript();
} }
MachineOperatorBuilder* JSIntrinsicLowering::machine() const {
return jsgraph()->machine();
}
SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const { SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const {
return jsgraph()->simplified(); return jsgraph()->simplified();
} }
......
...@@ -21,7 +21,6 @@ namespace compiler { ...@@ -21,7 +21,6 @@ namespace compiler {
class CommonOperatorBuilder; class CommonOperatorBuilder;
class JSOperatorBuilder; class JSOperatorBuilder;
class JSGraph; class JSGraph;
class MachineOperatorBuilder;
class SimplifiedOperatorBuilder; class SimplifiedOperatorBuilder;
...@@ -74,7 +73,6 @@ class JSIntrinsicLowering final : public AdvancedReducer { ...@@ -74,7 +73,6 @@ class JSIntrinsicLowering final : public AdvancedReducer {
Isolate* isolate() const; Isolate* isolate() const;
CommonOperatorBuilder* common() const; CommonOperatorBuilder* common() const;
JSOperatorBuilder* javascript() const; JSOperatorBuilder* javascript() const;
MachineOperatorBuilder* machine() const;
SimplifiedOperatorBuilder* simplified() const; SimplifiedOperatorBuilder* simplified() const;
DeoptimizationMode mode() const { return mode_; } DeoptimizationMode mode() const { return mode_; }
......
...@@ -219,7 +219,6 @@ class JSBinopReduction final { ...@@ -219,7 +219,6 @@ class JSBinopReduction final {
Graph* graph() const { return lowering_->graph(); } Graph* graph() const { return lowering_->graph(); }
JSGraph* jsgraph() { return lowering_->jsgraph(); } JSGraph* jsgraph() { return lowering_->jsgraph(); }
JSOperatorBuilder* javascript() { return lowering_->javascript(); } JSOperatorBuilder* javascript() { return lowering_->javascript(); }
MachineOperatorBuilder* machine() { return lowering_->machine(); }
CommonOperatorBuilder* common() { return jsgraph()->common(); } CommonOperatorBuilder* common() { return jsgraph()->common(); }
Zone* zone() const { return graph()->zone(); } Zone* zone() const { return graph()->zone(); }
...@@ -1019,7 +1018,11 @@ Reduction JSTypedLowering::ReduceJSToObject(Node* node) { ...@@ -1019,7 +1018,11 @@ Reduction JSTypedLowering::ReduceJSToObject(Node* node) {
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
if (!receiver_type->Is(Type::Receiver())) { if (receiver_type->Is(Type::Receiver())) {
ReplaceWithValue(node, receiver, effect, control);
return Replace(receiver);
}
// TODO(bmeurer/mstarzinger): Add support for lowering inside try blocks. // TODO(bmeurer/mstarzinger): Add support for lowering inside try blocks.
if (receiver_type->Maybe(Type::NullOrUndefined()) && if (receiver_type->Maybe(Type::NullOrUndefined()) &&
NodeProperties::IsExceptionalCall(node)) { NodeProperties::IsExceptionalCall(node)) {
...@@ -1027,70 +1030,44 @@ Reduction JSTypedLowering::ReduceJSToObject(Node* node) { ...@@ -1027,70 +1030,44 @@ Reduction JSTypedLowering::ReduceJSToObject(Node* node) {
return NoChange(); return NoChange();
} }
// Check whether {receiver} is a Smi.
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* etrue0 = effect;
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = effect;
// Determine the instance type of {receiver}.
Node* receiver_map = efalse0 =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
receiver, efalse0, if_false0);
Node* receiver_instance_type = efalse0 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
receiver_map, efalse0, if_false0);
// Check whether {receiver} is a spec object. // Check whether {receiver} is a spec object.
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), receiver);
Node* check1 = Node* branch =
graph()->NewNode(machine()->Uint32LessThanOrEqual(), graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
jsgraph()->Uint32Constant(FIRST_JS_RECEIVER_TYPE),
receiver_instance_type);
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check1, if_false0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* etrue1 = efalse0;
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* efalse1 = efalse0; Node* etrue = effect;
Node* rtrue = receiver;
// Convert {receiver} using the ToObjectStub. Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* if_convert = Node* efalse = effect;
graph()->NewNode(common()->Merge(2), if_true0, if_false1); Node* rfalse;
Node* econvert =
graph()->NewNode(common()->EffectPhi(2), etrue0, efalse1, if_convert);
Node* rconvert;
{ {
// Convert {receiver} using the ToObjectStub.
Callable callable = CodeFactory::ToObject(isolate()); Callable callable = CodeFactory::ToObject(isolate());
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, isolate(), graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNeedsFrameState, node->op()->properties()); CallDescriptor::kNeedsFrameState, node->op()->properties());
rconvert = econvert = graph()->NewNode( rfalse = efalse = graph()->NewNode(
common()->Call(desc), jsgraph()->HeapConstant(callable.code()), common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
receiver, context, frame_state, econvert, if_convert); receiver, context, frame_state, efalse, if_false);
if_false = graph()->NewNode(common()->IfSuccess(), rfalse);
} }
// The {receiver} is already a spec object. control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* if_done = if_true1; effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
Node* edone = etrue1;
Node* rdone = receiver;
control = graph()->NewNode(common()->Merge(2), if_convert, if_done); // Morph the {node} into an appropriate Phi.
effect = graph()->NewNode(common()->EffectPhi(2), econvert, edone, control); ReplaceWithValue(node, node, effect, control);
receiver = node->ReplaceInput(0, rtrue);
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), node->ReplaceInput(1, rfalse);
rconvert, rdone, control); node->ReplaceInput(2, control);
} node->TrimInputCount(3);
ReplaceWithValue(node, receiver, effect, control); NodeProperties::ChangeOp(node,
return Changed(receiver); common()->Phi(MachineRepresentation::kTagged, 2));
return Changed(node);
} }
Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) { Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 0); Node* receiver = NodeProperties::GetValueInput(node, 0);
...@@ -1144,7 +1121,10 @@ Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) { ...@@ -1144,7 +1121,10 @@ Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) {
return Replace(load); return Replace(load);
} }
// Compute byte offset. // Compute byte offset.
Node* offset = Word32Shl(key, static_cast<int>(k)); Node* offset =
(k == 0) ? key : graph()->NewNode(
simplified()->NumberShiftLeft(), key,
jsgraph()->Constant(static_cast<double>(k)));
Node* load = graph()->NewNode(simplified()->LoadBuffer(access), buffer, Node* load = graph()->NewNode(simplified()->LoadBuffer(access), buffer,
offset, length, effect, control); offset, length, effect, control);
ReplaceWithValue(node, load, load); ReplaceWithValue(node, load, load);
...@@ -1212,7 +1192,10 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { ...@@ -1212,7 +1192,10 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
return Changed(node); return Changed(node);
} }
// Compute byte offset. // Compute byte offset.
Node* offset = Word32Shl(key, static_cast<int>(k)); Node* offset =
(k == 0) ? key : graph()->NewNode(
simplified()->NumberShiftLeft(), key,
jsgraph()->Constant(static_cast<double>(k)));
// Turn into a StoreBuffer operation. // Turn into a StoreBuffer operation.
RelaxControls(node); RelaxControls(node);
node->ReplaceInput(0, buffer); node->ReplaceInput(0, buffer);
...@@ -1307,10 +1290,10 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) { ...@@ -1307,10 +1290,10 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) {
int is_access_check_needed_bit = 1 << Map::kIsAccessCheckNeeded; int is_access_check_needed_bit = 1 << Map::kIsAccessCheckNeeded;
Node* is_access_check_needed_num = Node* is_access_check_needed_num =
graph()->NewNode(simplified()->NumberBitwiseAnd(), map_bit_field, graph()->NewNode(simplified()->NumberBitwiseAnd(), map_bit_field,
jsgraph()->Uint32Constant(is_access_check_needed_bit)); jsgraph()->Constant(is_access_check_needed_bit));
Node* is_access_check_needed = Node* is_access_check_needed =
graph()->NewNode(machine()->Word32Equal(), is_access_check_needed_num, graph()->NewNode(simplified()->NumberEqual(), is_access_check_needed_num,
jsgraph()->Uint32Constant(is_access_check_needed_bit)); jsgraph()->Constant(is_access_check_needed_bit));
Node* branch_is_access_check_needed = graph()->NewNode( Node* branch_is_access_check_needed = graph()->NewNode(
common()->Branch(BranchHint::kFalse), is_access_check_needed, control); common()->Branch(BranchHint::kFalse), is_access_check_needed, control);
...@@ -1325,8 +1308,9 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) { ...@@ -1325,8 +1308,9 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) {
Node* map_instance_type = effect = graph()->NewNode( Node* map_instance_type = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()), simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
loop_object_map, loop_effect, control); loop_object_map, loop_effect, control);
Node* is_proxy = graph()->NewNode(machine()->Word32Equal(), map_instance_type, Node* is_proxy =
jsgraph()->Uint32Constant(JS_PROXY_TYPE)); graph()->NewNode(simplified()->NumberEqual(), map_instance_type,
jsgraph()->Constant(JS_PROXY_TYPE));
Node* branch_is_proxy = Node* branch_is_proxy =
graph()->NewNode(common()->Branch(BranchHint::kFalse), is_proxy, control); graph()->NewNode(common()->Branch(BranchHint::kFalse), is_proxy, control);
Node* if_is_proxy = graph()->NewNode(common()->IfTrue(), branch_is_proxy); Node* if_is_proxy = graph()->NewNode(common()->IfTrue(), branch_is_proxy);
...@@ -2043,13 +2027,6 @@ Reduction JSTypedLowering::Reduce(Node* node) { ...@@ -2043,13 +2027,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return NoChange(); return NoChange();
} }
Node* JSTypedLowering::Word32Shl(Node* const lhs, int32_t const rhs) {
if (rhs == 0) return lhs;
return graph()->NewNode(machine()->Word32Shl(), lhs,
jsgraph()->Int32Constant(rhs));
}
Node* JSTypedLowering::EmptyFrameState() { Node* JSTypedLowering::EmptyFrameState() {
return graph()->NewNode( return graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
...@@ -2077,17 +2054,15 @@ CommonOperatorBuilder* JSTypedLowering::common() const { ...@@ -2077,17 +2054,15 @@ CommonOperatorBuilder* JSTypedLowering::common() const {
return jsgraph()->common(); return jsgraph()->common();
} }
MachineOperatorBuilder* JSTypedLowering::machine() const {
return jsgraph()->machine();
}
SimplifiedOperatorBuilder* JSTypedLowering::simplified() const { SimplifiedOperatorBuilder* JSTypedLowering::simplified() const {
return jsgraph()->simplified(); return jsgraph()->simplified();
} }
MachineOperatorBuilder* JSTypedLowering::machine() const {
return jsgraph()->machine();
}
CompilationDependencies* JSTypedLowering::dependencies() const { CompilationDependencies* JSTypedLowering::dependencies() const {
return dependencies_; return dependencies_;
} }
......
...@@ -24,7 +24,6 @@ namespace compiler { ...@@ -24,7 +24,6 @@ namespace compiler {
class CommonOperatorBuilder; class CommonOperatorBuilder;
class JSGraph; class JSGraph;
class JSOperatorBuilder; class JSOperatorBuilder;
class MachineOperatorBuilder;
class SimplifiedOperatorBuilder; class SimplifiedOperatorBuilder;
...@@ -88,8 +87,6 @@ class JSTypedLowering final : public AdvancedReducer { ...@@ -88,8 +87,6 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceUI32Shift(Node* node, Signedness left_signedness, Reduction ReduceUI32Shift(Node* node, Signedness left_signedness,
const Operator* shift_op); const Operator* shift_op);
Node* Word32Shl(Node* const lhs, int32_t const rhs);
Node* EmptyFrameState(); Node* EmptyFrameState();
Factory* factory() const; Factory* factory() const;
......
// Copyright 2016 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
function foo() {
"use asm";
var o = new Int32Array(64 * 1024);
return () => { o[i1 >> 2] | 0; }
}
assertThrows(foo());
...@@ -29,10 +29,9 @@ class JSIntrinsicLoweringTest : public GraphTest { ...@@ -29,10 +29,9 @@ class JSIntrinsicLoweringTest : public GraphTest {
~JSIntrinsicLoweringTest() override {} ~JSIntrinsicLoweringTest() override {}
protected: protected:
Reduction Reduce(Node* node, MachineOperatorBuilder::Flags flags = Reduction Reduce(Node* node) {
MachineOperatorBuilder::kNoFlags) { MachineOperatorBuilder machine(zone(),
MachineOperatorBuilder machine(zone(), MachineType::PointerRepresentation(), MachineType::PointerRepresentation());
flags);
SimplifiedOperatorBuilder simplified(zone()); SimplifiedOperatorBuilder simplified(zone());
JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified, JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified,
&machine); &machine);
...@@ -87,11 +86,11 @@ TEST_F(JSIntrinsicLoweringTest, InlineIsArray) { ...@@ -87,11 +86,11 @@ TEST_F(JSIntrinsicLoweringTest, InlineIsArray) {
phi, phi,
IsPhi( IsPhi(
MachineRepresentation::kTagged, IsFalseConstant(), MachineRepresentation::kTagged, IsFalseConstant(),
IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), IsNumberEqual(IsLoadField(AccessBuilder::ForMapInstanceType(),
IsLoadField(AccessBuilder::ForMap(), input, IsLoadField(AccessBuilder::ForMap(), input,
effect, CaptureEq(&if_false)), effect, CaptureEq(&if_false)),
effect, _), effect, _),
IsInt32Constant(JS_ARRAY_TYPE)), IsNumberConstant(JS_ARRAY_TYPE)),
IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), IsMerge(IsIfTrue(AllOf(CaptureEq(&branch),
IsBranch(IsObjectIsSmi(input), control))), IsBranch(IsObjectIsSmi(input), control))),
AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch))))));
...@@ -118,11 +117,11 @@ TEST_F(JSIntrinsicLoweringTest, InlineIsTypedArray) { ...@@ -118,11 +117,11 @@ TEST_F(JSIntrinsicLoweringTest, InlineIsTypedArray) {
phi, phi,
IsPhi( IsPhi(
MachineRepresentation::kTagged, IsFalseConstant(), MachineRepresentation::kTagged, IsFalseConstant(),
IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), IsNumberEqual(IsLoadField(AccessBuilder::ForMapInstanceType(),
IsLoadField(AccessBuilder::ForMap(), input, IsLoadField(AccessBuilder::ForMap(), input,
effect, CaptureEq(&if_false)), effect, CaptureEq(&if_false)),
effect, _), effect, _),
IsInt32Constant(JS_TYPED_ARRAY_TYPE)), IsNumberConstant(JS_TYPED_ARRAY_TYPE)),
IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), IsMerge(IsIfTrue(AllOf(CaptureEq(&branch),
IsBranch(IsObjectIsSmi(input), control))), IsBranch(IsObjectIsSmi(input), control))),
AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch))))));
...@@ -149,11 +148,11 @@ TEST_F(JSIntrinsicLoweringTest, InlineIsRegExp) { ...@@ -149,11 +148,11 @@ TEST_F(JSIntrinsicLoweringTest, InlineIsRegExp) {
phi, phi,
IsPhi( IsPhi(
MachineRepresentation::kTagged, IsFalseConstant(), MachineRepresentation::kTagged, IsFalseConstant(),
IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), IsNumberEqual(IsLoadField(AccessBuilder::ForMapInstanceType(),
IsLoadField(AccessBuilder::ForMap(), input, IsLoadField(AccessBuilder::ForMap(), input,
effect, CaptureEq(&if_false)), effect, CaptureEq(&if_false)),
effect, _), effect, _),
IsInt32Constant(JS_REGEXP_TYPE)), IsNumberConstant(JS_REGEXP_TYPE)),
IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), IsMerge(IsIfTrue(AllOf(CaptureEq(&branch),
IsBranch(IsObjectIsSmi(input), control))), IsBranch(IsObjectIsSmi(input), control))),
AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch))))));
......
...@@ -619,7 +619,8 @@ TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) { ...@@ -619,7 +619,8 @@ TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) {
Matcher<Node*> offset_matcher = Matcher<Node*> offset_matcher =
element_size == 1 element_size == 1
? key ? key
: IsWord32Shl(key, IsInt32Constant(WhichPowerOf2(element_size))); : IsNumberShiftLeft(key,
IsNumberConstant(WhichPowerOf2(element_size)));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT( EXPECT_THAT(
...@@ -700,7 +701,8 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) { ...@@ -700,7 +701,8 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) {
Matcher<Node*> offset_matcher = Matcher<Node*> offset_matcher =
element_size == 1 element_size == 1
? key ? key
: IsWord32Shl(key, IsInt32Constant(WhichPowerOf2(element_size))); : IsNumberShiftLeft(
key, IsNumberConstant(WhichPowerOf2(element_size)));
ASSERT_TRUE(r.Changed()); ASSERT_TRUE(r.Changed());
EXPECT_THAT( EXPECT_THAT(
...@@ -747,7 +749,8 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithConversion) { ...@@ -747,7 +749,8 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithConversion) {
Matcher<Node*> offset_matcher = Matcher<Node*> offset_matcher =
element_size == 1 element_size == 1
? key ? key
: IsWord32Shl(key, IsInt32Constant(WhichPowerOf2(element_size))); : IsNumberShiftLeft(
key, IsNumberConstant(WhichPowerOf2(element_size)));
Matcher<Node*> value_matcher = Matcher<Node*> value_matcher =
IsToNumber(value, context, checkpoint, control); IsToNumber(value, context, checkpoint, control);
......
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