Commit 0b1e9ef2 authored by Z Duong Nguyen-Huu's avatar Z Duong Nguyen-Huu Committed by Commit Bot

Add new frozen, sealed packed elements kind

Design docs: bit.ly/fast-frozen-sealed-elements-in-v8
This change is only support the transition from packed elements to packed sealed elements (via object.seal) or to packed frozen elements (via object.freeze).
Added tests for non-extensible, sealed, frozen packed elements in https://chromium-review.googlesource.com/c/v8/v8/+/1474559
Added tests for non-extensible array in optimized code in https://chromium-review.googlesource.com/c/v8/v8/+/1531030 and https://chromium-review.googlesource.com/c/v8/v8/+/1544274

Using JSTests/ObjectFreeze micro-benchmarks for release build
Before:
TaggedTemplate-Numbers(Score): 0.967
TaggedTemplateLoose-Numbers(Score): 8.82
After:
TaggedTemplate-Numbers(Score): 1.51
TaggedTemplateLoose-Numbers(Score): 8.89

Bug: v8:6831
Change-Id: Ib1089f1bc02eafb8d76ffe617f8fa3e406abd5a4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1474559Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#60723}
parent 7e5d69e2
......@@ -817,6 +817,8 @@ uint32_t EstimateElementCount(Isolate* isolate, Handle<JSArray> array) {
case PACKED_SMI_ELEMENTS:
case HOLEY_SMI_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case HOLEY_ELEMENTS: {
// Fast elements can't have lengths that are not representable by
// a 32-bit signed integer.
......@@ -881,6 +883,8 @@ void CollectElementIndices(Isolate* isolate, Handle<JSObject> object,
switch (kind) {
case PACKED_SMI_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case HOLEY_SMI_ELEMENTS:
case HOLEY_ELEMENTS: {
DisallowHeapAllocation no_gc;
......@@ -1050,6 +1054,8 @@ bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver,
switch (array->GetElementsKind()) {
case PACKED_SMI_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case HOLEY_SMI_ELEMENTS:
case HOLEY_ELEMENTS: {
// Run through the elements FixedArray and use HasElement and GetElement
......@@ -1297,6 +1303,8 @@ Object Slow_ArrayConcat(BuiltinArguments* args, Handle<Object> species,
}
case HOLEY_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NO_ELEMENTS:
DCHECK_EQ(0u, length);
......
......@@ -262,6 +262,7 @@ TF_BUILTIN(ElementsTransitionAndStore_NoTransitionHandleCOW,
V(PACKED_SMI_ELEMENTS) \
V(HOLEY_SMI_ELEMENTS) \
V(PACKED_ELEMENTS) \
V(PACKED_SEALED_ELEMENTS) \
V(HOLEY_ELEMENTS) \
V(PACKED_DOUBLE_ELEMENTS) \
V(HOLEY_DOUBLE_ELEMENTS) \
......
......@@ -1502,7 +1502,10 @@ TNode<Object> CodeStubAssembler::LoadJSArgumentsObjectWithLength(
TNode<Smi> CodeStubAssembler::LoadFastJSArrayLength(
SloppyTNode<JSArray> array) {
TNode<Object> length = LoadJSArrayLength(array);
CSA_ASSERT(this, IsFastElementsKind(LoadElementsKind(array)));
CSA_ASSERT(this, Word32Or(IsFastElementsKind(LoadElementsKind(array)),
IsElementsKindInRange(LoadElementsKind(array),
PACKED_SEALED_ELEMENTS,
PACKED_FROZEN_ELEMENTS)));
// JSArray length is always a positive Smi for fast arrays.
CSA_SLOW_ASSERT(this, TaggedIsPositiveSmi(length));
return UncheckedCast<Smi>(length);
......@@ -2431,6 +2434,7 @@ TNode<Object> CodeStubAssembler::LoadFixedArrayBaseElementAsTagged(
int32_t kinds[] = {// Handled by if_packed.
PACKED_SMI_ELEMENTS, PACKED_ELEMENTS,
PACKED_SEALED_ELEMENTS, PACKED_FROZEN_ELEMENTS,
// Handled by if_holey.
HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS,
// Handled by if_packed_double.
......@@ -2438,7 +2442,7 @@ TNode<Object> CodeStubAssembler::LoadFixedArrayBaseElementAsTagged(
// Handled by if_holey_double.
HOLEY_DOUBLE_ELEMENTS};
Label* labels[] = {// PACKED_{SMI,}_ELEMENTS
&if_packed, &if_packed,
&if_packed, &if_packed, &if_packed, &if_packed,
// HOLEY_{SMI,}_ELEMENTS
&if_holey, &if_holey,
// PACKED_DOUBLE_ELEMENTS
......@@ -5893,6 +5897,13 @@ TNode<BoolT> CodeStubAssembler::IsExtensibleMap(SloppyTNode<Map> map) {
return IsSetWord32<Map::IsExtensibleBit>(LoadMapBitField2(map));
}
TNode<BoolT> CodeStubAssembler::IsFrozenOrSealedElementsKindMap(
SloppyTNode<Map> map) {
CSA_ASSERT(this, IsMap(map));
return IsElementsKindInRange(LoadMapElementsKind(map), PACKED_SEALED_ELEMENTS,
PACKED_FROZEN_ELEMENTS);
}
TNode<BoolT> CodeStubAssembler::IsExtensibleNonPrototypeMap(TNode<Map> map) {
int kMask = Map::IsExtensibleBit::kMask | Map::IsPrototypeMapBit::kMask;
int kExpected = Map::IsExtensibleBit::kMask;
......@@ -10651,7 +10662,8 @@ void CodeStubAssembler::EmitElementStore(Node* object, Node* key, Node* value,
BIND(&done);
return;
}
DCHECK(IsFastElementsKind(elements_kind));
DCHECK(IsFastElementsKind(elements_kind) ||
elements_kind == PACKED_SEALED_ELEMENTS);
Node* length =
SelectImpl(IsJSArray(object), [=]() { return LoadJSArrayLength(object); },
......@@ -10668,7 +10680,8 @@ void CodeStubAssembler::EmitElementStore(Node* object, Node* key, Node* value,
value = TryTaggedToFloat64(value, bailout);
}
if (IsGrowStoreMode(store_mode)) {
if (IsGrowStoreMode(store_mode) &&
!(elements_kind == PACKED_SEALED_ELEMENTS)) {
elements = CheckForCapacityGrow(object, elements, elements_kind, length,
intptr_key, parameter_mode, bailout);
} else {
......@@ -13405,6 +13418,15 @@ TNode<BoolT> CodeStubAssembler::IsElementsKindLessThanOrEqual(
return Int32LessThanOrEqual(target_kind, Int32Constant(reference_kind));
}
TNode<BoolT> CodeStubAssembler::IsElementsKindInRange(
TNode<Int32T> target_kind, ElementsKind lower_reference_kind,
ElementsKind higher_reference_kind) {
return Int32LessThanOrEqual(
Int32Sub(target_kind, Int32Constant(lower_reference_kind)),
Int32Sub(Int32Constant(higher_reference_kind),
Int32Constant(lower_reference_kind)));
}
Node* CodeStubAssembler::IsDebugActive() {
Node* is_debug_active = Load(
MachineType::Uint8(),
......
......@@ -2093,6 +2093,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsNameDictionary(SloppyTNode<HeapObject> object);
TNode<BoolT> IsGlobalDictionary(SloppyTNode<HeapObject> object);
TNode<BoolT> IsExtensibleMap(SloppyTNode<Map> map);
TNode<BoolT> IsFrozenOrSealedElementsKindMap(SloppyTNode<Map> map);
TNode<BoolT> IsExtensibleNonPrototypeMap(TNode<Map> map);
TNode<BoolT> IsExternalStringInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsFeedbackCell(SloppyTNode<HeapObject> object);
......@@ -2266,6 +2267,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
ElementsKind reference_kind);
TNode<BoolT> IsElementsKindLessThanOrEqual(TNode<Int32T> target_kind,
ElementsKind reference_kind);
// Check if reference_kind_a <= target_kind <= reference_kind_b
TNode<BoolT> IsElementsKindInRange(TNode<Int32T> target_kind,
ElementsKind lower_reference_kind,
ElementsKind higher_reference_kind);
// String helpers.
// Load a character from a String (might flatten a ConsString).
......
......@@ -34,6 +34,8 @@ int ElementsKindToShiftSize(ElementsKind elements_kind) {
return 3;
case PACKED_SMI_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case HOLEY_SMI_ELEMENTS:
case HOLEY_ELEMENTS:
case DICTIONARY_ELEMENTS:
......
......@@ -7,6 +7,7 @@
#include "src/base/macros.h"
#include "src/checks.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
......@@ -41,6 +42,10 @@ enum ElementsKind : uint8_t {
PACKED_DOUBLE_ELEMENTS,
HOLEY_DOUBLE_ELEMENTS,
// The sealed, frozen kind for packed elements.
PACKED_SEALED_ELEMENTS,
PACKED_FROZEN_ELEMENTS,
// The "slow" kind.
DICTIONARY_ELEMENTS,
......@@ -69,6 +74,7 @@ enum ElementsKind : uint8_t {
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND = UINT8_ELEMENTS,
LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND = BIGINT64_ELEMENTS,
TERMINAL_FAST_ELEMENTS_KIND = HOLEY_ELEMENTS,
LAST_FROZEN_ELEMENTS_KIND = PACKED_FROZEN_ELEMENTS,
// Alias for kSystemPointerSize-sized elements
#ifdef V8_COMPRESS_POINTERS
......@@ -103,18 +109,18 @@ inline bool IsDictionaryElementsKind(ElementsKind kind) {
}
inline bool IsSloppyArgumentsElementsKind(ElementsKind kind) {
return kind == FAST_SLOPPY_ARGUMENTS_ELEMENTS ||
kind == SLOW_SLOPPY_ARGUMENTS_ELEMENTS;
return IsInRange(kind, FAST_SLOPPY_ARGUMENTS_ELEMENTS,
SLOW_SLOPPY_ARGUMENTS_ELEMENTS);
}
inline bool IsStringWrapperElementsKind(ElementsKind kind) {
return kind == FAST_STRING_WRAPPER_ELEMENTS ||
kind == SLOW_STRING_WRAPPER_ELEMENTS;
return IsInRange(kind, FAST_STRING_WRAPPER_ELEMENTS,
SLOW_STRING_WRAPPER_ELEMENTS);
}
inline bool IsFixedTypedArrayElementsKind(ElementsKind kind) {
return kind >= FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND &&
kind <= LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND;
return IsInRange(kind, FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
}
inline bool IsTerminalElementsKind(ElementsKind kind) {
......@@ -124,7 +130,7 @@ inline bool IsTerminalElementsKind(ElementsKind kind) {
inline bool IsFastElementsKind(ElementsKind kind) {
STATIC_ASSERT(FIRST_FAST_ELEMENTS_KIND == 0);
return kind <= HOLEY_DOUBLE_ELEMENTS;
return kind <= LAST_FAST_ELEMENTS_KIND;
}
inline bool IsTransitionElementsKind(ElementsKind kind) {
......@@ -134,7 +140,7 @@ inline bool IsTransitionElementsKind(ElementsKind kind) {
}
inline bool IsDoubleElementsKind(ElementsKind kind) {
return kind == PACKED_DOUBLE_ELEMENTS || kind == HOLEY_DOUBLE_ELEMENTS;
return IsInRange(kind, PACKED_DOUBLE_ELEMENTS, HOLEY_DOUBLE_ELEMENTS);
}
......@@ -147,13 +153,17 @@ inline bool IsDoubleOrFloatElementsKind(ElementsKind kind) {
return IsDoubleElementsKind(kind) || IsFixedFloatElementsKind(kind);
}
inline bool IsFrozenOrSealedElementsKind(ElementsKind kind) {
return IsInRange(kind, PACKED_SEALED_ELEMENTS, PACKED_FROZEN_ELEMENTS);
}
inline bool IsSmiOrObjectElementsKind(ElementsKind kind) {
return kind == PACKED_SMI_ELEMENTS || kind == HOLEY_SMI_ELEMENTS ||
kind == PACKED_ELEMENTS || kind == HOLEY_ELEMENTS;
}
inline bool IsSmiElementsKind(ElementsKind kind) {
return kind == PACKED_SMI_ELEMENTS || kind == HOLEY_SMI_ELEMENTS;
return IsInRange(kind, PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS);
}
inline bool IsFastNumberElementsKind(ElementsKind kind) {
......@@ -161,7 +171,7 @@ inline bool IsFastNumberElementsKind(ElementsKind kind) {
}
inline bool IsObjectElementsKind(ElementsKind kind) {
return kind == PACKED_ELEMENTS || kind == HOLEY_ELEMENTS;
return IsInRange(kind, PACKED_ELEMENTS, HOLEY_ELEMENTS);
}
inline bool IsHoleyElementsKind(ElementsKind kind) {
......
......@@ -33,6 +33,8 @@
// - FastPackedSmiElementsAccessor
// - FastHoleySmiElementsAccessor
// - FastPackedObjectElementsAccessor
// - FastPackedFrozenObjectElementsAccessor
// - FastPackedSealedObjectElementsAccessor
// - FastHoleyObjectElementsAccessor
// - FastDoubleElementsAccessor
// - FastPackedDoubleElementsAccessor
......@@ -82,6 +84,10 @@ enum Where { AT_START, AT_END };
V(FastPackedDoubleElementsAccessor, PACKED_DOUBLE_ELEMENTS, \
FixedDoubleArray) \
V(FastHoleyDoubleElementsAccessor, HOLEY_DOUBLE_ELEMENTS, FixedDoubleArray) \
V(FastPackedSealedObjectElementsAccessor, PACKED_SEALED_ELEMENTS, \
FixedArray) \
V(FastPackedFrozenObjectElementsAccessor, PACKED_FROZEN_ELEMENTS, \
FixedArray) \
V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, NumberDictionary) \
V(FastSloppyArgumentsElementsAccessor, FAST_SLOPPY_ARGUMENTS_ELEMENTS, \
FixedArray) \
......@@ -1347,7 +1353,7 @@ class ElementsAccessorBase : public InternalElementsAccessor {
static uint32_t GetEntryForIndexImpl(Isolate* isolate, JSObject holder,
FixedArrayBase backing_store,
uint32_t index, PropertyFilter filter) {
DCHECK(IsFastElementsKind(kind()));
DCHECK(IsFastElementsKind(kind()) || IsFrozenOrSealedElementsKind(kind()));
uint32_t length = Subclass::GetMaxIndex(holder, backing_store);
if (IsHoleyElementsKind(kind())) {
return index < length &&
......@@ -2325,7 +2331,8 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
}
return Just(false);
}
} else if (!IsObjectElementsKind(Subclass::kind())) {
} else if (!IsObjectElementsKind(Subclass::kind()) &&
!IsFrozenOrSealedElementsKind(Subclass::kind())) {
// Search for non-number, non-Undefined value, with either
// PACKED_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, HOLEY_SMI_ELEMENTS or
// HOLEY_DOUBLE_ELEMENTS. Guaranteed to return false, since these
......@@ -2334,7 +2341,8 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
} else {
// Search for non-number, non-Undefined value with either
// PACKED_ELEMENTS or HOLEY_ELEMENTS.
DCHECK(IsObjectElementsKind(Subclass::kind()));
DCHECK(IsObjectElementsKind(Subclass::kind()) ||
IsFrozenOrSealedElementsKind(Subclass::kind()));
auto elements = FixedArray::cast(receiver->elements());
for (uint32_t k = start_from; k < length; ++k) {
......@@ -2549,6 +2557,8 @@ class FastSmiOrObjectElementsAccessor
case PACKED_SMI_ELEMENTS:
case HOLEY_SMI_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case HOLEY_ELEMENTS:
CopyObjectToObjectElements(isolate, from, from_kind, from_start, to,
to_kind, to_start, copy_size);
......@@ -2626,7 +2636,8 @@ class FastSmiOrObjectElementsAccessor
length = std::min(static_cast<uint32_t>(elements_base->length()), length);
// Only FAST_{,HOLEY_}ELEMENTS can store non-numbers.
if (!value->IsNumber() && !IsObjectElementsKind(Subclass::kind())) {
if (!value->IsNumber() && !IsObjectElementsKind(Subclass::kind()) &&
!IsFrozenOrSealedElementsKind(Subclass::kind())) {
return Just<int64_t>(-1);
}
// NaN can never be found by strict equality.
......@@ -2677,6 +2688,107 @@ class FastPackedObjectElementsAccessor
name) {}
};
class FastPackedFrozenObjectElementsAccessor
: public FastSmiOrObjectElementsAccessor<
FastPackedFrozenObjectElementsAccessor,
ElementsKindTraits<PACKED_FROZEN_ELEMENTS>> {
public:
explicit FastPackedFrozenObjectElementsAccessor(const char* name)
: FastSmiOrObjectElementsAccessor<
FastPackedFrozenObjectElementsAccessor,
ElementsKindTraits<PACKED_FROZEN_ELEMENTS>>(name) {}
static inline void SetImpl(Handle<JSObject> holder, uint32_t entry,
Object value) {
UNREACHABLE();
}
static inline void SetImpl(FixedArrayBase backing_store, uint32_t entry,
Object value) {
UNREACHABLE();
}
static inline void SetImpl(FixedArrayBase backing_store, uint32_t entry,
Object value, WriteBarrierMode mode) {
UNREACHABLE();
}
static Handle<Object> RemoveElement(Handle<JSArray> receiver,
Where remove_position) {
UNREACHABLE();
}
static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
UNREACHABLE();
}
static void DeleteAtEnd(Handle<JSObject> obj,
Handle<BackingStore> backing_store, uint32_t entry) {
UNREACHABLE();
}
static void DeleteCommon(Handle<JSObject> obj, uint32_t entry,
Handle<FixedArrayBase> store) {
UNREACHABLE();
}
static Handle<Object> PopImpl(Handle<JSArray> receiver) { UNREACHABLE(); }
static uint32_t PushImpl(Handle<JSArray> receiver, Arguments* args,
uint32_t push_size) {
UNREACHABLE();
}
static void AddImpl(Handle<JSObject> object, uint32_t index,
Handle<Object> value, PropertyAttributes attributes,
uint32_t new_capacity) {
UNREACHABLE();
}
};
class FastPackedSealedObjectElementsAccessor
: public FastSmiOrObjectElementsAccessor<
FastPackedSealedObjectElementsAccessor,
ElementsKindTraits<PACKED_SEALED_ELEMENTS>> {
public:
explicit FastPackedSealedObjectElementsAccessor(const char* name)
: FastSmiOrObjectElementsAccessor<
FastPackedSealedObjectElementsAccessor,
ElementsKindTraits<PACKED_SEALED_ELEMENTS>>(name) {}
static Handle<Object> RemoveElement(Handle<JSArray> receiver,
Where remove_position) {
UNREACHABLE();
}
static void DeleteImpl(Handle<JSObject> obj, uint32_t entry) {
UNREACHABLE();
}
static void DeleteAtEnd(Handle<JSObject> obj,
Handle<BackingStore> backing_store, uint32_t entry) {
UNREACHABLE();
}
static void DeleteCommon(Handle<JSObject> obj, uint32_t entry,
Handle<FixedArrayBase> store) {
UNREACHABLE();
}
static Handle<Object> PopImpl(Handle<JSArray> receiver) { UNREACHABLE(); }
static uint32_t PushImpl(Handle<JSArray> receiver, Arguments* args,
uint32_t push_size) {
UNREACHABLE();
}
static void AddImpl(Handle<JSObject> object, uint32_t index,
Handle<Object> value, PropertyAttributes attributes,
uint32_t new_capacity) {
UNREACHABLE();
}
};
class FastHoleyObjectElementsAccessor
: public FastSmiOrObjectElementsAccessor<
FastHoleyObjectElementsAccessor, ElementsKindTraits<HOLEY_ELEMENTS>> {
......@@ -2733,6 +2845,8 @@ class FastDoubleElementsAccessor
CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size);
break;
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case HOLEY_ELEMENTS:
CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size);
break;
......
......@@ -1886,27 +1886,29 @@ void AccessorAssembler::EmitElementLoad(
if_fast_double(this), if_fast_holey_double(this), if_nonfast(this),
if_dictionary(this);
GotoIf(
Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
Int32GreaterThan(elements_kind, Int32Constant(LAST_FROZEN_ELEMENTS_KIND)),
&if_nonfast);
EmitFastElementsBoundsCheck(object, elements, intptr_index,
is_jsarray_condition, out_of_bounds);
int32_t kinds[] = {// Handled by if_fast_packed.
PACKED_SMI_ELEMENTS, PACKED_ELEMENTS,
PACKED_SEALED_ELEMENTS, PACKED_FROZEN_ELEMENTS,
// Handled by if_fast_holey.
HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS,
// Handled by if_fast_double.
PACKED_DOUBLE_ELEMENTS,
// Handled by if_fast_holey_double.
HOLEY_DOUBLE_ELEMENTS};
Label* labels[] = {// FAST_{SMI,}_ELEMENTS
&if_fast_packed, &if_fast_packed,
// FAST_HOLEY_{SMI,}_ELEMENTS
&if_fast_holey, &if_fast_holey,
// PACKED_DOUBLE_ELEMENTS
&if_fast_double,
// HOLEY_DOUBLE_ELEMENTS
&if_fast_holey_double};
Label* labels[] = {
// FAST_{SMI,}_ELEMENTS
&if_fast_packed, &if_fast_packed, &if_fast_packed, &if_fast_packed,
// FAST_HOLEY_{SMI,}_ELEMENTS
&if_fast_holey, &if_fast_holey,
// PACKED_DOUBLE_ELEMENTS
&if_fast_double,
// HOLEY_DOUBLE_ELEMENTS
&if_fast_holey_double};
Switch(elements_kind, unimplemented_elements_kind, kinds, labels,
arraysize(kinds));
......
......@@ -1189,6 +1189,7 @@ Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map,
is_js_array, load_mode);
}
DCHECK(IsFastElementsKind(elements_kind) ||
IsFrozenOrSealedElementsKind(elements_kind) ||
IsFixedTypedArrayElementsKind(elements_kind));
bool convert_hole_to_undefined =
(elements_kind == HOLEY_SMI_ELEMENTS ||
......@@ -1943,6 +1944,7 @@ Handle<Object> KeyedStoreIC::StoreElementHandler(
code =
CodeFactory::KeyedStoreIC_SloppyArguments(isolate(), store_mode).code();
} else if (receiver_map->has_fast_elements() ||
PACKED_SEALED_ELEMENTS == receiver_map->elements_kind() ||
receiver_map->has_fixed_typed_array_elements()) {
TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_StoreFastElementStub);
code = CodeFactory::StoreFastElementIC(isolate(), store_mode).code();
......@@ -1955,7 +1957,8 @@ Handle<Object> KeyedStoreIC::StoreElementHandler(
} else {
// TODO(jgruber): Update counter name.
TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_StoreElementStub);
DCHECK_EQ(DICTIONARY_ELEMENTS, receiver_map->elements_kind());
DCHECK(DICTIONARY_ELEMENTS == receiver_map->elements_kind() ||
PACKED_FROZEN_ELEMENTS == receiver_map->elements_kind());
code = CodeFactory::KeyedStoreIC_Slow(isolate(), store_mode).code();
}
......
......@@ -1163,6 +1163,11 @@ LookupIterator::State LookupIterator::LookupInRegularHolder(
return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
}
property_details_ = accessor->GetDetails(js_object, number_);
if (map->is_frozen_or_sealed_elements()) {
PropertyAttributes attrs =
map->elements_kind() == PACKED_SEALED_ELEMENTS ? SEALED : FROZEN;
property_details_ = property_details_.CopyAddAttributes(attrs);
}
} else if (!map->is_dictionary_map()) {
DescriptorArray descriptors = map->instance_descriptors();
int number = descriptors->SearchWithCache(isolate_, *name_, map);
......
......@@ -637,6 +637,7 @@ void JSObject::JSObjectVerify(Isolate* isolate) {
// pointer may point to a one pointer filler map.
if (ElementsAreSafeToExamine()) {
CHECK_EQ((map()->has_fast_smi_or_object_elements() ||
map()->is_frozen_or_sealed_elements() ||
(elements() == GetReadOnlyRoots().empty_fixed_array()) ||
HasFastStringWrapperElements()),
(elements()->map() == GetReadOnlyRoots().fixed_array_map() ||
......@@ -1267,10 +1268,11 @@ void JSArray::JSArrayVerify(Isolate* isolate) {
}
if (!length()->IsNumber()) return;
// Verify that the length and the elements backing store are in sync.
if (length()->IsSmi() && HasFastElements()) {
if (length()->IsSmi() && (HasFastElements() || HasFrozenOrSealedElements())) {
if (elements()->length() > 0) {
CHECK_IMPLIES(HasDoubleElements(), elements()->IsFixedDoubleArray());
CHECK_IMPLIES(HasSmiOrObjectElements(), elements()->IsFixedArray());
CHECK_IMPLIES(HasSmiOrObjectElements() || HasFrozenOrSealedElements(),
elements()->IsFixedArray());
}
int size = Smi::ToInt(length());
// Holey / Packed backing stores might have slack or might have not been
......@@ -2249,6 +2251,8 @@ void JSObject::IncrementSpillStatistics(Isolate* isolate,
case PACKED_DOUBLE_ELEMENTS:
case HOLEY_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case FAST_STRING_WRAPPER_ELEMENTS: {
info->number_of_objects_with_fast_elements_++;
int holes = 0;
......
......@@ -669,6 +669,8 @@ void JSObject::PrintElements(std::ostream& os) { // NOLINT
case PACKED_SMI_ELEMENTS:
case HOLEY_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case FAST_STRING_WRAPPER_ELEMENTS: {
PrintFixedArrayElements(os, FixedArray::cast(elements()));
break;
......
......@@ -750,7 +750,7 @@ ElementsKind JSObject::GetElementsKind() const {
DCHECK(fixed_array->IsFixedArray());
DCHECK(fixed_array->IsDictionary());
} else {
DCHECK(kind > DICTIONARY_ELEMENTS);
DCHECK(kind > DICTIONARY_ELEMENTS || IsFrozenOrSealedElementsKind(kind));
}
DCHECK(!IsSloppyArgumentsElementsKind(kind) ||
(elements()->IsFixedArray() && elements()->length() >= 2));
......@@ -793,6 +793,10 @@ bool JSObject::HasPackedElements() {
return GetElementsKind() == PACKED_ELEMENTS;
}
bool JSObject::HasFrozenOrSealedElements() {
return IsFrozenOrSealedElementsKind(GetElementsKind());
}
bool JSObject::HasFastArgumentsElements() {
return GetElementsKind() == FAST_SLOPPY_ARGUMENTS_ELEMENTS;
}
......
......@@ -3671,6 +3671,8 @@ bool TestElementsIntegrityLevel(JSObject object, PropertyAttributes level) {
return false; // TypedArrays with elements can't be frozen.
return TestPropertiesIntegrityLevel(object, level);
}
if (kind == PACKED_FROZEN_ELEMENTS) return true;
if (kind == PACKED_SEALED_ELEMENTS && level != FROZEN) return true;
ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
// Only DICTIONARY_ELEMENTS and SLOW_SLOPPY_ARGUMENTS_ELEMENTS have
......@@ -3812,6 +3814,10 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
}
if (attrs == NONE && !object->map()->is_extensible()) return Just(true);
ElementsKind old_elements_kind = object->map()->elements_kind();
if (attrs != FROZEN && old_elements_kind == PACKED_SEALED_ELEMENTS)
return Just(true);
if (old_elements_kind == PACKED_FROZEN_ELEMENTS) return Just(true);
if (object->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, object);
......@@ -3869,7 +3875,8 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
Handle<Map> transition_map(transition, isolate);
DCHECK(transition_map->has_dictionary_elements() ||
transition_map->has_fixed_typed_array_elements() ||
transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS);
transition_map->elements_kind() == SLOW_STRING_WRAPPER_ELEMENTS ||
transition_map->is_frozen_or_sealed_elements());
DCHECK(!transition_map->is_extensible());
JSObject::MigrateToMap(object, transition_map);
} else if (transitions.CanHaveMoreTransitions()) {
......@@ -3911,6 +3918,10 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
}
}
if (object->map()->is_frozen_or_sealed_elements()) {
return Just(true);
}
// Both seal and preventExtensions always go through without modifications to
// typed array elements. Freeze works only if there are no actual elements.
if (object->HasFixedTypedArrayElements()) {
......@@ -3962,6 +3973,8 @@ bool JSObject::HasEnumerableElements() {
switch (object->GetElementsKind()) {
case PACKED_SMI_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS: {
int length = object->IsJSArray()
? Smi::ToInt(JSArray::cast(object)->length())
......@@ -4716,6 +4729,8 @@ int JSObject::GetFastElementsUsage() {
case PACKED_SMI_ELEMENTS:
case PACKED_DOUBLE_ELEMENTS:
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
return IsJSArray() ? Smi::ToInt(JSArray::cast(*this)->length())
: store->length();
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
......
......@@ -341,8 +341,10 @@ class JSObject : public JSReceiver {
inline bool HasSloppyArgumentsElements();
inline bool HasStringWrapperElements();
inline bool HasDictionaryElements();
// Returns true if an object has elements of PACKED_ELEMENTS
inline bool HasPackedElements();
inline bool HasFrozenOrSealedElements();
inline bool HasFixedTypedArrayElements();
......
......@@ -500,6 +500,10 @@ bool Map::has_dictionary_elements() const {
return IsDictionaryElementsKind(elements_kind());
}
bool Map::is_frozen_or_sealed_elements() const {
return IsFrozenOrSealedElementsKind(elements_kind());
}
void Map::set_is_dictionary_map(bool value) {
uint32_t new_bit_field3 = IsDictionaryMapBit::update(bit_field3(), value);
new_bit_field3 = IsUnstableBit::update(new_bit_field3, value);
......
......@@ -2048,6 +2048,22 @@ Handle<Map> Map::CopyForPreventExtensions(Isolate* isolate, Handle<Map> map,
ElementsKind new_kind = IsStringWrapperElementsKind(map->elements_kind())
? SLOW_STRING_WRAPPER_ELEMENTS
: DICTIONARY_ELEMENTS;
switch (map->elements_kind()) {
case PACKED_ELEMENTS:
if (attrs_to_add == SEALED) {
new_kind = PACKED_SEALED_ELEMENTS;
} else if (attrs_to_add == FROZEN) {
new_kind = PACKED_FROZEN_ELEMENTS;
}
break;
case PACKED_SEALED_ELEMENTS:
if (attrs_to_add == FROZEN) {
new_kind = PACKED_FROZEN_ELEMENTS;
}
break;
default:
break;
}
new_map->set_elements_kind(new_kind);
}
return new_map;
......
......@@ -421,6 +421,7 @@ class Map : public HeapObject {
inline bool has_fast_string_wrapper_elements() const;
inline bool has_fixed_typed_array_elements() const;
inline bool has_dictionary_elements() const;
inline bool is_frozen_or_sealed_elements() const;
// Returns true if the current map doesn't have DICTIONARY_ELEMENTS but if a
// map with DICTIONARY_ELEMENTS was found in the prototype chain.
......
......@@ -155,6 +155,8 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
// Deep copy own elements.
switch (copy->GetElementsKind()) {
case PACKED_ELEMENTS:
case PACKED_FROZEN_ELEMENTS:
case PACKED_SEALED_ELEMENTS:
case HOLEY_ELEMENTS: {
Handle<FixedArray> elements(FixedArray::cast(copy->elements()), isolate);
if (elements->map() == ReadOnlyRoots(isolate).fixed_cow_array_map()) {
......
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