[strong] Disallow implicit conversions for comparison

Implements the strong mode proposal's restrictions on implicit conversions
for the binary >, >=, <, and <= operators.

BUG=v8:3956
LOG=N

Review URL: https://codereview.chromium.org/1130283002

Cr-Commit-Position: refs/heads/master@{#28370}
This commit is contained in:
conradw 2015-05-12 08:23:53 -07:00 committed by Commit bot
parent 188297160d
commit 03ef40b46c
45 changed files with 657 additions and 195 deletions

View File

@ -93,9 +93,8 @@ void InternalArrayNArgumentsConstructorStub::InitializeDescriptor(
#define __ ACCESS_MASM(masm)
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Label* slow,
Condition cond);
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cond, bool strong);
static void EmitSmiNonsmiComparison(MacroAssembler* masm,
Register lhs,
Register rhs,
@ -238,9 +237,8 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
// Handle the case where the lhs and rhs are the same object.
// Equality is almost reflexive (everything but NaN), so this is a test
// for "identity and not NaN".
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Label* slow,
Condition cond) {
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cond, bool strong) {
Label not_identical;
Label heap_number, return_equal;
__ cmp(r0, r1);
@ -251,10 +249,20 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
// They are both equal and they are not both Smis so both of them are not
// Smis. If it's not a heap number, then return equal.
if (cond == lt || cond == gt) {
// Call runtime on identical JSObjects.
__ CompareObjectType(r0, r4, r4, FIRST_SPEC_OBJECT_TYPE);
__ b(ge, slow);
// Call runtime on identical symbols since we need to throw a TypeError.
__ cmp(r4, Operand(SYMBOL_TYPE));
__ b(eq, slow);
if (strong) {
// Call the runtime on anything that is converted in the semantics, since
// we need to throw a TypeError. Smis have already been ruled out.
__ cmp(r4, Operand(HEAP_NUMBER_TYPE));
__ b(eq, &return_equal);
__ tst(r4, Operand(kIsNotStringMask));
__ b(ne, slow);
}
} else {
__ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
__ b(eq, &heap_number);
@ -262,8 +270,16 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
if (cond != eq) {
__ cmp(r4, Operand(FIRST_SPEC_OBJECT_TYPE));
__ b(ge, slow);
// Call runtime on identical symbols since we need to throw a TypeError.
__ cmp(r4, Operand(SYMBOL_TYPE));
__ b(eq, slow);
if (strong) {
// Call the runtime on anything that is converted in the semantics,
// since we need to throw a TypeError. Smis and heap numbers have
// already been ruled out.
__ tst(r4, Operand(kIsNotStringMask));
__ b(ne, slow);
}
// Normally here we fall through to return_equal, but undefined is
// special: (undefined == undefined) == true, but
// (undefined <= undefined) == false! See ECMAScript 11.8.5.
@ -561,7 +577,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Handle the case where the objects are identical. Either returns the answer
// or goes to slow. Only falls through if the objects were not identical.
EmitIdenticalObjectComparison(masm, &slow, cc);
EmitIdenticalObjectComparison(masm, &slow, cc, strong());
// If either is a Smi (we know that not both are), then they can only
// be strictly equal if the other is a HeapNumber.
@ -663,7 +679,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc == eq) {
native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
native = Builtins::COMPARE;
native = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
int ncr; // NaN compare result
if (cc == lt || cc == le) {
ncr = GREATER;
@ -3567,7 +3583,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ bind(&unordered);
__ bind(&generic_stub);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -1059,8 +1059,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -5238,7 +5238,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);

View File

@ -1197,6 +1197,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -2612,7 +2612,7 @@ void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// This instruction also signals no smi code inlined.
__ cmp(r0, Operand::Zero());
@ -2929,7 +2929,8 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// This instruction also signals no smi code inlined.
__ cmp(r0, Operand::Zero());

View File

@ -203,13 +203,11 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
// See call site for description.
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Register left,
Register right,
Register scratch,
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Register left,
Register right, Register scratch,
FPRegister double_scratch,
Label* slow,
Condition cond) {
Label* slow, Condition cond,
bool strong) {
DCHECK(!AreAliased(left, right, scratch));
Label not_identical, return_equal, heap_number;
Register result = x0;
@ -223,10 +221,20 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
// Smis. If it's not a heap number, then return equal.
Register right_type = scratch;
if ((cond == lt) || (cond == gt)) {
// Call runtime on identical JSObjects. Otherwise return equal.
__ JumpIfObjectType(right, right_type, right_type, FIRST_SPEC_OBJECT_TYPE,
slow, ge);
// Call runtime on identical symbols since we need to throw a TypeError.
__ Cmp(right_type, SYMBOL_TYPE);
__ B(eq, slow);
if (strong) {
// Call the runtime on anything that is converted in the semantics, since
// we need to throw a TypeError. Smis have already been ruled out.
__ Cmp(right_type, Operand(HEAP_NUMBER_TYPE));
__ B(eq, &return_equal);
__ Tst(right_type, Operand(kIsNotStringMask));
__ B(ne, slow);
}
} else if (cond == eq) {
__ JumpIfHeapNumber(right, &heap_number);
} else {
@ -235,8 +243,16 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
// Comparing JS objects with <=, >= is complicated.
__ Cmp(right_type, FIRST_SPEC_OBJECT_TYPE);
__ B(ge, slow);
// Call runtime on identical symbols since we need to throw a TypeError.
__ Cmp(right_type, SYMBOL_TYPE);
__ B(eq, slow);
if (strong) {
// Call the runtime on anything that is converted in the semantics,
// since we need to throw a TypeError. Smis and heap numbers have
// already been ruled out.
__ Tst(right_type, Operand(kIsNotStringMask));
__ B(ne, slow);
}
// Normally here we fall through to return_equal, but undefined is
// special: (undefined == undefined) == true, but
// (undefined <= undefined) == false! See ECMAScript 11.8.5.
@ -513,7 +529,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Handle the case where the objects are identical. Either returns the answer
// or goes to slow. Only falls through if the objects were not identical.
EmitIdenticalObjectComparison(masm, lhs, rhs, x10, d0, &slow, cond);
EmitIdenticalObjectComparison(masm, lhs, rhs, x10, d0, &slow, cond, strong());
// If either is a smi (we know that at least one is not a smi), then they can
// only be strictly equal if the other is a HeapNumber.
@ -632,7 +648,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cond == eq) {
native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
native = Builtins::COMPARE;
native = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
int ncr; // NaN compare result
if ((cond == lt) || (cond == le)) {
ncr = GREATER;
@ -3485,7 +3501,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ Ret();
__ Bind(&unordered);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -1058,8 +1058,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -4925,7 +4925,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);

View File

@ -1182,6 +1182,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -2555,7 +2555,8 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
DCHECK(ToRegister(instr->left()).Is(x1));
DCHECK(ToRegister(instr->right()).Is(x0));
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// Signal that we don't inline smi code before this stub.
InlineSmiCheckInfo::EmitNotInlined(masm());
@ -5634,7 +5635,7 @@ void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
InlineSmiCheckInfo::EmitNotInlined(masm());

View File

@ -171,6 +171,7 @@ enum BuiltinExtraArguments {
V(EQUALS, 1) \
V(STRICT_EQUALS, 1) \
V(COMPARE, 2) \
V(COMPARE_STRONG, 2) \
V(ADD, 1) \
V(ADD_STRONG, 1) \
V(SUB, 1) \

View File

@ -105,8 +105,10 @@ Callable CodeFactory::KeyedStoreICInOptimizedCode(
// static
Callable CodeFactory::CompareIC(Isolate* isolate, Token::Value op) {
Handle<Code> code = CompareIC::GetUninitialized(isolate, op);
Callable CodeFactory::CompareIC(Isolate* isolate, Token::Value op,
LanguageMode language_mode) {
Handle<Code> code =
CompareIC::GetUninitialized(isolate, op, is_strong(language_mode));
return Callable(code, CompareDescriptor(isolate));
}

View File

@ -50,7 +50,8 @@ class CodeFactory final {
Isolate* isolate, LanguageMode mode,
InlineCacheState initialization_state);
static Callable CompareIC(Isolate* isolate, Token::Value op);
static Callable CompareIC(Isolate* isolate, Token::Value op,
LanguageMode language_mode);
static Callable BinaryOpIC(Isolate* isolate, Token::Value op,
LanguageMode language_mode);

View File

@ -1523,12 +1523,14 @@ class StringAddStub final : public HydrogenCodeStub {
class CompareICStub : public PlatformCodeStub {
public:
CompareICStub(Isolate* isolate, Token::Value op, CompareICState::State left,
CompareICState::State right, CompareICState::State state)
CompareICStub(Isolate* isolate, Token::Value op, bool strong,
CompareICState::State left, CompareICState::State right,
CompareICState::State state)
: PlatformCodeStub(isolate) {
DCHECK(Token::IsCompareOp(op));
minor_key_ = OpBits::encode(op - Token::EQ) | LeftStateBits::encode(left) |
RightStateBits::encode(right) | StateBits::encode(state);
minor_key_ = OpBits::encode(op - Token::EQ) | StrongBits::encode(strong) |
LeftStateBits::encode(left) | RightStateBits::encode(right) |
StateBits::encode(state);
}
void set_known_map(Handle<Map> map) { known_map_ = map; }
@ -1539,6 +1541,8 @@ class CompareICStub : public PlatformCodeStub {
return static_cast<Token::Value>(Token::EQ + OpBits::decode(minor_key_));
}
bool strong() const { return StrongBits::decode(minor_key_); }
CompareICState::State left() const {
return LeftStateBits::decode(minor_key_);
}
@ -1570,9 +1574,10 @@ class CompareICStub : public PlatformCodeStub {
}
class OpBits : public BitField<int, 0, 3> {};
class LeftStateBits : public BitField<CompareICState::State, 3, 4> {};
class RightStateBits : public BitField<CompareICState::State, 7, 4> {};
class StateBits : public BitField<CompareICState::State, 11, 4> {};
class StrongBits : public BitField<bool, 3, 1> {};
class LeftStateBits : public BitField<CompareICState::State, 4, 4> {};
class RightStateBits : public BitField<CompareICState::State, 8, 4> {};
class StateBits : public BitField<CompareICState::State, 12, 4> {};
Handle<Map> known_map_;

View File

@ -116,7 +116,8 @@ static CallDescriptor::Flags FlagsForNode(Node* node) {
void JSGenericLowering::ReplaceWithCompareIC(Node* node, Token::Value token) {
Callable callable = CodeFactory::CompareIC(isolate(), token);
Callable callable =
CodeFactory::CompareIC(isolate(), token, OpParameter<LanguageMode>(node));
CallDescriptor* desc_compare = Linkage::GetStubCallDescriptor(
isolate(), zone(), callable.descriptor(), 0,
CallDescriptor::kPatchableCallSiteWithNop | FlagsForNode(node),

View File

@ -470,6 +470,9 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
}
return r.ChangeToPureOperator(stringOp);
}
if (r.IsStrong() && !r.BothInputsAre(Type::Number())) {
return NoChange();
}
#if 0
// TODO(turbofan): General ToNumber disabled for now because:
// a) The inserted ToNumber operation screws up observability of valueOf.

View File

@ -11201,7 +11201,8 @@ HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
return result;
} else {
if (combined_rep.IsTagged() || combined_rep.IsNone()) {
HCompareGeneric* result = Add<HCompareGeneric>(left, right, op);
HCompareGeneric* result =
Add<HCompareGeneric>(left, right, op, function_language_mode());
result->set_observed_input_representation(1, left_rep);
result->set_observed_input_representation(2, right_rep);
if (result->HasObservableSideEffects()) {

View File

@ -1666,7 +1666,7 @@ static void BranchIfNotInternalizedString(MacroAssembler* masm,
void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
Label check_unequal_objects;
Label runtime_call, check_unequal_objects;
Condition cc = GetCondition();
Label miss;
@ -1700,12 +1700,17 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
Label check_for_nan;
__ cmp(edx, isolate()->factory()->undefined_value());
__ j(not_equal, &check_for_nan, Label::kNear);
__ Move(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
__ ret(0);
__ bind(&check_for_nan);
if (strong()) {
// In strong mode, this comparison must throw, so call the runtime.
__ j(equal, &runtime_call, Label::kFar);
} else {
Label check_for_nan;
__ j(not_equal, &check_for_nan, Label::kNear);
__ Move(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
__ ret(0);
__ bind(&check_for_nan);
}
}
// Test for NaN. Compare heap numbers in a general way,
@ -1714,12 +1719,20 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
Immediate(isolate()->factory()->heap_number_map()));
__ j(equal, &generic_heap_number_comparison, Label::kNear);
if (cc != equal) {
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
// Call runtime on identical JSObjects. Otherwise return equal.
__ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
__ j(above_equal, &not_identical);
__ cmpb(ecx, static_cast<uint8_t>(FIRST_SPEC_OBJECT_TYPE));
__ j(above_equal, &runtime_call, Label::kFar);
// Call runtime on identical symbols since we need to throw a TypeError.
__ CmpObjectType(eax, SYMBOL_TYPE, ecx);
__ j(equal, &not_identical);
__ cmpb(ecx, static_cast<uint8_t>(SYMBOL_TYPE));
__ j(equal, &runtime_call, Label::kFar);
if (strong()) {
// We have already tested for smis and heap numbers, so if both
// arguments are not strings we must proceed to the slow case.
__ test(ecx, Immediate(kIsNotStringMask));
__ j(not_zero, &runtime_call, Label::kFar);
}
}
__ Move(eax, Immediate(Smi::FromInt(EQUAL)));
__ ret(0);
@ -1864,7 +1877,6 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Non-strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
Label not_both_objects;
Label return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
@ -1873,11 +1885,11 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
STATIC_ASSERT(kSmiTagMask == 1);
__ lea(ecx, Operand(eax, edx, times_1, 0));
__ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &not_both_objects, Label::kNear);
__ j(not_zero, &runtime_call, Label::kNear);
__ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
__ j(below, &not_both_objects, Label::kNear);
__ j(below, &runtime_call, Label::kNear);
__ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
__ j(below, &not_both_objects, Label::kNear);
__ j(below, &runtime_call, Label::kNear);
// We do not bail out after this point. Both are JSObjects, and
// they are equal if and only if both are undetectable.
// The and of the undetectable flags is 1 if and only if they are equal.
@ -1894,8 +1906,8 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Return non-equal by returning the non-zero object pointer in eax,
// or return equal if we fell through to here.
__ ret(0); // rax, rdx were pushed
__ bind(&not_both_objects);
}
__ bind(&runtime_call);
// Push arguments below the return address.
__ pop(ecx);
@ -1907,7 +1919,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc == equal) {
builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
builtin = Builtins::COMPARE;
builtin = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
__ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
}
@ -3638,7 +3650,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ bind(&unordered);
__ bind(&generic_stub);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ jmp(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -996,8 +996,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -5164,7 +5164,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();

View File

@ -2503,7 +2503,7 @@ static Condition ComputeCompareCondition(Token::Value op) {
void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = ComputeCompareCondition(op);
@ -2774,7 +2774,8 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
void LCodeGen::DoCmpT(LCmpT* instr) {
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = ComputeCompareCondition(op);

View File

@ -1191,6 +1191,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
LOperand* context() { return inputs_[0]; }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -599,7 +599,8 @@ void CompareIC::Clear(Isolate* isolate, Address address, Code* target,
CompareICStub stub(target->stub_key(), isolate);
// Only clear CompareICs that can retain objects.
if (stub.state() != CompareICState::KNOWN_OBJECT) return;
SetTargetAtAddress(address, GetRawUninitialized(isolate, stub.op()),
SetTargetAtAddress(address,
GetRawUninitialized(isolate, stub.op(), stub.strong()),
constant_pool);
PatchInlinedSmiCode(address, DISABLE_INLINED_SMI_CHECK);
}
@ -2678,8 +2679,9 @@ RUNTIME_FUNCTION(BinaryOpIC_MissWithAllocationSite) {
}
Code* CompareIC::GetRawUninitialized(Isolate* isolate, Token::Value op) {
CompareICStub stub(isolate, op, CompareICState::UNINITIALIZED,
Code* CompareIC::GetRawUninitialized(Isolate* isolate, Token::Value op,
bool strong) {
CompareICStub stub(isolate, op, strong, CompareICState::UNINITIALIZED,
CompareICState::UNINITIALIZED,
CompareICState::UNINITIALIZED);
Code* code = NULL;
@ -2688,8 +2690,9 @@ Code* CompareIC::GetRawUninitialized(Isolate* isolate, Token::Value op) {
}
Handle<Code> CompareIC::GetUninitialized(Isolate* isolate, Token::Value op) {
CompareICStub stub(isolate, op, CompareICState::UNINITIALIZED,
Handle<Code> CompareIC::GetUninitialized(Isolate* isolate, Token::Value op,
bool strong) {
CompareICStub stub(isolate, op, strong, CompareICState::UNINITIALIZED,
CompareICState::UNINITIALIZED,
CompareICState::UNINITIALIZED);
return stub.GetCode();
@ -2706,7 +2709,8 @@ Code* CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) {
CompareICState::State state = CompareICState::TargetState(
old_stub.state(), old_stub.left(), old_stub.right(), op_,
HasInlinedSmiCode(address()), x, y);
CompareICStub stub(isolate(), op_, new_left, new_right, state);
CompareICStub stub(isolate(), op_, old_stub.strong(), new_left, new_right,
state);
if (state == CompareICState::KNOWN_OBJECT) {
stub.set_known_map(
Handle<Map>(Handle<JSObject>::cast(x)->map(), isolate()));

View File

@ -704,7 +704,8 @@ class CompareIC : public IC {
static Condition ComputeCondition(Token::Value op);
// Factory method for getting an uninitialized compare stub.
static Handle<Code> GetUninitialized(Isolate* isolate, Token::Value op);
static Handle<Code> GetUninitialized(Isolate* isolate, Token::Value op,
bool strong);
private:
static bool HasInlinedSmiCode(Address address);
@ -712,7 +713,8 @@ class CompareIC : public IC {
bool strict() const { return op_ == Token::EQ_STRICT; }
Condition GetCondition() const { return ComputeCondition(op_); }
static Code* GetRawUninitialized(Isolate* isolate, Token::Value op);
static Code* GetRawUninitialized(Isolate* isolate, Token::Value op,
bool strong);
static void Clear(Isolate* isolate, Address address, Code* target,
ConstantPoolArray* constant_pool);

View File

@ -93,9 +93,8 @@ void InternalArrayNArgumentsConstructorStub::InitializeDescriptor(
#define __ ACCESS_MASM(masm)
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Label* slow,
Condition cc);
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cc, bool strong);
static void EmitSmiNonsmiComparison(MacroAssembler* masm,
Register lhs,
Register rhs,
@ -276,9 +275,8 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
// Handle the case where the lhs and rhs are the same object.
// Equality is almost reflexive (everything but NaN), so this is a test
// for "identity and not NaN".
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Label* slow,
Condition cc) {
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cc, bool strong) {
Label not_identical;
Label heap_number, return_equal;
Register exp_mask_reg = t5;
@ -293,14 +291,31 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
// Smis. If it's not a heap number, then return equal.
__ GetObjectType(a0, t4, t4);
if (cc == less || cc == greater) {
// Call runtime on identical JSObjects.
__ Branch(slow, greater, t4, Operand(FIRST_SPEC_OBJECT_TYPE));
// Call runtime on identical symbols since we need to throw a TypeError.
__ Branch(slow, eq, t4, Operand(SYMBOL_TYPE));
if (strong) {
// Call the runtime on anything that is converted in the semantics, since
// we need to throw a TypeError. Smis have already been ruled out.
__ Branch(&return_equal, eq, t4, Operand(HEAP_NUMBER_TYPE));
__ And(t4, t4, Operand(kIsNotStringMask));
__ Branch(slow, ne, t4, Operand(zero_reg));
}
} else {
__ Branch(&heap_number, eq, t4, Operand(HEAP_NUMBER_TYPE));
// Comparing JS objects with <=, >= is complicated.
if (cc != eq) {
__ Branch(slow, greater, t4, Operand(FIRST_SPEC_OBJECT_TYPE));
// Call runtime on identical symbols since we need to throw a TypeError.
__ Branch(slow, eq, t4, Operand(SYMBOL_TYPE));
if (strong) {
// Call the runtime on anything that is converted in the semantics,
// since we need to throw a TypeError. Smis and heap numbers have
// already been ruled out.
__ And(t4, t4, Operand(kIsNotStringMask));
__ Branch(slow, ne, t4, Operand(zero_reg));
}
// Normally here we fall through to return_equal, but undefined is
// special: (undefined == undefined) == true, but
// (undefined <= undefined) == false! See ECMAScript 11.8.5.
@ -585,7 +600,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Handle the case where the objects are identical. Either returns the answer
// or goes to slow. Only falls through if the objects were not identical.
EmitIdenticalObjectComparison(masm, &slow, cc);
EmitIdenticalObjectComparison(masm, &slow, cc, strong());
// If either is a Smi (we know that not both are), then they can only
// be strictly equal if the other is a HeapNumber.
@ -713,7 +728,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc == eq) {
native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
native = Builtins::COMPARE;
native = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
int ncr; // NaN compare result.
if (cc == lt || cc == le) {
ncr = GREATER;
@ -3743,7 +3758,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ bind(&unordered);
__ bind(&generic_stub);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -1053,8 +1053,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -5238,7 +5238,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
}
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);

View File

@ -2543,7 +2543,7 @@ void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = ComputeCompareCondition(op);
@ -2845,7 +2845,8 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// On MIPS there is no need for a "no inlined smi code" marker (nop).

View File

@ -1175,6 +1175,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -92,9 +92,8 @@ void InternalArrayNArgumentsConstructorStub::InitializeDescriptor(
#define __ ACCESS_MASM(masm)
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Label* slow,
Condition cc);
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cc, bool strong);
static void EmitSmiNonsmiComparison(MacroAssembler* masm,
Register lhs,
Register rhs,
@ -272,9 +271,8 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
// Handle the case where the lhs and rhs are the same object.
// Equality is almost reflexive (everything but NaN), so this is a test
// for "identity and not NaN".
static void EmitIdenticalObjectComparison(MacroAssembler* masm,
Label* slow,
Condition cc) {
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cc, bool strong) {
Label not_identical;
Label heap_number, return_equal;
Register exp_mask_reg = t1;
@ -289,14 +287,31 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm,
// Smis. If it's not a heap number, then return equal.
__ GetObjectType(a0, t0, t0);
if (cc == less || cc == greater) {
// Call runtime on identical JSObjects.
__ Branch(slow, greater, t0, Operand(FIRST_SPEC_OBJECT_TYPE));
// Call runtime on identical symbols since we need to throw a TypeError.
__ Branch(slow, eq, t0, Operand(SYMBOL_TYPE));
if (strong) {
// Call the runtime on anything that is converted in the semantics, since
// we need to throw a TypeError. Smis have already been ruled out.
__ Branch(&return_equal, eq, t0, Operand(HEAP_NUMBER_TYPE));
__ And(t0, t0, Operand(kIsNotStringMask));
__ Branch(slow, ne, t0, Operand(zero_reg));
}
} else {
__ Branch(&heap_number, eq, t0, Operand(HEAP_NUMBER_TYPE));
// Comparing JS objects with <=, >= is complicated.
if (cc != eq) {
__ Branch(slow, greater, t0, Operand(FIRST_SPEC_OBJECT_TYPE));
// Call runtime on identical symbols since we need to throw a TypeError.
__ Branch(slow, eq, t0, Operand(SYMBOL_TYPE));
if (strong) {
// Call the runtime on anything that is converted in the semantics,
// since we need to throw a TypeError. Smis and heap numbers have
// already been ruled out.
__ And(t0, t0, Operand(kIsNotStringMask));
__ Branch(slow, ne, t0, Operand(zero_reg));
}
// Normally here we fall through to return_equal, but undefined is
// special: (undefined == undefined) == true, but
// (undefined <= undefined) == false! See ECMAScript 11.8.5.
@ -580,7 +595,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Handle the case where the objects are identical. Either returns the answer
// or goes to slow. Only falls through if the objects were not identical.
EmitIdenticalObjectComparison(masm, &slow, cc);
EmitIdenticalObjectComparison(masm, &slow, cc, strong());
// If either is a Smi (we know that not both are), then they can only
// be strictly equal if the other is a HeapNumber.
@ -708,7 +723,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc == eq) {
native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
native = Builtins::COMPARE;
native = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
int ncr; // NaN compare result.
if (cc == lt || cc == le) {
ncr = GREATER;
@ -3786,7 +3801,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ bind(&unordered);
__ bind(&generic_stub);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -1050,8 +1050,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -5241,7 +5241,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
}
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);

View File

@ -2542,7 +2542,7 @@ void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = ComputeCompareCondition(op);
@ -2844,7 +2844,8 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// On MIPS there is no need for a "no inlined smi code" marker (nop).

View File

@ -1174,6 +1174,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -94,7 +94,7 @@ void InternalArrayNArgumentsConstructorStub::InitializeDescriptor(
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cond);
Condition cond, bool strong);
static void EmitSmiNonsmiComparison(MacroAssembler* masm, Register lhs,
Register rhs, Label* lhs_not_nan,
Label* slow, bool strict);
@ -249,7 +249,7 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
// Equality is almost reflexive (everything but NaN), so this is a test
// for "identity and not NaN".
static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
Condition cond) {
Condition cond, bool strong) {
Label not_identical;
Label heap_number, return_equal;
__ cmp(r3, r4);
@ -260,10 +260,20 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
// They are both equal and they are not both Smis so both of them are not
// Smis. If it's not a heap number, then return equal.
if (cond == lt || cond == gt) {
// Call runtime on identical JSObjects.
__ CompareObjectType(r3, r7, r7, FIRST_SPEC_OBJECT_TYPE);
__ bge(slow);
// Call runtime on identical symbols since we need to throw a TypeError.
__ cmpi(r7, Operand(SYMBOL_TYPE));
__ beq(slow);
if (strong) {
// Call the runtime on anything that is converted in the semantics, since
// we need to throw a TypeError. Smis have already been ruled out.
__ cmpi(r7, Operand(HEAP_NUMBER_TYPE));
__ beq(&return_equal);
__ andi(r7, r7, Operand(kIsNotStringMask));
__ bne(slow, cr0);
}
} else {
__ CompareObjectType(r3, r7, r7, HEAP_NUMBER_TYPE);
__ beq(&heap_number);
@ -271,8 +281,16 @@ static void EmitIdenticalObjectComparison(MacroAssembler* masm, Label* slow,
if (cond != eq) {
__ cmpi(r7, Operand(FIRST_SPEC_OBJECT_TYPE));
__ bge(slow);
// Call runtime on identical symbols since we need to throw a TypeError.
__ cmpi(r7, Operand(SYMBOL_TYPE));
__ beq(slow);
if (strong) {
// Call the runtime on anything that is converted in the semantics,
// since we need to throw a TypeError. Smis and heap numbers have
// already been ruled out.
__ andi(r7, r7, Operand(kIsNotStringMask));
__ bne(slow, cr0);
}
// Normally here we fall through to return_equal, but undefined is
// special: (undefined == undefined) == true, but
// (undefined <= undefined) == false! See ECMAScript 11.8.5.
@ -687,7 +705,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc == eq) {
native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
native = Builtins::COMPARE;
native = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
int ncr; // NaN compare result
if (cc == lt || cc == le) {
ncr = GREATER;
@ -3780,7 +3798,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ bind(&unordered);
__ bind(&generic_stub);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -1017,8 +1017,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -5251,7 +5251,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();
PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);

View File

@ -2671,7 +2671,7 @@ void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// This instruction also signals no smi code inlined
__ cmpi(r3, Operand::Zero());
@ -2978,7 +2978,8 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
DCHECK(ToRegister(instr->context()).is(cp));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
// This instruction also signals no smi code inlined
__ cmpi(r3, Operand::Zero());

View File

@ -1151,6 +1151,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -19,6 +19,7 @@
var EQUALS;
var STRICT_EQUALS;
var COMPARE;
var COMPARE_STRONG;
var ADD;
var ADD_STRONG;
var STRING_ADD_LEFT;
@ -204,6 +205,14 @@ COMPARE = function COMPARE(x, ncr) {
}
}
// Strong mode COMPARE throws if an implicit conversion would be performed
COMPARE_STRONG = function COMPARE_STRONG(x, ncr) {
if (IS_STRING(this) && IS_STRING(x)) return %_StringCompare(this, x);
if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberCompare(this, x, ncr);
throw %MakeTypeError('strong_implicit_cast');
}
/* -----------------------------------

View File

@ -1533,7 +1533,7 @@ static void BranchIfNotInternalizedString(MacroAssembler* masm,
void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
Label check_unequal_objects, done;
Label runtime_call, check_unequal_objects, done;
Condition cc = GetCondition();
Factory* factory = isolate()->factory();
@ -1566,12 +1566,17 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
Label check_for_nan;
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
__ j(not_equal, &check_for_nan, Label::kNear);
__ Set(rax, NegativeComparisonResult(cc));
__ ret(0);
__ bind(&check_for_nan);
if (strong()) {
// In strong mode, this comparison must throw, so call the runtime.
__ j(equal, &runtime_call, Label::kFar);
} else {
Label check_for_nan;
__ j(not_equal, &check_for_nan, Label::kNear);
__ Set(rax, NegativeComparisonResult(cc));
__ ret(0);
__ bind(&check_for_nan);
}
}
// Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
@ -1582,12 +1587,20 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
factory->heap_number_map());
__ j(equal, &heap_number, Label::kNear);
if (cc != equal) {
__ movp(rcx, FieldOperand(rax, HeapObject::kMapOffset));
__ movzxbl(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset));
// Call runtime on identical objects. Otherwise return equal.
__ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
__ j(above_equal, &not_identical, Label::kNear);
__ cmpb(rcx, Immediate(static_cast<uint8_t>(FIRST_SPEC_OBJECT_TYPE)));
__ j(above_equal, &runtime_call, Label::kFar);
// Call runtime on identical symbols since we need to throw a TypeError.
__ CmpObjectType(rax, SYMBOL_TYPE, rcx);
__ j(equal, &not_identical, Label::kNear);
__ cmpb(rcx, Immediate(static_cast<uint8_t>(SYMBOL_TYPE)));
__ j(equal, &runtime_call, Label::kFar);
if (strong()) {
// We have already tested for smis and heap numbers, so if both
// arguments are not strings we must proceed to the slow case.
__ testb(rcx, Immediate(kIsNotStringMask));
__ j(not_zero, &runtime_call, Label::kFar);
}
}
__ Set(rax, EQUAL);
__ ret(0);
@ -1734,7 +1747,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Not strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
Label not_both_objects, return_unequal;
Label return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
// a heap object has the low bit clear.
@ -1742,11 +1755,11 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
STATIC_ASSERT(kSmiTagMask == 1);
__ leap(rcx, Operand(rax, rdx, times_1, 0));
__ testb(rcx, Immediate(kSmiTagMask));
__ j(not_zero, &not_both_objects, Label::kNear);
__ j(not_zero, &runtime_call, Label::kNear);
__ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rbx);
__ j(below, &not_both_objects, Label::kNear);
__ j(below, &runtime_call, Label::kNear);
__ CmpObjectType(rdx, FIRST_SPEC_OBJECT_TYPE, rcx);
__ j(below, &not_both_objects, Label::kNear);
__ j(below, &runtime_call, Label::kNear);
__ testb(FieldOperand(rbx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
__ j(zero, &return_unequal, Label::kNear);
@ -1760,8 +1773,8 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Return non-equal by returning the non-zero object pointer in rax,
// or return equal if we fell through to here.
__ ret(0);
__ bind(&not_both_objects);
}
__ bind(&runtime_call);
// Push arguments below the return address to prepare jump to builtin.
__ PopReturnAddressTo(rcx);
@ -1773,7 +1786,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc == equal) {
builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
builtin = Builtins::COMPARE;
builtin = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
__ Push(Smi::FromInt(NegativeComparisonResult(cc)));
}
@ -3598,7 +3611,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ bind(&unordered);
__ bind(&generic_stub);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ jmp(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -1019,8 +1019,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -5181,7 +5181,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();

View File

@ -2549,7 +2549,7 @@ void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
DCHECK(ToRegister(instr->context()).is(rsi));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = TokenToCondition(op, false);
@ -2833,7 +2833,8 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
DCHECK(ToRegister(instr->context()).is(rsi));
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = TokenToCondition(op, false);

View File

@ -1174,6 +1174,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -1341,7 +1341,7 @@ static void BranchIfNotInternalizedString(MacroAssembler* masm,
void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
Label check_unequal_objects;
Label runtime_call, check_unequal_objects;
Condition cc = GetCondition();
Label miss;
@ -1375,26 +1375,39 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
Label check_for_nan;
__ cmp(edx, isolate()->factory()->undefined_value());
__ j(not_equal, &check_for_nan, Label::kNear);
__ Move(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
__ ret(0);
__ bind(&check_for_nan);
if (strong()) {
// In strong mode, this comparison must throw, so call the runtime.
__ j(equal, &runtime_call, Label::kFar);
} else {
Label check_for_nan;
__ j(not_equal, &check_for_nan, Label::kNear);
__ Move(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
__ ret(0);
__ bind(&check_for_nan);
}
}
// Test for NaN. Compare heap numbers in a general way,
// to hanlde NaNs correctly.
// to handle NaNs correctly.
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(isolate()->factory()->heap_number_map()));
__ j(equal, &generic_heap_number_comparison, Label::kNear);
if (cc != equal) {
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
// Call runtime on identical JSObjects. Otherwise return equal.
__ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
__ j(above_equal, &not_identical);
__ cmpb(ecx, static_cast<uint8_t>(FIRST_SPEC_OBJECT_TYPE));
__ j(above_equal, &runtime_call, Label::kFar);
// Call runtime on identical symbols since we need to throw a TypeError.
__ CmpObjectType(eax, SYMBOL_TYPE, ecx);
__ j(equal, &not_identical);
__ cmpb(ecx, static_cast<uint8_t>(SYMBOL_TYPE));
__ j(equal, &runtime_call, Label::kFar);
if (strong()) {
// We have already tested for smis and heap numbers, so if both
// arguments are not strings we must proceed to the slow case.
__ test(ecx, Immediate(kIsNotStringMask));
__ j(not_zero, &runtime_call, Label::kFar);
}
}
__ Move(eax, Immediate(Smi::FromInt(EQUAL)));
__ ret(0);
@ -1551,7 +1564,6 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Non-strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
Label not_both_objects;
Label return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
@ -1560,11 +1572,11 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
STATIC_ASSERT(kSmiTagMask == 1);
__ lea(ecx, Operand(eax, edx, times_1, 0));
__ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &not_both_objects, Label::kNear);
__ j(not_zero, &runtime_call, Label::kNear);
__ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
__ j(below, &not_both_objects, Label::kNear);
__ j(below, &runtime_call, Label::kNear);
__ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
__ j(below, &not_both_objects, Label::kNear);
__ j(below, &runtime_call, Label::kNear);
// We do not bail out after this point. Both are JSObjects, and
// they are equal if and only if both are undetectable.
// The and of the undetectable flags is 1 if and only if they are equal.
@ -1581,8 +1593,8 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
// Return non-equal by returning the non-zero object pointer in eax,
// or return equal if we fell through to here.
__ ret(0); // rax, rdx were pushed
__ bind(&not_both_objects);
}
__ bind(&runtime_call);
// Push arguments below the return address.
__ pop(ecx);
@ -1594,7 +1606,7 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
if (cc == equal) {
builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
builtin = Builtins::COMPARE;
builtin = strong() ? Builtins::COMPARE_STRONG : Builtins::COMPARE;
__ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
}
@ -3300,7 +3312,7 @@ void CompareICStub::GenerateNumbers(MacroAssembler* masm) {
__ bind(&unordered);
__ bind(&generic_stub);
CompareICStub stub(isolate(), op(), CompareICState::GENERIC,
CompareICStub stub(isolate(), op(), strong(), CompareICState::GENERIC,
CompareICState::GENERIC, CompareICState::GENERIC);
__ jmp(stub.GetCode(), RelocInfo::CODE_TARGET);

View File

@ -988,8 +988,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), Token::EQ_STRICT,
language_mode()).code();
CallIC(ic, clause->CompareId());
patch_site.EmitPatchInfo();
@ -5151,7 +5151,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, language_mode()).code();
CallIC(ic, expr->CompareOperationFeedbackId());
patch_site.EmitPatchInfo();

View File

@ -2779,7 +2779,7 @@ static Condition ComputeCompareCondition(Token::Value op) {
void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op, SLOPPY).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = ComputeCompareCondition(op);
@ -3051,7 +3051,8 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
void LCodeGen::DoCmpT(LCmpT* instr) {
Token::Value op = instr->op();
Handle<Code> ic = CodeFactory::CompareIC(isolate(), op).code();
Handle<Code> ic =
CodeFactory::CompareIC(isolate(), op, instr->language_mode()).code();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
Condition condition = ComputeCompareCondition(op);

View File

@ -1202,6 +1202,8 @@ class LCmpT final : public LTemplateInstruction<1, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
LanguageMode language_mode() { return hydrogen()->language_mode(); }
LOperand* context() { return inputs_[0]; }
Token::Value op() const { return hydrogen()->token(); }
};

View File

@ -688,18 +688,20 @@ TEST_WITH_STRONG(MixedComparison1) {
for (size_t j = 0; j < arraysize(types); j++) {
Node* p1 = R.Parameter(types[j], 1);
{
Node* cmp = R.Binop(R.javascript.LessThan(language_mode), p0, p1);
const Operator* less_than = R.javascript.LessThan(language_mode);
Node* cmp = R.Binop(less_than, p0, p1);
Node* r = R.reduce(cmp);
if (!types[i]->Maybe(Type::String()) ||
!types[j]->Maybe(Type::String())) {
if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) {
R.CheckPureBinop(R.simplified.StringLessThan(), r);
} else {
R.CheckPureBinop(R.simplified.NumberLessThan(), r);
}
if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) {
R.CheckPureBinop(R.simplified.StringLessThan(), r);
} else if ((types[i]->Is(Type::Number()) &&
types[j]->Is(Type::Number())) ||
(!is_strong(language_mode) &&
(!types[i]->Maybe(Type::String()) ||
!types[j]->Maybe(Type::String())))) {
R.CheckPureBinop(R.simplified.NumberLessThan(), r);
} else {
CHECK_EQ(cmp, r); // No reduction of mixed types.
// No reduction of mixed types.
CHECK_EQ(r->op(), less_than);
}
}
}

View File

@ -6,6 +6,8 @@
"use strict";
//******************************************************************************
// Number function declarations
function inline_add_strong(x, y) {
"use strong";
return x + y;
@ -105,6 +107,42 @@ function inline_sar_strong_outer(x, y) {
return inline_sar_strong(x, y);
}
function inline_less_strong(x, y) {
"use strong";
return x < y;
}
function inline_less_strong_outer(x, y) {
return inline_less_strong(x, y);
}
function inline_greater_strong(x, y) {
"use strong";
return x > y;
}
function inline_greater_strong_outer(x, y) {
return inline_greater_strong(x, y);
}
function inline_less_equal_strong(x, y) {
"use strong";
return x <= y;
}
function inline_less_equal_strong_outer(x, y) {
return inline_less_equal_strong(x, y);
}
function inline_greater_equal_strong(x, y) {
"use strong";
return x >= y;
}
function inline_greater_equal_strong_outer(x, y) {
return inline_greater_equal_strong(x, y);
}
function inline_add(x, y) {
return x + y;
}
@ -204,19 +242,170 @@ function inline_sar_outer_strong(x, y) {
return inline_sar(x, y);
}
let strong_inner_funcs = [inline_add_strong_outer, inline_sub_strong_outer,
inline_mul_strong_outer, inline_div_strong_outer,
inline_mod_strong_outer, inline_or_strong_outer,
inline_and_strong_outer, inline_xor_strong_outer,
inline_shl_strong_outer, inline_shr_strong_outer];
function inline_less(x, y) {
return x < y;
}
let strong_outer_funcs = [inline_add_outer_strong, inline_sub_outer_strong,
inline_mul_outer_strong, inline_div_outer_strong,
inline_mod_outer_strong, inline_or_outer_strong,
inline_and_outer_strong, inline_xor_outer_strong,
inline_shl_outer_strong, inline_shr_outer_strong];
function inline_less_outer_strong(x, y) {
"use strong";
return inline_less(x, y);
}
for (let strong_inner_func of strong_inner_funcs) {
function inline_greater(x, y) {
return x > y;
}
function inline_greater_outer_strong(x, y) {
"use strong";
return inline_greater(x, y);
}
function inline_less_equal(x, y) {
return x <= y;
}
function inline_less_equal_outer_strong(x, y) {
"use strong";
return inline_less_equal(x, y);
}
function inline_greater_equal(x, y) {
return x >>> y;
}
function inline_greater_equal_outer_strong(x, y) {
"use strong";
return inline_greater_equal(x, y);
}
//******************************************************************************
// String function declarations
function inline_add_string_strong(x, y) {
"use strong";
return x + y;
}
function inline_add_string_strong_outer(x, y) {
return inline_add_string_strong(x, y);
}
function inline_less_string_strong(x, y) {
"use strong";
return x < y;
}
function inline_less_string_strong_outer(x, y) {
return inline_less_string_strong(x, y);
}
function inline_greater_string_strong(x, y) {
"use strong";
return x > y;
}
function inline_greater_string_strong_outer(x, y) {
return inline_greater_string_strong(x, y);
}
function inline_less_equal_string_strong(x, y) {
"use strong";
return x <= y;
}
function inline_less_equal_string_strong_outer(x, y) {
return inline_less_equal_string_strong(x, y);
}
function inline_greater_equal_string_strong(x, y) {
"use strong";
return x >= y;
}
function inline_greater_equal_string_strong_outer(x, y) {
return inline_greater_equal_string_strong(x, y);
}
function inline_add_string(x, y) {
return x + y;
}
function inline_add_string_outer_strong(x, y) {
"use strong";
return inline_add_string(x, y);
}
function inline_less_string(x, y) {
return x < y;
}
function inline_less_string_outer_strong(x, y) {
"use strong";
return inline_less_string(x, y);
}
function inline_greater_string(x, y) {
return x > y;
}
function inline_greater_string_outer_strong(x, y) {
"use strong";
return inline_greater_string(x, y);
}
function inline_less_equal_string(x, y) {
return x <= y;
}
function inline_less_equal_string_outer_strong(x, y) {
"use strong";
return inline_less_equal_string(x, y);
}
function inline_greater_equal_string(x, y) {
return x >= y;
}
function inline_greater_equal_string_outer_strong(x, y) {
"use strong";
return inline_greater_equal_string(x, y);
}
//******************************************************************************
// Testing
let strong_inner_funcs_num = [inline_add_strong_outer, inline_sub_strong_outer,
inline_mul_strong_outer, inline_div_strong_outer,
inline_mod_strong_outer, inline_or_strong_outer,
inline_and_strong_outer, inline_xor_strong_outer,
inline_shl_strong_outer, inline_shr_strong_outer,
inline_less_strong_outer,
inline_greater_strong_outer,
inline_less_equal_strong_outer,
inline_greater_equal_strong_outer];
let strong_outer_funcs_num = [inline_add_outer_strong, inline_sub_outer_strong,
inline_mul_outer_strong, inline_div_outer_strong,
inline_mod_outer_strong, inline_or_outer_strong,
inline_and_outer_strong, inline_xor_outer_strong,
inline_shl_outer_strong, inline_shr_outer_strong,
inline_less_outer_strong,
inline_greater_outer_strong,
inline_less_equal_outer_strong,
inline_greater_equal_outer_strong];
let strong_inner_funcs_string = [inline_add_string_strong_outer,
inline_less_string_strong_outer,
inline_greater_string_strong_outer,
inline_less_equal_string_strong_outer,
inline_greater_equal_string_strong_outer];
let strong_outer_funcs_string = [inline_add_string_outer_strong,
inline_less_string_outer_strong,
inline_greater_string_outer_strong,
inline_less_equal_string_outer_strong,
inline_greater_equal_string_outer_strong];
for (let strong_inner_func of strong_inner_funcs_num) {
assertThrows(function(){strong_inner_func(1, {})}, TypeError);
for (var i = 0; i < 100; i++) {
strong_inner_func(1, 2);
@ -225,7 +414,7 @@ for (let strong_inner_func of strong_inner_funcs) {
assertThrows(function(){strong_inner_func(1, {})}, TypeError);
}
for (let strong_outer_func of strong_outer_funcs) {
for (let strong_outer_func of strong_outer_funcs_num) {
assertDoesNotThrow(function(){strong_outer_func(1, {})});
for (var i = 0; i < 100; i++) {
strong_outer_func(1, 2);
@ -233,3 +422,21 @@ for (let strong_outer_func of strong_outer_funcs) {
%OptimizeFunctionOnNextCall(strong_outer_func);
assertDoesNotThrow(function(){strong_outer_func(1, {})});
}
for (let strong_inner_func of strong_inner_funcs_string) {
assertThrows(function(){strong_inner_func("foo", {})}, TypeError);
for (var i = 0; i < 100; i++) {
strong_inner_func("foo", "bar");
}
%OptimizeFunctionOnNextCall(strong_inner_func);
assertThrows(function(){strong_inner_func("foo", {})}, TypeError);
}
for (let strong_outer_func of strong_outer_funcs_string) {
assertDoesNotThrow(function(){strong_outer_func("foo", {})});
for (var i = 0; i < 100; i++) {
strong_outer_func("foo", "bar");
}
%OptimizeFunctionOnNextCall(strong_outer_func);
assertDoesNotThrow(function(){strong_outer_func("foo", {})});
}

View File

@ -6,22 +6,26 @@
"use strict";
// TODO(conradw): Implement other strong operators
// Boolean indicates whether an operator can be part of a compound assignment.
let strongNumberBinops = [
"-",
"*",
"/",
"%",
"|",
"&",
"^",
"<<",
">>",
">>>",
["-", true],
["*", true],
["/", true],
["%", true],
["|", true],
["&", true],
["^", true],
["<<", true],
[">>", true],
[">>>", true]
];
let strongStringOrNumberBinops = [
"+"
["+", true],
["<", false],
[">", false],
["<=", false],
[">=", false]
];
let strongBinops = strongNumberBinops.concat(strongStringOrNumberBinops);
@ -33,6 +37,8 @@ let strongUnops = [
];
let nonStringOrNumberValues = [
"null",
"undefined",
"{}",
"false",
"(function(){})",
@ -132,6 +138,46 @@ function sar_strong(x, y) {
return x >>> y;
}
function less_strong(x, y) {
"use strong";
return x < y;
}
function less_num_strong(x, y) {
"use strong";
return x < y;
}
function greater_strong(x, y) {
"use strong";
return x > y;
}
function greater_num_strong(x, y) {
"use strong";
return x > y;
}
function less_equal_strong(x, y) {
"use strong";
return x <= y;
}
function less_equal_num_strong(x, y) {
"use strong";
return x <= y;
}
function greater_equal_strong(x, y) {
"use strong";
return x >= y;
}
function greater_equal_num_strong(x, y) {
"use strong";
return x >= y;
}
function typed_add_strong(x, y) {
"use strong";
return (+x) + (+y);
@ -187,15 +233,40 @@ function typed_sar_strong(x, y) {
return (+x) >>> (+y);
}
function typed_less_strong(x, y) {
"use strong";
return (+x) < (+y);
}
function typed_greater_strong(x, y) {
"use strong";
return (+x) > (+y);
}
function typed_less_equal_strong(x, y) {
"use strong";
return (+x) <= (+y);
}
function typed_greater_equal_strong(x, y) {
"use strong";
return (+x) >= (+y);
}
let strongNumberFuncs = [add_num_strong, sub_strong, mul_strong, div_strong,
mod_strong, or_strong, and_strong, xor_strong,
shl_strong, shr_strong, sar_strong, typed_add_strong,
shl_strong, shr_strong, sar_strong, less_num_strong,
greater_num_strong, less_equal_num_strong,
greater_equal_num_strong, typed_add_strong,
typed_sub_strong, typed_mul_strong, typed_div_strong,
typed_mod_strong, typed_or_strong, typed_and_strong,
typed_xor_strong, typed_shl_strong, typed_shr_strong,
typed_sar_strong];
typed_sar_strong, typed_less_strong,
typed_greater_strong, typed_less_equal_strong,
typed_greater_equal_strong];
let strongStringOrNumberFuncs = [add_strong];
let strongStringOrNumberFuncs = [add_strong, less_strong, greater_strong,
less_equal_strong, greater_equal_strong];
let strongFuncs = strongNumberFuncs.concat(strongStringOrNumberFuncs);
@ -214,16 +285,20 @@ function assertStrongThrowBehaviour(expr) {
function checkArgumentCombinations(op, leftList, rightList, willThrow) {
for (let v1 of leftList) {
let assignExpr = "foo " + op + "= " + v1 + ";";
let assignExpr = "foo " + op[0] + "= " + v1 + ";";
for (let v2 of rightList) {
let compoundAssignment = "'use strong'; let foo = " + v2 + "; " +
assignExpr;
if(willThrow) {
assertThrows(compoundAssignment, TypeError);
assertStrongThrowBehaviour("(" + v1 + op + v2 + ")");
if (willThrow) {
if (op[1]) {
assertThrows(compoundAssignment, TypeError);
}
assertStrongThrowBehaviour("(" + v1 + op[0] + v2 + ")");
} else {
assertDoesNotThrow(compoundAssignment);
assertStrongNonThrowBehaviour("(" + v1 + op + v2 + ")");
if (op[1]) {
assertDoesNotThrow(compoundAssignment);
}
assertStrongNonThrowBehaviour("(" + v1 + op[0] + v2 + ")");
}
}
}