[Interpreter] Adds delete operator to interpreter.
Adds support for delete operator, it's implementation and tests. Adds tests for the following unary operators -BitwiseNot -Add -Sub BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1410953003 Cr-Commit-Position: refs/heads/master@{#31620}
This commit is contained in:
parent
d1f773026c
commit
95e26ec423
@ -512,6 +512,18 @@ void BytecodeGraphBuilder::VisitTypeOf(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BytecodeGraphBuilder::VisitDeletePropertyStrict(
|
||||||
|
const interpreter::BytecodeArrayIterator& iterator) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BytecodeGraphBuilder::VisitDeletePropertySloppy(
|
||||||
|
const interpreter::BytecodeArrayIterator& iterator) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void BytecodeGraphBuilder::VisitTestEqual(
|
void BytecodeGraphBuilder::VisitTestEqual(
|
||||||
const interpreter::BytecodeArrayIterator& iterator) {
|
const interpreter::BytecodeArrayIterator& iterator) {
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
|
@ -696,6 +696,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object,
|
||||||
|
LanguageMode language_mode) {
|
||||||
|
Output(BytecodeForDelete(language_mode), object.ToOperand());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
|
size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
|
||||||
// These constants shouldn't be added to the constant pool, the should use
|
// These constants shouldn't be added to the constant pool, the should use
|
||||||
// specialzed bytecodes instead.
|
// specialzed bytecodes instead.
|
||||||
@ -1015,6 +1022,22 @@ Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
|
Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) {
|
||||||
|
switch (language_mode) {
|
||||||
|
case SLOPPY:
|
||||||
|
return Bytecode::kDeletePropertySloppy;
|
||||||
|
case STRICT:
|
||||||
|
return Bytecode::kDeletePropertyStrict;
|
||||||
|
case STRONG:
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
return static_cast<Bytecode>(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
|
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
|
||||||
return kMinUInt8 <= value && value <= kMaxUInt8;
|
return kMinUInt8 <= value && value <= kMaxUInt8;
|
||||||
|
@ -160,6 +160,10 @@ class BytecodeArrayBuilder {
|
|||||||
BytecodeArrayBuilder& LogicalNot();
|
BytecodeArrayBuilder& LogicalNot();
|
||||||
BytecodeArrayBuilder& TypeOf();
|
BytecodeArrayBuilder& TypeOf();
|
||||||
|
|
||||||
|
// Deletes property from an object. This expects that accumulator contains
|
||||||
|
// the key to be deleted and the register contains a reference to the object.
|
||||||
|
BytecodeArrayBuilder& Delete(Register object, LanguageMode language_mode);
|
||||||
|
|
||||||
// Tests.
|
// Tests.
|
||||||
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
|
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
|
||||||
Strength strength);
|
Strength strength);
|
||||||
@ -206,6 +210,7 @@ class BytecodeArrayBuilder {
|
|||||||
static Bytecode BytecodeForLoadGlobal(LanguageMode language_mode);
|
static Bytecode BytecodeForLoadGlobal(LanguageMode language_mode);
|
||||||
static Bytecode BytecodeForStoreGlobal(LanguageMode language_mode);
|
static Bytecode BytecodeForStoreGlobal(LanguageMode language_mode);
|
||||||
static Bytecode BytecodeForCreateArguments(CreateArgumentsType type);
|
static Bytecode BytecodeForCreateArguments(CreateArgumentsType type);
|
||||||
|
static Bytecode BytecodeForDelete(LanguageMode language_mode);
|
||||||
|
|
||||||
static bool FitsInIdx8Operand(int value);
|
static bool FitsInIdx8Operand(int value);
|
||||||
static bool FitsInIdx8Operand(size_t value);
|
static bool FitsInIdx8Operand(size_t value);
|
||||||
|
@ -1429,15 +1429,76 @@ void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
|
|||||||
case Token::Value::VOID:
|
case Token::Value::VOID:
|
||||||
VisitVoid(expr);
|
VisitVoid(expr);
|
||||||
break;
|
break;
|
||||||
case Token::Value::BIT_NOT:
|
|
||||||
case Token::Value::DELETE:
|
case Token::Value::DELETE:
|
||||||
UNIMPLEMENTED();
|
VisitDelete(expr);
|
||||||
|
break;
|
||||||
|
case Token::Value::BIT_NOT:
|
||||||
|
case Token::Value::ADD:
|
||||||
|
case Token::Value::SUB:
|
||||||
|
// These operators are converted to an equivalent binary operators in
|
||||||
|
// the parser. These operators are not expected to be visited here.
|
||||||
|
UNREACHABLE();
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BytecodeGenerator::VisitDelete(UnaryOperation* expr) {
|
||||||
|
if (expr->expression()->IsProperty()) {
|
||||||
|
// Delete of an object property is allowed both in sloppy
|
||||||
|
// and strict modes.
|
||||||
|
Property* property = expr->expression()->AsProperty();
|
||||||
|
Register object = VisitForRegisterValue(property->obj());
|
||||||
|
VisitForAccumulatorValue(property->key());
|
||||||
|
builder()->Delete(object, language_mode());
|
||||||
|
} else if (expr->expression()->IsVariableProxy()) {
|
||||||
|
// Delete of an unqualified identifier is allowed in sloppy mode but is
|
||||||
|
// not allowed in strict mode. Deleting 'this' is allowed in both modes.
|
||||||
|
VariableProxy* proxy = expr->expression()->AsVariableProxy();
|
||||||
|
Variable* variable = proxy->var();
|
||||||
|
DCHECK(is_sloppy(language_mode()) || variable->HasThisName(isolate()));
|
||||||
|
switch (variable->location()) {
|
||||||
|
case VariableLocation::GLOBAL:
|
||||||
|
case VariableLocation::UNALLOCATED: {
|
||||||
|
// Global var, let, const or variables not explicitly declared.
|
||||||
|
Register global_object = execution_result()->NewRegister();
|
||||||
|
builder()
|
||||||
|
->LoadContextSlot(execution_context()->reg(),
|
||||||
|
Context::GLOBAL_OBJECT_INDEX)
|
||||||
|
.StoreAccumulatorInRegister(global_object)
|
||||||
|
.LoadLiteral(variable->name())
|
||||||
|
.Delete(global_object, language_mode());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VariableLocation::PARAMETER:
|
||||||
|
case VariableLocation::LOCAL:
|
||||||
|
case VariableLocation::CONTEXT: {
|
||||||
|
// Deleting local var/let/const, context variables, and arguments
|
||||||
|
// does not have any effect.
|
||||||
|
if (variable->HasThisName(isolate())) {
|
||||||
|
builder()->LoadTrue();
|
||||||
|
} else {
|
||||||
|
builder()->LoadFalse();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VariableLocation::LOOKUP: {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Delete of an unresolvable reference returns true.
|
||||||
|
VisitForEffect(expr->expression());
|
||||||
|
builder()->LoadTrue();
|
||||||
|
}
|
||||||
|
execution_result()->SetResultInAccumulator();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
|
void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
|
||||||
DCHECK(expr->expression()->IsValidReferenceExpressionOrThis());
|
DCHECK(expr->expression()->IsValidReferenceExpressionOrThis());
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ class BytecodeGenerator : public AstVisitor {
|
|||||||
void VisitVoid(UnaryOperation* expr);
|
void VisitVoid(UnaryOperation* expr);
|
||||||
void VisitTypeOf(UnaryOperation* expr);
|
void VisitTypeOf(UnaryOperation* expr);
|
||||||
void VisitNot(UnaryOperation* expr);
|
void VisitNot(UnaryOperation* expr);
|
||||||
|
void VisitDelete(UnaryOperation* expr);
|
||||||
|
|
||||||
// Helper visitors which perform common operations.
|
// Helper visitors which perform common operations.
|
||||||
Register VisitArguments(ZoneList<Expression*>* arguments);
|
Register VisitArguments(ZoneList<Expression*>* arguments);
|
||||||
|
@ -91,6 +91,8 @@ namespace interpreter {
|
|||||||
V(Dec, OperandType::kNone) \
|
V(Dec, OperandType::kNone) \
|
||||||
V(LogicalNot, OperandType::kNone) \
|
V(LogicalNot, OperandType::kNone) \
|
||||||
V(TypeOf, OperandType::kNone) \
|
V(TypeOf, OperandType::kNone) \
|
||||||
|
V(DeletePropertyStrict, OperandType::kReg8) \
|
||||||
|
V(DeletePropertySloppy, OperandType::kReg8) \
|
||||||
\
|
\
|
||||||
/* Call operations */ \
|
/* Call operations */ \
|
||||||
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
|
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
|
||||||
|
@ -644,6 +644,37 @@ void Interpreter::DoTypeOf(compiler::InterpreterAssembler* assembler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Interpreter::DoDelete(Runtime::FunctionId function_id,
|
||||||
|
compiler::InterpreterAssembler* assembler) {
|
||||||
|
Node* reg_index = __ BytecodeOperandReg8(0);
|
||||||
|
Node* object = __ LoadRegister(reg_index);
|
||||||
|
Node* key = __ GetAccumulator();
|
||||||
|
Node* result = __ CallRuntime(function_id, object, key);
|
||||||
|
__ SetAccumulator(result);
|
||||||
|
__ Dispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// DeletePropertyStrict
|
||||||
|
//
|
||||||
|
// Delete the property specified in the accumulator from the object
|
||||||
|
// referenced by the register operand following strict mode semantics.
|
||||||
|
void Interpreter::DoDeletePropertyStrict(
|
||||||
|
compiler::InterpreterAssembler* assembler) {
|
||||||
|
DoDelete(Runtime::kDeleteProperty_Strict, assembler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// DeletePropertySloppy
|
||||||
|
//
|
||||||
|
// Delete the property specified in the accumulator from the object
|
||||||
|
// referenced by the register operand following sloppy mode semantics.
|
||||||
|
void Interpreter::DoDeletePropertySloppy(
|
||||||
|
compiler::InterpreterAssembler* assembler) {
|
||||||
|
DoDelete(Runtime::kDeleteProperty_Sloppy, assembler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Call <callable> <receiver> <arg_count>
|
// Call <callable> <receiver> <arg_count>
|
||||||
//
|
//
|
||||||
// Call a JSfunction or Callable in |callable| with the |receiver| and
|
// Call a JSfunction or Callable in |callable| with the |receiver| and
|
||||||
|
@ -85,6 +85,10 @@ class Interpreter {
|
|||||||
void DoCreateLiteral(Runtime::FunctionId function_id,
|
void DoCreateLiteral(Runtime::FunctionId function_id,
|
||||||
compiler::InterpreterAssembler* assembler);
|
compiler::InterpreterAssembler* assembler);
|
||||||
|
|
||||||
|
// Generates code to perform delete via function_id.
|
||||||
|
void DoDelete(Runtime::FunctionId function_id,
|
||||||
|
compiler::InterpreterAssembler* assembler);
|
||||||
|
|
||||||
bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table);
|
bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table);
|
||||||
|
|
||||||
Isolate* isolate_;
|
Isolate* isolate_;
|
||||||
|
@ -1842,6 +1842,153 @@ TEST(UnaryOperators) {
|
|||||||
B(Return), //
|
B(Return), //
|
||||||
},
|
},
|
||||||
0},
|
0},
|
||||||
|
{"var x = 13;"
|
||||||
|
"return ~x;",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
{
|
||||||
|
B(LdaSmi8), U8(13), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaSmi8), U8(-1), //
|
||||||
|
B(BitwiseXor), R(0), //
|
||||||
|
B(Return), //
|
||||||
|
},
|
||||||
|
0},
|
||||||
|
{"var x = 13;"
|
||||||
|
"return +x;",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
{
|
||||||
|
B(LdaSmi8), U8(13), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaSmi8), U8(1), //
|
||||||
|
B(Mul), R(0), //
|
||||||
|
B(Return), //
|
||||||
|
},
|
||||||
|
0},
|
||||||
|
{"var x = 13;"
|
||||||
|
"return -x;",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
{
|
||||||
|
B(LdaSmi8), U8(13), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaSmi8), U8(-1), //
|
||||||
|
B(Mul), R(0), //
|
||||||
|
B(Return), //
|
||||||
|
},
|
||||||
|
0}};
|
||||||
|
|
||||||
|
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(Delete) {
|
||||||
|
InitializedHandleScope handle_scope;
|
||||||
|
BytecodeGeneratorHelper helper;
|
||||||
|
|
||||||
|
int deep_elements_flags =
|
||||||
|
ObjectLiteral::kFastElements | ObjectLiteral::kDisableMementos;
|
||||||
|
int closure = Register::function_closure().index();
|
||||||
|
int first_context_slot = Context::MIN_CONTEXT_SLOTS;
|
||||||
|
|
||||||
|
ExpectedSnippet<InstanceType> snippets[] = {
|
||||||
|
{"var a = {x:13, y:14}; return delete a.x;",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
12,
|
||||||
|
{
|
||||||
|
B(LdaConstant), U8(0), //
|
||||||
|
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaConstant), U8(1), //
|
||||||
|
B(DeletePropertySloppy), R(0), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
{InstanceType::FIXED_ARRAY_TYPE,
|
||||||
|
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
|
||||||
|
{"'use strict'; var a = {x:13, y:14}; return delete a.x;",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
12,
|
||||||
|
{
|
||||||
|
B(LdaConstant), U8(0), //
|
||||||
|
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaConstant), U8(1), //
|
||||||
|
B(DeletePropertyStrict), R(0), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
{InstanceType::FIXED_ARRAY_TYPE,
|
||||||
|
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
|
||||||
|
{"var a = {1:13, 2:14}; return delete a[2];",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
12,
|
||||||
|
{
|
||||||
|
B(LdaConstant), U8(0), //
|
||||||
|
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaSmi8), U8(2), //
|
||||||
|
B(DeletePropertySloppy), R(0), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
{InstanceType::FIXED_ARRAY_TYPE}},
|
||||||
|
{"var a = 10; return delete a;",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
{
|
||||||
|
B(LdaSmi8), U8(10), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaFalse), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
0},
|
||||||
|
{"'use strict';"
|
||||||
|
"var a = {1:10};"
|
||||||
|
"(function f1() {return a;});"
|
||||||
|
"return delete a[1];",
|
||||||
|
2 * kPointerSize,
|
||||||
|
1,
|
||||||
|
29,
|
||||||
|
{
|
||||||
|
B(CallRuntime), U16(Runtime::kNewFunctionContext), //
|
||||||
|
R(closure), U8(1), //
|
||||||
|
B(PushContext), R(0), //
|
||||||
|
B(LdaConstant), U8(0), //
|
||||||
|
B(CreateObjectLiteral), U8(0), U8(deep_elements_flags), //
|
||||||
|
B(StaContextSlot), R(0), U8(first_context_slot), //
|
||||||
|
B(LdaConstant), U8(1), //
|
||||||
|
B(CreateClosure), U8(0), //
|
||||||
|
B(LdaContextSlot), R(0), U8(first_context_slot), //
|
||||||
|
B(Star), R(1), //
|
||||||
|
B(LdaSmi8), U8(1), //
|
||||||
|
B(DeletePropertyStrict), R(1), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
{InstanceType::FIXED_ARRAY_TYPE,
|
||||||
|
InstanceType::SHARED_FUNCTION_INFO_TYPE}},
|
||||||
|
{"return delete 'test';",
|
||||||
|
0 * kPointerSize,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
B(LdaTrue), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
0},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < arraysize(snippets); i++) {
|
for (size_t i = 0; i < arraysize(snippets); i++) {
|
||||||
@ -1852,6 +1999,83 @@ TEST(UnaryOperators) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(GlobalDelete) {
|
||||||
|
InitializedHandleScope handle_scope;
|
||||||
|
BytecodeGeneratorHelper helper;
|
||||||
|
Zone zone;
|
||||||
|
|
||||||
|
int context = Register::function_context().index();
|
||||||
|
int global_object_index = Context::GLOBAL_OBJECT_INDEX;
|
||||||
|
FeedbackVectorSpec feedback_spec(&zone);
|
||||||
|
FeedbackVectorSlot slot = feedback_spec.AddLoadICSlot();
|
||||||
|
|
||||||
|
Handle<i::TypeFeedbackVector> vector =
|
||||||
|
i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec);
|
||||||
|
|
||||||
|
ExpectedSnippet<InstanceType> snippets[] = {
|
||||||
|
{"var a = {x:13, y:14};\n function f() { return delete a.x; };\n f();",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
{
|
||||||
|
B(LdaGlobalSloppy), U8(0), U8(vector->GetIndex(slot)), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaConstant), U8(1), //
|
||||||
|
B(DeletePropertySloppy), R(0), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE,
|
||||||
|
InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
|
||||||
|
{"a = {1:13, 2:14};\n"
|
||||||
|
"function f() {'use strict'; return delete a[1];};\n f();",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
{
|
||||||
|
B(LdaGlobalStrict), U8(0), U8(vector->GetIndex(slot)), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaSmi8), U8(1), //
|
||||||
|
B(DeletePropertyStrict), R(0), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
|
||||||
|
{"var a = {x:13, y:14};\n function f() { return delete a; };\n f();",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
{
|
||||||
|
B(LdaContextSlot), R(context), U8(global_object_index), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaConstant), U8(0), //
|
||||||
|
B(DeletePropertySloppy), R(0), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}},
|
||||||
|
{"b = 30;\n function f() { return delete b; };\n f();",
|
||||||
|
1 * kPointerSize,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
{
|
||||||
|
B(LdaContextSlot), R(context), U8(global_object_index), //
|
||||||
|
B(Star), R(0), //
|
||||||
|
B(LdaConstant), U8(0), //
|
||||||
|
B(DeletePropertySloppy), R(0), //
|
||||||
|
B(Return)
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
{InstanceType::ONE_BYTE_INTERNALIZED_STRING_TYPE}}};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST(FunctionLiterals) {
|
TEST(FunctionLiterals) {
|
||||||
InitializedHandleScope handle_scope;
|
InitializedHandleScope handle_scope;
|
||||||
BytecodeGeneratorHelper helper;
|
BytecodeGeneratorHelper helper;
|
||||||
|
@ -2335,3 +2335,166 @@ TEST(InterpreterConditional) {
|
|||||||
CHECK(return_value->SameValue(*conditional[i].second));
|
CHECK(return_value->SameValue(*conditional[i].second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(InterpreterDelete) {
|
||||||
|
HandleAndZoneScope handles;
|
||||||
|
i::Isolate* isolate = handles.main_isolate();
|
||||||
|
i::Factory* factory = isolate->factory();
|
||||||
|
|
||||||
|
// Tests for delete for local variables that work both in strict
|
||||||
|
// and sloppy modes
|
||||||
|
std::pair<const char*, Handle<Object>> test_delete[] = {
|
||||||
|
std::make_pair(
|
||||||
|
"var a = { x:10, y:'abc', z:30.2}; delete a.x; return a.x;\n",
|
||||||
|
factory->undefined_value()),
|
||||||
|
std::make_pair(
|
||||||
|
"var b = { x:10, y:'abc', z:30.2}; delete b.x; return b.y;\n",
|
||||||
|
factory->NewStringFromStaticChars("abc")),
|
||||||
|
std::make_pair("var c = { x:10, y:'abc', z:30.2}; var d = c; delete d.x; "
|
||||||
|
"return c.x;\n",
|
||||||
|
factory->undefined_value()),
|
||||||
|
std::make_pair("var e = { x:10, y:'abc', z:30.2}; var g = e; delete g.x; "
|
||||||
|
"return e.y;\n",
|
||||||
|
factory->NewStringFromStaticChars("abc")),
|
||||||
|
std::make_pair("var a = { x:10, y:'abc', z:30.2};\n"
|
||||||
|
"var b = a;"
|
||||||
|
"delete b.x;"
|
||||||
|
"return b.x;\n",
|
||||||
|
factory->undefined_value()),
|
||||||
|
std::make_pair("var a = {1:10};\n"
|
||||||
|
"(function f1() {return a;});"
|
||||||
|
"return delete a[1];",
|
||||||
|
factory->ToBoolean(true)),
|
||||||
|
std::make_pair("return delete this;", factory->ToBoolean(true)),
|
||||||
|
std::make_pair("return delete 'test';", factory->ToBoolean(true))};
|
||||||
|
|
||||||
|
// Test delete in sloppy mode
|
||||||
|
for (size_t i = 0; i < arraysize(test_delete); i++) {
|
||||||
|
std::string source(InterpreterTester::SourceForBody(test_delete[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(*test_delete[i].second));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test delete in strict mode
|
||||||
|
for (size_t i = 0; i < arraysize(test_delete); i++) {
|
||||||
|
std::string strict_test =
|
||||||
|
"'use strict'; " + std::string(test_delete[i].first);
|
||||||
|
std::string source(InterpreterTester::SourceForBody(strict_test.c_str()));
|
||||||
|
InterpreterTester tester(handles.main_isolate(), source.c_str());
|
||||||
|
auto callable = tester.GetCallable<>();
|
||||||
|
|
||||||
|
Handle<i::Object> return_value = callable().ToHandleChecked();
|
||||||
|
CHECK(return_value->SameValue(*test_delete[i].second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(InterpreterDeleteSloppyUnqualifiedIdentifier) {
|
||||||
|
HandleAndZoneScope handles;
|
||||||
|
i::Isolate* isolate = handles.main_isolate();
|
||||||
|
i::Factory* factory = isolate->factory();
|
||||||
|
|
||||||
|
// These tests generate a syntax error for strict mode. We don't
|
||||||
|
// test for it here.
|
||||||
|
std::pair<const char*, Handle<Object>> test_delete[] = {
|
||||||
|
std::make_pair("var a = { x:10, y:'abc'};\n"
|
||||||
|
"var b = delete a;\n"
|
||||||
|
"if (delete a) {\n"
|
||||||
|
" return undefined;\n"
|
||||||
|
"} else {\n"
|
||||||
|
" return a.x;\n"
|
||||||
|
"}\n",
|
||||||
|
Handle<Object>(Smi::FromInt(10), isolate)),
|
||||||
|
// TODO(mythria) When try-catch is implemented change the tests to check
|
||||||
|
// if delete actually deletes
|
||||||
|
std::make_pair("a = { x:10, y:'abc'};\n"
|
||||||
|
"var b = delete a;\n"
|
||||||
|
// "try{return a.x;} catch(e) {return b;}\n"
|
||||||
|
"return b;",
|
||||||
|
factory->ToBoolean(true)),
|
||||||
|
std::make_pair("a = { x:10, y:'abc'};\n"
|
||||||
|
"var b = delete c;\n"
|
||||||
|
"return b;",
|
||||||
|
factory->ToBoolean(true))};
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t i = 0; i < arraysize(test_delete); i++) {
|
||||||
|
std::string source(InterpreterTester::SourceForBody(test_delete[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(*test_delete[i].second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(InterpreterGlobalDelete) {
|
||||||
|
HandleAndZoneScope handles;
|
||||||
|
i::Isolate* isolate = handles.main_isolate();
|
||||||
|
i::Factory* factory = isolate->factory();
|
||||||
|
|
||||||
|
std::pair<const char*, Handle<Object>> test_global_delete[] = {
|
||||||
|
std::make_pair("var a = { x:10, y:'abc', z:30.2 };\n"
|
||||||
|
"function f() {\n"
|
||||||
|
" delete a.x;\n"
|
||||||
|
" return a.x;\n"
|
||||||
|
"}\n"
|
||||||
|
"f();\n",
|
||||||
|
factory->undefined_value()),
|
||||||
|
std::make_pair("var b = {1:10, 2:'abc', 3:30.2 };\n"
|
||||||
|
"function f() {\n"
|
||||||
|
" delete b[2];\n"
|
||||||
|
" return b[1];\n"
|
||||||
|
" }\n"
|
||||||
|
"f();\n",
|
||||||
|
Handle<Object>(Smi::FromInt(10), isolate)),
|
||||||
|
std::make_pair("var c = { x:10, y:'abc', z:30.2 };\n"
|
||||||
|
"function f() {\n"
|
||||||
|
" var d = c;\n"
|
||||||
|
" delete d.y;\n"
|
||||||
|
" return d.x;\n"
|
||||||
|
"}\n"
|
||||||
|
"f();\n",
|
||||||
|
Handle<Object>(Smi::FromInt(10), isolate)),
|
||||||
|
std::make_pair("e = { x:10, y:'abc' };\n"
|
||||||
|
"function f() {\n"
|
||||||
|
" return delete e;\n"
|
||||||
|
"}\n"
|
||||||
|
"f();\n",
|
||||||
|
factory->ToBoolean(true)),
|
||||||
|
std::make_pair("var g = { x:10, y:'abc' };\n"
|
||||||
|
"function f() {\n"
|
||||||
|
" return delete g;\n"
|
||||||
|
"}\n"
|
||||||
|
"f();\n",
|
||||||
|
factory->ToBoolean(false)),
|
||||||
|
std::make_pair("function f() {\n"
|
||||||
|
" var obj = {h:10, f1() {return delete this;}};\n"
|
||||||
|
" return obj.f1();\n"
|
||||||
|
"}\n"
|
||||||
|
"f();",
|
||||||
|
factory->ToBoolean(true)),
|
||||||
|
std::make_pair("function f() {\n"
|
||||||
|
" var obj = {h:10,\n"
|
||||||
|
" f1() {\n"
|
||||||
|
" 'use strict';\n"
|
||||||
|
" return delete this.h;}};\n"
|
||||||
|
" return obj.f1();\n"
|
||||||
|
"}\n"
|
||||||
|
"f();",
|
||||||
|
factory->ToBoolean(true))};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < arraysize(test_global_delete); i++) {
|
||||||
|
InterpreterTester tester(handles.main_isolate(),
|
||||||
|
test_global_delete[i].first);
|
||||||
|
auto callable = tester.GetCallable<>();
|
||||||
|
|
||||||
|
Handle<i::Object> return_value = callable().ToHandleChecked();
|
||||||
|
CHECK(return_value->SameValue(*test_global_delete[i].second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -105,6 +105,8 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
|||||||
// Emit unary operator invocations.
|
// Emit unary operator invocations.
|
||||||
builder.LogicalNot().TypeOf();
|
builder.LogicalNot().TypeOf();
|
||||||
|
|
||||||
|
// Emit delete
|
||||||
|
builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT);
|
||||||
|
|
||||||
// Emit new.
|
// Emit new.
|
||||||
builder.New(reg, reg, 0);
|
builder.New(reg, reg, 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user