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:
parent
f947eff31d
commit
1404664e34
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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__));
|
||||
|
Loading…
Reference in New Issue
Block a user