diff options
author | Myles Borins <mylesborins@google.com> | 2019-09-24 11:56:38 -0400 |
---|---|---|
committer | Myles Borins <myles.borins@gmail.com> | 2019-10-07 03:19:23 -0400 |
commit | f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2 (patch) | |
tree | f5edbccb3ffda2573d70a6e291e7157f290e0ae0 /deps/v8/src/interpreter/bytecode-generator.cc | |
parent | ffd22e81983056d09c064c59343a0e488236272d (diff) | |
download | android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.tar.gz android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.tar.bz2 android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.zip |
deps: update V8 to 7.8.279.9
PR-URL: https://github.com/nodejs/node/pull/29694
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Diffstat (limited to 'deps/v8/src/interpreter/bytecode-generator.cc')
-rw-r--r-- | deps/v8/src/interpreter/bytecode-generator.cc | 631 |
1 files changed, 495 insertions, 136 deletions
diff --git a/deps/v8/src/interpreter/bytecode-generator.cc b/deps/v8/src/interpreter/bytecode-generator.cc index d3b27b4375..6a0b02d852 100644 --- a/deps/v8/src/interpreter/bytecode-generator.cc +++ b/deps/v8/src/interpreter/bytecode-generator.cc @@ -14,7 +14,9 @@ #include "src/interpreter/bytecode-jump-table.h" #include "src/interpreter/bytecode-label.h" #include "src/interpreter/bytecode-register-allocator.h" +#include "src/interpreter/bytecode-register.h" #include "src/interpreter/control-flow-builders.h" +#include "src/logging/log.h" #include "src/objects/debug-objects.h" #include "src/objects/literal-objects-inl.h" #include "src/objects/objects-inl.h" @@ -915,39 +917,67 @@ class BytecodeGenerator::IteratorRecord final { Register next_; }; +class BytecodeGenerator::OptionalChainNullLabelScope final { + public: + explicit OptionalChainNullLabelScope(BytecodeGenerator* bytecode_generator) + : bytecode_generator_(bytecode_generator), + labels_(bytecode_generator->zone()) { + prev_ = bytecode_generator_->optional_chaining_null_labels_; + bytecode_generator_->optional_chaining_null_labels_ = &labels_; + } + + ~OptionalChainNullLabelScope() { + bytecode_generator_->optional_chaining_null_labels_ = prev_; + } + + BytecodeLabels* labels() { return &labels_; } + + private: + BytecodeGenerator* bytecode_generator_; + BytecodeLabels labels_; + BytecodeLabels* prev_; +}; + namespace { +template <typename PropertyT> +struct Accessors : public ZoneObject { + Accessors() : getter(nullptr), setter(nullptr) {} + PropertyT* getter; + PropertyT* setter; +}; + // A map from property names to getter/setter pairs allocated in the zone that // also provides a way of accessing the pairs in the order they were first // added so that the generated bytecode is always the same. +template <typename PropertyT> class AccessorTable - : public base::TemplateHashMap<Literal, ObjectLiteral::Accessors, + : public base::TemplateHashMap<Literal, Accessors<PropertyT>, bool (*)(void*, void*), ZoneAllocationPolicy> { public: explicit AccessorTable(Zone* zone) - : base::TemplateHashMap<Literal, ObjectLiteral::Accessors, + : base::TemplateHashMap<Literal, Accessors<PropertyT>, bool (*)(void*, void*), ZoneAllocationPolicy>( Literal::Match, ZoneAllocationPolicy(zone)), zone_(zone) {} - Iterator lookup(Literal* literal) { - Iterator it = find(literal, true, ZoneAllocationPolicy(zone_)); + Accessors<PropertyT>* LookupOrInsert(Literal* key) { + auto it = this->find(key, true, ZoneAllocationPolicy(zone_)); if (it->second == nullptr) { - it->second = new (zone_) ObjectLiteral::Accessors(); - ordered_accessors_.push_back({literal, it->second}); + it->second = new (zone_) Accessors<PropertyT>(); + ordered_accessors_.push_back({key, it->second}); } - return it; + return it->second; } - const std::vector<std::pair<Literal*, ObjectLiteral::Accessors*>>& + const std::vector<std::pair<Literal*, Accessors<PropertyT>*>>& ordered_accessors() { return ordered_accessors_; } private: - std::vector<std::pair<Literal*, ObjectLiteral::Accessors*>> - ordered_accessors_; + std::vector<std::pair<Literal*, Accessors<PropertyT>*>> ordered_accessors_; Zone* zone_; }; @@ -994,6 +1024,7 @@ BytecodeGenerator::BytecodeGenerator( execution_context_(nullptr), execution_result_(nullptr), incoming_new_target_or_generator_(), + optional_chaining_null_labels_(nullptr), dummy_feedback_slot_(feedback_spec(), FeedbackSlotKind::kCompareOp), generator_jump_table_(nullptr), suspend_count_(0), @@ -1012,7 +1043,7 @@ Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode( #ifdef DEBUG // Unoptimized compilation should be context-independent. Verify that we don't // access the native context by nulling it out during finalization. - SaveAndSwitchContext save(isolate, Context()); + NullContextScope null_context_scope(isolate); #endif AllocateDeferredConstants(isolate, script); @@ -1036,6 +1067,32 @@ Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode( return bytecode_array; } +Handle<ByteArray> BytecodeGenerator::FinalizeSourcePositionTable( + Isolate* isolate) { + DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); +#ifdef DEBUG + // Unoptimized compilation should be context-independent. Verify that we don't + // access the native context by nulling it out during finalization. + NullContextScope null_context_scope(isolate); +#endif + + Handle<ByteArray> source_position_table = + builder()->ToSourcePositionTable(isolate); + + LOG_CODE_EVENT(isolate, + CodeLinePosInfoRecordEvent( + info_->bytecode_array()->GetFirstBytecodeAddress(), + *source_position_table)); + + return source_position_table; +} + +#ifdef DEBUG +int BytecodeGenerator::CheckBytecodeMatches(Handle<BytecodeArray> bytecode) { + return builder()->CheckBytecodeMatches(bytecode); +} +#endif + void BytecodeGenerator::AllocateDeferredConstants(Isolate* isolate, Handle<Script> script) { // Build global declaration pair arrays. @@ -1383,8 +1440,9 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) { BuildVariableAssignment(variable, Token::INIT, HoleCheckMode::kElided); break; } - DCHECK_IMPLIES(decl->fun()->ShouldEagerCompile(), - IsInEagerLiterals(decl->fun(), *eager_inner_literals_)); + DCHECK_IMPLIES( + eager_inner_literals_ != nullptr && decl->fun()->ShouldEagerCompile(), + IsInEagerLiterals(decl->fun(), *eager_inner_literals_)); } void BytecodeGenerator::VisitModuleNamespaceImports() { @@ -1755,14 +1813,13 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { return; } - BytecodeLabel subject_null_label, subject_undefined_label; + BytecodeLabel subject_undefined_label; FeedbackSlot slot = feedback_spec()->AddForInSlot(); // Prepare the state for executing ForIn. builder()->SetExpressionAsStatementPosition(stmt->subject()); VisitForAccumulatorValue(stmt->subject()); - builder()->JumpIfUndefined(&subject_undefined_label); - builder()->JumpIfNull(&subject_null_label); + builder()->JumpIfUndefinedOrNull(&subject_undefined_label); Register receiver = register_allocator()->NewRegister(); builder()->ToObject(receiver); @@ -1804,7 +1861,6 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { builder()->StoreAccumulatorInRegister(index); loop_builder.JumpToHeader(loop_depth_); } - builder()->Bind(&subject_null_label); builder()->Bind(&subject_undefined_label); } @@ -1978,39 +2034,6 @@ bool BytecodeGenerator::ShouldOptimizeAsOneShot() const { info()->literal()->is_oneshot_iife(); } -void BytecodeGenerator::BuildPrivateClassMemberNameAssignment( - ClassLiteral::Property* property) { - DCHECK(property->is_private()); - switch (property->kind()) { - case ClassLiteral::Property::FIELD: { - // Create the private name symbols for fields during class - // evaluation and store them on the context. These will be - // used as keys later during instance or static initialization. - RegisterAllocationScope private_name_register_scope(this); - Register private_name = register_allocator()->NewRegister(); - VisitForRegisterValue(property->key(), private_name); - builder() - ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName()) - .StoreAccumulatorInRegister(private_name) - .CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name); - DCHECK_NOT_NULL(property->private_name_var()); - BuildVariableAssignment(property->private_name_var(), Token::INIT, - HoleCheckMode::kElided); - break; - } - case ClassLiteral::Property::METHOD: { - // Create the closures for private methods. - VisitForAccumulatorValue(property->value()); - BuildVariableAssignment(property->private_name_var(), Token::INIT, - HoleCheckMode::kElided); - break; - } - default: - // TODO(joyee): Private accessors are not yet supported. - UNREACHABLE(); - } -} - void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) { size_t class_boilerplate_entry = builder()->AllocateDeferredConstantPoolEntry(); @@ -2019,6 +2042,7 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) { VisitDeclarations(expr->scope()->declarations()); Register class_constructor = register_allocator()->NewRegister(); + AccessorTable<ClassLiteral::Property> private_accessors(zone()); { RegisterAllocationScope register_scope(this); RegisterList args = register_allocator()->NewGrowableRegisterList(); @@ -2076,7 +2100,44 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) { } if (property->is_private()) { - BuildPrivateClassMemberNameAssignment(property); + // Assign private class member's name variables. + switch (property->kind()) { + case ClassLiteral::Property::FIELD: { + // Create the private name symbols for fields during class + // evaluation and store them on the context. These will be + // used as keys later during instance or static initialization. + RegisterAllocationScope private_name_register_scope(this); + Register private_name = register_allocator()->NewRegister(); + VisitForRegisterValue(property->key(), private_name); + builder() + ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName()) + .StoreAccumulatorInRegister(private_name) + .CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name); + DCHECK_NOT_NULL(property->private_name_var()); + BuildVariableAssignment(property->private_name_var(), Token::INIT, + HoleCheckMode::kElided); + break; + } + case ClassLiteral::Property::METHOD: { + // Create the closures for private methods. + VisitForAccumulatorValue(property->value()); + BuildVariableAssignment(property->private_name_var(), Token::INIT, + HoleCheckMode::kElided); + break; + } + case ClassLiteral::Property::GETTER: { + Literal* key = property->key()->AsLiteral(); + DCHECK_NULL(private_accessors.LookupOrInsert(key)->getter); + private_accessors.LookupOrInsert(key)->getter = property; + break; + } + case ClassLiteral::Property::SETTER: { + Literal* key = property->key()->AsLiteral(); + DCHECK_NULL(private_accessors.LookupOrInsert(key)->setter); + private_accessors.LookupOrInsert(key)->setter = property; + break; + } + } // The private fields are initialized in the initializer function and // the private brand for the private methods are initialized in the // constructor instead. @@ -2122,6 +2183,37 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) { .CallRuntime(Runtime::kCreatePrivateNameSymbol, brand); BuildVariableAssignment(expr->scope()->brand(), Token::INIT, HoleCheckMode::kElided); + + // Store the home object for any private methods that need + // them. We do this here once the prototype and brand symbol has + // been created. Private accessors have their home object set later + // when they are defined. + for (int i = 0; i < expr->properties()->length(); i++) { + RegisterAllocationScope register_scope(this); + ClassLiteral::Property* property = expr->properties()->at(i); + if (property->NeedsHomeObjectOnClassPrototype()) { + Register func = register_allocator()->NewRegister(); + BuildVariableLoad(property->private_name_var(), HoleCheckMode::kElided); + builder()->StoreAccumulatorInRegister(func); + VisitSetHomeObject(func, prototype, property); + } + } + + // Define accessors, using only a single call to the runtime for each pair + // of corresponding getters and setters. + for (auto accessors : private_accessors.ordered_accessors()) { + RegisterAllocationScope inner_register_scope(this); + RegisterList accessors_reg = register_allocator()->NewRegisterList(2); + ClassLiteral::Property* getter = accessors.second->getter; + ClassLiteral::Property* setter = accessors.second->setter; + VisitLiteralAccessor(prototype, getter, accessors_reg[0]); + VisitLiteralAccessor(prototype, setter, accessors_reg[1]); + builder()->CallRuntime(Runtime::kCreatePrivateAccessors, accessors_reg); + Variable* var = getter != nullptr ? getter->private_name_var() + : setter->private_name_var(); + DCHECK_NOT_NULL(var); + BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kElided); + } } if (expr->instance_members_initializer_function() != nullptr) { @@ -2241,12 +2333,13 @@ void BytecodeGenerator::VisitInitializeClassMembersStatement( } } -void BytecodeGenerator::BuildThrowPrivateMethodWriteError( - const AstRawString* name) { +void BytecodeGenerator::BuildInvalidPropertyAccess(MessageTemplate tmpl, + Property* property) { RegisterAllocationScope register_scope(this); + const AstRawString* name = property->key()->AsVariableProxy()->raw_name(); RegisterList args = register_allocator()->NewRegisterList(2); builder() - ->LoadLiteral(Smi::FromEnum(MessageTemplate::kInvalidPrivateMethodWrite)) + ->LoadLiteral(Smi::FromEnum(tmpl)) .StoreAccumulatorInRegister(args[0]) .LoadLiteral(name) .StoreAccumulatorInRegister(args[1]) @@ -2437,7 +2530,7 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { } // Store computed values into the literal. - AccessorTable accessor_table(zone()); + AccessorTable<ObjectLiteral::Property> accessor_table(zone()); for (; property_index < expr->properties()->length(); property_index++) { ObjectLiteral::Property* property = expr->properties()->at(property_index); if (property->is_computed_name()) break; @@ -2506,12 +2599,12 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { } case ObjectLiteral::Property::GETTER: if (property->emit_store()) { - accessor_table.lookup(key)->second->getter = property; + accessor_table.LookupOrInsert(key)->getter = property; } break; case ObjectLiteral::Property::SETTER: if (property->emit_store()) { - accessor_table.lookup(key)->second->setter = property; + accessor_table.LookupOrInsert(key)->setter = property; } break; } @@ -2524,8 +2617,8 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { RegisterList args = register_allocator()->NewRegisterList(5); builder()->MoveRegister(literal, args[0]); VisitForRegisterValue(accessors.first, args[1]); - VisitObjectLiteralAccessor(literal, accessors.second->getter, args[2]); - VisitObjectLiteralAccessor(literal, accessors.second->setter, args[3]); + VisitLiteralAccessor(literal, accessors.second->getter, args[2]); + VisitLiteralAccessor(literal, accessors.second->setter, args[3]); builder() ->LoadLiteral(Smi::FromInt(NONE)) .StoreAccumulatorInRegister(args[4]) @@ -3017,6 +3110,7 @@ void BytecodeGenerator::BuildThrowIfHole(Variable* variable) { void BytecodeGenerator::BuildHoleCheckForVariableAssignment(Variable* variable, Token::Value op) { + DCHECK(!IsPrivateMethodOrAccessorVariableMode(variable->mode())); if (variable->is_this() && variable->mode() == VariableMode::kConst && op == Token::INIT) { // Perform an initialization check for 'this'. 'this' variable is the @@ -3201,10 +3295,10 @@ BytecodeGenerator::AssignmentLhsData::NamedSuperProperty( } // static BytecodeGenerator::AssignmentLhsData -BytecodeGenerator::AssignmentLhsData::PrivateMethod(Register object, - const AstRawString* name) { - return AssignmentLhsData(PRIVATE_METHOD, nullptr, RegisterList(), object, - Register(), nullptr, name); +BytecodeGenerator::AssignmentLhsData::PrivateMethodOrAccessor( + AssignType type, Property* property) { + return AssignmentLhsData(type, property, RegisterList(), Register(), + Register(), nullptr, nullptr); } // static BytecodeGenerator::AssignmentLhsData @@ -3237,12 +3331,12 @@ BytecodeGenerator::AssignmentLhsData BytecodeGenerator::PrepareAssignmentLhs( Register key = VisitForRegisterValue(property->key()); return AssignmentLhsData::KeyedProperty(object, key); } - case PRIVATE_METHOD: { + case PRIVATE_METHOD: + case PRIVATE_GETTER_ONLY: + case PRIVATE_SETTER_ONLY: + case PRIVATE_GETTER_AND_SETTER: { DCHECK(!property->IsSuperAccess()); - AccumulatorPreservingScope scope(this, accumulator_preserving_mode); - Register object = VisitForRegisterValue(property->obj()); - const AstRawString* name = property->key()->AsVariableProxy()->raw_name(); - return AssignmentLhsData::PrivateMethod(object, name); + return AssignmentLhsData::PrivateMethodOrAccessor(assign_type, property); } case NAMED_SUPER_PROPERTY: { AccumulatorPreservingScope scope(this, accumulator_preserving_mode); @@ -3316,8 +3410,7 @@ void BytecodeGenerator::BuildFinalizeIteration( ast_string_constants()->return_string(), feedback_index(feedback_spec()->AddLoadICSlot())) .StoreAccumulatorInRegister(method) - .JumpIfUndefined(iterator_is_done.New()) - .JumpIfNull(iterator_is_done.New()); + .JumpIfUndefinedOrNull(iterator_is_done.New()); { RegisterAllocationScope register_scope(this); @@ -3625,22 +3718,6 @@ void BytecodeGenerator::BuildDestructuringObjectAssignment( LookupHoistingMode lookup_hoisting_mode) { RegisterAllocationScope scope(this); - // if (value === null || value === undefined) - // throw new TypeError(kNonCoercible); - // - // TODO(leszeks): Eliminate check if value is known to be non-null (e.g. - // an object literal). - BytecodeLabel is_null_or_undefined, not_null_or_undefined; - builder() - ->JumpIfNull(&is_null_or_undefined) - .JumpIfNotUndefined(¬_null_or_undefined); - - { - builder()->Bind(&is_null_or_undefined); - builder()->SetExpressionPosition(pattern); - builder()->CallRuntime(Runtime::kThrowPatternAssignmentNonCoercible); - } - // Store the assignment value in a register. Register value; RegisterList rest_runtime_callargs; @@ -3651,7 +3728,34 @@ void BytecodeGenerator::BuildDestructuringObjectAssignment( } else { value = register_allocator()->NewRegister(); } - builder()->Bind(¬_null_or_undefined).StoreAccumulatorInRegister(value); + builder()->StoreAccumulatorInRegister(value); + + // if (value === null || value === undefined) + // throw new TypeError(kNonCoercible); + // + // Since the first property access on null/undefined will also trigger a + // TypeError, we can elide this check. The exception is when there are no + // properties and no rest property (this is an empty literal), or when the + // first property is a computed name and accessing it can have side effects. + // + // TODO(leszeks): Also eliminate this check if the value is known to be + // non-null (e.g. an object literal). + if (pattern->properties()->is_empty() || + (pattern->properties()->at(0)->is_computed_name() && + pattern->properties()->at(0)->kind() != ObjectLiteralProperty::SPREAD)) { + BytecodeLabel is_null_or_undefined, not_null_or_undefined; + builder() + ->JumpIfUndefinedOrNull(&is_null_or_undefined) + .Jump(¬_null_or_undefined); + + { + builder()->Bind(&is_null_or_undefined); + builder()->SetExpressionPosition(pattern); + builder()->CallRuntime(Runtime::kThrowPatternAssignmentNonCoercible, + value); + } + builder()->Bind(¬_null_or_undefined); + } int i = 0; for (ObjectLiteralProperty* pattern_property : *pattern->properties()) { @@ -3799,7 +3903,27 @@ void BytecodeGenerator::BuildAssignment( break; } case PRIVATE_METHOD: { - BuildThrowPrivateMethodWriteError(lhs_data.name()); + BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite, + lhs_data.expr()->AsProperty()); + break; + } + case PRIVATE_GETTER_ONLY: { + BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess, + lhs_data.expr()->AsProperty()); + break; + } + case PRIVATE_SETTER_ONLY: + case PRIVATE_GETTER_AND_SETTER: { + Register value = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(value); + Property* property = lhs_data.expr()->AsProperty(); + Register object = VisitForRegisterValue(property->obj()); + Register key = VisitForRegisterValue(property->key()); + BuildPrivateBrandCheck(property, object); + BuildPrivateSetterAccess(object, key, value); + if (!execution_result()->IsEffect()) { + builder()->LoadAccumulatorWithRegister(value); + } break; } } @@ -3847,11 +3971,16 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) { lhs_data.super_property_args().Truncate(3)); break; } - case PRIVATE_METHOD: { - BuildThrowPrivateMethodWriteError(lhs_data.name()); + case PRIVATE_METHOD: + case PRIVATE_GETTER_ONLY: + case PRIVATE_SETTER_ONLY: + case PRIVATE_GETTER_AND_SETTER: { + // ({ #foo: name } = obj) is currently syntactically invalid. + UNREACHABLE(); break; } } + BinaryOperation* binop = expr->AsCompoundAssignment()->binary_operation(); FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot(); if (expr->value()->IsSmiLiteral()) { @@ -4284,7 +4413,14 @@ void BytecodeGenerator::VisitThrow(Throw* expr) { } void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) { + if (property->is_optional_chain_link()) { + DCHECK_NOT_NULL(optional_chaining_null_labels_); + builder()->LoadAccumulatorWithRegister(obj).JumpIfUndefinedOrNull( + optional_chaining_null_labels_->New()); + } + AssignType property_kind = Property::GetAssignType(property); + switch (property_kind) { case NON_PROPERTY: UNREACHABLE(); @@ -4308,18 +4444,20 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) { case KEYED_SUPER_PROPERTY: VisitKeyedSuperPropertyLoad(property, Register::invalid_value()); break; + case PRIVATE_SETTER_ONLY: { + BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess, + property); + break; + } + case PRIVATE_GETTER_ONLY: + case PRIVATE_GETTER_AND_SETTER: { + Register key = VisitForRegisterValue(property->key()); + BuildPrivateBrandCheck(property, obj); + BuildPrivateGetterAccess(obj, key); + break; + } case PRIVATE_METHOD: { - Variable* private_name = property->key()->AsVariableProxy()->var(); - - // Perform the brand check. - DCHECK(private_name->requires_brand_check()); - ClassScope* scope = private_name->scope()->AsClassScope(); - Variable* brand = scope->brand(); - BuildVariableLoadForAccumulatorValue(brand, HoleCheckMode::kElided); - builder()->SetExpressionPosition(property); - builder()->LoadKeyedProperty( - obj, feedback_index(feedback_spec()->AddKeyedLoadICSlot())); - + BuildPrivateBrandCheck(property, obj); // In the case of private methods, property->key() is the function to be // loaded (stored in a context slot), so load this directly. VisitForAccumulatorValue(property->key()); @@ -4328,6 +4466,48 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) { } } +void BytecodeGenerator::BuildPrivateGetterAccess(Register object, + Register accessor_pair) { + RegisterAllocationScope scope(this); + Register accessor = register_allocator()->NewRegister(); + RegisterList args = register_allocator()->NewRegisterList(1); + + builder() + ->CallRuntime(Runtime::kLoadPrivateGetter, accessor_pair) + .StoreAccumulatorInRegister(accessor) + .MoveRegister(object, args[0]) + .CallProperty(accessor, args, + feedback_index(feedback_spec()->AddCallICSlot())); +} + +void BytecodeGenerator::BuildPrivateSetterAccess(Register object, + Register accessor_pair, + Register value) { + RegisterAllocationScope scope(this); + Register accessor = register_allocator()->NewRegister(); + RegisterList args = register_allocator()->NewRegisterList(2); + + builder() + ->CallRuntime(Runtime::kLoadPrivateSetter, accessor_pair) + .StoreAccumulatorInRegister(accessor) + .MoveRegister(object, args[0]) + .MoveRegister(value, args[1]) + .CallProperty(accessor, args, + feedback_index(feedback_spec()->AddCallICSlot())); +} + +void BytecodeGenerator::BuildPrivateBrandCheck(Property* property, + Register object) { + Variable* private_name = property->key()->AsVariableProxy()->var(); + DCHECK(private_name->requires_brand_check()); + ClassScope* scope = private_name->scope()->AsClassScope(); + Variable* brand = scope->brand(); + BuildVariableLoadForAccumulatorValue(brand, HoleCheckMode::kElided); + builder()->SetExpressionPosition(property); + builder()->LoadKeyedProperty( + object, feedback_index(feedback_spec()->AddKeyedLoadICSlot())); +} + void BytecodeGenerator::VisitPropertyLoadForRegister(Register obj, Property* expr, Register destination) { @@ -4376,6 +4556,16 @@ void BytecodeGenerator::VisitKeyedSuperPropertyLoad(Property* property, } } +void BytecodeGenerator::VisitOptionalChain(OptionalChain* expr) { + BytecodeLabel done; + OptionalChainNullLabelScope label_scope(this); + VisitForAccumulatorValue(expr->expression()); + builder()->Jump(&done); + label_scope.labels()->Bind(builder()); + builder()->LoadUndefined(); + builder()->Bind(&done); +} + void BytecodeGenerator::VisitProperty(Property* expr) { AssignType property_kind = Property::GetAssignType(expr); if (property_kind != NAMED_SUPER_PROPERTY && @@ -4509,6 +4699,12 @@ void BytecodeGenerator::VisitCall(Call* expr) { UNREACHABLE(); } + if (expr->is_optional_chain_link()) { + DCHECK_NOT_NULL(optional_chaining_null_labels_); + builder()->LoadAccumulatorWithRegister(callee).JumpIfUndefinedOrNull( + optional_chaining_null_labels_->New()); + } + // Evaluate all arguments to the function call and store in sequential args // registers. VisitArguments(expr->arguments(), &args); @@ -4770,6 +4966,29 @@ void BytecodeGenerator::VisitDelete(UnaryOperation* unary) { Register object = VisitForRegisterValue(property->obj()); VisitForAccumulatorValue(property->key()); builder()->Delete(object, language_mode()); + } else if (expr->IsOptionalChain()) { + Expression* expr_inner = expr->AsOptionalChain()->expression(); + if (expr_inner->IsProperty()) { + Property* property = expr_inner->AsProperty(); + DCHECK(!property->IsPrivateReference()); + BytecodeLabel done; + OptionalChainNullLabelScope label_scope(this); + VisitForAccumulatorValue(property->obj()); + if (property->is_optional_chain_link()) { + builder()->JumpIfUndefinedOrNull(label_scope.labels()->New()); + } + Register object = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(object); + VisitForAccumulatorValue(property->key()); + builder()->Delete(object, language_mode()); + builder()->Jump(&done); + label_scope.labels()->Bind(builder()); + builder()->LoadTrue(); + builder()->Bind(&done); + } else { + VisitForEffect(expr); + builder()->LoadTrue(); + } } else if (expr->IsVariableProxy() && !expr->AsVariableProxy()->is_new_target()) { // Delete of an unqualified identifier is allowed in sloppy mode but is @@ -4875,8 +5094,25 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) { break; } case PRIVATE_METHOD: { - BuildThrowPrivateMethodWriteError( - property->key()->AsVariableProxy()->raw_name()); + BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite, + property); + return; + } + case PRIVATE_GETTER_ONLY: { + BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess, + property); + return; + } + case PRIVATE_SETTER_ONLY: { + BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess, + property); + return; + } + case PRIVATE_GETTER_AND_SETTER: { + object = VisitForRegisterValue(property->obj()); + key = VisitForRegisterValue(property->key()); + BuildPrivateBrandCheck(property, object); + BuildPrivateGetterAccess(object, key); break; } } @@ -4945,9 +5181,18 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) { .CallRuntime(Runtime::kStoreKeyedToSuper, super_property_args); break; } + case PRIVATE_SETTER_ONLY: + case PRIVATE_GETTER_ONLY: case PRIVATE_METHOD: { - BuildThrowPrivateMethodWriteError( - property->key()->AsVariableProxy()->raw_name()); + UNREACHABLE(); + } + case PRIVATE_GETTER_AND_SETTER: { + Register value = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(value); + BuildPrivateSetterAccess(object, key, value); + if (!execution_result()->IsEffect()) { + builder()->LoadAccumulatorWithRegister(value); + } break; } } @@ -4969,6 +5214,9 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) { case Token::AND: VisitLogicalAndExpression(binop); break; + case Token::NULLISH: + VisitNullishExpression(binop); + break; default: VisitArithmeticExpression(binop); break; @@ -4986,6 +5234,9 @@ void BytecodeGenerator::VisitNaryOperation(NaryOperation* expr) { case Token::AND: VisitNaryLogicalAndExpression(expr); break; + case Token::NULLISH: + VisitNaryNullishExpression(expr); + break; default: VisitNaryArithmeticExpression(expr); break; @@ -5128,39 +5379,37 @@ void BytecodeGenerator::VisitImportCallExpression(ImportCallExpression* expr) { } void BytecodeGenerator::BuildGetIterator(IteratorType hint) { - RegisterList args = register_allocator()->NewRegisterList(1); - Register method = register_allocator()->NewRegister(); - Register obj = args[0]; - if (hint == IteratorType::kAsync) { + RegisterAllocationScope scope(this); + + Register obj = register_allocator()->NewRegister(); + Register method = register_allocator()->NewRegister(); + // Set method to GetMethod(obj, @@asyncIterator) builder()->StoreAccumulatorInRegister(obj).LoadAsyncIteratorProperty( obj, feedback_index(feedback_spec()->AddLoadICSlot())); - BytecodeLabel async_iterator_undefined, async_iterator_null, done; - // TODO(ignition): Add a single opcode for JumpIfNullOrUndefined - builder()->JumpIfUndefined(&async_iterator_undefined); - builder()->JumpIfNull(&async_iterator_null); + BytecodeLabel async_iterator_undefined, done; + builder()->JumpIfUndefinedOrNull(&async_iterator_undefined); // Let iterator be Call(method, obj) builder()->StoreAccumulatorInRegister(method).CallProperty( - method, args, feedback_index(feedback_spec()->AddCallICSlot())); + method, RegisterList(obj), + feedback_index(feedback_spec()->AddCallICSlot())); // If Type(iterator) is not Object, throw a TypeError exception. builder()->JumpIfJSReceiver(&done); builder()->CallRuntime(Runtime::kThrowSymbolAsyncIteratorInvalid); builder()->Bind(&async_iterator_undefined); - builder()->Bind(&async_iterator_null); // If method is undefined, // Let syncMethod be GetMethod(obj, @@iterator) builder() - ->LoadIteratorProperty(obj, - feedback_index(feedback_spec()->AddLoadICSlot())) + ->GetIterator(obj, feedback_index(feedback_spec()->AddLoadICSlot())) .StoreAccumulatorInRegister(method); // Let syncIterator be Call(syncMethod, obj) - builder()->CallProperty(method, args, + builder()->CallProperty(method, RegisterList(obj), feedback_index(feedback_spec()->AddCallICSlot())); // Return CreateAsyncFromSyncIterator(syncIterator) @@ -5171,16 +5420,22 @@ void BytecodeGenerator::BuildGetIterator(IteratorType hint) { builder()->Bind(&done); } else { - // Let method be GetMethod(obj, @@iterator). - builder() - ->StoreAccumulatorInRegister(obj) - .LoadIteratorProperty(obj, - feedback_index(feedback_spec()->AddLoadICSlot())) - .StoreAccumulatorInRegister(method); + { + RegisterAllocationScope scope(this); - // Let iterator be Call(method, obj). - builder()->CallProperty(method, args, - feedback_index(feedback_spec()->AddCallICSlot())); + Register obj = register_allocator()->NewRegister(); + Register method = register_allocator()->NewRegister(); + + // Let method be GetMethod(obj, @@iterator). + builder() + ->StoreAccumulatorInRegister(obj) + .GetIterator(obj, feedback_index(feedback_spec()->AddLoadICSlot())) + .StoreAccumulatorInRegister(method); + + // Let iterator be Call(method, obj). + builder()->CallProperty(method, RegisterList(obj), + feedback_index(feedback_spec()->AddCallICSlot())); + } // If Type(iterator) is not Object, throw a TypeError exception. BytecodeLabel no_type_error; @@ -5241,8 +5496,7 @@ void BytecodeGenerator::BuildCallIteratorMethod(Register iterator, FeedbackSlot slot = feedback_spec()->AddLoadICSlot(); builder() ->LoadNamedProperty(iterator, method_name, feedback_index(slot)) - .JumpIfUndefined(if_notcalled->New()) - .JumpIfNull(if_notcalled->New()) + .JumpIfUndefinedOrNull(if_notcalled->New()) .StoreAccumulatorInRegister(method) .CallProperty(method, receiver_and_args, feedback_index(feedback_spec()->AddCallICSlot())) @@ -5375,14 +5629,16 @@ void BytecodeGenerator::VisitNaryCommaExpression(NaryOperation* expr) { void BytecodeGenerator::VisitLogicalTestSubExpression( Token::Value token, Expression* expr, BytecodeLabels* then_labels, BytecodeLabels* else_labels, int coverage_slot) { - DCHECK(token == Token::OR || token == Token::AND); + DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH); BytecodeLabels test_next(zone()); if (token == Token::OR) { VisitForTest(expr, then_labels, &test_next, TestFallthrough::kElse); - } else { - DCHECK_EQ(Token::AND, token); + } else if (token == Token::AND) { VisitForTest(expr, &test_next, else_labels, TestFallthrough::kThen); + } else { + DCHECK_EQ(Token::NULLISH, token); + VisitForNullishTest(expr, then_labels, &test_next, else_labels); } test_next.Bind(builder()); @@ -5392,7 +5648,7 @@ void BytecodeGenerator::VisitLogicalTestSubExpression( void BytecodeGenerator::VisitLogicalTest(Token::Value token, Expression* left, Expression* right, int right_coverage_slot) { - DCHECK(token == Token::OR || token == Token::AND); + DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH); TestResultScope* test_result = execution_result()->AsTest(); BytecodeLabels* then_labels = test_result->then_labels(); BytecodeLabels* else_labels = test_result->else_labels(); @@ -5407,7 +5663,7 @@ void BytecodeGenerator::VisitLogicalTest(Token::Value token, Expression* left, void BytecodeGenerator::VisitNaryLogicalTest( Token::Value token, NaryOperation* expr, const NaryCodeCoverageSlots* coverage_slots) { - DCHECK(token == Token::OR || token == Token::AND); + DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH); DCHECK_GT(expr->subsequent_length(), 0); TestResultScope* test_result = execution_result()->AsTest(); @@ -5463,6 +5719,27 @@ bool BytecodeGenerator::VisitLogicalAndSubExpression(Expression* expr, return false; } +bool BytecodeGenerator::VisitNullishSubExpression(Expression* expr, + BytecodeLabels* end_labels, + int coverage_slot) { + if (expr->IsLiteralButNotNullOrUndefined()) { + VisitForAccumulatorValue(expr); + end_labels->Bind(builder()); + return true; + } else if (!expr->IsNullOrUndefinedLiteral()) { + VisitForAccumulatorValue(expr); + BytecodeLabel is_null_or_undefined; + builder() + ->JumpIfUndefinedOrNull(&is_null_or_undefined) + .Jump(end_labels->New()); + builder()->Bind(&is_null_or_undefined); + } + + BuildIncrementBlockCoverageCounterIfEnabled(coverage_slot); + + return false; +} + void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) { Expression* left = binop->left(); Expression* right = binop->right(); @@ -5585,6 +5862,68 @@ void BytecodeGenerator::VisitNaryLogicalAndExpression(NaryOperation* expr) { } } +void BytecodeGenerator::VisitNullishExpression(BinaryOperation* binop) { + Expression* left = binop->left(); + Expression* right = binop->right(); + + int right_coverage_slot = + AllocateBlockCoverageSlotIfEnabled(binop, SourceRangeKind::kRight); + + if (execution_result()->IsTest()) { + TestResultScope* test_result = execution_result()->AsTest(); + if (left->IsLiteralButNotNullOrUndefined() && left->ToBooleanIsTrue()) { + builder()->Jump(test_result->NewThenLabel()); + } else if (left->IsNullOrUndefinedLiteral() && + right->IsNullOrUndefinedLiteral()) { + BuildIncrementBlockCoverageCounterIfEnabled(right_coverage_slot); + builder()->Jump(test_result->NewElseLabel()); + } else { + VisitLogicalTest(Token::NULLISH, left, right, right_coverage_slot); + } + test_result->SetResultConsumedByTest(); + } else { + BytecodeLabels end_labels(zone()); + if (VisitNullishSubExpression(left, &end_labels, right_coverage_slot)) { + return; + } + VisitForAccumulatorValue(right); + end_labels.Bind(builder()); + } +} + +void BytecodeGenerator::VisitNaryNullishExpression(NaryOperation* expr) { + Expression* first = expr->first(); + DCHECK_GT(expr->subsequent_length(), 0); + + NaryCodeCoverageSlots coverage_slots(this, expr); + + if (execution_result()->IsTest()) { + TestResultScope* test_result = execution_result()->AsTest(); + if (first->IsLiteralButNotNullOrUndefined() && first->ToBooleanIsTrue()) { + builder()->Jump(test_result->NewThenLabel()); + } else { + VisitNaryLogicalTest(Token::NULLISH, expr, &coverage_slots); + } + test_result->SetResultConsumedByTest(); + } else { + BytecodeLabels end_labels(zone()); + if (VisitNullishSubExpression(first, &end_labels, + coverage_slots.GetSlotFor(0))) { + return; + } + for (size_t i = 0; i < expr->subsequent_length() - 1; ++i) { + if (VisitNullishSubExpression(expr->subsequent(i), &end_labels, + coverage_slots.GetSlotFor(i + 1))) { + return; + } + } + // We have to visit the last value even if it's nullish, because we need its + // actual value. + VisitForAccumulatorValue(expr->subsequent(expr->subsequent_length() - 1)); + end_labels.Bind(builder()); + } +} + void BytecodeGenerator::BuildNewLocalActivationContext() { ValueResultScope value_execution_result(this); Scope* scope = closure_scope(); @@ -5682,8 +6021,9 @@ void BytecodeGenerator::BuildNewLocalCatchContext(Scope* scope) { builder()->CreateCatchContext(exception, scope); } -void BytecodeGenerator::VisitObjectLiteralAccessor( - Register home_object, ObjectLiteralProperty* property, Register value_out) { +void BytecodeGenerator::VisitLiteralAccessor(Register home_object, + LiteralProperty* property, + Register value_out) { if (property == nullptr) { builder()->LoadNull().StoreAccumulatorInRegister(value_out); } else { @@ -5929,6 +6269,25 @@ void BytecodeGenerator::VisitForTest(Expression* expr, } } +// Visits the expression |expr| for testing its nullish value and jumping to the +// |then| or |other| label depending on value and short-circuit semantics +void BytecodeGenerator::VisitForNullishTest(Expression* expr, + BytecodeLabels* then_labels, + BytecodeLabels* test_next_labels, + BytecodeLabels* else_labels) { + // Nullish short circuits on undefined or null, otherwise we fall back to + // BuildTest with no fallthrough. + // TODO(joshualitt): We should do this in a TestResultScope. + TypeHint type_hint = VisitForAccumulatorValue(expr); + ToBooleanMode mode = ToBooleanModeFromTypeHint(type_hint); + + // Skip the nullish shortcircuit if we already have a boolean. + if (mode != ToBooleanMode::kAlreadyBoolean) { + builder()->JumpIfUndefinedOrNull(test_next_labels->New()); + } + BuildTest(mode, then_labels, else_labels, TestFallthrough::kNone); +} + void BytecodeGenerator::VisitInSameTestExecutionScope(Expression* expr) { DCHECK(execution_result()->IsTest()); { |