Ensure prototypes always stay fast by turning them fast again after an operation that turned them slow

BUG=
R=ishell@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22827 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
verwaest@chromium.org 2014-08-04 15:06:28 +00:00
parent f947eff31d
commit 1404664e34
4 changed files with 43 additions and 34 deletions

View File

@ -4067,10 +4067,13 @@ void JSObject::ConvertAndSetOwnProperty(LookupResult* lookup,
Handle<JSObject> object(lookup->holder());
if (object->map()->TooManyFastProperties(Object::MAY_BE_STORE_FROM_KEYED)) {
JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
} else if (object->map()->is_prototype_map()) {
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0);
}
if (!object->HasFastProperties()) {
ReplaceSlowProperty(object, name, value, attributes);
ReoptimizeIfPrototype(object);
return;
}
@ -5151,17 +5154,22 @@ Handle<Object> JSObject::SetHiddenPropertiesHashTable(Handle<JSObject> object,
Handle<Object> JSObject::DeletePropertyPostInterceptor(Handle<JSObject> object,
Handle<Name> name,
DeleteMode mode) {
DeleteMode delete_mode) {
// Check own property, ignore interceptor.
Isolate* isolate = object->GetIsolate();
LookupResult result(isolate);
object->LookupOwnRealNamedProperty(name, &result);
if (!result.IsFound()) return isolate->factory()->true_value();
LookupResult lookup(isolate);
object->LookupOwnRealNamedProperty(name, &lookup);
if (!lookup.IsFound()) return isolate->factory()->true_value();
PropertyNormalizationMode mode = object->map()->is_prototype_map()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
// Normalize object if needed.
NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
NormalizeProperties(object, mode, 0);
return DeleteNormalizedProperty(object, name, mode);
Handle<Object> result = DeleteNormalizedProperty(object, name, delete_mode);
ReoptimizeIfPrototype(object);
return result;
}
@ -5308,7 +5316,7 @@ MaybeHandle<Object> JSObject::DeleteElement(Handle<JSObject> object,
MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
Handle<Name> name,
DeleteMode mode) {
DeleteMode delete_mode) {
Isolate* isolate = object->GetIsolate();
// ECMA-262, 3rd, 8.6.2.5
DCHECK(name->IsName());
@ -5327,20 +5335,20 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
return JSGlobalObject::DeleteProperty(
Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter)), name,
mode);
delete_mode);
}
uint32_t index = 0;
if (name->AsArrayIndex(&index)) {
return DeleteElement(object, index, mode);
return DeleteElement(object, index, delete_mode);
}
LookupResult lookup(isolate);
object->LookupOwn(name, &lookup, true);
if (!lookup.IsFound()) return isolate->factory()->true_value();
// Ignore attributes if forcing a deletion.
if (lookup.IsDontDelete() && mode != FORCE_DELETION) {
if (mode == STRICT_DELETION) {
if (lookup.IsDontDelete() && delete_mode != FORCE_DELETION) {
if (delete_mode == STRICT_DELETION) {
// Deleting a non-configurable property in strict mode.
Handle<Object> args[2] = { name, object };
Handle<Object> error = isolate->factory()->NewTypeError(
@ -5362,8 +5370,8 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
// Check for interceptor.
if (lookup.IsInterceptor()) {
// Skip interceptor if forcing a deletion.
if (mode == FORCE_DELETION) {
result = DeletePropertyPostInterceptor(object, name, mode);
if (delete_mode == FORCE_DELETION) {
result = DeletePropertyPostInterceptor(object, name, delete_mode);
} else {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
@ -5371,10 +5379,14 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
Object);
}
} else {
PropertyNormalizationMode mode = object->map()->is_prototype_map()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
// Normalize object if needed.
NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
NormalizeProperties(object, mode, 0);
// Make sure the properties are normalized before removing the entry.
result = DeleteNormalizedProperty(object, name, mode);
result = DeleteNormalizedProperty(object, name, delete_mode);
ReoptimizeIfPrototype(object);
}
if (is_observed) {
@ -5696,6 +5708,7 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
Handle<Map> new_map = Map::CopyForFreeze(old_map);
JSObject::MigrateToMap(object, new_map);
} else {
DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
// Slow path: need to normalize properties for safety
NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
@ -6594,8 +6607,11 @@ void JSObject::SetPropertyCallback(Handle<JSObject> object,
Handle<Name> name,
Handle<Object> structure,
PropertyAttributes attributes) {
PropertyNormalizationMode mode = object->map()->is_prototype_map()
? KEEP_INOBJECT_PROPERTIES
: CLEAR_INOBJECT_PROPERTIES;
// Normalize object to make this operation simple.
NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
NormalizeProperties(object, mode, 0);
// For the global object allocate a new map to invalidate the global inline
// caches which have a global property cell reference directly in the code.
@ -6613,6 +6629,8 @@ void JSObject::SetPropertyCallback(Handle<JSObject> object,
// Update the dictionary with the new CALLBACKS property.
PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
SetNormalizedProperty(object, name, structure, details);
ReoptimizeIfPrototype(object);
}
@ -9970,6 +9988,12 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
}
void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
if (!object->map()->is_prototype_map()) return;
OptimizeAsPrototype(object);
}
Handle<Object> CacheInitialJSArrayMaps(
Handle<Context> native_context, Handle<Map> initial_map) {
// Replace all of the cached initial array maps in the native context with
@ -10132,9 +10156,6 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
Handle<Object> prototype;
if (function->has_instance_prototype()) {
prototype = handle(function->instance_prototype(), isolate);
// TODO(verwaest): Remove once "delete" keeps objects marked as prototypes
// fast as well.
JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(prototype));
} else {
prototype = isolate->factory()->NewFunctionPrototype(function);
}

View File

@ -2197,6 +2197,7 @@ class JSObject: public JSReceiver {
PropertyDetails details);
static void OptimizeAsPrototype(Handle<JSObject> object);
static void ReoptimizeIfPrototype(Handle<JSObject> object);
// Retrieve interceptors.
InterceptorInfo* GetNamedInterceptor();

View File

@ -5009,20 +5009,6 @@ RUNTIME_FUNCTION(Runtime_DefineDataPropertyUnchecked) {
// map.
if (lookup.IsFound() &&
(attr != lookup.GetAttributes() || lookup.IsPropertyCallbacks())) {
// New attributes - normalize to avoid writing to instance descriptor
if (js_object->IsJSGlobalProxy()) {
// Since the result is a property, the prototype will exist so
// we don't have to check for null.
PrototypeIterator iter(isolate, js_object);
js_object = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
}
if (attr != lookup.GetAttributes() ||
(lookup.IsPropertyCallbacks() &&
!lookup.GetCallbackObject()->IsAccessorInfo())) {
JSObject::NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0);
}
// Use IgnoreAttributes version since a readonly property may be
// overridden and SetProperty does not allow this.
Handle<Object> result;

View File

@ -40,7 +40,8 @@ SlowPrototype.prototype.bar = 2;
SlowPrototype.prototype.baz = 3;
delete SlowPrototype.prototype.baz;
assertFalse(%HasFastProperties(SlowPrototype.prototype));
// Prototypes stay fast even after deleting properties.
assertTrue(%HasFastProperties(SlowPrototype.prototype));
var fast_proto = new SlowPrototype();
assertTrue(%HasFastProperties(SlowPrototype.prototype));
assertTrue(%HasFastProperties(fast_proto.__proto__));