summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/objects.cc')
-rw-r--r--deps/v8/src/objects.cc2135
1 files changed, 1475 insertions, 660 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 254cd26f57..fcc2efad31 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -27,6 +27,7 @@
#include "v8.h"
+#include "accessors.h"
#include "api.h"
#include "arguments.h"
#include "bootstrapper.h"
@@ -247,6 +248,18 @@ MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw,
}
+Handle<Object> Object::GetProperty(Handle<Object> object, Handle<String> name) {
+ // TODO(rossberg): The index test should not be here but in the GetProperty
+ // method (or somewhere else entirely). Needs more global clean-up.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) return GetElement(object, index);
+ Isolate* isolate = object->IsHeapObject()
+ ? Handle<HeapObject>::cast(object)->GetIsolate()
+ : Isolate::Current();
+ CALL_HEAP_FUNCTION(isolate, object->GetProperty(*name), Object);
+}
+
+
Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) {
Isolate* isolate = object->IsHeapObject()
? Handle<HeapObject>::cast(object)->GetIsolate()
@@ -641,7 +654,8 @@ MaybeObject* Object::GetProperty(Object* receiver,
ASSERT(!value->IsTheHole() || result->IsReadOnly());
return value->IsTheHole() ? heap->undefined_value() : value;
case FIELD:
- value = result->holder()->FastPropertyAt(result->GetFieldIndex());
+ value = result->holder()->FastPropertyAt(
+ result->GetFieldIndex().field_index());
ASSERT(!value->IsTheHole() || result->IsReadOnly());
return value->IsTheHole() ? heap->undefined_value() : value;
case CONSTANT_FUNCTION:
@@ -881,14 +895,15 @@ MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) {
int len = length();
Object* object;
String* result;
- if (IsAsciiRepresentation()) {
- { MaybeObject* maybe_object = heap->AllocateRawAsciiString(len, tenure);
+ if (IsOneByteRepresentation()) {
+ { MaybeObject* maybe_object =
+ heap->AllocateRawOneByteString(len, tenure);
if (!maybe_object->ToObject(&object)) return maybe_object;
}
result = String::cast(object);
String* first = cs->first();
int first_length = first->length();
- char* dest = SeqAsciiString::cast(result)->GetChars();
+ char* dest = SeqOneByteString::cast(result)->GetChars();
WriteToFlat(first, dest, 0, first_length);
String* second = cs->second();
WriteToFlat(second,
@@ -941,7 +956,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
if (size < ExternalString::kShortSize) {
return false;
}
- bool is_ascii = this->IsAsciiRepresentation();
+ bool is_ascii = this->IsOneByteRepresentation();
bool is_symbol = this->IsSymbol();
// Morph the object to an external string by adjusting the map and
@@ -1118,6 +1133,10 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
}
break;
}
+ case JS_MODULE_TYPE: {
+ accumulator->Add("<JS Module>");
+ break;
+ }
// All other JSObjects are rather similar to each other (JSObject,
// JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
default: {
@@ -1340,7 +1359,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
SlicedString::BodyDescriptor::IterateBody(this, v);
break;
case kExternalStringTag:
- if ((type & kStringEncodingMask) == kAsciiStringTag) {
+ if ((type & kStringEncodingMask) == kOneByteStringTag) {
reinterpret_cast<ExternalAsciiString*>(this)->
ExternalAsciiStringIterateBody(v);
} else {
@@ -1539,8 +1558,9 @@ MaybeObject* JSObject::AddFastProperty(String* name,
PropertyAttributes attributes,
StoreFromKeyed store_mode) {
ASSERT(!IsJSGlobalProxy());
- ASSERT(map()->instance_descriptors()->Search(name) ==
- DescriptorArray::kNotFound);
+ ASSERT(DescriptorArray::kNotFound ==
+ map()->instance_descriptors()->Search(
+ name, map()->NumberOfOwnDescriptors()));
// Normalize the object if the name is an actual string (not the
// hidden symbols) and is not a real identifier.
@@ -1676,39 +1696,88 @@ MaybeObject* JSObject::AddProperty(String* name,
ASSERT(!IsJSGlobalProxy());
Map* map_of_this = map();
Heap* heap = GetHeap();
+ Isolate* isolate = heap->isolate();
+ MaybeObject* result;
if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK &&
!map_of_this->is_extensible()) {
if (strict_mode == kNonStrictMode) {
return value;
} else {
Handle<Object> args[1] = {Handle<String>(name)};
- return heap->isolate()->Throw(
+ return isolate->Throw(
*FACTORY->NewTypeError("object_not_extensible",
HandleVector(args, 1)));
}
}
+
if (HasFastProperties()) {
// Ensure the descriptor array does not get too big.
- if (map_of_this->instance_descriptors()->number_of_descriptors() <
+ if (map_of_this->NumberOfOwnDescriptors() <
DescriptorArray::kMaxNumberOfDescriptors) {
if (value->IsJSFunction()) {
- return AddConstantFunctionProperty(name,
- JSFunction::cast(value),
- attributes);
+ result = AddConstantFunctionProperty(name,
+ JSFunction::cast(value),
+ attributes);
} else {
- return AddFastProperty(name, value, attributes, store_mode);
+ result = AddFastProperty(name, value, attributes, store_mode);
}
} else {
// Normalize the object to prevent very large instance descriptors.
// This eliminates unwanted N^2 allocation and lookup behavior.
Object* obj;
- { MaybeObject* maybe_obj =
- NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
+ MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (!maybe->To(&obj)) return maybe;
+ result = AddSlowProperty(name, value, attributes);
}
+ } else {
+ result = AddSlowProperty(name, value, attributes);
}
- return AddSlowProperty(name, value, attributes);
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (FLAG_harmony_observation && map()->is_observed()) {
+ EnqueueChangeRecord(handle(this, isolate),
+ "new",
+ handle(name, isolate),
+ handle(heap->the_hole_value(), isolate));
+ }
+
+ return *hresult;
+}
+
+
+void JSObject::EnqueueChangeRecord(Handle<JSObject> object,
+ const char* type_str,
+ Handle<String> name,
+ Handle<Object> old_value) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope;
+ Handle<String> type = isolate->factory()->LookupAsciiSymbol(type_str);
+ if (object->IsJSGlobalObject()) {
+ object = handle(JSGlobalObject::cast(*object)->global_receiver(), isolate);
+ }
+ Handle<Object> args[] = { type, object, name, old_value };
+ bool threw;
+ Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()),
+ Handle<Object>(isolate->heap()->undefined_value()),
+ old_value->IsTheHole() ? 3 : 4, args,
+ &threw);
+ ASSERT(!threw);
+}
+
+
+void JSObject::DeliverChangeRecords(Isolate* isolate) {
+ ASSERT(isolate->observer_delivery_pending());
+ bool threw = false;
+ Execution::Call(
+ isolate->observers_deliver_changes(),
+ isolate->factory()->undefined_value(),
+ 0,
+ NULL,
+ &threw);
+ ASSERT(!threw);
+ isolate->set_observer_delivery_pending(false);
}
@@ -1760,6 +1829,7 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition(
Object* new_value,
PropertyAttributes attributes) {
Map* old_map = map();
+ Map* old_target = old_map->GetTransition(transition_index);
Object* result;
MaybeObject* maybe_result =
@@ -1770,13 +1840,35 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition(
// This method should only be used to convert existing transitions. Objects
// with the map of "new Object()" cannot have transitions in the first place.
- ASSERT(map() != GetIsolate()->empty_object_map());
+ Map* new_map = map();
+ ASSERT(new_map != GetIsolate()->empty_object_map());
// TODO(verwaest): From here on we lose existing map transitions, causing
// invalid back pointers. This will change once we can store multiple
// transitions with the same key.
- old_map->SetTransition(transition_index, map());
- map()->SetBackPointer(old_map);
+
+ bool owned_descriptors = old_map->owns_descriptors();
+ if (owned_descriptors ||
+ old_target->instance_descriptors() == old_map->instance_descriptors()) {
+ // Since the conversion above generated a new fast map with an additional
+ // property which can be shared as well, install this descriptor pointer
+ // along the entire chain of smaller maps.
+ Map* map;
+ DescriptorArray* new_descriptors = new_map->instance_descriptors();
+ DescriptorArray* old_descriptors = old_map->instance_descriptors();
+ for (Object* current = old_map;
+ !current->IsUndefined();
+ current = map->GetBackPointer()) {
+ map = Map::cast(current);
+ if (map->instance_descriptors() != old_descriptors) break;
+ map->SetEnumLength(Map::kInvalidEnumCache);
+ map->set_instance_descriptors(new_descriptors);
+ }
+ old_map->set_owns_descriptors(false);
+ }
+
+ old_map->SetTransition(transition_index, new_map);
+ new_map->SetBackPointer(old_map);
return result;
}
@@ -1883,7 +1975,7 @@ MaybeObject* JSReceiver::SetProperty(String* name,
StrictModeFlag strict_mode,
JSReceiver::StoreFromKeyed store_mode) {
LookupResult result(GetIsolate());
- LocalLookup(name, &result);
+ LocalLookup(name, &result, true);
if (!result.IsFound()) {
map()->LookupTransition(JSObject::cast(this), name, &result);
}
@@ -2100,11 +2192,13 @@ enum RightTrimMode { FROM_GC, FROM_MUTATOR };
static void ZapEndOfFixedArray(Address new_end, int to_trim) {
// If we are doing a big trim in old space then we zap the space.
Object** zap = reinterpret_cast<Object**>(new_end);
+ zap++; // Header of filler must be at least one word so skip that.
for (int i = 1; i < to_trim; i++) {
*zap++ = Smi::FromInt(0);
}
}
+
template<RightTrimMode trim_mode>
static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
ASSERT(elms->map() != HEAP->fixed_cow_array_map());
@@ -2117,12 +2211,8 @@ static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim);
- if (trim_mode == FROM_GC) {
-#ifdef DEBUG
- ZapEndOfFixedArray(new_end, to_trim);
-#endif
- } else {
- ZapEndOfFixedArray(new_end, to_trim);
+ if (trim_mode != FROM_GC || Heap::ShouldZapGarbage()) {
+ ZapEndOfFixedArray(new_end, to_trim);
}
int size_delta = to_trim * kPointerSize;
@@ -2145,13 +2235,31 @@ static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
}
-void Map::CopyAppendCallbackDescriptors(Handle<Map> map,
- Handle<Object> descriptors) {
+void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
+ Handle<DescriptorArray> descriptors(map->instance_descriptors());
+ if (slack <= descriptors->NumberOfSlackDescriptors()) return;
+ int number_of_descriptors = descriptors->number_of_descriptors();
+ Isolate* isolate = map->GetIsolate();
+ Handle<DescriptorArray> new_descriptors =
+ isolate->factory()->NewDescriptorArray(number_of_descriptors, slack);
+ DescriptorArray::WhitenessWitness witness(*new_descriptors);
+
+ for (int i = 0; i < number_of_descriptors; ++i) {
+ new_descriptors->CopyFrom(i, *descriptors, i, witness);
+ }
+
+ map->set_instance_descriptors(*new_descriptors);
+}
+
+
+void Map::AppendCallbackDescriptors(Handle<Map> map,
+ Handle<Object> descriptors) {
Isolate* isolate = map->GetIsolate();
Handle<DescriptorArray> array(map->instance_descriptors());
- v8::NeanderArray callbacks(descriptors);
+ NeanderArray callbacks(descriptors);
int nof_callbacks = callbacks.length();
- int descriptor_count = array->number_of_descriptors();
+
+ ASSERT(array->NumberOfSlackDescriptors() >= nof_callbacks);
// Ensure the keys are symbols before writing them into the instance
// descriptor. Since it may cause a GC, it has to be done before we
@@ -2164,23 +2272,7 @@ void Map::CopyAppendCallbackDescriptors(Handle<Map> map,
entry->set_name(*key);
}
- Handle<DescriptorArray> result =
- isolate->factory()->NewDescriptorArray(descriptor_count + nof_callbacks);
-
- // Ensure that marking will not progress and change color of objects.
- DescriptorArray::WhitenessWitness witness(*result);
-
- // Copy the descriptors from the array.
- if (0 < descriptor_count) {
- for (int i = 0; i < descriptor_count; i++) {
- result->CopyFrom(i, *array, i, witness);
- }
- }
-
- // After this point the GC is not allowed to run anymore until the map is in a
- // consistent state again, i.e., all the descriptors are appended and the
- // descriptor array is trimmed to the right size.
- Map::SetDescriptors(map, result);
+ int nof = map->NumberOfOwnDescriptors();
// Fill in new callback descriptors. Process the callbacks from
// back to front so that the last callback with a given name takes
@@ -2189,26 +2281,14 @@ void Map::CopyAppendCallbackDescriptors(Handle<Map> map,
AccessorInfo* entry = AccessorInfo::cast(callbacks.get(i));
String* key = String::cast(entry->name());
// Check if a descriptor with this name already exists before writing.
- if (LinearSearch(*result, key, map->NumberOfOwnDescriptors()) ==
- DescriptorArray::kNotFound) {
+ if (array->Search(key, nof) == DescriptorArray::kNotFound) {
CallbacksDescriptor desc(key, entry, entry->property_attributes());
- map->AppendDescriptor(&desc, witness);
+ array->Append(&desc);
+ nof += 1;
}
}
- int new_number_of_descriptors = map->NumberOfOwnDescriptors();
- // Reinstall the original descriptor array if no new elements were added.
- if (new_number_of_descriptors == descriptor_count) {
- Map::SetDescriptors(map, array);
- return;
- }
-
- // If duplicates were detected, trim the descriptor array to the right size.
- int new_array_size = DescriptorArray::LengthFor(new_number_of_descriptors);
- if (new_array_size < result->length()) {
- RightTrimFixedArray<FROM_MUTATOR>(
- isolate->heap(), *result, result->length() - new_array_size);
- }
+ map->SetNumberOfOwnDescriptors(nof);
}
@@ -2381,7 +2461,7 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
// occur as fields.
if (result->IsField() &&
result->IsReadOnly() &&
- FastPropertyAt(result->GetFieldIndex())->IsTheHole()) {
+ FastPropertyAt(result->GetFieldIndex().field_index())->IsTheHole()) {
result->DisallowCaching();
}
return;
@@ -2721,12 +2801,14 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler(
- JSReceiver* receiver,
+ JSReceiver* receiver_raw,
uint32_t index) {
Isolate* isolate = GetIsolate();
HandleScope scope(isolate);
+ Handle<JSProxy> proxy(this);
+ Handle<JSReceiver> receiver(receiver_raw);
Handle<String> name = isolate->factory()->Uint32ToString(index);
- return GetPropertyAttributeWithHandler(receiver, *name);
+ return proxy->GetPropertyAttributeWithHandler(*receiver, *name);
}
@@ -2782,13 +2864,22 @@ MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name,
}
-MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
+void JSObject::AddFastPropertyUsingMap(Handle<JSObject> object,
+ Handle<Map> map) {
+ CALL_HEAP_FUNCTION_VOID(
+ object->GetIsolate(),
+ object->AddFastPropertyUsingMap(*map));
+}
+
+
+MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
String* name_raw,
Object* value_raw,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
StoreFromKeyed store_mode) {
Heap* heap = GetHeap();
+ Isolate* isolate = heap->isolate();
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc;
@@ -2807,9 +2898,9 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
// Check access rights if needed.
if (IsAccessCheckNeeded()) {
- if (!heap->isolate()->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
+ if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(
- result, name_raw, value_raw, true, strict_mode);
+ lookup, name_raw, value_raw, true, strict_mode);
}
}
@@ -2818,66 +2909,78 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
if (proto->IsNull()) return value_raw;
ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->SetPropertyForResult(
- result, name_raw, value_raw, attributes, strict_mode, store_mode);
+ lookup, name_raw, value_raw, attributes, strict_mode, store_mode);
}
// From this point on everything needs to be handlified, because
// SetPropertyViaPrototypes might call back into JavaScript.
- HandleScope scope(GetIsolate());
+ HandleScope scope(isolate);
Handle<JSObject> self(this);
Handle<String> name(name_raw);
- Handle<Object> value(value_raw);
+ Handle<Object> value(value_raw, isolate);
- if (!result->IsProperty() && !self->IsJSContextExtensionObject()) {
+ if (!lookup->IsProperty() && !self->IsJSContextExtensionObject()) {
bool done = false;
MaybeObject* result_object = self->SetPropertyViaPrototypes(
*name, *value, attributes, strict_mode, &done);
if (done) return result_object;
}
- if (!result->IsFound()) {
+ if (!lookup->IsFound()) {
// Neither properties nor transitions found.
return self->AddProperty(
*name, *value, attributes, strict_mode, store_mode);
}
- if (result->IsProperty() && result->IsReadOnly()) {
+
+ if (lookup->IsProperty() && lookup->IsReadOnly()) {
if (strict_mode == kStrictMode) {
Handle<Object> args[] = { name, self };
- return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError(
+ return isolate->Throw(*isolate->factory()->NewTypeError(
"strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))));
} else {
return *value;
}
}
+ Handle<Object> old_value(heap->the_hole_value(), isolate);
+ if (FLAG_harmony_observation && map()->is_observed()) {
+ old_value = handle(lookup->GetLazyValue(), isolate);
+ }
+
// 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.
- switch (result->type()) {
+ MaybeObject* result = *value;
+ switch (lookup->type()) {
case NORMAL:
- return self->SetNormalizedProperty(result, *value);
+ result = self->SetNormalizedProperty(lookup, *value);
+ break;
case FIELD:
- return self->FastPropertyAtPut(result->GetFieldIndex(), *value);
+ result = self->FastPropertyAtPut(
+ lookup->GetFieldIndex().field_index(), *value);
+ break;
case CONSTANT_FUNCTION:
// Only replace the function if necessary.
- if (*value == result->GetConstantFunction()) return *value;
+ if (*value == lookup->GetConstantFunction()) return *value;
// Preserve the attributes of this existing property.
- attributes = result->GetAttributes();
- return self->ConvertDescriptorToField(*name, *value, attributes);
+ attributes = lookup->GetAttributes();
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ break;
case CALLBACKS: {
- Object* callback_object = result->GetCallbackObject();
+ Object* callback_object = lookup->GetCallbackObject();
return self->SetPropertyWithCallback(callback_object,
*name,
*value,
- result->holder(),
+ lookup->holder(),
strict_mode);
}
case INTERCEPTOR:
- return self->SetPropertyWithInterceptor(*name,
- *value,
- attributes,
- strict_mode);
+ result = self->SetPropertyWithInterceptor(*name,
+ *value,
+ attributes,
+ strict_mode);
+ break;
case TRANSITION: {
- Map* transition_map = result->GetTransitionTarget();
+ Map* transition_map = lookup->GetTransitionTarget();
int descriptor = transition_map->LastAdded();
DescriptorArray* descriptors = transition_map->instance_descriptors();
@@ -2886,37 +2989,55 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
if (details.type() == FIELD) {
if (attributes == details.attributes()) {
int field_index = descriptors->GetFieldIndex(descriptor);
- return self->AddFastPropertyUsingMap(transition_map,
- *name,
- *value,
- field_index);
+ result = self->AddFastPropertyUsingMap(transition_map,
+ *name,
+ *value,
+ field_index);
+ } else {
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
}
- return self->ConvertDescriptorToField(*name, *value, attributes);
} else if (details.type() == CALLBACKS) {
- return ConvertDescriptorToField(*name, *value, attributes);
- }
-
- ASSERT(details.type() == CONSTANT_FUNCTION);
-
- Object* constant_function = descriptors->GetValue(descriptor);
- // If the same constant function is being added we can simply
- // transition to the target map.
- if (constant_function == *value) {
- self->set_map(transition_map);
- return constant_function;
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ } else {
+ ASSERT(details.type() == CONSTANT_FUNCTION);
+
+ Object* constant_function = descriptors->GetValue(descriptor);
+ if (constant_function == *value) {
+ // If the same constant function is being added we can simply
+ // transition to the target map.
+ self->set_map(transition_map);
+ result = constant_function;
+ } else {
+ // Otherwise, replace with a map transition to a new map with a FIELD,
+ // even if the value is a constant function.
+ result = self->ConvertTransitionToMapTransition(
+ lookup->GetTransitionIndex(), *name, *value, attributes);
+ }
}
- // Otherwise, replace with a map transition to a new map with a FIELD,
- // even if the value is a constant function.
- return ConvertTransitionToMapTransition(
- result->GetTransitionIndex(), *name, *value, attributes);
+ break;
}
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
- return *value;
}
- UNREACHABLE(); // keep the compiler happy
- return *value;
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (FLAG_harmony_observation && map()->is_observed()) {
+ if (lookup->IsTransition()) {
+ EnqueueChangeRecord(self, "new", name, old_value);
+ } else {
+ LookupResult new_lookup(isolate);
+ self->LocalLookup(*name, &new_lookup, true);
+ ASSERT(!new_lookup.GetLazyValue()->IsTheHole());
+ if (!new_lookup.GetLazyValue()->SameValue(*old_value)) {
+ EnqueueChangeRecord(self, "updated", name, old_value);
+ }
+ }
+ }
+
+ return *hresult;
}
@@ -2942,22 +3063,22 @@ Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes(
MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
- String* name,
- Object* value,
+ String* name_raw,
+ Object* value_raw,
PropertyAttributes attributes) {
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc;
Isolate* isolate = GetIsolate();
- LookupResult result(isolate);
- LocalLookup(name, &result);
- if (!result.IsFound()) map()->LookupTransition(this, name, &result);
+ LookupResult lookup(isolate);
+ LocalLookup(name_raw, &lookup, true);
+ if (!lookup.IsFound()) map()->LookupTransition(this, name_raw, &lookup);
// Check access rights if needed.
if (IsAccessCheckNeeded()) {
- if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
- return SetPropertyWithFailedAccessCheck(&result,
- name,
- value,
+ if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
+ return SetPropertyWithFailedAccessCheck(&lookup,
+ name_raw,
+ value_raw,
false,
kNonStrictMode);
}
@@ -2965,40 +3086,69 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
if (IsJSGlobalProxy()) {
Object* proto = GetPrototype();
- if (proto->IsNull()) return value;
+ if (proto->IsNull()) return value_raw;
ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
- name,
- value,
+ name_raw,
+ value_raw,
attributes);
}
// Check for accessor in prototype chain removed here in clone.
- if (!result.IsFound()) {
+ if (!lookup.IsFound()) {
// Neither properties nor transitions found.
- return AddProperty(name, value, attributes, kNonStrictMode);
+ return AddProperty(name_raw, value_raw, attributes, kNonStrictMode);
+ }
+
+ // From this point on everything needs to be handlified.
+ HandleScope scope(isolate);
+ Handle<JSObject> self(this);
+ Handle<String> name(name_raw);
+ Handle<Object> value(value_raw, isolate);
+
+ Handle<Object> old_value(isolate->heap()->the_hole_value(), isolate);
+ PropertyAttributes old_attributes = ABSENT;
+ bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
+ if (is_observed) {
+ // Function prototypes are stored specially
+ if (self->IsJSFunction() &&
+ JSFunction::cast(*self)->should_have_prototype() &&
+ name->Equals(isolate->heap()->prototype_symbol())) {
+ MaybeObject* maybe = Accessors::FunctionGetPrototype(*self, NULL);
+ if (!maybe->ToHandle(&old_value, isolate)) return maybe;
+ } else {
+ old_value = handle(lookup.GetLazyValue(), isolate);
+ }
+ old_attributes = lookup.GetAttributes();
}
// Check of IsReadOnly removed from here in clone.
- switch (result.type()) {
+ MaybeObject* result = *value;
+ switch (lookup.type()) {
case NORMAL: {
PropertyDetails details = PropertyDetails(attributes, NORMAL);
- return SetNormalizedProperty(name, value, details);
+ result = self->SetNormalizedProperty(*name, *value, details);
+ break;
}
case FIELD:
- return FastPropertyAtPut(result.GetFieldIndex(), value);
+ result = self->FastPropertyAtPut(
+ lookup.GetFieldIndex().field_index(), *value);
+ break;
case CONSTANT_FUNCTION:
// Only replace the function if necessary.
- if (value == result.GetConstantFunction()) return value;
- // Preserve the attributes of this existing property.
- attributes = result.GetAttributes();
- return ConvertDescriptorToField(name, value, attributes);
+ if (*value != lookup.GetConstantFunction()) {
+ // Preserve the attributes of this existing property.
+ attributes = lookup.GetAttributes();
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ }
+ break;
case CALLBACKS:
case INTERCEPTOR:
// Override callback in clone
- return ConvertDescriptorToField(name, value, attributes);
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ break;
case TRANSITION: {
- Map* transition_map = result.GetTransitionTarget();
+ Map* transition_map = lookup.GetTransitionTarget();
int descriptor = transition_map->LastAdded();
DescriptorArray* descriptors = transition_map->instance_descriptors();
@@ -3007,29 +3157,48 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
if (details.type() == FIELD) {
if (attributes == details.attributes()) {
int field_index = descriptors->GetFieldIndex(descriptor);
- return AddFastPropertyUsingMap(transition_map,
- name,
- value,
- field_index);
+ result = self->AddFastPropertyUsingMap(
+ transition_map, *name, *value, field_index);
+ } else {
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
}
- return ConvertDescriptorToField(name, value, attributes);
} else if (details.type() == CALLBACKS) {
- return ConvertDescriptorToField(name, value, attributes);
- }
-
- ASSERT(details.type() == CONSTANT_FUNCTION);
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ } else {
+ ASSERT(details.type() == CONSTANT_FUNCTION);
- // Replace transition to CONSTANT FUNCTION with a map transition to a new
- // map with a FIELD, even if the value is a function.
- return ConvertTransitionToMapTransition(
- result.GetTransitionIndex(), name, value, attributes);
+ // Replace transition to CONSTANT FUNCTION with a map transition to a
+ // new map with a FIELD, even if the value is a function.
+ result = self->ConvertTransitionToMapTransition(
+ lookup.GetTransitionIndex(), *name, *value, attributes);
+ }
+ break;
}
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
}
- UNREACHABLE(); // keep the compiler happy
- return value;
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (is_observed) {
+ if (lookup.IsTransition()) {
+ EnqueueChangeRecord(self, "new", name, old_value);
+ } else {
+ LookupResult new_lookup(isolate);
+ self->LocalLookup(*name, &new_lookup, true);
+ ASSERT(!new_lookup.GetLazyValue()->IsTheHole());
+ if (old_value->IsTheHole() ||
+ new_lookup.GetAttributes() != old_attributes) {
+ EnqueueChangeRecord(self, "reconfigured", name, old_value);
+ } else if (!new_lookup.GetLazyValue()->SameValue(*old_value)) {
+ EnqueueChangeRecord(self, "updated", name, old_value);
+ }
+ }
+ }
+
+ return *hresult;
}
@@ -3110,42 +3279,43 @@ PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver(
String* key) {
uint32_t index = 0;
if (IsJSObject() && key->AsArrayIndex(&index)) {
- return JSObject::cast(this)->HasElementWithReceiver(receiver, index)
- ? NONE : ABSENT;
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ receiver, index, true);
}
// Named property.
- LookupResult result(GetIsolate());
- Lookup(key, &result);
- return GetPropertyAttribute(receiver, &result, key, true);
+ LookupResult lookup(GetIsolate());
+ Lookup(key, &lookup);
+ return GetPropertyAttributeForResult(receiver, &lookup, key, true);
}
-PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
- LookupResult* result,
- String* name,
- bool continue_search) {
+PropertyAttributes JSReceiver::GetPropertyAttributeForResult(
+ JSReceiver* receiver,
+ LookupResult* lookup,
+ String* name,
+ bool continue_search) {
// Check access rights if needed.
if (IsAccessCheckNeeded()) {
JSObject* this_obj = JSObject::cast(this);
Heap* heap = GetHeap();
if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) {
return this_obj->GetPropertyAttributeWithFailedAccessCheck(
- receiver, result, name, continue_search);
+ receiver, lookup, name, continue_search);
}
}
- if (result->IsFound()) {
- switch (result->type()) {
+ if (lookup->IsFound()) {
+ switch (lookup->type()) {
case NORMAL: // fall through
case FIELD:
case CONSTANT_FUNCTION:
case CALLBACKS:
- return result->GetAttributes();
+ return lookup->GetAttributes();
case HANDLER: {
- return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler(
+ return JSProxy::cast(lookup->proxy())->GetPropertyAttributeWithHandler(
receiver, name);
}
case INTERCEPTOR:
- return result->holder()->GetPropertyAttributeWithInterceptor(
+ return lookup->holder()->GetPropertyAttributeWithInterceptor(
JSObject::cast(receiver), name, continue_search);
case TRANSITION:
case NONEXISTENT:
@@ -3160,13 +3330,118 @@ PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) {
// Check whether the name is an array index.
uint32_t index = 0;
if (IsJSObject() && name->AsArrayIndex(&index)) {
- if (JSObject::cast(this)->HasLocalElement(index)) return NONE;
- return ABSENT;
+ return GetLocalElementAttribute(index);
}
// Named property.
- LookupResult result(GetIsolate());
- LocalLookup(name, &result);
- return GetPropertyAttribute(this, &result, name, false);
+ LookupResult lookup(GetIsolate());
+ LocalLookup(name, &lookup, true);
+ return GetPropertyAttributeForResult(this, &lookup, name, false);
+}
+
+
+PropertyAttributes JSObject::GetElementAttributeWithReceiver(
+ JSReceiver* receiver, uint32_t index, bool continue_search) {
+ Isolate* isolate = GetIsolate();
+
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return ABSENT;
+ }
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return ABSENT;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->GetElementAttributeWithReceiver(
+ receiver, index, continue_search);
+ }
+
+ // Check for lookup interceptor except when bootstrapping.
+ if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) {
+ return GetElementAttributeWithInterceptor(receiver, index, continue_search);
+ }
+
+ // Handle [] on String objects.
+ if (this->IsStringObjectWithCharacterAt(index)) {
+ return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
+ }
+
+ return GetElementAttributeWithoutInterceptor(
+ receiver, index, continue_search);
+}
+
+
+PropertyAttributes JSObject::GetElementAttributeWithInterceptor(
+ JSReceiver* receiver, uint32_t index, bool continue_search) {
+ Isolate* isolate = GetIsolate();
+ // Make sure that the top context does not change when doing
+ // callbacks or interceptor calls.
+ AssertNoContextChange ncc;
+ HandleScope scope(isolate);
+ Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
+ Handle<JSReceiver> hreceiver(receiver);
+ Handle<JSObject> holder(this);
+ CustomArguments args(isolate, interceptor->data(), receiver, this);
+ v8::AccessorInfo info(args.end());
+ if (!interceptor->query()->IsUndefined()) {
+ v8::IndexedPropertyQuery query =
+ v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
+ v8::Handle<v8::Integer> result;
+ {
+ // Leaving JavaScript.
+ VMState state(isolate, EXTERNAL);
+ result = query(index, info);
+ }
+ if (!result.IsEmpty())
+ return static_cast<PropertyAttributes>(result->Int32Value());
+ } else if (!interceptor->getter()->IsUndefined()) {
+ v8::IndexedPropertyGetter getter =
+ v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-get-has", this, index));
+ v8::Handle<v8::Value> result;
+ {
+ // Leaving JavaScript.
+ VMState state(isolate, EXTERNAL);
+ result = getter(index, info);
+ }
+ if (!result.IsEmpty()) return NONE;
+ }
+
+ return holder->GetElementAttributeWithoutInterceptor(
+ *hreceiver, index, continue_search);
+}
+
+
+PropertyAttributes JSObject::GetElementAttributeWithoutInterceptor(
+ JSReceiver* receiver, uint32_t index, bool continue_search) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSReceiver> hreceiver(receiver);
+ Handle<JSObject> holder(this);
+ PropertyAttributes attr = holder->GetElementsAccessor()->GetAttributes(
+ *hreceiver, *holder, index);
+ if (attr != ABSENT) return attr;
+
+ if (holder->IsStringObjectWithCharacterAt(index)) {
+ return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
+ }
+
+ if (!continue_search) return ABSENT;
+
+ Object* pt = holder->GetPrototype();
+ if (pt->IsJSProxy()) {
+ // We need to follow the spec and simulate a call to [[GetOwnProperty]].
+ return JSProxy::cast(pt)->GetElementAttributeWithHandler(*hreceiver, index);
+ }
+ if (pt->IsNull()) return ABSENT;
+ return JSObject::cast(pt)->GetElementAttributeWithReceiver(
+ *hreceiver, index, true);
}
@@ -3178,10 +3453,12 @@ MaybeObject* NormalizedMapCache::Get(JSObject* obj,
Object* result = get(index);
if (result->IsMap() &&
Map::cast(result)->EquivalentToForNormalization(fast, mode)) {
-#ifdef DEBUG
+#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
Map::cast(result)->SharedMapVerify();
}
+#endif
+#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
// The cached map should match newly created normalized map bit-by-bit,
// except for the code cache, which can contain some ics which can be
@@ -3271,19 +3548,19 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
Map* map_of_this = map();
// Allocate new content.
- int property_count = map_of_this->NumberOfDescribedProperties();
+ int real_size = map_of_this->NumberOfOwnDescriptors();
+ int property_count = real_size;
if (expected_additional_properties > 0) {
property_count += expected_additional_properties;
} else {
property_count += 2; // Make space for two more properties.
}
StringDictionary* dictionary;
- { MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count);
- if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
- }
+ MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
DescriptorArray* descs = map_of_this->instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ for (int i = 0; i < real_size; i++) {
PropertyDetails details = descs->GetDetails(i);
switch (details.type()) {
case CONSTANT_FUNCTION: {
@@ -3328,8 +3605,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
Heap* current_heap = GetHeap();
// Copy the next enumeration index from instance descriptor.
- int index = map_of_this->instance_descriptors()->NextEnumerationIndex();
- dictionary->SetNextEnumerationIndex(index);
+ dictionary->SetNextEnumerationIndex(real_size + 1);
Map* new_map;
MaybeObject* maybe_map =
@@ -3557,7 +3833,6 @@ Object* JSObject::GetHiddenProperty(String* key) {
ASSERT(!IsJSGlobalProxy());
MaybeObject* hidden_lookup =
GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
- ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg.
Object* inline_value = hidden_lookup->ToObjectUnchecked();
if (inline_value->IsSmi()) {
@@ -3598,13 +3873,11 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) {
return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value);
}
ASSERT(!IsJSGlobalProxy());
-
- // If there is no backing store yet, store the identity hash inline.
MaybeObject* hidden_lookup =
GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
- ASSERT(!hidden_lookup->IsFailure());
Object* inline_value = hidden_lookup->ToObjectUnchecked();
+ // If there is no backing store yet, store the identity hash inline.
if (value->IsSmi() &&
key == GetHeap()->identity_hash_symbol() &&
(inline_value->IsUndefined() || inline_value->IsSmi())) {
@@ -3641,15 +3914,16 @@ void JSObject::DeleteHiddenProperty(String* key) {
JSObject::cast(proxy_parent)->DeleteHiddenProperty(key);
return;
}
+ ASSERT(!IsJSGlobalProxy());
MaybeObject* hidden_lookup =
GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
- ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg.
- if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) return;
+ Object* inline_value = hidden_lookup->ToObjectUnchecked();
+
// We never delete (inline-stored) identity hashes.
- ASSERT(!hidden_lookup->ToObjectUnchecked()->IsSmi());
+ ASSERT(key != GetHeap()->identity_hash_symbol());
+ if (inline_value->IsUndefined() || inline_value->IsSmi()) return;
- ObjectHashTable* hashtable =
- ObjectHashTable::cast(hidden_lookup->ToObjectUnchecked());
+ ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value);
MaybeObject* delete_result = hashtable->Put(key, GetHeap()->the_hole_value());
USE(delete_result);
ASSERT(!delete_result->IsFailure()); // Delete does not cause GC.
@@ -3675,10 +3949,11 @@ MaybeObject* JSObject::GetHiddenPropertiesHashTable(
DescriptorArray* descriptors = this->map()->instance_descriptors();
if (descriptors->number_of_descriptors() > 0) {
int sorted_index = descriptors->GetSortedKeyIndex(0);
- if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) {
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() &&
+ sorted_index < map()->NumberOfOwnDescriptors()) {
ASSERT(descriptors->GetType(sorted_index) == FIELD);
- inline_value = this->FastPropertyAt(
- descriptors->GetFieldIndex(sorted_index));
+ inline_value =
+ this->FastPropertyAt(descriptors->GetFieldIndex(sorted_index));
} else {
inline_value = GetHeap()->undefined_value();
}
@@ -3743,7 +4018,8 @@ MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) {
DescriptorArray* descriptors = this->map()->instance_descriptors();
if (descriptors->number_of_descriptors() > 0) {
int sorted_index = descriptors->GetSortedKeyIndex(0);
- if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol()) {
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() &&
+ sorted_index < map()->NumberOfOwnDescriptors()) {
ASSERT(descriptors->GetType(sorted_index) == FIELD);
this->FastPropertyAtPut(descriptors->GetFieldIndex(sorted_index),
value);
@@ -3868,6 +4144,21 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
return isolate->heap()->false_value();
}
+ if (IsStringObjectWithCharacterAt(index)) {
+ if (mode == STRICT_DELETION) {
+ // Deleting a non-configurable property in strict mode.
+ HandleScope scope(isolate);
+ Handle<Object> holder(this);
+ Handle<Object> name = isolate->factory()->NewNumberFromUint(index);
+ Handle<Object> args[2] = { name, holder };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("strict_delete_property",
+ HandleVector(args, 2));
+ return isolate->Throw(*error);
+ }
+ return isolate->heap()->false_value();
+ }
+
if (IsJSGlobalProxy()) {
Object* proto = GetPrototype();
if (proto->IsNull()) return isolate->heap()->false_value();
@@ -3875,15 +4166,38 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
}
- if (HasIndexedInterceptor()) {
- // Skip interceptor if forcing deletion.
- if (mode != FORCE_DELETION) {
- return DeleteElementWithInterceptor(index);
+ // From this point on everything needs to be handlified.
+ HandleScope scope(isolate);
+ Handle<JSObject> self(this);
+
+ Handle<Object> old_value;
+ bool should_enqueue_change_record = false;
+ if (FLAG_harmony_observation && self->map()->is_observed()) {
+ should_enqueue_change_record = self->HasLocalElement(index);
+ if (should_enqueue_change_record) {
+ old_value = self->GetLocalElementAccessorPair(index) != NULL
+ ? Handle<Object>::cast(isolate->factory()->the_hole_value())
+ : Object::GetElement(self, index);
}
- mode = JSReceiver::FORCE_DELETION;
}
- return GetElementsAccessor()->Delete(this, index, mode);
+ MaybeObject* result;
+ // Skip interceptor if forcing deletion.
+ if (self->HasIndexedInterceptor() && mode != FORCE_DELETION) {
+ result = self->DeleteElementWithInterceptor(index);
+ } else {
+ result = self->GetElementsAccessor()->Delete(*self, index, mode);
+ }
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (should_enqueue_change_record && !self->HasLocalElement(index)) {
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
+ EnqueueChangeRecord(self, "deleted", name, old_value);
+ }
+
+ return *hresult;
}
@@ -3917,38 +4231,60 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
uint32_t index = 0;
if (name->AsArrayIndex(&index)) {
return DeleteElement(index, mode);
- } else {
- LookupResult result(isolate);
- LocalLookup(name, &result);
- if (!result.IsFound()) return isolate->heap()->true_value();
- // Ignore attributes if forcing a deletion.
- if (result.IsDontDelete() && mode != FORCE_DELETION) {
- if (mode == STRICT_DELETION) {
- // Deleting a non-configurable property in strict mode.
- HandleScope scope(isolate);
- Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) };
- return isolate->Throw(*isolate->factory()->NewTypeError(
- "strict_delete_property", HandleVector(args, 2)));
- }
- return isolate->heap()->false_value();
- }
- // Check for interceptor.
- if (result.IsInterceptor()) {
- // Skip interceptor if forcing a deletion.
- if (mode == FORCE_DELETION) {
- return DeletePropertyPostInterceptor(name, mode);
- }
- return DeletePropertyWithInterceptor(name);
+ }
+
+ LookupResult lookup(isolate);
+ LocalLookup(name, &lookup, true);
+ if (!lookup.IsFound()) return isolate->heap()->true_value();
+ // Ignore attributes if forcing a deletion.
+ if (lookup.IsDontDelete() && mode != FORCE_DELETION) {
+ if (mode == STRICT_DELETION) {
+ // Deleting a non-configurable property in strict mode.
+ HandleScope scope(isolate);
+ Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) };
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_delete_property", HandleVector(args, 2)));
}
+ return isolate->heap()->false_value();
+ }
+
+ // From this point on everything needs to be handlified.
+ HandleScope scope(isolate);
+ Handle<JSObject> self(this);
+ Handle<String> hname(name);
+
+ Handle<Object> old_value(isolate->heap()->the_hole_value());
+ bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
+ if (is_observed) {
+ old_value = handle(lookup.GetLazyValue(), isolate);
+ }
+ MaybeObject* result;
+
+ // Check for interceptor.
+ if (lookup.IsInterceptor()) {
+ // Skip interceptor if forcing a deletion.
+ if (mode == FORCE_DELETION) {
+ result = self->DeletePropertyPostInterceptor(*hname, mode);
+ } else {
+ result = self->DeletePropertyWithInterceptor(*hname);
+ }
+ } else {
// Normalize object if needed.
Object* obj;
- { MaybeObject* maybe_obj =
- NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
+ result = self->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (!result->To(&obj)) return result;
// Make sure the properties are normalized before removing the entry.
- return DeleteNormalizedProperty(name, mode);
+ result = self->DeleteNormalizedProperty(*hname, mode);
}
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (is_observed && !self->HasLocalProperty(*hname)) {
+ EnqueueChangeRecord(self, "deleted", hname, old_value);
+ }
+
+ return *hresult;
}
@@ -4178,21 +4514,15 @@ bool JSReceiver::IsSimpleEnum() {
}
-void Map::SetDescriptors(Handle<Map> map,
- Handle<DescriptorArray> descriptors) {
- Isolate* isolate = map->GetIsolate();
- CALL_HEAP_FUNCTION_VOID(isolate, map->SetDescriptors(*descriptors));
-}
-
-
-int Map::NumberOfDescribedProperties(PropertyAttributes filter) {
+int Map::NumberOfDescribedProperties(DescriptorFlag which,
+ PropertyAttributes filter) {
int result = 0;
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
- PropertyDetails details = descs->GetDetails(i);
- if ((details.attributes() & filter) == 0) {
- result++;
- }
+ int limit = which == ALL_DESCRIPTORS
+ ? descs->number_of_descriptors()
+ : NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
+ if ((descs->GetDetails(i).attributes() & filter) == 0) result++;
}
return result;
}
@@ -4200,7 +4530,8 @@ int Map::NumberOfDescribedProperties(PropertyAttributes filter) {
int Map::PropertyIndexFor(String* name) {
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ int limit = NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
if (name->Equals(descs->GetKey(i))) return descs->GetFieldIndex(i);
}
return -1;
@@ -4209,8 +4540,9 @@ int Map::PropertyIndexFor(String* name) {
int Map::NextFreePropertyIndex() {
int max_index = -1;
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ for (int i = 0; i < number_of_own_descriptors; i++) {
if (descs->GetType(i) == FIELD) {
int current_index = descs->GetFieldIndex(i);
if (current_index > max_index) max_index = current_index;
@@ -4222,8 +4554,9 @@ int Map::NextFreePropertyIndex() {
AccessorDescriptor* Map::FindAccessor(String* name) {
DescriptorArray* descs = instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
- if (name->Equals(descs->GetKey(i)) && descs->GetType(i) == CALLBACKS) {
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ for (int i = 0; i < number_of_own_descriptors; i++) {
+ if (descs->GetType(i) == CALLBACKS && name->Equals(descs->GetKey(i))) {
return descs->GetCallbacks(i);
}
}
@@ -4231,7 +4564,8 @@ AccessorDescriptor* Map::FindAccessor(String* name) {
}
-void JSReceiver::LocalLookup(String* name, LookupResult* result) {
+void JSReceiver::LocalLookup(
+ String* name, LookupResult* result, bool search_hidden_prototypes) {
ASSERT(name->IsString());
Heap* heap = GetHeap();
@@ -4240,7 +4574,8 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) {
Object* proto = GetPrototype();
if (proto->IsNull()) return result->NotFound();
ASSERT(proto->IsJSGlobalObject());
- return JSReceiver::cast(proto)->LocalLookup(name, result);
+ return JSReceiver::cast(proto)->LocalLookup(
+ name, result, search_hidden_prototypes);
}
if (IsJSProxy()) {
@@ -4270,6 +4605,14 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) {
}
js_object->LocalLookupRealNamedProperty(name, result);
+ if (result->IsFound() || !search_hidden_prototypes) return;
+
+ Object* proto = js_object->GetPrototype();
+ if (!proto->IsJSReceiver()) return;
+ JSReceiver* receiver = JSReceiver::cast(proto);
+ if (receiver->map()->is_hidden_prototype()) {
+ receiver->LocalLookup(name, result, search_hidden_prototypes);
+ }
}
@@ -4279,7 +4622,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) {
for (Object* current = this;
current != heap->null_value();
current = JSObject::cast(current)->GetPrototype()) {
- JSReceiver::cast(current)->LocalLookup(name, result);
+ JSReceiver::cast(current)->LocalLookup(name, result, false);
if (result->IsFound()) return;
}
result->NotFound();
@@ -4420,7 +4763,9 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name,
// to do a lookup, which seems to be a bit of overkill.
Heap* heap = GetHeap();
bool only_attribute_changes = getter->IsNull() && setter->IsNull();
- if (HasFastProperties() && !only_attribute_changes) {
+ if (HasFastProperties() && !only_attribute_changes &&
+ (map()->NumberOfOwnDescriptors() <
+ DescriptorArray::kMaxNumberOfDescriptors)) {
MaybeObject* getterOk = heap->undefined_value();
if (!getter->IsNull()) {
getterOk = DefineFastAccessor(name, ACCESSOR_GETTER, getter, attributes);
@@ -4550,14 +4895,14 @@ void JSObject::DefineAccessor(Handle<JSObject> object,
object->DefineAccessor(*name, *getter, *setter, attributes));
}
-MaybeObject* JSObject::DefineAccessor(String* name,
- Object* getter,
- Object* setter,
+MaybeObject* JSObject::DefineAccessor(String* name_raw,
+ Object* getter_raw,
+ Object* setter_raw,
PropertyAttributes attributes) {
Isolate* isolate = GetIsolate();
// Check access rights if needed.
if (IsAccessCheckNeeded() &&
- !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
+ !isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
return isolate->heap()->undefined_value();
}
@@ -4567,7 +4912,7 @@ MaybeObject* JSObject::DefineAccessor(String* name,
if (proto->IsNull()) return this;
ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->DefineAccessor(
- name, getter, setter, attributes);
+ name_raw, getter_raw, setter_raw, attributes);
}
// Make sure that the top context does not change when doing callbacks or
@@ -4575,14 +4920,50 @@ MaybeObject* JSObject::DefineAccessor(String* name,
AssertNoContextChange ncc;
// Try to flatten before operating on the string.
- name->TryFlatten();
+ name_raw->TryFlatten();
- if (!CanSetCallback(name)) return isolate->heap()->undefined_value();
+ if (!CanSetCallback(name_raw)) return isolate->heap()->undefined_value();
+
+ // From this point on everything needs to be handlified.
+ HandleScope scope(isolate);
+ Handle<JSObject> self(this);
+ Handle<String> name(name_raw);
+ Handle<Object> getter(getter_raw);
+ Handle<Object> setter(setter_raw);
uint32_t index = 0;
- return name->AsArrayIndex(&index) ?
- DefineElementAccessor(index, getter, setter, attributes) :
- DefinePropertyAccessor(name, getter, setter, attributes);
+ bool is_element = name->AsArrayIndex(&index);
+
+ Handle<Object> old_value = isolate->factory()->the_hole_value();
+ bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
+ bool preexists = false;
+ if (is_observed) {
+ if (is_element) {
+ preexists = HasLocalElement(index);
+ if (preexists && self->GetLocalElementAccessorPair(index) == NULL) {
+ old_value = Object::GetElement(self, index);
+ }
+ } else {
+ LookupResult lookup(isolate);
+ LocalLookup(*name, &lookup, true);
+ preexists = lookup.IsProperty();
+ if (preexists) old_value = handle(lookup.GetLazyValue(), isolate);
+ }
+ }
+
+ MaybeObject* result = is_element ?
+ self->DefineElementAccessor(index, *getter, *setter, attributes) :
+ self->DefinePropertyAccessor(*name, *getter, *setter, attributes);
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (is_observed) {
+ const char* type = preexists ? "reconfigured" : "new";
+ EnqueueChangeRecord(self, type, name, old_value);
+ }
+
+ return *hresult;
}
@@ -4647,9 +5028,10 @@ MaybeObject* JSObject::DefineFastAccessor(String* name,
if (result.IsFound()) {
Map* target = result.GetTransitionTarget();
- ASSERT(target->instance_descriptors()->number_of_descriptors() ==
- map()->instance_descriptors()->number_of_descriptors());
- ASSERT(target->instance_descriptors()->GetKey(descriptor_number) == name);
+ ASSERT(target->NumberOfOwnDescriptors() ==
+ map()->NumberOfOwnDescriptors());
+ // This works since descriptors are sorted in order of addition.
+ ASSERT(map()->instance_descriptors()->GetKey(descriptor_number) == name);
return TryAccessorTransition(
this, target, descriptor_number, component, accessor, attributes);
}
@@ -4661,7 +5043,8 @@ MaybeObject* JSObject::DefineFastAccessor(String* name,
if (result.IsFound()) {
Map* target = result.GetTransitionTarget();
int descriptor_number = target->LastAdded();
- ASSERT(target->instance_descriptors()->GetKey(descriptor_number) == name);
+ ASSERT(target->instance_descriptors()->GetKey(descriptor_number)
+ ->Equals(name));
return TryAccessorTransition(
this, target, descriptor_number, component, accessor, attributes);
}
@@ -4760,7 +5143,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
} else {
// Lookup the name.
LookupResult result(isolate);
- LocalLookup(name, &result);
+ LocalLookup(name, &result, true);
// ES5 forbids turning a property into an accessor if it's not
// configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5).
if (result.IsFound() && (result.IsReadOnly() || result.IsDontDelete())) {
@@ -4832,8 +5215,9 @@ Object* JSObject::LookupAccessor(String* name, AccessorComponent component) {
Object* JSObject::SlowReverseLookup(Object* value) {
if (HasFastProperties()) {
+ int number_of_own_descriptors = map()->NumberOfOwnDescriptors();
DescriptorArray* descs = map()->instance_descriptors();
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ for (int i = 0; i < number_of_own_descriptors; i++) {
if (descs->GetType(i) == FIELD) {
if (FastPropertyAt(descs->GetFieldIndex(i)) == value) {
return descs->GetKey(i);
@@ -4861,9 +5245,11 @@ MaybeObject* Map::RawCopy(int instance_size) {
result->set_constructor(constructor());
result->set_bit_field(bit_field());
result->set_bit_field2(bit_field2());
- result->set_bit_field3(bit_field3());
- result->SetNumberOfOwnDescriptors(0);
- result->SetEnumLength(kInvalidEnumCache);
+ int new_bit_field3 = bit_field3();
+ new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true);
+ new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0);
+ new_bit_field3 = EnumLengthBits::update(new_bit_field3, kInvalidEnumCache);
+ result->set_bit_field3(new_bit_field3);
return result;
}
@@ -4887,7 +5273,7 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
result->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
result->set_dictionary_map(true);
-#ifdef DEBUG
+#ifdef VERIFY_HEAP
if (FLAG_verify_heap && result->is_shared()) {
result->SharedMapVerify();
}
@@ -4913,22 +5299,100 @@ MaybeObject* Map::CopyDropDescriptors() {
}
+MaybeObject* Map::ShareDescriptor(DescriptorArray* descriptors,
+ Descriptor* descriptor) {
+ // Sanity check. This path is only to be taken if the map owns its descriptor
+ // array, implying that its NumberOfOwnDescriptors equals the number of
+ // descriptors in the descriptor array.
+ ASSERT(NumberOfOwnDescriptors() ==
+ instance_descriptors()->number_of_descriptors());
+ Map* result;
+ MaybeObject* maybe_result = CopyDropDescriptors();
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ String* name = descriptor->GetKey();
+
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions =
+ AddTransition(name, result, SIMPLE_TRANSITION);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+
+ int old_size = descriptors->number_of_descriptors();
+
+ DescriptorArray* new_descriptors;
+
+ if (descriptors->NumberOfSlackDescriptors() > 0) {
+ new_descriptors = descriptors;
+ new_descriptors->Append(descriptor);
+ } else {
+ // Descriptor arrays grow by 50%.
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(
+ old_size, old_size < 4 ? 1 : old_size / 2);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ DescriptorArray::WhitenessWitness witness(new_descriptors);
+
+ // Copy the descriptors, inserting a descriptor.
+ for (int i = 0; i < old_size; ++i) {
+ new_descriptors->CopyFrom(i, descriptors, i, witness);
+ }
+
+ new_descriptors->Append(descriptor, witness);
+
+ if (old_size > 0) {
+ // If the source descriptors had an enum cache we copy it. This ensures
+ // that the maps to which we push the new descriptor array back can rely
+ // on a cache always being available once it is set. If the map has more
+ // enumerated descriptors than available in the original cache, the cache
+ // will be lazily replaced by the extended cache when needed.
+ if (descriptors->HasEnumCache()) {
+ new_descriptors->CopyEnumCacheFrom(descriptors);
+ }
+
+ Map* map;
+ // Replace descriptors by new_descriptors in all maps that share it.
+ for (Object* current = GetBackPointer();
+ !current->IsUndefined();
+ current = map->GetBackPointer()) {
+ map = Map::cast(current);
+ if (map->instance_descriptors() != descriptors) break;
+ map->set_instance_descriptors(new_descriptors);
+ }
+
+ set_instance_descriptors(new_descriptors);
+ }
+ }
+
+ result->SetBackPointer(this);
+ result->InitializeDescriptors(new_descriptors);
+ ASSERT(result->NumberOfOwnDescriptors() == NumberOfOwnDescriptors() + 1);
+
+ set_transitions(transitions);
+ set_owns_descriptors(false);
+
+ return result;
+}
+
+
MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
String* name,
- TransitionFlag flag) {
+ TransitionFlag flag,
+ int descriptor_index) {
+ ASSERT(descriptors->IsSortedNoDuplicates());
+
Map* result;
MaybeObject* maybe_result = CopyDropDescriptors();
if (!maybe_result->To(&result)) return maybe_result;
- if (descriptors->number_of_descriptors() != 0) {
- MaybeObject* maybe_failure = result->SetDescriptors(descriptors);
- if (maybe_failure->IsFailure()) return maybe_failure;
- result->SetNumberOfOwnDescriptors(descriptors->number_of_descriptors());
- }
+ result->InitializeDescriptors(descriptors);
if (flag == INSERT_TRANSITION && CanHaveMoreTransitions()) {
TransitionArray* transitions;
- MaybeObject* maybe_transitions = AddTransition(name, result);
+ SimpleTransitionFlag simple_flag =
+ (descriptor_index == descriptors->number_of_descriptors() - 1)
+ ? SIMPLE_TRANSITION
+ : FULL_TRANSITION;
+ MaybeObject* maybe_transitions = AddTransition(name, result, simple_flag);
if (!maybe_transitions->To(&transitions)) return maybe_transitions;
set_transitions(transitions);
@@ -4940,12 +5404,6 @@ MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) {
- // Create a new free-floating map only if we are not allowed to store it.
- Map* new_map = NULL;
- MaybeObject* maybe_new_map = Copy();
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
- new_map->set_elements_kind(kind);
-
if (flag == INSERT_TRANSITION) {
ASSERT(!HasElementsTransition() ||
((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS ||
@@ -4956,10 +5414,40 @@ MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) {
ASSERT(!IsFastElementsKind(kind) ||
IsMoreGeneralElementsKindTransition(elements_kind(), kind));
ASSERT(kind != elements_kind());
+ }
+
+ bool insert_transition =
+ flag == INSERT_TRANSITION && !HasElementsTransition();
+
+ if (insert_transition && owns_descriptors()) {
+ // In case the map owned its own descriptors, share the descriptors and
+ // transfer ownership to the new map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
MaybeObject* added_elements = set_elements_transition_map(new_map);
if (added_elements->IsFailure()) return added_elements;
+ new_map->set_elements_kind(kind);
+ new_map->InitializeDescriptors(instance_descriptors());
+ new_map->SetBackPointer(this);
+ set_owns_descriptors(false);
+ return new_map;
+ }
+
+ // In case the map did not own its own descriptors, a split is forced by
+ // copying the map; creating a new descriptor array cell.
+ // Create a new free-floating map only if we are not allowed to store it.
+ Map* new_map;
+ MaybeObject* maybe_new_map = Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ new_map->set_elements_kind(kind);
+
+ if (insert_transition) {
+ MaybeObject* added_elements = set_elements_transition_map(new_map);
+ if (added_elements->IsFailure()) return added_elements;
new_map->SetBackPointer(this);
}
@@ -4977,13 +5465,25 @@ MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() {
Map* map = ctor->initial_map();
DescriptorArray* descriptors = map->instance_descriptors();
- return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION);
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors =
+ descriptors->CopyUpTo(number_of_own_descriptors);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0);
}
MaybeObject* Map::Copy() {
DescriptorArray* descriptors = instance_descriptors();
- return CopyReplaceDescriptors(descriptors, NULL, OMIT_TRANSITION);
+ DescriptorArray* new_descriptors;
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ MaybeObject* maybe_descriptors =
+ descriptors->CopyUpTo(number_of_own_descriptors);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ return CopyReplaceDescriptors(new_descriptors, NULL, OMIT_TRANSITION, 0);
}
@@ -4995,28 +5495,39 @@ MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor,
MaybeObject* maybe_failure = descriptor->KeyToSymbol();
if (maybe_failure->IsFailure()) return maybe_failure;
- String* key = descriptor->GetKey();
- ASSERT(descriptors->Search(key) == DescriptorArray::kNotFound);
-
- int old_size = descriptors->number_of_descriptors();
+ int old_size = NumberOfOwnDescriptors();
int new_size = old_size + 1;
+ descriptor->SetEnumerationIndex(new_size);
+
+ if (flag == INSERT_TRANSITION &&
+ owns_descriptors() &&
+ CanHaveMoreTransitions()) {
+ return ShareDescriptor(descriptors, descriptor);
+ }
DescriptorArray* new_descriptors;
- MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size);
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(old_size, 1);
if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
- FixedArray::WhitenessWitness witness(new_descriptors);
+ DescriptorArray::WhitenessWitness witness(new_descriptors);
// Copy the descriptors, inserting a descriptor.
for (int i = 0; i < old_size; ++i) {
new_descriptors->CopyFrom(i, descriptors, i, witness);
}
- new_descriptors->Append(descriptor, witness, old_size);
+ if (old_size != descriptors->number_of_descriptors()) {
+ new_descriptors->SetNumberOfDescriptors(new_size);
+ new_descriptors->Set(old_size, descriptor, witness);
+ new_descriptors->Sort();
+ } else {
+ new_descriptors->Append(descriptor, witness);
+ }
- SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
+ String* key = descriptor->GetKey();
+ int insertion_index = new_descriptors->number_of_descriptors() - 1;
- return CopyReplaceDescriptors(new_descriptors, key, flag);
+ return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index);
}
@@ -5029,21 +5540,38 @@ MaybeObject* Map::CopyInsertDescriptor(Descriptor* descriptor,
if (maybe_result->IsFailure()) return maybe_result;
// We replace the key if it is already present.
- int index = old_descriptors->SearchWithCache(descriptor->GetKey());
+ int index = old_descriptors->SearchWithCache(descriptor->GetKey(), this);
if (index != DescriptorArray::kNotFound) {
- return CopyReplaceDescriptor(descriptor, index, flag);
+ return CopyReplaceDescriptor(old_descriptors, descriptor, index, flag);
}
return CopyAddDescriptor(descriptor, flag);
}
-MaybeObject* Map::CopyReplaceDescriptor(Descriptor* descriptor,
+MaybeObject* DescriptorArray::CopyUpTo(int enumeration_index) {
+ if (enumeration_index == 0) return GetHeap()->empty_descriptor_array();
+
+ int size = enumeration_index;
+
+ DescriptorArray* descriptors;
+ MaybeObject* maybe_descriptors = Allocate(size);
+ if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors;
+ DescriptorArray::WhitenessWitness witness(descriptors);
+
+ for (int i = 0; i < size; ++i) {
+ descriptors->CopyFrom(i, this, i, witness);
+ }
+
+ if (number_of_descriptors() != enumeration_index) descriptors->Sort();
+
+ return descriptors;
+}
+
+
+MaybeObject* Map::CopyReplaceDescriptor(DescriptorArray* descriptors,
+ Descriptor* descriptor,
int insertion_index,
TransitionFlag flag) {
- DescriptorArray* descriptors = instance_descriptors();
- int size = descriptors->number_of_descriptors();
- ASSERT(0 <= insertion_index && insertion_index < size);
-
// Ensure the key is a symbol.
MaybeObject* maybe_failure = descriptor->KeyToSymbol();
if (maybe_failure->IsFailure()) return maybe_failure;
@@ -5051,27 +5579,30 @@ MaybeObject* Map::CopyReplaceDescriptor(Descriptor* descriptor,
String* key = descriptor->GetKey();
ASSERT(key == descriptors->GetKey(insertion_index));
+ int new_size = NumberOfOwnDescriptors();
+ ASSERT(0 <= insertion_index && insertion_index < new_size);
+
+ PropertyDetails details = descriptors->GetDetails(insertion_index);
+ ASSERT_LE(details.descriptor_index(), new_size);
+ descriptor->SetEnumerationIndex(details.descriptor_index());
+
DescriptorArray* new_descriptors;
- MaybeObject* maybe_descriptors = DescriptorArray::Allocate(size);
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size);
if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+ DescriptorArray::WhitenessWitness witness(new_descriptors);
- FixedArray::WhitenessWitness witness(new_descriptors);
-
- // Copy the descriptors, replacing a descriptor.
- for (int index = 0; index < size; ++index) {
- if (index == insertion_index) continue;
- new_descriptors->CopyFrom(index, descriptors, index, witness);
+ for (int i = 0; i < new_size; ++i) {
+ if (i == insertion_index) {
+ new_descriptors->Set(i, descriptor, witness);
+ } else {
+ new_descriptors->CopyFrom(i, descriptors, i, witness);
+ }
}
- PropertyDetails original_details = descriptors->GetDetails(insertion_index);
- descriptor->SetEnumerationIndex(original_details.descriptor_index());
- descriptor->SetSortedKey(original_details.pointer());
-
- new_descriptors->Set(insertion_index, descriptor, witness);
-
- SLOW_ASSERT(new_descriptors->IsSortedNoDuplicates());
+ // Re-sort if descriptors were removed.
+ if (new_size != descriptors->length()) new_descriptors->Sort();
- return CopyReplaceDescriptors(new_descriptors, key, flag);
+ return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index);
}
@@ -5846,39 +6377,39 @@ bool FixedArray::IsEqualTo(FixedArray* other) {
#endif
-MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
+MaybeObject* DescriptorArray::Allocate(int number_of_descriptors, int slack) {
Heap* heap = Isolate::Current()->heap();
// Do not use DescriptorArray::cast on incomplete object.
+ int size = number_of_descriptors + slack;
+ if (size == 0) return heap->empty_descriptor_array();
FixedArray* result;
- if (number_of_descriptors == 0) return heap->empty_descriptor_array();
// Allocate the array of keys.
- MaybeObject* maybe_array =
- heap->AllocateFixedArray(LengthFor(number_of_descriptors));
+ MaybeObject* maybe_array = heap->AllocateFixedArray(LengthFor(size));
if (!maybe_array->To(&result)) return maybe_array;
+ result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors));
result->set(kEnumCacheIndex, Smi::FromInt(0));
return result;
}
+void DescriptorArray::ClearEnumCache() {
+ set(kEnumCacheIndex, Smi::FromInt(0));
+}
+
+
void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
FixedArray* new_cache,
Object* new_index_cache) {
ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
ASSERT(new_index_cache->IsSmi() || new_index_cache->IsFixedArray());
- if (HasEnumCache()) {
- FixedArray::cast(get(kEnumCacheIndex))->
- set(kEnumCacheBridgeCacheIndex, new_cache);
- FixedArray::cast(get(kEnumCacheIndex))->
- set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
- } else {
- if (IsEmpty()) return; // Do nothing for empty descriptor array.
- FixedArray::cast(bridge_storage)->
- set(kEnumCacheBridgeCacheIndex, new_cache);
- FixedArray::cast(bridge_storage)->
- set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
- set(kEnumCacheIndex, bridge_storage);
- }
+ ASSERT(!IsEmpty());
+ ASSERT(!HasEnumCache() || new_cache->length() > GetEnumCache()->length());
+ FixedArray::cast(bridge_storage)->
+ set(kEnumCacheBridgeCacheIndex, new_cache);
+ FixedArray::cast(bridge_storage)->
+ set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
+ set(kEnumCacheIndex, bridge_storage);
}
@@ -5948,6 +6479,7 @@ void DescriptorArray::Sort() {
parent_index = child_index;
}
}
+ ASSERT(IsSortedNoDuplicates());
}
@@ -6025,10 +6557,10 @@ String::FlatContent String::GetFlatContent() {
ASSERT(shape.representation_tag() != kConsStringTag &&
shape.representation_tag() != kSlicedStringTag);
}
- if (shape.encoding_tag() == kAsciiStringTag) {
+ if (shape.encoding_tag() == kOneByteStringTag) {
const char* start;
if (shape.representation_tag() == kSeqStringTag) {
- start = SeqAsciiString::cast(string)->GetChars();
+ start = SeqOneByteString::cast(string)->GetChars();
} else {
start = ExternalAsciiString::cast(string)->GetChars();
}
@@ -6111,7 +6643,7 @@ const uc16* String::GetTwoByteData() {
const uc16* String::GetTwoByteData(unsigned start) {
- ASSERT(!IsAsciiRepresentationUnderneath());
+ ASSERT(!IsOneByteRepresentationUnderneath());
switch (StringShape(this).representation_tag()) {
case kSeqStringTag:
return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
@@ -6192,7 +6724,7 @@ void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
}
-const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock(
+const unibrow::byte* SeqOneByteString::SeqOneByteStringReadBlock(
unsigned* remaining,
unsigned* offset_ptr,
unsigned max_chars) {
@@ -6320,7 +6852,7 @@ void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
}
-void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
+void SeqOneByteString::SeqOneByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
unsigned* offset_ptr,
unsigned max_chars) {
unsigned capacity = rbb->capacity - rbb->cursor;
@@ -6363,9 +6895,9 @@ const unibrow::byte* String::ReadBlock(String* input,
}
switch (StringShape(input).representation_tag()) {
case kSeqStringTag:
- if (input->IsAsciiRepresentation()) {
- SeqAsciiString* str = SeqAsciiString::cast(input);
- return str->SeqAsciiStringReadBlock(&rbb->remaining,
+ if (input->IsOneByteRepresentation()) {
+ SeqOneByteString* str = SeqOneByteString::cast(input);
+ return str->SeqOneByteStringReadBlock(&rbb->remaining,
offset_ptr,
max_chars);
} else {
@@ -6380,7 +6912,7 @@ const unibrow::byte* String::ReadBlock(String* input,
offset_ptr,
max_chars);
case kExternalStringTag:
- if (input->IsAsciiRepresentation()) {
+ if (input->IsOneByteRepresentation()) {
return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
&rbb->remaining,
offset_ptr,
@@ -6494,8 +7026,128 @@ void StringInputBuffer::Seek(unsigned pos) {
}
-void SafeStringInputBuffer::Seek(unsigned pos) {
- Reset(pos, input_);
+String* ConsStringIteratorOp::Operate(ConsString* consString,
+ unsigned* outerOffset, int32_t* typeOut, unsigned* lengthOut) {
+ ASSERT(*lengthOut == (unsigned)consString->length());
+ // Push the root string.
+ PushLeft(consString);
+ root_ = consString;
+ root_type_ = *typeOut;
+ root_length_ = *lengthOut;
+ unsigned targetOffset = *outerOffset;
+ unsigned offset = 0;
+ while (true) {
+ // Loop until the string is found which contains the target offset.
+ String* string = consString->first();
+ unsigned length = string->length();
+ int32_t type;
+ if (targetOffset < offset + length) {
+ // Target offset is in the left branch.
+ // Mark the descent.
+ ClearRightDescent();
+ // Keep going if we're still in a ConString.
+ type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) == kConsStringTag) {
+ consString = ConsString::cast(string);
+ PushLeft(consString);
+ continue;
+ }
+ } else {
+ // Descend right.
+ // Update progress through the string.
+ offset += length;
+ // Keep going if we're still in a ConString.
+ string = consString->second();
+ type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) == kConsStringTag) {
+ consString = ConsString::cast(string);
+ PushRight(consString, type);
+ continue;
+ }
+ // Mark the descent.
+ SetRightDescent();
+ // Need this to be updated for the current string.
+ length = string->length();
+ // Account for the possibility of an empty right leaf.
+ while (length == 0) {
+ bool blewStack;
+ // Need to adjust maximum depth for NextLeaf to work.
+ AdjustMaximumDepth();
+ string = NextLeaf(&blewStack, &type);
+ if (string == NULL) {
+ // Luckily, this case is impossible.
+ ASSERT(!blewStack);
+ return NULL;
+ }
+ length = string->length();
+ }
+ }
+ // Tell the stack we're done decending.
+ AdjustMaximumDepth();
+ ASSERT(length != 0);
+ // Adjust return values and exit.
+ unsigned innerOffset = targetOffset - offset;
+ consumed_ += length - innerOffset;
+ *outerOffset = innerOffset;
+ *typeOut = type;
+ *lengthOut = length;
+ return string;
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+String* ConsStringIteratorOp::NextLeaf(bool* blewStack, int32_t* typeOut) {
+ while (true) {
+ // Tree traversal complete.
+ if (depth_ == 0) {
+ *blewStack = false;
+ return NULL;
+ }
+ // We've lost track of higher nodes.
+ if (maximum_depth_ - depth_ == kStackSize) {
+ *blewStack = true;
+ return NULL;
+ }
+ // Check if we're done with this level.
+ bool haveAlreadyReadRight = trace_ & MaskForDepth(depth_ - 1);
+ if (haveAlreadyReadRight) {
+ Pop();
+ continue;
+ }
+ // Go right.
+ ConsString* consString = frames_[OffsetForDepth(depth_ - 1)];
+ String* string = consString->second();
+ int32_t type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) != kConsStringTag) {
+ // Don't need to mark the descent here.
+ // Pop stack so next iteration is in correct place.
+ Pop();
+ *typeOut = type;
+ return string;
+ }
+ // No need to mark the descent.
+ consString = ConsString::cast(string);
+ PushRight(consString, type);
+ // Need to traverse all the way left.
+ while (true) {
+ // Continue left.
+ // Update marker.
+ ClearRightDescent();
+ string = consString->first();
+ type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) != kConsStringTag) {
+ AdjustMaximumDepth();
+ *typeOut = type;
+ return string;
+ }
+ consString = ConsString::cast(string);
+ PushLeft(consString);
+ }
+ }
+ UNREACHABLE();
+ return NULL;
}
@@ -6512,8 +7164,8 @@ void String::ReadBlockIntoBuffer(String* input,
switch (StringShape(input).representation_tag()) {
case kSeqStringTag:
- if (input->IsAsciiRepresentation()) {
- SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb,
+ if (input->IsOneByteRepresentation()) {
+ SeqOneByteString::cast(input)->SeqOneByteStringReadBlockIntoBuffer(rbb,
offset_ptr,
max_chars);
return;
@@ -6529,7 +7181,7 @@ void String::ReadBlockIntoBuffer(String* input,
max_chars);
return;
case kExternalStringTag:
- if (input->IsAsciiRepresentation()) {
+ if (input->IsOneByteRepresentation()) {
ExternalAsciiString::cast(input)->
ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
} else {
@@ -6714,7 +7366,7 @@ void String::WriteToFlat(String* src,
while (true) {
ASSERT(0 <= from && from <= to && to <= source->length());
switch (StringShape(source).full_representation_tag()) {
- case kAsciiStringTag | kExternalStringTag: {
+ case kOneByteStringTag | kExternalStringTag: {
CopyChars(sink,
ExternalAsciiString::cast(source)->GetChars() + from,
to - from);
@@ -6728,9 +7380,9 @@ void String::WriteToFlat(String* src,
to - from);
return;
}
- case kAsciiStringTag | kSeqStringTag: {
+ case kOneByteStringTag | kSeqStringTag: {
CopyChars(sink,
- SeqAsciiString::cast(source)->GetChars() + from,
+ SeqOneByteString::cast(source)->GetChars() + from,
to - from);
return;
}
@@ -6740,7 +7392,7 @@ void String::WriteToFlat(String* src,
to - from);
return;
}
- case kAsciiStringTag | kConsStringTag:
+ case kOneByteStringTag | kConsStringTag:
case kTwoByteStringTag | kConsStringTag: {
ConsString* cons_string = ConsString::cast(source);
String* first = cons_string->first();
@@ -6765,9 +7417,9 @@ void String::WriteToFlat(String* src,
// common case of sequential ascii right child.
if (to - boundary == 1) {
sink[boundary - from] = static_cast<sinkchar>(second->Get(0));
- } else if (second->IsSeqAsciiString()) {
+ } else if (second->IsSeqOneByteString()) {
CopyChars(sink + boundary - from,
- SeqAsciiString::cast(second)->GetChars(),
+ SeqOneByteString::cast(second)->GetChars(),
to - boundary);
} else {
WriteToFlat(second,
@@ -6781,7 +7433,7 @@ void String::WriteToFlat(String* src,
}
break;
}
- case kAsciiStringTag | kSlicedStringTag:
+ case kOneByteStringTag | kSlicedStringTag:
case kTwoByteStringTag | kSlicedStringTag: {
SlicedString* slice = SlicedString::cast(source);
unsigned offset = slice->offset();
@@ -6906,8 +7558,8 @@ bool String::SlowEquals(String* other) {
if (StringShape(lhs).IsSequentialAscii() &&
StringShape(rhs).IsSequentialAscii()) {
- const char* str1 = SeqAsciiString::cast(lhs)->GetChars();
- const char* str2 = SeqAsciiString::cast(rhs)->GetChars();
+ const char* str1 = SeqOneByteString::cast(lhs)->GetChars();
+ const char* str2 = SeqOneByteString::cast(rhs)->GetChars();
return CompareRawStringContents(Vector<const char>(str1, len),
Vector<const char>(str2, len));
}
@@ -7035,7 +7687,7 @@ uint32_t String::ComputeAndSetHash() {
// Compute the hash code.
uint32_t field = 0;
if (StringShape(this).IsSequentialAscii()) {
- field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(),
+ field = HashSequentialString(SeqOneByteString::cast(this)->GetChars(),
len,
GetHeap()->HashSeed());
} else if (StringShape(this).IsSequentialTwoByte()) {
@@ -7103,6 +7755,36 @@ bool String::SlowAsArrayIndex(uint32_t* index) {
}
+String* SeqString::Truncate(int new_length) {
+ Heap* heap = GetHeap();
+ if (new_length <= 0) return heap->empty_string();
+
+ int string_size, allocated_string_size;
+ int old_length = length();
+ if (old_length <= new_length) return this;
+
+ if (IsSeqOneByteString()) {
+ allocated_string_size = SeqOneByteString::SizeFor(old_length);
+ string_size = SeqOneByteString::SizeFor(new_length);
+ } else {
+ allocated_string_size = SeqTwoByteString::SizeFor(old_length);
+ string_size = SeqTwoByteString::SizeFor(new_length);
+ }
+
+ int delta = allocated_string_size - string_size;
+ set_length(new_length);
+
+ // String sizes are pointer size aligned, so that we can use filler objects
+ // that are a multiple of pointer size.
+ Address end_of_string = address() + string_size;
+ heap->CreateFillerObjectAt(end_of_string, delta);
+ if (Marking::IsBlack(Marking::MarkBitFrom(this))) {
+ MemoryChunk::IncrementLiveBytesFromMutator(address(), -delta);
+ }
+ return this;
+}
+
+
uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
// For array indexes mix the length into the hash as an array index could
// be zero.
@@ -7138,7 +7820,6 @@ void StringHasher::AddSurrogatePairNoIndex(uc32 c) {
uint32_t StringHasher::GetHashField() {
- ASSERT(is_valid());
if (length_ <= String::kMaxHashCalcLength) {
if (is_array_index()) {
return MakeArrayIndexHash(array_index(), length_);
@@ -7193,13 +7874,46 @@ void String::PrintOn(FILE* file) {
}
+static void TrimEnumCache(Heap* heap, Map* map, DescriptorArray* descriptors) {
+ int live_enum = map->EnumLength();
+ if (live_enum == Map::kInvalidEnumCache) {
+ live_enum = map->NumberOfDescribedProperties(OWN_DESCRIPTORS, DONT_ENUM);
+ }
+ if (live_enum == 0) return descriptors->ClearEnumCache();
+
+ FixedArray* enum_cache = descriptors->GetEnumCache();
+
+ int to_trim = enum_cache->length() - live_enum;
+ if (to_trim <= 0) return;
+ RightTrimFixedArray<FROM_GC>(heap, descriptors->GetEnumCache(), to_trim);
+
+ if (!descriptors->HasEnumIndicesCache()) return;
+ FixedArray* enum_indices_cache = descriptors->GetEnumIndicesCache();
+ RightTrimFixedArray<FROM_GC>(heap, enum_indices_cache, to_trim);
+}
+
+
+static void TrimDescriptorArray(Heap* heap,
+ Map* map,
+ DescriptorArray* descriptors,
+ int number_of_own_descriptors) {
+ int number_of_descriptors = descriptors->number_of_descriptors();
+ int to_trim = number_of_descriptors - number_of_own_descriptors;
+ if (to_trim <= 0) return;
+
+ RightTrimFixedArray<FROM_GC>(heap, descriptors, to_trim);
+ descriptors->SetNumberOfDescriptors(number_of_own_descriptors);
+
+ if (descriptors->HasEnumCache()) TrimEnumCache(heap, map, descriptors);
+ descriptors->Sort();
+}
+
+
// Clear a possible back pointer in case the transition leads to a dead map.
// Return true in case a back pointer has been cleared and false otherwise.
-static bool ClearBackPointer(Heap* heap, Object* target) {
- ASSERT(target->IsMap());
- Map* map = Map::cast(target);
- if (Marking::MarkBitFrom(map).Get()) return false;
- map->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
+static bool ClearBackPointer(Heap* heap, Map* target) {
+ if (Marking::MarkBitFrom(target).Get()) return false;
+ target->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
return true;
}
@@ -7218,9 +7932,17 @@ void Map::ClearNonLiveTransitions(Heap* heap) {
int transition_index = 0;
+ DescriptorArray* descriptors = instance_descriptors();
+ bool descriptors_owner_died = false;
+
// Compact all live descriptors to the left.
for (int i = 0; i < t->number_of_transitions(); ++i) {
- if (!ClearBackPointer(heap, t->GetTarget(i))) {
+ Map* target = t->GetTarget(i);
+ if (ClearBackPointer(heap, target)) {
+ if (target->instance_descriptors() == descriptors) {
+ descriptors_owner_died = true;
+ }
+ } else {
if (i != transition_index) {
String* key = t->GetKey(i);
t->SetKey(transition_index, key);
@@ -7235,6 +7957,9 @@ void Map::ClearNonLiveTransitions(Heap* heap) {
if (t->HasElementsTransition() &&
ClearBackPointer(heap, t->elements_transition())) {
+ if (t->elements_transition()->instance_descriptors() == descriptors) {
+ descriptors_owner_died = true;
+ }
t->ClearElementsTransition();
} else {
// If there are no transitions to be cleared, return.
@@ -7243,19 +7968,21 @@ void Map::ClearNonLiveTransitions(Heap* heap) {
if (transition_index == t->number_of_transitions()) return;
}
- // If the final transition array does not contain any live transitions, remove
- // the transition array from the map.
- if (transition_index == 0 &&
- !t->HasElementsTransition() &&
- !t->HasPrototypeTransitions() &&
- t->descriptors()->IsEmpty()) {
- return ClearTransitions(heap);
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+
+ if (descriptors_owner_died) {
+ if (number_of_own_descriptors > 0) {
+ TrimDescriptorArray(heap, this, descriptors, number_of_own_descriptors);
+ ASSERT(descriptors->number_of_descriptors() == number_of_own_descriptors);
+ } else {
+ ASSERT(descriptors == GetHeap()->empty_descriptor_array());
+ }
}
int trim = t->number_of_transitions() - transition_index;
if (trim > 0) {
- RightTrimFixedArray<FROM_GC>(
- heap, t, trim * TransitionArray::kTransitionSize);
+ RightTrimFixedArray<FROM_GC>(heap, t, t->IsSimpleTransition()
+ ? trim : trim * TransitionArray::kTransitionSize);
}
}
@@ -7289,6 +8016,7 @@ bool Map::EquivalentToForNormalization(Map* other,
instance_type() == other->instance_type() &&
bit_field() == other->bit_field() &&
bit_field2() == other->bit_field2() &&
+ is_observed() == other->is_observed() &&
function_with_prototype() == other->function_with_prototype();
}
@@ -7469,6 +8197,35 @@ MaybeObject* JSObject::OptimizeAsPrototype() {
}
+MUST_USE_RESULT static MaybeObject* CacheInitialJSArrayMaps(
+ Context* native_context, Map* initial_map) {
+ // Replace all of the cached initial array maps in the native context with
+ // the appropriate transitioned elements kind maps.
+ Heap* heap = native_context->GetHeap();
+ MaybeObject* maybe_maps =
+ heap->AllocateFixedArrayWithHoles(kElementsKindCount);
+ FixedArray* maps;
+ if (!maybe_maps->To(&maps)) return maybe_maps;
+
+ Map* current_map = initial_map;
+ ElementsKind kind = current_map->elements_kind();
+ ASSERT(kind == GetInitialFastElementsKind());
+ maps->set(kind, current_map);
+ for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
+ i < kFastElementsKindCount; ++i) {
+ Map* new_map;
+ ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
+ MaybeObject* maybe_new_map =
+ current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ maps->set(next_kind, new_map);
+ current_map = new_map;
+ }
+ native_context->set_js_array_maps(maps);
+ return initial_map;
+}
+
+
MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
ASSERT(value->IsJSReceiver());
Heap* heap = GetHeap();
@@ -7483,14 +8240,29 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
// Now some logic for the maps of the objects that are created by using this
// function as a constructor.
if (has_initial_map()) {
- // If the function has allocated the initial map
- // replace it with a copy containing the new prototype.
+ // If the function has allocated the initial map replace it with a
+ // copy containing the new prototype. Also complete any in-object
+ // slack tracking that is in progress at this point because it is
+ // still tracking the old copy.
+ if (shared()->IsInobjectSlackTrackingInProgress()) {
+ shared()->CompleteInobjectSlackTracking();
+ }
Map* new_map;
- MaybeObject* maybe_new_map = initial_map()->Copy();
- if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ MaybeObject* maybe_object = initial_map()->Copy();
+ if (!maybe_object->To(&new_map)) return maybe_object;
new_map->set_prototype(value);
- MaybeObject* maybe_object = set_initial_map_and_cache_transitions(new_map);
- if (maybe_object->IsFailure()) return maybe_object;
+
+ // If the function is used as the global Array function, cache the
+ // initial map (and transitioned versions) in the native context.
+ Context* native_context = context()->native_context();
+ Object* array_function = native_context->get(Context::ARRAY_FUNCTION_INDEX);
+ if (array_function->IsJSFunction() &&
+ this == JSFunction::cast(array_function)) {
+ MaybeObject* ok = CacheInitialJSArrayMaps(native_context, new_map);
+ if (ok->IsFailure()) return ok;
+ }
+
+ set_initial_map(new_map);
} else {
// Put the value in the initial map field until an initial map is
// needed. At that point, a new initial map is created and the
@@ -7875,7 +8647,7 @@ void SharedFunctionInfo::DetachInitialMap() {
// constructor is called. The countdown will continue and (possibly after
// several more GCs) CompleteInobjectSlackTracking will eventually be called.
Heap* heap = map->GetHeap();
- set_initial_map(heap->raw_unchecked_undefined_value());
+ set_initial_map(heap->undefined_value());
Builtins* builtins = heap->isolate()->builtins();
ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown),
*RawField(this, kConstructStubOffset));
@@ -8003,6 +8775,15 @@ void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
}
+void ObjectVisitor::VisitCodeAgeSequence(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsCodeAgeSequence(rinfo->rmode()));
+ Object* stub = rinfo->code_age_stub();
+ if (stub) {
+ VisitPointer(&stub);
+ }
+}
+
+
void ObjectVisitor::VisitCodeEntry(Address entry_address) {
Object* code = Code::GetObjectFromEntryAddress(entry_address);
Object* old_code = code;
@@ -8197,6 +8978,7 @@ void Code::ClearInlineCaches() {
void Code::ClearTypeFeedbackCells(Heap* heap) {
+ if (kind() != FUNCTION) return;
Object* raw_info = type_feedback_info();
if (raw_info->IsTypeFeedbackInfo()) {
TypeFeedbackCells* type_feedback_cells =
@@ -8211,7 +8993,92 @@ void Code::ClearTypeFeedbackCells(Heap* heap) {
bool Code::allowed_in_shared_map_code_cache() {
return is_keyed_load_stub() || is_keyed_store_stub() ||
- (is_compare_ic_stub() && compare_state() == CompareIC::KNOWN_OBJECTS);
+ (is_compare_ic_stub() &&
+ ICCompareStub::CompareState(stub_info()) == CompareIC::KNOWN_OBJECTS);
+}
+
+
+void Code::MakeCodeAgeSequenceYoung(byte* sequence) {
+ PatchPlatformCodeAge(sequence, kNoAge, NO_MARKING_PARITY);
+}
+
+
+void Code::MakeOlder(MarkingParity current_parity) {
+ byte* sequence = FindCodeAgeSequence();
+ if (sequence != NULL) {
+ Age age;
+ MarkingParity code_parity;
+ GetCodeAgeAndParity(sequence, &age, &code_parity);
+ if (age != kLastCodeAge && code_parity != current_parity) {
+ PatchPlatformCodeAge(sequence, static_cast<Age>(age + 1),
+ current_parity);
+ }
+ }
+}
+
+
+bool Code::IsOld() {
+ byte* sequence = FindCodeAgeSequence();
+ if (sequence == NULL) return false;
+ Age age;
+ MarkingParity parity;
+ GetCodeAgeAndParity(sequence, &age, &parity);
+ return age >= kSexagenarianCodeAge;
+}
+
+
+byte* Code::FindCodeAgeSequence() {
+ return FLAG_age_code &&
+ prologue_offset() != kPrologueOffsetNotSet &&
+ (kind() == OPTIMIZED_FUNCTION ||
+ (kind() == FUNCTION && !has_debug_break_slots()))
+ ? instruction_start() + prologue_offset()
+ : NULL;
+}
+
+
+void Code::GetCodeAgeAndParity(Code* code, Age* age,
+ MarkingParity* parity) {
+ Isolate* isolate = Isolate::Current();
+ Builtins* builtins = isolate->builtins();
+ Code* stub = NULL;
+#define HANDLE_CODE_AGE(AGE) \
+ stub = *builtins->Make##AGE##CodeYoungAgainEvenMarking(); \
+ if (code == stub) { \
+ *age = k##AGE##CodeAge; \
+ *parity = EVEN_MARKING_PARITY; \
+ return; \
+ } \
+ stub = *builtins->Make##AGE##CodeYoungAgainOddMarking(); \
+ if (code == stub) { \
+ *age = k##AGE##CodeAge; \
+ *parity = ODD_MARKING_PARITY; \
+ return; \
+ }
+ CODE_AGE_LIST(HANDLE_CODE_AGE)
+#undef HANDLE_CODE_AGE
+ UNREACHABLE();
+}
+
+
+Code* Code::GetCodeAgeStub(Age age, MarkingParity parity) {
+ Isolate* isolate = Isolate::Current();
+ Builtins* builtins = isolate->builtins();
+ switch (age) {
+#define HANDLE_CODE_AGE(AGE) \
+ case k##AGE##CodeAge: { \
+ Code* stub = parity == EVEN_MARKING_PARITY \
+ ? *builtins->Make##AGE##CodeYoungAgainEvenMarking() \
+ : *builtins->Make##AGE##CodeYoungAgainOddMarking(); \
+ return stub; \
+ }
+ CODE_AGE_LIST(HANDLE_CODE_AGE)
+#undef HANDLE_CODE_AGE
+ default:
+ UNREACHABLE();
+ break;
+ }
+ return NULL;
}
@@ -8473,11 +9340,15 @@ void Code::Disassemble(const char* name, FILE* out) {
PrintF(out, "argc = %d\n", arguments_count());
}
if (is_compare_ic_stub()) {
- CompareIC::State state = CompareIC::ComputeState(this);
- PrintF(out, "compare_state = %s\n", CompareIC::GetStateName(state));
- }
- if (is_compare_ic_stub() && major_key() == CodeStub::CompareIC) {
- Token::Value op = CompareIC::ComputeOperation(this);
+ ASSERT(major_key() == CodeStub::CompareIC);
+ CompareIC::State left_state, right_state, handler_state;
+ Token::Value op;
+ ICCompareStub::DecodeMinorKey(stub_info(), &left_state, &right_state,
+ &handler_state, &op);
+ PrintF(out, "compare_state = %s*%s -> %s\n",
+ CompareIC::GetStateName(left_state),
+ CompareIC::GetStateName(right_state),
+ CompareIC::GetStateName(handler_state));
PrintF(out, "compare_operation = %s\n", Token::Name(op));
}
}
@@ -8523,8 +9394,6 @@ void Code::Disassemble(const char* name, FILE* out) {
PrintF(out, "\n");
}
PrintF(out, "\n");
- // Just print if type feedback info is ever used for optimized code.
- ASSERT(type_feedback_info()->IsUndefined());
} else if (kind() == FUNCTION) {
unsigned offset = stack_check_table_offset();
// If there is no stack check table, the "table start" will at or after
@@ -8566,9 +9435,8 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(
// Allocate a new fast elements backing store.
FixedArray* new_elements;
- { MaybeObject* maybe = heap->AllocateFixedArrayWithHoles(capacity);
- if (!maybe->To(&new_elements)) return maybe;
- }
+ MaybeObject* maybe = heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe->To(&new_elements)) return maybe;
ElementsKind elements_kind = GetElementsKind();
ElementsKind new_elements_kind;
@@ -8592,10 +9460,10 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(
}
FixedArrayBase* old_elements = elements();
ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind);
- { MaybeObject* maybe_obj =
- accessor->CopyElements(this, new_elements, new_elements_kind);
- if (maybe_obj->IsFailure()) return maybe_obj;
- }
+ MaybeObject* maybe_obj =
+ accessor->CopyElements(this, new_elements, new_elements_kind);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+
if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) {
Map* new_map = map();
if (new_elements_kind != elements_kind) {
@@ -8701,7 +9569,53 @@ void JSArray::Expand(int required_size) {
MaybeObject* JSArray::SetElementsLength(Object* len) {
// We should never end in here with a pixel or external array.
ASSERT(AllowsSetElementsLength());
- return GetElementsAccessor()->SetLength(this, len);
+ if (!(FLAG_harmony_observation && map()->is_observed()))
+ return GetElementsAccessor()->SetLength(this, len);
+
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSArray> self(this);
+ List<Handle<String> > indices;
+ List<Handle<Object> > old_values;
+ Handle<Object> old_length_handle(self->length());
+ Handle<Object> new_length_handle(len);
+ uint32_t old_length = 0;
+ CHECK(old_length_handle->ToArrayIndex(&old_length));
+ uint32_t new_length = 0;
+ if (!new_length_handle->ToArrayIndex(&new_length))
+ return Failure::InternalError();
+
+ // TODO(adamk): This loop can be very slow for arrays in dictionary mode.
+ // Find another way to iterate over arrays with dictionary elements.
+ for (uint32_t i = old_length - 1; i + 1 > new_length; --i) {
+ PropertyAttributes attributes = self->GetLocalElementAttribute(i);
+ if (attributes == ABSENT) continue;
+ // A non-configurable property will cause the truncation operation to
+ // stop at this index.
+ if (attributes == DONT_DELETE) break;
+ old_values.Add(
+ self->GetLocalElementAccessorPair(i) == NULL
+ ? Object::GetElement(self, i)
+ : Handle<Object>::cast(isolate->factory()->the_hole_value()));
+ indices.Add(isolate->factory()->Uint32ToString(i));
+ }
+
+ MaybeObject* result =
+ self->GetElementsAccessor()->SetLength(*self, *new_length_handle);
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ CHECK(self->length()->ToArrayIndex(&new_length));
+ if (old_length != new_length) {
+ for (int i = 0; i < indices.length(); ++i) {
+ JSObject::EnqueueChangeRecord(
+ self, "deleted", indices[i], old_values[i]);
+ }
+ JSObject::EnqueueChangeRecord(
+ self, "updated", isolate->factory()->length_symbol(),
+ old_length_handle);
+ }
+ return *hresult;
}
@@ -8765,6 +9679,22 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
}
+void Map::ZapTransitions() {
+ TransitionArray* transition_array = transitions();
+ MemsetPointer(transition_array->data_start(),
+ GetHeap()->the_hole_value(),
+ transition_array->length());
+}
+
+
+void Map::ZapPrototypeTransitions() {
+ FixedArray* proto_transitions = GetPrototypeTransitions();
+ MemsetPointer(proto_transitions->data_start(),
+ GetHeap()->the_hole_value(),
+ proto_transitions->length());
+}
+
+
MaybeObject* JSReceiver::SetPrototype(Object* value,
bool skip_hidden_prototypes) {
#ifdef DEBUG
@@ -8862,203 +9792,51 @@ MaybeObject* JSObject::EnsureCanContainElements(Arguments* args,
}
-bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) {
- Isolate* isolate = GetIsolate();
- // Make sure that the top context does not change when doing
- // callbacks or interceptor calls.
- AssertNoContextChange ncc;
- HandleScope scope(isolate);
- Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
- Handle<JSReceiver> receiver_handle(receiver);
- Handle<JSObject> holder_handle(this);
- CustomArguments args(isolate, interceptor->data(), receiver, this);
- v8::AccessorInfo info(args.end());
- if (!interceptor->query()->IsUndefined()) {
- v8::IndexedPropertyQuery query =
- v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
- LOG(isolate,
- ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
- v8::Handle<v8::Integer> result;
- {
- // Leaving JavaScript.
- VMState state(isolate, EXTERNAL);
- result = query(index, info);
- }
- if (!result.IsEmpty()) {
- ASSERT(result->IsInt32());
- return true; // absence of property is signaled by empty handle.
- }
- } else if (!interceptor->getter()->IsUndefined()) {
- v8::IndexedPropertyGetter getter =
- v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
- LOG(isolate,
- ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index));
- v8::Handle<v8::Value> result;
- {
- // Leaving JavaScript.
- VMState state(isolate, EXTERNAL);
- result = getter(index, info);
- }
- if (!result.IsEmpty()) return true;
+PropertyType JSObject::GetLocalPropertyType(String* name) {
+ uint32_t index = 0;
+ if (name->AsArrayIndex(&index)) {
+ return GetLocalElementType(index);
}
+ LookupResult lookup(GetIsolate());
+ LocalLookup(name, &lookup, true);
+ return lookup.type();
+}
- if (holder_handle->GetElementsAccessor()->HasElement(
- *receiver_handle, *holder_handle, index)) {
- return true;
- }
- if (holder_handle->IsStringObjectWithCharacterAt(index)) return true;
- Object* pt = holder_handle->GetPrototype();
- if (pt->IsJSProxy()) {
- // We need to follow the spec and simulate a call to [[GetOwnProperty]].
- return JSProxy::cast(pt)->GetElementAttributeWithHandler(
- receiver, index) != ABSENT;
- }
- if (pt->IsNull()) return false;
- return JSObject::cast(pt)->HasElementWithReceiver(*receiver_handle, index);
+PropertyType JSObject::GetLocalElementType(uint32_t index) {
+ return GetElementsAccessor()->GetType(this, this, index);
}
-JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
- // Check access rights if needed.
- if (IsAccessCheckNeeded()) {
- Heap* heap = GetHeap();
- if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return UNDEFINED_ELEMENT;
- }
- }
-
- if (IsJSGlobalProxy()) {
- Object* proto = GetPrototype();
- if (proto->IsNull()) return UNDEFINED_ELEMENT;
- ASSERT(proto->IsJSGlobalObject());
- return JSObject::cast(proto)->HasLocalElement(index);
+AccessorPair* JSObject::GetLocalPropertyAccessorPair(String* name) {
+ uint32_t index = 0;
+ if (name->AsArrayIndex(&index)) {
+ return GetLocalElementAccessorPair(index);
}
- // Check for lookup interceptor
- if (HasIndexedInterceptor()) {
- return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
- : UNDEFINED_ELEMENT;
- }
+ LookupResult lookup(GetIsolate());
+ LocalLookupRealNamedProperty(name, &lookup);
- // Handle [] on String objects.
- if (this->IsStringObjectWithCharacterAt(index)) {
- return STRING_CHARACTER_ELEMENT;
- }
-
- switch (GetElementsKind()) {
- case FAST_SMI_ELEMENTS:
- case FAST_ELEMENTS:
- case FAST_HOLEY_SMI_ELEMENTS:
- case FAST_HOLEY_ELEMENTS: {
- uint32_t length = IsJSArray() ?
- static_cast<uint32_t>
- (Smi::cast(JSArray::cast(this)->length())->value()) :
- static_cast<uint32_t>(FixedArray::cast(elements())->length());
- if ((index < length) &&
- !FixedArray::cast(elements())->get(index)->IsTheHole()) {
- return FAST_ELEMENT;
- }
- break;
- }
- case FAST_DOUBLE_ELEMENTS:
- case FAST_HOLEY_DOUBLE_ELEMENTS: {
- uint32_t length = IsJSArray() ?
- static_cast<uint32_t>
- (Smi::cast(JSArray::cast(this)->length())->value()) :
- static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
- if ((index < length) &&
- !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
- return FAST_ELEMENT;
- }
- break;
- }
- case EXTERNAL_PIXEL_ELEMENTS: {
- ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
- if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
- break;
- }
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS: {
- ExternalArray* array = ExternalArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
- break;
- }
- case DICTIONARY_ELEMENTS: {
- if (element_dictionary()->FindEntry(index) !=
- SeededNumberDictionary::kNotFound) {
- return DICTIONARY_ELEMENT;
- }
- break;
- }
- case NON_STRICT_ARGUMENTS_ELEMENTS: {
- // Aliased parameters and non-aliased elements in a fast backing store
- // behave as FAST_ELEMENT. Non-aliased elements in a dictionary
- // backing store behave as DICTIONARY_ELEMENT.
- FixedArray* parameter_map = FixedArray::cast(elements());
- uint32_t length = parameter_map->length();
- Object* probe =
- index < (length - 2) ? parameter_map->get(index + 2) : NULL;
- if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
- // If not aliased, check the arguments.
- FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
- if (arguments->IsDictionary()) {
- SeededNumberDictionary* dictionary =
- SeededNumberDictionary::cast(arguments);
- if (dictionary->FindEntry(index) != SeededNumberDictionary::kNotFound) {
- return DICTIONARY_ELEMENT;
- }
- } else {
- length = arguments->length();
- probe = (index < length) ? arguments->get(index) : NULL;
- if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT;
- }
- break;
- }
+ if (lookup.IsPropertyCallbacks() &&
+ lookup.GetCallbackObject()->IsAccessorPair()) {
+ return AccessorPair::cast(lookup.GetCallbackObject());
}
-
- return UNDEFINED_ELEMENT;
+ return NULL;
}
-bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) {
- // Check access rights if needed.
- if (IsAccessCheckNeeded()) {
- Heap* heap = GetHeap();
- if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
- return false;
- }
- }
-
- // Check for lookup interceptor
- if (HasIndexedInterceptor()) {
- return HasElementWithInterceptor(receiver, index);
- }
-
- ElementsAccessor* accessor = GetElementsAccessor();
- if (accessor->HasElement(receiver, this, index)) {
- return true;
+AccessorPair* JSObject::GetLocalElementAccessorPair(uint32_t index) {
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return NULL;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->GetLocalElementAccessorPair(index);
}
- // Handle [] on String objects.
- if (this->IsStringObjectWithCharacterAt(index)) return true;
+ // Check for lookup interceptor.
+ if (HasIndexedInterceptor()) return NULL;
- Object* pt = GetPrototype();
- if (pt->IsNull()) return false;
- if (pt->IsJSProxy()) {
- // We need to follow the spec and simulate a call to [[GetOwnProperty]].
- return JSProxy::cast(pt)->GetElementAttributeWithHandler(
- receiver, index) != ABSENT;
- }
- return JSObject::cast(pt)->HasElementWithReceiver(receiver, index);
+ return GetElementsAccessor()->GetAccessorPair(this, this, index);
}
@@ -9367,7 +10145,7 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
- Object* value,
+ Object* value_raw,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
bool check_prototype,
@@ -9375,24 +10153,23 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
Isolate* isolate = GetIsolate();
Heap* heap = isolate->heap();
+ Handle<JSObject> self(this);
+ Handle<Object> value(value_raw);
// Insert element in the dictionary.
- FixedArray* elements = FixedArray::cast(this->elements());
+ Handle<FixedArray> elements(FixedArray::cast(this->elements()));
bool is_arguments =
(elements->map() == heap->non_strict_arguments_elements_map());
- SeededNumberDictionary* dictionary = NULL;
- if (is_arguments) {
- dictionary = SeededNumberDictionary::cast(elements->get(1));
- } else {
- dictionary = SeededNumberDictionary::cast(elements);
- }
+ Handle<SeededNumberDictionary> dictionary(is_arguments
+ ? SeededNumberDictionary::cast(elements->get(1))
+ : SeededNumberDictionary::cast(*elements));
int entry = dictionary->FindEntry(index);
if (entry != SeededNumberDictionary::kNotFound) {
Object* element = dictionary->ValueAt(entry);
PropertyDetails details = dictionary->DetailsAt(entry);
if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) {
- return SetElementWithCallback(element, index, value, this, strict_mode);
+ return SetElementWithCallback(element, index, *value, this, strict_mode);
} else {
dictionary->UpdateMaxNumberKey(index);
// If a value has not been initialized we allow writing to it even if it
@@ -9421,24 +10198,24 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
Context* context = Context::cast(elements->get(0));
int context_index = entry->aliased_context_slot();
ASSERT(!context->get(context_index)->IsTheHole());
- context->set(context_index, value);
+ context->set(context_index, *value);
// For elements that are still writable we keep slow aliasing.
- if (!details.IsReadOnly()) value = element;
+ if (!details.IsReadOnly()) value = handle(element, isolate);
}
- dictionary->ValueAtPut(entry, value);
+ dictionary->ValueAtPut(entry, *value);
}
} else {
// Index not already used. Look for an accessor in the prototype chain.
+ // Can cause GC!
if (check_prototype) {
bool found;
- MaybeObject* result =
- SetElementWithCallbackSetterInPrototypes(
- index, value, &found, strict_mode);
+ MaybeObject* result = SetElementWithCallbackSetterInPrototypes(
+ index, *value, &found, strict_mode);
if (found) return result;
}
// When we set the is_extensible flag to false we always force the
// element into dictionary mode (and force them to stay there).
- if (!map()->is_extensible()) {
+ if (!self->map()->is_extensible()) {
if (strict_mode == kNonStrictMode) {
return isolate->heap()->undefined_value();
} else {
@@ -9453,30 +10230,31 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
}
FixedArrayBase* new_dictionary;
PropertyDetails details = PropertyDetails(attributes, NORMAL);
- MaybeObject* maybe = dictionary->AddNumberEntry(index, value, details);
+ MaybeObject* maybe = dictionary->AddNumberEntry(index, *value, details);
if (!maybe->To(&new_dictionary)) return maybe;
- if (dictionary != SeededNumberDictionary::cast(new_dictionary)) {
+ if (*dictionary != SeededNumberDictionary::cast(new_dictionary)) {
if (is_arguments) {
elements->set(1, new_dictionary);
} else {
- set_elements(new_dictionary);
+ self->set_elements(new_dictionary);
}
- dictionary = SeededNumberDictionary::cast(new_dictionary);
+ dictionary =
+ handle(SeededNumberDictionary::cast(new_dictionary), isolate);
}
}
// Update the array length if this JSObject is an array.
- if (IsJSArray()) {
+ if (self->IsJSArray()) {
MaybeObject* result =
- JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value);
+ JSArray::cast(*self)->JSArrayUpdateLengthFromIndex(index, *value);
if (result->IsFailure()) return result;
}
// Attempt to put this object back in fast case.
- if (ShouldConvertToFastElements()) {
+ if (self->ShouldConvertToFastElements()) {
uint32_t new_length = 0;
- if (IsJSArray()) {
- CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length));
+ if (self->IsJSArray()) {
+ CHECK(JSArray::cast(*self)->length()->ToArrayIndex(&new_length));
} else {
new_length = dictionary->max_number_key() + 1;
}
@@ -9485,16 +10263,15 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
: kDontAllowSmiElements;
bool has_smi_only_elements = false;
bool should_convert_to_fast_double_elements =
- ShouldConvertToFastDoubleElements(&has_smi_only_elements);
+ self->ShouldConvertToFastDoubleElements(&has_smi_only_elements);
if (has_smi_only_elements) {
smi_mode = kForceSmiElements;
}
MaybeObject* result = should_convert_to_fast_double_elements
- ? SetFastDoubleElementsCapacityAndLength(new_length, new_length)
- : SetFastElementsCapacityAndLength(new_length,
- new_length,
- smi_mode);
- ValidateElements();
+ ? self->SetFastDoubleElementsCapacityAndLength(new_length, new_length)
+ : self->SetFastElementsCapacityAndLength(
+ new_length, new_length, smi_mode);
+ self->ValidateElements();
if (result->IsFailure()) return result;
#ifdef DEBUG
if (FLAG_trace_normalization) {
@@ -9503,7 +10280,7 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
}
#endif
}
- return value;
+ return *value;
}
@@ -9659,28 +10436,27 @@ Handle<Object> JSObject::SetElement(Handle<JSObject> object,
MaybeObject* JSObject::SetElement(uint32_t index,
- Object* value,
+ Object* value_raw,
PropertyAttributes attributes,
StrictModeFlag strict_mode,
bool check_prototype,
SetPropertyMode set_mode) {
+ Isolate* isolate = GetIsolate();
+
// Check access rights if needed.
if (IsAccessCheckNeeded()) {
- Heap* heap = GetHeap();
- if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
- HandleScope scope(heap->isolate());
- Handle<Object> value_handle(value);
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET);
- return *value_handle;
+ if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
+ return value_raw;
}
}
if (IsJSGlobalProxy()) {
Object* proto = GetPrototype();
- if (proto->IsNull()) return value;
+ if (proto->IsNull()) return value_raw;
ASSERT(proto->IsJSGlobalObject());
return JSObject::cast(proto)->SetElement(index,
- value,
+ value_raw,
attributes,
strict_mode,
check_prototype,
@@ -9689,10 +10465,8 @@ MaybeObject* JSObject::SetElement(uint32_t index,
// Don't allow element properties to be redefined for external arrays.
if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) {
- Isolate* isolate = GetHeap()->isolate();
- Handle<Object> receiver(this);
Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
- Handle<Object> args[] = { receiver, number };
+ Handle<Object> args[] = { handle(this, isolate), number };
Handle<Object> error = isolate->factory()->NewTypeError(
"redef_external_array_element", HandleVector(args, ARRAY_SIZE(args)));
return isolate->Throw(*error);
@@ -9707,22 +10481,55 @@ MaybeObject* JSObject::SetElement(uint32_t index,
dictionary->set_requires_slow_elements();
}
+ if (!(FLAG_harmony_observation && map()->is_observed())) {
+ return HasIndexedInterceptor()
+ ? SetElementWithInterceptor(
+ index, value_raw, attributes, strict_mode, check_prototype, set_mode)
+ : SetElementWithoutInterceptor(
+ index, value_raw, attributes, strict_mode, check_prototype, set_mode);
+ }
+
+ // From here on, everything has to be handlified.
+ Handle<JSObject> self(this);
+ Handle<Object> value(value_raw);
+ PropertyAttributes old_attributes = self->GetLocalElementAttribute(index);
+ Handle<Object> old_value = isolate->factory()->the_hole_value();
+ Handle<Object> old_length;
+
+ if (old_attributes != ABSENT) {
+ if (self->GetLocalElementAccessorPair(index) == NULL)
+ old_value = Object::GetElement(self, index);
+ } else if (self->IsJSArray()) {
+ // Store old array length in case adding an element grows the array.
+ old_length = handle(Handle<JSArray>::cast(self)->length(), isolate);
+ }
+
// Check for lookup interceptor
- if (HasIndexedInterceptor()) {
- return SetElementWithInterceptor(index,
- value,
- attributes,
- strict_mode,
- check_prototype,
- set_mode);
+ MaybeObject* result = self->HasIndexedInterceptor()
+ ? self->SetElementWithInterceptor(
+ index, *value, attributes, strict_mode, check_prototype, set_mode)
+ : self->SetElementWithoutInterceptor(
+ index, *value, attributes, strict_mode, check_prototype, set_mode);
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
+ PropertyAttributes new_attributes = self->GetLocalElementAttribute(index);
+ if (old_attributes == ABSENT) {
+ EnqueueChangeRecord(self, "new", name, old_value);
+ if (self->IsJSArray() &&
+ !old_length->SameValue(Handle<JSArray>::cast(self)->length())) {
+ EnqueueChangeRecord(
+ self, "updated", isolate->factory()->length_symbol(), old_length);
+ }
+ } else if (old_attributes != new_attributes || old_value->IsTheHole()) {
+ EnqueueChangeRecord(self, "reconfigured", name, old_value);
+ } else if (!old_value->SameValue(*Object::GetElement(self, index))) {
+ EnqueueChangeRecord(self, "updated", name, old_value);
}
- return SetElementWithoutInterceptor(index,
- value,
- attributes,
- strict_mode,
- check_prototype,
- set_mode);
+ return *hresult;
}
@@ -9838,6 +10645,8 @@ MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) {
to_kind = GetHoleyElementsKind(to_kind);
}
+ if (from_kind == to_kind) return this;
+
Isolate* isolate = GetIsolate();
if (elements() == isolate->heap()->empty_fixed_array() ||
(IsFastSmiOrObjectElementsKind(from_kind) &&
@@ -10372,9 +11181,16 @@ bool JSObject::HasRealNamedCallbackProperty(String* key) {
int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
- return HasFastProperties() ?
- map()->NumberOfDescribedProperties(filter) :
- property_dictionary()->NumberOfElementsFilterAttributes(filter);
+ if (HasFastProperties()) {
+ Map* map = this->map();
+ if (filter == NONE) return map->NumberOfOwnDescriptors();
+ if (filter == DONT_ENUM) {
+ int result = map->EnumLength();
+ if (result != Map::kInvalidEnumCache) return result;
+ }
+ return map->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter);
+ }
+ return property_dictionary()->NumberOfElementsFilterAttributes(filter);
}
@@ -10497,9 +11313,10 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
ASSERT(storage->length() >= (NumberOfLocalProperties() - index));
if (HasFastProperties()) {
+ int real_size = map()->NumberOfOwnDescriptors();
DescriptorArray* descs = map()->instance_descriptors();
- ASSERT(storage->length() >= index + descs->number_of_descriptors());
- for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ ASSERT(storage->length() >= index + real_size);
+ for (int i = 0; i < real_size; i++) {
storage->set(index + i, descs->GetKey(i));
}
} else {
@@ -10931,11 +11748,10 @@ class AsciiSymbolKey : public SequentialSymbolKey<char> {
class SubStringAsciiSymbolKey : public HashTableKey {
public:
- explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string,
+ explicit SubStringAsciiSymbolKey(Handle<SeqOneByteString> string,
int from,
- int length,
- uint32_t seed)
- : string_(string), from_(from), length_(length), seed_(seed) { }
+ int length)
+ : string_(string), from_(from), length_(length) { }
uint32_t Hash() {
ASSERT(length_ >= 0);
@@ -10952,7 +11768,7 @@ class SubStringAsciiSymbolKey : public HashTableKey {
// chance this is an array index.
while (i < length_ && hasher.is_array_index()) {
hasher.AddCharacter(static_cast<uc32>(
- string_->SeqAsciiStringGet(i + from_)));
+ string_->SeqOneByteStringGet(i + from_)));
i++;
}
@@ -10960,7 +11776,7 @@ class SubStringAsciiSymbolKey : public HashTableKey {
// index.
while (i < length_) {
hasher.AddCharacterNoIndex(static_cast<uc32>(
- string_->SeqAsciiStringGet(i + from_)));
+ string_->SeqOneByteStringGet(i + from_)));
i++;
}
hash_field_ = hasher.GetHashField();
@@ -10988,11 +11804,10 @@ class SubStringAsciiSymbolKey : public HashTableKey {
}
private:
- Handle<SeqAsciiString> string_;
+ Handle<SeqOneByteString> string_;
int from_;
int length_;
uint32_t hash_field_;
- uint32_t seed_;
};
@@ -11827,7 +12642,7 @@ class TwoCharHashTableKey : public HashTableKey {
hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;
- if ((hash & String::kHashBitMask) == 0) hash = String::kZeroHash;
+ if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash;
#ifdef DEBUG
StringHasher hasher(2, seed);
hasher.AddCharacter(c1);
@@ -11913,11 +12728,12 @@ MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
}
-MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle<SeqAsciiString> str,
- int from,
- int length,
- Object** s) {
- SubStringAsciiSymbolKey key(str, from, length, GetHeap()->HashSeed());
+MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(
+ Handle<SeqOneByteString> str,
+ int from,
+ int length,
+ Object** s) {
+ SubStringAsciiSymbolKey key(str, from, length);
return LookupKey(&key, s);
}
@@ -12202,8 +13018,8 @@ MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
int pos = 0;
for (int i = 0; i < capacity; i++) {
if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
- enumeration_order->set(
- pos++, Smi::FromInt(DetailsAt(i).dictionary_index()));
+ int index = DetailsAt(i).dictionary_index();
+ enumeration_order->set(pos++, Smi::FromInt(index));
}
}
@@ -12332,7 +13148,8 @@ MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
// Insert element at empty or deleted entry
if (!details.IsDeleted() &&
- details.dictionary_index() == 0 && Shape::kIsEnumerable) {
+ details.dictionary_index() == 0 &&
+ Shape::kIsEnumerable) {
// Assign an enumeration index to the property and update
// SetNextEnumerationIndex.
int index = NextEnumerationIndex();
@@ -12604,8 +13421,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
PropertyType type = DetailsAt(i).type();
ASSERT(type != FIELD);
instance_descriptor_length++;
- if (type == NORMAL &&
- (!value->IsJSFunction() || heap->InNewSpace(value))) {
+ if (type == NORMAL && !value->IsJSFunction()) {
number_of_fields += 1;
}
}
@@ -12638,7 +13454,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
return maybe_descriptors;
}
- FixedArray::WhitenessWitness witness(descriptors);
+ DescriptorArray::WhitenessWitness witness(descriptors);
int number_of_allocated_fields =
number_of_fields + unused_property_fields - inobject_props;
@@ -12670,7 +13486,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
int enumeration_index = details.descriptor_index();
PropertyType type = details.type();
- if (value->IsJSFunction() && !heap->InNewSpace(value)) {
+ if (value->IsJSFunction()) {
ConstantFunctionDescriptor d(key,
JSFunction::cast(value),
details.attributes(),
@@ -12705,8 +13521,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
descriptors->Sort();
- MaybeObject* maybe_failure = new_map->InitializeDescriptors(descriptors);
- if (maybe_failure->IsFailure()) return maybe_failure;
+ new_map->InitializeDescriptors(descriptors);
new_map->set_unused_property_fields(unused_property_fields);
// Transform the object.