[Interpreter] Add support for for count operations.

Adds support for count operations to the interpreter. Deals with count
operations on locals, globals, context allocated variables and named and
keyed properties.

Adds the following bytecodes:
  ToNumber
  Inc
  Dec

BUG=v8:4280
LOG=N
TBR=mstarzinger@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#31484}
This commit is contained in:
rmcilroy 2015-10-22 13:40:20 -07:00 committed by Commit bot
parent 5676415b4a
commit 0030805643
11 changed files with 620 additions and 40 deletions

View File

@ -476,6 +476,18 @@ void BytecodeGraphBuilder::VisitShiftRightLogical(
}
void BytecodeGraphBuilder::VisitInc(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitDec(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitLogicalNot(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
@ -560,6 +572,12 @@ void BytecodeGraphBuilder::VisitToName(
}
void BytecodeGraphBuilder::VisitToNumber(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::VisitJump(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();

View File

@ -172,6 +172,17 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op,
Strength strength) {
if (is_strong(strength)) {
UNIMPLEMENTED();
}
Output(BytecodeForCountOperation(op));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() {
Output(Bytecode::kLogicalNot);
return *this;
@ -465,6 +476,14 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() {
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber() {
// TODO(rmcilroy): consider omitting if the preceeding bytecode always returns
// a number.
Output(Bytecode::kToNumber);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
@ -826,6 +845,20 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) {
switch (op) {
case Token::Value::ADD:
return Bytecode::kInc;
case Token::Value::SUB:
return Bytecode::kDec;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
switch (op) {

View File

@ -146,6 +146,9 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg,
Strength strength);
// Count Operators (value stored in accumulator).
BytecodeArrayBuilder& CountOperation(Token::Value op, Strength strength);
// Unary Operators.
BytecodeArrayBuilder& LogicalNot();
BytecodeArrayBuilder& TypeOf();
@ -157,6 +160,7 @@ class BytecodeArrayBuilder {
// Casts.
BytecodeArrayBuilder& CastAccumulatorToBoolean();
BytecodeArrayBuilder& CastAccumulatorToName();
BytecodeArrayBuilder& CastAccumulatorToNumber();
// Flow Control.
BytecodeArrayBuilder& Bind(BytecodeLabel* label);
@ -186,6 +190,7 @@ class BytecodeArrayBuilder {
Isolate* isolate() const { return isolate_; }
static Bytecode BytecodeForBinaryOperation(Token::Value op);
static Bytecode BytecodeForCountOperation(Token::Value op);
static Bytecode BytecodeForCompareOperation(Token::Value op);
static Bytecode BytecodeForLoadIC(LanguageMode language_mode);
static Bytecode BytecodeForKeyedLoadIC(LanguageMode language_mode);

View File

@ -960,6 +960,21 @@ void BytecodeGenerator::VisitVariableLoad(Variable* variable,
}
void BytecodeGenerator::VisitVariableLoadForAccumulatorValue(
Variable* variable, FeedbackVectorSlot slot) {
AccumulatorResultScope accumulator_result(this);
VisitVariableLoad(variable, slot);
}
Register BytecodeGenerator::VisitVariableLoadForRegisterValue(
Variable* variable, FeedbackVectorSlot slot) {
RegisterResultScope register_scope(this);
VisitVariableLoad(variable, slot);
return register_scope.ResultRegister();
}
void BytecodeGenerator::VisitVariableAssignment(Variable* variable,
FeedbackVectorSlot slot) {
switch (variable->location()) {
@ -1174,10 +1189,8 @@ void BytecodeGenerator::VisitCall(Call* expr) {
builder()->LoadUndefined().StoreAccumulatorInRegister(receiver);
// Load callee as a global variable.
VariableProxy* proxy = callee_expr->AsVariableProxy();
// Result scope for VisitVariableLoad to avoid using our temporaries
// and double setting the result in our result_scope() and.
AccumulatorResultScope accumulator_execution_result(this);
VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot());
VisitVariableLoadForAccumulatorValue(proxy->var(),
proxy->VariableFeedbackSlot());
builder()->StoreAccumulatorInRegister(callee);
break;
}
@ -1295,7 +1308,93 @@ void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
UNIMPLEMENTED();
DCHECK(expr->expression()->IsValidReferenceExpressionOrThis());
// Left-hand side can only be a property, a global or a variable slot.
Property* property = expr->expression()->AsProperty();
LhsKind assign_type = Property::GetAssignType(property);
// TODO(rmcilroy): Set is_postfix to false if visiting for effect.
bool is_postfix = expr->is_postfix();
// Evaluate LHS expression and get old value.
Register obj, key, old_value;
size_t name_index = kMaxUInt32;
switch (assign_type) {
case VARIABLE: {
VariableProxy* proxy = expr->expression()->AsVariableProxy();
VisitVariableLoadForAccumulatorValue(proxy->var(),
proxy->VariableFeedbackSlot());
break;
}
case NAMED_PROPERTY: {
FeedbackVectorSlot slot = property->PropertyFeedbackSlot();
obj = VisitForRegisterValue(property->obj());
name_index = builder()->GetConstantPoolEntry(
property->key()->AsLiteral()->AsPropertyName());
builder()->LoadNamedProperty(obj, name_index, feedback_index(slot),
language_mode());
break;
}
case KEYED_PROPERTY: {
FeedbackVectorSlot slot = property->PropertyFeedbackSlot();
obj = VisitForRegisterValue(property->obj());
// Use visit for accumulator here since we need the key in the accumulator
// for the LoadKeyedProperty.
key = execution_result()->NewRegister();
VisitForAccumulatorValue(property->key());
builder()->StoreAccumulatorInRegister(key).LoadKeyedProperty(
obj, feedback_index(slot), language_mode());
break;
}
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
UNIMPLEMENTED();
}
// Convert old value into a number.
if (!is_strong(language_mode())) {
builder()->CastAccumulatorToNumber();
}
// Save result for postfix expressions.
if (is_postfix) {
old_value = execution_result()->NewRegister();
builder()->StoreAccumulatorInRegister(old_value);
}
// Perform +1/-1 operation.
builder()->CountOperation(expr->binary_op(), language_mode_strength());
// Store the value.
FeedbackVectorSlot feedback_slot = expr->CountSlot();
switch (assign_type) {
case VARIABLE: {
Variable* variable = expr->expression()->AsVariableProxy()->var();
VisitVariableAssignment(variable, feedback_slot);
break;
}
case NAMED_PROPERTY: {
builder()->StoreNamedProperty(
obj, name_index, feedback_index(feedback_slot), language_mode());
break;
}
case KEYED_PROPERTY: {
builder()->StoreKeyedProperty(obj, key, feedback_index(feedback_slot),
language_mode());
break;
}
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
UNIMPLEMENTED();
}
// Restore old value for postfix expressions.
if (is_postfix) {
execution_result()->SetResultInRegister(old_value);
} else {
execution_result()->SetResultInAccumulator();
}
}

View File

@ -41,17 +41,28 @@ class BytecodeGenerator : public AstVisitor {
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
Register VisitArguments(ZoneList<Expression*>* arguments);
// Dispatched from VisitBinaryOperation.
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitCommaExpression(BinaryOperation* binop);
void VisitLogicalOrExpression(BinaryOperation* binop);
void VisitLogicalAndExpression(BinaryOperation* binop);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
void VisitTypeOf(UnaryOperation* expr);
void VisitNot(UnaryOperation* expr);
// Helper visitors which perform common operations.
Register VisitArguments(ZoneList<Expression*>* arguments);
void VisitPropertyLoad(Register obj, Property* expr);
void VisitPropertyLoadForAccumulator(Register obj, Property* expr);
void VisitVariableLoad(Variable* variable, FeedbackVectorSlot slot);
void VisitVariableLoadForAccumulatorValue(Variable* variable,
FeedbackVectorSlot slot);
MUST_USE_RESULT Register VisitVariableLoadForRegisterValue(
Variable* variable, FeedbackVectorSlot slot);
void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot);
void VisitNewLocalFunctionContext();
@ -64,10 +75,6 @@ class BytecodeGenerator : public AstVisitor {
ObjectLiteralProperty* property,
Register value_out);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
void VisitTypeOf(UnaryOperation* expr);
void VisitNot(UnaryOperation* expr);
// Visitors for obtaining expression result in the accumulator, in a
// register, or just getting the effect.

View File

@ -87,6 +87,8 @@ namespace interpreter {
V(ShiftRightLogical, OperandType::kReg8) \
\
/* Unary Operators */ \
V(Inc, OperandType::kNone) \
V(Dec, OperandType::kNone) \
V(LogicalNot, OperandType::kNone) \
V(TypeOf, OperandType::kNone) \
\
@ -113,6 +115,7 @@ namespace interpreter {
/* Cast operators */ \
V(ToBoolean, OperandType::kNone) \
V(ToName, OperandType::kNone) \
V(ToNumber, OperandType::kNone) \
\
/* Literals */ \
V(CreateRegExpLiteral, OperandType::kIdx8, OperandType::kReg8) \

View File

@ -591,6 +591,32 @@ void Interpreter::DoShiftRightLogical(
}
void Interpreter::DoCountOp(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler) {
Node* value = __ GetAccumulator();
Node* one = __ NumberConstant(1);
Node* result = __ CallRuntime(function_id, value, one);
__ SetAccumulator(result);
__ Dispatch();
}
// Inc
//
// Increments value in the accumulator by one.
void Interpreter::DoInc(compiler::InterpreterAssembler* assembler) {
DoCountOp(Runtime::kAdd, assembler);
}
// Dec
//
// Decrements value in the accumulator by one.
void Interpreter::DoDec(compiler::InterpreterAssembler* assembler) {
DoCountOp(Runtime::kSubtract, assembler);
}
// LogicalNot
//
// Perform logical-not on the accumulator, first casting the
@ -776,6 +802,17 @@ void Interpreter::DoToName(compiler::InterpreterAssembler* assembler) {
}
// ToNumber
//
// Cast the object referenced by the accumulator to a number.
void Interpreter::DoToNumber(compiler::InterpreterAssembler* assembler) {
Node* accumulator = __ GetAccumulator();
Node* result = __ CallRuntime(Runtime::kToNumber, accumulator);
__ SetAccumulator(result);
__ Dispatch();
}
// Jump <imm8>
//
// Jump by number of bytes represented by the immediate operand |imm8|.

View File

@ -54,6 +54,10 @@ class Interpreter {
void DoBinaryOp(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler);
// Generates code to perform the count operations via |function_id|.
void DoCountOp(Runtime::FunctionId function_id,
compiler::InterpreterAssembler* assembler);
// Generates code to perform the comparison operation associated with
// |compare_op|.
void DoCompareOp(Token::Value compare_op,

View File

@ -2916,6 +2916,295 @@ TEST(ContextParameters) {
}
}
TEST(CountOperators) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
int closure = Register::function_closure().index();
int first_context_slot = Context::MIN_CONTEXT_SLOTS;
int object_literal_flags =
ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos;
ExpectedSnippet<InstanceType> snippets[] = {
{"var a = 1; return ++a;",
1 * kPointerSize,
1,
11,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(Ldar), R(0), //
B(ToNumber), //
B(Inc), //
B(Star), R(0), //
B(Return), //
}},
{"var a = 1; return a++;",
2 * kPointerSize,
1,
15,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(Ldar), R(0), //
B(ToNumber), //
B(Star), R(1), //
B(Inc), //
B(Star), R(0), //
B(Ldar), R(1), //
B(Return), //
}},
{"var a = 1; return --a;",
1 * kPointerSize,
1,
11,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(Ldar), R(0), //
B(ToNumber), //
B(Dec), //
B(Star), R(0), //
B(Return), //
}},
{"var a = 1; return a--;",
2 * kPointerSize,
1,
15,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(Ldar), R(0), //
B(ToNumber), //
B(Star), R(1), //
B(Dec), //
B(Star), R(0), //
B(Ldar), R(1), //
B(Return), //
}},
{"var a = { val: 1 }; return a.val++;",
2 * kPointerSize,
1,
22,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(object_literal_flags), //
B(Star), R(0), //
B(LoadICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Star), R(1), //
B(Inc), //
B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot2)), //
B(Ldar), R(1), //
B(Return), //
},
2,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"var a = { val: 1 }; return --a.val;",
1 * kPointerSize,
1,
18,
{
B(LdaConstant), U8(0), //
B(CreateObjectLiteral), U8(0), U8(object_literal_flags), //
B(Star), R(0), //
B(LoadICSloppy), R(0), U8(1), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Dec), //
B(StoreICSloppy), R(0), U8(1), U8(vector->GetIndex(slot2)), //
B(Return), //
},
2,
{InstanceType::FIXED_ARRAY_TYPE,
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
{"var name = 'var'; var a = { val: 1 }; return a[name]--;",
4 * kPointerSize,
1,
29,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(CreateObjectLiteral), U8(0), U8(object_literal_flags), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Star), R(2), //
B(KeyedLoadICSloppy), R(1), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Star), R(3), //
B(Dec), //
B(KeyedStoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot2)), //
B(Ldar), R(3), //
B(Return), //
},
2,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::FIXED_ARRAY_TYPE}},
{"var name = 'var'; var a = { val: 1 }; return ++a[name];",
3 * kPointerSize,
1,
25,
{
B(LdaConstant), U8(0), //
B(Star), R(0), //
B(LdaConstant), U8(1), //
B(CreateObjectLiteral), U8(0), U8(object_literal_flags), //
B(Star), R(1), //
B(Ldar), R(0), //
B(Star), R(2), //
B(KeyedLoadICSloppy), R(1), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Inc), //
B(KeyedStoreICSloppy), R(1), R(2), U8(vector->GetIndex(slot2)), //
B(Return), //
},
2,
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
InstanceType::FIXED_ARRAY_TYPE}},
{"var a = 1; var b = function() { return a }; return ++a;",
2 * kPointerSize,
1,
27,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(1), //
B(LdaSmi8), U8(1), //
B(StaContextSlot), R(1), U8(first_context_slot), //
B(LdaConstant), U8(0), //
B(CreateClosure), U8(0), //
B(Star), R(0), //
B(LdaContextSlot), R(1), U8(first_context_slot), //
B(ToNumber), //
B(Inc), //
B(StaContextSlot), R(1), U8(first_context_slot), //
B(Return), //
},
1,
{InstanceType::SHARED_FUNCTION_INFO_TYPE}},
{"var a = 1; var b = function() { return a }; return a--;",
3 * kPointerSize,
1,
31,
{
B(CallRuntime), U16(Runtime::kNewFunctionContext), R(closure), //
U8(1), //
B(PushContext), R(1), //
B(LdaSmi8), U8(1), //
B(StaContextSlot), R(1), U8(first_context_slot), //
B(LdaConstant), U8(0), //
B(CreateClosure), U8(0), //
B(Star), R(0), //
B(LdaContextSlot), R(1), U8(first_context_slot), //
B(ToNumber), //
B(Star), R(2), //
B(Dec), //
B(StaContextSlot), R(1), U8(first_context_slot), //
B(Ldar), R(2), //
B(Return), //
},
1,
{InstanceType::SHARED_FUNCTION_INFO_TYPE}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CheckBytecodeArrayEqual(snippets[i], bytecode_array);
}
}
TEST(GlobalCountOperators) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Zone zone;
FeedbackVectorSpec feedback_spec(&zone);
FeedbackVectorSlot slot1 = feedback_spec.AddLoadICSlot();
FeedbackVectorSlot slot2 = feedback_spec.AddStoreICSlot();
Handle<i::TypeFeedbackVector> vector =
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
ExpectedSnippet<const char*> snippets[] = {
{"var global = 1;\nfunction f() { return ++global; }\nf()",
0,
1,
9,
{
B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Inc), //
B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), //
B(Return), //
},
1,
{"global"}},
{"var global = 1;\nfunction f() { return global--; }\nf()",
1 * kPointerSize,
1,
13,
{
B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Star), R(0), //
B(Dec), //
B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), //
B(Ldar), R(0), //
B(Return),
},
1,
{"global"}},
{"unallocated = 1;\nfunction f() { 'use strict'; return --unallocated; }"
"f()",
0,
1,
9,
{
B(LdaGlobalStrict), U8(0), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Dec), //
B(StaGlobalStrict), U8(0), U8(vector->GetIndex(slot2)), //
B(Return), //
},
1,
{"unallocated"}},
{"unallocated = 1;\nfunction f() { return unallocated++; }\nf()",
1 * kPointerSize,
1,
13,
{
B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot1)), //
B(ToNumber), //
B(Star), R(0), //
B(Inc), //
B(StaGlobalSloppy), U8(0), U8(vector->GetIndex(slot2)), //
B(Ldar), R(0), //
B(Return),
},
1,
{"unallocated"}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -1724,11 +1724,11 @@ TEST(InterpreterArrayLiterals) {
std::make_pair("return [][0];\n",
factory->undefined_value()),
std::make_pair("return [1, 3, 2][1];\n",
Handle<Object>(Smi::FromInt(3), isolate)),
handle(Smi::FromInt(3), isolate)),
std::make_pair("return ['a', 'b', 'c'][2];\n",
factory->NewStringFromStaticChars("c")),
std::make_pair("var a = 100; return [a, a + 1, a + 2, a + 3][2];\n",
Handle<Object>(Smi::FromInt(102), isolate)),
handle(Smi::FromInt(102), isolate)),
std::make_pair("return [[1, 2, 3], ['a', 'b', 'c']][1][0];\n",
factory->NewStringFromStaticChars("a")),
std::make_pair("var t = 't'; return [[t, t + 'est'], [1 + t]][0][1];\n",
@ -1757,26 +1757,26 @@ TEST(InterpreterObjectLiterals) {
std::make_pair("return { name: 'string', val: 9.2 }.name;",
factory->NewStringFromStaticChars("string")),
std::make_pair("var a = 15; return { name: 'string', val: a }.val;",
Handle<Object>(Smi::FromInt(15), isolate)),
handle(Smi::FromInt(15), isolate)),
std::make_pair("var a = 5; return { val: a, val: a + 1 }.val;",
Handle<Object>(Smi::FromInt(6), isolate)),
handle(Smi::FromInt(6), isolate)),
std::make_pair("return { func: function() { return 'test' } }.func();",
factory->NewStringFromStaticChars("test")),
std::make_pair("return { func(a) { return a + 'st'; } }.func('te');",
factory->NewStringFromStaticChars("test")),
std::make_pair("return { get a() { return 22; } }.a;",
Handle<Object>(Smi::FromInt(22), isolate)),
handle(Smi::FromInt(22), isolate)),
std::make_pair("var a = { get b() { return this.x + 't'; },\n"
" set b(val) { this.x = val + 's' } };\n"
"a.b = 'te';\n"
"return a.b;",
factory->NewStringFromStaticChars("test")),
std::make_pair("var a = 123; return { 1: a }[1];",
Handle<Object>(Smi::FromInt(123), isolate)),
handle(Smi::FromInt(123), isolate)),
std::make_pair("return Object.getPrototypeOf({ __proto__: null });",
factory->null_value()),
std::make_pair("var a = 'test'; return { [a]: 1 }.test;",
Handle<Object>(Smi::FromInt(1), isolate)),
handle(Smi::FromInt(1), isolate)),
std::make_pair("var a = 'test'; return { b: a, [a]: a + 'ing' }['test']",
factory->NewStringFromStaticChars("testing")),
std::make_pair("var a = 'proto_str';\n"
@ -1785,7 +1785,7 @@ TEST(InterpreterObjectLiterals) {
factory->NewStringFromStaticChars("proto_str")),
std::make_pair("var n = 'name';\n"
"return { [n]: 'val', get a() { return 987 } }['a'];",
Handle<Object>(Smi::FromInt(987), isolate)),
handle(Smi::FromInt(987), isolate)),
};
for (size_t i = 0; i < arraysize(literals); i++) {
@ -1864,19 +1864,19 @@ TEST(InterpreterContextVariables) {
std::pair<const char*, Handle<Object>> context_vars[5] = {
std::make_pair("var a; (function() { a = 1; })(); return a;",
Handle<Object>(Smi::FromInt(1), isolate)),
handle(Smi::FromInt(1), isolate)),
std::make_pair("var a = 10; (function() { a; })(); return a;",
Handle<Object>(Smi::FromInt(10), isolate)),
handle(Smi::FromInt(10), isolate)),
std::make_pair("var a = 20; var b = 30;\n"
"return (function() { return a + b; })();",
Handle<Object>(Smi::FromInt(50), isolate)),
handle(Smi::FromInt(50), isolate)),
std::make_pair("'use strict'; let a = 1;\n"
"{ let b = 2; return (function() { return a + b; })(); }",
Handle<Object>(Smi::FromInt(3), isolate)),
handle(Smi::FromInt(3), isolate)),
std::make_pair("'use strict'; let a = 10;\n"
"{ let b = 20; var c = function() { [a, b] };\n"
" return a + b; }",
Handle<Object>(Smi::FromInt(30), isolate)),
handle(Smi::FromInt(30), isolate)),
};
for (size_t i = 0; i < arraysize(context_vars); i++) {
@ -1896,11 +1896,11 @@ TEST(InterpreterContextParameters) {
std::pair<const char*, Handle<Object>> context_params[3] = {
std::make_pair("return (function() { return arg1; })();",
Handle<Object>(Smi::FromInt(1), isolate)),
handle(Smi::FromInt(1), isolate)),
std::make_pair("(function() { arg1 = 4; })(); return arg1;",
Handle<Object>(Smi::FromInt(4), isolate)),
handle(Smi::FromInt(4), isolate)),
std::make_pair("(function() { arg3 = arg2 - arg1; })(); return arg3;",
Handle<Object>(Smi::FromInt(1), isolate)),
handle(Smi::FromInt(1), isolate)),
};
for (size_t i = 0; i < arraysize(context_params); i++) {
@ -1910,9 +1910,9 @@ TEST(InterpreterContextParameters) {
auto callable =
tester.GetCallable<Handle<Object>, Handle<Object>, Handle<Object>>();
Handle<Object> a1 = Handle<Object>(Smi::FromInt(1), isolate);
Handle<Object> a2 = Handle<Object>(Smi::FromInt(2), isolate);
Handle<Object> a3 = Handle<Object>(Smi::FromInt(3), isolate);
Handle<Object> a1 = handle(Smi::FromInt(1), isolate);
Handle<Object> a2 = handle(Smi::FromInt(2), isolate);
Handle<Object> a3 = handle(Smi::FromInt(3), isolate);
Handle<i::Object> return_value = callable(a1, a2, a3).ToHandleChecked();
CHECK(return_value->SameValue(*context_params[i].second));
}
@ -1927,14 +1927,14 @@ TEST(InterpreterComma) {
std::pair<const char*, Handle<Object>> literals[6] = {
std::make_pair("var a; return 0, a;\n", factory->undefined_value()),
std::make_pair("return 'a', 2.2, 3;\n",
Handle<Object>(Smi::FromInt(3), isolate)),
handle(Smi::FromInt(3), isolate)),
std::make_pair("return 'a', 'b', 'c';\n",
factory->NewStringFromStaticChars("c")),
std::make_pair("return 3.2, 2.3, 4.5;\n", factory->NewNumber(4.5)),
std::make_pair("var a = 10; return b = a, b = b+1;\n",
Handle<Object>(Smi::FromInt(11), isolate)),
handle(Smi::FromInt(11), isolate)),
std::make_pair("var a = 10; return b = a, b = b+1, b + 10;\n",
Handle<Object>(Smi::FromInt(21), isolate))};
handle(Smi::FromInt(21), isolate))};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
@ -1955,7 +1955,7 @@ TEST(InterpreterLogicalOr) {
std::pair<const char*, Handle<Object>> literals[5] = {
std::make_pair("var a, b; return a || b;\n", factory->undefined_value()),
std::make_pair("var a, b = 10; return a || b;\n",
Handle<Object>(Smi::FromInt(10), isolate)),
handle(Smi::FromInt(10), isolate)),
std::make_pair("var a = '0', b = 10; return a || b;\n",
factory->NewStringFromStaticChars("0")),
std::make_pair("return 0 || 3.2;\n", factory->NewNumber(3.2)),
@ -1982,17 +1982,17 @@ TEST(InterpreterLogicalAnd) {
std::make_pair("var a, b = 10; return a && b;\n",
factory->undefined_value()),
std::make_pair("var a = 0, b = 10; return a && b / a;\n",
Handle<Object>(Smi::FromInt(0), isolate)),
handle(Smi::FromInt(0), isolate)),
std::make_pair("var a = '0', b = 10; return a && b;\n",
Handle<Object>(Smi::FromInt(10), isolate)),
handle(Smi::FromInt(10), isolate)),
std::make_pair("return 0.0 && 3.2;\n",
Handle<Object>(Smi::FromInt(0), isolate)),
handle(Smi::FromInt(0), isolate)),
std::make_pair("return 'a' && 'b';\n",
factory->NewStringFromStaticChars("b")),
std::make_pair("return 'a' && 0 || 'b', 'c';\n",
factory->NewStringFromStaticChars("c")),
std::make_pair("var x = 1, y = 3; return x && 0 + 1 || y;\n",
Handle<Object>(Smi::FromInt(1), isolate))};
handle(Smi::FromInt(1), isolate))};
for (size_t i = 0; i < arraysize(literals); i++) {
std::string source(InterpreterTester::SourceForBody(literals[i].first));
@ -2043,7 +2043,7 @@ TEST(InterpreterThrow) {
std::make_pair("throw undefined;\n",
factory->undefined_value()),
std::make_pair("throw 1;\n",
Handle<Object>(Smi::FromInt(1), isolate)),
handle(Smi::FromInt(1), isolate)),
std::make_pair("throw 'Error';\n",
factory->NewStringFromStaticChars("Error")),
std::make_pair("var a = true; if (a) { throw 'Error'; }\n",
@ -2065,3 +2065,83 @@ TEST(InterpreterThrow) {
CHECK(thrown_obj->SameValue(*throws[i].second));
}
}
TEST(InterpreterCountOperators) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
std::pair<const char*, Handle<Object>> count_ops[16] = {
std::make_pair("var a = 1; return ++a;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = 1; return a++;",
handle(Smi::FromInt(1), isolate)),
std::make_pair("var a = 5; return --a;",
handle(Smi::FromInt(4), isolate)),
std::make_pair("var a = 5; return a--;",
handle(Smi::FromInt(5), isolate)),
std::make_pair("var a = 5.2; return --a;",
factory->NewHeapNumber(4.2)),
std::make_pair("var a = 'string'; return ++a;",
factory->nan_value()),
std::make_pair("var a = 'string'; return a--;",
factory->nan_value()),
std::make_pair("var a = true; return ++a;",
handle(Smi::FromInt(2), isolate)),
std::make_pair("var a = false; return a--;",
handle(Smi::FromInt(0), isolate)),
std::make_pair("var a = { val: 11 }; return ++a.val;",
handle(Smi::FromInt(12), isolate)),
std::make_pair("var a = { val: 11 }; return a.val--;",
handle(Smi::FromInt(11), isolate)),
std::make_pair("var a = { val: 11 }; return ++a.val;",
handle(Smi::FromInt(12), isolate)),
std::make_pair("var name = 'val'; var a = { val: 22 }; return --a[name];",
handle(Smi::FromInt(21), isolate)),
std::make_pair("var name = 'val'; var a = { val: 22 }; return a[name]++;",
handle(Smi::FromInt(22), isolate)),
std::make_pair("var a = 1; (function() { a = 2 })(); return ++a;",
handle(Smi::FromInt(3), isolate)),
std::make_pair("var a = 1; (function() { a = 2 })(); return a--;",
handle(Smi::FromInt(2), isolate)),
};
for (size_t i = 0; i < arraysize(count_ops); i++) {
std::string source(InterpreterTester::SourceForBody(count_ops[i].first));
InterpreterTester tester(handles.main_isolate(), source.c_str());
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*count_ops[i].second));
}
}
TEST(InterpreterGlobalCountOperators) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
std::pair<const char*, Handle<Object>> count_ops[6] = {
std::make_pair("var global = 100;function f(){ return ++global; }",
handle(Smi::FromInt(101), isolate)),
std::make_pair("var global = 100; function f(){ return --global; }",
handle(Smi::FromInt(99), isolate)),
std::make_pair("var global = 100; function f(){ return global++; }",
handle(Smi::FromInt(100), isolate)),
std::make_pair("unallocated = 200; function f(){ return ++unallocated; }",
handle(Smi::FromInt(201), isolate)),
std::make_pair("unallocated = 200; function f(){ return --unallocated; }",
handle(Smi::FromInt(199), isolate)),
std::make_pair("unallocated = 200; function f(){ return unallocated++; }",
handle(Smi::FromInt(200), isolate)),
};
for (size_t i = 0; i < arraysize(count_ops); i++) {
InterpreterTester tester(handles.main_isolate(), count_ops[i].first);
auto callable = tester.GetCallable<>();
Handle<i::Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*count_ops[i].second));
}
}

View File

@ -94,9 +94,14 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.BinaryOperation(Token::Value::SAR, reg, Strength::WEAK)
.BinaryOperation(Token::Value::SHR, reg, Strength::WEAK);
// Emit count operatior invocations
builder.CountOperation(Token::Value::ADD, Strength::WEAK)
.CountOperation(Token::Value::SUB, Strength::WEAK);
// Emit unary operator invocations.
builder.LogicalNot().TypeOf();
// Emit new.
builder.New(reg, reg, 0);
@ -113,7 +118,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CompareOperation(Token::Value::IN, reg, Strength::WEAK);
// Emit cast operator invocations.
builder.LoadNull()
builder.CastAccumulatorToNumber()
.CastAccumulatorToBoolean()
.CastAccumulatorToName();