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:
parent
5ca16fb8c7
commit
e26ff6c45f
@ -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();
|
||||
|
@ -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
165
src/ic.cc
@ -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();
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
145
src/objects.cc
145
src/objects.cc
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
108
src/property.cc
108
src/property.cc
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
129
src/property.h
129
src/property.h
@ -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_);
|
||||
}
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user