[es2015] Fix ToPrimitive conversions in relational comparisons.

The order in which ToNumber(left) and ToPrimitive(right,hint Number)
is called when performing an abstract relational comparison is
observable, and we need to make sure to trigger the conversions in
the correct order.

Bug: chromium:687063
Change-Id: Idc9edb99643c4cf1774b89dcdc319ed5dc7cdc8a
Reviewed-on: https://chromium-review.googlesource.com/1236557
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56125}
This commit is contained in:
Benedikt Meurer 2018-09-20 22:00:30 +02:00 committed by Commit Bot
parent 5e8581a77c
commit 08aec7d721
2 changed files with 34 additions and 2 deletions

View File

@ -10713,15 +10713,16 @@ Node* CodeStubAssembler::RelationalComparison(Operation op, Node* left,
}
// If {left} is a receiver, call ToPrimitive(left, hint Number).
// Otherwise call ToNumeric(left) and then ToNumeric(right).
// Otherwise call ToNumeric(right) and then ToNumeric(left), the
// order here is important as it's observable by user code.
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
Label if_left_receiver(this, Label::kDeferred);
GotoIf(IsJSReceiverInstanceType(left_instance_type),
&if_left_receiver);
var_right.Bind(CallBuiltin(Builtins::kToNumeric, context, right));
var_left.Bind(
CallBuiltin(Builtins::kNonNumberToNumeric, context, left));
var_right.Bind(CallBuiltin(Builtins::kToNumeric, context, right));
Goto(&loop);
BIND(&if_left_receiver);

View File

@ -0,0 +1,31 @@
// 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
// Collect the actual properties looked up on the Proxy.
const actual = [];
// Perform a relational comparison with a Proxy on the right hand
// side and a Symbol which cannot be turned into a Number on the
// left hand side.
function foo() {
actual.length = 0;
const lhs = Symbol();
const rhs = new Proxy({}, {
get: function(target, property, receiver) {
actual.push(property);
return undefined;
}
});
return lhs < rhs;
}
assertThrows(foo, TypeError);
assertEquals([Symbol.toPrimitive, 'valueOf', 'toString'], actual);
assertThrows(foo, TypeError);
assertEquals([Symbol.toPrimitive, 'valueOf', 'toString'], actual);
%OptimizeFunctionOnNextCall(foo);
assertThrows(foo, TypeError);
assertEquals([Symbol.toPrimitive, 'valueOf', 'toString'], actual);