[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(
|
||||
const interpreter::BytecodeArrayIterator& iterator) {
|
||||
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) {
|
||||
// These constants shouldn't be added to the constant pool, the should use
|
||||
// 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
|
||||
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
|
||||
return kMinUInt8 <= value && value <= kMaxUInt8;
|
||||
|
@ -160,6 +160,10 @@ class BytecodeArrayBuilder {
|
||||
BytecodeArrayBuilder& LogicalNot();
|
||||
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.
|
||||
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
|
||||
Strength strength);
|
||||
@ -206,6 +210,7 @@ class BytecodeArrayBuilder {
|
||||
static Bytecode BytecodeForLoadGlobal(LanguageMode language_mode);
|
||||
static Bytecode BytecodeForStoreGlobal(LanguageMode language_mode);
|
||||
static Bytecode BytecodeForCreateArguments(CreateArgumentsType type);
|
||||
static Bytecode BytecodeForDelete(LanguageMode language_mode);
|
||||
|
||||
static bool FitsInIdx8Operand(int value);
|
||||
static bool FitsInIdx8Operand(size_t value);
|
||||
|
@ -1429,15 +1429,76 @@ void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
|
||||
case Token::Value::VOID:
|
||||
VisitVoid(expr);
|
||||
break;
|
||||
case Token::Value::BIT_NOT:
|
||||
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:
|
||||
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) {
|
||||
DCHECK(expr->expression()->IsValidReferenceExpressionOrThis());
|
||||
|
||||
|
@ -51,6 +51,7 @@ class BytecodeGenerator : public AstVisitor {
|
||||
void VisitVoid(UnaryOperation* expr);
|
||||
void VisitTypeOf(UnaryOperation* expr);
|
||||
void VisitNot(UnaryOperation* expr);
|
||||
void VisitDelete(UnaryOperation* expr);
|
||||
|
||||
// Helper visitors which perform common operations.
|
||||
Register VisitArguments(ZoneList<Expression*>* arguments);
|
||||
|
@ -91,6 +91,8 @@ namespace interpreter {
|
||||
V(Dec, OperandType::kNone) \
|
||||
V(LogicalNot, OperandType::kNone) \
|
||||
V(TypeOf, OperandType::kNone) \
|
||||
V(DeletePropertyStrict, OperandType::kReg8) \
|
||||
V(DeletePropertySloppy, OperandType::kReg8) \
|
||||
\
|
||||
/* Call operations */ \
|
||||
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 a JSfunction or Callable in |callable| with the |receiver| and
|
||||
|
@ -85,6 +85,10 @@ class Interpreter {
|
||||
void DoCreateLiteral(Runtime::FunctionId function_id,
|
||||
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);
|
||||
|
||||
Isolate* isolate_;
|
||||
|
@ -1842,6 +1842,153 @@ TEST(UnaryOperators) {
|
||||
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(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++) {
|
||||
@ -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) {
|
||||
InitializedHandleScope handle_scope;
|
||||
BytecodeGeneratorHelper helper;
|
||||
|
@ -2335,3 +2335,166 @@ TEST(InterpreterConditional) {
|
||||
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.
|
||||
builder.LogicalNot().TypeOf();
|
||||
|
||||
// Emit delete
|
||||
builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT);
|
||||
|
||||
// Emit new.
|
||||
builder.New(reg, reg, 0);
|
||||
|
Loading…
Reference in New Issue
Block a user