Reland^2 of [stubs] KeyedStoreGeneric: inline dictionary property stores
For dictionary-mode receivers, the KeyedStoreGeneric stub can store properties directly in most cases. Doing so avoids the need to have an entry in the stub cache for every map/property combination. Original review: https://codereview.chromium.org/2504403005/ Review-Url: https://codereview.chromium.org/2528883003 Cr-Commit-Position: refs/heads/master@{#41272}
This commit is contained in:
parent
c4ccbaa3ea
commit
9be747666c
@ -2566,10 +2566,9 @@ void Builtins::Generate_ArrayIteratorPrototypeNext(
|
||||
|
||||
Node* elements =
|
||||
assembler.AllocateFixedArray(FAST_ELEMENTS, assembler.Int32Constant(2));
|
||||
assembler.StoreFixedArrayElement(elements, assembler.Int32Constant(0),
|
||||
index, SKIP_WRITE_BARRIER);
|
||||
assembler.StoreFixedArrayElement(elements, assembler.Int32Constant(1),
|
||||
var_value.value(), SKIP_WRITE_BARRIER);
|
||||
assembler.StoreFixedArrayElement(elements, 0, index, SKIP_WRITE_BARRIER);
|
||||
assembler.StoreFixedArrayElement(elements, 1, var_value.value(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
|
||||
Node* entry = assembler.Allocate(JSArray::kSize);
|
||||
Node* map =
|
||||
|
@ -293,8 +293,7 @@ compiler::Node* ConstructNewResultFromMatchInfo(Isolate* isolate,
|
||||
a->AllocateRegExpResult(context, num_results, start, string);
|
||||
Node* const result_elements = a->LoadElements(result);
|
||||
|
||||
a->StoreFixedArrayElement(result_elements, a->IntPtrConstant(0), first,
|
||||
SKIP_WRITE_BARRIER);
|
||||
a->StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER);
|
||||
|
||||
a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out);
|
||||
|
||||
@ -1846,7 +1845,7 @@ compiler::Node* ReplaceGlobalCallableFastPath(
|
||||
|
||||
Node* const replacement_str = a->ToString(context, replacement_obj);
|
||||
a->StoreFixedArrayElement(res_elems, index, replacement_str,
|
||||
UPDATE_WRITE_BARRIER, mode);
|
||||
UPDATE_WRITE_BARRIER, 0, mode);
|
||||
|
||||
a->Goto(&do_continue);
|
||||
a->Bind(&do_continue);
|
||||
|
@ -1336,12 +1336,14 @@ Node* CodeStubAssembler::StoreObjectFieldRoot(Node* object, int offset,
|
||||
Node* CodeStubAssembler::StoreFixedArrayElement(Node* object, Node* index_node,
|
||||
Node* value,
|
||||
WriteBarrierMode barrier_mode,
|
||||
int additional_offset,
|
||||
ParameterMode parameter_mode) {
|
||||
DCHECK(barrier_mode == SKIP_WRITE_BARRIER ||
|
||||
barrier_mode == UPDATE_WRITE_BARRIER);
|
||||
Node* offset =
|
||||
ElementOffsetFromIndex(index_node, FAST_HOLEY_ELEMENTS, parameter_mode,
|
||||
FixedArray::kHeaderSize - kHeapObjectTag);
|
||||
int header_size =
|
||||
FixedArray::kHeaderSize + additional_offset - kHeapObjectTag;
|
||||
Node* offset = ElementOffsetFromIndex(index_node, FAST_HOLEY_ELEMENTS,
|
||||
parameter_mode, header_size);
|
||||
MachineRepresentation rep = MachineRepresentation::kTagged;
|
||||
if (barrier_mode == SKIP_WRITE_BARRIER) {
|
||||
return StoreNoWriteBarrier(rep, object, offset, value);
|
||||
@ -4036,19 +4038,62 @@ Node* CodeStubAssembler::IntPtrMax(Node* left, Node* right) {
|
||||
MachineType::PointerRepresentation());
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
Node* CodeStubAssembler::GetNumberOfElements(Node* dictionary) {
|
||||
return LoadFixedArrayElement(
|
||||
dictionary, IntPtrConstant(Dictionary::kNumberOfElementsIndex), 0,
|
||||
INTPTR_PARAMETERS);
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
void CodeStubAssembler::SetNumberOfElements(Node* dictionary,
|
||||
Node* num_elements_smi) {
|
||||
StoreFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex,
|
||||
num_elements_smi, SKIP_WRITE_BARRIER);
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
Node* CodeStubAssembler::GetNumberOfDeletedElements(Node* dictionary) {
|
||||
return LoadFixedArrayElement(
|
||||
dictionary, IntPtrConstant(Dictionary::kNumberOfDeletedElementsIndex), 0,
|
||||
INTPTR_PARAMETERS);
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
Node* CodeStubAssembler::GetCapacity(Node* dictionary) {
|
||||
return LoadFixedArrayElement(dictionary,
|
||||
IntPtrConstant(Dictionary::kCapacityIndex), 0,
|
||||
INTPTR_PARAMETERS);
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
Node* CodeStubAssembler::GetNextEnumerationIndex(Node* dictionary) {
|
||||
return LoadFixedArrayElement(
|
||||
dictionary, IntPtrConstant(Dictionary::kNextEnumerationIndexIndex), 0,
|
||||
INTPTR_PARAMETERS);
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
void CodeStubAssembler::SetNextEnumerationIndex(Node* dictionary,
|
||||
Node* next_enum_index_smi) {
|
||||
StoreFixedArrayElement(dictionary, Dictionary::kNextEnumerationIndexIndex,
|
||||
next_enum_index_smi, SKIP_WRITE_BARRIER);
|
||||
}
|
||||
|
||||
template <typename Dictionary>
|
||||
void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
Node* unique_name, Label* if_found,
|
||||
Variable* var_name_index,
|
||||
Label* if_not_found,
|
||||
int inlined_probes) {
|
||||
int inlined_probes,
|
||||
LookupMode mode) {
|
||||
CSA_ASSERT(this, IsDictionary(dictionary));
|
||||
DCHECK_EQ(MachineType::PointerRepresentation(), var_name_index->rep());
|
||||
DCHECK_IMPLIES(mode == kFindInsertionIndex,
|
||||
inlined_probes == 0 && if_found == nullptr);
|
||||
Comment("NameDictionaryLookup");
|
||||
|
||||
Node* capacity = SmiUntag(LoadFixedArrayElement(
|
||||
dictionary, IntPtrConstant(Dictionary::kCapacityIndex), 0,
|
||||
INTPTR_PARAMETERS));
|
||||
Node* capacity = SmiUntag(GetCapacity<Dictionary>(dictionary));
|
||||
Node* mask = IntPtrSub(capacity, IntPtrConstant(1));
|
||||
Node* hash = ChangeUint32ToWord(LoadNameHash(unique_name));
|
||||
|
||||
@ -4068,8 +4113,13 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
count = IntPtrConstant(i + 1);
|
||||
entry = WordAnd(IntPtrAdd(entry, count), mask);
|
||||
}
|
||||
if (mode == kFindInsertionIndex) {
|
||||
// Appease the variable merging algorithm for "Goto(&loop)" below.
|
||||
var_name_index->Bind(IntPtrConstant(0));
|
||||
}
|
||||
|
||||
Node* undefined = UndefinedConstant();
|
||||
Node* the_hole = mode == kFindExisting ? nullptr : TheHoleConstant();
|
||||
|
||||
Variable var_count(this, MachineType::PointerRepresentation());
|
||||
Variable var_entry(this, MachineType::PointerRepresentation());
|
||||
@ -4089,7 +4139,12 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
Node* current =
|
||||
LoadFixedArrayElement(dictionary, index, 0, INTPTR_PARAMETERS);
|
||||
GotoIf(WordEqual(current, undefined), if_not_found);
|
||||
GotoIf(WordEqual(current, unique_name), if_found);
|
||||
if (mode == kFindExisting) {
|
||||
GotoIf(WordEqual(current, unique_name), if_found);
|
||||
} else {
|
||||
DCHECK_EQ(kFindInsertionIndex, mode);
|
||||
GotoIf(WordEqual(current, the_hole), if_not_found);
|
||||
}
|
||||
|
||||
// See Dictionary::NextProbe().
|
||||
count = IntPtrAdd(count, IntPtrConstant(1));
|
||||
@ -4103,9 +4158,9 @@ void CodeStubAssembler::NameDictionaryLookup(Node* dictionary,
|
||||
|
||||
// Instantiate template methods to workaround GCC compilation issue.
|
||||
template void CodeStubAssembler::NameDictionaryLookup<NameDictionary>(
|
||||
Node*, Node*, Label*, Variable*, Label*, int);
|
||||
Node*, Node*, Label*, Variable*, Label*, int, LookupMode);
|
||||
template void CodeStubAssembler::NameDictionaryLookup<GlobalDictionary>(
|
||||
Node*, Node*, Label*, Variable*, Label*, int);
|
||||
Node*, Node*, Label*, Variable*, Label*, int, LookupMode);
|
||||
|
||||
Node* CodeStubAssembler::ComputeIntegerHash(Node* key, Node* seed) {
|
||||
// See v8::internal::ComputeIntegerHash()
|
||||
@ -4131,9 +4186,7 @@ void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary,
|
||||
DCHECK_EQ(MachineType::PointerRepresentation(), var_entry->rep());
|
||||
Comment("NumberDictionaryLookup");
|
||||
|
||||
Node* capacity = SmiUntag(LoadFixedArrayElement(
|
||||
dictionary, IntPtrConstant(Dictionary::kCapacityIndex), 0,
|
||||
INTPTR_PARAMETERS));
|
||||
Node* capacity = SmiUntag(GetCapacity<Dictionary>(dictionary));
|
||||
Node* mask = IntPtrSub(capacity, IntPtrConstant(1));
|
||||
|
||||
Node* int32_seed;
|
||||
@ -4197,6 +4250,121 @@ void CodeStubAssembler::NumberDictionaryLookup(Node* dictionary,
|
||||
}
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
void CodeStubAssembler::FindInsertionEntry(Node* dictionary, Node* key,
|
||||
Variable* var_key_index) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
template <>
|
||||
void CodeStubAssembler::FindInsertionEntry<NameDictionary>(
|
||||
Node* dictionary, Node* key, Variable* var_key_index) {
|
||||
Label done(this);
|
||||
NameDictionaryLookup<NameDictionary>(dictionary, key, nullptr, var_key_index,
|
||||
&done, 0, kFindInsertionIndex);
|
||||
Bind(&done);
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
void CodeStubAssembler::InsertEntry(Node* dictionary, Node* key, Node* value,
|
||||
Node* index, Node* enum_index) {
|
||||
UNREACHABLE(); // Use specializations instead.
|
||||
}
|
||||
|
||||
template <>
|
||||
void CodeStubAssembler::InsertEntry<NameDictionary>(Node* dictionary,
|
||||
Node* name, Node* value,
|
||||
Node* index,
|
||||
Node* enum_index) {
|
||||
// Store name and value.
|
||||
StoreFixedArrayElement(dictionary, index, name, UPDATE_WRITE_BARRIER, 0,
|
||||
INTPTR_PARAMETERS);
|
||||
const int kNameToValueOffset =
|
||||
(NameDictionary::kEntryValueIndex - NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
StoreFixedArrayElement(dictionary, index, value, UPDATE_WRITE_BARRIER,
|
||||
kNameToValueOffset, INTPTR_PARAMETERS);
|
||||
|
||||
// Prepare details of the new property.
|
||||
Variable var_details(this, MachineRepresentation::kTaggedSigned);
|
||||
const int kInitialIndex = 0;
|
||||
PropertyDetails d(NONE, DATA, kInitialIndex, PropertyCellType::kNoCell);
|
||||
enum_index =
|
||||
WordShl(enum_index, PropertyDetails::DictionaryStorageField::kShift);
|
||||
STATIC_ASSERT(kInitialIndex == 0);
|
||||
var_details.Bind(WordOr(SmiConstant(d.AsSmi()), enum_index));
|
||||
|
||||
// Private names must be marked non-enumerable.
|
||||
Label not_private(this, &var_details);
|
||||
GotoUnless(IsSymbolMap(LoadMap(name)), ¬_private);
|
||||
Node* flags = SmiToWord32(LoadObjectField(name, Symbol::kFlagsOffset));
|
||||
const int kPrivateMask = 1 << Symbol::kPrivateBit;
|
||||
GotoUnless(IsSetWord32(flags, kPrivateMask), ¬_private);
|
||||
Node* dont_enum =
|
||||
WordShl(SmiConstant(DONT_ENUM), PropertyDetails::AttributesField::kShift);
|
||||
var_details.Bind(WordOr(var_details.value(), dont_enum));
|
||||
Goto(¬_private);
|
||||
Bind(¬_private);
|
||||
|
||||
// Finally, store the details.
|
||||
const int kNameToDetailsOffset =
|
||||
(NameDictionary::kEntryDetailsIndex - NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
StoreFixedArrayElement(dictionary, index, var_details.value(),
|
||||
SKIP_WRITE_BARRIER, kNameToDetailsOffset,
|
||||
INTPTR_PARAMETERS);
|
||||
}
|
||||
|
||||
template <>
|
||||
void CodeStubAssembler::InsertEntry<GlobalDictionary>(Node* dictionary,
|
||||
Node* key, Node* value,
|
||||
Node* index,
|
||||
Node* enum_index) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
template <class Dictionary>
|
||||
void CodeStubAssembler::Add(Node* dictionary, Node* key, Node* value,
|
||||
Label* bailout) {
|
||||
Node* capacity = GetCapacity<Dictionary>(dictionary);
|
||||
Node* nof = GetNumberOfElements<Dictionary>(dictionary);
|
||||
Node* new_nof = SmiAdd(nof, SmiConstant(1));
|
||||
// Require 33% to still be free after adding additional_elements.
|
||||
// Computing "x + (x >> 1)" on a Smi x does not return a valid Smi!
|
||||
// But that's OK here because it's only used for a comparison.
|
||||
Node* required_capacity_pseudo_smi = SmiAdd(new_nof, WordShr(new_nof, 1));
|
||||
GotoIf(UintPtrLessThan(capacity, required_capacity_pseudo_smi), bailout);
|
||||
// Require rehashing if more than 50% of free elements are deleted elements.
|
||||
Node* deleted = GetNumberOfDeletedElements<Dictionary>(dictionary);
|
||||
CSA_ASSERT(this, UintPtrGreaterThan(capacity, new_nof));
|
||||
Node* half_of_free_elements = WordShr(SmiSub(capacity, new_nof), 1);
|
||||
GotoIf(UintPtrGreaterThan(deleted, half_of_free_elements), bailout);
|
||||
Node* enum_index = nullptr;
|
||||
if (Dictionary::kIsEnumerable) {
|
||||
enum_index = GetNextEnumerationIndex<Dictionary>(dictionary);
|
||||
Node* new_enum_index = SmiAdd(enum_index, SmiConstant(1));
|
||||
Node* max_enum_index =
|
||||
SmiConstant(PropertyDetails::DictionaryStorageField::kMax);
|
||||
GotoIf(UintPtrGreaterThan(new_enum_index, max_enum_index), bailout);
|
||||
|
||||
// No more bailouts after this point.
|
||||
// Operations from here on can have side effects.
|
||||
|
||||
SetNextEnumerationIndex<Dictionary>(dictionary, new_enum_index);
|
||||
} else {
|
||||
USE(enum_index);
|
||||
}
|
||||
SetNumberOfElements<Dictionary>(dictionary, new_nof);
|
||||
|
||||
Variable var_key_index(this, MachineType::PointerRepresentation());
|
||||
FindInsertionEntry<Dictionary>(dictionary, key, &var_key_index);
|
||||
InsertEntry<Dictionary>(dictionary, key, value, var_key_index.value(),
|
||||
enum_index);
|
||||
}
|
||||
|
||||
template void CodeStubAssembler::Add<NameDictionary>(Node*, Node*, Node*,
|
||||
Label*);
|
||||
|
||||
void CodeStubAssembler::DescriptorLookupLinear(Node* unique_name,
|
||||
Node* descriptors, Node* nof,
|
||||
Label* if_found,
|
||||
@ -5194,7 +5362,7 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(Node* receiver, Node* key,
|
||||
var_result.Bind(result);
|
||||
} else {
|
||||
StoreFixedArrayElement(the_context, mapped_index, value,
|
||||
UPDATE_WRITE_BARRIER, INTPTR_PARAMETERS);
|
||||
UPDATE_WRITE_BARRIER, 0, INTPTR_PARAMETERS);
|
||||
}
|
||||
Goto(&end);
|
||||
}
|
||||
@ -5217,7 +5385,7 @@ Node* CodeStubAssembler::EmitKeyedSloppyArguments(Node* receiver, Node* key,
|
||||
GotoIf(WordEqual(result, TheHoleConstant()), bailout);
|
||||
var_result.Bind(result);
|
||||
} else {
|
||||
StoreFixedArrayElement(backing_store, key, value, UPDATE_WRITE_BARRIER,
|
||||
StoreFixedArrayElement(backing_store, key, value, UPDATE_WRITE_BARRIER, 0,
|
||||
INTPTR_PARAMETERS);
|
||||
}
|
||||
Goto(&end);
|
||||
@ -5300,7 +5468,7 @@ void CodeStubAssembler::StoreElement(Node* elements, ElementsKind kind,
|
||||
value = Float64SilenceNaN(value);
|
||||
StoreFixedDoubleArrayElement(elements, index, value, mode);
|
||||
} else {
|
||||
StoreFixedArrayElement(elements, index, value, barrier_mode, mode);
|
||||
StoreFixedArrayElement(elements, index, value, barrier_mode, 0, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5692,7 +5860,7 @@ Node* CodeStubAssembler::CreateAllocationSiteInFeedbackVector(
|
||||
StoreObjectField(site, AllocationSite::kWeakNextOffset, next_site);
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, site_list, site);
|
||||
|
||||
StoreFixedArrayElement(feedback_vector, slot, site, UPDATE_WRITE_BARRIER,
|
||||
StoreFixedArrayElement(feedback_vector, slot, site, UPDATE_WRITE_BARRIER, 0,
|
||||
CodeStubAssembler::SMI_PARAMETERS);
|
||||
return site;
|
||||
}
|
||||
@ -5710,7 +5878,7 @@ Node* CodeStubAssembler::CreateWeakCellInFeedbackVector(Node* feedback_vector,
|
||||
Heap::kTheHoleValueRootIndex);
|
||||
|
||||
// Store the WeakCell in the feedback vector.
|
||||
StoreFixedArrayElement(feedback_vector, slot, cell, UPDATE_WRITE_BARRIER,
|
||||
StoreFixedArrayElement(feedback_vector, slot, cell, UPDATE_WRITE_BARRIER, 0,
|
||||
CodeStubAssembler::SMI_PARAMETERS);
|
||||
return cell;
|
||||
}
|
||||
|
@ -20,21 +20,24 @@ class StubCache;
|
||||
|
||||
enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
|
||||
|
||||
#define HEAP_CONSTANT_LIST(V) \
|
||||
V(BooleanMap, BooleanMap) \
|
||||
V(CodeMap, CodeMap) \
|
||||
V(empty_string, EmptyString) \
|
||||
V(EmptyFixedArray, EmptyFixedArray) \
|
||||
V(FalseValue, False) \
|
||||
V(FixedArrayMap, FixedArrayMap) \
|
||||
V(FixedCOWArrayMap, FixedCOWArrayMap) \
|
||||
V(FixedDoubleArrayMap, FixedDoubleArrayMap) \
|
||||
V(HeapNumberMap, HeapNumberMap) \
|
||||
V(MinusZeroValue, MinusZero) \
|
||||
V(NanValue, Nan) \
|
||||
V(NullValue, Null) \
|
||||
V(TheHoleValue, TheHole) \
|
||||
V(TrueValue, True) \
|
||||
#define HEAP_CONSTANT_LIST(V) \
|
||||
V(AccessorInfoMap, AccessorInfoMap) \
|
||||
V(BooleanMap, BooleanMap) \
|
||||
V(CodeMap, CodeMap) \
|
||||
V(empty_string, EmptyString) \
|
||||
V(EmptyFixedArray, EmptyFixedArray) \
|
||||
V(FalseValue, False) \
|
||||
V(FixedArrayMap, FixedArrayMap) \
|
||||
V(FixedCOWArrayMap, FixedCOWArrayMap) \
|
||||
V(FixedDoubleArrayMap, FixedDoubleArrayMap) \
|
||||
V(FunctionTemplateInfoMap, FunctionTemplateInfoMap) \
|
||||
V(HeapNumberMap, HeapNumberMap) \
|
||||
V(MinusZeroValue, MinusZero) \
|
||||
V(NanValue, Nan) \
|
||||
V(NullValue, Null) \
|
||||
V(SymbolMap, SymbolMap) \
|
||||
V(TheHoleValue, TheHole) \
|
||||
V(TrueValue, True) \
|
||||
V(UndefinedValue, Undefined)
|
||||
|
||||
// Provides JavaScript-specific "macro-assembler" functionality on top of the
|
||||
@ -357,15 +360,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
// Store an array element to a FixedArray.
|
||||
compiler::Node* StoreFixedArrayElement(
|
||||
compiler::Node* object, int index, compiler::Node* value,
|
||||
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
|
||||
ParameterMode parameter_mode = INTEGER_PARAMETERS) {
|
||||
return StoreFixedArrayElement(object, Int32Constant(index), value,
|
||||
barrier_mode, parameter_mode);
|
||||
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER) {
|
||||
return StoreFixedArrayElement(object, IntPtrConstant(index), value,
|
||||
barrier_mode, 0, INTPTR_PARAMETERS);
|
||||
}
|
||||
|
||||
compiler::Node* StoreFixedArrayElement(
|
||||
compiler::Node* object, compiler::Node* index, compiler::Node* value,
|
||||
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
|
||||
int additional_offset = 0,
|
||||
ParameterMode parameter_mode = INTEGER_PARAMETERS);
|
||||
|
||||
compiler::Node* StoreFixedDoubleArrayElement(
|
||||
@ -762,16 +765,38 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
// Calculate a valid size for the a hash table.
|
||||
compiler::Node* HashTableComputeCapacity(compiler::Node* at_least_space_for);
|
||||
|
||||
template <class Dictionary>
|
||||
compiler::Node* GetNumberOfElements(compiler::Node* dictionary);
|
||||
|
||||
template <class Dictionary>
|
||||
void SetNumberOfElements(compiler::Node* dictionary,
|
||||
compiler::Node* num_elements_smi);
|
||||
|
||||
template <class Dictionary>
|
||||
compiler::Node* GetNumberOfDeletedElements(compiler::Node* dictionary);
|
||||
|
||||
template <class Dictionary>
|
||||
compiler::Node* GetCapacity(compiler::Node* dictionary);
|
||||
|
||||
template <class Dictionary>
|
||||
compiler::Node* GetNextEnumerationIndex(compiler::Node* dictionary);
|
||||
|
||||
template <class Dictionary>
|
||||
void SetNextEnumerationIndex(compiler::Node* dictionary,
|
||||
compiler::Node* next_enum_index_smi);
|
||||
|
||||
// Looks up an entry in a NameDictionaryBase successor. If the entry is found
|
||||
// control goes to {if_found} and {var_name_index} contains an index of the
|
||||
// key field of the entry found. If the key is not found control goes to
|
||||
// {if_not_found}.
|
||||
static const int kInlinedDictionaryProbes = 4;
|
||||
enum LookupMode { kFindExisting, kFindInsertionIndex };
|
||||
template <typename Dictionary>
|
||||
void NameDictionaryLookup(compiler::Node* dictionary,
|
||||
compiler::Node* unique_name, Label* if_found,
|
||||
Variable* var_name_index, Label* if_not_found,
|
||||
int inlined_probes = kInlinedDictionaryProbes);
|
||||
int inlined_probes = kInlinedDictionaryProbes,
|
||||
LookupMode mode = kFindExisting);
|
||||
|
||||
compiler::Node* ComputeIntegerHash(compiler::Node* key, compiler::Node* seed);
|
||||
|
||||
@ -780,6 +805,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* intptr_index, Label* if_found,
|
||||
Variable* var_entry, Label* if_not_found);
|
||||
|
||||
template <class Dictionary>
|
||||
void FindInsertionEntry(compiler::Node* dictionary, compiler::Node* key,
|
||||
Variable* var_key_index);
|
||||
|
||||
template <class Dictionary>
|
||||
void InsertEntry(compiler::Node* dictionary, compiler::Node* key,
|
||||
compiler::Node* value, compiler::Node* index,
|
||||
compiler::Node* enum_index);
|
||||
|
||||
template <class Dictionary>
|
||||
void Add(compiler::Node* dictionary, compiler::Node* key,
|
||||
compiler::Node* value, Label* bailout);
|
||||
|
||||
// Tries to check if {object} has own {unique_name} property.
|
||||
void TryHasOwnProperty(compiler::Node* object, compiler::Node* map,
|
||||
compiler::Node* instance_type,
|
||||
|
@ -2117,7 +2117,7 @@ void StoreScriptContextFieldStub::GenerateAssembly(
|
||||
Node* script_context = assembler.LoadScriptContext(context, context_index());
|
||||
assembler.StoreFixedArrayElement(
|
||||
script_context, assembler.IntPtrConstant(slot_index()), value,
|
||||
UPDATE_WRITE_BARRIER, CodeStubAssembler::INTPTR_PARAMETERS);
|
||||
UPDATE_WRITE_BARRIER, 0, CodeStubAssembler::INTPTR_PARAMETERS);
|
||||
assembler.Return(value);
|
||||
}
|
||||
|
||||
@ -2621,21 +2621,19 @@ compiler::Node* FastNewFunctionContextStub::Generate(
|
||||
assembler->SmiFromWord32(length));
|
||||
|
||||
// Set up the fixed slots.
|
||||
assembler->StoreFixedArrayElement(
|
||||
function_context, assembler->Int32Constant(Context::CLOSURE_INDEX),
|
||||
function, SKIP_WRITE_BARRIER);
|
||||
assembler->StoreFixedArrayElement(
|
||||
function_context, assembler->Int32Constant(Context::PREVIOUS_INDEX),
|
||||
context, SKIP_WRITE_BARRIER);
|
||||
assembler->StoreFixedArrayElement(
|
||||
function_context, assembler->Int32Constant(Context::EXTENSION_INDEX),
|
||||
assembler->TheHoleConstant(), SKIP_WRITE_BARRIER);
|
||||
assembler->StoreFixedArrayElement(function_context, Context::CLOSURE_INDEX,
|
||||
function, SKIP_WRITE_BARRIER);
|
||||
assembler->StoreFixedArrayElement(function_context, Context::PREVIOUS_INDEX,
|
||||
context, SKIP_WRITE_BARRIER);
|
||||
assembler->StoreFixedArrayElement(function_context, Context::EXTENSION_INDEX,
|
||||
assembler->TheHoleConstant(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
|
||||
// Copy the native context from the previous context.
|
||||
Node* native_context = assembler->LoadNativeContext(context);
|
||||
assembler->StoreFixedArrayElement(
|
||||
function_context, assembler->Int32Constant(Context::NATIVE_CONTEXT_INDEX),
|
||||
native_context, SKIP_WRITE_BARRIER);
|
||||
assembler->StoreFixedArrayElement(function_context,
|
||||
Context::NATIVE_CONTEXT_INDEX,
|
||||
native_context, SKIP_WRITE_BARRIER);
|
||||
|
||||
// Initialize the rest of the slots to undefined.
|
||||
Node* undefined = assembler->UndefinedConstant();
|
||||
|
@ -1367,9 +1367,8 @@ void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
|
||||
const int32_t kMaxLinear = 210;
|
||||
Label stub_cache(this);
|
||||
Node* bitfield3 = LoadMapBitField3(receiver_map);
|
||||
Node* nof =
|
||||
DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
|
||||
GotoIf(UintPtrGreaterThan(nof, IntPtrConstant(kMaxLinear)), &stub_cache);
|
||||
Node* nof = DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
|
||||
GotoIf(Uint32LessThan(Int32Constant(kMaxLinear), nof), &stub_cache);
|
||||
Node* descriptors = LoadMapDescriptors(receiver_map);
|
||||
Variable var_name_index(this, MachineType::PointerRepresentation());
|
||||
Label if_descriptor_found(this);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "src/ic/keyed-store-generic.h"
|
||||
|
||||
#include "src/code-factory.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
#include "src/contexts.h"
|
||||
#include "src/ic/accessor-assembler-impl.h"
|
||||
@ -34,7 +35,8 @@ class KeyedStoreGenericAssembler : public AccessorAssemblerImpl {
|
||||
Node* value, Node* context, Label* slow);
|
||||
|
||||
void EmitGenericPropertyStore(Node* receiver, Node* receiver_map,
|
||||
const StoreICParameters* p, Label* slow);
|
||||
const StoreICParameters* p, Label* slow,
|
||||
LanguageMode language_mode);
|
||||
|
||||
void BranchIfPrototypesHaveNonFastElements(Node* receiver_map,
|
||||
Label* non_fast_elements,
|
||||
@ -63,6 +65,13 @@ class KeyedStoreGenericAssembler : public AccessorAssemblerImpl {
|
||||
Node* current_elements_kind, Node* context,
|
||||
ElementsKind packed_kind,
|
||||
ElementsKind packed_kind_2, Label* bailout);
|
||||
|
||||
void JumpIfDataProperty(Node* details, Label* writable, Label* readonly);
|
||||
void LookupPropertyOnPrototypeChain(Node* receiver_map, Node* name,
|
||||
Label* accessor,
|
||||
Variable* var_accessor_pair,
|
||||
Variable* var_accessor_holder,
|
||||
Label* readonly, Label* bailout);
|
||||
};
|
||||
|
||||
void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state,
|
||||
@ -478,28 +487,253 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore(
|
||||
}
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
|
||||
Node* receiver, Node* receiver_map, const StoreICParameters* p,
|
||||
Label* slow) {
|
||||
Comment("stub cache probe");
|
||||
// TODO(jkummerow): Don't rely on the stub cache as much.
|
||||
// - existing properties can be overwritten inline (unless readonly).
|
||||
// - for dictionary mode receivers, we can even add properties inline
|
||||
// (unless the prototype chain prevents it).
|
||||
Variable var_handler(this, MachineRepresentation::kTagged);
|
||||
Label found_handler(this, &var_handler), stub_cache_miss(this);
|
||||
TryProbeStubCache(isolate()->store_stub_cache(), receiver, p->name,
|
||||
&found_handler, &var_handler, &stub_cache_miss);
|
||||
Bind(&found_handler);
|
||||
void KeyedStoreGenericAssembler::JumpIfDataProperty(Node* details,
|
||||
Label* writable,
|
||||
Label* readonly) {
|
||||
// Accessor properties never have the READ_ONLY attribute set.
|
||||
GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
|
||||
readonly);
|
||||
Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
|
||||
GotoIf(Word32Equal(kind, Int32Constant(kData)), writable);
|
||||
// Fall through if it's an accessor property.
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::LookupPropertyOnPrototypeChain(
|
||||
Node* receiver_map, Node* name, Label* accessor,
|
||||
Variable* var_accessor_pair, Variable* var_accessor_holder, Label* readonly,
|
||||
Label* bailout) {
|
||||
Label ok_to_write(this);
|
||||
Variable var_holder(this, MachineRepresentation::kTagged);
|
||||
var_holder.Bind(LoadMapPrototype(receiver_map));
|
||||
Variable var_holder_map(this, MachineRepresentation::kTagged);
|
||||
var_holder_map.Bind(LoadMap(var_holder.value()));
|
||||
|
||||
Variable* merged_variables[] = {&var_holder, &var_holder_map};
|
||||
Label loop(this, arraysize(merged_variables), merged_variables);
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
Comment("KeyedStoreGeneric found handler");
|
||||
HandleStoreICHandlerCase(p, var_handler.value(), slow);
|
||||
Node* holder = var_holder.value();
|
||||
Node* holder_map = var_holder_map.value();
|
||||
Node* instance_type = LoadMapInstanceType(holder_map);
|
||||
Label next_proto(this);
|
||||
{
|
||||
Label found(this), found_fast(this), found_dict(this), found_global(this);
|
||||
Variable var_meta_storage(this, MachineRepresentation::kTagged);
|
||||
Variable var_entry(this, MachineType::PointerRepresentation());
|
||||
TryLookupProperty(holder, holder_map, instance_type, name, &found_fast,
|
||||
&found_dict, &found_global, &var_meta_storage,
|
||||
&var_entry, &next_proto, bailout);
|
||||
Bind(&found_fast);
|
||||
{
|
||||
Node* descriptors = var_meta_storage.value();
|
||||
Node* name_index = var_entry.value();
|
||||
// TODO(jkummerow): Add helper functions for accessing value and
|
||||
// details by entry.
|
||||
const int kNameToDetailsOffset = (DescriptorArray::kDescriptorDetails -
|
||||
DescriptorArray::kDescriptorKey) *
|
||||
kPointerSize;
|
||||
Node* details = LoadAndUntagToWord32FixedArrayElement(
|
||||
descriptors, name_index, kNameToDetailsOffset);
|
||||
JumpIfDataProperty(details, &ok_to_write, readonly);
|
||||
|
||||
// Accessor case.
|
||||
Variable var_details(this, MachineRepresentation::kWord32);
|
||||
LoadPropertyFromFastObject(holder, holder_map, descriptors, name_index,
|
||||
&var_details, var_accessor_pair);
|
||||
var_accessor_holder->Bind(holder);
|
||||
Goto(accessor);
|
||||
}
|
||||
|
||||
Bind(&found_dict);
|
||||
{
|
||||
Node* dictionary = var_meta_storage.value();
|
||||
Node* entry = var_entry.value();
|
||||
const int kNameToDetailsOffset = (NameDictionary::kEntryDetailsIndex -
|
||||
NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
Node* details = LoadAndUntagToWord32FixedArrayElement(
|
||||
dictionary, entry, kNameToDetailsOffset);
|
||||
JumpIfDataProperty(details, &ok_to_write, readonly);
|
||||
|
||||
// Accessor case.
|
||||
const int kNameToValueOffset = (NameDictionary::kEntryValueIndex -
|
||||
NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
var_accessor_pair->Bind(
|
||||
LoadFixedArrayElement(dictionary, entry, kNameToValueOffset));
|
||||
var_accessor_holder->Bind(holder);
|
||||
Goto(accessor);
|
||||
}
|
||||
|
||||
Bind(&found_global);
|
||||
{
|
||||
Node* dictionary = var_meta_storage.value();
|
||||
Node* entry = var_entry.value();
|
||||
const int kNameToValueOffset = (GlobalDictionary::kEntryValueIndex -
|
||||
GlobalDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
|
||||
Node* property_cell =
|
||||
LoadFixedArrayElement(dictionary, entry, kNameToValueOffset);
|
||||
|
||||
Node* value =
|
||||
LoadObjectField(property_cell, PropertyCell::kValueOffset);
|
||||
GotoIf(WordEqual(value, TheHoleConstant()), &next_proto);
|
||||
Node* details = LoadAndUntagToWord32ObjectField(
|
||||
property_cell, PropertyCell::kDetailsOffset);
|
||||
JumpIfDataProperty(details, &ok_to_write, readonly);
|
||||
|
||||
// Accessor case.
|
||||
var_accessor_pair->Bind(value);
|
||||
var_accessor_holder->Bind(holder);
|
||||
Goto(accessor);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&next_proto);
|
||||
// Bailout if it can be an integer indexed exotic case.
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
|
||||
bailout);
|
||||
Node* proto = LoadMapPrototype(holder_map);
|
||||
GotoIf(WordEqual(proto, NullConstant()), &ok_to_write);
|
||||
var_holder.Bind(proto);
|
||||
var_holder_map.Bind(LoadMap(proto));
|
||||
Goto(&loop);
|
||||
}
|
||||
Bind(&stub_cache_miss);
|
||||
Bind(&ok_to_write);
|
||||
}
|
||||
|
||||
void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
|
||||
Node* receiver, Node* receiver_map, const StoreICParameters* p, Label* slow,
|
||||
LanguageMode language_mode) {
|
||||
Variable var_accessor_pair(this, MachineRepresentation::kTagged);
|
||||
Variable var_accessor_holder(this, MachineRepresentation::kTagged);
|
||||
Label stub_cache(this), fast_properties(this), dictionary_properties(this),
|
||||
accessor(this), readonly(this);
|
||||
Node* properties = LoadProperties(receiver);
|
||||
Node* properties_map = LoadMap(properties);
|
||||
Branch(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
|
||||
&dictionary_properties, &fast_properties);
|
||||
|
||||
Bind(&fast_properties);
|
||||
{
|
||||
Comment("KeyedStoreGeneric_miss");
|
||||
TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
|
||||
p->vector, p->receiver, p->name);
|
||||
// TODO(jkummerow): Does it make sense to support some cases here inline?
|
||||
// Maybe overwrite existing writable properties?
|
||||
// Maybe support map transitions?
|
||||
Goto(&stub_cache);
|
||||
}
|
||||
|
||||
Bind(&dictionary_properties);
|
||||
{
|
||||
Comment("dictionary property store");
|
||||
// We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
|
||||
// seeing global objects here (which would need special handling).
|
||||
|
||||
Variable var_name_index(this, MachineType::PointerRepresentation());
|
||||
Label dictionary_found(this, &var_name_index), not_found(this);
|
||||
NameDictionaryLookup<NameDictionary>(properties, p->name, &dictionary_found,
|
||||
&var_name_index, ¬_found);
|
||||
Bind(&dictionary_found);
|
||||
{
|
||||
Label overwrite(this);
|
||||
const int kNameToDetailsOffset = (NameDictionary::kEntryDetailsIndex -
|
||||
NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
Node* details = LoadAndUntagToWord32FixedArrayElement(
|
||||
properties, var_name_index.value(), kNameToDetailsOffset);
|
||||
JumpIfDataProperty(details, &overwrite, &readonly);
|
||||
|
||||
// Accessor case.
|
||||
const int kNameToValueOffset =
|
||||
(NameDictionary::kEntryValueIndex - NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
var_accessor_pair.Bind(LoadFixedArrayElement(
|
||||
properties, var_name_index.value(), kNameToValueOffset));
|
||||
var_accessor_holder.Bind(receiver);
|
||||
Goto(&accessor);
|
||||
|
||||
Bind(&overwrite);
|
||||
{
|
||||
StoreFixedArrayElement(properties, var_name_index.value(), p->value,
|
||||
UPDATE_WRITE_BARRIER, kNameToValueOffset,
|
||||
INTPTR_PARAMETERS);
|
||||
Return(p->value);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(¬_found);
|
||||
{
|
||||
LookupPropertyOnPrototypeChain(receiver_map, p->name, &accessor,
|
||||
&var_accessor_pair, &var_accessor_holder,
|
||||
&readonly, slow);
|
||||
Add<NameDictionary>(properties, p->name, p->value, slow);
|
||||
Return(p->value);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&accessor);
|
||||
{
|
||||
Label not_callable(this);
|
||||
Node* accessor_pair = var_accessor_pair.value();
|
||||
GotoIf(IsAccessorInfoMap(LoadMap(accessor_pair)), slow);
|
||||
CSA_ASSERT(this, HasInstanceType(accessor_pair, ACCESSOR_PAIR_TYPE));
|
||||
Node* setter = LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
|
||||
Node* setter_map = LoadMap(setter);
|
||||
// FunctionTemplateInfo setters are not supported yet.
|
||||
GotoIf(IsFunctionTemplateInfoMap(setter_map), slow);
|
||||
GotoUnless(IsCallableMap(setter_map), ¬_callable);
|
||||
|
||||
Callable callable = CodeFactory::Call(isolate());
|
||||
CallJS(callable, p->context, setter, receiver, p->value);
|
||||
Return(p->value);
|
||||
|
||||
Bind(¬_callable);
|
||||
{
|
||||
if (language_mode == STRICT) {
|
||||
Node* message =
|
||||
SmiConstant(Smi::FromInt(MessageTemplate::kNoSetterInCallback));
|
||||
TailCallRuntime(Runtime::kThrowTypeError, p->context, message, p->name,
|
||||
var_accessor_holder.value());
|
||||
} else {
|
||||
DCHECK_EQ(SLOPPY, language_mode);
|
||||
Return(p->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&readonly);
|
||||
{
|
||||
if (language_mode == STRICT) {
|
||||
Node* message =
|
||||
SmiConstant(Smi::FromInt(MessageTemplate::kStrictReadOnlyProperty));
|
||||
Node* type = Typeof(p->receiver, p->context);
|
||||
TailCallRuntime(Runtime::kThrowTypeError, p->context, message, p->name,
|
||||
type, p->receiver);
|
||||
} else {
|
||||
DCHECK_EQ(SLOPPY, language_mode);
|
||||
Return(p->value);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&stub_cache);
|
||||
{
|
||||
Comment("stub cache probe");
|
||||
Variable var_handler(this, MachineRepresentation::kTagged);
|
||||
Label found_handler(this, &var_handler), stub_cache_miss(this);
|
||||
TryProbeStubCache(isolate()->store_stub_cache(), receiver, p->name,
|
||||
&found_handler, &var_handler, &stub_cache_miss);
|
||||
Bind(&found_handler);
|
||||
{
|
||||
Comment("KeyedStoreGeneric found handler");
|
||||
HandleStoreICHandlerCase(p, var_handler.value(), slow);
|
||||
}
|
||||
Bind(&stub_cache_miss);
|
||||
{
|
||||
Comment("KeyedStoreGeneric_miss");
|
||||
TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value,
|
||||
p->slot, p->vector, p->receiver, p->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,7 +773,7 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
|
||||
Comment("key is unique name");
|
||||
KeyedStoreGenericAssembler::StoreICParameters p(context, receiver, name,
|
||||
value, slot, vector);
|
||||
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow);
|
||||
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow, language_mode);
|
||||
}
|
||||
|
||||
Bind(&slow);
|
||||
|
@ -3851,6 +3851,8 @@ class Dictionary: public HashTable<Derived, Shape, Key> {
|
||||
static const int kMaxNumberKeyIndex = DerivedHashTable::kPrefixStartIndex;
|
||||
static const int kNextEnumerationIndexIndex = kMaxNumberKeyIndex + 1;
|
||||
|
||||
static const bool kIsEnumerable = Shape::kIsEnumerable;
|
||||
|
||||
protected:
|
||||
// Generic at put operation.
|
||||
MUST_USE_RESULT static Handle<Derived> AtPut(
|
||||
@ -9595,14 +9597,15 @@ class Symbol: public Name {
|
||||
static const int kFlagsOffset = kNameOffset + kPointerSize;
|
||||
static const int kSize = kFlagsOffset + kPointerSize;
|
||||
|
||||
// Flags layout.
|
||||
static const int kPrivateBit = 0;
|
||||
static const int kWellKnownSymbolBit = 1;
|
||||
|
||||
typedef FixedBodyDescriptor<kNameOffset, kFlagsOffset, kSize> BodyDescriptor;
|
||||
|
||||
void SymbolShortPrint(std::ostream& os);
|
||||
|
||||
private:
|
||||
static const int kPrivateBit = 0;
|
||||
static const int kWellKnownSymbolBit = 1;
|
||||
|
||||
const char* PrivateSymbolToName() const;
|
||||
|
||||
#if TRACE_MAPS
|
||||
|
21
test/mjsunit/regress/regress-crbug-668101.js
Normal file
21
test/mjsunit/regress/regress-crbug-668101.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2016 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
|
||||
|
||||
function f(a, i, v) {
|
||||
a[i] = v;
|
||||
}
|
||||
|
||||
f("make it generic", 0, 0);
|
||||
|
||||
var a = new Array(3);
|
||||
// Fast properties.
|
||||
f(a, "length", 2);
|
||||
assertEquals(2, a.length);
|
||||
|
||||
// Dictionary properties.
|
||||
%OptimizeObjectForAddingMultipleProperties(a, 1);
|
||||
f(a, "length", 1);
|
||||
assertEquals(1, a.length);
|
@ -2,7 +2,30 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
class A {}
|
||||
class B {}
|
||||
Object.assign(B, A);
|
||||
assertEquals("class B {}", B.toString());
|
||||
|
||||
(function() {
|
||||
function f(a, i, v) {
|
||||
a[i] = v;
|
||||
}
|
||||
|
||||
f("make it generic", 0, 0);
|
||||
|
||||
var o = {foo: "foo"};
|
||||
%OptimizeObjectForAddingMultipleProperties(o, 10);
|
||||
|
||||
var s = %CreatePrivateSymbol("priv");
|
||||
f(o, s, "private");
|
||||
%ToFastProperties(o);
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(o, s);
|
||||
assertEquals("private", desc.value);
|
||||
assertTrue(desc.writable);
|
||||
assertFalse(desc.enumerable);
|
||||
assertTrue(desc.configurable);
|
||||
})();
|
||||
|
@ -25,6 +25,8 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
// Test dictionary store ICs.
|
||||
|
||||
// Function that stores property 'x' on an object.
|
||||
@ -62,3 +64,27 @@ assertEquals(42, o.x);
|
||||
delete o.x;
|
||||
store(o);
|
||||
assertEquals(42, o.x);
|
||||
|
||||
(function() {
|
||||
var o = {};
|
||||
for (var i = 0; i < 100; i++) {
|
||||
var k = "key" + i;
|
||||
o[k] = "foo";
|
||||
delete o[k];
|
||||
}
|
||||
})();
|
||||
|
||||
(function() {
|
||||
function f(a, i, v) {
|
||||
a[i] = v;
|
||||
}
|
||||
|
||||
f("make it generic", 0, 0);
|
||||
|
||||
var o = {};
|
||||
for (var i = 0; i < 100; i++) {
|
||||
var k = %InternalizeString("key" + i);
|
||||
f(o, k, "foo");
|
||||
delete o[k];
|
||||
}
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user