Commit fbcf0221 authored by Caitlin Potter's avatar Caitlin Potter Committed by Commit Bot

[CloneObjectIC] add CSA implementation of slow case

The CSA implementation is a separate handler so that TF has the
opportunity to reduce to a direct call, skipping some of the dispatching
in the CloneObjectIC stub.

This patch moves the looping over a source object's keys and values into the
base CodeStubAssembler, so that it can be shared between ObjectAssignFast
and CloneObjectIC_Slow.

During each step of the loop, storing is delegated to a new SetPropertyInLiteral
helper in KeyedStoreGenericGenerator, which performs a store without consulting
the prototype chain, and automatically reconfigures accessors into data
properties regardless of their attributes.

BUG=v8:8067, v8:7611
R=ishell@chromium.org, jkummerow@chromium.org

Change-Id: I06ae89f37e9b4265aab67389cf68a96529f90578
Reviewed-on: https://chromium-review.googlesource.com/1182122
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55806}
parent 30af54c4
......@@ -648,6 +648,7 @@ namespace internal {
TFH(LoadGlobalICTrampoline, LoadGlobal) \
TFH(LoadGlobalICInsideTypeofTrampoline, LoadGlobal) \
TFH(CloneObjectIC, CloneObjectWithVector) \
TFH(CloneObjectIC_Slow, CloneObjectWithVector) \
\
/* IterableToList */ \
/* ES #sec-iterabletolist */ \
......
......@@ -39,6 +39,7 @@ IC_BUILTIN(KeyedStoreIC)
IC_BUILTIN(KeyedStoreICTrampoline)
IC_BUILTIN(StoreInArrayLiteralIC)
IC_BUILTIN(CloneObjectIC)
IC_BUILTIN(CloneObjectIC_Slow)
IC_BUILTIN_PARAM(LoadGlobalIC, LoadGlobalIC, NOT_INSIDE_TYPEOF)
IC_BUILTIN_PARAM(LoadGlobalICInsideTypeof, LoadGlobalIC, INSIDE_TYPEOF)
......
......@@ -46,11 +46,6 @@ class ObjectBuiltinsAssembler : public CodeStubAssembler {
TNode<Word32T> IsStringWrapperElementsKind(TNode<Map> map);
// Checks that |map| has only simple properties, returns bitfield3.
TNode<Uint32T> EnsureOnlyHasSimpleProperties(TNode<Map> map,
TNode<Int32T> instance_type,
Label* bailout);
void ObjectAssignFast(TNode<Context> context, TNode<JSReceiver> to,
TNode<Object> from, Label* slow);
};
......@@ -524,18 +519,6 @@ TF_BUILTIN(ObjectAssign, ObjectBuiltinsAssembler) {
args.PopAndReturn(to);
}
TNode<Uint32T> ObjectBuiltinsAssembler::EnsureOnlyHasSimpleProperties(
TNode<Map> map, TNode<Int32T> instance_type, Label* bailout) {
GotoIf(IsCustomElementsReceiverInstanceType(instance_type), bailout);
TNode<Uint32T> bit_field3 = LoadMapBitField3(map);
GotoIf(IsSetWord32(bit_field3, Map::IsDictionaryMapBit::kMask |
Map::HasHiddenPrototypeBit::kMask),
bailout);
return bit_field3;
}
// This function mimics what FastAssign() function does for C++ implementation.
void ObjectBuiltinsAssembler::ObjectAssignFast(TNode<Context> context,
TNode<JSReceiver> to,
......@@ -567,132 +550,18 @@ void ObjectBuiltinsAssembler::ObjectAssignFast(TNode<Context> context,
TNode<BoolT> to_is_simple_receiver = IsSimpleObjectMap(to_map);
GotoIfNot(IsJSObjectInstanceType(from_instance_type), slow);
TNode<Uint32T> from_bit_field3 =
EnsureOnlyHasSimpleProperties(from_map, from_instance_type, slow);
GotoIfNot(IsEmptyFixedArray(LoadElements(CAST(from))), slow);
TNode<DescriptorArray> from_descriptors = LoadMapDescriptors(from_map);
TNode<Uint32T> nof_descriptors =
DecodeWord32<Map::NumberOfOwnDescriptorsBits>(from_bit_field3);
TVARIABLE(BoolT, var_stable, Int32TrueConstant());
VariableList list({&var_stable}, zone());
DescriptorArrayForEach(
list, Unsigned(Int32Constant(0)), nof_descriptors,
[=, &var_stable](TNode<UintPtrT> descriptor_key_index) {
TNode<Name> next_key = CAST(
LoadWeakFixedArrayElement(from_descriptors, descriptor_key_index));
TVARIABLE(Object, var_value, SmiConstant(0));
Label do_store(this), next_iteration(this);
{
TVARIABLE(Map, var_from_map);
TVARIABLE(HeapObject, var_meta_storage);
TVARIABLE(IntPtrT, var_entry);
TVARIABLE(Uint32T, var_details);
Label if_found(this);
Label if_found_fast(this), if_found_dict(this);
Label if_stable(this), if_not_stable(this);
Branch(var_stable.value(), &if_stable, &if_not_stable);
BIND(&if_stable);
{
// Directly decode from the descriptor array if |from| did not
// change shape.
var_from_map = from_map;
var_meta_storage = from_descriptors;
var_entry = Signed(descriptor_key_index);
Goto(&if_found_fast);
}
BIND(&if_not_stable);
{
// If the map did change, do a slower lookup. We are still
// guaranteed that the object has a simple shape, and that the key
// is a name.
var_from_map = LoadMap(CAST(from));
TryLookupPropertyInSimpleObject(
CAST(from), var_from_map.value(), next_key, &if_found_fast,
&if_found_dict, &var_meta_storage, &var_entry, &next_iteration);
}
BIND(&if_found_fast);
{
TNode<DescriptorArray> descriptors = CAST(var_meta_storage.value());
TNode<IntPtrT> name_index = var_entry.value();
// Skip non-enumerable properties.
var_details = LoadDetailsByKeyIndex(descriptors, name_index);
GotoIf(IsSetWord32(var_details.value(),
PropertyDetails::kAttributesDontEnumMask),
&next_iteration);
LoadPropertyFromFastObject(from, var_from_map.value(), descriptors,
name_index, var_details.value(),
&var_value);
Goto(&if_found);
}
BIND(&if_found_dict);
{
Node* dictionary = var_meta_storage.value();
Node* entry = var_entry.value();
TNode<Uint32T> details =
LoadDetailsByKeyIndex<NameDictionary>(dictionary, entry);
// Skip non-enumerable properties.
GotoIf(
IsSetWord32(details, PropertyDetails::kAttributesDontEnumMask),
&next_iteration);
var_details = details;
var_value = LoadValueByKeyIndex<NameDictionary>(dictionary, entry);
Goto(&if_found);
}
// Here we have details and value which could be an accessor.
BIND(&if_found);
{
Label slow_load(this, Label::kDeferred);
var_value =
CallGetterIfAccessor(var_value.value(), var_details.value(),
context, from, &slow_load, kCallJSGetter);
Goto(&do_store);
BIND(&slow_load);
{
var_value =
CallRuntime(Runtime::kGetProperty, context, from, next_key);
Goto(&do_store);
}
}
}
// Store property to target object.
BIND(&do_store);
{
KeyedStoreGenericGenerator::SetProperty(
state(), context, to, to_is_simple_receiver, next_key,
var_value.value(), LanguageMode::kStrict);
// Check if the |from| object is still stable, i.e. we can proceed
// using property details from preloaded |from_descriptors|.
var_stable = Select<BoolT>(
var_stable.value(),
[=] { return WordEqual(LoadMap(CAST(from)), from_map); },
[=] { return Int32FalseConstant(); });
Goto(&next_iteration);
}
BIND(&next_iteration);
});
ForEachEnumerableOwnProperty(context, from_map, CAST(from),
[=](TNode<Name> key, TNode<Object> value) {
KeyedStoreGenericGenerator::SetProperty(
state(), context, to,
to_is_simple_receiver, key, value,
LanguageMode::kStrict);
},
slow);
Goto(&done);
BIND(&done);
}
......
......@@ -1744,6 +1744,19 @@ TNode<Object> CodeStubAssembler::LoadMapBackPointer(SloppyTNode<Map> map) {
[=] { return UndefinedConstant(); });
}
TNode<Uint32T> CodeStubAssembler::EnsureOnlyHasSimpleProperties(
TNode<Map> map, TNode<Int32T> instance_type, Label* bailout) {
// This check can have false positives, since it applies to any JSValueType.
GotoIf(IsCustomElementsReceiverInstanceType(instance_type), bailout);
TNode<Uint32T> bit_field3 = LoadMapBitField3(map);
GotoIf(IsSetWord32(bit_field3, Map::IsDictionaryMapBit::kMask |
Map::HasHiddenPrototypeBit::kMask),
bailout);
return bit_field3;
}
TNode<IntPtrT> CodeStubAssembler::LoadJSReceiverIdentityHash(
SloppyTNode<Object> receiver, Label* if_no_hash) {
TVARIABLE(IntPtrT, var_hash);
......@@ -5369,6 +5382,13 @@ TNode<BoolT> CodeStubAssembler::IsExtensibleMap(SloppyTNode<Map> map) {
return IsSetWord32<Map::IsExtensibleBit>(LoadMapBitField2(map));
}
TNode<BoolT> CodeStubAssembler::IsExtensibleNonPrototypeMap(TNode<Map> map) {
int kMask = Map::IsExtensibleBit::kMask | Map::IsPrototypeMapBit::kMask;
int kExpected = Map::IsExtensibleBit::kMask;
return Word32Equal(Word32And(LoadMapBitField2(map), Int32Constant(kMask)),
Int32Constant(kExpected));
}
TNode<BoolT> CodeStubAssembler::IsCallableMap(SloppyTNode<Map> map) {
CSA_ASSERT(this, IsMap(map));
return IsSetWord32<Map::IsCallableBit>(LoadMapBitField(map));
......@@ -8221,6 +8241,125 @@ void CodeStubAssembler::DescriptorArrayForEach(
IndexAdvanceMode::kPost);
}
void CodeStubAssembler::ForEachEnumerableOwnProperty(
TNode<Context> context, TNode<Map> map, TNode<JSObject> object,
const ForEachKeyValueFunction& body, Label* bailout) {
TNode<Int32T> type = LoadMapInstanceType(map);
TNode<Uint32T> bit_field3 = EnsureOnlyHasSimpleProperties(map, type, bailout);
TNode<DescriptorArray> descriptors = LoadMapDescriptors(map);
TNode<Uint32T> nof_descriptors =
DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bit_field3);
TVARIABLE(BoolT, var_stable, Int32TrueConstant());
VariableList list({&var_stable}, zone());
DescriptorArrayForEach(
list, Unsigned(Int32Constant(0)), nof_descriptors,
[=, &var_stable](TNode<UintPtrT> descriptor_key_index) {
TNode<Name> next_key =
CAST(LoadWeakFixedArrayElement(descriptors, descriptor_key_index));
TVARIABLE(Object, var_value, SmiConstant(0));
Label callback(this), next_iteration(this);
{
TVARIABLE(Map, var_map);
TVARIABLE(HeapObject, var_meta_storage);
TVARIABLE(IntPtrT, var_entry);
TVARIABLE(Uint32T, var_details);
Label if_found(this);
Label if_found_fast(this), if_found_dict(this);
Label if_stable(this), if_not_stable(this);
Branch(var_stable.value(), &if_stable, &if_not_stable);
BIND(&if_stable);
{
// Directly decode from the descriptor array if |object| did not
// change shape.
var_map = map;
var_meta_storage = descriptors;
var_entry = Signed(descriptor_key_index);
Goto(&if_found_fast);
}
BIND(&if_not_stable);
{
// If the map did change, do a slower lookup. We are still
// guaranteed that the object has a simple shape, and that the key
// is a name.
var_map = LoadMap(object);
TryLookupPropertyInSimpleObject(
object, var_map.value(), next_key, &if_found_fast,
&if_found_dict, &var_meta_storage, &var_entry, &next_iteration);
}
BIND(&if_found_fast);
{
TNode<DescriptorArray> descriptors = CAST(var_meta_storage.value());
TNode<IntPtrT> name_index = var_entry.value();
// Skip non-enumerable properties.
var_details = LoadDetailsByKeyIndex(descriptors, name_index);
GotoIf(IsSetWord32(var_details.value(),
PropertyDetails::kAttributesDontEnumMask),
&next_iteration);
LoadPropertyFromFastObject(object, var_map.value(), descriptors,
name_index, var_details.value(),
&var_value);
Goto(&if_found);
}
BIND(&if_found_dict);
{
TNode<NameDictionary> dictionary = CAST(var_meta_storage.value());
TNode<IntPtrT> entry = var_entry.value();
TNode<Uint32T> details =
LoadDetailsByKeyIndex<NameDictionary>(dictionary, entry);
// Skip non-enumerable properties.
GotoIf(
IsSetWord32(details, PropertyDetails::kAttributesDontEnumMask),
&next_iteration);
var_details = details;
var_value = LoadValueByKeyIndex<NameDictionary>(dictionary, entry);
Goto(&if_found);
}
// Here we have details and value which could be an accessor.
BIND(&if_found);
{
Label slow_load(this, Label::kDeferred);
var_value = CallGetterIfAccessor(var_value.value(),
var_details.value(), context,
object, &slow_load, kCallJSGetter);
Goto(&callback);
BIND(&slow_load);
var_value =
CallRuntime(Runtime::kGetProperty, context, object, next_key);
Goto(&callback);
BIND(&callback);
body(next_key, var_value.value());
// Check if |object| is still stable, i.e. we can proceed using
// property details from preloaded |descriptors|.
var_stable =
Select<BoolT>(var_stable.value(),
[=] { return WordEqual(LoadMap(object), map); },
[=] { return Int32FalseConstant(); });
Goto(&next_iteration);
}
}
BIND(&next_iteration);
});
}
void CodeStubAssembler::DescriptorLookup(
SloppyTNode<Name> unique_name, SloppyTNode<DescriptorArray> descriptors,
SloppyTNode<Uint32T> bitfield3, Label* if_found,
......
......@@ -934,6 +934,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* LoadMapEnumLength(SloppyTNode<Map> map);
// Load the back-pointer of a Map.
TNode<Object> LoadMapBackPointer(SloppyTNode<Map> map);
// Checks that |map| has only simple properties, returns bitfield3.
TNode<Uint32T> EnsureOnlyHasSimpleProperties(TNode<Map> map,
TNode<Int32T> instance_type,
Label* bailout);
// Load the identity hash of a JSRececiver.
TNode<IntPtrT> LoadJSReceiverIdentityHash(SloppyTNode<Object> receiver,
Label* if_no_hash = nullptr);
......@@ -1799,6 +1803,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<BoolT> IsNameDictionary(SloppyTNode<HeapObject> object);
TNode<BoolT> IsGlobalDictionary(SloppyTNode<HeapObject> object);
TNode<BoolT> IsExtensibleMap(SloppyTNode<Map> map);
TNode<BoolT> IsExtensibleNonPrototypeMap(TNode<Map> map);
TNode<BoolT> IsExternalStringInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsFastJSArray(SloppyTNode<Object> object,
SloppyTNode<Context> context);
......@@ -2935,6 +2940,17 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<Uint32T> end_descriptor,
const ForEachDescriptorBodyFunction& body);
typedef std::function<void(TNode<Name> key, TNode<Object> value)>
ForEachKeyValueFunction;
// For each JSObject property (in DescriptorArray order), check if the key is
// enumerable, and if so, load the value from the receiver and evaluate the
// closure.
void ForEachEnumerableOwnProperty(TNode<Context> context, TNode<Map> map,
TNode<JSObject> object,
const ForEachKeyValueFunction& body,
Label* bailout);
TNode<Object> CallGetterIfAccessor(Node* value, Node* details, Node* context,
Node* receiver, Label* if_bailout,
GetOwnPropertyMode mode = kCallJSGetter);
......
......@@ -4,11 +4,13 @@
#include "src/ic/accessor-assembler.h"
#include "src/ast/ast.h"
#include "src/code-factory.h"
#include "src/code-stubs.h"
#include "src/counters.h"
#include "src/ic/handler-configuration.h"
#include "src/ic/ic.h"
#include "src/ic/keyed-store-generic.h"
#include "src/ic/stub-cache.h"
#include "src/objects-inl.h"
#include "src/objects/module.h"
......@@ -807,9 +809,14 @@ void AccessorAssembler::EmitAccessCheck(Node* expected_native_context,
void AccessorAssembler::JumpIfDataProperty(Node* details, Label* writable,
Label* readonly) {
// Accessor properties never have the READ_ONLY attribute set.
GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
readonly);
if (readonly) {
// Accessor properties never have the READ_ONLY attribute set.
GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
readonly);
} else {
CSA_ASSERT(this, IsNotSetWord32(details,
PropertyDetails::kAttributesReadOnlyMask));
}
Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
GotoIf(Word32Equal(kind, Int32Constant(kData)), writable);
// Fall through if it's an accessor property.
......@@ -946,7 +953,8 @@ void AccessorAssembler::HandleStoreICHandlerCase(
BIND(&store_transition);
{
TNode<Map> map = CAST(map_or_property_cell);
HandleStoreICTransitionMapHandlerCase(p, map, miss, false);
HandleStoreICTransitionMapHandlerCase(p, map, miss,
kCheckPrototypeValidity);
Return(p->value);
}
}
......@@ -954,10 +962,13 @@ void AccessorAssembler::HandleStoreICHandlerCase(
void AccessorAssembler::HandleStoreICTransitionMapHandlerCase(
const StoreICParameters* p, TNode<Map> transition_map, Label* miss,
bool validate_transition_handler) {
Node* maybe_validity_cell =
LoadObjectField(transition_map, Map::kPrototypeValidityCellOffset);
CheckPrototypeValidityCell(maybe_validity_cell, miss);
StoreTransitionMapFlags flags) {
DCHECK_EQ(0, flags & ~kStoreTransitionMapFlagsMask);
if (flags & kCheckPrototypeValidity) {
Node* maybe_validity_cell =
LoadObjectField(transition_map, Map::kPrototypeValidityCellOffset);
CheckPrototypeValidityCell(maybe_validity_cell, miss);
}
TNode<Uint32T> bitfield3 = LoadMapBitField3(transition_map);
CSA_ASSERT(this, IsClearWord32<Map::IsDictionaryMapBit>(bitfield3));
......@@ -971,7 +982,7 @@ void AccessorAssembler::HandleStoreICTransitionMapHandlerCase(
Node* factor = IntPtrConstant(DescriptorArray::kEntrySize);
TNode<IntPtrT> last_key_index = UncheckedCast<IntPtrT>(IntPtrAdd(
IntPtrConstant(DescriptorArray::ToKeyIndex(-1)), IntPtrMul(nof, factor)));
if (validate_transition_handler) {
if (flags & kValidateTransitionHandler) {
Node* key = LoadWeakFixedArrayElement(descriptors, last_key_index);
GotoIf(WordNotEqual(key, p->name), miss);
} else {
......@@ -981,7 +992,7 @@ void AccessorAssembler::HandleStoreICTransitionMapHandlerCase(
p->name));
}
Node* details = LoadDetailsByKeyIndex(descriptors, last_key_index);
if (validate_transition_handler) {
if (flags & kValidateTransitionHandler) {
// Follow transitions only in the following cases:
// 1) name is a non-private symbol and attributes equal to NONE,
// 2) name is a private symbol and attributes equal to DONT_ENUM.
......@@ -3479,6 +3490,76 @@ void AccessorAssembler::GenerateStoreInArrayLiteralIC() {
StoreInArrayLiteralIC(&p);
}
void AccessorAssembler::GenerateCloneObjectIC_Slow() {
typedef CloneObjectWithVectorDescriptor Descriptor;
TNode<HeapObject> source = CAST(Parameter(Descriptor::kSource));
TNode<Smi> flags = CAST(Parameter(Descriptor::kFlags));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// The Slow case uses the same call interface as CloneObjectIC, so that it
// can be tail called from it. However, the feedback slot and vector are not
// used.
TNode<Context> native_context = LoadNativeContext(context);
TNode<JSFunction> object_fn =
CAST(LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX));
TNode<Map> initial_map = CAST(
LoadObjectField(object_fn, JSFunction::kPrototypeOrInitialMapOffset));
CSA_ASSERT(this, IsMap(initial_map));
TNode<JSObject> result = CAST(AllocateJSObjectFromMap(initial_map));
{
Label did_set_proto_if_needed(this);
TNode<BoolT> is_null_proto = SmiNotEqual(
SmiAnd(flags, SmiConstant(ObjectLiteral::kHasNullPrototype)),
SmiConstant(Smi::kZero));
GotoIfNot(is_null_proto, &did_set_proto_if_needed);
CallRuntime(Runtime::kInternalSetPrototype, context, result,
NullConstant());
Goto(&did_set_proto_if_needed);
BIND(&did_set_proto_if_needed);
}
ReturnIf(IsNullOrUndefined(source), result);
CSA_ASSERT(this, IsJSReceiver(source));
Label call_runtime(this, Label::kDeferred);
Label done(this);
TNode<Map> map = LoadMap(source);
TNode<Int32T> type = LoadMapInstanceType(map);
{
Label cont(this);
GotoIf(IsJSObjectInstanceType(type), &cont);
GotoIfNot(IsStringInstanceType(type), &done);
Branch(SmiEqual(LoadStringLengthAsSmi(CAST(source)), SmiConstant(0)), &done,
&call_runtime);
BIND(&cont);
}
GotoIfNot(IsEmptyFixedArray(LoadElements(CAST(source))), &call_runtime);
ForEachEnumerableOwnProperty(
context, map, CAST(source),
[=](TNode<Name> key, TNode<Object> value) {
KeyedStoreGenericGenerator::SetPropertyInLiteral(state(), context,
result, key, value);
},
&call_runtime);
Goto(&done);
BIND(&call_runtime);
CallRuntime(Runtime::kCopyDataProperties, context, result, source);
Goto(&done);
BIND(&done);
Return(result);
}
void AccessorAssembler::GenerateCloneObjectIC() {
typedef CloneObjectWithVectorDescriptor Descriptor;
Node* source = Parameter(Descriptor::kSource);
......@@ -3587,7 +3668,8 @@ void AccessorAssembler::GenerateCloneObjectIC() {
GotoIfNot(WordEqual(strong_feedback,
LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&miss);
TailCallRuntime(Runtime::kCloneObjectIC_Slow, context, source, flags);
TailCallBuiltin(Builtins::kCloneObjectIC_Slow, context, source, flags, slot,
vector);
}
BIND(&miss);
......
......@@ -43,6 +43,7 @@ class AccessorAssembler : public CodeStubAssembler {
void GenerateStoreGlobalIC();
void GenerateStoreGlobalICTrampoline();
void GenerateCloneObjectIC();
void GenerateCloneObjectIC_Slow();
void GenerateLoadGlobalIC(TypeofMode typeof_mode);
void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode);
......@@ -109,10 +110,16 @@ class AccessorAssembler : public CodeStubAssembler {
void HandleStoreICHandlerCase(
const StoreICParameters* p, TNode<MaybeObject> handler, Label* miss,
ICMode ic_mode, ElementSupport support_elements = kOnlyProperties);
enum StoreTransitionMapFlags {
kCheckPrototypeValidity = 1 << 0,
kValidateTransitionHandler = 1 << 1,
kStoreTransitionMapFlagsMask =
kCheckPrototypeValidity | kValidateTransitionHandler,
};
void HandleStoreICTransitionMapHandlerCase(const StoreICParameters* p,
TNode<Map> transition_map,
Label* miss,
bool validate_transition_handler);
StoreTransitionMapFlags flags);
void JumpIfDataProperty(Node* details, Label* writable, Label* readonly);
......
This diff is collapsed.
......@@ -30,6 +30,11 @@ class KeyedStoreGenericGenerator {
TNode<Context> context, TNode<Object> receiver,
TNode<Object> key, TNode<Object> value,
LanguageMode language_mode);
static void SetPropertyInLiteral(compiler::CodeAssemblerState* state,
TNode<Context> context,
TNode<JSObject> receiver, TNode<Name> key,
TNode<Object> value);
};
class StoreICUninitializedGenerator {
......
......@@ -635,6 +635,31 @@ RUNTIME_FUNCTION(Runtime_SetNamedProperty) {
language_mode, StoreOrigin::kNamed));
}
// Similar to DefineDataPropertyInLiteral, but does not update feedback, and
// and does not have a flags parameter for performing SetFunctionName().
//
// Currently, this is used for ObjectLiteral spread properties.
RUNTIME_FUNCTION(Runtime_StoreDataPropertyInLiteral) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
bool success;
LookupIterator it = LookupIterator::PropertyOrElement(
isolate, object, key, &success, LookupIterator::OWN);
Maybe<bool> result =
JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE, kDontThrow);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
DCHECK(result.IsJust());
USE(result);
return *value;
}
namespace {
// ES6 section 12.5.4.
......
......@@ -332,6 +332,7 @@ namespace internal {
F(SetDataProperties, 2, 1) \
F(SetKeyedProperty, 4, 1) \
F(SetNamedProperty, 4, 1) \
F(StoreDataPropertyInLiteral, 3, 1) \
F(ShrinkPropertyDictionary, 1, 1) \
F(ToFastProperties, 1, 1) \
F(ToInteger, 1, 1) \
......
// 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.
(function testMegamorphicWithDontEnumTransition() {
function spread(o) { return { ...o }; }
// Set up transition tree
let obj = { ...{}, a: 0, b: 1, c: 2, };
Object.defineProperty(obj, "boom", { enumerable: false, configurable: true,
writable: true });
// make CloneObjectIC MEGAMORPHIC
spread(new Proxy({}, {}));
// Ensure we don't crash, and create the correct object
let result = spread({ a: 0, b: 1, c: 2, boom: 3 });
assertEquals({ a: 0, b: 1, c: 2, boom: 3 }, result);
assertEquals({
enumerable: true,
writable: true,
configurable: true,
value: 3,
}, Object.getOwnPropertyDescriptor(result, "boom"));
})();
// 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.
(function testMegamorphicWithNonSimpleTransitionHandler() {
function spread(o) { return { ...o }; }
// Set up transition tree
let obj = { ...{}, a: 0, b: 1, boom: 2};
// make CloneObjectIC MEGAMORPHIC
spread(new Proxy({}, {}));
// Ensure we don't crash, and create the correct object
assertEquals({ a: 0, b: 1, c: 2 }, spread({ a: 0, b: 1, c: 2 }));
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment