diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index 9536df172d..001ebfd0cf 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -3005,96 +3005,104 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { BuildCreateObjectLiteral(literal, flags, entry); } - // Store computed values into the literal. - AccessorTable 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; - if (!clone_object_spread && property->IsCompileTimeValue()) continue; + // If we used CloneObject for the first element is spread case, we already + // copied accessors. Therefore skip the static initialization and treat all + // properties after the spread as dynamic. + // TOOD(v8:9888): Use new Define ICs instead of Set ICs in the clone object + // spread case. + if (!clone_object_spread) { + // Store computed values into the literal. + AccessorTable 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; + if (property->IsCompileTimeValue()) continue; - RegisterAllocationScope inner_register_scope(this); - Literal* key = property->key()->AsLiteral(); - switch (property->kind()) { - case ObjectLiteral::Property::SPREAD: - UNREACHABLE(); - case ObjectLiteral::Property::CONSTANT: - case ObjectLiteral::Property::MATERIALIZED_LITERAL: - DCHECK(clone_object_spread || !property->value()->IsCompileTimeValue()); - V8_FALLTHROUGH; - case ObjectLiteral::Property::COMPUTED: { - // It is safe to use [[Put]] here because the boilerplate already - // contains computed properties with an uninitialized value. - if (key->IsStringLiteral()) { - DCHECK(key->IsPropertyName()); - object_literal_context_scope.SetEnteredIf( - property->value()->IsConciseMethodDefinition()); - if (property->emit_store()) { - builder()->SetExpressionPosition(property->value()); - VisitForAccumulatorValue(property->value()); - FeedbackSlot slot = feedback_spec()->AddStoreOwnICSlot(); - builder()->StoreNamedOwnProperty(literal, key->AsRawPropertyName(), - feedback_index(slot)); + RegisterAllocationScope inner_register_scope(this); + Literal* key = property->key()->AsLiteral(); + switch (property->kind()) { + case ObjectLiteral::Property::SPREAD: + case ObjectLiteral::Property::CONSTANT: + UNREACHABLE(); + case ObjectLiteral::Property::MATERIALIZED_LITERAL: + DCHECK(!property->value()->IsCompileTimeValue()); + V8_FALLTHROUGH; + case ObjectLiteral::Property::COMPUTED: { + // It is safe to use [[Put]] here because the boilerplate already + // contains computed properties with an uninitialized value. + if (key->IsStringLiteral()) { + DCHECK(key->IsPropertyName()); + object_literal_context_scope.SetEnteredIf( + property->value()->IsConciseMethodDefinition()); + if (property->emit_store()) { + builder()->SetExpressionPosition(property->value()); + VisitForAccumulatorValue(property->value()); + FeedbackSlot slot = feedback_spec()->AddStoreOwnICSlot(); + builder()->StoreNamedOwnProperty( + literal, key->AsRawPropertyName(), feedback_index(slot)); + } else { + builder()->SetExpressionPosition(property->value()); + VisitForEffect(property->value()); + } } else { + RegisterList args = register_allocator()->NewRegisterList(3); + + builder()->MoveRegister(literal, args[0]); + builder()->SetExpressionPosition(property->key()); + VisitForRegisterValue(property->key(), args[1]); + + object_literal_context_scope.SetEnteredIf( + property->value()->IsConciseMethodDefinition()); builder()->SetExpressionPosition(property->value()); - VisitForEffect(property->value()); + VisitForRegisterValue(property->value(), args[2]); + if (property->emit_store()) { + builder()->CallRuntime(Runtime::kSetKeyedProperty, args); + } } - } else { - RegisterList args = register_allocator()->NewRegisterList(3); - + break; + } + case ObjectLiteral::Property::PROTOTYPE: { + // __proto__:null is handled by CreateObjectLiteral. + if (property->IsNullPrototype()) break; + DCHECK(property->emit_store()); + DCHECK(!property->NeedsSetFunctionName()); + RegisterList args = register_allocator()->NewRegisterList(2); builder()->MoveRegister(literal, args[0]); - builder()->SetExpressionPosition(property->key()); - VisitForRegisterValue(property->key(), args[1]); - - object_literal_context_scope.SetEnteredIf( - property->value()->IsConciseMethodDefinition()); + object_literal_context_scope.SetEnteredIf(false); builder()->SetExpressionPosition(property->value()); - VisitForRegisterValue(property->value(), args[2]); + VisitForRegisterValue(property->value(), args[1]); + builder()->CallRuntime(Runtime::kInternalSetPrototype, args); + break; + } + case ObjectLiteral::Property::GETTER: if (property->emit_store()) { - builder()->CallRuntime(Runtime::kSetKeyedProperty, args); + accessor_table.LookupOrInsert(key)->getter = property; } - } - break; + break; + case ObjectLiteral::Property::SETTER: + if (property->emit_store()) { + accessor_table.LookupOrInsert(key)->setter = property; + } + break; } - case ObjectLiteral::Property::PROTOTYPE: { - // __proto__:null is handled by CreateObjectLiteral. - if (property->IsNullPrototype()) break; - DCHECK(property->emit_store()); - DCHECK(!property->NeedsSetFunctionName()); - RegisterList args = register_allocator()->NewRegisterList(2); - builder()->MoveRegister(literal, args[0]); - object_literal_context_scope.SetEnteredIf(false); - builder()->SetExpressionPosition(property->value()); - VisitForRegisterValue(property->value(), args[1]); - builder()->CallRuntime(Runtime::kInternalSetPrototype, args); - break; - } - case ObjectLiteral::Property::GETTER: - if (property->emit_store()) { - accessor_table.LookupOrInsert(key)->getter = property; - } - break; - case ObjectLiteral::Property::SETTER: - if (property->emit_store()) { - accessor_table.LookupOrInsert(key)->setter = property; - } - break; } - } - // Define accessors, using only a single call to the runtime for each pair of - // corresponding getters and setters. - object_literal_context_scope.SetEnteredIf(true); - for (auto accessors : accessor_table.ordered_accessors()) { - RegisterAllocationScope inner_register_scope(this); - RegisterList args = register_allocator()->NewRegisterList(5); - builder()->MoveRegister(literal, args[0]); - VisitForRegisterValue(accessors.first, args[1]); - VisitLiteralAccessor(accessors.second->getter, args[2]); - VisitLiteralAccessor(accessors.second->setter, args[3]); - builder() - ->LoadLiteral(Smi::FromInt(NONE)) - .StoreAccumulatorInRegister(args[4]) - .CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, args); + // Define accessors, using only a single call to the runtime for each pair + // of corresponding getters and setters. + object_literal_context_scope.SetEnteredIf(true); + for (auto accessors : accessor_table.ordered_accessors()) { + RegisterAllocationScope inner_register_scope(this); + RegisterList args = register_allocator()->NewRegisterList(5); + builder()->MoveRegister(literal, args[0]); + VisitForRegisterValue(accessors.first, args[1]); + VisitLiteralAccessor(accessors.second->getter, args[2]); + VisitLiteralAccessor(accessors.second->setter, args[3]); + builder() + ->LoadLiteral(Smi::FromInt(NONE)) + .StoreAccumulatorInRegister(args[4]) + .CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, args); + } } // Object literals have two parts. The "static" part on the left contains no diff --git a/test/mjsunit/regress/regress-crbug-1251366.js b/test/mjsunit/regress/regress-crbug-1251366.js new file mode 100644 index 0000000000..c2e3a74845 --- /dev/null +++ b/test/mjsunit/regress/regress-crbug-1251366.js @@ -0,0 +1,13 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +Object.defineProperty(Object.prototype, "x", { + set: function (val) {} +}); + +let o = {...{}, x: 3}; +assertEquals(o.x, 3); + +let o2 = {...{x: 1}, x: 3}; +assertEquals(o2.x, 3);