Get rid of the TRANSITION PropertyType and consistently use CanHoldValue().

R=verwaest@chromium.org

Review URL: https://codereview.chromium.org/223193005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20494 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
bmeurer@chromium.org 2014-04-04 04:49:07 +00:00
parent 5ca16fb8c7
commit e26ff6c45f
11 changed files with 310 additions and 300 deletions

View File

@ -2464,7 +2464,6 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
// Do not occur since the from object has fast properties.
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
// No element in instance descriptors have proxy or interceptor type.
UNREACHABLE();

View File

@ -1671,7 +1671,6 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
break;
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;

165
src/ic.cc
View File

@ -1167,9 +1167,7 @@ static bool LookupForWrite(Handle<JSObject> receiver,
// chain check. This avoids a double lookup, but requires us to pass in the
// receiver when trying to fetch extra information from the transition.
receiver->map()->LookupTransition(*holder, *name, lookup);
if (!lookup->IsTransition()) return false;
PropertyDetails target_details = lookup->GetTransitionDetails();
if (target_details.IsReadOnly()) return false;
if (!lookup->IsTransition() || lookup->IsReadOnly()) return false;
// If the value that's being stored does not fit in the field that the
// instance would transition to, create a new transition that fits the value.
@ -1178,7 +1176,7 @@ static bool LookupForWrite(Handle<JSObject> receiver,
// Ensure the instance and its map were migrated before trying to update the
// transition target.
ASSERT(!receiver->map()->is_deprecated());
if (!value->FitsRepresentation(target_details.representation())) {
if (!lookup->CanHoldValue(value)) {
Handle<Map> target(lookup->GetTransitionTarget());
Map::GeneralizeRepresentation(
target, target->LastAdded(),
@ -1327,93 +1325,94 @@ Handle<Code> StoreIC::CompileHandler(LookupResult* lookup,
Handle<JSObject> holder(lookup->holder());
// Handlers do not use strict mode.
StoreStubCompiler compiler(isolate(), SLOPPY, kind());
switch (lookup->type()) {
case FIELD:
return compiler.CompileStoreField(receiver, lookup, name);
case TRANSITION: {
// Explicitly pass in the receiver map since LookupForWrite may have
// stored something else than the receiver in the holder.
Handle<Map> transition(lookup->GetTransitionTarget());
PropertyDetails details = transition->GetLastDescriptorDetails();
if (details.type() == CALLBACKS || details.attributes() != NONE) break;
if (lookup->IsTransition()) {
// Explicitly pass in the receiver map since LookupForWrite may have
// stored something else than the receiver in the holder.
Handle<Map> transition(lookup->GetTransitionTarget());
PropertyDetails details = lookup->GetPropertyDetails();
if (details.type() != CALLBACKS && details.attributes() == NONE) {
return compiler.CompileStoreTransition(
receiver, lookup, transition, name);
}
case NORMAL:
if (kind() == Code::KEYED_STORE_IC) break;
if (receiver->IsJSGlobalProxy() || receiver->IsGlobalObject()) {
// The stub generated for the global object picks the value directly
// from the property cell. So the property must be directly on the
// global object.
Handle<GlobalObject> global = receiver->IsJSGlobalProxy()
? handle(GlobalObject::cast(receiver->GetPrototype()))
: Handle<GlobalObject>::cast(receiver);
Handle<PropertyCell> cell(global->GetPropertyCell(lookup), isolate());
Handle<HeapType> union_type = PropertyCell::UpdatedType(cell, value);
StoreGlobalStub stub(
union_type->IsConstant(), receiver->IsJSGlobalProxy());
Handle<Code> code = stub.GetCodeCopyFromTemplate(
isolate(), global, cell);
// TODO(verwaest): Move caching of these NORMAL stubs outside as well.
HeapObject::UpdateMapCodeCache(receiver, name, code);
return code;
}
ASSERT(holder.is_identical_to(receiver));
return isolate()->builtins()->StoreIC_Normal();
case CALLBACKS: {
Handle<Object> callback(lookup->GetCallbackObject(), isolate());
if (callback->IsExecutableAccessorInfo()) {
Handle<ExecutableAccessorInfo> info =
Handle<ExecutableAccessorInfo>::cast(callback);
if (v8::ToCData<Address>(info->setter()) == 0) break;
if (!holder->HasFastProperties()) break;
if (!info->IsCompatibleReceiver(*receiver)) break;
return compiler.CompileStoreCallback(receiver, holder, name, info);
} else if (callback->IsAccessorPair()) {
Handle<Object> setter(
Handle<AccessorPair>::cast(callback)->setter(), isolate());
if (!setter->IsJSFunction()) break;
if (holder->IsGlobalObject()) break;
if (!holder->HasFastProperties()) break;
Handle<JSFunction> function = Handle<JSFunction>::cast(setter);
CallOptimization call_optimization(function);
if (call_optimization.is_simple_api_call() &&
call_optimization.IsCompatibleReceiver(receiver, holder)) {
return compiler.CompileStoreCallback(
receiver, holder, name, call_optimization);
} else {
switch (lookup->type()) {
case FIELD:
return compiler.CompileStoreField(receiver, lookup, name);
case NORMAL:
if (kind() == Code::KEYED_STORE_IC) break;
if (receiver->IsJSGlobalProxy() || receiver->IsGlobalObject()) {
// The stub generated for the global object picks the value directly
// from the property cell. So the property must be directly on the
// global object.
Handle<GlobalObject> global = receiver->IsJSGlobalProxy()
? handle(GlobalObject::cast(receiver->GetPrototype()))
: Handle<GlobalObject>::cast(receiver);
Handle<PropertyCell> cell(global->GetPropertyCell(lookup), isolate());
Handle<HeapType> union_type = PropertyCell::UpdatedType(cell, value);
StoreGlobalStub stub(
union_type->IsConstant(), receiver->IsJSGlobalProxy());
Handle<Code> code = stub.GetCodeCopyFromTemplate(
isolate(), global, cell);
// TODO(verwaest): Move caching of these NORMAL stubs outside as well.
HeapObject::UpdateMapCodeCache(receiver, name, code);
return code;
}
return compiler.CompileStoreViaSetter(
receiver, holder, name, Handle<JSFunction>::cast(setter));
}
// TODO(dcarney): Handle correctly.
if (callback->IsDeclaredAccessorInfo()) break;
ASSERT(callback->IsForeign());
ASSERT(holder.is_identical_to(receiver));
return isolate()->builtins()->StoreIC_Normal();
case CALLBACKS: {
Handle<Object> callback(lookup->GetCallbackObject(), isolate());
if (callback->IsExecutableAccessorInfo()) {
Handle<ExecutableAccessorInfo> info =
Handle<ExecutableAccessorInfo>::cast(callback);
if (v8::ToCData<Address>(info->setter()) == 0) break;
if (!holder->HasFastProperties()) break;
if (!info->IsCompatibleReceiver(*receiver)) break;
return compiler.CompileStoreCallback(receiver, holder, name, info);
} else if (callback->IsAccessorPair()) {
Handle<Object> setter(
Handle<AccessorPair>::cast(callback)->setter(), isolate());
if (!setter->IsJSFunction()) break;
if (holder->IsGlobalObject()) break;
if (!holder->HasFastProperties()) break;
Handle<JSFunction> function = Handle<JSFunction>::cast(setter);
CallOptimization call_optimization(function);
if (call_optimization.is_simple_api_call() &&
call_optimization.IsCompatibleReceiver(receiver, holder)) {
return compiler.CompileStoreCallback(
receiver, holder, name, call_optimization);
}
return compiler.CompileStoreViaSetter(
receiver, holder, name, Handle<JSFunction>::cast(setter));
}
// TODO(dcarney): Handle correctly.
if (callback->IsDeclaredAccessorInfo()) break;
ASSERT(callback->IsForeign());
// Use specialized code for setting the length of arrays with fast
// properties. Slow properties might indicate redefinition of the length
// property.
if (receiver->IsJSArray() &&
name->Equals(isolate()->heap()->length_string()) &&
Handle<JSArray>::cast(receiver)->AllowsSetElementsLength() &&
receiver->HasFastProperties()) {
return compiler.CompileStoreArrayLength(receiver, lookup, name);
}
// Use specialized code for setting the length of arrays with fast
// properties. Slow properties might indicate redefinition of the length
// property.
if (receiver->IsJSArray() &&
name->Equals(isolate()->heap()->length_string()) &&
Handle<JSArray>::cast(receiver)->AllowsSetElementsLength() &&
receiver->HasFastProperties()) {
return compiler.CompileStoreArrayLength(receiver, lookup, name);
}
// No IC support for old-style native accessors.
break;
// No IC support for old-style native accessors.
break;
}
case INTERCEPTOR:
if (kind() == Code::KEYED_STORE_IC) break;
ASSERT(HasInterceptorSetter(*holder));
return compiler.CompileStoreInterceptor(receiver, name);
case CONSTANT:
break;
case NONEXISTENT:
case HANDLER:
UNREACHABLE();
break;
}
case INTERCEPTOR:
if (kind() == Code::KEYED_STORE_IC) break;
ASSERT(HasInterceptorSetter(*holder));
return compiler.CompileStoreInterceptor(receiver, name);
case CONSTANT:
break;
case NONEXISTENT:
case HANDLER:
UNREACHABLE();
break;
}
return slow_stub();
}

View File

@ -2629,15 +2629,9 @@ void Map::LookupDescriptor(JSObject* holder,
void Map::LookupTransition(JSObject* holder,
Name* name,
LookupResult* result) {
if (HasTransitionArray()) {
TransitionArray* transition_array = transitions();
int number = transition_array->Search(name);
if (number != TransitionArray::kNotFound) {
return result->TransitionResult(
holder, transition_array->GetTarget(number));
}
}
result->NotFound();
int transition_index = this->SearchTransition(name);
if (transition_index == TransitionArray::kNotFound) return result->NotFound();
result->TransitionResult(holder, this->GetTransition(transition_index));
}
@ -4904,6 +4898,12 @@ Map* Map::GetTransition(int transition_index) {
}
int Map::SearchTransition(Name* name) {
if (HasTransitionArray()) return transitions()->Search(name);
return TransitionArray::kNotFound;
}
MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
TransitionArray* transitions;
MaybeObject* maybe_transitions = AddTransition(

View File

@ -271,7 +271,6 @@ void JSObject::PrintProperties(FILE* out) {
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
// There are no transitions in the descriptor array.
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@ -428,7 +427,6 @@ void JSObject::PrintTransitions(FILE* out) {
case NORMAL:
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@ -1223,7 +1221,6 @@ void TransitionArray::PrintTransitions(FILE* out) {
case NORMAL:
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;

View File

@ -670,7 +670,6 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
}
case HANDLER:
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
}
@ -944,7 +943,6 @@ MaybeObject* Object::GetProperty(Object* receiver,
RETURN_IF_EMPTY_HANDLE(isolate, value);
return *value;
}
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@ -3077,7 +3075,6 @@ Handle<Object> JSObject::SetPropertyViaPrototypes(Handle<JSObject> object,
return JSProxy::SetPropertyViaPrototypesWithHandler(
proxy, object, name, value, attributes, strict_mode, done);
}
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@ -3915,7 +3912,7 @@ Handle<Object> JSObject::SetPropertyUsingTransition(
// Keep the target CONSTANT if the same value is stored.
// TODO(verwaest): Also support keeping the placeholder
// (value->IsUninitialized) as constant.
if (!value->FitsRepresentation(details.representation()) ||
if (!lookup->CanHoldValue(value) ||
(details.type() == CONSTANT &&
descriptors->GetValue(descriptor) != *value)) {
transition_map = Map::GeneralizeRepresentation(transition_map,
@ -3948,7 +3945,7 @@ static void SetPropertyToField(LookupResult* lookup,
Handle<Name> name,
Handle<Object> value) {
Representation representation = lookup->representation();
if (!value->FitsRepresentation(representation) ||
if (!lookup->CanHoldValue(value) ||
lookup->type() == CONSTANT) {
JSObject::GeneralizeFieldRepresentation(handle(lookup->holder()),
lookup->GetDescriptorIndex(),
@ -4091,34 +4088,35 @@ Handle<Object> JSObject::SetPropertyForResult(Handle<JSObject> object,
// This is a real property that is not read-only, or it is a
// transition or null descriptor and there are no setters in the prototypes.
Handle<Object> result = value;
switch (lookup->type()) {
case NORMAL:
SetNormalizedProperty(handle(lookup->holder()), lookup, value);
break;
case FIELD:
SetPropertyToField(lookup, name, value);
break;
case CONSTANT:
// Only replace the constant if necessary.
if (*value == lookup->GetConstant()) return value;
SetPropertyToField(lookup, name, value);
break;
case CALLBACKS: {
Handle<Object> callback_object(lookup->GetCallbackObject(), isolate);
return SetPropertyWithCallback(object, callback_object, name, value,
handle(lookup->holder()), strict_mode);
if (lookup->IsTransition()) {
result = SetPropertyUsingTransition(handle(lookup->holder()), lookup,
name, value, attributes);
} else {
switch (lookup->type()) {
case NORMAL:
SetNormalizedProperty(handle(lookup->holder()), lookup, value);
break;
case FIELD:
SetPropertyToField(lookup, name, value);
break;
case CONSTANT:
// Only replace the constant if necessary.
if (*value == lookup->GetConstant()) return value;
SetPropertyToField(lookup, name, value);
break;
case CALLBACKS: {
Handle<Object> callback_object(lookup->GetCallbackObject(), isolate);
return SetPropertyWithCallback(object, callback_object, name, value,
handle(lookup->holder()), strict_mode);
}
case INTERCEPTOR:
result = SetPropertyWithInterceptor(
handle(lookup->holder()), name, value, attributes, strict_mode);
break;
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
}
case INTERCEPTOR:
result = SetPropertyWithInterceptor(handle(lookup->holder()), name, value,
attributes, strict_mode);
break;
case TRANSITION:
result = SetPropertyUsingTransition(handle(lookup->holder()), lookup,
name, value, attributes);
break;
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
}
RETURN_IF_EMPTY_HANDLE_VALUE(isolate, result, Handle<Object>());
@ -4216,33 +4214,33 @@ Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes(
}
// Check of IsReadOnly removed from here in clone.
switch (lookup.type()) {
case NORMAL:
ReplaceSlowProperty(object, name, value, attributes);
break;
case FIELD:
SetPropertyToFieldWithAttributes(&lookup, name, value, attributes);
break;
case CONSTANT:
// Only replace the constant if necessary.
if (lookup.GetAttributes() != attributes ||
*value != lookup.GetConstant()) {
if (lookup.IsTransition()) {
Handle<Object> result = SetPropertyUsingTransition(
handle(lookup.holder()), &lookup, name, value, attributes);
RETURN_IF_EMPTY_HANDLE_VALUE(isolate, result, Handle<Object>());
} else {
switch (lookup.type()) {
case NORMAL:
ReplaceSlowProperty(object, name, value, attributes);
break;
case FIELD:
SetPropertyToFieldWithAttributes(&lookup, name, value, attributes);
}
break;
case CALLBACKS:
ConvertAndSetLocalProperty(&lookup, name, value, attributes);
break;
case TRANSITION: {
Handle<Object> result = SetPropertyUsingTransition(
handle(lookup.holder()), &lookup, name, value, attributes);
RETURN_IF_EMPTY_HANDLE_VALUE(isolate, result, Handle<Object>());
break;
break;
case CONSTANT:
// Only replace the constant if necessary.
if (lookup.GetAttributes() != attributes ||
*value != lookup.GetConstant()) {
SetPropertyToFieldWithAttributes(&lookup, name, value, attributes);
}
break;
case CALLBACKS:
ConvertAndSetLocalProperty(&lookup, name, value, attributes);
break;
case NONEXISTENT:
case HANDLER:
case INTERCEPTOR:
UNREACHABLE();
}
case NONEXISTENT:
case HANDLER:
case INTERCEPTOR:
UNREACHABLE();
}
if (is_observed) {
@ -4386,7 +4384,6 @@ PropertyAttributes JSReceiver::GetPropertyAttributeForResult(
Handle<JSObject>::cast(receiver),
name,
continue_search);
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
}
@ -4629,7 +4626,6 @@ void JSObject::NormalizeProperties(Handle<JSObject> object,
break;
case HANDLER:
case NORMAL:
case TRANSITION:
case NONEXISTENT:
UNREACHABLE();
break;
@ -5579,11 +5575,11 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
}
}
LookupResult result(isolate);
Handle<Map> old_map(object->map());
old_map->LookupTransition(*object, isolate->heap()->frozen_symbol(), &result);
if (result.IsTransition()) {
Handle<Map> transition_map(result.GetTransitionTarget());
Handle<Map> old_map(object->map(), isolate);
int transition_index = old_map->SearchTransition(
isolate->heap()->frozen_symbol());
if (transition_index != TransitionArray::kNotFound) {
Handle<Map> transition_map(old_map->GetTransition(transition_index));
ASSERT(transition_map->has_dictionary_elements());
ASSERT(transition_map->is_frozen());
ASSERT(!transition_map->is_extensible());
@ -5635,22 +5631,19 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
void JSObject::SetObserved(Handle<JSObject> object) {
ASSERT(!object->map()->is_observed());
Isolate* isolate = object->GetIsolate();
LookupResult result(isolate);
object->map()->LookupTransition(*object,
isolate->heap()->observed_symbol(),
&result);
Handle<Map> new_map;
if (result.IsTransition()) {
new_map = handle(result.GetTransitionTarget());
Handle<Map> old_map(object->map(), isolate);
ASSERT(!old_map->is_observed());
int transition_index = old_map->SearchTransition(
isolate->heap()->observed_symbol());
if (transition_index != TransitionArray::kNotFound) {
new_map = handle(old_map->GetTransition(transition_index), isolate);
ASSERT(new_map->is_observed());
} else if (object->map()->CanHaveMoreTransitions()) {
new_map = Map::CopyForObserved(handle(object->map()));
} else if (old_map->CanHaveMoreTransitions()) {
new_map = Map::CopyForObserved(old_map);
} else {
new_map = Map::Copy(handle(object->map()));
new_map = Map::Copy(old_map);
new_map->set_is_observed();
}
JSObject::MigrateToMap(object, new_map);

View File

@ -6001,6 +6001,7 @@ class Map: public HeapObject {
Map* transitioned_map);
inline void SetTransition(int transition_index, Map* target);
inline Map* GetTransition(int transition_index);
inline int SearchTransition(Name* name);
static Handle<TransitionArray> AddTransition(Handle<Map> map,
Handle<Name> key,

View File

@ -77,9 +77,8 @@ enum PropertyType {
// Only in lookup results, not in descriptors.
HANDLER = 4,
INTERCEPTOR = 5,
TRANSITION = 6,
// Only used as a marker in LookupResult.
NONEXISTENT = 7
NONEXISTENT = 6
};

View File

@ -29,59 +29,61 @@ void LookupResult::Print(FILE* out) {
PrintF(out, "LookupResult:\n");
PrintF(out, " -cacheable = %s\n", IsCacheable() ? "true" : "false");
PrintF(out, " -attributes = %x\n", GetAttributes());
switch (type()) {
case NORMAL:
PrintF(out, " -type = normal\n");
PrintF(out, " -entry = %d", GetDictionaryEntry());
break;
case CONSTANT:
PrintF(out, " -type = constant\n");
PrintF(out, " -value:\n");
GetConstant()->Print(out);
PrintF(out, "\n");
break;
case FIELD:
PrintF(out, " -type = field\n");
PrintF(out, " -index = %d", GetFieldIndex().field_index());
PrintF(out, "\n");
break;
case CALLBACKS:
PrintF(out, " -type = call backs\n");
PrintF(out, " -callback object:\n");
GetCallbackObject()->Print(out);
break;
case HANDLER:
PrintF(out, " -type = lookup proxy\n");
break;
case INTERCEPTOR:
PrintF(out, " -type = lookup interceptor\n");
break;
case TRANSITION:
switch (GetTransitionDetails().type()) {
case FIELD:
PrintF(out, " -type = map transition\n");
PrintF(out, " -map:\n");
GetTransitionTarget()->Print(out);
PrintF(out, "\n");
return;
case CONSTANT:
PrintF(out, " -type = constant property transition\n");
PrintF(out, " -map:\n");
GetTransitionTarget()->Print(out);
PrintF(out, "\n");
return;
case CALLBACKS:
PrintF(out, " -type = callbacks transition\n");
PrintF(out, " -callback object:\n");
GetCallbackObject()->Print(out);
return;
default:
UNREACHABLE();
return;
}
case NONEXISTENT:
UNREACHABLE();
break;
if (IsTransition()) {
switch (type()) {
case FIELD:
PrintF(out, " -type = map transition\n");
PrintF(out, " -map:\n");
GetTransitionTarget()->Print(out);
PrintF(out, "\n");
break;
case CONSTANT:
PrintF(out, " -type = constant property transition\n");
PrintF(out, " -map:\n");
GetTransitionTarget()->Print(out);
PrintF(out, "\n");
break;
case CALLBACKS:
PrintF(out, " -type = callbacks transition\n");
PrintF(out, " -callback object:\n");
GetCallbackObject()->Print(out);
break;
default:
UNREACHABLE();
break;
}
} else {
switch (type()) {
case NORMAL:
PrintF(out, " -type = normal\n");
PrintF(out, " -entry = %d", GetDictionaryEntry());
break;
case CONSTANT:
PrintF(out, " -type = constant\n");
PrintF(out, " -value:\n");
GetConstant()->Print(out);
PrintF(out, "\n");
break;
case FIELD:
PrintF(out, " -type = field\n");
PrintF(out, " -index = %d", GetFieldIndex().field_index());
PrintF(out, "\n");
break;
case CALLBACKS:
PrintF(out, " -type = call backs\n");
PrintF(out, " -callback object:\n");
GetCallbackObject()->Print(out);
break;
case HANDLER:
PrintF(out, " -type = lookup proxy\n");
break;
case INTERCEPTOR:
PrintF(out, " -type = lookup interceptor\n");
break;
case NONEXISTENT:
UNREACHABLE();
break;
}
}
}

View File

@ -180,16 +180,15 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
bool CanHoldValue(Handle<Object> value) {
if (IsNormal()) return true;
ASSERT(!IsTransition());
return value->FitsRepresentation(details_.representation());
}
void TransitionResult(JSObject* holder, Map* target) {
lookup_type_ = TRANSITION_TYPE;
details_ = PropertyDetails(NONE, TRANSITION, Representation::None());
number_ = target->LastAdded();
details_ = target->instance_descriptors()->GetDetails(number_);
holder_ = holder;
transition_ = target;
number_ = 0xAAAA;
}
void DictionaryResult(JSObject* holder, int entry) {
@ -239,20 +238,17 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
Representation representation() const {
ASSERT(IsFound());
ASSERT(!IsTransition());
ASSERT(details_.type() != NONEXISTENT);
return details_.representation();
}
PropertyAttributes GetAttributes() const {
ASSERT(!IsTransition());
ASSERT(IsFound());
ASSERT(details_.type() != NONEXISTENT);
return details_.attributes();
}
PropertyDetails GetPropertyDetails() const {
ASSERT(!IsTransition());
return details_;
}
@ -264,38 +260,40 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
// Property callbacks does not include transitions to callbacks.
bool IsPropertyCallbacks() const {
ASSERT(!(details_.type() == CALLBACKS && !IsFound()));
return details_.type() == CALLBACKS;
return !IsTransition() && details_.type() == CALLBACKS;
}
bool IsReadOnly() const {
ASSERT(IsFound());
ASSERT(!IsTransition());
ASSERT(details_.type() != NONEXISTENT);
return details_.IsReadOnly();
}
bool IsField() const {
ASSERT(!(details_.type() == FIELD && !IsFound()));
return details_.type() == FIELD;
return IsDescriptorOrDictionary() && type() == FIELD;
}
bool IsNormal() const {
ASSERT(!(details_.type() == NORMAL && !IsFound()));
return details_.type() == NORMAL;
return IsDescriptorOrDictionary() && type() == NORMAL;
}
bool IsConstant() const {
ASSERT(!(details_.type() == CONSTANT && !IsFound()));
return details_.type() == CONSTANT;
return IsDescriptorOrDictionary() && type() == CONSTANT;
}
bool IsConstantFunction() const {
return IsConstant() && GetValue()->IsJSFunction();
return IsConstant() && GetConstant()->IsJSFunction();
}
bool IsDontDelete() const { return details_.IsDontDelete(); }
bool IsDontEnum() const { return details_.IsDontEnum(); }
bool IsFound() const { return lookup_type_ != NOT_FOUND; }
bool IsDescriptorOrDictionary() const {
return lookup_type_ == DESCRIPTOR_TYPE || lookup_type_ == DICTIONARY_TYPE;
}
bool IsTransition() const { return lookup_type_ == TRANSITION_TYPE; }
bool IsHandler() const { return lookup_type_ == HANDLER_TYPE; }
bool IsInterceptor() const { return lookup_type_ == INTERCEPTOR_TYPE; }
@ -306,20 +304,30 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
}
bool IsDataProperty() const {
switch (type()) {
case FIELD:
case NORMAL:
case CONSTANT:
return true;
case CALLBACKS: {
Object* callback = GetCallbackObject();
return callback->IsAccessorInfo() || callback->IsForeign();
}
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
switch (lookup_type_) {
case NOT_FOUND:
case TRANSITION_TYPE:
case HANDLER_TYPE:
case INTERCEPTOR_TYPE:
return false;
case DESCRIPTOR_TYPE:
case DICTIONARY_TYPE:
switch (type()) {
case FIELD:
case NORMAL:
case CONSTANT:
return true;
case CALLBACKS: {
Object* callback = GetCallbackObject();
return callback->IsAccessorInfo() || callback->IsForeign();
}
case HANDLER:
case INTERCEPTOR:
case NONEXISTENT:
UNREACHABLE();
return false;
}
}
UNREACHABLE();
return false;
@ -329,45 +337,52 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
void DisallowCaching() { cacheable_ = false; }
Object* GetLazyValue() const {
switch (type()) {
case FIELD:
return holder()->RawFastPropertyAt(GetFieldIndex().field_index());
case NORMAL: {
Object* value;
value = holder()->property_dictionary()->ValueAt(GetDictionaryEntry());
if (holder()->IsGlobalObject()) {
value = PropertyCell::cast(value)->value();
}
return value;
}
case CONSTANT:
return GetConstant();
case CALLBACKS:
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
case NONEXISTENT:
switch (lookup_type_) {
case NOT_FOUND:
case TRANSITION_TYPE:
case HANDLER_TYPE:
case INTERCEPTOR_TYPE:
return isolate()->heap()->the_hole_value();
case DESCRIPTOR_TYPE:
case DICTIONARY_TYPE:
switch (type()) {
case FIELD:
return holder()->RawFastPropertyAt(GetFieldIndex().field_index());
case NORMAL: {
Object* value = holder()->property_dictionary()->ValueAt(
GetDictionaryEntry());
if (holder()->IsGlobalObject()) {
value = PropertyCell::cast(value)->value();
}
return value;
}
case CONSTANT:
return GetConstant();
case CALLBACKS:
return isolate()->heap()->the_hole_value();
case HANDLER:
case INTERCEPTOR:
case NONEXISTENT:
UNREACHABLE();
return NULL;
}
}
UNREACHABLE();
return NULL;
}
Map* GetTransitionTarget() const {
ASSERT(IsTransition());
return transition_;
}
PropertyDetails GetTransitionDetails() const {
ASSERT(IsTransition());
return transition_->GetLastDescriptorDetails();
}
bool IsTransitionToField() const {
return IsTransition() && GetTransitionDetails().type() == FIELD;
return IsTransition() && details_.type() == FIELD;
}
bool IsTransitionToConstant() const {
return IsTransition() && GetTransitionDetails().type() == CONSTANT;
return IsTransition() && details_.type() == CONSTANT;
}
int GetDescriptorIndex() const {
@ -376,7 +391,8 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
}
PropertyIndex GetFieldIndex() const {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(lookup_type_ == DESCRIPTOR_TYPE ||
lookup_type_ == TRANSITION_TYPE);
return PropertyIndex::NewFieldIndex(GetFieldIndexFromMap(holder()->map()));
}
@ -409,7 +425,8 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
}
Object* GetCallbackObject() const {
ASSERT(type() == CALLBACKS && !IsTransition());
ASSERT(!IsTransition());
ASSERT(type() == CALLBACKS);
return GetValue();
}
@ -420,6 +437,8 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
Object* GetValue() const {
if (lookup_type_ == DESCRIPTOR_TYPE) {
return GetValueFromMap(holder()->map());
} else if (lookup_type_ == TRANSITION_TYPE) {
return GetValueFromMap(transition_);
}
// In the dictionary case, the data is held in the value field.
ASSERT(lookup_type_ == DICTIONARY_TYPE);
@ -427,13 +446,15 @@ class LookupResult V8_FINAL BASE_EMBEDDED {
}
Object* GetValueFromMap(Map* map) const {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(lookup_type_ == DESCRIPTOR_TYPE ||
lookup_type_ == TRANSITION_TYPE);
ASSERT(number_ < map->NumberOfOwnDescriptors());
return map->instance_descriptors()->GetValue(number_);
}
int GetFieldIndexFromMap(Map* map) const {
ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
ASSERT(lookup_type_ == DESCRIPTOR_TYPE ||
lookup_type_ == TRANSITION_TYPE);
ASSERT(number_ < map->NumberOfOwnDescriptors());
return map->instance_descriptors()->GetFieldIndex(number_);
}

View File

@ -5217,23 +5217,23 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDataProperty) {
CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
LookupResult lookup(isolate);
object->LookupRealNamedProperty(*key, &lookup);
if (!lookup.IsFound()) return isolate->heap()->undefined_value();
switch (lookup.type()) {
case NORMAL:
return lookup.holder()->GetNormalizedProperty(&lookup);
case FIELD:
return lookup.holder()->FastPropertyAt(
lookup.representation(),
lookup.GetFieldIndex().field_index());
case CONSTANT:
return lookup.GetConstant();
case CALLBACKS:
case HANDLER:
case INTERCEPTOR:
case TRANSITION:
return isolate->heap()->undefined_value();
case NONEXISTENT:
UNREACHABLE();
if (lookup.IsFound() && !lookup.IsTransition()) {
switch (lookup.type()) {
case NORMAL:
return lookup.holder()->GetNormalizedProperty(&lookup);
case FIELD:
return lookup.holder()->FastPropertyAt(
lookup.representation(),
lookup.GetFieldIndex().field_index());
case CONSTANT:
return lookup.GetConstant();
case CALLBACKS:
case HANDLER:
case INTERCEPTOR:
break;
case NONEXISTENT:
UNREACHABLE();
}
}
return isolate->heap()->undefined_value();
}
@ -10783,6 +10783,7 @@ static MaybeObject* DebugLookupResultValue(Heap* heap,
LookupResult* result,
bool* caught_exception) {
Object* value;
if (result->IsTransition()) return heap->undefined_value();
switch (result->type()) {
case NORMAL:
value = result->holder()->GetNormalizedProperty(result);
@ -10826,7 +10827,6 @@ static MaybeObject* DebugLookupResultValue(Heap* heap,
}
}
case INTERCEPTOR:
case TRANSITION:
return heap->undefined_value();
case HANDLER:
case NONEXISTENT: