From 95e26ec423fa5ac6375dc6950e8c31d494aba334 Mon Sep 17 00:00:00 2001 From: mythria Date: Wed, 28 Oct 2015 02:49:20 -0700 Subject: [PATCH] [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} --- src/compiler/bytecode-graph-builder.cc | 12 + src/interpreter/bytecode-array-builder.cc | 23 ++ src/interpreter/bytecode-array-builder.h | 5 + src/interpreter/bytecode-generator.cc | 65 ++++- src/interpreter/bytecode-generator.h | 1 + src/interpreter/bytecodes.h | 2 + src/interpreter/interpreter.cc | 31 +++ src/interpreter/interpreter.h | 4 + .../interpreter/test-bytecode-generator.cc | 224 ++++++++++++++++++ test/cctest/interpreter/test-interpreter.cc | 163 +++++++++++++ .../bytecode-array-builder-unittest.cc | 2 + 11 files changed, 530 insertions(+), 2 deletions(-) diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc index a1676019b9..be40706321 100644 --- a/src/compiler/bytecode-graph-builder.cc +++ b/src/compiler/bytecode-graph-builder.cc @@ -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(); diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index 93a86de5cd..2ce9d51377 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -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) { // 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(-1); +} + + // static bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) { return kMinUInt8 <= value && value <= kMaxUInt8; diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index 4ee7be2b5f..4ec50bb623 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -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); diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index 110a3e84dc..a8407294a7 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -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()); diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h index 2d730feeef..c153188ef0 100644 --- a/src/interpreter/bytecode-generator.h +++ b/src/interpreter/bytecode-generator.h @@ -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* arguments); diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index c220394950..f21f17c0da 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -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) \ diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index b139ab46b3..5d45fe3b25 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -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 // // Call a JSfunction or Callable in |callable| with the |receiver| and diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h index 6bd15078be..29a3c69638 100644 --- a/src/interpreter/interpreter.h +++ b/src/interpreter/interpreter.h @@ -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 handler_table); Isolate* isolate_; diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index 2dd5167cb2..8325b5b7db 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -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 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 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 vector = + i::NewTypeFeedbackVector(helper.isolate(), &feedback_spec); + + ExpectedSnippet 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 bytecode_array = + helper.MakeBytecode(snippets[i].code_snippet, "f"); + CheckBytecodeArrayEqual(snippets[i], bytecode_array); + } +} + + TEST(FunctionLiterals) { InitializedHandleScope handle_scope; BytecodeGeneratorHelper helper; diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index 76749e2fad..164959019a 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -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> 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 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 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> 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(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 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> 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(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(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 return_value = callable().ToHandleChecked(); + CHECK(return_value->SameValue(*test_global_delete[i].second)); + } +} diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index e1cda3d121..629e4bc135 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -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);