Commit 09d4ba01 authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[builtins] Properly handle non-simple target in Object.assign.

Plus a bit of CSA typification.

Bug: v8:7725
Change-Id: I43fea4a4c0739f9c24d84035816b046e742372ee
Reviewed-on: https://chromium-review.googlesource.com/1051653Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53102}
parent 18f2636a
......@@ -563,6 +563,7 @@ void ObjectBuiltinsAssembler::ObjectAssignFast(TNode<Context> context,
// cached representation of the source. Handle this case in runtime.
TNode<Map> to_map = LoadMap(to);
GotoIf(IsDeprecatedMap(to_map), slow);
TNode<BoolT> to_is_simple_receiver = IsSimpleObjectMap(to_map);
GotoIfNot(IsJSObjectInstanceType(from_instance_type), slow);
TNode<Uint32T> from_bit_field3 =
......@@ -673,9 +674,9 @@ void ObjectBuiltinsAssembler::ObjectAssignFast(TNode<Context> context,
// Store property to target object.
BIND(&do_store);
{
KeyedStoreGenericGenerator::SetProperty(state(), context, to,
next_key, var_value.value(),
LanguageMode::kStrict);
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|.
......
......@@ -7446,19 +7446,22 @@ void CodeStubAssembler::Lookup(TNode<Name> unique_name, TNode<Array> array,
}
}
TNode<BoolT> CodeStubAssembler::IsSimpleObjectMap(TNode<Map> map) {
uint32_t mask =
Map::HasNamedInterceptorBit::kMask | Map::IsAccessCheckNeededBit::kMask;
// !IsSpecialReceiverType && !IsNamedInterceptor && !IsAccessCheckNeeded
return Select<BoolT>(
IsSpecialReceiverInstanceType(LoadMapInstanceType(map)),
[=] { return Int32FalseConstant(); },
[=] { return IsClearWord32(LoadMapBitField(map), mask); });
}
void CodeStubAssembler::TryLookupPropertyInSimpleObject(
TNode<JSObject> object, TNode<Map> map, TNode<Name> unique_name,
Label* if_found_fast, Label* if_found_dict,
TVariable<HeapObject>* var_meta_storage, TVariable<IntPtrT>* var_name_index,
Label* if_not_found) {
CSA_ASSERT(
this,
Word32BinaryNot(IsSpecialReceiverInstanceType(LoadMapInstanceType(map))));
uint32_t mask =
Map::HasNamedInterceptorBit::kMask | Map::IsAccessCheckNeededBit::kMask;
CSA_ASSERT(this, Word32BinaryNot(IsSetWord32(LoadMapBitField(map), mask)));
USE(mask);
CSA_ASSERT(this, IsSimpleObjectMap(map));
TNode<Uint32T> bit_field3 = LoadMapBitField3(map);
Label if_isfastmap(this), if_isslowmap(this);
......
......@@ -1442,6 +1442,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<BoolT> IsCustomElementsReceiverInstanceType(
TNode<Int32T> instance_type);
TNode<BoolT> IsSpecialReceiverMap(SloppyTNode<Map> map);
// Returns true if the map corresponds to non-special fast or dictionary
// object.
TNode<BoolT> IsSimpleObjectMap(TNode<Map> map);
TNode<BoolT> IsStringInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsString(SloppyTNode<HeapObject> object);
TNode<BoolT> IsSymbolInstanceType(SloppyTNode<Int32T> instance_type);
......@@ -1663,18 +1666,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Returns true if any of the |T|'s bits in given |word| are set.
template <typename T>
Node* IsSetWord(Node* word) {
TNode<BoolT> IsSetWord(SloppyTNode<WordT> word) {
return IsSetWord(word, T::kMask);
}
// Returns true if any of the mask's bits in given |word| are set.
Node* IsSetWord(Node* word, uint32_t mask) {
TNode<BoolT> IsSetWord(SloppyTNode<WordT> word, uint32_t mask) {
return WordNotEqual(WordAnd(word, IntPtrConstant(mask)), IntPtrConstant(0));
}
// Returns true if any of the mask's bit are set in the given Smi.
// Smi-encoding of the mask is performed implicitly!
Node* IsSetSmi(Node* smi, int untagged_mask) {
TNode<BoolT> IsSetSmi(SloppyTNode<Smi> smi, int untagged_mask) {
intptr_t mask_word = bit_cast<intptr_t>(Smi::FromInt(untagged_mask));
return WordNotEqual(
WordAnd(BitcastTaggedToWord(smi), IntPtrConstant(mask_word)),
......@@ -1683,24 +1686,24 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Returns true if all of the |T|'s bits in given |word32| are clear.
template <typename T>
Node* IsClearWord32(Node* word32) {
TNode<BoolT> IsClearWord32(SloppyTNode<Word32T> word32) {
return IsClearWord32(word32, T::kMask);
}
// Returns true if all of the mask's bits in given |word32| are clear.
Node* IsClearWord32(Node* word32, uint32_t mask) {
TNode<BoolT> IsClearWord32(SloppyTNode<Word32T> word32, uint32_t mask) {
return Word32Equal(Word32And(word32, Int32Constant(mask)),
Int32Constant(0));
}
// Returns true if all of the |T|'s bits in given |word| are clear.
template <typename T>
Node* IsClearWord(Node* word) {
TNode<BoolT> IsClearWord(SloppyTNode<WordT> word) {
return IsClearWord(word, T::kMask);
}
// Returns true if all of the mask's bits in given |word| are clear.
Node* IsClearWord(Node* word, uint32_t mask) {
TNode<BoolT> IsClearWord(SloppyTNode<WordT> word, uint32_t mask) {
return WordEqual(WordAnd(word, IntPtrConstant(mask)), IntPtrConstant(0));
}
......
......@@ -32,8 +32,8 @@ class KeyedStoreGenericAssembler : public AccessorAssembler {
// Generates code for [[Set]] operation, the |unique_name| is supposed to be
// unique otherwise this code will always go to runtime.
void SetProperty(TNode<Context> context, TNode<JSReceiver> receiver,
TNode<Name> unique_name, TNode<Object> value,
LanguageMode language_mode);
TNode<BoolT> is_simple_receiver, TNode<Name> unique_name,
TNode<Object> value, LanguageMode language_mode);
private:
enum UpdateLength {
......@@ -50,12 +50,14 @@ class KeyedStoreGenericAssembler : public AccessorAssembler {
// If language mode is not provided it is deduced from the feedback slot's
// kind.
void EmitGenericPropertyStore(Node* receiver, Node* receiver_map,
void EmitGenericPropertyStore(TNode<JSReceiver> receiver,
TNode<Map> receiver_map,
const StoreICParameters* p,
ExitPoint* exit_point, Label* slow,
Maybe<LanguageMode> maybe_language_mode);
void EmitGenericPropertyStore(Node* receiver, Node* receiver_map,
void EmitGenericPropertyStore(SloppyTNode<JSReceiver> receiver,
SloppyTNode<Map> receiver_map,
const StoreICParameters* p, Label* slow) {
ExitPoint direct_exit(this);
EmitGenericPropertyStore(receiver, receiver_map, p, &direct_exit, slow,
......@@ -110,10 +112,11 @@ void StoreICUninitializedGenerator::Generate(
void KeyedStoreGenericGenerator::SetProperty(
compiler::CodeAssemblerState* state, TNode<Context> context,
TNode<JSReceiver> receiver, TNode<Name> name, TNode<Object> value,
LanguageMode language_mode) {
TNode<JSReceiver> receiver, TNode<BoolT> is_simple_receiver,
TNode<Name> name, TNode<Object> value, LanguageMode language_mode) {
KeyedStoreGenericAssembler assembler(state);
assembler.SetProperty(context, receiver, name, value, language_mode);
assembler.SetProperty(context, receiver, is_simple_receiver, name, value,
language_mode);
}
void KeyedStoreGenericAssembler::BranchIfPrototypesHaveNonFastElements(
......@@ -633,9 +636,10 @@ void KeyedStoreGenericAssembler::LookupPropertyOnPrototypeChain(
}
void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
Node* receiver, Node* receiver_map, const StoreICParameters* p,
ExitPoint* exit_point, Label* slow,
TNode<JSReceiver> receiver, TNode<Map> receiver_map,
const StoreICParameters* p, ExitPoint* exit_point, Label* slow,
Maybe<LanguageMode> maybe_language_mode) {
CSA_ASSERT(this, IsSimpleObjectMap(receiver_map));
VARIABLE(var_accessor_pair, MachineRepresentation::kTagged);
VARIABLE(var_accessor_holder, MachineRepresentation::kTagged);
Label stub_cache(this), fast_properties(this), dictionary_properties(this),
......@@ -757,7 +761,7 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
VARIABLE(var_name_index, MachineType::PointerRepresentation());
Label dictionary_found(this, &var_name_index), not_found(this);
Node* properties = LoadSlowProperties(receiver);
TNode<NameDictionary> properties = CAST(LoadSlowProperties(CAST(receiver)));
NameDictionaryLookup<NameDictionary>(properties, p->name, &dictionary_found,
&var_name_index, &not_found);
BIND(&dictionary_found);
......@@ -982,6 +986,7 @@ void KeyedStoreGenericAssembler::StoreIC_Uninitialized() {
void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context,
TNode<JSReceiver> receiver,
TNode<BoolT> is_simple_receiver,
TNode<Name> unique_name,
TNode<Object> value,
LanguageMode language_mode) {
......@@ -990,6 +995,10 @@ void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context,
Label done(this), slow(this, Label::kDeferred);
ExitPoint exit_point(this, [&](Node* result) { Goto(&done); });
CSA_ASSERT(this, Word32Equal(is_simple_receiver,
IsSimpleObjectMap(LoadMap(receiver))));
GotoIfNot(is_simple_receiver, &slow);
EmitGenericPropertyStore(receiver, LoadMap(receiver), &p, &exit_point, &slow,
Just(language_mode));
......
......@@ -21,8 +21,8 @@ class KeyedStoreGenericGenerator {
// Building block for fast path of Object.assign implementation.
static void SetProperty(compiler::CodeAssemblerState* state,
TNode<Context> context, TNode<JSReceiver> receiver,
TNode<Name> name, TNode<Object> value,
LanguageMode language_mode);
TNode<BoolT> is_simple_receiver, TNode<Name> name,
TNode<Object> value, LanguageMode language_mode);
};
class StoreICUninitializedGenerator {
......
// 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.
var proxy = new Proxy({}, {});
Object.assign(proxy, { b: "boom", a: "ah", o: "ouch" });
assertEquals(["b", "a", "o"], Object.getOwnPropertyNames(proxy));
assertEquals("boom", proxy.b);
assertEquals("ah", proxy.a);
assertEquals("ouch", proxy.o);
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