| Index: src/compiler/property-access-info.cc
|
| diff --git a/src/compiler/property-access-info.cc b/src/compiler/property-access-info.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..45c0784f6e9dc3b7cef8911890063d9f6adfcedd
|
| --- /dev/null
|
| +++ b/src/compiler/property-access-info.cc
|
| @@ -0,0 +1,313 @@
|
| +// Copyright 2015 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <ostream>
|
| +
|
| +#include "src/accessors.h"
|
| +#include "src/compilation-dependencies.h"
|
| +#include "src/compiler/property-access-info.h"
|
| +#include "src/field-index-inl.h"
|
| +#include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker!
|
| +#include "src/type-cache.h"
|
| +#include "src/types-inl.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace compiler {
|
| +
|
| +std::ostream& operator<<(std::ostream& os, PropertyAccessMode access_mode) {
|
| + switch (access_mode) {
|
| + case PropertyAccessMode::kLoad:
|
| + return os << "Load";
|
| + case PropertyAccessMode::kStore:
|
| + return os << "Store";
|
| + }
|
| + UNREACHABLE();
|
| + return os;
|
| +}
|
| +
|
| +
|
| +PropertyAccessInfo::PropertyAccessInfo()
|
| + : kind_(kInvalid), receiver_type_(Type::None()), field_type_(Type::Any()) {}
|
| +
|
| +
|
| +PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder,
|
| + Handle<Object> constant,
|
| + Type* receiver_type)
|
| + : kind_(kDataConstant),
|
| + receiver_type_(receiver_type),
|
| + constant_(constant),
|
| + holder_(holder),
|
| + field_type_(Type::Any()) {}
|
| +
|
| +
|
| +PropertyAccessInfo::PropertyAccessInfo(MaybeHandle<JSObject> holder,
|
| + MaybeHandle<Map> transition_map,
|
| + FieldIndex field_index, Type* field_type,
|
| + Type* receiver_type)
|
| + : kind_(kDataField),
|
| + receiver_type_(receiver_type),
|
| + transition_map_(transition_map),
|
| + holder_(holder),
|
| + field_index_(field_index),
|
| + field_type_(field_type) {}
|
| +
|
| +
|
| +PropertyAccessInfoFactory::PropertyAccessInfoFactory(
|
| + CompilationDependencies* dependencies, Handle<Context> native_context,
|
| + Zone* zone)
|
| + : dependencies_(dependencies),
|
| + native_context_(native_context),
|
| + isolate_(native_context->GetIsolate()),
|
| + type_cache_(TypeCache::Get()),
|
| + zone_(zone) {}
|
| +
|
| +
|
| +namespace {
|
| +
|
| +bool CanInlinePropertyAccess(Handle<Map> map) {
|
| + // TODO(bmeurer): Do something about the number stuff.
|
| + if (map->instance_type() == HEAP_NUMBER_TYPE) return false;
|
| + if (map->instance_type() < FIRST_NONSTRING_TYPE) return true;
|
| + return map->IsJSObjectMap() && !map->is_dictionary_map() &&
|
| + !map->has_named_interceptor() &&
|
| + // TODO(verwaest): Whitelist contexts to which we have access.
|
| + !map->is_access_check_needed();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
|
| + Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode,
|
| + PropertyAccessInfo* access_info) {
|
| + // Check if it is safe to inline property access for the {map}.
|
| + if (!CanInlinePropertyAccess(map)) return false;
|
| +
|
| + // Compute the receiver type.
|
| + Handle<Map> receiver_map = map;
|
| + Type* receiver_type = Type::Class(receiver_map, zone());
|
| +
|
| + // We support fast inline cases for certain JSObject getters.
|
| + if (access_mode == PropertyAccessMode::kLoad) {
|
| + // Check for special JSObject field accessors.
|
| + int offset;
|
| + if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) {
|
| + FieldIndex field_index = FieldIndex::ForInObjectOffset(offset);
|
| + Type* field_type = Type::Tagged();
|
| + if (map->IsStringMap()) {
|
| + DCHECK(Name::Equals(factory()->length_string(), name));
|
| + // The String::length property is always a smi in the range
|
| + // [0, String::kMaxLength].
|
| + field_type = type_cache_.kStringLengthType;
|
| + } else if (map->IsJSArrayMap()) {
|
| + DCHECK(Name::Equals(factory()->length_string(), name));
|
| + // The JSArray::length property is a smi in the range
|
| + // [0, FixedDoubleArray::kMaxLength] in case of fast double
|
| + // elements, a smi in the range [0, FixedArray::kMaxLength]
|
| + // in case of other fast elements, and [0, kMaxUInt32] in
|
| + // case of other arrays.
|
| + if (IsFastDoubleElementsKind(map->elements_kind())) {
|
| + field_type = type_cache_.kFixedDoubleArrayLengthType;
|
| + } else if (IsFastElementsKind(map->elements_kind())) {
|
| + field_type = type_cache_.kFixedArrayLengthType;
|
| + } else {
|
| + field_type = type_cache_.kJSArrayLengthType;
|
| + }
|
| + }
|
| + *access_info =
|
| + PropertyAccessInfo::DataField(receiver_type, field_index, field_type);
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + MaybeHandle<JSObject> holder;
|
| + while (true) {
|
| + // Lookup the named property on the {map}.
|
| + Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
|
| + int const number = descriptors->SearchWithCache(*name, *map);
|
| + if (number != DescriptorArray::kNotFound) {
|
| + PropertyDetails const details = descriptors->GetDetails(number);
|
| + if (access_mode == PropertyAccessMode::kStore) {
|
| + // Don't bother optimizing stores to read-only properties.
|
| + if (details.IsReadOnly()) {
|
| + return false;
|
| + }
|
| + // Check for store to data property on a prototype.
|
| + if (details.kind() == kData && !holder.is_null()) {
|
| + // We need to add the data field to the receiver. Leave the loop
|
| + // and check whether we already have a transition for this field.
|
| + // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
|
| + break;
|
| + }
|
| + }
|
| + if (details.type() == DATA_CONSTANT) {
|
| + *access_info = PropertyAccessInfo::DataConstant(
|
| + receiver_type, handle(descriptors->GetValue(number), isolate()),
|
| + holder);
|
| + return true;
|
| + } else if (details.type() == DATA) {
|
| + int index = descriptors->GetFieldIndex(number);
|
| + Representation field_representation = details.representation();
|
| + FieldIndex field_index = FieldIndex::ForPropertyIndex(
|
| + *map, index, field_representation.IsDouble());
|
| + Type* field_type = Type::Tagged();
|
| + if (field_representation.IsSmi()) {
|
| + field_type = type_cache_.kSmi;
|
| + } else if (field_representation.IsDouble()) {
|
| + field_type = type_cache_.kFloat64;
|
| + } else if (field_representation.IsHeapObject()) {
|
| + // Extract the field type from the property details (make sure its
|
| + // representation is TaggedPointer to reflect the heap object case).
|
| + field_type = Type::Intersect(
|
| + Type::Convert<HeapType>(
|
| + handle(descriptors->GetFieldType(number), isolate()), zone()),
|
| + Type::TaggedPointer(), zone());
|
| + if (field_type->Is(Type::None())) {
|
| + // Store is not safe if the field type was cleared.
|
| + if (access_mode == PropertyAccessMode::kStore) return false;
|
| +
|
| + // The field type was cleared by the GC, so we don't know anything
|
| + // about the contents now.
|
| + // TODO(bmeurer): It would be awesome to make this saner in the
|
| + // runtime/GC interaction.
|
| + field_type = Type::TaggedPointer();
|
| + } else if (!Type::Any()->Is(field_type)) {
|
| + // Add proper code dependencies in case of stable field map(s).
|
| + Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate());
|
| + dependencies()->AssumeFieldType(field_owner_map);
|
| + }
|
| + DCHECK(field_type->Is(Type::TaggedPointer()));
|
| + }
|
| + *access_info = PropertyAccessInfo::DataField(receiver_type, field_index,
|
| + field_type, holder);
|
| + return true;
|
| + } else {
|
| + // TODO(bmeurer): Add support for accessors.
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + // Don't search on the prototype chain for special indices in case of
|
| + // integer indexed exotic objects (see ES6 section 9.4.5).
|
| + if (map->IsJSTypedArrayMap() && name->IsString() &&
|
| + IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) {
|
| + return false;
|
| + }
|
| +
|
| + // Don't lookup private symbols on the prototype chain.
|
| + if (name->IsPrivate()) return false;
|
| +
|
| + // Walk up the prototype chain.
|
| + if (!map->prototype()->IsJSObject()) {
|
| + // Perform the implicit ToObject for primitives here.
|
| + // Implemented according to ES6 section 7.3.2 GetV (V, P).
|
| + Handle<JSFunction> constructor;
|
| + if (Map::GetConstructorFunction(map, native_context())
|
| + .ToHandle(&constructor)) {
|
| + map = handle(constructor->initial_map(), isolate());
|
| + DCHECK(map->prototype()->IsJSObject());
|
| + } else if (map->prototype()->IsNull()) {
|
| + // Store to property not found on the receiver or any prototype, we need
|
| + // to transition to a new data property.
|
| + // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
|
| + if (access_mode == PropertyAccessMode::kStore) {
|
| + break;
|
| + }
|
| + // TODO(bmeurer): Handle the not found case if the prototype is null.
|
| + return false;
|
| + } else {
|
| + return false;
|
| + }
|
| + }
|
| + Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate());
|
| + if (map_prototype->map()->is_deprecated()) {
|
| + // Try to migrate the prototype object so we don't embed the deprecated
|
| + // map into the optimized code.
|
| + JSObject::TryMigrateInstance(map_prototype);
|
| + }
|
| + map = handle(map_prototype->map(), isolate());
|
| + holder = map_prototype;
|
| +
|
| + // Check if it is safe to inline property access for the {map}.
|
| + if (!CanInlinePropertyAccess(map)) return false;
|
| + }
|
| + DCHECK_EQ(PropertyAccessMode::kStore, access_mode);
|
| +
|
| + // Check if the {receiver_map} has a data transition with the given {name}.
|
| + if (receiver_map->unused_property_fields() == 0) return false;
|
| + if (Map* transition = TransitionArray::SearchTransition(*receiver_map, kData,
|
| + *name, NONE)) {
|
| + Handle<Map> transition_map(transition, isolate());
|
| + int const number = transition_map->LastAdded();
|
| + PropertyDetails const details =
|
| + transition_map->instance_descriptors()->GetDetails(number);
|
| + // Don't bother optimizing stores to read-only properties.
|
| + if (details.IsReadOnly()) return false;
|
| + // TODO(bmeurer): Handle transition to data constant?
|
| + if (details.type() != DATA) return false;
|
| + int const index = details.field_index();
|
| + Representation field_representation = details.representation();
|
| + FieldIndex field_index = FieldIndex::ForPropertyIndex(
|
| + *transition_map, index, field_representation.IsDouble());
|
| + Type* field_type = Type::Tagged();
|
| + if (field_representation.IsSmi()) {
|
| + field_type = type_cache_.kSmi;
|
| + } else if (field_representation.IsDouble()) {
|
| + // TODO(bmeurer): Add support for storing to double fields.
|
| + return false;
|
| + } else if (field_representation.IsHeapObject()) {
|
| + // Extract the field type from the property details (make sure its
|
| + // representation is TaggedPointer to reflect the heap object case).
|
| + field_type = Type::Intersect(
|
| + Type::Convert<HeapType>(
|
| + handle(
|
| + transition_map->instance_descriptors()->GetFieldType(number),
|
| + isolate()),
|
| + zone()),
|
| + Type::TaggedPointer(), zone());
|
| + if (field_type->Is(Type::None())) {
|
| + // Store is not safe if the field type was cleared.
|
| + return false;
|
| + } else if (!Type::Any()->Is(field_type)) {
|
| + // Add proper code dependencies in case of stable field map(s).
|
| + Handle<Map> field_owner_map(transition_map->FindFieldOwner(number),
|
| + isolate());
|
| + dependencies()->AssumeFieldType(field_owner_map);
|
| + }
|
| + DCHECK(field_type->Is(Type::TaggedPointer()));
|
| + }
|
| + dependencies()->AssumeMapNotDeprecated(transition_map);
|
| + *access_info = PropertyAccessInfo::DataField(
|
| + receiver_type, field_index, field_type, holder, transition_map);
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| +bool PropertyAccessInfoFactory::ComputePropertyAccessInfos(
|
| + MapHandleList const& maps, Handle<Name> name,
|
| + PropertyAccessMode access_mode,
|
| + ZoneVector<PropertyAccessInfo>* access_infos) {
|
| + for (Handle<Map> map : maps) {
|
| + if (Map::TryUpdate(map).ToHandle(&map)) {
|
| + PropertyAccessInfo access_info;
|
| + if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) {
|
| + return false;
|
| + }
|
| + access_infos->push_back(access_info);
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +Factory* PropertyAccessInfoFactory::factory() const {
|
| + return isolate()->factory();
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|