diff options
Diffstat (limited to 'src/objects.cc')
-rw-r--r-- | src/objects.cc | 3377 |
1 files changed, 1567 insertions, 1810 deletions
diff --git a/src/objects.cc b/src/objects.cc index e711a219..9f9a6280 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -18,6 +18,7 @@ #include "src/api-arguments-inl.h" #include "src/api-natives.h" #include "src/api.h" +#include "src/arguments.h" #include "src/base/bits.h" #include "src/base/utils/random-number-generator.h" #include "src/bootstrapper.h" @@ -28,6 +29,7 @@ #include "src/counters-inl.h" #include "src/counters.h" #include "src/date.h" +#include "src/debug/debug-evaluate.h" #include "src/debug/debug.h" #include "src/deoptimizer.h" #include "src/elements.h" @@ -49,6 +51,7 @@ #include "src/log.h" #include "src/lookup.h" #include "src/macro-assembler.h" +#include "src/map-updater.h" #include "src/messages.h" #include "src/objects-body-descriptors-inl.h" #include "src/property-descriptor.h" @@ -139,7 +142,8 @@ MaybeHandle<JSReceiver> Object::ConvertReceiver(Isolate* isolate, } // static -MaybeHandle<Object> Object::ToNumber(Handle<Object> input) { +MaybeHandle<Object> Object::ConvertToNumber(Isolate* isolate, + Handle<Object> input) { while (true) { if (input->IsNumber()) { return input; @@ -150,15 +154,10 @@ MaybeHandle<Object> Object::ToNumber(Handle<Object> input) { if (input->IsOddball()) { return Oddball::ToNumber(Handle<Oddball>::cast(input)); } - Isolate* const isolate = Handle<HeapObject>::cast(input)->GetIsolate(); if (input->IsSymbol()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToNumber), Object); } - if (input->IsSimd128Value()) { - THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSimdToNumber), - Object); - } ASSIGN_RETURN_ON_EXCEPTION( isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input), ToPrimitiveHint::kNumber), @@ -166,28 +165,33 @@ MaybeHandle<Object> Object::ToNumber(Handle<Object> input) { } } - // static -MaybeHandle<Object> Object::ToInteger(Isolate* isolate, Handle<Object> input) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); +MaybeHandle<Object> Object::ConvertToInteger(Isolate* isolate, + Handle<Object> input) { + ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input), + Object); + if (input->IsSmi()) return input; return isolate->factory()->NewNumber(DoubleToInteger(input->Number())); } - // static -MaybeHandle<Object> Object::ToInt32(Isolate* isolate, Handle<Object> input) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); +MaybeHandle<Object> Object::ConvertToInt32(Isolate* isolate, + Handle<Object> input) { + ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input), + Object); + if (input->IsSmi()) return input; return isolate->factory()->NewNumberFromInt(DoubleToInt32(input->Number())); } - // static -MaybeHandle<Object> Object::ToUint32(Isolate* isolate, Handle<Object> input) { - ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); +MaybeHandle<Object> Object::ConvertToUint32(Isolate* isolate, + Handle<Object> input) { + ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ConvertToNumber(isolate, input), + Object); + if (input->IsSmi()) return handle(Smi::cast(*input)->ToUint32Smi(), isolate); return isolate->factory()->NewNumberFromUint(DoubleToUint32(input->Number())); } - // static MaybeHandle<Name> Object::ConvertToName(Isolate* isolate, Handle<Object> input) { @@ -198,12 +202,35 @@ MaybeHandle<Name> Object::ConvertToName(Isolate* isolate, return ToString(isolate, input); } +// ES6 7.1.14 // static -MaybeHandle<String> Object::ToString(Isolate* isolate, Handle<Object> input) { - while (true) { - if (input->IsString()) { - return Handle<String>::cast(input); +MaybeHandle<Object> Object::ConvertToPropertyKey(Isolate* isolate, + Handle<Object> value) { + // 1. Let key be ToPrimitive(argument, hint String). + MaybeHandle<Object> maybe_key = + Object::ToPrimitive(value, ToPrimitiveHint::kString); + // 2. ReturnIfAbrupt(key). + Handle<Object> key; + if (!maybe_key.ToHandle(&key)) return key; + // 3. If Type(key) is Symbol, then return key. + if (key->IsSymbol()) return key; + // 4. Return ToString(key). + // Extending spec'ed behavior, we'd be happy to return an element index. + if (key->IsSmi()) return key; + if (key->IsHeapNumber()) { + uint32_t uint_value; + if (value->ToArrayLength(&uint_value) && + uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) { + return handle(Smi::FromInt(static_cast<int>(uint_value)), isolate); } + } + return Object::ToString(isolate, key); +} + +// static +MaybeHandle<String> Object::ConvertToString(Isolate* isolate, + Handle<Object> input) { + while (true) { if (input->IsOddball()) { return handle(Handle<Oddball>::cast(input)->to_string(), isolate); } @@ -214,13 +241,15 @@ MaybeHandle<String> Object::ToString(Isolate* isolate, Handle<Object> input) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kSymbolToString), String); } - if (input->IsSimd128Value()) { - return Simd128Value::ToString(Handle<Simd128Value>::cast(input)); - } ASSIGN_RETURN_ON_EXCEPTION( isolate, input, JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(input), ToPrimitiveHint::kString), String); + // The previous isString() check happened in Object::ToString and thus we + // put it at the end of the loop in this helper. + if (input->IsString()) { + return Handle<String>::cast(input); + } } } @@ -268,8 +297,7 @@ Handle<String> Object::NoSideEffectsToString(Isolate* isolate, Handle<Object> input) { DisallowJavascriptExecution no_js(isolate); - if (input->IsString() || input->IsNumber() || input->IsOddball() || - input->IsSimd128Value()) { + if (input->IsString() || input->IsNumber() || input->IsOddball()) { return Object::ToString(isolate, input).ToHandleChecked(); } else if (input->IsFunction()) { // -- F u n c t i o n @@ -375,11 +403,16 @@ Handle<String> Object::NoSideEffectsToString(Isolate* isolate, } // static -MaybeHandle<Object> Object::ToLength(Isolate* isolate, Handle<Object> input) { +MaybeHandle<Object> Object::ConvertToLength(Isolate* isolate, + Handle<Object> input) { ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); + if (input->IsSmi()) { + int value = std::max(Smi::cast(*input)->value(), 0); + return handle(Smi::FromInt(value), isolate); + } double len = DoubleToInteger(input->Number()); if (len <= 0.0) { - len = 0.0; + return handle(Smi::kZero, isolate); } else if (len >= kMaxSafeInteger) { len = kMaxSafeInteger; } @@ -387,10 +420,12 @@ MaybeHandle<Object> Object::ToLength(Isolate* isolate, Handle<Object> input) { } // static -MaybeHandle<Object> Object::ToIndex(Isolate* isolate, Handle<Object> input, - MessageTemplate::Template error_index) { - if (input->IsUndefined(isolate)) return isolate->factory()->NewNumber(0.0); +MaybeHandle<Object> Object::ConvertToIndex( + Isolate* isolate, Handle<Object> input, + MessageTemplate::Template error_index) { + if (input->IsUndefined(isolate)) return handle(Smi::kZero, isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, input, ToNumber(input), Object); + if (input->IsSmi() && Smi::cast(*input)->value() >= 0) return input; double len = DoubleToInteger(input->Number()) + 0.0; auto js_len = isolate->factory()->NewNumber(len); if (len < 0.0 || len > kMaxSafeInteger) { @@ -404,7 +439,7 @@ bool Object::BooleanValue() { DCHECK(IsHeapObject()); Isolate* isolate = HeapObject::cast(this)->GetIsolate(); if (IsBoolean()) return IsTrue(isolate); - if (IsUndefined(isolate) || IsNull(isolate)) return false; + if (IsNullOrUndefined(isolate)) return false; if (IsUndetectable()) return false; // Undetectable object is false. if (IsString()) return String::cast(this)->length() != 0; if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); @@ -537,18 +572,6 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) { } else { return Just(false); } - } else if (x->IsSimd128Value()) { - if (y->IsSimd128Value()) { - return Just(Simd128Value::Equals(Handle<Simd128Value>::cast(x), - Handle<Simd128Value>::cast(y))); - } else if (y->IsJSReceiver()) { - if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) - .ToHandle(&y)) { - return Nothing<bool>(); - } - } else { - return Just(false); - } } else if (x->IsJSReceiver()) { if (y->IsJSReceiver()) { return Just(x.is_identical_to(y)); @@ -574,9 +597,6 @@ bool Object::StrictEquals(Object* that) { } else if (this->IsString()) { if (!that->IsString()) return false; return String::cast(this)->Equals(String::cast(that)); - } else if (this->IsSimd128Value()) { - if (!that->IsSimd128Value()) return false; - return Simd128Value::cast(this)->Equals(Simd128Value::cast(that)); } return this == that; } @@ -592,10 +612,6 @@ Handle<String> Object::TypeOf(Isolate* isolate, Handle<Object> object) { if (object->IsString()) return isolate->factory()->string_string(); if (object->IsSymbol()) return isolate->factory()->symbol_string(); if (object->IsString()) return isolate->factory()->string_string(); -#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ - if (object->Is##Type()) return isolate->factory()->type##_string(); - SIMD128_TYPES(SIMD128_TYPE) -#undef SIMD128_TYPE if (object->IsCallable()) return isolate->factory()->function_string(); return isolate->factory()->object_string(); } @@ -847,7 +863,7 @@ MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver, Isolate* isolate = receiver->GetIsolate(); ASSIGN_RETURN_ON_EXCEPTION(isolate, func, JSReceiver::GetProperty(receiver, name), Object); - if (func->IsNull(isolate) || func->IsUndefined(isolate)) { + if (func->IsNullOrUndefined(isolate)) { return isolate->factory()->undefined_value(); } if (!func->IsCallable()) { @@ -858,10 +874,30 @@ MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver, return func; } +namespace { +MaybeHandle<FixedArray> CreateListFromArrayLikeFastPath( + Isolate* isolate, Handle<Object> object, ElementTypes element_types) { + if (element_types != ElementTypes::kAll || !object->IsJSArray()) { + return MaybeHandle<FixedArray>(); + } + Handle<JSArray> array = Handle<JSArray>::cast(object); + uint32_t length; + if (!array->HasArrayPrototype(isolate) || + !array->length()->ToUint32(&length) || !array->HasFastElements() || + !JSObject::PrototypeHasNoElements(isolate, *array)) { + return MaybeHandle<FixedArray>(); + } + return array->GetElementsAccessor()->CreateListFromArray(isolate, array); +} +} // namespace // static MaybeHandle<FixedArray> Object::CreateListFromArrayLike( Isolate* isolate, Handle<Object> object, ElementTypes element_types) { + // Fast-path for JS_ARRAY_TYPE. + MaybeHandle<FixedArray> fast_result = + CreateListFromArrayLikeFastPath(isolate, object, element_types); + if (!fast_result.is_null()) return fast_result; // 1. ReturnIfAbrupt(object). // 2. (default elementTypes -- not applicable.) // 3. If Type(obj) is not Object, throw a TypeError exception. @@ -872,6 +908,7 @@ MaybeHandle<FixedArray> Object::CreateListFromArrayLike( "CreateListFromArrayLike")), FixedArray); } + // 4. Let len be ? ToLength(? Get(obj, "length")). Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); Handle<Object> raw_length_number; @@ -1790,11 +1827,13 @@ MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck( GetPropertyWithInterceptor(it, &done), Object); if (done) return result; } + } else { - MaybeHandle<Object> result; + Handle<Object> result; bool done; - result = GetPropertyWithInterceptorInternal(it, interceptor, &done); - RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); + ASSIGN_RETURN_ON_EXCEPTION( + isolate, result, + GetPropertyWithInterceptorInternal(it, interceptor, &done), Object); if (done) return result; } @@ -1830,7 +1869,7 @@ Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck( } else { Maybe<PropertyAttributes> result = GetPropertyAttributesWithInterceptorInternal(it, interceptor); - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<PropertyAttributes>()); + if (isolate->has_pending_exception()) return Nothing<PropertyAttributes>(); if (result.FromMaybe(ABSENT) != ABSENT) return result; } isolate->ReportFailedAccessCheck(checked); @@ -1866,10 +1905,9 @@ Maybe<bool> JSObject::SetPropertyWithFailedAccessCheck( } else { Maybe<bool> result = SetPropertyWithInterceptorInternal( it, interceptor, should_throw, value); - RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); + if (isolate->has_pending_exception()) return Nothing<bool>(); if (result.IsJust()) return result; } - isolate->ReportFailedAccessCheck(checked); RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>()); return Just(true); @@ -1937,6 +1975,178 @@ Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate, } } +namespace { + +bool HasExcludedProperty( + const ScopedVector<Handle<Object>>* excluded_properties, + Handle<Object> search_element) { + // TODO(gsathya): Change this to be a hashtable. + for (int i = 0; i < excluded_properties->length(); i++) { + if (search_element->SameValue(*excluded_properties->at(i))) { + return true; + } + } + + return false; +} + +MUST_USE_RESULT Maybe<bool> FastAssign( + Handle<JSReceiver> target, Handle<Object> source, + const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) { + // Non-empty strings are the only non-JSReceivers that need to be handled + // explicitly by Object.assign. + if (!source->IsJSReceiver()) { + return Just(!source->IsString() || String::cast(*source)->length() == 0); + } + + // If the target is deprecated, the object will be updated on first store. If + // the source for that store equals the target, this will invalidate the + // cached representation of the source. Preventively upgrade the target. + // Do this on each iteration since any property load could cause deprecation. + if (target->map()->is_deprecated()) { + JSObject::MigrateInstance(Handle<JSObject>::cast(target)); + } + + Isolate* isolate = target->GetIsolate(); + Handle<Map> map(JSReceiver::cast(*source)->map(), isolate); + + if (!map->IsJSObjectMap()) return Just(false); + if (!map->OnlyHasSimpleProperties()) return Just(false); + + Handle<JSObject> from = Handle<JSObject>::cast(source); + if (from->elements() != isolate->heap()->empty_fixed_array()) { + return Just(false); + } + + Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate); + int length = map->NumberOfOwnDescriptors(); + + bool stable = true; + + for (int i = 0; i < length; i++) { + Handle<Name> next_key(descriptors->GetKey(i), isolate); + Handle<Object> prop_value; + // Directly decode from the descriptor array if |from| did not change shape. + if (stable) { + PropertyDetails details = descriptors->GetDetails(i); + if (!details.IsEnumerable()) continue; + if (details.kind() == kData) { + if (details.location() == kDescriptor) { + prop_value = handle(descriptors->GetValue(i), isolate); + } else { + Representation representation = details.representation(); + FieldIndex index = FieldIndex::ForDescriptor(*map, i); + prop_value = JSObject::FastPropertyAt(from, representation, index); + } + } else { + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, prop_value, JSReceiver::GetProperty(from, next_key), + Nothing<bool>()); + stable = from->map() == *map; + } + } else { + // 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. + LookupIterator it(from, next_key, from, + LookupIterator::OWN_SKIP_INTERCEPTOR); + if (!it.IsFound()) continue; + DCHECK(it.state() == LookupIterator::DATA || + it.state() == LookupIterator::ACCESSOR); + if (!it.IsEnumerable()) continue; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, prop_value, Object::GetProperty(&it), Nothing<bool>()); + } + + if (use_set) { + LookupIterator it(target, next_key, target); + bool call_to_js = it.IsFound() && it.state() != LookupIterator::DATA; + Maybe<bool> result = Object::SetProperty( + &it, prop_value, STRICT, Object::CERTAINLY_NOT_STORE_FROM_KEYED); + if (result.IsNothing()) return result; + if (stable && call_to_js) stable = from->map() == *map; + } else { + if (excluded_properties != nullptr && + HasExcludedProperty(excluded_properties, next_key)) { + continue; + } + + // 4a ii 2. Perform ? CreateDataProperty(target, nextKey, propValue). + bool success; + LookupIterator it = LookupIterator::PropertyOrElement( + isolate, target, next_key, &success, LookupIterator::OWN); + CHECK(success); + CHECK( + JSObject::CreateDataProperty(&it, prop_value, Object::THROW_ON_ERROR) + .FromJust()); + } + } + + return Just(true); +} +} // namespace + +// static +Maybe<bool> JSReceiver::SetOrCopyDataProperties( + Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source, + const ScopedVector<Handle<Object>>* excluded_properties, bool use_set) { + Maybe<bool> fast_assign = + FastAssign(target, source, excluded_properties, use_set); + if (fast_assign.IsNothing()) return Nothing<bool>(); + if (fast_assign.FromJust()) return Just(true); + + Handle<JSReceiver> from = Object::ToObject(isolate, source).ToHandleChecked(); + // 3b. Let keys be ? from.[[OwnPropertyKeys]](). + Handle<FixedArray> keys; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, keys, + KeyAccumulator::GetKeys(from, KeyCollectionMode::kOwnOnly, ALL_PROPERTIES, + GetKeysConversion::kKeepNumbers), + Nothing<bool>()); + + // 4. Repeat for each element nextKey of keys in List order, + for (int j = 0; j < keys->length(); ++j) { + Handle<Object> next_key(keys->get(j), isolate); + // 4a i. Let desc be ? from.[[GetOwnProperty]](nextKey). + PropertyDescriptor desc; + Maybe<bool> found = + JSReceiver::GetOwnPropertyDescriptor(isolate, from, next_key, &desc); + if (found.IsNothing()) return Nothing<bool>(); + // 4a ii. If desc is not undefined and desc.[[Enumerable]] is true, then + if (found.FromJust() && desc.enumerable()) { + // 4a ii 1. Let propValue be ? Get(from, nextKey). + Handle<Object> prop_value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, prop_value, + Runtime::GetObjectProperty(isolate, from, next_key), Nothing<bool>()); + + if (use_set) { + // 4c ii 2. Let status be ? Set(to, nextKey, propValue, true). + Handle<Object> status; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, status, Runtime::SetObjectProperty( + isolate, target, next_key, prop_value, STRICT), + Nothing<bool>()); + } else { + if (excluded_properties != nullptr && + HasExcludedProperty(excluded_properties, next_key)) { + continue; + } + + // 4a ii 2. Perform ! CreateDataProperty(target, nextKey, propValue). + bool success; + LookupIterator it = LookupIterator::PropertyOrElement( + isolate, target, next_key, &success, LookupIterator::OWN); + CHECK(success); + CHECK(JSObject::CreateDataProperty(&it, prop_value, + Object::THROW_ON_ERROR) + .FromJust()); + } + } + } + + return Just(true); +} + Map* Object::GetPrototypeChainRootMap(Isolate* isolate) { DisallowHeapAllocation no_alloc; if (IsSmi()) { @@ -1944,8 +2154,8 @@ Map* Object::GetPrototypeChainRootMap(Isolate* isolate) { return native_context->number_function()->initial_map(); } - // The object is either a number, a string, a symbol, a boolean, a SIMD value, - // a real JS object, or a Harmony proxy. + // The object is either a number, a string, a symbol, a boolean, a real JS + // object, or a Harmony proxy. HeapObject* heap_object = HeapObject::cast(this); return heap_object->map()->GetPrototypeChainRootMap(isolate); } @@ -1971,8 +2181,8 @@ namespace { // objects. This avoids a double lookup in the cases where we know we will // add the hash to the JSObject if it does not already exist. Object* GetSimpleHash(Object* object) { - // The object is either a Smi, a HeapNumber, a name, an odd-ball, - // a SIMD value type, a real JS object, or a Harmony proxy. + // The object is either a Smi, a HeapNumber, a name, an odd-ball, a real JS + // object, or a Harmony proxy. if (object->IsSmi()) { uint32_t hash = ComputeIntegerHash(Smi::cast(object)->value(), kZeroHashSeed); @@ -1996,10 +2206,6 @@ Object* GetSimpleHash(Object* object) { uint32_t hash = Oddball::cast(object)->to_string()->Hash(); return Smi::FromInt(hash); } - if (object->IsSimd128Value()) { - uint32_t hash = Simd128Value::cast(object)->Hash(); - return Smi::FromInt(hash & Smi::kMaxValue); - } DCHECK(object->IsJSReceiver()); // Simply return the receiver as it is guaranteed to not be a SMI. return object; @@ -2046,23 +2252,6 @@ bool Object::SameValue(Object* other) { if (IsString() && other->IsString()) { return String::cast(this)->Equals(String::cast(other)); } - if (IsFloat32x4() && other->IsFloat32x4()) { - Float32x4* a = Float32x4::cast(this); - Float32x4* b = Float32x4::cast(other); - for (int i = 0; i < 4; i++) { - float x = a->get_lane(i); - float y = b->get_lane(i); - // Implements the ES5 SameValue operation for floating point types. - // http://www.ecma-international.org/ecma-262/6.0/#sec-samevalue - if (x != y && !(std::isnan(x) && std::isnan(y))) return false; - if (std::signbit(x) != std::signbit(y)) return false; - } - return true; - } else if (IsSimd128Value() && other->IsSimd128Value()) { - Simd128Value* a = Simd128Value::cast(this); - Simd128Value* b = Simd128Value::cast(other); - return a->map() == b->map() && a->BitwiseEquals(b); - } return false; } @@ -2082,23 +2271,6 @@ bool Object::SameValueZero(Object* other) { if (IsString() && other->IsString()) { return String::cast(this)->Equals(String::cast(other)); } - if (IsFloat32x4() && other->IsFloat32x4()) { - Float32x4* a = Float32x4::cast(this); - Float32x4* b = Float32x4::cast(other); - for (int i = 0; i < 4; i++) { - float x = a->get_lane(i); - float y = b->get_lane(i); - // Implements the ES6 SameValueZero operation for floating point types. - // http://www.ecma-international.org/ecma-262/6.0/#sec-samevaluezero - if (x != y && !(std::isnan(x) && std::isnan(y))) return false; - // SameValueZero doesn't distinguish between 0 and -0. - } - return true; - } else if (IsSimd128Value() && other->IsSimd128Value()) { - Simd128Value* a = Simd128Value::cast(this); - Simd128Value* b = Simd128Value::cast(other); - return a->map() == b->map() && a->BitwiseEquals(b); - } return false; } @@ -2154,6 +2326,40 @@ MaybeHandle<Object> Object::ArraySpeciesConstructor( } } +bool Object::IterationHasObservableEffects() { + // Check that this object is an array. + if (!IsJSArray()) return true; + JSArray* spread_array = JSArray::cast(this); + Isolate* isolate = spread_array->GetIsolate(); + + // Check that we have the original ArrayPrototype. + JSObject* array_proto = JSObject::cast(spread_array->map()->prototype()); + if (!isolate->is_initial_array_prototype(array_proto)) return true; + + // Check that the ArrayPrototype hasn't been modified in a way that would + // affect iteration. + if (!isolate->IsArrayIteratorLookupChainIntact()) return true; + + // Check that the map of the initial array iterator hasn't changed. + Map* iterator_map = isolate->initial_array_iterator_prototype()->map(); + if (!isolate->is_initial_array_iterator_prototype_map(iterator_map)) { + return true; + } + + // For FastPacked kinds, iteration will have the same effect as simply + // accessing each property in order. + ElementsKind array_kind = spread_array->GetElementsKind(); + if (IsFastPackedElementsKind(array_kind)) return false; + + // For FastHoley kinds, an element access on a hole would cause a lookup on + // the prototype. This could have different results if the prototype has been + // changed. + if (IsFastHoleyElementsKind(array_kind) && + isolate->IsFastArrayConstructorPrototypeChainIntact()) { + return false; + } + return true; +} void Object::ShortPrint(FILE* out) { OFStream os(out); @@ -2182,9 +2388,6 @@ std::ostream& operator<<(std::ostream& os, const Brief& v) { return os; } -// Declaration of the static Smi::kZero constant. -Smi* const Smi::kZero(nullptr); - void Smi::SmiPrint(std::ostream& os) const { // NOLINT os << value(); } @@ -2219,7 +2422,16 @@ Handle<String> String::SlowFlatten(Handle<ConsString> cons, DCHECK(cons->second()->length() != 0); // TurboFan can create cons strings with empty first parts. - if (cons->first()->length() == 0) return handle(cons->second()); + while (cons->first()->length() == 0) { + // We do not want to call this function recursively. Therefore we call + // String::Flatten only in those cases where String::SlowFlatten is not + // called again. + if (cons->second()->IsConsString() && !cons->second()->IsFlat()) { + cons = handle(ConsString::cast(cons->second())); + } else { + return String::Flatten(handle(cons->second())); + } + } DCHECK(AllowHeapAllocation::IsAllowed()); Isolate* isolate = cons->GetIsolate(); @@ -2270,7 +2482,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { Heap* heap = GetHeap(); bool is_one_byte = this->IsOneByteRepresentation(); bool is_internalized = this->IsInternalizedString(); - bool has_pointers = this->IsConsString() || this->IsSlicedString(); + bool has_pointers = StringShape(this).IsIndirect(); // Morph the string to an external string by replacing the map and // reinitializing the fields. This won't work if the space the existing @@ -2311,7 +2523,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { self->set_resource(resource); if (is_internalized) self->Hash(); // Force regeneration of the hash value. - heap->AdjustLiveBytes(this, new_size - size, Heap::CONCURRENT_TO_SWEEPER); + heap->AdjustLiveBytes(this, new_size - size); return true; } @@ -2342,7 +2554,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { if (size < ExternalString::kShortSize) return false; Heap* heap = GetHeap(); bool is_internalized = this->IsInternalizedString(); - bool has_pointers = this->IsConsString() || this->IsSlicedString(); + bool has_pointers = StringShape(this).IsIndirect(); // Morph the string to an external string by replacing the map and // reinitializing the fields. This won't work if the space the existing @@ -2377,7 +2589,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) { self->set_resource(resource); if (is_internalized) self->Hash(); // Force regeneration of the hash value. - heap->AdjustLiveBytes(this, new_size - size, Heap::CONCURRENT_TO_SWEEPER); + heap->AdjustLiveBytes(this, new_size - size); return true; } @@ -2619,10 +2831,10 @@ void Map::PrintReconfiguration(FILE* file, int modify_index, PropertyKind kind, void Map::PrintGeneralization( FILE* file, const char* reason, int modify_index, int split, - int descriptors, bool constant_to_field, Representation old_representation, - Representation new_representation, MaybeHandle<FieldType> old_field_type, - MaybeHandle<Object> old_value, MaybeHandle<FieldType> new_field_type, - MaybeHandle<Object> new_value) { + int descriptors, bool descriptor_to_field, + Representation old_representation, Representation new_representation, + MaybeHandle<FieldType> old_field_type, MaybeHandle<Object> old_value, + MaybeHandle<FieldType> new_field_type, MaybeHandle<Object> new_value) { OFStream os(file); os << "[generalizing]"; Name* name = instance_descriptors()->GetKey(modify_index); @@ -2632,7 +2844,7 @@ void Map::PrintGeneralization( os << "{symbol " << static_cast<void*>(name) << "}"; } os << ":"; - if (constant_to_field) { + if (descriptor_to_field) { os << "c"; } else { os << old_representation.Mnemonic() << "{"; @@ -2673,8 +2885,8 @@ void JSObject::PrintInstanceMigration(FILE* file, if (!o_r.Equals(n_r)) { String::cast(o->GetKey(i))->PrintOn(file); PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic()); - } else if (o->GetDetails(i).type() == DATA_CONSTANT && - n->GetDetails(i).type() == DATA) { + } else if (o->GetDetails(i).location() == kDescriptor && + n->GetDetails(i).location() == kField) { Name* name = o->GetKey(i); if (name->IsString()) { String::cast(name)->PrintOn(file); @@ -2809,17 +3021,6 @@ void HeapObject::HeapObjectShortPrint(std::ostream& os) { // NOLINT os << '>'; break; } - case SIMD128_VALUE_TYPE: { -#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ - if (Is##Type()) { \ - os << "<" #Type ">"; \ - break; \ - } - SIMD128_TYPES(SIMD128_TYPE) -#undef SIMD128_TYPE - UNREACHABLE(); - break; - } case JS_PROXY_TYPE: os << "<JSProxy>"; break; @@ -2910,101 +3111,6 @@ void HeapNumber::HeapNumberPrint(std::ostream& os) { // NOLINT #define READ_BYTE_FIELD(p, offset) \ (*reinterpret_cast<const byte*>(FIELD_ADDR_CONST(p, offset))) - -// static -Handle<String> Simd128Value::ToString(Handle<Simd128Value> input) { -#define SIMD128_TYPE(TYPE, Type, type, lane_count, lane_type) \ - if (input->Is##Type()) return Type::ToString(Handle<Type>::cast(input)); - SIMD128_TYPES(SIMD128_TYPE) -#undef SIMD128_TYPE - UNREACHABLE(); - return Handle<String>::null(); -} - - -// static -Handle<String> Float32x4::ToString(Handle<Float32x4> input) { - Isolate* const isolate = input->GetIsolate(); - char arr[100]; - Vector<char> buffer(arr, arraysize(arr)); - std::ostringstream os; - os << "SIMD.Float32x4(" - << std::string(DoubleToCString(input->get_lane(0), buffer)) << ", " - << std::string(DoubleToCString(input->get_lane(1), buffer)) << ", " - << std::string(DoubleToCString(input->get_lane(2), buffer)) << ", " - << std::string(DoubleToCString(input->get_lane(3), buffer)) << ")"; - return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); -} - - -#define SIMD128_BOOL_TO_STRING(Type, lane_count) \ - Handle<String> Type::ToString(Handle<Type> input) { \ - Isolate* const isolate = input->GetIsolate(); \ - std::ostringstream os; \ - os << "SIMD." #Type "("; \ - os << (input->get_lane(0) ? "true" : "false"); \ - for (int i = 1; i < lane_count; i++) { \ - os << ", " << (input->get_lane(i) ? "true" : "false"); \ - } \ - os << ")"; \ - return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); \ - } -SIMD128_BOOL_TO_STRING(Bool32x4, 4) -SIMD128_BOOL_TO_STRING(Bool16x8, 8) -SIMD128_BOOL_TO_STRING(Bool8x16, 16) -#undef SIMD128_BOOL_TO_STRING - - -#define SIMD128_INT_TO_STRING(Type, lane_count) \ - Handle<String> Type::ToString(Handle<Type> input) { \ - Isolate* const isolate = input->GetIsolate(); \ - char arr[100]; \ - Vector<char> buffer(arr, arraysize(arr)); \ - std::ostringstream os; \ - os << "SIMD." #Type "("; \ - os << IntToCString(input->get_lane(0), buffer); \ - for (int i = 1; i < lane_count; i++) { \ - os << ", " << IntToCString(input->get_lane(i), buffer); \ - } \ - os << ")"; \ - return isolate->factory()->NewStringFromAsciiChecked(os.str().c_str()); \ - } -SIMD128_INT_TO_STRING(Int32x4, 4) -SIMD128_INT_TO_STRING(Uint32x4, 4) -SIMD128_INT_TO_STRING(Int16x8, 8) -SIMD128_INT_TO_STRING(Uint16x8, 8) -SIMD128_INT_TO_STRING(Int8x16, 16) -SIMD128_INT_TO_STRING(Uint8x16, 16) -#undef SIMD128_INT_TO_STRING - - -bool Simd128Value::BitwiseEquals(const Simd128Value* other) const { - return READ_INT64_FIELD(this, kValueOffset) == - READ_INT64_FIELD(other, kValueOffset) && - READ_INT64_FIELD(this, kValueOffset + kInt64Size) == - READ_INT64_FIELD(other, kValueOffset + kInt64Size); -} - - -uint32_t Simd128Value::Hash() const { - uint32_t seed = v8::internal::kZeroHashSeed; - uint32_t hash; - hash = ComputeIntegerHash(READ_INT32_FIELD(this, kValueOffset), seed); - hash = ComputeIntegerHash( - READ_INT32_FIELD(this, kValueOffset + 1 * kInt32Size), hash * 31); - hash = ComputeIntegerHash( - READ_INT32_FIELD(this, kValueOffset + 2 * kInt32Size), hash * 31); - hash = ComputeIntegerHash( - READ_INT32_FIELD(this, kValueOffset + 3 * kInt32Size), hash * 31); - return hash; -} - - -void Simd128Value::CopyBits(void* destination) const { - memcpy(destination, &READ_BYTE_FIELD(this, kValueOffset), kSimd128Size); -} - - String* JSReceiver::class_name() { if (IsFunction()) { return GetHeap()->Function_string(); @@ -3064,8 +3170,7 @@ Handle<String> JSReceiver::GetConstructorName(Handle<JSReceiver> receiver) { : result; } - -Context* JSReceiver::GetCreationContext() { +Handle<Context> JSReceiver::GetCreationContext() { JSReceiver* receiver = this; while (receiver->IsJSBoundFunction()) { receiver = JSBoundFunction::cast(receiver)->bound_target_function(); @@ -3081,17 +3186,29 @@ Context* JSReceiver::GetCreationContext() { function = JSFunction::cast(receiver); } - return function->context()->native_context(); + return function->has_context() + ? Handle<Context>(function->context()->native_context()) + : Handle<Context>::null(); } -static Handle<Object> WrapType(Handle<FieldType> type) { +Handle<Object> Map::WrapFieldType(Handle<FieldType> type) { if (type->IsClass()) return Map::WeakCellForMap(type->AsClass()); return type; } +FieldType* Map::UnwrapFieldType(Object* wrapped_type) { + Object* value = wrapped_type; + if (value->IsWeakCell()) { + if (WeakCell::cast(value)->cleared()) return FieldType::None(); + value = WeakCell::cast(value)->value(); + } + return FieldType::cast(value); +} + MaybeHandle<Map> Map::CopyWithField(Handle<Map> map, Handle<Name> name, Handle<FieldType> type, PropertyAttributes attributes, + PropertyConstness constness, Representation representation, TransitionFlag flag) { DCHECK(DescriptorArray::kNotFound == @@ -3113,11 +3230,12 @@ MaybeHandle<Map> Map::CopyWithField(Handle<Map> map, Handle<Name> name, type = FieldType::Any(isolate); } - Handle<Object> wrapped_type(WrapType(type)); + Handle<Object> wrapped_type(WrapFieldType(type)); - DataDescriptor new_field_desc(name, index, wrapped_type, attributes, - representation); - Handle<Map> new_map = Map::CopyAddDescriptor(map, &new_field_desc, flag); + DCHECK_IMPLIES(!FLAG_track_constant_fields, constness == kMutable); + Descriptor d = Descriptor::DataField(name, index, attributes, constness, + representation, wrapped_type); + Handle<Map> new_map = Map::CopyAddDescriptor(map, &d, flag); int unused_property_fields = new_map->unused_property_fields() - 1; if (unused_property_fields < 0) { unused_property_fields += JSObject::kFieldsAdded; @@ -3137,9 +3255,18 @@ MaybeHandle<Map> Map::CopyWithConstant(Handle<Map> map, return MaybeHandle<Map>(); } - // Allocate new instance descriptors with (name, constant) added. - DataConstantDescriptor new_constant_desc(name, constant, attributes); - return Map::CopyAddDescriptor(map, &new_constant_desc, flag); + if (FLAG_track_constant_fields) { + Isolate* isolate = map->GetIsolate(); + Representation representation = constant->OptimalRepresentation(); + Handle<FieldType> type = constant->OptimalType(isolate, representation); + return CopyWithField(map, name, type, attributes, kConst, representation, + flag); + } else { + // Allocate new instance descriptors with (name, constant) added. + Descriptor d = Descriptor::DataConstant(name, 0, constant, attributes); + Handle<Map> new_map = Map::CopyAddDescriptor(map, &d, flag); + return new_map; + } } const char* Representation::Mnemonic() const { @@ -3157,6 +3284,34 @@ const char* Representation::Mnemonic() const { } } +bool Map::TransitionRemovesTaggedField(Map* target) { + int inobject = GetInObjectProperties(); + int target_inobject = target->GetInObjectProperties(); + for (int i = target_inobject; i < inobject; i++) { + FieldIndex index = FieldIndex::ForPropertyIndex(this, i); + if (!IsUnboxedDoubleField(index)) return true; + } + return false; +} + +bool Map::TransitionChangesTaggedFieldToUntaggedField(Map* target) { + int inobject = GetInObjectProperties(); + int target_inobject = target->GetInObjectProperties(); + int limit = Min(inobject, target_inobject); + for (int i = 0; i < limit; i++) { + FieldIndex index = FieldIndex::ForPropertyIndex(target, i); + if (!IsUnboxedDoubleField(index) && target->IsUnboxedDoubleField(index)) { + return true; + } + } + return false; +} + +bool Map::TransitionRequiresSynchronizationWithGC(Map* target) { + return TransitionRemovesTaggedField(target) || + TransitionChangesTaggedFieldToUntaggedField(target); +} + bool Map::InstancesNeedRewriting(Map* target) { int target_number_of_fields = target->NumberOfFields(); int target_inobject = target->GetInObjectProperties(); @@ -3276,7 +3431,7 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { FieldIndex::ForDescriptor(*new_map, new_map->LastAdded()); DCHECK(details.representation().IsDouble()); DCHECK(!new_map->IsUnboxedDoubleField(index)); - Handle<Object> value = isolate->factory()->NewHeapNumber(0, MUTABLE); + Handle<Object> value = isolate->factory()->NewMutableHeapNumber(); object->RawFastPropertyAtPut(index, *value); object->synchronized_set_map(*new_map); return; @@ -3292,11 +3447,12 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { // Properly initialize newly added property. Handle<Object> value; if (details.representation().IsDouble()) { - value = isolate->factory()->NewHeapNumber(0, MUTABLE); + value = isolate->factory()->NewMutableHeapNumber(); } else { value = isolate->factory()->uninitialized_value(); } - DCHECK_EQ(DATA, details.type()); + DCHECK_EQ(kField, details.location()); + DCHECK_EQ(kData, details.kind()); int target_index = details.field_index() - new_map->GetInObjectProperties(); DCHECK(target_index >= 0); // Must be a backing store index. new_storage->set(target_index, *value); @@ -3339,36 +3495,40 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { for (int i = 0; i < old_nof; i++) { PropertyDetails details = new_descriptors->GetDetails(i); - if (details.type() != DATA) continue; + if (details.location() != kField) continue; + DCHECK_EQ(kData, details.kind()); PropertyDetails old_details = old_descriptors->GetDetails(i); Representation old_representation = old_details.representation(); Representation representation = details.representation(); Handle<Object> value; - if (old_details.type() == ACCESSOR_CONSTANT) { - // In case of kAccessor -> kData property reconfiguration, the property - // must already be prepared for data or certain type. - DCHECK(!details.representation().IsNone()); - if (details.representation().IsDouble()) { - value = isolate->factory()->NewHeapNumber(0, MUTABLE); + if (old_details.location() == kDescriptor) { + if (old_details.kind() == kAccessor) { + // In case of kAccessor -> kData property reconfiguration, the property + // must already be prepared for data of certain type. + DCHECK(!details.representation().IsNone()); + if (details.representation().IsDouble()) { + value = isolate->factory()->NewMutableHeapNumber(); + } else { + value = isolate->factory()->uninitialized_value(); + } } else { - value = isolate->factory()->uninitialized_value(); + DCHECK_EQ(kData, old_details.kind()); + value = handle(old_descriptors->GetValue(i), isolate); + DCHECK(!old_representation.IsDouble() && !representation.IsDouble()); } - } else if (old_details.type() == DATA_CONSTANT) { - value = handle(old_descriptors->GetValue(i), isolate); - DCHECK(!old_representation.IsDouble() && !representation.IsDouble()); } else { + DCHECK_EQ(kField, old_details.location()); FieldIndex index = FieldIndex::ForDescriptor(*old_map, i); if (object->IsUnboxedDoubleField(index)) { - double old = object->RawFastDoublePropertyAt(index); - value = isolate->factory()->NewHeapNumber( - old, representation.IsDouble() ? MUTABLE : IMMUTABLE); + uint64_t old_bits = object->RawFastDoublePropertyAsBitsAt(index); + value = isolate->factory()->NewHeapNumberFromBits( + old_bits, representation.IsDouble() ? MUTABLE : IMMUTABLE); } else { value = handle(object->RawFastPropertyAt(index), isolate); if (!old_representation.IsDouble() && representation.IsDouble()) { - if (old_representation.IsNone()) { - value = handle(Smi::kZero, isolate); - } + DCHECK_IMPLIES(old_representation.IsNone(), + value->IsUninitialized(isolate)); value = Object::NewStorageFor(isolate, value, representation); } else if (old_representation.IsDouble() && !representation.IsDouble()) { @@ -3384,10 +3544,11 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { for (int i = old_nof; i < new_nof; i++) { PropertyDetails details = new_descriptors->GetDetails(i); - if (details.type() != DATA) continue; + if (details.location() != kField) continue; + DCHECK_EQ(kData, details.kind()); Handle<Object> value; if (details.representation().IsDouble()) { - value = isolate->factory()->NewHeapNumber(0, MUTABLE); + value = isolate->factory()->NewMutableHeapNumber(); } else { value = isolate->factory()->uninitialized_value(); } @@ -3401,6 +3562,8 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { Heap* heap = isolate->heap(); + heap->NotifyObjectLayoutChange(*object, no_allocation); + // Copy (real) inobject properties. If necessary, stop at number_of_fields to // avoid overwriting |one_pointer_filler_map|. int limit = Min(inobject, number_of_fields); @@ -3411,12 +3574,16 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { // yet. if (new_map->IsUnboxedDoubleField(index)) { DCHECK(value->IsMutableHeapNumber()); - object->RawFastDoublePropertyAtPut(index, - HeapNumber::cast(value)->value()); + // Ensure that all bits of the double value are preserved. + object->RawFastDoublePropertyAsBitsAtPut( + index, HeapNumber::cast(value)->value_as_bits()); if (i < old_number_of_fields && !old_map->IsUnboxedDoubleField(index)) { // Transition from tagged to untagged slot. heap->ClearRecordedSlot(*object, HeapObject::RawField(*object, index.offset())); + } else { + DCHECK(!heap->HasRecordedSlot( + *object, HeapObject::RawField(*object, index.offset()))); } } else { object->RawFastPropertyAtPut(index, value); @@ -3427,7 +3594,7 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { // If there are properties in the new backing store, trim it to the correct // size and install the backing store into the object. if (external > 0) { - heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(*array, inobject); + heap->RightTrimFixedArray(*array, inobject); object->set_properties(*array); } @@ -3440,8 +3607,7 @@ void MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) { Address address = object->address(); heap->CreateFillerObjectAt(address + new_instance_size, instance_size_delta, ClearRecordedSlots::kYes); - heap->AdjustLiveBytes(*object, -instance_size_delta, - Heap::CONCURRENT_TO_SWEEPER); + heap->AdjustLiveBytes(*object, -instance_size_delta); } // We are storing the new map using release store after creating a filler for @@ -3476,17 +3642,10 @@ void MigrateFastToSlow(Handle<JSObject> object, Handle<Map> new_map, for (int i = 0; i < real_size; i++) { PropertyDetails details = descs->GetDetails(i); Handle<Name> key(descs->GetKey(i)); - switch (details.type()) { - case DATA_CONSTANT: { - Handle<Object> value(descs->GetConstant(i), isolate); - PropertyDetails d(details.attributes(), DATA, i + 1, - PropertyCellType::kNoCell); - dictionary = NameDictionary::Add(dictionary, key, value, d); - break; - } - case DATA: { - FieldIndex index = FieldIndex::ForDescriptor(*map, i); - Handle<Object> value; + Handle<Object> value; + if (details.location() == kField) { + FieldIndex index = FieldIndex::ForDescriptor(*map, i); + if (details.kind() == kData) { if (object->IsUnboxedDoubleField(index)) { double old_value = object->RawFastDoublePropertyAt(index); value = isolate->factory()->NewHeapNumber(old_value); @@ -3498,27 +3657,19 @@ void MigrateFastToSlow(Handle<JSObject> object, Handle<Map> new_map, value = isolate->factory()->NewHeapNumber(old->value()); } } - PropertyDetails d(details.attributes(), DATA, i + 1, - PropertyCellType::kNoCell); - dictionary = NameDictionary::Add(dictionary, key, value, d); - break; - } - case ACCESSOR: { - FieldIndex index = FieldIndex::ForDescriptor(*map, i); - Handle<Object> value(object->RawFastPropertyAt(index), isolate); - PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1, - PropertyCellType::kNoCell); - dictionary = NameDictionary::Add(dictionary, key, value, d); - break; - } - case ACCESSOR_CONSTANT: { - Handle<Object> value(descs->GetCallbacksObject(i), isolate); - PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1, - PropertyCellType::kNoCell); - dictionary = NameDictionary::Add(dictionary, key, value, d); - break; + } else { + DCHECK_EQ(kAccessor, details.kind()); + value = handle(object->RawFastPropertyAt(index), isolate); } + + } else { + DCHECK_EQ(kDescriptor, details.location()); + value = handle(descs->GetValue(i), isolate); } + DCHECK(!value.is_null()); + PropertyDetails d(details.kind(), details.attributes(), i + 1, + PropertyCellType::kNoCell); + dictionary = NameDictionary::Add(dictionary, key, value, d); } // Copy the next enumeration index from instance descriptor. @@ -3527,17 +3678,18 @@ void MigrateFastToSlow(Handle<JSObject> object, Handle<Map> new_map, // From here on we cannot fail and we shouldn't GC anymore. DisallowHeapAllocation no_allocation; + Heap* heap = isolate->heap(); + heap->NotifyObjectLayoutChange(*object, no_allocation); + // Resize the object in the heap if necessary. int new_instance_size = new_map->instance_size(); int instance_size_delta = map->instance_size() - new_instance_size; DCHECK(instance_size_delta >= 0); if (instance_size_delta > 0) { - Heap* heap = isolate->heap(); heap->CreateFillerObjectAt(object->address() + new_instance_size, instance_size_delta, ClearRecordedSlots::kYes); - heap->AdjustLiveBytes(*object, -instance_size_delta, - Heap::CONCURRENT_TO_SWEEPER); + heap->AdjustLiveBytes(*object, -instance_size_delta); } // We are storing the new map using release store after creating a filler for @@ -3647,22 +3799,31 @@ int Map::NumberOfFields() { return result; } -Handle<Map> Map::CopyGeneralizeAllRepresentations( - Handle<Map> map, ElementsKind elements_kind, int modify_index, - StoreMode store_mode, PropertyKind kind, PropertyAttributes attributes, - const char* reason) { +void DescriptorArray::GeneralizeAllFields() { + int length = number_of_descriptors(); + for (int i = 0; i < length; i++) { + PropertyDetails details = GetDetails(i); + details = details.CopyWithRepresentation(Representation::Tagged()); + if (details.location() == kField) { + DCHECK_EQ(kData, details.kind()); + details = details.CopyWithConstness(kMutable); + SetValue(i, FieldType::Any()); + } + set(ToDetailsIndex(i), details.AsSmi()); + } +} + +Handle<Map> Map::CopyGeneralizeAllFields(Handle<Map> map, + ElementsKind elements_kind, + int modify_index, PropertyKind kind, + PropertyAttributes attributes, + const char* reason) { Isolate* isolate = map->GetIsolate(); Handle<DescriptorArray> old_descriptors(map->instance_descriptors(), isolate); int number_of_own_descriptors = map->NumberOfOwnDescriptors(); Handle<DescriptorArray> descriptors = DescriptorArray::CopyUpTo(old_descriptors, number_of_own_descriptors); - - for (int i = 0; i < number_of_own_descriptors; i++) { - descriptors->SetRepresentation(i, Representation::Tagged()); - if (descriptors->GetDetails(i).type() == DATA) { - descriptors->SetValue(i, FieldType::Any()); - } - } + descriptors->GeneralizeAllFields(); Handle<LayoutDescriptor> new_layout_descriptor( LayoutDescriptor::FastPointerLayout(), isolate); @@ -3673,14 +3834,16 @@ Handle<Map> Map::CopyGeneralizeAllRepresentations( // Unless the instance is being migrated, ensure that modify_index is a field. if (modify_index >= 0) { PropertyDetails details = descriptors->GetDetails(modify_index); - if (store_mode == FORCE_FIELD && - (details.type() != DATA || details.attributes() != attributes)) { - int field_index = details.type() == DATA ? details.field_index() - : new_map->NumberOfFields(); - DataDescriptor d(handle(descriptors->GetKey(modify_index), isolate), - field_index, attributes, Representation::Tagged()); + if (details.constness() != kMutable || details.location() != kField || + details.attributes() != attributes) { + int field_index = details.location() == kField + ? details.field_index() + : new_map->NumberOfFields(); + Descriptor d = Descriptor::DataField( + handle(descriptors->GetKey(modify_index), isolate), field_index, + attributes, Representation::Tagged()); descriptors->Replace(modify_index, &d); - if (details.type() != DATA) { + if (details.location() != kField) { int unused_property_fields = new_map->unused_property_fields() - 1; if (unused_property_fields < 0) { unused_property_fields += JSObject::kFieldsAdded; @@ -3693,14 +3856,13 @@ Handle<Map> Map::CopyGeneralizeAllRepresentations( if (FLAG_trace_generalization) { MaybeHandle<FieldType> field_type = FieldType::None(isolate); - if (details.type() == DATA) { + if (details.location() == kField) { field_type = handle( map->instance_descriptors()->GetFieldType(modify_index), isolate); } map->PrintGeneralization( stdout, reason, modify_index, new_map->NumberOfOwnDescriptors(), - new_map->NumberOfOwnDescriptors(), - details.type() == DATA_CONSTANT && store_mode == FORCE_FIELD, + new_map->NumberOfOwnDescriptors(), details.location() == kDescriptor, details.representation(), Representation::Tagged(), field_type, MaybeHandle<Object>(), FieldType::Any(isolate), MaybeHandle<Object>()); @@ -3725,13 +3887,6 @@ void Map::DeprecateTransitionTree() { } -static inline bool EqualImmutableValues(Object* obj1, Object* obj2) { - if (obj1 == obj2) return true; // Valid for both kData and kAccessor kinds. - // TODO(ishell): compare AccessorPairs. - return false; -} - - // Installs |new_descriptors| over the current instance_descriptors to ensure // proper sharing of descriptor arrays. void Map::ReplaceDescriptors(DescriptorArray* new_descriptors, @@ -3774,50 +3929,9 @@ Map* Map::FindRootMap() { } -Map* Map::FindLastMatchMap(int verbatim, - int length, - DescriptorArray* descriptors) { - DisallowHeapAllocation no_allocation; - - // This can only be called on roots of transition trees. - DCHECK_EQ(verbatim, NumberOfOwnDescriptors()); - - Map* current = this; - - for (int i = verbatim; i < length; i++) { - Name* name = descriptors->GetKey(i); - PropertyDetails details = descriptors->GetDetails(i); - Map* next = TransitionArray::SearchTransition(current, details.kind(), name, - details.attributes()); - if (next == NULL) break; - DescriptorArray* next_descriptors = next->instance_descriptors(); - - PropertyDetails next_details = next_descriptors->GetDetails(i); - DCHECK_EQ(details.kind(), next_details.kind()); - DCHECK_EQ(details.attributes(), next_details.attributes()); - if (details.location() != next_details.location()) break; - if (!details.representation().Equals(next_details.representation())) break; - - if (next_details.location() == kField) { - FieldType* next_field_type = next_descriptors->GetFieldType(i); - if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) { - break; - } - } else { - if (!EqualImmutableValues(descriptors->GetValue(i), - next_descriptors->GetValue(i))) { - break; - } - } - current = next; - } - return current; -} - - Map* Map::FindFieldOwner(int descriptor) { DisallowHeapAllocation no_allocation; - DCHECK_EQ(DATA, instance_descriptors()->GetDetails(descriptor).type()); + DCHECK_EQ(kField, instance_descriptors()->GetDetails(descriptor).location()); Map* result = this; Isolate* isolate = GetIsolate(); while (true) { @@ -3830,15 +3944,16 @@ Map* Map::FindFieldOwner(int descriptor) { return result; } - void Map::UpdateFieldType(int descriptor, Handle<Name> name, + PropertyConstness new_constness, Representation new_representation, Handle<Object> new_wrapped_type) { DCHECK(new_wrapped_type->IsSmi() || new_wrapped_type->IsWeakCell()); // We store raw pointers in the queue, so no allocations are allowed. DisallowHeapAllocation no_allocation; PropertyDetails details = instance_descriptors()->GetDetails(descriptor); - if (details.type() != DATA) return; + if (details.location() != kField) return; + DCHECK_EQ(kData, details.kind()); Zone zone(GetIsolate()->allocator(), ZONE_NAME); ZoneQueue<Map*> backlog(&zone); @@ -3857,15 +3972,19 @@ void Map::UpdateFieldType(int descriptor, Handle<Name> name, DescriptorArray* descriptors = current->instance_descriptors(); PropertyDetails details = descriptors->GetDetails(descriptor); + // Currently constness change implies map change. + DCHECK_EQ(new_constness, details.constness()); + // It is allowed to change representation here only from None to something. DCHECK(details.representation().Equals(new_representation) || details.representation().IsNone()); // Skip if already updated the shared descriptor. if (descriptors->GetValue(descriptor) != *new_wrapped_type) { - DataDescriptor d(name, descriptors->GetFieldIndex(descriptor), - new_wrapped_type, details.attributes(), - new_representation); + DCHECK_IMPLIES(!FLAG_track_constant_fields, new_constness == kMutable); + Descriptor d = Descriptor::DataField( + name, descriptors->GetFieldIndex(descriptor), details.attributes(), + new_constness, new_representation, new_wrapped_type); descriptors->Replace(descriptor, &d); } } @@ -3895,25 +4014,28 @@ Handle<FieldType> Map::GeneralizeFieldType(Representation rep1, // static -void Map::GeneralizeFieldType(Handle<Map> map, int modify_index, - Representation new_representation, - Handle<FieldType> new_field_type) { +void Map::GeneralizeField(Handle<Map> map, int modify_index, + PropertyConstness new_constness, + Representation new_representation, + Handle<FieldType> new_field_type) { Isolate* isolate = map->GetIsolate(); // Check if we actually need to generalize the field type at all. Handle<DescriptorArray> old_descriptors(map->instance_descriptors(), isolate); - Representation old_representation = - old_descriptors->GetDetails(modify_index).representation(); + PropertyDetails old_details = old_descriptors->GetDetails(modify_index); + PropertyConstness old_constness = old_details.constness(); + Representation old_representation = old_details.representation(); Handle<FieldType> old_field_type(old_descriptors->GetFieldType(modify_index), isolate); - if (old_representation.Equals(new_representation) && + if (old_constness == new_constness && + old_representation.Equals(new_representation) && !FieldTypeIsCleared(new_representation, *new_field_type) && // Checking old_field_type for being cleared is not necessary because // the NowIs check below would fail anyway in that case. new_field_type->NowIs(old_field_type)) { - DCHECK(Map::GeneralizeFieldType(old_representation, old_field_type, - new_representation, new_field_type, isolate) + DCHECK(GeneralizeFieldType(old_representation, old_field_type, + new_representation, new_field_type, isolate) ->NowIs(old_field_type)); return; } @@ -3931,9 +4053,9 @@ void Map::GeneralizeFieldType(Handle<Map> map, int modify_index, PropertyDetails details = descriptors->GetDetails(modify_index); Handle<Name> name(descriptors->GetKey(modify_index)); - Handle<Object> wrapped_type(WrapType(new_field_type)); - field_owner->UpdateFieldType(modify_index, name, new_representation, - wrapped_type); + Handle<Object> wrapped_type(WrapFieldType(new_field_type)); + field_owner->UpdateFieldType(modify_index, name, new_constness, + new_representation, wrapped_type); field_owner->dependent_code()->DeoptimizeDependentCodeGroup( isolate, DependentCode::kFieldOwnerGroup); @@ -3946,577 +4068,40 @@ void Map::GeneralizeFieldType(Handle<Map> map, int modify_index, } } -static inline Handle<FieldType> GetFieldType( - Isolate* isolate, Handle<DescriptorArray> descriptors, int descriptor, - PropertyLocation location, Representation representation) { -#ifdef DEBUG - PropertyDetails details = descriptors->GetDetails(descriptor); - DCHECK_EQ(kData, details.kind()); - DCHECK_EQ(details.location(), location); -#endif - if (location == kField) { - return handle(descriptors->GetFieldType(descriptor), isolate); - } else { - return descriptors->GetValue(descriptor) - ->OptimalType(isolate, representation); - } -} - -// Reconfigures elements kind to |new_elements_kind| and/or property at -// |modify_index| with |new_kind|, |new_attributes|, |store_mode| and/or -// |new_representation|/|new_field_type|. -// If |modify_index| is negative then no properties are reconfigured but the -// map is migrated to the up-to-date non-deprecated state. -// -// This method rewrites or completes the transition tree to reflect the new -// change. To avoid high degrees over polymorphism, and to stabilize quickly, -// on every rewrite the new type is deduced by merging the current type with -// any potential new (partial) version of the type in the transition tree. -// To do this, on each rewrite: -// - Search the root of the transition tree using FindRootMap. -// - Find/create a |root_map| with requested |new_elements_kind|. -// - Find |target_map|, the newest matching version of this map using the -// virtually "enhanced" |old_map|'s descriptor array (i.e. whose entry at -// |modify_index| is considered to be of |new_kind| and having -// |new_attributes|) to walk the transition tree. -// - Merge/generalize the "enhanced" descriptor array of the |old_map| and -// descriptor array of the |target_map|. -// - Generalize the |modify_index| descriptor using |new_representation| and -// |new_field_type|. -// - Walk the tree again starting from the root towards |target_map|. Stop at -// |split_map|, the first map who's descriptor array does not match the merged -// descriptor array. -// - If |target_map| == |split_map|, |target_map| is in the expected state. -// Return it. -// - Otherwise, invalidate the outdated transition target from |target_map|, and -// replace its transition tree with a new branch for the updated descriptors. -Handle<Map> Map::Reconfigure(Handle<Map> old_map, - ElementsKind new_elements_kind, int modify_index, - PropertyKind new_kind, - PropertyAttributes new_attributes, - Representation new_representation, - Handle<FieldType> new_field_type, - StoreMode store_mode) { - DCHECK_NE(kAccessor, new_kind); // TODO(ishell): not supported yet. - DCHECK(store_mode != FORCE_FIELD || modify_index >= 0); - Isolate* isolate = old_map->GetIsolate(); - - Handle<DescriptorArray> old_descriptors( - old_map->instance_descriptors(), isolate); - int old_nof = old_map->NumberOfOwnDescriptors(); - - // If it's just a representation generalization case (i.e. property kind and - // attributes stays unchanged) it's fine to transition from None to anything - // but double without any modification to the object, because the default - // uninitialized value for representation None can be overwritten by both - // smi and tagged values. Doubles, however, would require a box allocation. - if (modify_index >= 0 && !new_representation.IsNone() && - !new_representation.IsDouble() && - old_map->elements_kind() == new_elements_kind) { - PropertyDetails old_details = old_descriptors->GetDetails(modify_index); - Representation old_representation = old_details.representation(); - - if (old_representation.IsNone()) { - DCHECK_EQ(new_kind, old_details.kind()); - DCHECK_EQ(new_attributes, old_details.attributes()); - DCHECK_EQ(DATA, old_details.type()); - if (FLAG_trace_generalization) { - old_map->PrintGeneralization( - stdout, "uninitialized field", modify_index, - old_map->NumberOfOwnDescriptors(), - old_map->NumberOfOwnDescriptors(), false, old_representation, - new_representation, - handle(old_descriptors->GetFieldType(modify_index), isolate), - MaybeHandle<Object>(), new_field_type, MaybeHandle<Object>()); - } - Handle<Map> field_owner(old_map->FindFieldOwner(modify_index), isolate); - - GeneralizeFieldType(field_owner, modify_index, new_representation, - new_field_type); - DCHECK(old_descriptors->GetDetails(modify_index) - .representation() - .Equals(new_representation)); - DCHECK( - old_descriptors->GetFieldType(modify_index)->NowIs(new_field_type)); - return old_map; - } - } - - // Check the state of the root map. - Handle<Map> root_map(old_map->FindRootMap(), isolate); - if (!old_map->EquivalentToForTransition(*root_map)) { - return CopyGeneralizeAllRepresentations( - old_map, new_elements_kind, modify_index, store_mode, new_kind, - new_attributes, "GenAll_NotEquivalent"); - } - - ElementsKind from_kind = root_map->elements_kind(); - ElementsKind to_kind = new_elements_kind; - // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS. - if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS && - to_kind != SLOW_STRING_WRAPPER_ELEMENTS && - to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS && - !(IsTransitionableFastElementsKind(from_kind) && - IsMoreGeneralElementsKindTransition(from_kind, to_kind))) { - return CopyGeneralizeAllRepresentations( - old_map, to_kind, modify_index, store_mode, new_kind, new_attributes, - "GenAll_InvalidElementsTransition"); - } - int root_nof = root_map->NumberOfOwnDescriptors(); - if (modify_index >= 0 && modify_index < root_nof) { - PropertyDetails old_details = old_descriptors->GetDetails(modify_index); - if (old_details.kind() != new_kind || - old_details.attributes() != new_attributes) { - return CopyGeneralizeAllRepresentations( - old_map, to_kind, modify_index, store_mode, new_kind, new_attributes, - "GenAll_RootModification1"); - } - if ((old_details.type() != DATA && store_mode == FORCE_FIELD) || - (old_details.type() == DATA && - (!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) || - !new_representation.fits_into(old_details.representation())))) { - return CopyGeneralizeAllRepresentations( - old_map, to_kind, modify_index, store_mode, new_kind, new_attributes, - "GenAll_RootModification2"); - } - } - - // From here on, use the map with correct elements kind as root map. - if (from_kind != to_kind) { - root_map = Map::AsElementsKind(root_map, to_kind); - } - - Handle<Map> target_map = root_map; - for (int i = root_nof; i < old_nof; ++i) { - PropertyDetails old_details = old_descriptors->GetDetails(i); - PropertyKind next_kind; - PropertyLocation next_location; - PropertyAttributes next_attributes; - Representation next_representation; - bool property_kind_reconfiguration = false; - - if (modify_index == i) { - DCHECK_EQ(FORCE_FIELD, store_mode); - property_kind_reconfiguration = old_details.kind() != new_kind; - - next_kind = new_kind; - next_location = kField; - next_attributes = new_attributes; - // If property kind is not reconfigured merge the result with - // representation/field type from the old descriptor. - next_representation = new_representation; - if (!property_kind_reconfiguration) { - next_representation = - next_representation.generalize(old_details.representation()); - } - - } else { - next_kind = old_details.kind(); - next_location = old_details.location(); - next_attributes = old_details.attributes(); - next_representation = old_details.representation(); - } - Map* transition = TransitionArray::SearchTransition( - *target_map, next_kind, old_descriptors->GetKey(i), next_attributes); - if (transition == NULL) break; - Handle<Map> tmp_map(transition, isolate); - - Handle<DescriptorArray> tmp_descriptors = handle( - tmp_map->instance_descriptors(), isolate); - - // Check if target map is incompatible. - PropertyDetails tmp_details = tmp_descriptors->GetDetails(i); - DCHECK_EQ(next_kind, tmp_details.kind()); - DCHECK_EQ(next_attributes, tmp_details.attributes()); - if (next_kind == kAccessor && - !EqualImmutableValues(old_descriptors->GetValue(i), - tmp_descriptors->GetValue(i))) { - return CopyGeneralizeAllRepresentations( - old_map, to_kind, modify_index, store_mode, new_kind, new_attributes, - "GenAll_Incompatible"); - } - if (next_location == kField && tmp_details.location() == kDescriptor) break; - - Representation tmp_representation = tmp_details.representation(); - if (!next_representation.fits_into(tmp_representation)) break; - - PropertyLocation old_location = old_details.location(); - PropertyLocation tmp_location = tmp_details.location(); - if (tmp_location == kField) { - if (next_kind == kData) { - Handle<FieldType> next_field_type; - if (modify_index == i) { - next_field_type = new_field_type; - if (!property_kind_reconfiguration) { - Handle<FieldType> old_field_type = - GetFieldType(isolate, old_descriptors, i, - old_details.location(), tmp_representation); - Representation old_representation = old_details.representation(); - next_field_type = GeneralizeFieldType( - old_representation, old_field_type, new_representation, - next_field_type, isolate); - } - } else { - Handle<FieldType> old_field_type = - GetFieldType(isolate, old_descriptors, i, old_details.location(), - tmp_representation); - next_field_type = old_field_type; - } - GeneralizeFieldType(tmp_map, i, tmp_representation, next_field_type); - } - } else if (old_location == kField || - !EqualImmutableValues(old_descriptors->GetValue(i), - tmp_descriptors->GetValue(i))) { - break; - } - DCHECK(!tmp_map->is_deprecated()); - target_map = tmp_map; - } - - // Directly change the map if the target map is more general. - Handle<DescriptorArray> target_descriptors( - target_map->instance_descriptors(), isolate); - int target_nof = target_map->NumberOfOwnDescriptors(); - if (target_nof == old_nof && - (store_mode != FORCE_FIELD || - (modify_index >= 0 && - target_descriptors->GetDetails(modify_index).location() == kField))) { -#ifdef DEBUG - if (modify_index >= 0) { - PropertyDetails details = target_descriptors->GetDetails(modify_index); - DCHECK_EQ(new_kind, details.kind()); - DCHECK_EQ(new_attributes, details.attributes()); - DCHECK(new_representation.fits_into(details.representation())); - DCHECK(details.location() != kField || - new_field_type->NowIs( - target_descriptors->GetFieldType(modify_index))); - } -#endif - if (*target_map != *old_map) { - old_map->NotifyLeafMapLayoutChange(); - } - return target_map; - } - - // Find the last compatible target map in the transition tree. - for (int i = target_nof; i < old_nof; ++i) { - PropertyDetails old_details = old_descriptors->GetDetails(i); - PropertyKind next_kind; - PropertyAttributes next_attributes; - if (modify_index == i) { - next_kind = new_kind; - next_attributes = new_attributes; - } else { - next_kind = old_details.kind(); - next_attributes = old_details.attributes(); - } - Map* transition = TransitionArray::SearchTransition( - *target_map, next_kind, old_descriptors->GetKey(i), next_attributes); - if (transition == NULL) break; - Handle<Map> tmp_map(transition, isolate); - Handle<DescriptorArray> tmp_descriptors( - tmp_map->instance_descriptors(), isolate); - - // Check if target map is compatible. -#ifdef DEBUG - PropertyDetails tmp_details = tmp_descriptors->GetDetails(i); - DCHECK_EQ(next_kind, tmp_details.kind()); - DCHECK_EQ(next_attributes, tmp_details.attributes()); -#endif - if (next_kind == kAccessor && - !EqualImmutableValues(old_descriptors->GetValue(i), - tmp_descriptors->GetValue(i))) { - return CopyGeneralizeAllRepresentations( - old_map, to_kind, modify_index, store_mode, new_kind, new_attributes, - "GenAll_Incompatible"); - } - DCHECK(!tmp_map->is_deprecated()); - target_map = tmp_map; - } - target_nof = target_map->NumberOfOwnDescriptors(); - target_descriptors = handle(target_map->instance_descriptors(), isolate); - - // Allocate a new descriptor array large enough to hold the required - // descriptors, with minimally the exact same size as the old descriptor - // array. - int new_slack = Max( - old_nof, old_descriptors->number_of_descriptors()) - old_nof; - Handle<DescriptorArray> new_descriptors = DescriptorArray::Allocate( - isolate, old_nof, new_slack); - DCHECK(new_descriptors->length() > target_descriptors->length() || - new_descriptors->NumberOfSlackDescriptors() > 0 || - new_descriptors->number_of_descriptors() == - old_descriptors->number_of_descriptors()); - DCHECK(new_descriptors->number_of_descriptors() == old_nof); - - // 0 -> |root_nof| - int current_offset = 0; - for (int i = 0; i < root_nof; ++i) { - PropertyDetails old_details = old_descriptors->GetDetails(i); - if (old_details.location() == kField) { - current_offset += old_details.field_width_in_words(); - } - Descriptor d(handle(old_descriptors->GetKey(i), isolate), - handle(old_descriptors->GetValue(i), isolate), - old_details); - new_descriptors->Set(i, &d); - } - - // |root_nof| -> |target_nof| - for (int i = root_nof; i < target_nof; ++i) { - Handle<Name> target_key(target_descriptors->GetKey(i), isolate); - PropertyDetails old_details = old_descriptors->GetDetails(i); - PropertyDetails target_details = target_descriptors->GetDetails(i); - - PropertyKind next_kind; - PropertyAttributes next_attributes; - PropertyLocation next_location; - Representation next_representation; - bool property_kind_reconfiguration = false; - - if (modify_index == i) { - DCHECK_EQ(FORCE_FIELD, store_mode); - property_kind_reconfiguration = old_details.kind() != new_kind; - - next_kind = new_kind; - next_attributes = new_attributes; - next_location = kField; - - // Merge new representation/field type with ones from the target - // descriptor. If property kind is not reconfigured merge the result with - // representation/field type from the old descriptor. - next_representation = - new_representation.generalize(target_details.representation()); - if (!property_kind_reconfiguration) { - next_representation = - next_representation.generalize(old_details.representation()); - } - } else { - // Merge old_descriptor and target_descriptor entries. - DCHECK_EQ(target_details.kind(), old_details.kind()); - next_kind = target_details.kind(); - next_attributes = target_details.attributes(); - next_location = - old_details.location() == kField || - target_details.location() == kField || - !EqualImmutableValues(target_descriptors->GetValue(i), - old_descriptors->GetValue(i)) - ? kField - : kDescriptor; - - next_representation = old_details.representation().generalize( - target_details.representation()); - } - DCHECK_EQ(next_kind, target_details.kind()); - DCHECK_EQ(next_attributes, target_details.attributes()); - - if (next_location == kField) { - if (next_kind == kData) { - Handle<FieldType> target_field_type = - GetFieldType(isolate, target_descriptors, i, - target_details.location(), next_representation); - - Handle<FieldType> next_field_type; - if (modify_index == i) { - next_field_type = GeneralizeFieldType( - target_details.representation(), target_field_type, - new_representation, new_field_type, isolate); - if (!property_kind_reconfiguration) { - Handle<FieldType> old_field_type = - GetFieldType(isolate, old_descriptors, i, - old_details.location(), next_representation); - next_field_type = GeneralizeFieldType( - old_details.representation(), old_field_type, - next_representation, next_field_type, isolate); - } - } else { - Handle<FieldType> old_field_type = - GetFieldType(isolate, old_descriptors, i, old_details.location(), - next_representation); - next_field_type = GeneralizeFieldType( - old_details.representation(), old_field_type, next_representation, - target_field_type, isolate); - } - Handle<Object> wrapped_type(WrapType(next_field_type)); - DataDescriptor d(target_key, current_offset, wrapped_type, - next_attributes, next_representation); - current_offset += d.GetDetails().field_width_in_words(); - new_descriptors->Set(i, &d); - } else { - UNIMPLEMENTED(); // TODO(ishell): implement. - } - } else { - PropertyDetails details(next_attributes, next_kind, next_location, - next_representation); - Descriptor d(target_key, handle(target_descriptors->GetValue(i), isolate), - details); - new_descriptors->Set(i, &d); - } - } - - // |target_nof| -> |old_nof| - for (int i = target_nof; i < old_nof; ++i) { - PropertyDetails old_details = old_descriptors->GetDetails(i); - Handle<Name> old_key(old_descriptors->GetKey(i), isolate); - - // Merge old_descriptor entry and modified details together. - PropertyKind next_kind; - PropertyAttributes next_attributes; - PropertyLocation next_location; - Representation next_representation; - bool property_kind_reconfiguration = false; - - if (modify_index == i) { - DCHECK_EQ(FORCE_FIELD, store_mode); - // In case of property kind reconfiguration it is not necessary to - // take into account representation/field type of the old descriptor. - property_kind_reconfiguration = old_details.kind() != new_kind; - - next_kind = new_kind; - next_attributes = new_attributes; - next_location = kField; - next_representation = new_representation; - if (!property_kind_reconfiguration) { - next_representation = - next_representation.generalize(old_details.representation()); - } - } else { - next_kind = old_details.kind(); - next_attributes = old_details.attributes(); - next_location = old_details.location(); - next_representation = old_details.representation(); - } - - if (next_location == kField) { - if (next_kind == kData) { - Handle<FieldType> next_field_type; - if (modify_index == i) { - next_field_type = new_field_type; - if (!property_kind_reconfiguration) { - Handle<FieldType> old_field_type = - GetFieldType(isolate, old_descriptors, i, - old_details.location(), next_representation); - next_field_type = GeneralizeFieldType( - old_details.representation(), old_field_type, - next_representation, next_field_type, isolate); - } - } else { - Handle<FieldType> old_field_type = - GetFieldType(isolate, old_descriptors, i, old_details.location(), - next_representation); - next_field_type = old_field_type; - } - - Handle<Object> wrapped_type(WrapType(next_field_type)); - - DataDescriptor d(old_key, current_offset, wrapped_type, next_attributes, - next_representation); - current_offset += d.GetDetails().field_width_in_words(); - new_descriptors->Set(i, &d); - } else { - UNIMPLEMENTED(); // TODO(ishell): implement. - } - } else { - PropertyDetails details(next_attributes, next_kind, next_location, - next_representation); - Descriptor d(old_key, handle(old_descriptors->GetValue(i), isolate), - details); - new_descriptors->Set(i, &d); - } - } - - new_descriptors->Sort(); - - DCHECK(store_mode != FORCE_FIELD || - new_descriptors->GetDetails(modify_index).location() == kField); - - Handle<Map> split_map(root_map->FindLastMatchMap( - root_nof, old_nof, *new_descriptors), isolate); - int split_nof = split_map->NumberOfOwnDescriptors(); - DCHECK_NE(old_nof, split_nof); - - PropertyKind split_kind; - PropertyAttributes split_attributes; - if (modify_index == split_nof) { - split_kind = new_kind; - split_attributes = new_attributes; - } else { - PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof); - split_kind = split_prop_details.kind(); - split_attributes = split_prop_details.attributes(); - } - - // Invalidate a transition target at |key|. - Map* maybe_transition = TransitionArray::SearchTransition( - *split_map, split_kind, old_descriptors->GetKey(split_nof), - split_attributes); - if (maybe_transition != NULL) { - maybe_transition->DeprecateTransitionTree(); - } - - // If |maybe_transition| is not NULL then the transition array already - // contains entry for given descriptor. This means that the transition - // could be inserted regardless of whether transitions array is full or not. - if (maybe_transition == NULL && - !TransitionArray::CanHaveMoreTransitions(split_map)) { - return CopyGeneralizeAllRepresentations( - old_map, to_kind, modify_index, store_mode, new_kind, new_attributes, - "GenAll_CantHaveMoreTransitions"); - } - - old_map->NotifyLeafMapLayoutChange(); - - if (FLAG_trace_generalization && modify_index >= 0) { - PropertyDetails old_details = old_descriptors->GetDetails(modify_index); - PropertyDetails new_details = new_descriptors->GetDetails(modify_index); - MaybeHandle<FieldType> old_field_type; - MaybeHandle<FieldType> new_field_type; - MaybeHandle<Object> old_value; - MaybeHandle<Object> new_value; - if (old_details.type() == DATA) { - old_field_type = - handle(old_descriptors->GetFieldType(modify_index), isolate); - } else { - old_value = handle(old_descriptors->GetValue(modify_index), isolate); - } - if (new_details.type() == DATA) { - new_field_type = - handle(new_descriptors->GetFieldType(modify_index), isolate); - } else { - new_value = handle(new_descriptors->GetValue(modify_index), isolate); - } - - old_map->PrintGeneralization( - stdout, "", modify_index, split_nof, old_nof, - old_details.location() == kDescriptor && store_mode == FORCE_FIELD, - old_details.representation(), new_details.representation(), - old_field_type, old_value, new_field_type, new_value); - } - - Handle<LayoutDescriptor> new_layout_descriptor = - LayoutDescriptor::New(split_map, new_descriptors, old_nof); - - Handle<Map> new_map = - AddMissingTransitions(split_map, new_descriptors, new_layout_descriptor); - - // Deprecated part of the transition tree is no longer reachable, so replace - // current instance descriptors in the "survived" part of the tree with - // the new descriptors to maintain descriptors sharing invariant. - split_map->ReplaceDescriptors(*new_descriptors, *new_layout_descriptor); - return new_map; +// TODO(ishell): remove. +// static +Handle<Map> Map::ReconfigureProperty(Handle<Map> map, int modify_index, + PropertyKind new_kind, + PropertyAttributes new_attributes, + Representation new_representation, + Handle<FieldType> new_field_type) { + DCHECK_EQ(kData, new_kind); // Only kData case is supported. + MapUpdater mu(map->GetIsolate(), map); + return mu.ReconfigureToDataField(modify_index, new_attributes, kConst, + new_representation, new_field_type); +} + +// TODO(ishell): remove. +// static +Handle<Map> Map::ReconfigureElementsKind(Handle<Map> map, + ElementsKind new_elements_kind) { + MapUpdater mu(map->GetIsolate(), map); + return mu.ReconfigureElementsKind(new_elements_kind); } +// Generalize all fields and update the transition tree. +Handle<Map> Map::GeneralizeAllFields(Handle<Map> map) { + Isolate* isolate = map->GetIsolate(); + Handle<FieldType> any_type = FieldType::Any(isolate); -// Generalize the representation of all DATA descriptors. -Handle<Map> Map::GeneralizeAllFieldRepresentations( - Handle<Map> map) { Handle<DescriptorArray> descriptors(map->instance_descriptors()); for (int i = 0; i < map->NumberOfOwnDescriptors(); ++i) { PropertyDetails details = descriptors->GetDetails(i); - if (details.type() == DATA) { - map = ReconfigureProperty(map, i, kData, details.attributes(), - Representation::Tagged(), - FieldType::Any(map->GetIsolate()), FORCE_FIELD); + if (details.location() == kField) { + DCHECK_EQ(kData, details.kind()); + MapUpdater mu(isolate, map); + map = mu.ReconfigureToDataField(i, details.attributes(), kMutable, + Representation::Tagged(), any_type); } } return map; @@ -4569,49 +4154,51 @@ Map* Map::TryReplayPropertyTransitions(Map* old_map) { PropertyDetails new_details = new_descriptors->GetDetails(i); DCHECK_EQ(old_details.kind(), new_details.kind()); DCHECK_EQ(old_details.attributes(), new_details.attributes()); + if (!IsGeneralizableTo(old_details.constness(), new_details.constness())) { + return nullptr; + } + DCHECK(IsGeneralizableTo(old_details.location(), new_details.location())); if (!old_details.representation().fits_into(new_details.representation())) { return nullptr; } - switch (new_details.type()) { - case DATA: { + if (new_details.location() == kField) { + if (new_details.kind() == kData) { FieldType* new_type = new_descriptors->GetFieldType(i); // Cleared field types need special treatment. They represent lost // knowledge, so we must first generalize the new_type to "Any". if (FieldTypeIsCleared(new_details.representation(), new_type)) { return nullptr; } - PropertyType old_property_type = old_details.type(); - if (old_property_type == DATA) { + DCHECK_EQ(kData, old_details.kind()); + if (old_details.location() == kField) { FieldType* old_type = old_descriptors->GetFieldType(i); if (FieldTypeIsCleared(old_details.representation(), old_type) || !old_type->NowIs(new_type)) { return nullptr; } } else { - DCHECK(old_property_type == DATA_CONSTANT); + DCHECK_EQ(kDescriptor, old_details.location()); + DCHECK(!FLAG_track_constant_fields); Object* old_value = old_descriptors->GetValue(i); if (!new_type->NowContains(old_value)) { return nullptr; } } - break; - } - case ACCESSOR: { + + } else { + DCHECK_EQ(kAccessor, new_details.kind()); #ifdef DEBUG FieldType* new_type = new_descriptors->GetFieldType(i); DCHECK(new_type->IsAny()); #endif - break; + UNREACHABLE(); } - - case DATA_CONSTANT: - case ACCESSOR_CONSTANT: { - Object* old_value = old_descriptors->GetValue(i); - Object* new_value = new_descriptors->GetValue(i); - if (old_details.location() == kField || old_value != new_value) { - return nullptr; - } - break; + } else { + DCHECK_EQ(kDescriptor, new_details.location()); + Object* old_value = old_descriptors->GetValue(i); + Object* new_value = new_descriptors->GetValue(i); + if (old_details.location() == kField || old_value != new_value) { + return nullptr; } } } @@ -4623,9 +4210,8 @@ Map* Map::TryReplayPropertyTransitions(Map* old_map) { // static Handle<Map> Map::Update(Handle<Map> map) { if (!map->is_deprecated()) return map; - return ReconfigureProperty(map, -1, kData, NONE, Representation::None(), - FieldType::None(map->GetIsolate()), - ALLOW_IN_DESCRIPTOR); + MapUpdater mu(map->GetIsolate(), map); + return mu.Update(); } Maybe<bool> JSObject::SetPropertyWithInterceptor(LookupIterator* it, @@ -4915,7 +4501,7 @@ Maybe<bool> Object::SetDataProperty(LookupIterator* it, Handle<Object> value) { it->PrepareForDataProperty(to_assign); // Write the property value. - it->WriteDataValue(to_assign); + it->WriteDataValue(to_assign, false); #if VERIFY_HEAP if (FLAG_verify_heap) { @@ -4989,7 +4575,7 @@ Maybe<bool> Object::AddDataProperty(LookupIterator* it, Handle<Object> value, it->ApplyTransitionToDataProperty(receiver); // Write the property value. - it->WriteDataValue(value); + it->WriteDataValue(value, true); #if VERIFY_HEAP if (FLAG_verify_heap) { @@ -5045,6 +4631,36 @@ void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) { map->UpdateDescriptors(*new_descriptors, layout_descriptor); } +// static +Handle<Map> Map::GetObjectCreateMap(Handle<HeapObject> prototype) { + Isolate* isolate = prototype->GetIsolate(); + Handle<Map> map(isolate->native_context()->object_function()->initial_map(), + isolate); + if (map->prototype() == *prototype) return map; + if (prototype->IsNull(isolate)) { + return isolate->slow_object_with_null_prototype_map(); + } + if (prototype->IsJSObject()) { + Handle<JSObject> js_prototype = Handle<JSObject>::cast(prototype); + if (!js_prototype->map()->is_prototype_map()) { + JSObject::OptimizeAsPrototype(js_prototype, FAST_PROTOTYPE); + } + Handle<PrototypeInfo> info = + Map::GetOrCreatePrototypeInfo(js_prototype, isolate); + // TODO(verwaest): Use inobject slack tracking for this map. + if (info->HasObjectCreateMap()) { + map = handle(info->ObjectCreateMap(), isolate); + } else { + map = Map::CopyInitialMap(map); + Map::SetPrototype(map, prototype, FAST_PROTOTYPE); + PrototypeInfo::SetObjectCreateMap(info, map); + } + return map; + } + + return Map::TransitionToPrototype(map, prototype, REGULAR_PROTOTYPE); +} + template <class T> static int AppendUniqueCallbacks(Handle<TemplateList> callbacks, Handle<typename T::Array> array, @@ -5094,8 +4710,9 @@ struct DescriptorArrayAppender { int valid_descriptors, Handle<DescriptorArray> array) { DisallowHeapAllocation no_gc; - AccessorConstantDescriptor desc(key, entry, entry->property_attributes()); - array->Append(&desc); + Descriptor d = + Descriptor::AccessorConstant(key, entry, entry->property_attributes()); + array->Append(&d); } }; @@ -5631,7 +5248,7 @@ Handle<Context> JSFunction::GetFunctionRealm(Handle<JSFunction> function) { MaybeHandle<Context> JSObject::GetFunctionRealm(Handle<JSObject> object) { DCHECK(object->map()->is_constructor()); DCHECK(!object->IsJSFunction()); - return handle(object->GetCreationContext()); + return object->GetCreationContext(); } @@ -5979,7 +5596,7 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, iteration_order = NameDictionary::DoGenerateNewEnumerationIndices(dictionary); } else { - iteration_order = NameDictionary::BuildIterationIndicesArray(dictionary); + iteration_order = NameDictionary::IterationIndices(dictionary); } int instance_descriptor_length = iteration_order->length(); @@ -5990,10 +5607,16 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, int index = Smi::cast(iteration_order->get(i))->value(); DCHECK(dictionary->IsKey(isolate, dictionary->KeyAt(index))); - Object* value = dictionary->ValueAt(index); - PropertyType type = dictionary->DetailsAt(index).type(); - if (type == DATA && !value->IsJSFunction()) { - number_of_fields += 1; + PropertyKind kind = dictionary->DetailsAt(index).kind(); + if (kind == kData) { + if (FLAG_track_constant_fields) { + number_of_fields += 1; + } else { + Object* value = dictionary->ValueAt(index); + if (!value->IsJSFunction()) { + number_of_fields += 1; + } + } } } @@ -6057,14 +5680,28 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, Object* value = dictionary->ValueAt(index); PropertyDetails details = dictionary->DetailsAt(index); + DCHECK_EQ(kField, details.location()); + DCHECK_EQ(kMutable, details.constness()); int enumeration_index = details.dictionary_index(); - PropertyType type = details.type(); - if (value->IsJSFunction()) { - DataConstantDescriptor d(key, handle(value, isolate), - details.attributes()); - descriptors->Set(enumeration_index - 1, &d); - } else if (type == DATA) { + Descriptor d; + if (details.kind() == kData) { + if (!FLAG_track_constant_fields && value->IsJSFunction()) { + d = Descriptor::DataConstant(key, handle(value, isolate), + details.attributes()); + } else { + d = Descriptor::DataField( + key, current_offset, details.attributes(), kDefaultFieldConstness, + // TODO(verwaest): value->OptimalRepresentation(); + Representation::Tagged(), FieldType::Any(isolate)); + } + } else { + DCHECK_EQ(kAccessor, details.kind()); + d = Descriptor::AccessorConstant(key, handle(value, isolate), + details.attributes()); + } + details = d.GetDetails(); + if (details.location() == kField) { if (current_offset < inobject_props) { object->InObjectPropertyAtPut(current_offset, value, UPDATE_WRITE_BARRIER); @@ -6072,18 +5709,9 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, int offset = current_offset - inobject_props; fields->set(offset, value); } - DataDescriptor d(key, current_offset, details.attributes(), - // TODO(verwaest): value->OptimalRepresentation(); - Representation::Tagged()); - current_offset += d.GetDetails().field_width_in_words(); - descriptors->Set(enumeration_index - 1, &d); - } else if (type == ACCESSOR_CONSTANT) { - AccessorConstantDescriptor d(key, handle(value, isolate), - details.attributes()); - descriptors->Set(enumeration_index - 1, &d); - } else { - UNREACHABLE(); + current_offset += details.field_width_in_words(); } + descriptors->Set(enumeration_index - 1, &d); } DCHECK(current_offset == number_of_fields); @@ -6123,9 +5751,10 @@ void JSObject::ResetElements(Handle<JSObject> object) { void JSObject::RequireSlowElements(SeededNumberDictionary* dictionary) { if (dictionary->requires_slow_elements()) return; dictionary->set_requires_slow_elements(); - // TODO(verwaest): Remove this hack. if (map()->is_prototype_map()) { - TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate()); + // If this object is a prototype (the callee will check), invalidate any + // prototype chains involving it. + InvalidatePrototypeChains(map()); } } @@ -6412,33 +6041,6 @@ Maybe<bool> JSReceiver::DeletePropertyOrElement(Handle<JSReceiver> object, return DeleteProperty(&it, language_mode); } - -// ES6 7.1.14 -// static -MaybeHandle<Object> Object::ToPropertyKey(Isolate* isolate, - Handle<Object> value) { - // 1. Let key be ToPrimitive(argument, hint String). - MaybeHandle<Object> maybe_key = - Object::ToPrimitive(value, ToPrimitiveHint::kString); - // 2. ReturnIfAbrupt(key). - Handle<Object> key; - if (!maybe_key.ToHandle(&key)) return key; - // 3. If Type(key) is Symbol, then return key. - if (key->IsSymbol()) return key; - // 4. Return ToString(key). - // Extending spec'ed behavior, we'd be happy to return an element index. - if (key->IsSmi()) return key; - if (key->IsHeapNumber()) { - uint32_t uint_value; - if (value->ToArrayLength(&uint_value) && - uint_value <= static_cast<uint32_t>(Smi::kMaxValue)) { - return handle(Smi::FromInt(static_cast<int>(uint_value)), isolate); - } - } - return Object::ToString(isolate, key); -} - - // ES6 19.1.2.4 // static Object* JSReceiver::DefineProperty(Isolate* isolate, Handle<Object> object, @@ -6563,12 +6165,15 @@ Maybe<bool> JSReceiver::DefineOwnProperty(Isolate* isolate, return JSProxy::DefineOwnProperty(isolate, Handle<JSProxy>::cast(object), key, desc, should_throw); } + if (object->IsJSTypedArray()) { + return JSTypedArray::DefineOwnProperty( + isolate, Handle<JSTypedArray>::cast(object), key, desc, should_throw); + } // TODO(neis): Special case for JSModuleNamespace? // OrdinaryDefineOwnProperty, by virtue of calling - // DefineOwnPropertyIgnoreAttributes, can handle arguments (ES6 9.4.4.2) - // and IntegerIndexedExotics (ES6 9.4.5.3), with one exception: - // TODO(jkummerow): Setting an indexed accessor on a typed array should throw. + // DefineOwnPropertyIgnoreAttributes, can handle arguments + // (ES#sec-arguments-exotic-objects-defineownproperty-p-desc). return OrdinaryDefineOwnProperty(isolate, Handle<JSObject>::cast(object), key, desc, should_throw); } @@ -6970,7 +6575,6 @@ bool PropertyKeyToArrayLength(Handle<Object> value, uint32_t* length) { return false; } - bool PropertyKeyToArrayIndex(Handle<Object> index_obj, uint32_t* output) { return PropertyKeyToArrayLength(index_obj, output) && *output != kMaxUInt32; } @@ -7300,12 +6904,12 @@ Maybe<bool> JSProxy::SetPrivateProperty(Isolate* isolate, Handle<JSProxy> proxy, if (it.IsFound()) { DCHECK_EQ(LookupIterator::DATA, it.state()); DCHECK_EQ(DONT_ENUM, it.property_attributes()); - it.WriteDataValue(value); + it.WriteDataValue(value, false); return Just(true); } Handle<NameDictionary> dict(proxy->property_dictionary()); - PropertyDetails details(DONT_ENUM, DATA, 0, PropertyCellType::kNoCell); + PropertyDetails details(kData, DONT_ENUM, 0, PropertyCellType::kNoCell); Handle<NameDictionary> result = NameDictionary::Add(dict, private_name, value, details); if (!dict.is_identical_to(result)) proxy->set_properties(*result); @@ -7330,7 +6934,13 @@ namespace { Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it, PropertyDescriptor* desc) { - if (it->state() == LookupIterator::INTERCEPTOR) { + bool has_access = true; + if (it->state() == LookupIterator::ACCESS_CHECK) { + has_access = it->HasAccess() || JSObject::AllCanRead(it); + it->Next(); + } + + if (has_access && it->state() == LookupIterator::INTERCEPTOR) { Isolate* isolate = it->isolate(); Handle<InterceptorInfo> interceptor = it->GetInterceptor(); if (!interceptor->descriptor()->IsUndefined(isolate)) { @@ -7374,6 +6984,7 @@ Maybe<bool> GetPropertyDescriptorWithInterceptor(LookupIterator* it, } } } + it->Restart(); return Just(false); } } // namespace @@ -7998,7 +7609,7 @@ void ApplyAttributesToDictionary(Isolate* isolate, PropertyDetails details = dictionary->DetailsAt(i); int attrs = attributes; // READ_ONLY is an invalid attribute for JS setters/getters. - if ((attributes & READ_ONLY) && details.type() == ACCESSOR_CONSTANT) { + if ((attributes & READ_ONLY) && details.kind() == kAccessor) { Object* v = dictionary->ValueAt(i); if (v->IsPropertyCell()) v = PropertyCell::cast(v)->value(); if (v->IsAccessorPair()) attrs &= ~READ_ONLY; @@ -8238,12 +7849,14 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk( int limit = copy->map()->NumberOfOwnDescriptors(); for (int i = 0; i < limit; i++) { PropertyDetails details = descriptors->GetDetails(i); - if (details.type() != DATA) continue; + if (details.location() != kField) continue; + DCHECK_EQ(kData, details.kind()); FieldIndex index = FieldIndex::ForDescriptor(copy->map(), i); if (object->IsUnboxedDoubleField(index)) { if (copying) { - double value = object->RawFastDoublePropertyAt(index); - copy->RawFastDoublePropertyAtPut(index, value); + // Ensure that all bits of the double value are preserved. + uint64_t value = object->RawFastDoublePropertyAsBitsAt(index); + copy->RawFastDoublePropertyAsBitsAtPut(index, value); } } else { Handle<Object> value(object->RawFastPropertyAt(index), isolate); @@ -8563,8 +8176,8 @@ bool Map::OnlyHasSimpleProperties() { // Wrapped string elements aren't explicitly stored in the elements backing // store, but are loaded indirectly from the underlying string. return !IsStringWrapperElementsKind(elements_kind()) && - instance_type() > LAST_SPECIAL_RECEIVER_TYPE && - !has_hidden_prototype() && !is_dictionary_map(); + !IsSpecialReceiverMap() && !has_hidden_prototype() && + !is_dictionary_map(); } MUST_USE_RESULT Maybe<bool> FastGetOwnValuesOrEntries( @@ -8832,7 +8445,9 @@ Object* JSObject::SlowReverseLookup(Object* value) { DescriptorArray* descs = map()->instance_descriptors(); bool value_is_number = value->IsNumber(); for (int i = 0; i < number_of_own_descriptors; i++) { - if (descs->GetType(i) == DATA) { + PropertyDetails details = descs->GetDetails(i); + if (details.location() == kField) { + DCHECK_EQ(kData, details.kind()); FieldIndex field_index = FieldIndex::ForDescriptor(map(), i); if (IsUnboxedDoubleField(field_index)) { if (value_is_number) { @@ -8852,9 +8467,12 @@ Object* JSObject::SlowReverseLookup(Object* value) { return descs->GetKey(i); } } - } else if (descs->GetType(i) == DATA_CONSTANT) { - if (descs->GetConstant(i) == value) { - return descs->GetKey(i); + } else { + DCHECK_EQ(kDescriptor, details.location()); + if (details.kind() == kData) { + if (descs->GetValue(i) == value) { + return descs->GetKey(i); + } } } } @@ -9000,12 +8618,13 @@ Handle<Map> Map::CopyInitialMap(Handle<Map> map, int instance_size, Isolate* isolate = map->GetIsolate(); // Strict function maps have Function as a constructor but the // Function's initial map is a sloppy function map. Same holds for - // GeneratorFunction and its initial map. + // GeneratorFunction / AsyncFunction and its initial map. Object* constructor = map->GetConstructor(); DCHECK(constructor->IsJSFunction()); DCHECK(*map == JSFunction::cast(constructor)->initial_map() || *map == *isolate->strict_function_map() || - *map == *isolate->strict_generator_function_map()); + *map == *isolate->generator_function_map() || + *map == *isolate->async_function_map()); #endif // Initial maps must always own their descriptors and it's descriptor array // does not contain descriptors that do not belong to the map. @@ -9161,13 +8780,7 @@ Handle<Map> Map::CopyReplaceDescriptors( CHECK(maybe_name.ToHandle(&name)); ConnectTransition(map, result, name, simple_flag); } else { - int length = descriptors->number_of_descriptors(); - for (int i = 0; i < length; i++) { - descriptors->SetRepresentation(i, Representation::Tagged()); - if (descriptors->GetDetails(i).type() == DATA) { - descriptors->SetValue(i, FieldType::Any()); - } - } + descriptors->GeneralizeAllFields(); result->InitializeDescriptors(*descriptors, LayoutDescriptor::FastPointerLayout()); } @@ -9449,42 +9062,45 @@ Handle<Map> Map::CopyForPreventExtensions(Handle<Map> map, return new_map; } -FieldType* DescriptorArray::GetFieldType(int descriptor_number) { - DCHECK(GetDetails(descriptor_number).location() == kField); - Object* value = GetValue(descriptor_number); - if (value->IsWeakCell()) { - if (WeakCell::cast(value)->cleared()) return FieldType::None(); - value = WeakCell::cast(value)->value(); - } - return FieldType::cast(value); -} - namespace { -bool CanHoldValue(DescriptorArray* descriptors, int descriptor, Object* value) { +bool CanHoldValue(DescriptorArray* descriptors, int descriptor, + PropertyConstness constness, Object* value) { PropertyDetails details = descriptors->GetDetails(descriptor); - switch (details.type()) { - case DATA: - return value->FitsRepresentation(details.representation()) && + if (details.location() == kField) { + if (details.kind() == kData) { + return IsGeneralizableTo(constness, details.constness()) && + value->FitsRepresentation(details.representation()) && descriptors->GetFieldType(descriptor)->NowContains(value); + } else { + DCHECK_EQ(kAccessor, details.kind()); + return false; + } - case DATA_CONSTANT: - DCHECK(descriptors->GetConstant(descriptor) != value || + } else { + DCHECK_EQ(kDescriptor, details.location()); + DCHECK_EQ(kConst, details.constness()); + if (details.kind() == kData) { + DCHECK(!FLAG_track_constant_fields); + DCHECK(descriptors->GetValue(descriptor) != value || value->FitsRepresentation(details.representation())); - return descriptors->GetConstant(descriptor) == value; - - case ACCESSOR: - case ACCESSOR_CONSTANT: + return descriptors->GetValue(descriptor) == value; + } else { + DCHECK_EQ(kAccessor, details.kind()); return false; + } } - UNREACHABLE(); return false; } Handle<Map> UpdateDescriptorForValue(Handle<Map> map, int descriptor, + PropertyConstness constness, Handle<Object> value) { - if (CanHoldValue(map->instance_descriptors(), descriptor, *value)) return map; + if (CanHoldValue(map->instance_descriptors(), descriptor, constness, + *value)) { + return map; + } Isolate* isolate = map->GetIsolate(); PropertyAttributes attributes = @@ -9492,25 +9108,27 @@ Handle<Map> UpdateDescriptorForValue(Handle<Map> map, int descriptor, Representation representation = value->OptimalRepresentation(); Handle<FieldType> type = value->OptimalType(isolate, representation); - return Map::ReconfigureProperty(map, descriptor, kData, attributes, - representation, type, FORCE_FIELD); + MapUpdater mu(isolate, map); + return mu.ReconfigureToDataField(descriptor, attributes, constness, + representation, type); } } // namespace // static Handle<Map> Map::PrepareForDataProperty(Handle<Map> map, int descriptor, + PropertyConstness constness, Handle<Object> value) { // Dictionaries can store any property value. DCHECK(!map->is_dictionary_map()); // Update to the newest map before storing the property. - return UpdateDescriptorForValue(Update(map), descriptor, value); + return UpdateDescriptorForValue(Update(map), descriptor, constness, value); } - Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name, Handle<Object> value, PropertyAttributes attributes, + PropertyConstness constness, StoreFromKeyed store_mode) { RuntimeCallTimerScope stats_scope( *map, map->is_prototype_map() @@ -9533,19 +9151,19 @@ Handle<Map> Map::TransitionToDataProperty(Handle<Map> map, Handle<Name> name, ->GetDetails(descriptor) .attributes()); - return UpdateDescriptorForValue(transition, descriptor, value); + return UpdateDescriptorForValue(transition, descriptor, constness, value); } TransitionFlag flag = INSERT_TRANSITION; MaybeHandle<Map> maybe_map; - if (value->IsJSFunction()) { + if (!FLAG_track_constant_fields && value->IsJSFunction()) { maybe_map = Map::CopyWithConstant(map, name, value, attributes, flag); } else if (!map->TooManyFastProperties(store_mode)) { Isolate* isolate = name->GetIsolate(); Representation representation = value->OptimalRepresentation(); Handle<FieldType> type = value->OptimalType(isolate, representation); - maybe_map = - Map::CopyWithField(map, name, type, attributes, representation, flag); + maybe_map = Map::CopyWithField(map, name, type, attributes, constness, + representation, flag); } Handle<Map> result; @@ -9576,9 +9194,9 @@ Handle<Map> Map::ReconfigureExistingProperty(Handle<Map> map, int descriptor, if (!map->GetBackPointer()->IsMap()) { // There is no benefit from reconstructing transition tree for maps without // back pointers. - return CopyGeneralizeAllRepresentations( - map, map->elements_kind(), descriptor, FORCE_FIELD, kind, attributes, - "GenAll_AttributesMismatchProtoMap"); + return CopyGeneralizeAllFields(map, map->elements_kind(), descriptor, kind, + attributes, + "GenAll_AttributesMismatchProtoMap"); } if (FLAG_trace_generalization) { @@ -9586,9 +9204,12 @@ Handle<Map> Map::ReconfigureExistingProperty(Handle<Map> map, int descriptor, } Isolate* isolate = map->GetIsolate(); - Handle<Map> new_map = ReconfigureProperty( - map, descriptor, kind, attributes, Representation::None(), - FieldType::None(isolate), FORCE_FIELD); + + MapUpdater mu(isolate, map); + DCHECK_EQ(kData, kind); // Only kData case is supported so far. + Handle<Map> new_map = mu.ReconfigureToDataField( + descriptor, attributes, kDefaultFieldConstness, Representation::None(), + FieldType::None(isolate)); return new_map; } @@ -9648,7 +9269,7 @@ Handle<Map> Map::TransitionToAccessorProperty(Isolate* isolate, Handle<Map> map, return Map::Normalize(map, mode, "AccessorsOverwritingNonLast"); } PropertyDetails old_details = old_descriptors->GetDetails(descriptor); - if (old_details.type() != ACCESSOR_CONSTANT) { + if (old_details.kind() != kAccessor) { return Map::Normalize(map, mode, "AccessorsOverwritingNonAccessors"); } @@ -9690,8 +9311,8 @@ Handle<Map> Map::TransitionToAccessorProperty(Isolate* isolate, Handle<Map> map, pair->SetComponents(*getter, *setter); TransitionFlag flag = INSERT_TRANSITION; - AccessorConstantDescriptor new_desc(name, pair, attributes); - return Map::CopyInsertDescriptor(map, &new_desc, flag); + Descriptor d = Descriptor::AccessorConstant(name, pair, attributes); + return Map::CopyInsertDescriptor(map, &d, flag); } @@ -9770,15 +9391,13 @@ Handle<DescriptorArray> DescriptorArray::CopyUpToAddAttributes( if (!key->IsPrivate()) { int mask = DONT_DELETE | DONT_ENUM; // READ_ONLY is an invalid attribute for JS setters/getters. - if (details.type() != ACCESSOR_CONSTANT || !value->IsAccessorPair()) { + if (details.kind() != kAccessor || !value->IsAccessorPair()) { mask |= READ_ONLY; } details = details.CopyAddAttributes( static_cast<PropertyAttributes>(attributes & mask)); } - Descriptor inner_desc( - handle(key), handle(value, desc->GetIsolate()), details); - descriptors->SetDescriptor(i, &inner_desc); + descriptors->Set(i, key, value, details); } } else { for (int i = 0; i < size; ++i) { @@ -9799,7 +9418,8 @@ bool DescriptorArray::IsEqualUpTo(DescriptorArray* desc, int nof_descriptors) { } PropertyDetails details = GetDetails(i); PropertyDetails other_details = desc->GetDetails(i); - if (details.type() != other_details.type() || + if (details.kind() != other_details.kind() || + details.location() != other_details.location() || !details.representation().Equals(other_details.representation())) { return false; } @@ -10113,8 +9733,7 @@ Handle<FixedArray> FixedArray::SetAndGrow(Handle<FixedArray> array, int index, void FixedArray::Shrink(int new_length) { DCHECK(0 <= new_length && new_length <= length()); if (new_length < length()) { - GetHeap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>( - this, length() - new_length); + GetHeap()->RightTrimFixedArray(this, length() - new_length); } } @@ -10127,7 +9746,6 @@ void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) { } } - #ifdef DEBUG bool FixedArray::IsEqualTo(FixedArray* other) { if (length() != other->length()) return false; @@ -10308,6 +9926,12 @@ Handle<ArrayList> ArrayList::Add(Handle<ArrayList> array, Handle<Object> obj1, return array; } +Handle<ArrayList> ArrayList::New(Isolate* isolate, int size) { + Handle<ArrayList> result = Handle<ArrayList>::cast( + isolate->factory()->NewFixedArray(size + kFirstIndex)); + result->SetLength(0); + return result; +} bool ArrayList::IsFull() { int capacity = length(); @@ -10442,17 +10066,11 @@ void DescriptorArray::SetEnumCache(Handle<DescriptorArray> descriptors, } } - void DescriptorArray::CopyFrom(int index, DescriptorArray* src) { - Object* value = src->GetValue(index); PropertyDetails details = src->GetDetails(index); - Descriptor desc(handle(src->GetKey(index)), - handle(value, src->GetIsolate()), - details); - SetDescriptor(index, &desc); + Set(index, src->GetKey(index), src->GetValue(index), details); } - void DescriptorArray::Sort() { // In-place heap sort. int len = number_of_descriptors(); @@ -10554,34 +10172,12 @@ Handle<DeoptimizationOutputData> DeoptimizationOutputData::New( SharedFunctionInfo* DeoptimizationInputData::GetInlinedFunction(int index) { if (index == -1) { - return SharedFunctionInfo::cast(this->SharedFunctionInfo()); + return SharedFunctionInfo::cast(SharedFunctionInfo()); } else { return SharedFunctionInfo::cast(LiteralArray()->get(index)); } } -const int LiteralsArray::kFeedbackVectorOffset = - LiteralsArray::OffsetOfElementAt(LiteralsArray::kVectorIndex); - -const int LiteralsArray::kOffsetToFirstLiteral = - LiteralsArray::OffsetOfElementAt(LiteralsArray::kFirstLiteralIndex); - -// static -Handle<LiteralsArray> LiteralsArray::New(Isolate* isolate, - Handle<TypeFeedbackVector> vector, - int number_of_literals, - PretenureFlag pretenure) { - if (vector->is_empty() && number_of_literals == 0) { - return Handle<LiteralsArray>::cast( - isolate->factory()->empty_literals_array()); - } - Handle<FixedArray> literals = isolate->factory()->NewFixedArray( - number_of_literals + kFirstLiteralIndex, pretenure); - Handle<LiteralsArray> casted_literals = Handle<LiteralsArray>::cast(literals); - casted_literals->set_feedback_vector(*vector); - return casted_literals; -} - int HandlerTable::LookupRange(int pc_offset, int* data_out, CatchPrediction* prediction_out) { int innermost_handler = -1; @@ -10598,7 +10194,7 @@ int HandlerTable::LookupRange(int pc_offset, int* data_out, int handler_offset = HandlerOffsetField::decode(handler_field); CatchPrediction prediction = HandlerPredictionField::decode(handler_field); int handler_data = Smi::cast(get(i + kRangeDataIndex))->value(); - if (pc_offset > start_offset && pc_offset <= end_offset) { + if (pc_offset >= start_offset && pc_offset < end_offset) { DCHECK_GE(start_offset, innermost_start); DCHECK_LT(end_offset, innermost_end); innermost_handler = handler_offset; @@ -10668,11 +10264,7 @@ Handle<String> String::Trim(Handle<String> string, TrimMode mode) { return isolate->factory()->NewSubString(string, left, right); } -bool String::LooksValid() { - if (!GetIsolate()->heap()->Contains(this)) return false; - return true; -} - +bool String::LooksValid() { return GetIsolate()->heap()->Contains(this); } // static MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) { @@ -10806,8 +10398,7 @@ String::FlatContent String::GetFlatContent() { } string = cons->first(); shape = StringShape(string); - } - if (shape.representation_tag() == kSlicedStringTag) { + } else if (shape.representation_tag() == kSlicedStringTag) { SlicedString* slice = SlicedString::cast(string); offset = slice->offset(); string = slice->parent(); @@ -10815,6 +10406,13 @@ String::FlatContent String::GetFlatContent() { DCHECK(shape.representation_tag() != kConsStringTag && shape.representation_tag() != kSlicedStringTag); } + if (shape.representation_tag() == kThinStringTag) { + ThinString* thin = ThinString::cast(string); + string = thin->actual(); + shape = StringShape(string); + DCHECK(!shape.IsCons()); + DCHECK(!shape.IsSliced()); + } if (shape.encoding_tag() == kOneByteStringTag) { const uint8_t* start; if (shape.representation_tag() == kSeqStringTag) { @@ -10900,6 +10498,7 @@ const uc16* String::GetTwoByteData(unsigned start) { return slice->parent()->GetTwoByteData(start + slice->offset()); } case kConsStringTag: + case kThinStringTag: UNREACHABLE(); return NULL; } @@ -11166,6 +10765,7 @@ uint16_t ConsString::ConsStringGet(int index) { return 0; } +uint16_t ThinString::ThinStringGet(int index) { return actual()->Get(index); } uint16_t SlicedString::SlicedStringGet(int index) { return parent()->Get(offset() + index); @@ -11260,6 +10860,10 @@ void String::WriteToFlat(String* src, WriteToFlat(slice->parent(), sink, from + offset, to + offset); return; } + case kOneByteStringTag | kThinStringTag: + case kTwoByteStringTag | kThinStringTag: + source = ThinString::cast(source)->actual(); + break; } } } @@ -11481,6 +11085,17 @@ bool String::SlowEquals(String* other) { if (len != other->length()) return false; if (len == 0) return true; + // Fast check: if at least one ThinString is involved, dereference it/them + // and restart. + if (this->IsThinString() || other->IsThinString()) { + if (other->IsThinString()) other = ThinString::cast(other)->actual(); + if (this->IsThinString()) { + return ThinString::cast(this)->actual()->Equals(other); + } else { + return this->Equals(other); + } + } + // Fast check: if hash code is computed for both strings // a fast negative check can be performed. if (HasHashCode() && other->HasHashCode()) { @@ -11522,6 +11137,14 @@ bool String::SlowEquals(Handle<String> one, Handle<String> two) { if (one_length != two->length()) return false; if (one_length == 0) return true; + // Fast check: if at least one ThinString is involved, dereference it/them + // and restart. + if (one->IsThinString() || two->IsThinString()) { + if (one->IsThinString()) one = handle(ThinString::cast(*one)->actual()); + if (two->IsThinString()) two = handle(ThinString::cast(*two)->actual()); + return String::Equals(one, two); + } + // Fast check: if hash code is computed for both strings // a fast negative check can be performed. if (one->HasHashCode() && two->HasHashCode()) { @@ -11630,7 +11253,7 @@ ComparisonResult String::Compare(Handle<String> x, Handle<String> y) { Object* String::IndexOf(Isolate* isolate, Handle<Object> receiver, Handle<Object> search, Handle<Object> position) { - if (receiver->IsNull(isolate) || receiver->IsUndefined(isolate)) { + if (receiver->IsNullOrUndefined(isolate)) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined, isolate->factory()->NewStringFromAsciiChecked( @@ -11647,11 +11270,9 @@ Object* String::IndexOf(Isolate* isolate, Handle<Object> receiver, ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position, Object::ToInteger(isolate, position)); - double index = std::max(position->Number(), 0.0); - index = std::min(index, static_cast<double>(receiver_string->length())); - - return Smi::FromInt(String::IndexOf(isolate, receiver_string, search_string, - static_cast<uint32_t>(index))); + uint32_t index = receiver_string->ToValidIndex(*position); + return Smi::FromInt( + String::IndexOf(isolate, receiver_string, search_string, index)); } namespace { @@ -11833,7 +11454,7 @@ int StringMatchBackwards(Vector<const schar> subject, Object* String::LastIndexOf(Isolate* isolate, Handle<Object> receiver, Handle<Object> search, Handle<Object> position) { - if (receiver->IsNull(isolate) || receiver->IsUndefined(isolate)) { + if (receiver->IsNullOrUndefined(isolate)) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined, isolate->factory()->NewStringFromAsciiChecked( @@ -11857,11 +11478,7 @@ Object* String::LastIndexOf(Isolate* isolate, Handle<Object> receiver, } else { ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position, Object::ToInteger(isolate, position)); - - double position_number = std::max(position->Number(), 0.0); - position_number = std::min(position_number, - static_cast<double>(receiver_string->length())); - start_index = static_cast<uint32_t>(position_number); + start_index = receiver_string->ToValidIndex(*position); } uint32_t pattern_length = search_string->length(); @@ -12006,6 +11623,9 @@ bool String::SlowAsArrayIndex(uint32_t* index) { Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) { + Heap* heap = string->GetHeap(); + if (new_length == 0) return heap->isolate()->factory()->empty_string(); + int new_size, old_size; int old_length = string->length(); if (old_length <= new_length) return string; @@ -12025,18 +11645,16 @@ Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) { DCHECK_OBJECT_ALIGNED(start_of_string); DCHECK_OBJECT_ALIGNED(start_of_string + new_size); - Heap* heap = string->GetHeap(); // Sizes are pointer size aligned, so that we can use filler objects // that are a multiple of pointer size. heap->CreateFillerObjectAt(start_of_string + new_size, delta, ClearRecordedSlots::kNo); - heap->AdjustLiveBytes(*string, -delta, Heap::CONCURRENT_TO_SWEEPER); + heap->AdjustLiveBytes(*string, -delta); // We are storing the new length using release store after creating a filler // for the left-over space to avoid races with the sweeper thread. string->synchronized_set_length(new_length); - if (new_length == 0) return heap->isolate()->factory()->empty_string(); return string; } @@ -12209,7 +11827,9 @@ bool Map::EquivalentToForNormalization(Map* other, int properties = mode == CLEAR_INOBJECT_PROPERTIES ? 0 : other->GetInObjectProperties(); return CheckEquivalent(this, other) && bit_field2() == other->bit_field2() && - GetInObjectProperties() == properties; + GetInObjectProperties() == properties && + JSObject::GetInternalFieldCount(this) == + JSObject::GetInternalFieldCount(other); } @@ -12283,60 +11903,33 @@ void JSFunction::AttemptConcurrentOptimization() { } // static -Handle<LiteralsArray> SharedFunctionInfo::FindOrCreateLiterals( - Handle<SharedFunctionInfo> shared, Handle<Context> native_context) { - Isolate* isolate = shared->GetIsolate(); - CodeAndLiterals result = - shared->SearchOptimizedCodeMap(*native_context, BailoutId::None()); - if (result.literals != nullptr) { - DCHECK(shared->feedback_metadata()->is_empty() || - !result.literals->feedback_vector()->is_empty()); - return handle(result.literals, isolate); - } - - Handle<TypeFeedbackVector> feedback_vector = - TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); - Handle<LiteralsArray> literals = - LiteralsArray::New(isolate, feedback_vector, shared->num_literals()); - Handle<Code> code; - if (result.code != nullptr) { - code = Handle<Code>(result.code, isolate); - } - AddToOptimizedCodeMap(shared, native_context, code, literals, - BailoutId::None()); - return literals; -} - -// static void SharedFunctionInfo::AddToOptimizedCodeMap( Handle<SharedFunctionInfo> shared, Handle<Context> native_context, - MaybeHandle<Code> code, Handle<LiteralsArray> literals, - BailoutId osr_ast_id) { + Handle<Code> code, BailoutId osr_ast_id) { Isolate* isolate = shared->GetIsolate(); if (isolate->serializer_enabled()) return; - DCHECK(code.is_null() || - code.ToHandleChecked()->kind() == Code::OPTIMIZED_FUNCTION); + DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION); DCHECK(native_context->IsNativeContext()); - STATIC_ASSERT(kEntryLength == 4); + STATIC_ASSERT(kEntryLength == 2); Handle<FixedArray> new_code_map; int entry; + if (!osr_ast_id.IsNone()) { + Context::AddToOptimizedCodeMap(native_context, shared, code, osr_ast_id); + return; + } + + DCHECK(osr_ast_id.IsNone()); if (shared->OptimizedCodeMapIsCleared()) { new_code_map = isolate->factory()->NewFixedArray(kInitialLength, TENURED); entry = kEntriesStart; } else { Handle<FixedArray> old_code_map(shared->optimized_code_map(), isolate); - entry = shared->SearchOptimizedCodeMapEntry(*native_context, osr_ast_id); + entry = shared->SearchOptimizedCodeMapEntry(*native_context); if (entry >= kEntriesStart) { - // Just set the code and literals of the entry. - if (!code.is_null()) { - Handle<WeakCell> code_cell = - isolate->factory()->NewWeakCell(code.ToHandleChecked()); - old_code_map->set(entry + kCachedCodeOffset, *code_cell); - } - Handle<WeakCell> literals_cell = - isolate->factory()->NewWeakCell(literals); - old_code_map->set(entry + kLiteralsOffset, *literals_cell); + // Just set the code of the entry. + Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code); + old_code_map->set(entry + kCachedCodeOffset, *code_cell); return; } @@ -12364,16 +11957,11 @@ void SharedFunctionInfo::AddToOptimizedCodeMap( } } - Handle<WeakCell> code_cell = - code.is_null() ? isolate->factory()->empty_weak_cell() - : isolate->factory()->NewWeakCell(code.ToHandleChecked()); - Handle<WeakCell> literals_cell = isolate->factory()->NewWeakCell(literals); + Handle<WeakCell> code_cell = isolate->factory()->NewWeakCell(code); WeakCell* context_cell = native_context->self_weak_cell(); new_code_map->set(entry + kContextOffset, context_cell); new_code_map->set(entry + kCachedCodeOffset, *code_cell); - new_code_map->set(entry + kLiteralsOffset, *literals_cell); - new_code_map->set(entry + kOsrAstIdOffset, Smi::FromInt(osr_ast_id.ToInt())); #ifdef DEBUG for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) { @@ -12383,9 +11971,6 @@ void SharedFunctionInfo::AddToOptimizedCodeMap( DCHECK(cell->cleared() || (cell->value()->IsCode() && Code::cast(cell->value())->kind() == Code::OPTIMIZED_FUNCTION)); - cell = WeakCell::cast(new_code_map->get(i + kLiteralsOffset)); - DCHECK(cell->cleared() || cell->value()->IsFixedArray()); - DCHECK(new_code_map->get(i + kOsrAstIdOffset)->IsSmi()); } #endif @@ -12405,81 +11990,62 @@ void SharedFunctionInfo::ClearOptimizedCodeMap() { void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, const char* reason) { DisallowHeapAllocation no_gc; - if (OptimizedCodeMapIsCleared()) return; + Isolate* isolate = GetIsolate(); + bool found = false; - Heap* heap = GetHeap(); - FixedArray* code_map = optimized_code_map(); - int dst = kEntriesStart; - int length = code_map->length(); - for (int src = kEntriesStart; src < length; src += kEntryLength) { - DCHECK(WeakCell::cast(code_map->get(src))->cleared() || - WeakCell::cast(code_map->get(src))->value()->IsNativeContext()); - if (WeakCell::cast(code_map->get(src + kCachedCodeOffset))->value() == - optimized_code) { - BailoutId osr(Smi::cast(code_map->get(src + kOsrAstIdOffset))->value()); - if (FLAG_trace_opt) { - PrintF("[evicting entry from optimizing code map (%s) for ", reason); - ShortPrint(); - if (osr.IsNone()) { + if (!OptimizedCodeMapIsCleared()) { + Heap* heap = isolate->heap(); + FixedArray* code_map = optimized_code_map(); + int length = code_map->length(); + for (int src = kEntriesStart; src < length; src += kEntryLength) { + DCHECK(WeakCell::cast(code_map->get(src))->cleared() || + WeakCell::cast(code_map->get(src))->value()->IsNativeContext()); + found = WeakCell::cast(code_map->get(src + kCachedCodeOffset))->value() == + optimized_code; + if (found) { + if (FLAG_trace_opt) { + PrintF("[evicting entry from optimizing code map (%s) for ", reason); + ShortPrint(); PrintF("]\n"); - } else { - PrintF(" (osr ast id %d)]\n", osr.ToInt()); } + // Just clear the code. + code_map->set(src + kCachedCodeOffset, heap->empty_weak_cell(), + SKIP_WRITE_BARRIER); } - if (!osr.IsNone()) { - // Evict the src entry by not copying it to the dst entry. - continue; - } - // In case of non-OSR entry just clear the code in order to proceed - // sharing literals. - code_map->set(src + kCachedCodeOffset, heap->empty_weak_cell(), - SKIP_WRITE_BARRIER); - } - - // Keep the src entry by copying it to the dst entry. - if (dst != src) { - code_map->set(dst + kContextOffset, code_map->get(src + kContextOffset)); - code_map->set(dst + kCachedCodeOffset, - code_map->get(src + kCachedCodeOffset)); - code_map->set(dst + kLiteralsOffset, - code_map->get(src + kLiteralsOffset)); - code_map->set(dst + kOsrAstIdOffset, - code_map->get(src + kOsrAstIdOffset)); - } - dst += kEntryLength; - } - if (dst != length) { - // Always trim even when array is cleared because of heap verifier. - heap->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(code_map, - length - dst); - if (code_map->length() == kEntriesStart) { - ClearOptimizedCodeMap(); } } -} - -void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) { - FixedArray* code_map = optimized_code_map(); - DCHECK(shrink_by % kEntryLength == 0); - DCHECK(shrink_by <= code_map->length() - kEntriesStart); - // Always trim even when array is cleared because of heap verifier. - GetHeap()->RightTrimFixedArray<Heap::SEQUENTIAL_TO_SWEEPER>(code_map, - shrink_by); - if (code_map->length() == kEntriesStart) { - ClearOptimizedCodeMap(); + if (!found) { + // We didn't find the code in here. It must be osr'd code. + isolate->EvictOSROptimizedCode(optimized_code, reason); } } // static void JSFunction::EnsureLiterals(Handle<JSFunction> function) { Handle<SharedFunctionInfo> shared(function->shared()); - Handle<Context> native_context(function->context()->native_context()); - if (function->literals() == - function->GetIsolate()->heap()->empty_literals_array()) { - Handle<LiteralsArray> literals = - SharedFunctionInfo::FindOrCreateLiterals(shared, native_context); - function->set_literals(*literals); + Isolate* isolate = shared->GetIsolate(); + + FeedbackVectorState state = function->GetFeedbackVectorState(isolate); + switch (state) { + case TOP_LEVEL_SCRIPT_NEEDS_VECTOR: { + // A top level script didn't get it's literals installed. + Handle<FeedbackVector> feedback_vector = + FeedbackVector::New(isolate, shared); + Handle<Cell> new_cell = + isolate->factory()->NewOneClosureCell(feedback_vector); + function->set_feedback_vector_cell(*new_cell); + break; + } + case NEEDS_VECTOR: { + Handle<FeedbackVector> feedback_vector = + FeedbackVector::New(isolate, shared); + function->feedback_vector_cell()->set_value(*feedback_vector); + break; + } + case HAS_VECTOR: + // Nothing to do. + break; } } @@ -12524,19 +12090,10 @@ void Map::CompleteInobjectSlackTracking() { static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) { DisallowHeapAllocation no_gc; if (!object->HasFastProperties()) return false; - Map* map = object->map(); - if (map->is_prototype_map()) return false; - DescriptorArray* descriptors = map->instance_descriptors(); - for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) { - PropertyDetails details = descriptors->GetDetails(i); - if (details.location() == kDescriptor) continue; - if (details.representation().IsHeapObject() || - details.representation().IsTagged()) { - FieldIndex index = FieldIndex::ForDescriptor(map, i); - if (object->RawFastPropertyAt(index)->IsJSFunction()) return true; - } - } - return false; + if (object->IsJSGlobalProxy()) return false; + if (object->GetIsolate()->bootstrapper()->IsActive()) return false; + return !object->map()->is_prototype_map() || + !object->map()->should_be_fast_prototype_map(); } // static @@ -12551,8 +12108,10 @@ void JSObject::MakePrototypesFast(Handle<Object> receiver, if (!current->IsJSObject()) return; Handle<JSObject> current_obj = Handle<JSObject>::cast(current); Map* current_map = current_obj->map(); - if (current_map->is_prototype_map() && - !current_map->should_be_fast_prototype_map()) { + if (current_map->is_prototype_map()) { + // If the map is already marked as should be fast, we're done. Its + // prototypes will have been marked already as well. + if (current_map->should_be_fast_prototype_map()) return; Handle<Map> map(current_map); Map::SetShouldBeFastPrototypeMap(map, true, isolate); JSObject::OptimizeAsPrototype(current_obj, FAST_PROTOTYPE); @@ -12758,9 +12317,17 @@ void Map::SetShouldBeFastPrototypeMap(Handle<Map> map, bool value, // static Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map, Isolate* isolate) { - Handle<Object> maybe_prototype( - map->GetPrototypeChainRootMap(isolate)->prototype(), isolate); - if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null(); + Handle<Object> maybe_prototype; + if (map->IsJSGlobalObjectMap()) { + DCHECK(map->is_prototype_map()); + // Global object is prototype of a global proxy and therefore we can + // use its validity cell for guarding global object's prototype change. + maybe_prototype = isolate->global_object(); + } else { + maybe_prototype = + handle(map->GetPrototypeChainRootMap(isolate)->prototype(), isolate); + if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null(); + } Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype); // Ensure the prototype is registered with its own prototypes so its cell // will be invalidated when necessary. @@ -12997,6 +12564,7 @@ bool CanSubclassHaveInobjectProperties(InstanceType instance_type) { case JS_API_OBJECT_TYPE: case JS_ARRAY_BUFFER_TYPE: case JS_ARRAY_TYPE: + case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE: case JS_CONTEXT_EXTENSION_OBJECT_TYPE: case JS_DATA_VIEW_TYPE: case JS_DATE_TYPE: @@ -13038,7 +12606,6 @@ bool CanSubclassHaveInobjectProperties(InstanceType instance_type) { case ODDBALL_TYPE: case PROPERTY_CELL_TYPE: case SHARED_FUNCTION_INFO_TYPE: - case SIMD128_VALUE_TYPE: case SYMBOL_TYPE: case WEAK_CELL_TYPE: @@ -13131,7 +12698,7 @@ MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate, // Link initial map and constructor function if the new.target is actually a // subclass constructor. - if (IsSubclassConstructor(function->shared()->kind())) { + if (IsDerivedConstructor(function->shared()->kind())) { Handle<Object> prototype(function->instance_prototype(), isolate); InstanceType instance_type = constructor_initial_map->instance_type(); DCHECK(CanSubclassHaveInobjectProperties(instance_type)); @@ -13281,8 +12848,7 @@ Handle<String> JSFunction::ToString(Handle<JSFunction> function) { Handle<SharedFunctionInfo> shared_info(function->shared(), isolate); // Check if {function} should hide its source code. - if (!shared_info->script()->IsScript() || - Script::cast(shared_info->script())->hide_source()) { + if (!shared_info->IsUserJavaScript()) { return NativeCodeFunctionSourceString(shared_info); } @@ -13304,6 +12870,10 @@ Handle<String> JSFunction::ToString(Handle<JSFunction> function) { return NativeCodeFunctionSourceString(shared_info); } + if (FLAG_harmony_function_tostring) { + return Handle<String>::cast(shared_info->GetSourceCodeHarmony()); + } + IncrementalStringBuilder builder(isolate); FunctionKind kind = shared_info->kind(); if (!IsArrowFunction(kind)) { @@ -13355,8 +12925,8 @@ void Script::SetEvalOrigin(Handle<Script> script, // position, but store it as negative value for lazy translation. StackTraceFrameIterator it(script->GetIsolate()); if (!it.done() && it.is_javascript()) { - FrameSummary summary = FrameSummary::GetFirst(it.javascript_frame()); - script->set_eval_from_shared(summary.function()->shared()); + FrameSummary summary = FrameSummary::GetTop(it.javascript_frame()); + script->set_eval_from_shared(summary.AsJavaScript().function()->shared()); script->set_eval_from_position(-summary.code_offset()); return; } @@ -13409,15 +12979,7 @@ bool Script::GetPositionInfo(Handle<Script> script, int position, PositionInfo* info, OffsetFlag offset_flag) { // For wasm, we do not create an artificial line_ends array, but do the // translation directly. - if (script->type() == Script::TYPE_WASM) { - Handle<WasmCompiledModule> compiled_module( - WasmCompiledModule::cast(script->wasm_compiled_module())); - DCHECK_LE(0, position); - return wasm::GetPositionInfo(compiled_module, - static_cast<uint32_t>(position), info); - } - - InitLineEnds(script); + if (script->type() != Script::TYPE_WASM) InitLineEnds(script); return script->GetPositionInfo(position, info, offset_flag); } @@ -13453,6 +13015,16 @@ bool Script::GetPositionInfo(int position, PositionInfo* info, OffsetFlag offset_flag) const { DisallowHeapAllocation no_allocation; + // For wasm, we do not rely on the line_ends array, but do the translation + // directly. + if (type() == Script::TYPE_WASM) { + Handle<WasmCompiledModule> compiled_module( + WasmCompiledModule::cast(wasm_compiled_module())); + DCHECK_LE(0, position); + return compiled_module->GetPositionInfo(static_cast<uint32_t>(position), + info); + } + if (line_ends()->IsUndefined(GetIsolate())) { // Slow mode: we do not have line_ends. We have to iterate through source. if (!GetPositionInfoSlow(this, position, info)) return false; @@ -13546,15 +13118,11 @@ int Script::GetLineNumber(int code_pos) const { return info.line; } -Handle<Object> Script::GetNameOrSourceURL(Handle<Script> script) { - Isolate* isolate = script->GetIsolate(); - +Object* Script::GetNameOrSourceURL() { + Isolate* isolate = GetIsolate(); // Keep in sync with ScriptNameOrSourceURL in messages.js. - - if (!script->source_url()->IsUndefined(isolate)) { - return handle(script->source_url(), isolate); - } - return handle(script->name(), isolate); + if (!source_url()->IsUndefined(isolate)) return source_url(); + return name(); } @@ -13583,53 +13151,68 @@ Handle<JSObject> Script::GetWrapper(Handle<Script> script) { return result; } - MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo( - FunctionLiteral* fun) { - WeakFixedArray::Iterator iterator(shared_function_infos()); - SharedFunctionInfo* shared; - while ((shared = iterator.Next<SharedFunctionInfo>())) { - if (fun->function_token_position() == shared->function_token_position() && - fun->start_position() == shared->start_position() && - fun->end_position() == shared->end_position()) { - return Handle<SharedFunctionInfo>(shared); - } + Isolate* isolate, const FunctionLiteral* fun) { + DCHECK_NE(fun->function_literal_id(), FunctionLiteral::kIdTypeInvalid); + DCHECK_LT(fun->function_literal_id(), shared_function_infos()->length()); + Object* shared = shared_function_infos()->get(fun->function_literal_id()); + if (shared->IsUndefined(isolate) || WeakCell::cast(shared)->cleared()) { + return MaybeHandle<SharedFunctionInfo>(); } - return MaybeHandle<SharedFunctionInfo>(); + return handle(SharedFunctionInfo::cast(WeakCell::cast(shared)->value())); } - Script::Iterator::Iterator(Isolate* isolate) : iterator_(isolate->heap()->script_list()) {} Script* Script::Iterator::Next() { return iterator_.Next<Script>(); } +SharedFunctionInfo::ScriptIterator::ScriptIterator(Handle<Script> script) + : ScriptIterator(script->GetIsolate(), + handle(script->shared_function_infos())) {} -SharedFunctionInfo::Iterator::Iterator(Isolate* isolate) - : script_iterator_(isolate), - sfi_iterator_(isolate->heap()->noscript_shared_function_infos()) {} +SharedFunctionInfo::ScriptIterator::ScriptIterator( + Isolate* isolate, Handle<FixedArray> shared_function_infos) + : isolate_(isolate), + shared_function_infos_(shared_function_infos), + index_(0) {} +SharedFunctionInfo* SharedFunctionInfo::ScriptIterator::Next() { + while (index_ < shared_function_infos_->length()) { + Object* raw = shared_function_infos_->get(index_++); + if (raw->IsUndefined(isolate_) || WeakCell::cast(raw)->cleared()) continue; + return SharedFunctionInfo::cast(WeakCell::cast(raw)->value()); + } + return nullptr; +} -bool SharedFunctionInfo::Iterator::NextScript() { - Script* script = script_iterator_.Next(); - if (script == NULL) return false; - sfi_iterator_.Reset(script->shared_function_infos()); - return true; +void SharedFunctionInfo::ScriptIterator::Reset(Handle<Script> script) { + shared_function_infos_ = handle(script->shared_function_infos()); + index_ = 0; } +SharedFunctionInfo::GlobalIterator::GlobalIterator(Isolate* isolate) + : script_iterator_(isolate), + noscript_sfi_iterator_(isolate->heap()->noscript_shared_function_infos()), + sfi_iterator_(handle(script_iterator_.Next(), isolate)) {} -SharedFunctionInfo* SharedFunctionInfo::Iterator::Next() { - do { - SharedFunctionInfo* next = sfi_iterator_.Next<SharedFunctionInfo>(); - if (next != NULL) return next; - } while (NextScript()); - return NULL; +SharedFunctionInfo* SharedFunctionInfo::GlobalIterator::Next() { + SharedFunctionInfo* next = noscript_sfi_iterator_.Next<SharedFunctionInfo>(); + if (next != nullptr) return next; + for (;;) { + next = sfi_iterator_.Next(); + if (next != nullptr) return next; + Script* next_script = script_iterator_.Next(); + if (next_script == nullptr) return nullptr; + sfi_iterator_.Reset(handle(next_script)); + } } void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared, Handle<Object> script_object) { + DCHECK_NE(shared->function_literal_id(), FunctionLiteral::kIdTypeInvalid); if (shared->script() == *script_object) return; Isolate* isolate = shared->GetIsolate(); @@ -13637,39 +13220,52 @@ void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared, // the shared function info may be temporarily in two lists. // This is okay because the gc-time processing of these lists can tolerate // duplicates. - Handle<Object> list; if (script_object->IsScript()) { Handle<Script> script = Handle<Script>::cast(script_object); - list = handle(script->shared_function_infos(), isolate); + Handle<FixedArray> list = handle(script->shared_function_infos(), isolate); +#ifdef DEBUG + DCHECK_LT(shared->function_literal_id(), list->length()); + if (list->get(shared->function_literal_id())->IsWeakCell() && + !WeakCell::cast(list->get(shared->function_literal_id()))->cleared()) { + DCHECK( + WeakCell::cast(list->get(shared->function_literal_id()))->value() == + *shared); + } +#endif + Handle<WeakCell> cell = isolate->factory()->NewWeakCell(shared); + list->set(shared->function_literal_id(), *cell); } else { - list = isolate->factory()->noscript_shared_function_infos(); - } + Handle<Object> list = isolate->factory()->noscript_shared_function_infos(); #ifdef DEBUG - if (FLAG_enable_slow_asserts) { - WeakFixedArray::Iterator iterator(*list); - SharedFunctionInfo* next; - while ((next = iterator.Next<SharedFunctionInfo>())) { - DCHECK_NE(next, *shared); + if (FLAG_enable_slow_asserts) { + WeakFixedArray::Iterator iterator(*list); + SharedFunctionInfo* next; + while ((next = iterator.Next<SharedFunctionInfo>())) { + DCHECK_NE(next, *shared); + } } - } #endif // DEBUG - list = WeakFixedArray::Add(list, shared); - if (script_object->IsScript()) { - Handle<Script> script = Handle<Script>::cast(script_object); - script->set_shared_function_infos(*list); - } else { + list = WeakFixedArray::Add(list, shared); + isolate->heap()->SetRootNoScriptSharedFunctionInfos(*list); } - // Remove shared function info from old script's list. if (shared->script()->IsScript()) { + // Remove shared function info from old script's list. Script* old_script = Script::cast(shared->script()); - if (old_script->shared_function_infos()->IsWeakFixedArray()) { - WeakFixedArray* list = - WeakFixedArray::cast(old_script->shared_function_infos()); - list->Remove(shared); + + // Due to liveedit, it might happen that the old_script doesn't know + // about the SharedFunctionInfo, so we have to guard against that. + Handle<FixedArray> infos(old_script->shared_function_infos(), isolate); + if (shared->function_literal_id() < infos->length()) { + Object* raw = old_script->shared_function_infos()->get( + shared->function_literal_id()); + if (!raw->IsWeakCell() || WeakCell::cast(raw)->value() == *shared) { + old_script->shared_function_infos()->set( + shared->function_literal_id(), isolate->heap()->undefined_value()); + } } } else { // Remove shared function info from root array. @@ -13688,6 +13284,16 @@ String* SharedFunctionInfo::DebugName() { return String::cast(n); } +bool SharedFunctionInfo::HasNoSideEffect() { + if (!computed_has_no_side_effect()) { + DisallowHeapAllocation not_handlified; + Handle<SharedFunctionInfo> info(this); + set_has_no_side_effect(DebugEvaluate::FunctionHasNoSideEffect(info)); + set_computed_has_no_side_effect(true); + } + return has_no_side_effect(); +} + // The filter is a pattern that matches function names in this way: // "*" all; the default // "-" all but the top-level function @@ -13738,6 +13344,15 @@ Handle<Object> SharedFunctionInfo::GetSourceCode() { source, start_position(), end_position()); } +Handle<Object> SharedFunctionInfo::GetSourceCodeHarmony() { + Isolate* isolate = GetIsolate(); + if (!HasSourceCode()) return isolate->factory()->undefined_value(); + Handle<String> script_source(String::cast(Script::cast(script())->source())); + int start_pos = function_token_position(); + if (start_pos == kNoSourcePosition) start_pos = start_position(); + return isolate->factory()->NewSubString(script_source, start_pos, + end_position()); +} bool SharedFunctionInfo::IsInlineable() { // Check that the function has a script associated with it. @@ -13790,7 +13405,7 @@ void JSFunction::CalculateInstanceSizeForDerivedClass( JSFunction* func = JSFunction::cast(current); SharedFunctionInfo* shared = func->shared(); expected_nof_properties += shared->expected_nof_properties(); - if (!IsSubclassConstructor(shared->kind())) { + if (!IsDerivedConstructor(shared->kind())) { break; } } @@ -13933,8 +13548,6 @@ void SharedFunctionInfo::InitFromFunctionLiteral( shared_info->set_language_mode(lit->language_mode()); shared_info->set_uses_arguments(lit->scope()->arguments() != NULL); shared_info->set_has_duplicate_parameters(lit->has_duplicate_parameters()); - shared_info->set_is_function(lit->is_function()); - shared_info->set_never_compiled(true); shared_info->set_kind(lit->kind()); if (!IsConstructable(lit->kind(), lit->language_mode())) { shared_info->SetConstructStub( @@ -13942,9 +13555,7 @@ void SharedFunctionInfo::InitFromFunctionLiteral( } shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject()); shared_info->set_asm_function(lit->scope()->asm_function()); - shared_info->set_requires_class_field_init(lit->requires_class_field_init()); - shared_info->set_is_class_field_initializer( - lit->is_class_field_initializer()); + shared_info->set_function_literal_id(lit->function_literal_id()); SetExpectedNofPropertiesFromEstimate(shared_info, lit); } @@ -13999,19 +13610,15 @@ void SharedFunctionInfo::ResetForNewContext(int new_ic_age) { } } - -int SharedFunctionInfo::SearchOptimizedCodeMapEntry(Context* native_context, - BailoutId osr_ast_id) { +int SharedFunctionInfo::SearchOptimizedCodeMapEntry(Context* native_context) { DisallowHeapAllocation no_gc; DCHECK(native_context->IsNativeContext()); if (!OptimizedCodeMapIsCleared()) { FixedArray* optimized_code_map = this->optimized_code_map(); int length = optimized_code_map->length(); - Smi* osr_ast_id_smi = Smi::FromInt(osr_ast_id.ToInt()); for (int i = kEntriesStart; i < length; i += kEntryLength) { if (WeakCell::cast(optimized_code_map->get(i + kContextOffset)) - ->value() == native_context && - optimized_code_map->get(i + kOsrAstIdOffset) == osr_ast_id_smi) { + ->value() == native_context) { return i; } } @@ -14031,20 +13638,20 @@ void SharedFunctionInfo::ClearCodeFromOptimizedCodeMap() { } } -CodeAndLiterals SharedFunctionInfo::SearchOptimizedCodeMap( - Context* native_context, BailoutId osr_ast_id) { - CodeAndLiterals result = {nullptr, nullptr}; - int entry = SearchOptimizedCodeMapEntry(native_context, osr_ast_id); +Code* SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context, + BailoutId osr_ast_id) { + Code* result = nullptr; + if (!osr_ast_id.IsNone()) { + return native_context->SearchOptimizedCodeMap(this, osr_ast_id); + } + + DCHECK(osr_ast_id.IsNone()); + int entry = SearchOptimizedCodeMapEntry(native_context); if (entry != kNotFound) { FixedArray* code_map = optimized_code_map(); DCHECK_LE(entry + kEntryLength, code_map->length()); WeakCell* cell = WeakCell::cast(code_map->get(entry + kCachedCodeOffset)); - WeakCell* literals_cell = - WeakCell::cast(code_map->get(entry + kLiteralsOffset)); - - result = {cell->cleared() ? nullptr : Code::cast(cell->value()), - literals_cell->cleared() ? nullptr : LiteralsArray::cast( - literals_cell->value())}; + result = cell->cleared() ? nullptr : Code::cast(cell->value()); } return result; } @@ -14289,7 +13896,8 @@ void Code::ClearInlineCaches() { RelocInfo* info = it.rinfo(); Code* target(Code::GetCodeFromTargetAddress(info->target_address())); if (target->is_inline_cache_stub()) { - IC::Clear(this->GetIsolate(), info->pc(), info->host()->constant_pool()); + ICUtility::Clear(this->GetIsolate(), info->pc(), + info->host()->constant_pool()); } } } @@ -14324,11 +13932,10 @@ int AbstractCode::SourceStatementPosition(int offset) { } void JSFunction::ClearTypeFeedbackInfo() { - feedback_vector()->ClearSlots(shared()); -} - -void JSFunction::ClearTypeFeedbackInfoAtGCTime() { - feedback_vector()->ClearSlotsAtGCTime(shared()); + if (feedback_vector_cell()->value()->IsFeedbackVector()) { + FeedbackVector* vector = feedback_vector(); + vector->ClearSlots(this); + } } BailoutId Code::TranslatePcOffsetToAstId(uint32_t pc_offset) { @@ -14353,21 +13960,13 @@ uint32_t Code::TranslateAstIdToPcOffset(BailoutId ast_id) { return 0; } -int Code::LookupRangeInHandlerTable(int code_offset, int* data, - HandlerTable::CatchPrediction* prediction) { - DCHECK(!is_optimized_code()); - HandlerTable* table = HandlerTable::cast(handler_table()); - return table->LookupRange(code_offset, data, prediction); -} - void Code::MakeCodeAgeSequenceYoung(byte* sequence, Isolate* isolate) { - PatchPlatformCodeAge(isolate, sequence, kNoAgeCodeAge, NO_MARKING_PARITY); + PatchPlatformCodeAge(isolate, sequence, kNoAgeCodeAge); } void Code::MarkCodeAsExecuted(byte* sequence, Isolate* isolate) { - PatchPlatformCodeAge(isolate, sequence, kExecutedOnceCodeAge, - NO_MARKING_PARITY); + PatchPlatformCodeAge(isolate, sequence, kExecutedOnceCodeAge); } @@ -14401,28 +14000,25 @@ void Code::MakeYoung(Isolate* isolate) { void Code::PreAge(Isolate* isolate) { byte* sequence = FindCodeAgeSequence(); if (sequence != NULL) { - PatchPlatformCodeAge(isolate, sequence, kPreAgedCodeAge, NO_MARKING_PARITY); + PatchPlatformCodeAge(isolate, sequence, kPreAgedCodeAge); } } void Code::MarkToBeExecutedOnce(Isolate* isolate) { byte* sequence = FindCodeAgeSequence(); if (sequence != NULL) { - PatchPlatformCodeAge(isolate, sequence, kToBeExecutedOnceCodeAge, - NO_MARKING_PARITY); + PatchPlatformCodeAge(isolate, sequence, kToBeExecutedOnceCodeAge); } } -void Code::MakeOlder(MarkingParity current_parity) { +void Code::MakeOlder() { byte* sequence = FindCodeAgeSequence(); if (sequence != NULL) { - Age age; - MarkingParity code_parity; Isolate* isolate = GetIsolate(); - GetCodeAgeAndParity(isolate, sequence, &age, &code_parity); + Age age = GetCodeAge(isolate, sequence); Age next_age = NextAge(age); - if (age != next_age && code_parity != current_parity) { - PatchPlatformCodeAge(isolate, sequence, next_age, current_parity); + if (age != next_age) { + PatchPlatformCodeAge(isolate, sequence, next_age); } } } @@ -14448,77 +14044,47 @@ Code::Age Code::GetAge() { if (sequence == NULL) { return kNoAgeCodeAge; } - Age age; - MarkingParity parity; - GetCodeAgeAndParity(GetIsolate(), sequence, &age, &parity); - return age; + return GetCodeAge(GetIsolate(), sequence); } - -void Code::GetCodeAgeAndParity(Code* code, Age* age, - MarkingParity* parity) { +Code::Age Code::GetAgeOfCodeAgeStub(Code* code) { Isolate* isolate = code->GetIsolate(); Builtins* builtins = isolate->builtins(); - Code* stub = NULL; -#define HANDLE_CODE_AGE(AGE) \ - stub = *builtins->Make##AGE##CodeYoungAgainEvenMarking(); \ - if (code == stub) { \ - *age = k##AGE##CodeAge; \ - *parity = EVEN_MARKING_PARITY; \ - return; \ - } \ - stub = *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ - if (code == stub) { \ - *age = k##AGE##CodeAge; \ - *parity = ODD_MARKING_PARITY; \ - return; \ +#define HANDLE_CODE_AGE(AGE) \ + if (code == *builtins->Make##AGE##CodeYoungAgain()) { \ + return k##AGE##CodeAge; \ } CODE_AGE_LIST(HANDLE_CODE_AGE) #undef HANDLE_CODE_AGE - stub = *builtins->MarkCodeAsExecutedOnce(); - if (code == stub) { - *age = kNotExecutedCodeAge; - *parity = NO_MARKING_PARITY; - return; + if (code == *builtins->MarkCodeAsExecutedOnce()) { + return kNotExecutedCodeAge; } - stub = *builtins->MarkCodeAsExecutedTwice(); - if (code == stub) { - *age = kExecutedOnceCodeAge; - *parity = NO_MARKING_PARITY; - return; + if (code == *builtins->MarkCodeAsExecutedTwice()) { + return kExecutedOnceCodeAge; } - stub = *builtins->MarkCodeAsToBeExecutedOnce(); - if (code == stub) { - *age = kToBeExecutedOnceCodeAge; - *parity = NO_MARKING_PARITY; - return; + if (code == *builtins->MarkCodeAsToBeExecutedOnce()) { + return kToBeExecutedOnceCodeAge; } UNREACHABLE(); + return kNoAgeCodeAge; } - -Code* Code::GetCodeAgeStub(Isolate* isolate, Age age, MarkingParity parity) { +Code* Code::GetCodeAgeStub(Isolate* isolate, Age age) { Builtins* builtins = isolate->builtins(); switch (age) { -#define HANDLE_CODE_AGE(AGE) \ - case k##AGE##CodeAge: { \ - Code* stub = parity == EVEN_MARKING_PARITY \ - ? *builtins->Make##AGE##CodeYoungAgainEvenMarking() \ - : *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ - return stub; \ - } +#define HANDLE_CODE_AGE(AGE) \ + case k##AGE##CodeAge: { \ + return *builtins->Make##AGE##CodeYoungAgain(); \ + } CODE_AGE_LIST(HANDLE_CODE_AGE) #undef HANDLE_CODE_AGE case kNotExecutedCodeAge: { - DCHECK(parity == NO_MARKING_PARITY); return *builtins->MarkCodeAsExecutedOnce(); } case kExecutedOnceCodeAge: { - DCHECK(parity == NO_MARKING_PARITY); return *builtins->MarkCodeAsExecutedTwice(); } case kToBeExecutedOnceCodeAge: { - DCHECK(parity == NO_MARKING_PARITY); return *builtins->MarkCodeAsToBeExecutedOnce(); } default: @@ -14712,14 +14278,24 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint( break; } + case Translation::CONSTRUCT_STUB_FRAME: { + int bailout_id = iterator.Next(); + int shared_info_id = iterator.Next(); + Object* shared_info = LiteralArray()->get(shared_info_id); + unsigned height = iterator.Next(); + os << "{bailout_id=" << bailout_id << ", function=" + << Brief(SharedFunctionInfo::cast(shared_info)->DebugName()) + << ", height=" << height << "}"; + break; + } + case Translation::COMPILED_STUB_FRAME: { Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next()); os << "{kind=" << stub_kind << "}"; break; } - case Translation::ARGUMENTS_ADAPTOR_FRAME: - case Translation::CONSTRUCT_STUB_FRAME: { + case Translation::ARGUMENTS_ADAPTOR_FRAME: { int shared_info_id = iterator.Next(); Object* shared_info = LiteralArray()->get(shared_info_id); unsigned height = iterator.Next(); @@ -14906,8 +14482,8 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT if (!IC::ICUseVector(kind())) { InlineCacheState ic_state = IC::StateFromCode(this); os << "ic_state = " << ICState2String(ic_state) << "\n"; + PrintExtraICState(os, kind(), extra_ic_state()); } - PrintExtraICState(os, kind(), extra_ic_state()); if (is_compare_ic_stub()) { DCHECK(CodeStub::GetMajorKey(this) == CodeStub::CompareIC); CompareICStub stub(stub_key(), GetIsolate()); @@ -15122,11 +14698,17 @@ void BytecodeArray::CopyBytecodesTo(BytecodeArray* to) { from->length()); } -int BytecodeArray::LookupRangeInHandlerTable( - int code_offset, int* data, HandlerTable::CatchPrediction* prediction) { - HandlerTable* table = HandlerTable::cast(handler_table()); - code_offset++; // Point after current bytecode. - return table->LookupRange(code_offset, data, prediction); +void BytecodeArray::MakeOlder() { + Age age = bytecode_age(); + if (age < kLastBytecodeAge) { + set_bytecode_age(static_cast<Age>(age + 1)); + } + DCHECK_GE(bytecode_age(), kFirstBytecodeAge); + DCHECK_LE(bytecode_age(), kLastBytecodeAge); +} + +bool BytecodeArray::IsOld() const { + return bytecode_age() >= kIsOldBytecodeAge; } // static @@ -15565,9 +15147,6 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, // SpiderMonkey behaves this way. if (!value->IsJSReceiver() && !value->IsNull(isolate)) return Just(true); - bool dictionary_elements_in_chain = - object->map()->DictionaryElementsInPrototypeChainOnly(); - bool all_extensible = object->map()->is_extensible(); Handle<JSObject> real_receiver = object; if (from_javascript) { @@ -15633,14 +15212,6 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object, DCHECK(new_map->prototype() == *value); JSObject::MigrateToMap(real_receiver, new_map); - if (from_javascript && !dictionary_elements_in_chain && - new_map->DictionaryElementsInPrototypeChainOnly()) { - // If the prototype chain didn't previously have element callbacks, then - // KeyedStoreICs need to be cleared to ensure any that involve this - // map go generic. - TypeFeedbackVector::ClearAllKeyedStoreICs(isolate); - } - heap->ClearInstanceofCache(); DCHECK(size == object->Size()); return Just(true); @@ -15837,12 +15408,10 @@ Maybe<bool> JSObject::AddDataElement(Handle<JSObject> object, uint32_t index, ElementsAccessor* accessor = ElementsAccessor::ForKind(to); accessor->Add(object, index, value, attributes, new_capacity); - uint32_t new_length = old_length; - Handle<Object> new_length_handle; if (object->IsJSArray() && index >= old_length) { - new_length = index + 1; - new_length_handle = isolate->factory()->NewNumberFromUint(new_length); - JSArray::cast(*object)->set_length(*new_length_handle); + Handle<Object> new_length = + isolate->factory()->NewNumberFromUint(index + 1); + JSArray::cast(*object)->set_length(*new_length); } return Just(true); @@ -16147,7 +15716,8 @@ void Dictionary<Derived, Shape, Key>::Print(std::ostream& os) { // NOLINT } else { os << Brief(k); } - os << ": " << Brief(this->ValueAt(i)) << " " << this->DetailsAt(i); + os << ": " << Brief(this->ValueAt(i)) << " "; + this->DetailsAt(i).PrintAsSlowTo(os); } } } @@ -16213,118 +15783,6 @@ int FixedArrayBase::GetMaxLengthForNewSpaceAllocation(ElementsKind kind) { ElementsKindToShiftSize(kind)); } -void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) { - Object* temp = get(i); - set(i, get(j)); - set(j, temp); - if (this != numbers) { - temp = numbers->get(i); - numbers->set(i, Smi::cast(numbers->get(j))); - numbers->set(j, Smi::cast(temp)); - } -} - - -static void InsertionSortPairs(FixedArray* content, - FixedArray* numbers, - int len) { - for (int i = 1; i < len; i++) { - int j = i; - while (j > 0 && - (NumberToUint32(numbers->get(j - 1)) > - NumberToUint32(numbers->get(j)))) { - content->SwapPairs(numbers, j - 1, j); - j--; - } - } -} - - -void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) { - // In-place heap sort. - DCHECK(content->length() == numbers->length()); - - // Bottom-up max-heap construction. - for (int i = 1; i < len; ++i) { - int child_index = i; - while (child_index > 0) { - int parent_index = ((child_index + 1) >> 1) - 1; - uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); - uint32_t child_value = NumberToUint32(numbers->get(child_index)); - if (parent_value < child_value) { - content->SwapPairs(numbers, parent_index, child_index); - } else { - break; - } - child_index = parent_index; - } - } - - // Extract elements and create sorted array. - for (int i = len - 1; i > 0; --i) { - // Put max element at the back of the array. - content->SwapPairs(numbers, 0, i); - // Sift down the new top element. - int parent_index = 0; - while (true) { - int child_index = ((parent_index + 1) << 1) - 1; - if (child_index >= i) break; - uint32_t child1_value = NumberToUint32(numbers->get(child_index)); - uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1)); - uint32_t parent_value = NumberToUint32(numbers->get(parent_index)); - if (child_index + 1 >= i || child1_value > child2_value) { - if (parent_value > child1_value) break; - content->SwapPairs(numbers, parent_index, child_index); - parent_index = child_index; - } else { - if (parent_value > child2_value) break; - content->SwapPairs(numbers, parent_index, child_index + 1); - parent_index = child_index + 1; - } - } - } -} - - -// Sort this array and the numbers as pairs wrt. the (distinct) numbers. -void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) { - DCHECK(this->length() == numbers->length()); - // For small arrays, simply use insertion sort. - if (len <= 10) { - InsertionSortPairs(this, numbers, len); - return; - } - // Check the range of indices. - uint32_t min_index = NumberToUint32(numbers->get(0)); - uint32_t max_index = min_index; - uint32_t i; - for (i = 1; i < len; i++) { - if (NumberToUint32(numbers->get(i)) < min_index) { - min_index = NumberToUint32(numbers->get(i)); - } else if (NumberToUint32(numbers->get(i)) > max_index) { - max_index = NumberToUint32(numbers->get(i)); - } - } - if (max_index - min_index + 1 == len) { - // Indices form a contiguous range, unless there are duplicates. - // Do an in-place linear time sort assuming distinct numbers, but - // avoid hanging in case they are not. - for (i = 0; i < len; i++) { - uint32_t p; - uint32_t j = 0; - // While the current element at i is not at its correct position p, - // swap the elements at these two positions. - while ((p = NumberToUint32(numbers->get(i)) - min_index) != i && - j++ < len) { - SwapPairs(numbers, i, p); - } - } - } else { - HeapSortPairs(this, numbers, len); - return; - } -} - bool JSObject::WasConstructedFromApiFunction() { auto instance_type = map()->instance_type(); bool is_api_object = instance_type == JS_API_OBJECT_TYPE || @@ -16344,94 +15802,6 @@ bool JSObject::WasConstructedFromApiFunction() { return is_api_object; } -MaybeHandle<String> Object::ObjectProtoToString(Isolate* isolate, - Handle<Object> object) { - if (*object == isolate->heap()->undefined_value()) { - return isolate->factory()->undefined_to_string(); - } - if (*object == isolate->heap()->null_value()) { - return isolate->factory()->null_to_string(); - } - - Handle<JSReceiver> receiver = - Object::ToObject(isolate, object).ToHandleChecked(); - - // For proxies, we must check IsArray() before get(toStringTag) to comply - // with the specification - Maybe<bool> is_array = Nothing<bool>(); - InstanceType instance_type = receiver->map()->instance_type(); - if (instance_type == JS_PROXY_TYPE) { - is_array = Object::IsArray(receiver); - MAYBE_RETURN(is_array, MaybeHandle<String>()); - } - - Handle<String> tag; - Handle<Object> to_string_tag; - ASSIGN_RETURN_ON_EXCEPTION( - isolate, to_string_tag, - JSReceiver::GetProperty(receiver, - isolate->factory()->to_string_tag_symbol()), - String); - if (to_string_tag->IsString()) { - tag = Handle<String>::cast(to_string_tag); - } else { - switch (instance_type) { - case JS_API_OBJECT_TYPE: - case JS_SPECIAL_API_OBJECT_TYPE: - tag = handle(receiver->class_name(), isolate); - break; - case JS_ARGUMENTS_TYPE: - return isolate->factory()->arguments_to_string(); - case JS_ARRAY_TYPE: - return isolate->factory()->array_to_string(); - case JS_BOUND_FUNCTION_TYPE: - case JS_FUNCTION_TYPE: - return isolate->factory()->function_to_string(); - case JS_ERROR_TYPE: - return isolate->factory()->error_to_string(); - case JS_DATE_TYPE: - return isolate->factory()->date_to_string(); - case JS_REGEXP_TYPE: - return isolate->factory()->regexp_to_string(); - case JS_PROXY_TYPE: { - if (is_array.FromJust()) { - return isolate->factory()->array_to_string(); - } - if (receiver->IsCallable()) { - return isolate->factory()->function_to_string(); - } - return isolate->factory()->object_to_string(); - } - case JS_VALUE_TYPE: { - Object* value = JSValue::cast(*receiver)->value(); - if (value->IsString()) { - return isolate->factory()->string_to_string(); - } - if (value->IsNumber()) { - return isolate->factory()->number_to_string(); - } - if (value->IsBoolean()) { - return isolate->factory()->boolean_to_string(); - } - if (value->IsSymbol()) { - return isolate->factory()->object_to_string(); - } - UNREACHABLE(); - tag = handle(receiver->class_name(), isolate); - break; - } - default: - return isolate->factory()->object_to_string(); - } - } - - IncrementalStringBuilder builder(isolate); - builder.AppendCString("[object "); - builder.AppendString(tag); - builder.AppendCharacter(']'); - return builder.Finish(); -} - const char* Symbol::PrivateSymbolToName() const { Heap* heap = GetIsolate()->heap(); #define SYMBOL_CHECK_AND_PRINT(name) \ @@ -16460,12 +15830,22 @@ void Symbol::SymbolShortPrint(std::ostream& os) { // StringSharedKeys are used as keys in the eval cache. class StringSharedKey : public HashTableKey { public: + // This tuple unambiguously identifies calls to eval() or + // CreateDynamicFunction() (such as through the Function() constructor). + // * source is the string passed into eval(). For dynamic functions, this is + // the effective source for the function, some of which is implicitly + // generated. + // * shared is the shared function info for the function containing the call + // to eval(). for dynamic functions, shared is the native context closure. + // * When positive, position is the position in the source where eval is + // called. When negative, position is the negation of the position in the + // dynamic function's effective source where the ')' ends the parameters. StringSharedKey(Handle<String> source, Handle<SharedFunctionInfo> shared, - LanguageMode language_mode, int scope_position) + LanguageMode language_mode, int position) : source_(source), shared_(shared), language_mode_(language_mode), - scope_position_(scope_position) {} + position_(position) {} bool IsMatch(Object* other) override { DisallowHeapAllocation no_allocation; @@ -16481,8 +15861,8 @@ class StringSharedKey : public HashTableKey { DCHECK(is_valid_language_mode(language_unchecked)); LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked); if (language_mode != language_mode_) return false; - int scope_position = Smi::cast(other_array->get(3))->value(); - if (scope_position != scope_position_) return false; + int position = Smi::cast(other_array->get(3))->value(); + if (position != position_) return false; String* source = String::cast(other_array->get(1)); return source->Equals(*source_); } @@ -16490,7 +15870,7 @@ class StringSharedKey : public HashTableKey { static uint32_t StringSharedHashHelper(String* source, SharedFunctionInfo* shared, LanguageMode language_mode, - int scope_position) { + int position) { uint32_t hash = source->Hash(); if (shared->HasSourceCode()) { // Instead of using the SharedFunctionInfo pointer in the hash @@ -16502,14 +15882,14 @@ class StringSharedKey : public HashTableKey { hash ^= String::cast(script->source())->Hash(); STATIC_ASSERT(LANGUAGE_END == 2); if (is_strict(language_mode)) hash ^= 0x8000; - hash += scope_position; + hash += position; } return hash; } uint32_t Hash() override { return StringSharedHashHelper(*source_, *shared_, language_mode_, - scope_position_); + position_); } uint32_t HashForObject(Object* obj) override { @@ -16523,9 +15903,8 @@ class StringSharedKey : public HashTableKey { int language_unchecked = Smi::cast(other_array->get(2))->value(); DCHECK(is_valid_language_mode(language_unchecked)); LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked); - int scope_position = Smi::cast(other_array->get(3))->value(); - return StringSharedHashHelper(source, shared, language_mode, - scope_position); + int position = Smi::cast(other_array->get(3))->value(); + return StringSharedHashHelper(source, shared, language_mode, position); } @@ -16534,7 +15913,7 @@ class StringSharedKey : public HashTableKey { array->set(0, *shared_); array->set(1, *source_); array->set(2, Smi::FromInt(language_mode_)); - array->set(3, Smi::FromInt(scope_position_)); + array->set(3, Smi::FromInt(position_)); return array; } @@ -16542,9 +15921,22 @@ class StringSharedKey : public HashTableKey { Handle<String> source_; Handle<SharedFunctionInfo> shared_; LanguageMode language_mode_; - int scope_position_; + int position_; }; +// static +const char* JSPromise::Status(int status) { + switch (status) { + case v8::Promise::kFulfilled: + return "resolved"; + case v8::Promise::kPending: + return "pending"; + case v8::Promise::kRejected: + return "rejected"; + } + UNREACHABLE(); + return NULL; +} namespace { @@ -16816,6 +16208,17 @@ class InternalizedStringKey : public HashTableKey { DCHECK(string_->IsInternalizedString()); return string_; } + if (FLAG_thin_strings) { + // External strings get special treatment, to avoid copying their + // contents. + if (string_->IsExternalOneByteString()) { + return isolate->factory() + ->InternalizeExternalString<ExternalOneByteString>(string_); + } else if (string_->IsExternalTwoByteString()) { + return isolate->factory() + ->InternalizeExternalString<ExternalTwoByteString>(string_); + } + } // Otherwise allocate a new internalized string. return isolate->factory()->NewInternalizedStringImpl( string_, string_->length(), string_->hash_field()); @@ -16825,6 +16228,7 @@ class InternalizedStringKey : public HashTableKey { return String::cast(obj)->Hash(); } + private: Handle<String> string_; }; @@ -16858,7 +16262,13 @@ Handle<Derived> HashTable<Derived, Shape, Key>::New( if (capacity > HashTable::kMaxCapacity) { v8::internal::Heap::FatalProcessOutOfMemory("invalid table size", true); } + return New(isolate, capacity, pretenure); +} +template <typename Derived, typename Shape, typename Key> +Handle<Derived> HashTable<Derived, Shape, Key>::New(Isolate* isolate, + int capacity, + PretenureFlag pretenure) { Factory* factory = isolate->factory(); int length = EntryToIndex(capacity); Handle<FixedArray> array = factory->NewFixedArray(length, pretenure); @@ -16871,7 +16281,6 @@ Handle<Derived> HashTable<Derived, Shape, Key>::New( return table; } - // Find entry for key otherwise return kNotFound. template <typename Derived, typename Shape> int NameDictionaryBase<Derived, Shape>::FindEntry(Handle<Name> key) { @@ -17145,6 +16554,10 @@ Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, uint32_t>::New( Isolate*, int at_least_space_for, PretenureFlag pretenure, MinimumCapacity capacity_option); +template Handle<SeededNumberDictionary> +Dictionary<SeededNumberDictionary, SeededNumberDictionaryShape, + uint32_t>::NewEmpty(Isolate*, PretenureFlag pretenure); + template Handle<UnseededNumberDictionary> Dictionary<UnseededNumberDictionary, UnseededNumberDictionaryShape, uint32_t>::New(Isolate*, int at_least_space_for, @@ -17155,6 +16568,10 @@ template Handle<NameDictionary> Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::New( Isolate*, int n, PretenureFlag pretenure, MinimumCapacity capacity_option); +template Handle<NameDictionary> +Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::NewEmpty( + Isolate*, PretenureFlag pretenure); + template Handle<GlobalDictionary> Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::New( Isolate*, int n, PretenureFlag pretenure, MinimumCapacity capacity_option); @@ -17220,10 +16637,6 @@ Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::Add( template Handle<FixedArray> Dictionary< NameDictionary, NameDictionaryShape, - Handle<Name> >::BuildIterationIndicesArray(Handle<NameDictionary>); - -template Handle<FixedArray> Dictionary< - NameDictionary, NameDictionaryShape, Handle<Name> >::GenerateNewEnumerationIndices(Handle<NameDictionary>); template Handle<SeededNumberDictionary> @@ -17278,6 +16691,12 @@ Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CopyEnumKeysTo( Handle<FixedArray> storage, KeyCollectionMode mode, KeyAccumulator* accumulator); +template Handle<FixedArray> +Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: + IterationIndices( + Handle< + Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>> + dictionary); template void Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: CollectKeysTo(Handle<Dictionary<GlobalDictionary, GlobalDictionaryShape, @@ -17285,6 +16704,10 @@ Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: dictionary, KeyAccumulator* keys); +template Handle<FixedArray> +Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::IterationIndices( + Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>> + dictionary); template void Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CollectKeysTo( Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>> @@ -17321,7 +16744,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( HandleScope scope(isolate); Handle<Object> value(dict->ValueAt(i), isolate); PropertyDetails details = dict->DetailsAt(i); - if (details.type() == ACCESSOR_CONSTANT || details.IsReadOnly()) { + if (details.kind() == kAccessor || details.IsReadOnly()) { // Bail out and do the sorting of undefineds and array holes in JS. // Also bail out if the element is not supposed to be moved. return bailout; @@ -17337,7 +16760,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( return bailout; } else { Handle<Object> result = SeededNumberDictionary::AddNumberEntry( - new_dict, pos, value, details, object->map()->is_prototype_map()); + new_dict, pos, value, details, object); DCHECK(result.is_identical_to(new_dict)); USE(result); pos++; @@ -17348,7 +16771,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( return bailout; } else { Handle<Object> result = SeededNumberDictionary::AddNumberEntry( - new_dict, key, value, details, object->map()->is_prototype_map()); + new_dict, key, value, details, object); DCHECK(result.is_identical_to(new_dict)); USE(result); } @@ -17365,7 +16788,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( HandleScope scope(isolate); Handle<Object> result = SeededNumberDictionary::AddNumberEntry( new_dict, pos, isolate->factory()->undefined_value(), no_details, - object->map()->is_prototype_map()); + object); DCHECK(result.is_identical_to(new_dict)); USE(result); pos++; @@ -17386,7 +16809,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort( Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, uint32_t limit) { Isolate* isolate = object->GetIsolate(); - if (object->HasSloppyArgumentsElements()) { + if (object->HasSloppyArgumentsElements() || !object->map()->is_extensible()) { return handle(Smi::FromInt(-1), isolate); } @@ -17503,11 +16926,11 @@ Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, } result = undefs; while (undefs < holes) { - elements->set_undefined(undefs); + elements->set_undefined(isolate, undefs); undefs++; } while (holes < limit) { - elements->set_the_hole(holes); + elements->set_the_hole(isolate, holes); holes++; } } @@ -17515,6 +16938,98 @@ Handle<Object> JSObject::PrepareElementsForSort(Handle<JSObject> object, return isolate->factory()->NewNumberFromUint(result); } +namespace { + +bool CanonicalNumericIndexString(Isolate* isolate, Handle<Object> s, + Handle<Object>* index) { + DCHECK(s->IsString() || s->IsSmi()); + + Handle<Object> result; + if (s->IsSmi()) { + result = s; + } else { + result = String::ToNumber(Handle<String>::cast(s)); + if (!result->IsMinusZero()) { + Handle<String> str = Object::ToString(isolate, result).ToHandleChecked(); + // Avoid treating strings like "2E1" and "20" as the same key. + if (!str->SameValue(*s)) return false; + } + } + *index = result; + return true; +} + +} // anonymous namespace + +// ES#sec-integer-indexed-exotic-objects-defineownproperty-p-desc +// static +Maybe<bool> JSTypedArray::DefineOwnProperty(Isolate* isolate, + Handle<JSTypedArray> o, + Handle<Object> key, + PropertyDescriptor* desc, + ShouldThrow should_throw) { + // 1. Assert: IsPropertyKey(P) is true. + DCHECK(key->IsName() || key->IsNumber()); + // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. + // 3. If Type(P) is String, then + if (key->IsString() || key->IsSmi()) { + // 3a. Let numericIndex be ! CanonicalNumericIndexString(P) + // 3b. If numericIndex is not undefined, then + Handle<Object> numeric_index; + if (CanonicalNumericIndexString(isolate, key, &numeric_index)) { + // 3b i. If IsInteger(numericIndex) is false, return false. + // 3b ii. If numericIndex = -0, return false. + // 3b iii. If numericIndex < 0, return false. + // FIXME: the standard allows up to 2^53 elements. + uint32_t index; + if (numeric_index->IsMinusZero() || !numeric_index->ToUint32(&index)) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kInvalidTypedArrayIndex)); + } + // 3b iv. Let length be O.[[ArrayLength]]. + uint32_t length = o->length()->Number(); + // 3b v. If numericIndex ≥ length, return false. + if (index >= length) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kInvalidTypedArrayIndex)); + } + // 3b vi. If IsAccessorDescriptor(Desc) is true, return false. + if (PropertyDescriptor::IsAccessorDescriptor(desc)) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, key)); + } + // 3b vii. If Desc has a [[Configurable]] field and if + // Desc.[[Configurable]] is true, return false. + // 3b viii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] + // is false, return false. + // 3b ix. If Desc has a [[Writable]] field and if Desc.[[Writable]] is + // false, return false. + if ((desc->has_configurable() && desc->configurable()) || + (desc->has_enumerable() && !desc->enumerable()) || + (desc->has_writable() && !desc->writable())) { + RETURN_FAILURE(isolate, should_throw, + NewTypeError(MessageTemplate::kRedefineDisallowed, key)); + } + // 3b x. If Desc has a [[Value]] field, then + // 3b x 1. Let value be Desc.[[Value]]. + // 3b x 2. Return ? IntegerIndexedElementSet(O, numericIndex, value). + if (desc->has_value()) { + if (!desc->has_configurable()) desc->set_configurable(false); + if (!desc->has_enumerable()) desc->set_enumerable(true); + if (!desc->has_writable()) desc->set_writable(true); + Handle<Object> value = desc->value(); + RETURN_ON_EXCEPTION_VALUE(isolate, + SetOwnElementIgnoreAttributes( + o, index, value, desc->ToAttributes()), + Nothing<bool>()); + } + // 3b xi. Return true. + return Just(true); + } + } + // 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc). + return OrdinaryDefineOwnProperty(isolate, o, key, desc, should_throw); +} ExternalArrayType JSTypedArray::type() { switch (elements()->map()->instance_type()) { @@ -17577,12 +17092,12 @@ Handle<PropertyCell> JSGlobalObject::EnsureEmptyPropertyCell( if (original_cell_type == PropertyCellType::kInvalidated) { cell = PropertyCell::InvalidateEntry(dictionary, entry); } - PropertyDetails details(NONE, DATA, 0, cell_type); + PropertyDetails details(kData, NONE, 0, cell_type); cell->set_property_details(details); return cell; } cell = isolate->factory()->NewPropertyCell(); - PropertyDetails details(NONE, DATA, 0, cell_type); + PropertyDetails details(kData, NONE, 0, cell_type); dictionary = GlobalDictionary::Add(dictionary, name, cell, details, entry_out); // {*entry_out} is initialized inside GlobalDictionary::Add(). @@ -17661,6 +17176,9 @@ MaybeHandle<String> StringTable::InternalizeStringIfExists( if (string->IsInternalizedString()) { return string; } + if (string->IsThinString()) { + return handle(Handle<ThinString>::cast(string)->actual(), isolate); + } return LookupStringIfExists(isolate, string); } @@ -17707,31 +17225,98 @@ void StringTable::EnsureCapacityForDeserialization(Isolate* isolate, isolate->heap()->SetRootStringTable(*table); } +namespace { + +template <class StringClass> +void MigrateExternalStringResource(Isolate* isolate, Handle<String> from, + Handle<String> to) { + Handle<StringClass> cast_from = Handle<StringClass>::cast(from); + Handle<StringClass> cast_to = Handle<StringClass>::cast(to); + const typename StringClass::Resource* to_resource = cast_to->resource(); + if (to_resource == nullptr) { + // |to| is a just-created internalized copy of |from|. Migrate the resource. + cast_to->set_resource(cast_from->resource()); + // Zap |from|'s resource pointer to reflect the fact that |from| has + // relinquished ownership of its resource. + cast_from->set_resource(nullptr); + } else if (to_resource != cast_from->resource()) { + // |to| already existed and has its own resource. Finalize |from|. + isolate->heap()->FinalizeExternalString(*from); + } +} + +} // namespace Handle<String> StringTable::LookupString(Isolate* isolate, Handle<String> string) { + if (string->IsThinString()) { + DCHECK(Handle<ThinString>::cast(string)->actual()->IsInternalizedString()); + return handle(Handle<ThinString>::cast(string)->actual(), isolate); + } if (string->IsConsString() && string->IsFlat()) { - string = String::Flatten(string); + string = handle(Handle<ConsString>::cast(string)->first(), isolate); if (string->IsInternalizedString()) return string; } InternalizedStringKey key(string); Handle<String> result = LookupKey(isolate, &key); - if (string->IsConsString()) { - Handle<ConsString> cons = Handle<ConsString>::cast(string); - cons->set_first(*result); - cons->set_second(isolate->heap()->empty_string()); - } else if (string->IsSlicedString()) { - STATIC_ASSERT(ConsString::kSize == SlicedString::kSize); - DisallowHeapAllocation no_gc; - bool one_byte = result->IsOneByteRepresentation(); - Handle<Map> map = one_byte ? isolate->factory()->cons_one_byte_string_map() - : isolate->factory()->cons_string_map(); - string->set_map(*map); - Handle<ConsString> cons = Handle<ConsString>::cast(string); - cons->set_first(*result); - cons->set_second(isolate->heap()->empty_string()); + if (FLAG_thin_strings) { + if (string->IsExternalString()) { + if (result->IsExternalOneByteString()) { + MigrateExternalStringResource<ExternalOneByteString>(isolate, string, + result); + } else if (result->IsExternalTwoByteString()) { + MigrateExternalStringResource<ExternalTwoByteString>(isolate, string, + result); + } else { + // If the external string is duped into an existing non-external + // internalized string, free its resource (it's about to be rewritten + // into a ThinString below). + isolate->heap()->FinalizeExternalString(*string); + } + } + + // The LookupKey() call above tries to internalize the string in-place. + // In cases where that wasn't possible (e.g. new-space strings), turn them + // into ThinStrings referring to their internalized versions now. + if (!string->IsInternalizedString()) { + DisallowHeapAllocation no_gc; + bool one_byte = result->IsOneByteRepresentation(); + Handle<Map> map = one_byte + ? isolate->factory()->thin_one_byte_string_map() + : isolate->factory()->thin_string_map(); + int old_size = string->Size(); + DCHECK(old_size >= ThinString::kSize); + string->synchronized_set_map(*map); + Handle<ThinString> thin = Handle<ThinString>::cast(string); + thin->set_actual(*result); + Address thin_end = thin->address() + ThinString::kSize; + int size_delta = old_size - ThinString::kSize; + if (size_delta != 0) { + Heap* heap = isolate->heap(); + heap->CreateFillerObjectAt(thin_end, size_delta, + ClearRecordedSlots::kNo); + heap->AdjustLiveBytes(*thin, -size_delta); + } + } + } else { // !FLAG_thin_strings + if (string->IsConsString()) { + Handle<ConsString> cons = Handle<ConsString>::cast(string); + cons->set_first(*result); + cons->set_second(isolate->heap()->empty_string()); + } else if (string->IsSlicedString()) { + STATIC_ASSERT(ConsString::kSize == SlicedString::kSize); + DisallowHeapAllocation no_gc; + bool one_byte = result->IsOneByteRepresentation(); + Handle<Map> map = one_byte + ? isolate->factory()->cons_one_byte_string_map() + : isolate->factory()->cons_string_map(); + string->set_map(*map); + Handle<ConsString> cons = Handle<ConsString>::cast(string); + cons->set_first(*result); + cons->set_second(isolate->heap()->empty_string()); + } } return result; } @@ -17819,21 +17404,153 @@ Handle<Object> CompilationCacheTable::Lookup(Handle<String> src, return Handle<Object>(get(index + 1), isolate); } +namespace { -Handle<Object> CompilationCacheTable::LookupEval( - Handle<String> src, Handle<SharedFunctionInfo> outer_info, - LanguageMode language_mode, int scope_position) { - Isolate* isolate = GetIsolate(); - // Cache key is the tuple (source, outer shared function info, scope position) - // to unambiguously identify the context chain the cached eval code assumes. - StringSharedKey key(src, outer_info, language_mode, scope_position); +const int kLiteralEntryLength = 2; +const int kLiteralInitialLength = 2; +const int kLiteralContextOffset = 0; +const int kLiteralLiteralsOffset = 1; + +int SearchLiteralsMapEntry(CompilationCacheTable* cache, int cache_entry, + Context* native_context) { + DisallowHeapAllocation no_gc; + DCHECK(native_context->IsNativeContext()); + Object* obj = cache->get(cache_entry); + + if (obj->IsFixedArray()) { + FixedArray* literals_map = FixedArray::cast(obj); + int length = literals_map->length(); + for (int i = 0; i < length; i += kLiteralEntryLength) { + if (WeakCell::cast(literals_map->get(i + kLiteralContextOffset)) + ->value() == native_context) { + return i; + } + } + } + return -1; +} + +void AddToLiteralsMap(Handle<CompilationCacheTable> cache, int cache_entry, + Handle<Context> native_context, Handle<Cell> literals) { + Isolate* isolate = native_context->GetIsolate(); + DCHECK(native_context->IsNativeContext()); + STATIC_ASSERT(kLiteralEntryLength == 2); + Handle<FixedArray> new_literals_map; + int entry; + + Object* obj = cache->get(cache_entry); + + if (!obj->IsFixedArray() || FixedArray::cast(obj)->length() == 0) { + new_literals_map = + isolate->factory()->NewFixedArray(kLiteralInitialLength, TENURED); + entry = 0; + } else { + Handle<FixedArray> old_literals_map(FixedArray::cast(obj), isolate); + entry = SearchLiteralsMapEntry(*cache, cache_entry, *native_context); + if (entry >= 0) { + // Just set the code of the entry. + Handle<WeakCell> literals_cell = + isolate->factory()->NewWeakCell(literals); + old_literals_map->set(entry + kLiteralLiteralsOffset, *literals_cell); + return; + } + + // Can we reuse an entry? + DCHECK(entry < 0); + int length = old_literals_map->length(); + for (int i = 0; i < length; i += kLiteralEntryLength) { + if (WeakCell::cast(old_literals_map->get(i + kLiteralContextOffset)) + ->cleared()) { + new_literals_map = old_literals_map; + entry = i; + break; + } + } + + if (entry < 0) { + // Copy old optimized code map and append one new entry. + new_literals_map = isolate->factory()->CopyFixedArrayAndGrow( + old_literals_map, kLiteralEntryLength, TENURED); + entry = old_literals_map->length(); + } + } + + Handle<WeakCell> literals_cell = isolate->factory()->NewWeakCell(literals); + WeakCell* context_cell = native_context->self_weak_cell(); + + new_literals_map->set(entry + kLiteralContextOffset, context_cell); + new_literals_map->set(entry + kLiteralLiteralsOffset, *literals_cell); + +#ifdef DEBUG + for (int i = 0; i < new_literals_map->length(); i += kLiteralEntryLength) { + WeakCell* cell = + WeakCell::cast(new_literals_map->get(i + kLiteralContextOffset)); + DCHECK(cell->cleared() || cell->value()->IsNativeContext()); + cell = WeakCell::cast(new_literals_map->get(i + kLiteralLiteralsOffset)); + DCHECK(cell->cleared() || (cell->value()->IsCell())); + } +#endif + + Object* old_literals_map = cache->get(cache_entry); + if (old_literals_map != *new_literals_map) { + cache->set(cache_entry, *new_literals_map); + } +} + +Cell* SearchLiteralsMap(CompilationCacheTable* cache, int cache_entry, + Context* native_context) { + Cell* result = nullptr; + int entry = SearchLiteralsMapEntry(cache, cache_entry, native_context); + if (entry >= 0) { + FixedArray* literals_map = FixedArray::cast(cache->get(cache_entry)); + DCHECK_LE(entry + kLiteralEntryLength, literals_map->length()); + WeakCell* cell = + WeakCell::cast(literals_map->get(entry + kLiteralLiteralsOffset)); + + result = cell->cleared() ? nullptr : Cell::cast(cell->value()); + } + DCHECK(result == nullptr || result->IsCell()); + return result; +} + +} // namespace + +InfoVectorPair CompilationCacheTable::LookupScript(Handle<String> src, + Handle<Context> context, + LanguageMode language_mode) { + InfoVectorPair empty_result; + Handle<SharedFunctionInfo> shared(context->closure()->shared()); + StringSharedKey key(src, shared, language_mode, kNoSourcePosition); int entry = FindEntry(&key); - if (entry == kNotFound) return isolate->factory()->undefined_value(); + if (entry == kNotFound) return empty_result; int index = EntryToIndex(entry); - if (!get(index)->IsFixedArray()) return isolate->factory()->undefined_value(); - return Handle<Object>(get(EntryToIndex(entry) + 1), isolate); + if (!get(index)->IsFixedArray()) return empty_result; + Object* obj = get(index + 1); + if (obj->IsSharedFunctionInfo()) { + Cell* literals = + SearchLiteralsMap(this, index + 2, context->native_context()); + return InfoVectorPair(SharedFunctionInfo::cast(obj), literals); + } + return empty_result; } +InfoVectorPair CompilationCacheTable::LookupEval( + Handle<String> src, Handle<SharedFunctionInfo> outer_info, + Handle<Context> native_context, LanguageMode language_mode, int position) { + InfoVectorPair empty_result; + StringSharedKey key(src, outer_info, language_mode, position); + int entry = FindEntry(&key); + if (entry == kNotFound) return empty_result; + int index = EntryToIndex(entry); + if (!get(index)->IsFixedArray()) return empty_result; + Object* obj = get(EntryToIndex(entry) + 1); + if (obj->IsSharedFunctionInfo()) { + Cell* literals = + SearchLiteralsMap(this, EntryToIndex(entry) + 2, *native_context); + return InfoVectorPair(SharedFunctionInfo::cast(obj), literals); + } + return empty_result; +} Handle<Object> CompilationCacheTable::LookupRegExp(Handle<String> src, JSRegExp::Flags flags) { @@ -17861,20 +17578,41 @@ Handle<CompilationCacheTable> CompilationCacheTable::Put( return cache; } +Handle<CompilationCacheTable> CompilationCacheTable::PutScript( + Handle<CompilationCacheTable> cache, Handle<String> src, + Handle<Context> context, LanguageMode language_mode, + Handle<SharedFunctionInfo> value, Handle<Cell> literals) { + Isolate* isolate = cache->GetIsolate(); + Handle<SharedFunctionInfo> shared(context->closure()->shared()); + Handle<Context> native_context(context->native_context()); + StringSharedKey key(src, shared, language_mode, kNoSourcePosition); + Handle<Object> k = key.AsHandle(isolate); + cache = EnsureCapacity(cache, 1, &key); + int entry = cache->FindInsertionEntry(key.Hash()); + cache->set(EntryToIndex(entry), *k); + cache->set(EntryToIndex(entry) + 1, *value); + AddToLiteralsMap(cache, EntryToIndex(entry) + 2, native_context, literals); + cache->ElementAdded(); + return cache; +} Handle<CompilationCacheTable> CompilationCacheTable::PutEval( Handle<CompilationCacheTable> cache, Handle<String> src, Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> value, - int scope_position) { + Handle<Context> native_context, Handle<Cell> literals, int position) { Isolate* isolate = cache->GetIsolate(); - StringSharedKey key(src, outer_info, value->language_mode(), scope_position); + StringSharedKey key(src, outer_info, value->language_mode(), position); { Handle<Object> k = key.AsHandle(isolate); - DisallowHeapAllocation no_allocation_scope; int entry = cache->FindEntry(&key); if (entry != kNotFound) { cache->set(EntryToIndex(entry), *k); cache->set(EntryToIndex(entry) + 1, *value); + // AddToLiteralsMap may allocate a new sub-array to live in the entry, + // but it won't change the cache array. Therefore EntryToIndex and + // entry remains correct. + AddToLiteralsMap(cache, EntryToIndex(entry) + 2, native_context, + literals); return cache; } } @@ -17924,9 +17662,14 @@ void CompilationCacheTable::Age() { } } else if (get(entry_index)->IsFixedArray()) { SharedFunctionInfo* info = SharedFunctionInfo::cast(get(value_index)); - if (info->code()->kind() != Code::FUNCTION || info->code()->IsOld()) { - NoWriteBarrierSet(this, entry_index, the_hole_value); - NoWriteBarrierSet(this, value_index, the_hole_value); + bool is_old = + info->IsInterpreted() + ? info->bytecode_array()->IsOld() + : info->code()->kind() != Code::FUNCTION || info->code()->IsOld(); + if (is_old) { + for (int i = 0; i < kEntrySize; i++) { + NoWriteBarrierSet(this, entry_index + i, the_hole_value); + } ElementRemoved(); } } @@ -17941,8 +17684,9 @@ void CompilationCacheTable::Remove(Object* value) { int entry_index = EntryToIndex(entry); int value_index = entry_index + 1; if (get(value_index) == value) { - NoWriteBarrierSet(this, entry_index, the_hole_value); - NoWriteBarrierSet(this, value_index, the_hole_value); + for (int i = 0; i < kEntrySize; i++) { + NoWriteBarrierSet(this, entry_index + i, the_hole_value); + } ElementRemoved(); } } @@ -17962,44 +17706,24 @@ Handle<Derived> Dictionary<Derived, Shape, Key>::New( return dict; } - template <typename Derived, typename Shape, typename Key> -Handle<FixedArray> Dictionary<Derived, Shape, Key>::BuildIterationIndicesArray( - Handle<Derived> dictionary) { - Isolate* isolate = dictionary->GetIsolate(); - Factory* factory = isolate->factory(); - int length = dictionary->NumberOfElements(); - - Handle<FixedArray> iteration_order = factory->NewFixedArray(length); - Handle<FixedArray> enumeration_order = factory->NewFixedArray(length); - - // Fill both the iteration order array and the enumeration order array - // with property details. - int capacity = dictionary->Capacity(); - int pos = 0; - for (int i = 0; i < capacity; i++) { - if (dictionary->IsKey(isolate, dictionary->KeyAt(i))) { - int index = dictionary->DetailsAt(i).dictionary_index(); - iteration_order->set(pos, Smi::FromInt(i)); - enumeration_order->set(pos, Smi::FromInt(index)); - pos++; - } - } - DCHECK(pos == length); - - // Sort the arrays wrt. enumeration order. - iteration_order->SortPairs(*enumeration_order, enumeration_order->length()); - return iteration_order; +Handle<Derived> Dictionary<Derived, Shape, Key>::NewEmpty( + Isolate* isolate, PretenureFlag pretenure) { + Handle<Derived> dict = DerivedHashTable::New(isolate, 1, pretenure); + // Attempt to add one element to the empty dictionary must cause reallocation. + DCHECK(!dict->HasSufficientCapacityToAdd(1)); + // Initialize the next enumeration index. + dict->SetNextEnumerationIndex(PropertyDetails::kInitialIndex); + return dict; } - template <typename Derived, typename Shape, typename Key> Handle<FixedArray> Dictionary<Derived, Shape, Key>::GenerateNewEnumerationIndices( Handle<Derived> dictionary) { int length = dictionary->NumberOfElements(); - Handle<FixedArray> iteration_order = BuildIterationIndicesArray(dictionary); + Handle<FixedArray> iteration_order = IterationIndices(dictionary); DCHECK(iteration_order->length() == length); // Iterate over the dictionary using the enumeration order and update @@ -18130,15 +17854,15 @@ bool SeededNumberDictionary::HasComplexElements() { if (!this->IsKey(isolate, k)) continue; DCHECK(!IsDeleted(i)); PropertyDetails details = this->DetailsAt(i); - if (details.type() == ACCESSOR_CONSTANT) return true; + if (details.kind() == kAccessor) return true; PropertyAttributes attr = details.attributes(); if (attr & ALL_ATTRIBUTES_MASK) return true; } return false; } -void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key, - bool used_as_prototype) { +void SeededNumberDictionary::UpdateMaxNumberKey( + uint32_t key, Handle<JSObject> dictionary_holder) { DisallowHeapAllocation no_allocation; // If the dictionary requires slow elements an element has already // been added at a high index. @@ -18146,9 +17870,8 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key, // Check if this index is high enough that we should require slow // elements. if (key > kRequiresSlowElementsLimit) { - if (used_as_prototype) { - // TODO(verwaest): Remove this hack. - TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate()); + if (!dictionary_holder.is_null()) { + dictionary_holder->RequireSlowElements(this); } set_requires_slow_elements(); return; @@ -18161,11 +17884,11 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key, } } - Handle<SeededNumberDictionary> SeededNumberDictionary::AddNumberEntry( Handle<SeededNumberDictionary> dictionary, uint32_t key, - Handle<Object> value, PropertyDetails details, bool used_as_prototype) { - dictionary->UpdateMaxNumberKey(key, used_as_prototype); + Handle<Object> value, PropertyDetails details, + Handle<JSObject> dictionary_holder) { + dictionary->UpdateMaxNumberKey(key, dictionary_holder); SLOW_DCHECK(dictionary->FindEntry(key) == kNotFound); return Add(dictionary, key, value, details); } @@ -18193,8 +17916,8 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::DeleteKey( Handle<SeededNumberDictionary> SeededNumberDictionary::AtNumberPut( Handle<SeededNumberDictionary> dictionary, uint32_t key, - Handle<Object> value, bool used_as_prototype) { - dictionary->UpdateMaxNumberKey(key, used_as_prototype); + Handle<Object> value, Handle<JSObject> dictionary_holder) { + dictionary->UpdateMaxNumberKey(key, dictionary_holder); return AtPut(dictionary, key, value); } @@ -18206,13 +17929,13 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::AtNumberPut( return AtPut(dictionary, key, value); } - Handle<SeededNumberDictionary> SeededNumberDictionary::Set( Handle<SeededNumberDictionary> dictionary, uint32_t key, - Handle<Object> value, PropertyDetails details, bool used_as_prototype) { + Handle<Object> value, PropertyDetails details, + Handle<JSObject> dictionary_holder) { int entry = dictionary->FindEntry(key); if (entry == kNotFound) { - return AddNumberEntry(dictionary, key, value, details, used_as_prototype); + return AddNumberEntry(dictionary, key, value, details, dictionary_holder); } // Preserve enumeration index. details = details.set_index(dictionary->DetailsAt(entry).dictionary_index()); @@ -18271,6 +17994,7 @@ void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo( Handle<Dictionary<Derived, Shape, Key>> dictionary, Handle<FixedArray> storage, KeyCollectionMode mode, KeyAccumulator* accumulator) { + DCHECK_IMPLIES(mode != KeyCollectionMode::kOwnOnly, accumulator != nullptr); Isolate* isolate = dictionary->GetIsolate(); int length = storage->length(); int capacity = dictionary->Capacity(); @@ -18296,7 +18020,7 @@ void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo( storage->set(properties, Smi::FromInt(i)); } properties++; - if (properties == length) break; + if (mode == KeyCollectionMode::kOwnOnly && properties == length) break; } CHECK_EQ(length, properties); @@ -18313,6 +18037,34 @@ void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo( } template <typename Derived, typename Shape, typename Key> +Handle<FixedArray> Dictionary<Derived, Shape, Key>::IterationIndices( + Handle<Dictionary<Derived, Shape, Key>> dictionary) { + Isolate* isolate = dictionary->GetIsolate(); + int capacity = dictionary->Capacity(); + int length = dictionary->NumberOfElements(); + Handle<FixedArray> array = isolate->factory()->NewFixedArray(length); + int array_size = 0; + { + DisallowHeapAllocation no_gc; + Dictionary<Derived, Shape, Key>* raw_dict = *dictionary; + for (int i = 0; i < capacity; i++) { + Object* k = raw_dict->KeyAt(i); + if (!raw_dict->IsKey(isolate, k)) continue; + if (raw_dict->IsDeleted(i)) continue; + array->set(array_size++, Smi::FromInt(i)); + } + + DCHECK_EQ(array_size, length); + + EnumIndexComparator<Derived> cmp(static_cast<Derived*>(raw_dict)); + Smi** start = reinterpret_cast<Smi**>(array->GetFirstElementAddress()); + std::sort(start, start + array_size, cmp); + } + array->Shrink(array_size); + return array; +} + +template <typename Derived, typename Shape, typename Key> void Dictionary<Derived, Shape, Key>::CollectKeysTo( Handle<Dictionary<Derived, Shape, Key>> dictionary, KeyAccumulator* keys) { Isolate* isolate = keys->isolate(); @@ -18966,6 +18718,40 @@ bool JSWeakCollection::Delete(Handle<JSWeakCollection> weak_collection, return was_present; } +Handle<JSArray> JSWeakCollection::GetEntries(Handle<JSWeakCollection> holder, + int max_entries) { + Isolate* isolate = holder->GetIsolate(); + Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table())); + if (max_entries == 0 || max_entries > table->NumberOfElements()) { + max_entries = table->NumberOfElements(); + } + int values_per_entry = holder->IsJSWeakMap() ? 2 : 1; + Handle<FixedArray> entries = + isolate->factory()->NewFixedArray(max_entries * values_per_entry); + // Recompute max_values because GC could have removed elements from the table. + if (max_entries > table->NumberOfElements()) { + max_entries = table->NumberOfElements(); + } + + { + DisallowHeapAllocation no_gc; + int count = 0; + for (int i = 0; + count / values_per_entry < max_entries && i < table->Capacity(); i++) { + Handle<Object> key(table->KeyAt(i), isolate); + if (table->IsKey(isolate, *key)) { + entries->set(count++, *key); + if (values_per_entry > 1) { + Object* value = table->Lookup(key); + entries->set(count++, value); + } + } + } + DCHECK_EQ(max_entries * values_per_entry, count); + } + return isolate->factory()->NewJSArrayWithElements(entries); +} + // Check if there is a break point at this source position. bool DebugInfo::HasBreakPoint(int source_position) { // Get the break point info object for this code offset. @@ -19052,11 +18838,8 @@ void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, int source_position, DCHECK(index != kNoBreakPointInfo); // Allocate new BreakPointInfo object and set the break point. - Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast( - isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE)); - new_break_point_info->set_source_position(source_position); - new_break_point_info->set_break_point_objects( - isolate->heap()->undefined_value()); + Handle<BreakPointInfo> new_break_point_info = + isolate->factory()->NewBreakPointInfo(source_position); BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object); debug_info->break_points()->set(index, *new_break_point_info); } @@ -19366,27 +19149,6 @@ void JSDate::SetValue(Object* value, bool is_value_nan) { } -// static -MaybeHandle<Object> JSDate::ToPrimitive(Handle<JSReceiver> receiver, - Handle<Object> hint) { - Isolate* const isolate = receiver->GetIsolate(); - if (hint->IsString()) { - Handle<String> hint_string = Handle<String>::cast(hint); - if (hint_string->Equals(isolate->heap()->number_string())) { - return JSReceiver::OrdinaryToPrimitive(receiver, - OrdinaryToPrimitiveHint::kNumber); - } - if (hint_string->Equals(isolate->heap()->default_string()) || - hint_string->Equals(isolate->heap()->string_string())) { - return JSReceiver::OrdinaryToPrimitive(receiver, - OrdinaryToPrimitiveHint::kString); - } - } - THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kInvalidHint, hint), - Object); -} - - void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) { int days = DateCache::DaysFromTime(local_time_ms); int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days); @@ -19472,6 +19234,11 @@ void JSArrayBuffer::Neuter() { set_backing_store(NULL); set_byte_length(Smi::kZero); set_was_neutered(true); + // Invalidate the neutering protector. + Isolate* const isolate = GetIsolate(); + if (isolate->IsArrayBufferNeuteringIntact()) { + isolate->InvalidateArrayBufferNeuteringProtector(); + } } @@ -19719,24 +19486,14 @@ void PropertyCell::SetValueWithInvalidation(Handle<PropertyCell> cell, int JSGeneratorObject::source_position() const { CHECK(is_suspended()); - AbstractCode* code; - int code_offset; - if (function()->shared()->HasBytecodeArray()) { - // New-style generators. - DCHECK(!function()->shared()->HasBaselineCode()); - code_offset = Smi::cast(input_or_debug_pos())->value(); - // The stored bytecode offset is relative to a different base than what - // is used in the source position table, hence the subtraction. - code_offset -= BytecodeArray::kHeaderSize - kHeapObjectTag; - code = AbstractCode::cast(function()->shared()->bytecode_array()); - } else { - // Old-style generators. - DCHECK(function()->shared()->HasBaselineCode()); - code_offset = continuation(); - CHECK(0 <= code_offset); - CHECK(code_offset < function()->code()->instruction_size()); - code = AbstractCode::cast(function()->shared()->code()); - } + DCHECK(function()->shared()->HasBytecodeArray()); + DCHECK(!function()->shared()->HasBaselineCode()); + int code_offset = Smi::cast(input_or_debug_pos())->value(); + // The stored bytecode offset is relative to a different base than what + // is used in the source position table, hence the subtraction. + code_offset -= BytecodeArray::kHeaderSize - kHeapObjectTag; + AbstractCode* code = + AbstractCode::cast(function()->shared()->bytecode_array()); return code->SourcePosition(code_offset); } |