diff --git a/src/compiler/js-operator.cc b/src/compiler/js-operator.cc index 7ccd79153f..8c6db00ae7 100644 --- a/src/compiler/js-operator.cc +++ b/src/compiler/js-operator.cc @@ -1210,7 +1210,7 @@ const Operator* JSOperatorBuilder::CloneObject(VectorSlotPair const& feedback, IrOpcode::kJSCloneObject, // opcode Operator::kNoProperties, // properties "JSCloneObject", // name - 1, 1, 1, 1, 1, 1, // counts + 1, 1, 1, 1, 1, 2, // counts parameters); // parameter } diff --git a/src/ic/ic.cc b/src/ic/ic.cc index 07865b1289..1e2c2ed4e7 100644 --- a/src/ic/ic.cc +++ b/src/ic/ic.cc @@ -2517,7 +2517,7 @@ static Handle FastCloneObjectMap(Isolate* isolate, Handle layout = LayoutDescriptor::New(isolate, map, descriptors, size); map->InitializeDescriptors(*descriptors, *layout); - map->CopyUnusedPropertyFields(*source_map); + map->CopyUnusedPropertyFieldsAdjustedForInstanceSize(*source_map); return map; } @@ -2571,7 +2571,7 @@ RUNTIME_FUNCTION(Runtime_CloneObjectIC_Miss) { RUNTIME_FUNCTION(Runtime_CloneObjectIC_Slow) { HandleScope scope(isolate); DCHECK_EQ(2, args.length()); - Handle source = args.at(0); + Handle source = args.at(0); int flags = args.smi_at(1); RETURN_RESULT_OR_FAILURE(isolate, CloneObjectSlowPath(isolate, source, flags)); diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index cc39a97bd9..55de420792 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -2116,9 +2116,9 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { // Create literal object. int property_index = 0; - bool is_spread = + bool clone_object_spread = expr->properties()->first()->kind() == ObjectLiteral::Property::SPREAD; - if (is_spread) { + if (clone_object_spread) { // Avoid the slow path for spreads in the following common cases: // 1) `let obj = { ...source }` // 2) `let obj = { ...source, override: 1 }` @@ -2137,9 +2137,6 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { builder()->CloneObject(from_value, flags, clone_index); builder()->StoreAccumulatorInRegister(literal); property_index++; - - // FIXME: incorporate compile-time constants following the initial spread - // into the CloneObject opcode, to be included in the final value. } else { size_t entry; // If constant properties is an empty fixed array, use a cached empty fixed @@ -2162,7 +2159,7 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { for (; property_index < expr->properties()->length(); property_index++) { ObjectLiteral::Property* property = expr->properties()->at(property_index); if (property->is_computed_name()) break; - if (!is_spread && property->IsCompileTimeValue()) continue; + if (!clone_object_spread && property->IsCompileTimeValue()) continue; RegisterAllocationScope inner_register_scope(this); Literal* key = property->key()->AsLiteral(); @@ -2171,7 +2168,7 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { UNREACHABLE(); case ObjectLiteral::Property::CONSTANT: case ObjectLiteral::Property::MATERIALIZED_LITERAL: - DCHECK(is_spread || !property->value()->IsCompileTimeValue()); + DCHECK(clone_object_spread || !property->value()->IsCompileTimeValue()); V8_FALLTHROUGH; case ObjectLiteral::Property::COMPUTED: { // It is safe to use [[Put]] here because the boilerplate already diff --git a/src/objects.cc b/src/objects.cc index 082f2bb3ba..c3cb233901 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -10224,9 +10224,9 @@ Handle DescriptorArray::CopyForFastObjectClone( // Ensure the ObjectClone property details are NONE, and that all source // details did not contain DONT_ENUM. - PropertyDetails new_details( - kData, NONE, details.location(), kDefaultFieldConstness, - details.representation(), details.field_index()); + PropertyDetails new_details(kData, NONE, details.location(), + details.constness(), details.representation(), + details.field_index()); descriptors->Set(i, key, src->GetValue(i), new_details); } diff --git a/src/objects/map-inl.h b/src/objects/map-inl.h index b75f7be0b7..57b0e8c6e6 100644 --- a/src/objects/map-inl.h +++ b/src/objects/map-inl.h @@ -360,6 +360,17 @@ void Map::CopyUnusedPropertyFields(Map* map) { DCHECK_EQ(UnusedPropertyFields(), map->UnusedPropertyFields()); } +void Map::CopyUnusedPropertyFieldsAdjustedForInstanceSize(Map* map) { + int value = map->used_or_unused_instance_size_in_words(); + if (value >= JSValue::kFieldsAdded) { + // Unused in-object fields. Adjust the offset from the object’s start + // so it matches the distance to the object’s end. + value += instance_size_in_words() - map->instance_size_in_words(); + } + set_used_or_unused_instance_size_in_words(value); + DCHECK_EQ(UnusedPropertyFields(), map->UnusedPropertyFields()); +} + void Map::AccountAddedPropertyField() { // Update used instance size and unused property fields number. STATIC_ASSERT(JSObject::kFieldsAdded == JSObject::kHeaderSize / kPointerSize); diff --git a/src/objects/map.h b/src/objects/map.h index 37780f7f03..88636d4561 100644 --- a/src/objects/map.h +++ b/src/objects/map.h @@ -219,6 +219,7 @@ class Map : public HeapObject { // Updates the counters tracking unused fields in the property array. inline void SetOutOfObjectUnusedPropertyFields(int unused_property_fields); inline void CopyUnusedPropertyFields(Map* map); + inline void CopyUnusedPropertyFieldsAdjustedForInstanceSize(Map* map); inline void AccountAddedPropertyField(); inline void AccountAddedOutOfObjectPropertyField( int unused_in_property_array); diff --git a/test/mjsunit/es9/regress/regress-866282.js b/test/mjsunit/es9/regress/regress-866282.js new file mode 100644 index 0000000000..ae23c05c27 --- /dev/null +++ b/test/mjsunit/es9/regress/regress-866282.js @@ -0,0 +1,17 @@ +// Copyright 2018 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. + +// Runtime_ObjectCloneIC_Slow() source argument must be a HeapObject handle, +// because undefined/null are allowed. +function spread(o) { return { ...o }; } + +// Transition to MEGAMORPHIC +assertEquals({}, spread(new function C1() {})); +assertEquals({}, spread(new function C2() {})); +assertEquals({}, spread(new function C3() {})); +assertEquals({}, spread(new function C4() {})); +assertEquals({}, spread(new function C5() {})); + +// Trigger Runtime_ObjectCloneIC_Slow() with a non-JSReceiver. +assertEquals({}, spread(undefined)); diff --git a/test/mjsunit/es9/regress/regress-866357.js b/test/mjsunit/es9/regress/regress-866357.js new file mode 100644 index 0000000000..3b6230b0f4 --- /dev/null +++ b/test/mjsunit/es9/regress/regress-866357.js @@ -0,0 +1,17 @@ +// Copyright 2018 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. + +// Flags: --allow-natives-syntax + +// Check that we do appropriate used/unused field accounting +var p = Promise.resolve(); +var then = p.then = () => {}; + +function spread() { return { ...p }; } + +assertEquals({ then }, spread()); +assertEquals({ then }, spread()); +assertEquals({ then }, spread()); +%OptimizeFunctionOnNextCall(spread); +assertEquals({ then }, spread()); diff --git a/test/mjsunit/es9/regress/regress-866727.js b/test/mjsunit/es9/regress/regress-866727.js new file mode 100644 index 0000000000..ddfcf7edf9 --- /dev/null +++ b/test/mjsunit/es9/regress/regress-866727.js @@ -0,0 +1,19 @@ +// Copyright 2018 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. + +// Flags: --allow-natives-syntax + +// Check that IfException/IfSuccess rewiring works in JSInliner +function test() { + var spread = function(value) { return { ...value }; } + try { + assertEquals({}, spread()); + } catch (e) {} +} + +test(); +test(); +test(); +%OptimizeFunctionOnNextCall(test); +test(); diff --git a/test/mjsunit/es9/regress/regress-866861.js b/test/mjsunit/es9/regress/regress-866861.js new file mode 100644 index 0000000000..dc6c764f5c --- /dev/null +++ b/test/mjsunit/es9/regress/regress-866861.js @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +// Check that property constness for out-of-object fields is valid +var o = {}; +var toString = o.toString = function() {}; +try { +assertEquals({ toString }, o = { ...o }); +} catch (e) {} +o.toString = [];