Index: src/objects.cc |
=================================================================== |
--- src/objects.cc (revision 9531) |
+++ src/objects.cc (working copy) |
@@ -39,7 +39,9 @@ |
#include "hydrogen.h" |
#include "objects-inl.h" |
#include "objects-visiting.h" |
+#include "objects-visiting-inl.h" |
#include "macro-assembler.h" |
+#include "mark-compact.h" |
#include "safepoint-table.h" |
#include "string-stream.h" |
#include "utils.h" |
@@ -132,27 +134,20 @@ |
void Object::Lookup(String* name, LookupResult* result) { |
Object* holder = NULL; |
- if (IsSmi()) { |
- Context* global_context = Isolate::Current()->context()->global_context(); |
- holder = global_context->number_function()->instance_prototype(); |
+ if (IsJSReceiver()) { |
+ holder = this; |
} else { |
- HeapObject* heap_object = HeapObject::cast(this); |
- if (heap_object->IsJSObject()) { |
- return JSObject::cast(this)->Lookup(name, result); |
- } else if (heap_object->IsJSProxy()) { |
- return result->HandlerResult(); |
- } |
Context* global_context = Isolate::Current()->context()->global_context(); |
- if (heap_object->IsString()) { |
+ if (IsNumber()) { |
+ holder = global_context->number_function()->instance_prototype(); |
+ } else if (IsString()) { |
holder = global_context->string_function()->instance_prototype(); |
- } else if (heap_object->IsHeapNumber()) { |
- holder = global_context->number_function()->instance_prototype(); |
- } else if (heap_object->IsBoolean()) { |
+ } else if (IsBoolean()) { |
holder = global_context->boolean_function()->instance_prototype(); |
} |
} |
ASSERT(holder != NULL); // Cannot handle null or undefined. |
- JSObject::cast(holder)->Lookup(name, result); |
+ JSReceiver::cast(holder)->Lookup(name, result); |
} |
@@ -167,10 +162,9 @@ |
} |
-MaybeObject* Object::GetPropertyWithCallback(Object* receiver, |
- Object* structure, |
- String* name, |
- Object* holder) { |
+MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, |
+ Object* structure, |
+ String* name) { |
Isolate* isolate = name->GetIsolate(); |
// To accommodate both the old and the new api we switch on the |
// data structure used to store the callbacks. Eventually foreign |
@@ -191,10 +185,9 @@ |
v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); |
HandleScope scope(isolate); |
JSObject* self = JSObject::cast(receiver); |
- JSObject* holder_handle = JSObject::cast(holder); |
Handle<String> key(name); |
LOG(isolate, ApiNamedPropertyAccess("load", self, name)); |
- CustomArguments args(isolate, data->data(), self, holder_handle); |
+ CustomArguments args(isolate, data->data(), self, this); |
v8::AccessorInfo info(args.end()); |
v8::Handle<v8::Value> result; |
{ |
@@ -212,9 +205,9 @@ |
// __defineGetter__ callback |
if (structure->IsFixedArray()) { |
Object* getter = FixedArray::cast(structure)->get(kGetterIndex); |
- if (getter->IsJSFunction()) { |
- return Object::GetPropertyWithDefinedGetter(receiver, |
- JSFunction::cast(getter)); |
+ if (getter->IsSpecFunction()) { |
+ // TODO(rossberg): nicer would be to cast to some JSCallable here... |
+ return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); |
} |
// Getter is not a function. |
return isolate->heap()->undefined_value(); |
@@ -225,47 +218,64 @@ |
} |
-MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw, |
- String* name_raw, |
- Object* handler_raw) { |
- Isolate* isolate = name_raw->GetIsolate(); |
+MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw, |
+ String* name_raw) { |
+ Isolate* isolate = GetIsolate(); |
HandleScope scope(isolate); |
Handle<Object> receiver(receiver_raw); |
Handle<Object> name(name_raw); |
- Handle<Object> handler(handler_raw); |
- // Extract trap function. |
- Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("get"); |
- Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); |
+ Handle<Object> args[] = { receiver, name }; |
+ Handle<Object> result = CallTrap( |
+ "get", isolate->derived_get_trap(), ARRAY_SIZE(args), args); |
if (isolate->has_pending_exception()) return Failure::Exception(); |
- if (trap->IsUndefined()) { |
- // Get the derived `get' property. |
- trap = isolate->derived_get_trap(); |
- } |
- // Call trap function. |
- Object** args[] = { receiver.location(), name.location() }; |
- bool has_exception; |
- Handle<Object> result = |
- Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); |
- if (has_exception) return Failure::Exception(); |
- |
return *result; |
} |
+MaybeObject* JSProxy::GetElementWithHandler(Object* receiver, |
+ uint32_t index) { |
+ String* name; |
+ MaybeObject* maybe = GetHeap()->Uint32ToString(index); |
+ if (!maybe->To<String>(&name)) return maybe; |
+ return GetPropertyWithHandler(receiver, name); |
+} |
+ |
+ |
+MaybeObject* JSProxy::SetElementWithHandler(uint32_t index, |
+ Object* value, |
+ StrictModeFlag strict_mode) { |
+ String* name; |
+ MaybeObject* maybe = GetHeap()->Uint32ToString(index); |
+ if (!maybe->To<String>(&name)) return maybe; |
+ return SetPropertyWithHandler(name, value, NONE, strict_mode); |
+} |
+ |
+ |
+bool JSProxy::HasElementWithHandler(uint32_t index) { |
+ String* name; |
+ MaybeObject* maybe = GetHeap()->Uint32ToString(index); |
+ if (!maybe->To<String>(&name)) return maybe; |
+ return HasPropertyWithHandler(name); |
+} |
+ |
+ |
MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver, |
- JSFunction* getter) { |
+ JSReceiver* getter) { |
HandleScope scope; |
- Handle<JSFunction> fun(JSFunction::cast(getter)); |
+ Handle<JSReceiver> fun(getter); |
Handle<Object> self(receiver); |
#ifdef ENABLE_DEBUGGER_SUPPORT |
Debug* debug = fun->GetHeap()->isolate()->debug(); |
// Handle stepping into a getter if step into is active. |
- if (debug->StepInActive()) { |
- debug->HandleStepIn(fun, Handle<Object>::null(), 0, false); |
+ // TODO(rossberg): should this apply to getters that are function proxies? |
+ if (debug->StepInActive() && fun->IsJSFunction()) { |
+ debug->HandleStepIn( |
+ Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); |
} |
#endif |
+ |
bool has_pending_exception; |
Handle<Object> result = |
Execution::Call(fun, self, 0, NULL, &has_pending_exception); |
@@ -290,10 +300,8 @@ |
AccessorInfo* info = AccessorInfo::cast(obj); |
if (info->all_can_read()) { |
*attributes = result->GetAttributes(); |
- return GetPropertyWithCallback(receiver, |
- result->GetCallbackObject(), |
- name, |
- result->holder()); |
+ return result->holder()->GetPropertyWithCallback( |
+ receiver, result->GetCallbackObject(), name); |
} |
} |
break; |
@@ -486,7 +494,7 @@ |
} |
JSGlobalPropertyCell* cell = |
JSGlobalPropertyCell::cast(dictionary->ValueAt(entry)); |
- cell->set_value(cell->heap()->the_hole_value()); |
+ cell->set_value(cell->GetHeap()->the_hole_value()); |
dictionary->DetailsAtPut(entry, details.AsDeleted()); |
} else { |
Object* deleted = dictionary->DeleteProperty(entry, mode); |
@@ -566,30 +574,26 @@ |
} |
*attributes = result->GetAttributes(); |
Object* value; |
- JSObject* holder = result->holder(); |
switch (result->type()) { |
case NORMAL: |
- value = holder->GetNormalizedProperty(result); |
+ value = result->holder()->GetNormalizedProperty(result); |
ASSERT(!value->IsTheHole() || result->IsReadOnly()); |
return value->IsTheHole() ? heap->undefined_value() : value; |
case FIELD: |
- value = holder->FastPropertyAt(result->GetFieldIndex()); |
+ value = result->holder()->FastPropertyAt(result->GetFieldIndex()); |
ASSERT(!value->IsTheHole() || result->IsReadOnly()); |
return value->IsTheHole() ? heap->undefined_value() : value; |
case CONSTANT_FUNCTION: |
return result->GetConstantFunction(); |
case CALLBACKS: |
- return GetPropertyWithCallback(receiver, |
- result->GetCallbackObject(), |
- name, |
- holder); |
- case HANDLER: { |
- JSProxy* proxy = JSProxy::cast(this); |
- return GetPropertyWithHandler(receiver, name, proxy->handler()); |
- } |
+ return result->holder()->GetPropertyWithCallback( |
+ receiver, result->GetCallbackObject(), name); |
+ case HANDLER: |
+ return result->proxy()->GetPropertyWithHandler(receiver, name); |
case INTERCEPTOR: { |
JSObject* recvr = JSObject::cast(receiver); |
- return holder->GetPropertyWithInterceptor(recvr, name, attributes); |
+ return result->holder()->GetPropertyWithInterceptor( |
+ recvr, name, attributes); |
} |
case MAP_TRANSITION: |
case ELEMENTS_TRANSITION: |
@@ -613,28 +617,21 @@ |
for (holder = this; |
holder != heap->null_value(); |
holder = holder->GetPrototype()) { |
- if (holder->IsSmi()) { |
- Context* global_context = Isolate::Current()->context()->global_context(); |
- holder = global_context->number_function()->instance_prototype(); |
- } else { |
- HeapObject* heap_object = HeapObject::cast(holder); |
- if (!heap_object->IsJSObject()) { |
- Isolate* isolate = heap->isolate(); |
- Context* global_context = isolate->context()->global_context(); |
- if (heap_object->IsString()) { |
- holder = global_context->string_function()->instance_prototype(); |
- } else if (heap_object->IsHeapNumber()) { |
- holder = global_context->number_function()->instance_prototype(); |
- } else if (heap_object->IsBoolean()) { |
- holder = global_context->boolean_function()->instance_prototype(); |
- } else if (heap_object->IsJSProxy()) { |
- // TODO(rossberg): do something |
- return heap->undefined_value(); // For now... |
- } else { |
- // Undefined and null have no indexed properties. |
- ASSERT(heap_object->IsUndefined() || heap_object->IsNull()); |
- return heap->undefined_value(); |
- } |
+ if (!holder->IsJSObject()) { |
+ Isolate* isolate = heap->isolate(); |
+ Context* global_context = isolate->context()->global_context(); |
+ if (holder->IsNumber()) { |
+ holder = global_context->number_function()->instance_prototype(); |
+ } else if (holder->IsString()) { |
+ holder = global_context->string_function()->instance_prototype(); |
+ } else if (holder->IsBoolean()) { |
+ holder = global_context->boolean_function()->instance_prototype(); |
+ } else if (holder->IsJSProxy()) { |
+ return JSProxy::cast(holder)->GetElementWithHandler(receiver, index); |
+ } else { |
+ // Undefined and null have no indexed properties. |
+ ASSERT(holder->IsUndefined() || holder->IsNull()); |
+ return heap->undefined_value(); |
} |
} |
@@ -877,6 +874,9 @@ |
// Fill the remainder of the string with dead wood. |
int new_size = this->Size(); // Byte size of the external String object. |
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); |
+ if (Marking::IsBlack(Marking::MarkBitFrom(this))) { |
+ MemoryChunk::IncrementLiveBytes(this->address(), new_size - size); |
+ } |
return true; |
} |
@@ -923,6 +923,10 @@ |
// Fill the remainder of the string with dead wood. |
int new_size = this->Size(); // Byte size of the external String object. |
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size); |
+ if (Marking::IsBlack(Marking::MarkBitFrom(this))) { |
+ MemoryChunk::IncrementLiveBytes(this->address(), new_size - size); |
+ } |
+ |
return true; |
} |
@@ -998,8 +1002,7 @@ |
break; |
} |
case JS_WEAK_MAP_TYPE: { |
- int elements = JSWeakMap::cast(this)->table()->NumberOfElements(); |
- accumulator->Add("<JS WeakMap[%d]>", elements); |
+ accumulator->Add("<JS WeakMap>"); |
break; |
} |
case JS_REGEXP_TYPE: { |
@@ -1027,7 +1030,7 @@ |
// JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). |
default: { |
Map* map_of_this = map(); |
- Heap* heap = map_of_this->heap(); |
+ Heap* heap = GetHeap(); |
Object* constructor = map_of_this->constructor(); |
bool printed = false; |
if (constructor->IsHeapObject() && |
@@ -1049,7 +1052,6 @@ |
global_object ? "Global Object: " : "", |
vowel ? "n" : ""); |
accumulator->Put(str); |
- accumulator->Put('>'); |
printed = true; |
} |
} |
@@ -1071,7 +1073,6 @@ |
void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { |
- // if (!HEAP->InNewSpace(this)) PrintF("*", this); |
Heap* heap = GetHeap(); |
if (!heap->Contains(this)) { |
accumulator->Add("!!!INVALID POINTER!!!"); |
@@ -1102,6 +1103,9 @@ |
case BYTE_ARRAY_TYPE: |
accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length()); |
break; |
+ case FREE_SPACE_TYPE: |
+ accumulator->Add("<FreeSpace[%u]>", FreeSpace::cast(this)->Size()); |
+ break; |
case EXTERNAL_PIXEL_ARRAY_TYPE: |
accumulator->Add("<ExternalPixelArray[%u]>", |
ExternalPixelArray::cast(this)->length()); |
@@ -1277,6 +1281,7 @@ |
case HEAP_NUMBER_TYPE: |
case FILLER_TYPE: |
case BYTE_ARRAY_TYPE: |
+ case FREE_SPACE_TYPE: |
case EXTERNAL_PIXEL_ARRAY_TYPE: |
case EXTERNAL_BYTE_ARRAY_TYPE: |
case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: |
@@ -1533,7 +1538,7 @@ |
// If the old map is the global object map (from new Object()), |
// then transitions are not added to it, so we are done. |
- Heap* heap = old_map->heap(); |
+ Heap* heap = GetHeap(); |
if (old_map == heap->isolate()->context()->global_context()-> |
object_function()->map()) { |
return function; |
@@ -1609,7 +1614,7 @@ |
StrictModeFlag strict_mode) { |
ASSERT(!IsJSGlobalProxy()); |
Map* map_of_this = map(); |
- Heap* heap = map_of_this->heap(); |
+ Heap* heap = GetHeap(); |
if (!map_of_this->is_extensible()) { |
if (strict_mode == kNonStrictMode) { |
return heap->undefined_value(); |
@@ -1658,6 +1663,14 @@ |
// found. Use set property to handle all these cases. |
return SetProperty(&result, name, value, attributes, strict_mode); |
} |
+ bool found = false; |
+ MaybeObject* result_object; |
+ result_object = SetPropertyWithCallbackSetterInPrototypes(name, |
+ value, |
+ attributes, |
+ &found, |
+ strict_mode); |
+ if (found) return result_object; |
// Add a new real property. |
return AddProperty(name, value, attributes, strict_mode); |
} |
@@ -1696,7 +1709,7 @@ |
return result; |
} |
// Do not add transitions to the map of "new Object()". |
- if (map() == old_map->heap()->isolate()->context()->global_context()-> |
+ if (map() == GetIsolate()->context()->global_context()-> |
object_function()->map()) { |
return result; |
} |
@@ -1880,8 +1893,9 @@ |
if (structure->IsFixedArray()) { |
Object* setter = FixedArray::cast(structure)->get(kSetterIndex); |
- if (setter->IsJSFunction()) { |
- return SetPropertyWithDefinedSetter(JSFunction::cast(setter), value); |
+ if (setter->IsSpecFunction()) { |
+ // TODO(rossberg): nicer would be to cast to some JSCallable here... |
+ return SetPropertyWithDefinedSetter(JSReceiver::cast(setter), value); |
} else { |
if (strict_mode == kNonStrictMode) { |
return value; |
@@ -1900,17 +1914,19 @@ |
} |
-MaybeObject* JSObject::SetPropertyWithDefinedSetter(JSFunction* setter, |
- Object* value) { |
+MaybeObject* JSReceiver::SetPropertyWithDefinedSetter(JSReceiver* setter, |
+ Object* value) { |
Isolate* isolate = GetIsolate(); |
Handle<Object> value_handle(value, isolate); |
- Handle<JSFunction> fun(JSFunction::cast(setter), isolate); |
- Handle<JSObject> self(this, isolate); |
+ Handle<JSReceiver> fun(setter, isolate); |
+ Handle<JSReceiver> self(this, isolate); |
#ifdef ENABLE_DEBUGGER_SUPPORT |
Debug* debug = isolate->debug(); |
// Handle stepping into a setter if step into is active. |
- if (debug->StepInActive()) { |
- debug->HandleStepIn(fun, Handle<Object>::null(), 0, false); |
+ // TODO(rossberg): should this apply to getters that are function proxies? |
+ if (debug->StepInActive() && fun->IsJSFunction()) { |
+ debug->HandleStepIn( |
+ Handle<JSFunction>::cast(fun), Handle<Object>::null(), 0, false); |
} |
#endif |
bool has_pending_exception; |
@@ -1928,6 +1944,9 @@ |
for (Object* pt = GetPrototype(); |
pt != heap->null_value(); |
pt = pt->GetPrototype()) { |
+ if (pt->IsJSProxy()) { |
+ return result->HandlerResult(JSProxy::cast(pt)); |
+ } |
JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); |
if (result->IsProperty()) { |
if (result->type() == CALLBACKS && !result->IsReadOnly()) return; |
@@ -1948,6 +1967,16 @@ |
for (Object* pt = GetPrototype(); |
pt != heap->null_value(); |
pt = pt->GetPrototype()) { |
+ if (pt->IsJSProxy()) { |
+ String* name; |
+ MaybeObject* maybe = GetHeap()->Uint32ToString(index); |
+ if (!maybe->To<String>(&name)) { |
+ *found = true; // Force abort |
+ return maybe; |
+ } |
+ return JSProxy::cast(pt)->SetPropertyWithHandlerIfDefiningSetter( |
+ name, value, NONE, strict_mode, found); |
+ } |
if (!JSObject::cast(pt)->HasDictionaryElements()) { |
continue; |
} |
@@ -1969,7 +1998,61 @@ |
return heap->the_hole_value(); |
} |
+MaybeObject* JSObject::SetPropertyWithCallbackSetterInPrototypes( |
+ String* name, |
+ Object* value, |
+ PropertyAttributes attributes, |
+ bool* found, |
+ StrictModeFlag strict_mode) { |
+ LookupResult result; |
+ LookupCallbackSetterInPrototypes(name, &result); |
+ Heap* heap = GetHeap(); |
+ if (result.IsFound()) { |
+ *found = true; |
+ if (result.type() == CALLBACKS) { |
+ return SetPropertyWithCallback(result.GetCallbackObject(), |
+ name, |
+ value, |
+ result.holder(), |
+ strict_mode); |
+ } else if (result.type() == HANDLER) { |
+ // We could not find a local property so let's check whether there is an |
+ // accessor that wants to handle the property. |
+ LookupResult accessor_result; |
+ LookupCallbackSetterInPrototypes(name, &accessor_result); |
+ if (accessor_result.IsFound()) { |
+ if (accessor_result.type() == CALLBACKS) { |
+ return SetPropertyWithCallback(accessor_result.GetCallbackObject(), |
+ name, |
+ value, |
+ accessor_result.holder(), |
+ strict_mode); |
+ } else if (accessor_result.type() == HANDLER) { |
+ // There is a proxy in the prototype chain. Invoke its |
+ // getOwnPropertyDescriptor trap. |
+ bool found = false; |
+ // SetPropertyWithHandlerIfDefiningSetter can cause GC, |
+ // make sure to use the handlified references after calling |
+ // the function. |
+ Handle<JSObject> self(this); |
+ Handle<String> hname(name); |
+ Handle<Object> hvalue(value); |
+ MaybeObject* result = |
+ accessor_result.proxy()->SetPropertyWithHandlerIfDefiningSetter( |
+ name, value, attributes, strict_mode, &found); |
+ if (found) return result; |
+ // The proxy does not define the property as an accessor. |
+ // Consequently, it has no effect on setting the receiver. |
+ return self->AddProperty(*hname, *hvalue, attributes, strict_mode); |
+ } |
+ } |
+ } |
+ } |
+ *found = false; |
+ return heap->the_hole_value(); |
+} |
+ |
void JSObject::LookupInDescriptor(String* name, LookupResult* result) { |
DescriptorArray* descriptors = map()->instance_descriptors(); |
int number = descriptors->SearchWithCache(name); |
@@ -1985,7 +2068,8 @@ |
String* name, |
LookupResult* result) { |
DescriptorArray* descriptors = instance_descriptors(); |
- DescriptorLookupCache* cache = heap()->isolate()->descriptor_lookup_cache(); |
+ DescriptorLookupCache* cache = |
+ GetHeap()->isolate()->descriptor_lookup_cache(); |
int number = cache->Lookup(descriptors, name); |
if (number == DescriptorLookupCache::kAbsent) { |
number = descriptors->Search(name); |
@@ -1999,17 +2083,111 @@ |
} |
-MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind, |
- bool safe_to_add_transition) { |
- Heap* current_heap = heap(); |
- DescriptorArray* descriptors = instance_descriptors(); |
+static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents, |
+ ElementsKind elements_kind) { |
+ if (descriptor_contents->IsMap()) { |
+ Map* map = Map::cast(descriptor_contents); |
+ if (map->elements_kind() == elements_kind) { |
+ return map; |
+ } |
+ return NULL; |
+ } |
+ |
+ FixedArray* map_array = FixedArray::cast(descriptor_contents); |
+ for (int i = 0; i < map_array->length(); ++i) { |
+ Object* current = map_array->get(i); |
+ // Skip undefined slots, they are sentinels for reclaimed maps. |
+ if (!current->IsUndefined()) { |
+ Map* current_map = Map::cast(map_array->get(i)); |
+ if (current_map->elements_kind() == elements_kind) { |
+ return current_map; |
+ } |
+ } |
+ } |
+ |
+ return NULL; |
+} |
+ |
+ |
+static MaybeObject* AddElementsTransitionMapToDescriptor( |
+ Object* descriptor_contents, |
+ Map* new_map) { |
+ // Nothing was in the descriptor for an ELEMENTS_TRANSITION, |
+ // simply add the map. |
+ if (descriptor_contents == NULL) { |
+ return new_map; |
+ } |
+ |
+ // There was already a map in the descriptor, create a 2-element FixedArray |
+ // to contain the existing map plus the new one. |
+ FixedArray* new_array; |
+ Heap* heap = new_map->GetHeap(); |
+ if (descriptor_contents->IsMap()) { |
+ // Must tenure, DescriptorArray expects no new-space objects. |
+ MaybeObject* maybe_new_array = heap->AllocateFixedArray(2, TENURED); |
+ if (!maybe_new_array->To<FixedArray>(&new_array)) { |
+ return maybe_new_array; |
+ } |
+ new_array->set(0, descriptor_contents); |
+ new_array->set(1, new_map); |
+ return new_array; |
+ } |
+ |
+ // The descriptor already contained a list of maps for different ElementKinds |
+ // of ELEMENTS_TRANSITION, first check the existing array for an undefined |
+ // slot, and if that's not available, create a FixedArray to hold the existing |
+ // maps plus the new one and fill it in. |
+ FixedArray* array = FixedArray::cast(descriptor_contents); |
+ for (int i = 0; i < array->length(); ++i) { |
+ if (array->get(i)->IsUndefined()) { |
+ array->set(i, new_map); |
+ return array; |
+ } |
+ } |
+ |
+ // Must tenure, DescriptorArray expects no new-space objects. |
+ MaybeObject* maybe_new_array = |
+ heap->AllocateFixedArray(array->length() + 1, TENURED); |
+ if (!maybe_new_array->To<FixedArray>(&new_array)) { |
+ return maybe_new_array; |
+ } |
+ int i = 0; |
+ while (i < array->length()) { |
+ new_array->set(i, array->get(i)); |
+ ++i; |
+ } |
+ new_array->set(i, new_map); |
+ return new_array; |
+} |
+ |
+ |
+MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) { |
+ Heap* current_heap = GetHeap(); |
+ Map* current_map = map(); |
+ DescriptorArray* descriptors = current_map->instance_descriptors(); |
String* elements_transition_sentinel_name = current_heap->empty_symbol(); |
+ if (current_map->elements_kind() == elements_kind) return current_map; |
+ |
+ // Only objects with FastProperties can have DescriptorArrays and can track |
+ // element-related maps. Also don't add descriptors to maps that are shared. |
+ bool safe_to_add_transition = HasFastProperties() && |
+ !current_map->IsUndefined() && |
+ !current_map->is_shared(); |
+ |
+ // Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps cause by objects |
+ // with elements that switch back and forth between dictionary and fast |
+ // element mode. |
+ if ((current_map->elements_kind() == DICTIONARY_ELEMENTS && |
+ elements_kind == FAST_ELEMENTS)) { |
+ safe_to_add_transition = false; |
+ } |
+ |
+ Object* descriptor_contents = NULL; |
if (safe_to_add_transition) { |
// It's only safe to manipulate the descriptor array if it would be |
// safe to add a transition. |
- ASSERT(!is_shared()); // no transitions can be added to shared maps. |
// Check if the elements transition already exists. |
DescriptorLookupCache* cache = |
current_heap->isolate()->descriptor_lookup_cache(); |
@@ -2025,9 +2203,15 @@ |
// return it. |
if (index != DescriptorArray::kNotFound) { |
PropertyDetails details(PropertyDetails(descriptors->GetDetails(index))); |
- if (details.type() == ELEMENTS_TRANSITION && |
- details.elements_kind() == elements_kind) { |
- return descriptors->GetValue(index); |
+ if (details.type() == ELEMENTS_TRANSITION) { |
+ descriptor_contents = descriptors->GetValue(index); |
+ Map* maybe_transition_map = |
+ GetElementsTransitionMapFromDescriptor(descriptor_contents, |
+ elements_kind); |
+ if (maybe_transition_map != NULL) { |
+ ASSERT(maybe_transition_map->IsMap()); |
+ return maybe_transition_map; |
+ } |
} else { |
safe_to_add_transition = false; |
} |
@@ -2037,26 +2221,29 @@ |
// No transition to an existing map for the given ElementsKind. Make a new |
// one. |
Object* obj; |
- { MaybeObject* maybe_map = CopyDropTransitions(); |
+ { MaybeObject* maybe_map = current_map->CopyDropTransitions(); |
if (!maybe_map->ToObject(&obj)) return maybe_map; |
} |
Map* new_map = Map::cast(obj); |
new_map->set_elements_kind(elements_kind); |
- GetIsolate()->counters()->map_to_external_array_elements()->Increment(); |
// Only remember the map transition if the object's map is NOT equal to the |
// global object_function's map and there is not an already existing |
// non-matching element transition. |
- bool allow_map_transition = |
- safe_to_add_transition && |
+ bool allow_map_transition = safe_to_add_transition && |
(GetIsolate()->context()->global_context()->object_function()->map() != |
map()); |
if (allow_map_transition) { |
- // Allocate new instance descriptors for the old map with map transition. |
+ MaybeObject* maybe_new_contents = |
+ AddElementsTransitionMapToDescriptor(descriptor_contents, new_map); |
+ Object* new_contents; |
+ if (!maybe_new_contents->ToObject(&new_contents)) { |
+ return maybe_new_contents; |
+ } |
+ |
ElementsTransitionDescriptor desc(elements_transition_sentinel_name, |
- Map::cast(new_map), |
- elements_kind); |
+ new_contents); |
Object* new_descriptors; |
MaybeObject* maybe_new_descriptors = descriptors->CopyInsert( |
&desc, |
@@ -2065,7 +2252,7 @@ |
return maybe_new_descriptors; |
} |
descriptors = DescriptorArray::cast(new_descriptors); |
- set_instance_descriptors(descriptors); |
+ current_map->set_instance_descriptors(descriptors); |
} |
return new_map; |
@@ -2078,6 +2265,7 @@ |
Object* proto = GetPrototype(); |
if (proto->IsNull()) return result->NotFound(); |
ASSERT(proto->IsJSGlobalObject()); |
+ // A GlobalProxy's prototype should always be a proper JSObject. |
return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result); |
} |
@@ -2204,7 +2392,7 @@ |
PropertyAttributes attributes, |
StrictModeFlag strict_mode) { |
if (result->IsFound() && result->type() == HANDLER) { |
- return JSProxy::cast(this)->SetPropertyWithHandler( |
+ return result->proxy()->SetPropertyWithHandler( |
key, value, attributes, strict_mode); |
} else { |
return JSObject::cast(this)->SetPropertyForResult( |
@@ -2218,23 +2406,12 @@ |
HandleScope scope(isolate); |
Handle<Object> receiver(this); |
Handle<Object> name(name_raw); |
- Handle<Object> handler(this->handler()); |
- // Extract trap function. |
- Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("has"); |
- Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); |
+ Handle<Object> args[] = { name }; |
+ Handle<Object> result = CallTrap( |
+ "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args); |
if (isolate->has_pending_exception()) return Failure::Exception(); |
- if (trap->IsUndefined()) { |
- trap = isolate->derived_has_trap(); |
- } |
- // Call trap function. |
- Object** args[] = { name.location() }; |
- bool has_exception; |
- Handle<Object> result = |
- Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); |
- if (has_exception) return Failure::Exception(); |
- |
return result->ToBoolean()->IsTrue(); |
} |
@@ -2249,24 +2426,82 @@ |
Handle<Object> receiver(this); |
Handle<Object> name(name_raw); |
Handle<Object> value(value_raw); |
- Handle<Object> handler(this->handler()); |
- // Extract trap function. |
- Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("set"); |
- Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); |
+ Handle<Object> args[] = { receiver, name, value }; |
+ CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args); |
if (isolate->has_pending_exception()) return Failure::Exception(); |
- if (trap->IsUndefined()) { |
- trap = isolate->derived_set_trap(); |
+ |
+ return *value; |
+} |
+ |
+ |
+MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandlerIfDefiningSetter( |
+ String* name_raw, |
+ Object* value_raw, |
+ PropertyAttributes attributes, |
+ StrictModeFlag strict_mode, |
+ bool* found) { |
+ *found = true; // except where defined otherwise... |
+ Isolate* isolate = GetHeap()->isolate(); |
+ Handle<JSProxy> proxy(this); |
+ Handle<String> name(name_raw); |
+ Handle<Object> value(value_raw); |
+ Handle<Object> args[] = { name }; |
+ Handle<Object> result = proxy->CallTrap( |
+ "getOwnPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); |
+ if (isolate->has_pending_exception()) return Failure::Exception(); |
+ |
+ if (!result->IsUndefined()) { |
+ // The proxy handler cares about this property. |
+ // Check whether it is virtualized as an accessor. |
+ // Emulate [[GetProperty]] semantics for proxies. |
+ bool has_pending_exception; |
+ Object** argv[] = { result.location() }; |
+ Handle<Object> desc = |
+ Execution::Call(isolate->to_complete_property_descriptor(), result, |
+ ARRAY_SIZE(argv), argv, &has_pending_exception); |
+ if (has_pending_exception) return Failure::Exception(); |
+ |
+ Handle<String> conf_name = |
+ isolate->factory()->LookupAsciiSymbol("configurable_"); |
+ Handle<Object> configurable(v8::internal::GetProperty(desc, conf_name)); |
+ ASSERT(!isolate->has_pending_exception()); |
+ if (configurable->IsFalse()) { |
+ Handle<Object> args[] = { Handle<Object>(proxy->handler()), proxy, name }; |
+ Handle<Object> error = isolate->factory()->NewTypeError( |
+ "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); |
+ return isolate->Throw(*error); |
+ } |
+ ASSERT(configurable->IsTrue()); |
+ |
+ // Check for AccessorDescriptor. |
+ Handle<String> set_name = isolate->factory()->LookupAsciiSymbol("set_"); |
+ Handle<Object> setter(v8::internal::GetProperty(desc, set_name)); |
+ ASSERT(!isolate->has_pending_exception()); |
+ if (!setter->IsUndefined()) { |
+ // We have a setter -- invoke it. |
+ // TODO(rossberg): nicer would be to cast to some JSCallable here... |
+ return proxy->SetPropertyWithDefinedSetter( |
+ JSReceiver::cast(*setter), *value); |
+ } else { |
+ Handle<String> get_name = isolate->factory()->LookupAsciiSymbol("get_"); |
+ Handle<Object> getter(v8::internal::GetProperty(desc, get_name)); |
+ ASSERT(!isolate->has_pending_exception()); |
+ if (!getter->IsUndefined()) { |
+ // We have a getter but no setter -- the property may not be |
+ // written. In strict mode, throw an error. |
+ if (strict_mode == kNonStrictMode) return *value; |
+ Handle<Object> args[] = { name, proxy }; |
+ Handle<Object> error = isolate->factory()->NewTypeError( |
+ "no_setter_in_callback", HandleVector(args, ARRAY_SIZE(args))); |
+ return isolate->Throw(*error); |
+ } |
+ } |
+ // Fall-through. |
} |
- // Call trap function. |
- Object** args[] = { |
- receiver.location(), name.location(), value.location() |
- }; |
- bool has_exception; |
- Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); |
- if (has_exception) return Failure::Exception(); |
- |
+ // The proxy does not define the property as an accessor. |
+ *found = false; |
return *value; |
} |
@@ -2277,31 +2512,16 @@ |
HandleScope scope(isolate); |
Handle<Object> receiver(this); |
Handle<Object> name(name_raw); |
- Handle<Object> handler(this->handler()); |
- // Extract trap function. |
- Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete"); |
- Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); |
+ Handle<Object> args[] = { name }; |
+ Handle<Object> result = CallTrap( |
+ "delete", Handle<Object>(), ARRAY_SIZE(args), args); |
if (isolate->has_pending_exception()) return Failure::Exception(); |
- if (trap->IsUndefined()) { |
- Handle<Object> args[] = { handler, trap_name }; |
- Handle<Object> error = isolate->factory()->NewTypeError( |
- "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); |
- isolate->Throw(*error); |
- return Failure::Exception(); |
- } |
- // Call trap function. |
- Object** args[] = { name.location() }; |
- bool has_exception; |
- Handle<Object> result = |
- Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception); |
- if (has_exception) return Failure::Exception(); |
- |
Object* bool_result = result->ToBoolean(); |
- if (mode == STRICT_DELETION && |
- bool_result == isolate->heap()->false_value()) { |
- Handle<Object> args[] = { handler, trap_name }; |
+ if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) { |
+ Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete"); |
+ Handle<Object> args[] = { Handle<Object>(handler()), trap_name }; |
Handle<Object> error = isolate->factory()->NewTypeError( |
"handler_failed", HandleVector(args, ARRAY_SIZE(args))); |
isolate->Throw(*error); |
@@ -2311,39 +2531,73 @@ |
} |
+MUST_USE_RESULT MaybeObject* JSProxy::DeleteElementWithHandler( |
+ uint32_t index, |
+ DeleteMode mode) { |
+ Isolate* isolate = GetIsolate(); |
+ HandleScope scope(isolate); |
+ Handle<String> name = isolate->factory()->Uint32ToString(index); |
+ return JSProxy::DeletePropertyWithHandler(*name, mode); |
+} |
+ |
+ |
MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( |
JSReceiver* receiver_raw, |
- String* name_raw, |
- bool* has_exception) { |
+ String* name_raw) { |
Isolate* isolate = GetIsolate(); |
HandleScope scope(isolate); |
+ Handle<JSProxy> proxy(this); |
Handle<JSReceiver> receiver(receiver_raw); |
Handle<Object> name(name_raw); |
- Handle<Object> handler(this->handler()); |
- // Extract trap function. |
- Handle<String> trap_name = |
- isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); |
- Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); |
+ Handle<Object> args[] = { name }; |
+ Handle<Object> result = CallTrap( |
+ "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args); |
if (isolate->has_pending_exception()) return NONE; |
- if (trap->IsUndefined()) { |
- Handle<Object> args[] = { handler, trap_name }; |
+ |
+ if (result->IsUndefined()) return ABSENT; |
+ |
+ bool has_pending_exception; |
+ Object** argv[] = { result.location() }; |
+ Handle<Object> desc = |
+ Execution::Call(isolate->to_complete_property_descriptor(), result, |
+ ARRAY_SIZE(argv), argv, &has_pending_exception); |
+ if (has_pending_exception) return NONE; |
+ |
+ // Convert result to PropertyAttributes. |
+ Handle<String> enum_n = isolate->factory()->LookupAsciiSymbol("enumerable"); |
+ Handle<Object> enumerable(v8::internal::GetProperty(desc, enum_n)); |
+ if (isolate->has_pending_exception()) return NONE; |
+ Handle<String> conf_n = isolate->factory()->LookupAsciiSymbol("configurable"); |
+ Handle<Object> configurable(v8::internal::GetProperty(desc, conf_n)); |
+ if (isolate->has_pending_exception()) return NONE; |
+ Handle<String> writ_n = isolate->factory()->LookupAsciiSymbol("writable"); |
+ Handle<Object> writable(v8::internal::GetProperty(desc, writ_n)); |
+ if (isolate->has_pending_exception()) return NONE; |
+ |
+ if (configurable->IsFalse()) { |
+ Handle<Object> args[] = { Handle<Object>(proxy->handler()), proxy, name }; |
Handle<Object> error = isolate->factory()->NewTypeError( |
- "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); |
+ "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); |
isolate->Throw(*error); |
- *has_exception = true; |
return NONE; |
} |
- // Call trap function. |
- Object** args[] = { name.location() }; |
- Handle<Object> result = |
- Execution::Call(trap, handler, ARRAY_SIZE(args), args, has_exception); |
- if (has_exception) return NONE; |
+ int attributes = NONE; |
+ if (enumerable->ToBoolean()->IsFalse()) attributes |= DONT_ENUM; |
+ if (configurable->ToBoolean()->IsFalse()) attributes |= DONT_DELETE; |
+ if (writable->ToBoolean()->IsFalse()) attributes |= READ_ONLY; |
+ return static_cast<PropertyAttributes>(attributes); |
+} |
- // TODO(rossberg): convert result to PropertyAttributes |
- USE(result); |
- return NONE; |
+ |
+MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler( |
+ JSReceiver* receiver, |
+ uint32_t index) { |
+ Isolate* isolate = GetIsolate(); |
+ HandleScope scope(isolate); |
+ Handle<String> name = isolate->factory()->Uint32ToString(index); |
+ return GetPropertyAttributeWithHandler(receiver, *name); |
} |
@@ -2352,6 +2606,9 @@ |
HandleScope scope(isolate); |
Handle<JSProxy> self(this); |
+ // Save identity hash. |
+ MaybeObject* maybe_hash = GetIdentityHash(OMIT_CREATION); |
+ |
if (IsJSFunctionProxy()) { |
isolate->factory()->BecomeJSFunction(self); |
// Code will be set on the JavaScript side. |
@@ -2359,10 +2616,45 @@ |
isolate->factory()->BecomeJSObject(self); |
} |
ASSERT(self->IsJSObject()); |
+ |
+ // Inherit identity, if it was present. |
+ Object* hash; |
+ if (maybe_hash->To<Object>(&hash) && hash->IsSmi()) { |
+ Handle<JSObject> new_self(JSObject::cast(*self)); |
+ isolate->factory()->SetIdentityHash(new_self, hash); |
+ } |
} |
+MUST_USE_RESULT Handle<Object> JSProxy::CallTrap( |
+ const char* name, |
+ Handle<Object> derived, |
+ int argc, |
+ Handle<Object> args[]) { |
+ Isolate* isolate = GetIsolate(); |
+ Handle<Object> handler(this->handler()); |
+ Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol(name); |
+ Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); |
+ if (isolate->has_pending_exception()) return trap; |
+ |
+ if (trap->IsUndefined()) { |
+ if (derived.is_null()) { |
+ Handle<Object> args[] = { handler, trap_name }; |
+ Handle<Object> error = isolate->factory()->NewTypeError( |
+ "handler_trap_missing", HandleVector(args, ARRAY_SIZE(args))); |
+ isolate->Throw(*error); |
+ return Handle<Object>(); |
+ } |
+ trap = Handle<Object>(derived); |
+ } |
+ |
+ Object*** argv = reinterpret_cast<Object***>(args); |
+ bool threw; |
+ return Execution::Call(trap, handler, argc, argv, &threw); |
+} |
+ |
+ |
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, |
String* name, |
Object* value, |
@@ -2386,48 +2678,46 @@ |
} |
// Check access rights if needed. |
- if (IsAccessCheckNeeded() |
- && !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) { |
- return SetPropertyWithFailedAccessCheck(result, |
- name, |
- value, |
- true, |
- strict_mode); |
+ if (IsAccessCheckNeeded()) { |
+ if (!heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_SET)) { |
+ return SetPropertyWithFailedAccessCheck( |
+ result, name, value, true, strict_mode); |
+ } |
} |
if (IsJSGlobalProxy()) { |
Object* proto = GetPrototype(); |
if (proto->IsNull()) return value; |
ASSERT(proto->IsJSGlobalObject()); |
- return JSObject::cast(proto)->SetProperty( |
+ return JSObject::cast(proto)->SetPropertyForResult( |
result, name, value, attributes, strict_mode); |
} |
if (!result->IsProperty() && !IsJSContextExtensionObject()) { |
- // We could not find a local property so let's check whether there is an |
- // accessor that wants to handle the property. |
- LookupResult accessor_result; |
- LookupCallbackSetterInPrototypes(name, &accessor_result); |
- if (accessor_result.IsProperty()) { |
- return SetPropertyWithCallback(accessor_result.GetCallbackObject(), |
- name, |
- value, |
- accessor_result.holder(), |
- strict_mode); |
- } |
+ bool found = false; |
+ MaybeObject* result_object; |
+ result_object = SetPropertyWithCallbackSetterInPrototypes(name, |
+ value, |
+ attributes, |
+ &found, |
+ strict_mode); |
+ if (found) return result_object; |
} |
+ |
+ // At this point, no GC should have happened, as this would invalidate |
+ // 'result', which we cannot handlify! |
+ |
if (!result->IsFound()) { |
// Neither properties nor transitions found. |
return AddProperty(name, value, attributes, strict_mode); |
} |
if (result->IsReadOnly() && result->IsProperty()) { |
if (strict_mode == kStrictMode) { |
- HandleScope scope(heap->isolate()); |
- Handle<String> key(name); |
- Handle<Object> holder(this); |
- Handle<Object> args[2] = { key, holder }; |
+ Handle<JSObject> self(this); |
+ Handle<String> hname(name); |
+ Handle<Object> args[] = { hname, self }; |
return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError( |
- "strict_read_only_property", HandleVector(args, 2))); |
+ "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); |
} else { |
return value; |
} |
@@ -2656,9 +2946,8 @@ |
String* key) { |
uint32_t index = 0; |
if (IsJSObject() && key->AsArrayIndex(&index)) { |
- if (JSObject::cast(this)->HasElementWithReceiver(receiver, index)) |
- return NONE; |
- return ABSENT; |
+ return JSObject::cast(this)->HasElementWithReceiver(receiver, index) |
+ ? NONE : ABSENT; |
} |
// Named property. |
LookupResult result; |
@@ -2688,10 +2977,8 @@ |
case CALLBACKS: |
return result->GetAttributes(); |
case HANDLER: { |
- // TODO(rossberg): propagate exceptions properly. |
- bool has_exception = false; |
- return JSProxy::cast(this)->GetPropertyAttributeWithHandler( |
- receiver, name, &has_exception); |
+ return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler( |
+ receiver, name); |
} |
case INTERCEPTOR: |
return result->holder()->GetPropertyAttributeWithInterceptor( |
@@ -2857,7 +3144,7 @@ |
} |
} |
- Heap* current_heap = map_of_this->heap(); |
+ Heap* current_heap = GetHeap(); |
// Copy the next enumeration index from instance descriptor. |
int index = map_of_this->instance_descriptors()->NextEnumerationIndex(); |
@@ -2879,7 +3166,11 @@ |
ASSERT(instance_size_delta >= 0); |
current_heap->CreateFillerObjectAt(this->address() + new_instance_size, |
instance_size_delta); |
+ if (Marking::IsBlack(Marking::MarkBitFrom(this))) { |
+ MemoryChunk::IncrementLiveBytes(this->address(), -instance_size_delta); |
+ } |
+ |
set_map(new_map); |
new_map->clear_instance_descriptors(); |
@@ -2912,13 +3203,14 @@ |
FixedArrayBase* array = FixedArrayBase::cast(elements()); |
Map* old_map = array->map(); |
bool is_arguments = |
- (old_map == old_map->heap()->non_strict_arguments_elements_map()); |
+ (old_map == old_map->GetHeap()->non_strict_arguments_elements_map()); |
if (is_arguments) { |
array = FixedArrayBase::cast(FixedArray::cast(array)->get(1)); |
} |
if (array->IsDictionary()) return array; |
ASSERT(HasFastElements() || |
+ HasFastSmiOnlyElements() || |
HasFastDoubleElements() || |
HasFastArgumentsElements()); |
// Compute the effective length and allocate a new backing store. |
@@ -2953,7 +3245,8 @@ |
if (!maybe_value_object->ToObject(&value)) return maybe_value_object; |
} |
} else { |
- ASSERT(old_map->has_fast_elements()); |
+ ASSERT(old_map->has_fast_elements() || |
+ old_map->has_fast_smi_only_elements()); |
value = FixedArray::cast(array)->get(i); |
} |
PropertyDetails details = PropertyDetails(NONE, NORMAL); |
@@ -2973,13 +3266,14 @@ |
// Set the new map first to satify the elements type assert in |
// set_elements(). |
Object* new_map; |
- MaybeObject* maybe = map()->GetSlowElementsMap(); |
+ MaybeObject* maybe = GetElementsTransitionMap(DICTIONARY_ELEMENTS); |
if (!maybe->ToObject(&new_map)) return maybe; |
set_map(Map::cast(new_map)); |
set_elements(dictionary); |
} |
- old_map->isolate()->counters()->elements_to_dictionary()->Increment(); |
+ old_map->GetHeap()->isolate()->counters()->elements_to_dictionary()-> |
+ Increment(); |
#ifdef DEBUG |
if (FLAG_trace_normalization) { |
@@ -2993,95 +3287,219 @@ |
} |
-MaybeObject* JSObject::GetHiddenProperties(HiddenPropertiesFlag flag) { |
+Smi* JSReceiver::GenerateIdentityHash() { |
Isolate* isolate = GetIsolate(); |
- Heap* heap = isolate->heap(); |
- Object* holder = BypassGlobalProxy(); |
- if (holder->IsUndefined()) return heap->undefined_value(); |
- JSObject* obj = JSObject::cast(holder); |
- if (obj->HasFastProperties()) { |
+ |
+ int hash_value; |
+ int attempts = 0; |
+ do { |
+ // Generate a random 32-bit hash value but limit range to fit |
+ // within a smi. |
+ hash_value = V8::Random(isolate) & Smi::kMaxValue; |
+ attempts++; |
+ } while (hash_value == 0 && attempts < 30); |
+ hash_value = hash_value != 0 ? hash_value : 1; // never return 0 |
+ |
+ return Smi::FromInt(hash_value); |
+} |
+ |
+ |
+MaybeObject* JSObject::SetIdentityHash(Object* hash, CreationFlag flag) { |
+ MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_symbol(), |
+ hash); |
+ if (maybe->IsFailure()) return maybe; |
+ return this; |
+} |
+ |
+ |
+MaybeObject* JSObject::GetIdentityHash(CreationFlag flag) { |
+ Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_symbol()); |
+ if (stored_value->IsSmi()) return stored_value; |
+ |
+ Smi* hash = GenerateIdentityHash(); |
+ MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_symbol(), |
+ hash); |
+ if (result->IsFailure()) return result; |
+ if (result->ToObjectUnchecked()->IsUndefined()) { |
+ // Trying to get hash of detached proxy. |
+ return Smi::FromInt(0); |
+ } |
+ return hash; |
+} |
+ |
+ |
+MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) { |
+ Object* hash = this->hash(); |
+ if (!hash->IsSmi() && flag == ALLOW_CREATION) { |
+ hash = GenerateIdentityHash(); |
+ set_hash(hash); |
+ } |
+ return hash; |
+} |
+ |
+ |
+Object* JSObject::GetHiddenProperty(String* key) { |
+ if (IsJSGlobalProxy()) { |
+ // For a proxy, use the prototype as target object. |
+ Object* proxy_parent = GetPrototype(); |
+ // If the proxy is detached, return undefined. |
+ if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); |
+ ASSERT(proxy_parent->IsJSGlobalObject()); |
+ return JSObject::cast(proxy_parent)->GetHiddenProperty(key); |
+ } |
+ ASSERT(!IsJSGlobalProxy()); |
+ MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); |
+ ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. |
+ if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) { |
+ return GetHeap()->undefined_value(); |
+ } |
+ StringDictionary* dictionary = |
+ StringDictionary::cast(hidden_lookup->ToObjectUnchecked()); |
+ int entry = dictionary->FindEntry(key); |
+ if (entry == StringDictionary::kNotFound) return GetHeap()->undefined_value(); |
+ return dictionary->ValueAt(entry); |
+} |
+ |
+ |
+MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { |
+ if (IsJSGlobalProxy()) { |
+ // For a proxy, use the prototype as target object. |
+ Object* proxy_parent = GetPrototype(); |
+ // If the proxy is detached, return undefined. |
+ if (proxy_parent->IsNull()) return GetHeap()->undefined_value(); |
+ ASSERT(proxy_parent->IsJSGlobalObject()); |
+ return JSObject::cast(proxy_parent)->SetHiddenProperty(key, value); |
+ } |
+ ASSERT(!IsJSGlobalProxy()); |
+ MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(true); |
+ StringDictionary* dictionary; |
+ if (!hidden_lookup->To<StringDictionary>(&dictionary)) return hidden_lookup; |
+ |
+ // If it was found, check if the key is already in the dictionary. |
+ int entry = dictionary->FindEntry(key); |
+ if (entry != StringDictionary::kNotFound) { |
+ // If key was found, just update the value. |
+ dictionary->ValueAtPut(entry, value); |
+ return this; |
+ } |
+ // Key was not already in the dictionary, so add the entry. |
+ MaybeObject* insert_result = dictionary->Add(key, |
+ value, |
+ PropertyDetails(NONE, NORMAL)); |
+ StringDictionary* new_dict; |
+ if (!insert_result->To<StringDictionary>(&new_dict)) return insert_result; |
+ if (new_dict != dictionary) { |
+ // If adding the key expanded the dictionary (i.e., Add returned a new |
+ // dictionary), store it back to the object. |
+ MaybeObject* store_result = SetHiddenPropertiesDictionary(new_dict); |
+ if (store_result->IsFailure()) return store_result; |
+ } |
+ // Return this to mark success. |
+ return this; |
+} |
+ |
+ |
+void JSObject::DeleteHiddenProperty(String* key) { |
+ if (IsJSGlobalProxy()) { |
+ // For a proxy, use the prototype as target object. |
+ Object* proxy_parent = GetPrototype(); |
+ // If the proxy is detached, return immediately. |
+ if (proxy_parent->IsNull()) return; |
+ ASSERT(proxy_parent->IsJSGlobalObject()); |
+ JSObject::cast(proxy_parent)->DeleteHiddenProperty(key); |
+ return; |
+ } |
+ MaybeObject* hidden_lookup = GetHiddenPropertiesDictionary(false); |
+ ASSERT(!hidden_lookup->IsFailure()); // No failure when passing false as arg. |
+ if (hidden_lookup->ToObjectUnchecked()->IsUndefined()) return; |
+ StringDictionary* dictionary = |
+ StringDictionary::cast(hidden_lookup->ToObjectUnchecked()); |
+ int entry = dictionary->FindEntry(key); |
+ if (entry == StringDictionary::kNotFound) { |
+ // Key wasn't in dictionary. Deletion is a success. |
+ return; |
+ } |
+ // Key was in the dictionary. Remove it. |
+ dictionary->DeleteProperty(entry, JSReceiver::FORCE_DELETION); |
+} |
+ |
+ |
+bool JSObject::HasHiddenProperties() { |
+ LookupResult lookup; |
+ LocalLookupRealNamedProperty(GetHeap()->hidden_symbol(), &lookup); |
+ return lookup.IsFound(); |
+} |
+ |
+ |
+MaybeObject* JSObject::GetHiddenPropertiesDictionary(bool create_if_absent) { |
+ ASSERT(!IsJSGlobalProxy()); |
+ if (HasFastProperties()) { |
// If the object has fast properties, check whether the first slot |
// in the descriptor array matches the hidden symbol. Since the |
// hidden symbols hash code is zero (and no other string has hash |
// code zero) it will always occupy the first entry if present. |
- DescriptorArray* descriptors = obj->map()->instance_descriptors(); |
+ DescriptorArray* descriptors = this->map()->instance_descriptors(); |
if ((descriptors->number_of_descriptors() > 0) && |
- (descriptors->GetKey(0) == heap->hidden_symbol()) && |
+ (descriptors->GetKey(0) == GetHeap()->hidden_symbol()) && |
descriptors->IsProperty(0)) { |
ASSERT(descriptors->GetType(0) == FIELD); |
- return obj->FastPropertyAt(descriptors->GetFieldIndex(0)); |
+ Object* hidden_store = |
+ this->FastPropertyAt(descriptors->GetFieldIndex(0)); |
+ return StringDictionary::cast(hidden_store); |
} |
- } |
- |
- // Only attempt to find the hidden properties in the local object and not |
- // in the prototype chain. |
- if (!obj->HasHiddenPropertiesObject()) { |
- // Hidden properties object not found. Allocate a new hidden properties |
- // object if requested. Otherwise return the undefined value. |
- if (flag == ALLOW_CREATION) { |
- Object* hidden_obj; |
- { MaybeObject* maybe_obj = heap->AllocateJSObject( |
- isolate->context()->global_context()->object_function()); |
- if (!maybe_obj->ToObject(&hidden_obj)) return maybe_obj; |
- } |
- // Don't allow leakage of the hidden object through accessors |
- // on Object.prototype. |
- { |
- MaybeObject* maybe_obj = |
- JSObject::cast(hidden_obj)->SetPrototype(heap->null_value(), false); |
- if (maybe_obj->IsFailure()) return maybe_obj; |
- } |
- return obj->SetHiddenPropertiesObject(hidden_obj); |
- } else { |
- return heap->undefined_value(); |
+ } else { |
+ PropertyAttributes attributes; |
+ // You can't install a getter on a property indexed by the hidden symbol, |
+ // so we can be sure that GetLocalPropertyPostInterceptor returns a real |
+ // object. |
+ Object* lookup = |
+ GetLocalPropertyPostInterceptor(this, |
+ GetHeap()->hidden_symbol(), |
+ &attributes)->ToObjectUnchecked(); |
+ if (!lookup->IsUndefined()) { |
+ return StringDictionary::cast(lookup); |
} |
} |
- return obj->GetHiddenPropertiesObject(); |
+ if (!create_if_absent) return GetHeap()->undefined_value(); |
+ const int kInitialSize = 5; |
+ MaybeObject* dict_alloc = StringDictionary::Allocate(kInitialSize); |
+ StringDictionary* dictionary; |
+ if (!dict_alloc->To<StringDictionary>(&dictionary)) return dict_alloc; |
+ MaybeObject* store_result = |
+ SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), |
+ dictionary, |
+ DONT_ENUM, |
+ kNonStrictMode); |
+ if (store_result->IsFailure()) return store_result; |
+ return dictionary; |
} |
-MaybeObject* JSObject::GetIdentityHash(HiddenPropertiesFlag flag) { |
- Isolate* isolate = GetIsolate(); |
- Object* hidden_props_obj; |
- { MaybeObject* maybe_obj = GetHiddenProperties(flag); |
- if (!maybe_obj->ToObject(&hidden_props_obj)) return maybe_obj; |
- } |
- if (!hidden_props_obj->IsJSObject()) { |
- // We failed to create hidden properties. That's a detached |
- // global proxy. |
- ASSERT(hidden_props_obj->IsUndefined()); |
- return Smi::FromInt(0); |
- } |
- JSObject* hidden_props = JSObject::cast(hidden_props_obj); |
- String* hash_symbol = isolate->heap()->identity_hash_symbol(); |
- { |
- // Note that HasLocalProperty() can cause a GC in the general case in the |
- // presence of interceptors. |
- AssertNoAllocation no_alloc; |
- if (hidden_props->HasLocalProperty(hash_symbol)) { |
- MaybeObject* hash = hidden_props->GetProperty(hash_symbol); |
- return Smi::cast(hash->ToObjectChecked()); |
+MaybeObject* JSObject::SetHiddenPropertiesDictionary( |
+ StringDictionary* dictionary) { |
+ ASSERT(!IsJSGlobalProxy()); |
+ ASSERT(HasHiddenProperties()); |
+ if (HasFastProperties()) { |
+ // If the object has fast properties, check whether the first slot |
+ // in the descriptor array matches the hidden symbol. Since the |
+ // hidden symbols hash code is zero (and no other string has hash |
+ // code zero) it will always occupy the first entry if present. |
+ DescriptorArray* descriptors = this->map()->instance_descriptors(); |
+ if ((descriptors->number_of_descriptors() > 0) && |
+ (descriptors->GetKey(0) == GetHeap()->hidden_symbol()) && |
+ descriptors->IsProperty(0)) { |
+ ASSERT(descriptors->GetType(0) == FIELD); |
+ this->FastPropertyAtPut(descriptors->GetFieldIndex(0), dictionary); |
+ return this; |
} |
} |
- |
- int hash_value; |
- int attempts = 0; |
- do { |
- // Generate a random 32-bit hash value but limit range to fit |
- // within a smi. |
- hash_value = V8::Random(isolate) & Smi::kMaxValue; |
- attempts++; |
- } while (hash_value == 0 && attempts < 30); |
- hash_value = hash_value != 0 ? hash_value : 1; // never return 0 |
- |
- Smi* hash = Smi::FromInt(hash_value); |
- { MaybeObject* result = hidden_props->SetLocalPropertyIgnoreAttributes( |
- hash_symbol, |
- hash, |
- static_cast<PropertyAttributes>(None)); |
- if (result->IsFailure()) return result; |
- } |
- return hash; |
+ MaybeObject* store_result = |
+ SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), |
+ dictionary, |
+ DONT_ENUM, |
+ kNonStrictMode); |
+ if (store_result->IsFailure()) return store_result; |
+ return this; |
} |
@@ -3201,12 +3619,19 @@ |
MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) { |
if (IsJSProxy()) { |
return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode); |
- } else { |
- return JSObject::cast(this)->DeleteProperty(name, mode); |
} |
+ return JSObject::cast(this)->DeleteProperty(name, mode); |
} |
+MaybeObject* JSReceiver::DeleteElement(uint32_t index, DeleteMode mode) { |
+ if (IsJSProxy()) { |
+ return JSProxy::cast(this)->DeleteElementWithHandler(index, mode); |
+ } |
+ return JSObject::cast(this)->DeleteElement(index, mode); |
+} |
+ |
+ |
MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { |
Isolate* isolate = GetIsolate(); |
// ECMA-262, 3rd, 8.6.2.5 |
@@ -3267,7 +3692,8 @@ |
bool JSObject::ReferencesObjectFromElements(FixedArray* elements, |
ElementsKind kind, |
Object* object) { |
- ASSERT(kind == FAST_ELEMENTS || kind == DICTIONARY_ELEMENTS); |
+ ASSERT(kind == FAST_ELEMENTS || |
+ kind == DICTIONARY_ELEMENTS); |
if (kind == FAST_ELEMENTS) { |
int length = IsJSArray() |
? Smi::cast(JSArray::cast(this)->length())->value() |
@@ -3287,7 +3713,7 @@ |
// Check whether this object references another object. |
bool JSObject::ReferencesObject(Object* obj) { |
Map* map_of_this = map(); |
- Heap* heap = map_of_this->heap(); |
+ Heap* heap = GetHeap(); |
AssertNoAllocation no_alloc; |
// Is the object the constructor for this object? |
@@ -3322,6 +3748,8 @@ |
// Raw pixels and external arrays do not reference other |
// objects. |
break; |
+ case FAST_SMI_ONLY_ELEMENTS: |
+ break; |
case FAST_ELEMENTS: |
case DICTIONARY_ELEMENTS: { |
FixedArray* elements = FixedArray::cast(this->elements()); |
@@ -3509,15 +3937,6 @@ |
void JSReceiver::LocalLookup(String* name, LookupResult* result) { |
- if (IsJSProxy()) { |
- result->HandlerResult(); |
- } else { |
- JSObject::cast(this)->LocalLookup(name, result); |
- } |
-} |
- |
- |
-void JSObject::LocalLookup(String* name, LookupResult* result) { |
ASSERT(name->IsString()); |
Heap* heap = GetHeap(); |
@@ -3526,28 +3945,36 @@ |
Object* proto = GetPrototype(); |
if (proto->IsNull()) return result->NotFound(); |
ASSERT(proto->IsJSGlobalObject()); |
- return JSObject::cast(proto)->LocalLookup(name, result); |
+ return JSReceiver::cast(proto)->LocalLookup(name, result); |
} |
+ if (IsJSProxy()) { |
+ result->HandlerResult(JSProxy::cast(this)); |
+ return; |
+ } |
+ |
// Do not use inline caching if the object is a non-global object |
// that requires access checks. |
- if (!IsJSGlobalProxy() && IsAccessCheckNeeded()) { |
+ if (IsAccessCheckNeeded()) { |
result->DisallowCaching(); |
} |
+ JSObject* js_object = JSObject::cast(this); |
+ |
// Check __proto__ before interceptor. |
if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) { |
- result->ConstantResult(this); |
+ result->ConstantResult(js_object); |
return; |
} |
// Check for lookup interceptor except when bootstrapping. |
- if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) { |
- result->InterceptorResult(this); |
+ if (js_object->HasNamedInterceptor() && |
+ !heap->isolate()->bootstrapper()->IsActive()) { |
+ result->InterceptorResult(js_object); |
return; |
} |
- LocalLookupRealNamedProperty(name, result); |
+ js_object->LocalLookupRealNamedProperty(name, result); |
} |
@@ -3557,7 +3984,7 @@ |
for (Object* current = this; |
current != heap->null_value(); |
current = JSObject::cast(current)->GetPrototype()) { |
- JSObject::cast(current)->LocalLookup(name, result); |
+ JSReceiver::cast(current)->LocalLookup(name, result); |
if (result->IsProperty()) return; |
} |
result->NotFound(); |
@@ -3614,6 +4041,7 @@ |
if (is_element) { |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
case FAST_DOUBLE_ELEMENTS: |
break; |
@@ -3800,7 +4228,7 @@ |
bool is_getter, |
Object* fun, |
PropertyAttributes attributes) { |
- ASSERT(fun->IsJSFunction() || fun->IsUndefined()); |
+ ASSERT(fun->IsSpecFunction() || fun->IsUndefined()); |
Isolate* isolate = GetIsolate(); |
// Check access rights if needed. |
if (IsAccessCheckNeeded() && |
@@ -3863,6 +4291,7 @@ |
// Accessors overwrite previous callbacks (cf. with getters/setters). |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
case FAST_DOUBLE_ELEMENTS: |
break; |
@@ -4086,7 +4515,7 @@ |
// Allocate the code cache if not present. |
if (code_cache()->IsFixedArray()) { |
Object* result; |
- { MaybeObject* maybe_result = code->heap()->AllocateCodeCache(); |
+ { MaybeObject* maybe_result = GetHeap()->AllocateCodeCache(); |
if (!maybe_result->ToObject(&result)) return maybe_result; |
} |
set_code_cache(result); |
@@ -4128,7 +4557,7 @@ |
// Traverse the transition tree without using a stack. We do this by |
// reversing the pointers in the maps and descriptor arrays. |
Map* current = this; |
- Map* meta_map = heap()->meta_map(); |
+ Map* meta_map = GetHeap()->meta_map(); |
Object** map_or_index_field = NULL; |
while (current != meta_map) { |
DescriptorArray* d = reinterpret_cast<DescriptorArray*>( |
@@ -4149,7 +4578,7 @@ |
// of the next map and recording the index in the transition array in |
// the map field of the array. |
Map* next = Map::cast(contents->get(i)); |
- next->set_map(current); |
+ next->set_map_unsafe(current); |
*map_or_index_field = Smi::FromInt(i + 2); |
current = next; |
map_done = false; |
@@ -4174,23 +4603,23 @@ |
Object* perhaps_map = prototype_transitions->get(i); |
if (perhaps_map->IsMap()) { |
Map* next = Map::cast(perhaps_map); |
- next->set_map(current); |
+ next->set_map_unsafe(current); |
*proto_map_or_index_field = |
Smi::FromInt(i + kProtoTransitionElementsPerEntry); |
current = next; |
continue; |
} |
} |
- *proto_map_or_index_field = heap()->fixed_array_map(); |
+ *proto_map_or_index_field = GetHeap()->fixed_array_map(); |
if (map_or_index_field != NULL) { |
- *map_or_index_field = heap()->fixed_array_map(); |
+ *map_or_index_field = GetHeap()->fixed_array_map(); |
} |
// The callback expects a map to have a real map as its map, so we save |
// the map field, which is being used to track the traversal and put the |
// correct map (the meta_map) in place while we do the callback. |
Map* prev = current->map(); |
- current->set_map(meta_map); |
+ current->set_map_unsafe(meta_map); |
callback(current, data); |
current = prev; |
} |
@@ -4406,7 +4835,7 @@ |
MUST_USE_RESULT MaybeObject* AsObject() { |
ASSERT(code_ != NULL); |
Object* obj; |
- { MaybeObject* maybe_obj = code_->heap()->AllocateFixedArray(2); |
+ { MaybeObject* maybe_obj = code_->GetHeap()->AllocateFixedArray(2); |
if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
} |
FixedArray* pair = FixedArray::cast(obj); |
@@ -5995,7 +6424,7 @@ |
if (StringShape(this).IsSymbol()) return false; |
Map* map = this->map(); |
- Heap* heap = map->heap(); |
+ Heap* heap = GetHeap(); |
if (map == heap->string_map()) { |
this->set_map(heap->undetectable_string_map()); |
return true; |
@@ -6198,29 +6627,45 @@ |
} |
+void Map::CreateOneBackPointer(Map* target) { |
+#ifdef DEBUG |
+ // Verify target. |
+ Object* source_prototype = prototype(); |
+ Object* target_prototype = target->prototype(); |
+ ASSERT(source_prototype->IsJSReceiver() || |
+ source_prototype->IsMap() || |
+ source_prototype->IsNull()); |
+ ASSERT(target_prototype->IsJSReceiver() || |
+ target_prototype->IsNull()); |
+ ASSERT(source_prototype->IsMap() || |
+ source_prototype == target_prototype); |
+#endif |
+ // Point target back to source. set_prototype() will not let us set |
+ // the prototype to a map, as we do here. |
+ *RawField(target, kPrototypeOffset) = this; |
+} |
+ |
+ |
void Map::CreateBackPointers() { |
DescriptorArray* descriptors = instance_descriptors(); |
for (int i = 0; i < descriptors->number_of_descriptors(); i++) { |
if (descriptors->GetType(i) == MAP_TRANSITION || |
descriptors->GetType(i) == ELEMENTS_TRANSITION || |
descriptors->GetType(i) == CONSTANT_TRANSITION) { |
- // Get target. |
- Map* target = Map::cast(descriptors->GetValue(i)); |
-#ifdef DEBUG |
- // Verify target. |
- Object* source_prototype = prototype(); |
- Object* target_prototype = target->prototype(); |
- ASSERT(source_prototype->IsJSObject() || |
- source_prototype->IsMap() || |
- source_prototype->IsNull()); |
- ASSERT(target_prototype->IsJSObject() || |
- target_prototype->IsNull()); |
- ASSERT(source_prototype->IsMap() || |
- source_prototype == target_prototype); |
-#endif |
- // Point target back to source. set_prototype() will not let us set |
- // the prototype to a map, as we do here. |
- *RawField(target, kPrototypeOffset) = this; |
+ Object* object = reinterpret_cast<Object*>(descriptors->GetValue(i)); |
+ if (object->IsMap()) { |
+ CreateOneBackPointer(reinterpret_cast<Map*>(object)); |
+ } else { |
+ ASSERT(object->IsFixedArray()); |
+ ASSERT(descriptors->GetType(i) == ELEMENTS_TRANSITION); |
+ FixedArray* array = reinterpret_cast<FixedArray*>(object); |
+ for (int i = 0; i < array->length(); ++i) { |
+ Map* target = reinterpret_cast<Map*>(array->get(i)); |
+ if (!target->IsUndefined()) { |
+ CreateOneBackPointer(target); |
+ } |
+ } |
+ } |
} |
} |
} |
@@ -6247,16 +6692,46 @@ |
if (details.type() == MAP_TRANSITION || |
details.type() == ELEMENTS_TRANSITION || |
details.type() == CONSTANT_TRANSITION) { |
- Map* target = reinterpret_cast<Map*>(contents->get(i)); |
- ASSERT(target->IsHeapObject()); |
- if (!target->IsMarked()) { |
- ASSERT(target->IsMap()); |
- contents->set_unchecked(i + 1, NullDescriptorDetails); |
- contents->set_null_unchecked(heap, i); |
- ASSERT(target->prototype() == this || |
- target->prototype() == real_prototype); |
- // Getter prototype() is read-only, set_prototype() has side effects. |
- *RawField(target, Map::kPrototypeOffset) = real_prototype; |
+ Object* object = reinterpret_cast<Object*>(contents->get(i)); |
+ if (object->IsMap()) { |
+ Map* target = reinterpret_cast<Map*>(object); |
+ ASSERT(target->IsHeapObject()); |
+ MarkBit map_mark = Marking::MarkBitFrom(target); |
+ if (!map_mark.Get()) { |
+ ASSERT(target->IsMap()); |
+ contents->set_unchecked(i + 1, NullDescriptorDetails); |
+ contents->set_null_unchecked(heap, i); |
+ ASSERT(target->prototype() == this || |
+ target->prototype() == real_prototype); |
+ // Getter prototype() is read-only, set_prototype() has side effects. |
+ *RawField(target, Map::kPrototypeOffset) = real_prototype; |
+ } |
+ } else { |
+ ASSERT(object->IsFixedArray()); |
+ ASSERT(details.type() == ELEMENTS_TRANSITION); |
+ FixedArray* array = reinterpret_cast<FixedArray*>(contents->get(i)); |
+ bool reachable_map_found = false; |
+ for (int j = 0; j < array->length(); ++j) { |
+ Map* target = reinterpret_cast<Map*>(array->get(j)); |
+ ASSERT(target->IsHeapObject()); |
+ MarkBit map_mark = Marking::MarkBitFrom(target); |
+ if (!map_mark.Get()) { |
+ ASSERT(target->IsMap()); |
+ array->set_undefined(j); |
+ ASSERT(target->prototype() == this || |
+ target->prototype() == real_prototype); |
+ // Getter prototype() is read-only, set_prototype() has side |
+ // effects. |
+ *RawField(target, Map::kPrototypeOffset) = real_prototype; |
+ } else { |
+ reachable_map_found = true; |
+ } |
+ } |
+ // If no map was found, make sure the FixedArray also gets collected. |
+ if (!reachable_map_found) { |
+ contents->set_unchecked(i + 1, NullDescriptorDetails); |
+ contents->set_null_unchecked(heap, i); |
+ } |
} |
} |
} |
@@ -6362,7 +6837,7 @@ |
if (!maybe_new_map->ToObject(&new_object)) return maybe_new_map; |
} |
Map* new_map = Map::cast(new_object); |
- Heap* heap = new_map->heap(); |
+ Heap* heap = new_map->GetHeap(); |
set_map(new_map); |
new_map->set_constructor(value); |
new_map->set_non_instance_prototype(true); |
@@ -6393,7 +6868,7 @@ |
ASSERT(shared()->strict_mode() || map() == global_context->function_map()); |
set_map(no_prototype_map); |
- set_prototype_or_initial_map(no_prototype_map->heap()->the_hole_value()); |
+ set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value()); |
return this; |
} |
@@ -6701,7 +7176,7 @@ |
set_construction_count(kGenerousAllocationCount); |
} |
set_initial_map(map); |
- Builtins* builtins = map->heap()->isolate()->builtins(); |
+ Builtins* builtins = map->GetHeap()->isolate()->builtins(); |
ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), |
construct_stub()); |
set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); |
@@ -6721,8 +7196,9 @@ |
// then StartInobjectTracking will be called again the next time the |
// constructor is called. The countdown will continue and (possibly after |
// several more GCs) CompleteInobjectSlackTracking will eventually be called. |
- set_initial_map(map->heap()->raw_unchecked_undefined_value()); |
- Builtins* builtins = map->heap()->isolate()->builtins(); |
+ Heap* heap = map->GetHeap(); |
+ set_initial_map(heap->raw_unchecked_undefined_value()); |
+ Builtins* builtins = heap->isolate()->builtins(); |
ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), |
*RawField(this, kConstructStubOffset)); |
set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric)); |
@@ -6738,7 +7214,7 @@ |
// Resume inobject slack tracking. |
set_initial_map(map); |
- Builtins* builtins = map->heap()->isolate()->builtins(); |
+ Builtins* builtins = map->GetHeap()->isolate()->builtins(); |
ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric), |
*RawField(this, kConstructStubOffset)); |
set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown)); |
@@ -6770,7 +7246,7 @@ |
ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress()); |
Map* map = Map::cast(initial_map()); |
- Heap* heap = map->heap(); |
+ Heap* heap = map->GetHeap(); |
set_initial_map(heap->undefined_value()); |
Builtins* builtins = heap->isolate()->builtins(); |
ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), |
@@ -6833,7 +7309,7 @@ |
void Code::InvalidateRelocation() { |
- set_relocation_info(heap()->empty_byte_array()); |
+ set_relocation_info(GetHeap()->empty_byte_array()); |
} |
@@ -6867,7 +7343,7 @@ |
Handle<Object> p = it.rinfo()->target_object_handle(origin); |
it.rinfo()->set_target_object(*p); |
} else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { |
- Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle(); |
+ Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle(); |
it.rinfo()->set_target_cell(*cell); |
} else if (RelocInfo::IsCodeTarget(mode)) { |
// rewrite code handles in inline cache targets to direct |
@@ -7270,8 +7746,10 @@ |
} |
-MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity, |
- int length) { |
+MaybeObject* JSObject::SetFastElementsCapacityAndLength( |
+ int capacity, |
+ int length, |
+ SetFastElementsCapacityMode set_capacity_mode) { |
Heap* heap = GetHeap(); |
// We should never end in here with a pixel or external array. |
ASSERT(!HasExternalArrayElements()); |
@@ -7288,15 +7766,25 @@ |
Map* new_map = NULL; |
if (elements()->map() != heap->non_strict_arguments_elements_map()) { |
Object* object; |
- MaybeObject* maybe = map()->GetFastElementsMap(); |
+ bool has_fast_smi_only_elements = |
+ FLAG_smi_only_arrays && |
+ (set_capacity_mode == kAllowSmiOnlyElements) && |
+ (elements()->map()->has_fast_smi_only_elements() || |
+ elements() == heap->empty_fixed_array()); |
+ ElementsKind elements_kind = has_fast_smi_only_elements |
+ ? FAST_SMI_ONLY_ELEMENTS |
+ : FAST_ELEMENTS; |
+ MaybeObject* maybe = GetElementsTransitionMap(elements_kind); |
if (!maybe->ToObject(&object)) return maybe; |
new_map = Map::cast(object); |
} |
- switch (GetElementsKind()) { |
+ ElementsKind elements_kind = GetElementsKind(); |
+ switch (elements_kind) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: { |
AssertNoAllocation no_gc; |
- WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc); |
+ WriteBarrierMode mode(new_elements->GetWriteBarrierMode(no_gc)); |
CopyFastElementsToFast(FixedArray::cast(elements()), new_elements, mode); |
set_map(new_map); |
set_elements(new_elements); |
@@ -7391,13 +7879,15 @@ |
} |
FixedDoubleArray* elems = FixedDoubleArray::cast(obj); |
- { MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap(); |
+ { MaybeObject* maybe_obj = |
+ GetElementsTransitionMap(FAST_DOUBLE_ELEMENTS); |
if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
} |
Map* new_map = Map::cast(obj); |
AssertNoAllocation no_gc; |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: { |
elems->Initialize(FixedArray::cast(elements())); |
break; |
@@ -7435,8 +7925,9 @@ |
uint32_t new_length = static_cast<uint32_t>(len->Number()); |
switch (GetElementsKind()) { |
- case FAST_ELEMENTS: { |
- case FAST_DOUBLE_ELEMENTS: |
+ case FAST_SMI_ONLY_ELEMENTS: |
+ case FAST_ELEMENTS: |
+ case FAST_DOUBLE_ELEMENTS: { |
// Make sure we never try to shrink dense arrays into sparse arrays. |
ASSERT(static_cast<uint32_t>( |
FixedArrayBase::cast(elements())->length()) <= new_length); |
@@ -7502,7 +7993,7 @@ |
Handle<FixedArray> new_backing = FACTORY->NewFixedArray(new_size); |
// Can't use this any more now because we may have had a GC! |
for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i)); |
- self->SetContent(*new_backing); |
+ GetIsolate()->factory()->SetContent(self, new_backing); |
} |
@@ -7525,13 +8016,15 @@ |
if (value < 0) return ArrayLengthRangeError(GetHeap()); |
ElementsKind elements_kind = GetElementsKind(); |
switch (elements_kind) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
case FAST_DOUBLE_ELEMENTS: { |
int old_capacity = FixedArrayBase::cast(elements())->length(); |
if (value <= old_capacity) { |
if (IsJSArray()) { |
Object* obj; |
- if (elements_kind == FAST_ELEMENTS) { |
+ if (elements_kind == FAST_ELEMENTS || |
+ elements_kind == FAST_SMI_ONLY_ELEMENTS) { |
MaybeObject* maybe_obj = EnsureWritableFastElements(); |
if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
} |
@@ -7542,7 +8035,8 @@ |
} else { |
Address filler_start; |
int filler_size; |
- if (GetElementsKind() == FAST_ELEMENTS) { |
+ if (elements_kind == FAST_ELEMENTS || |
+ elements_kind == FAST_SMI_ONLY_ELEMENTS) { |
FixedArray* fast_elements = FixedArray::cast(elements()); |
fast_elements->set_length(value); |
filler_start = fast_elements->address() + |
@@ -7562,13 +8056,14 @@ |
} else { |
// Otherwise, fill the unused tail with holes. |
int old_length = FastD2I(JSArray::cast(this)->length()->Number()); |
- if (GetElementsKind() == FAST_ELEMENTS) { |
+ if (elements_kind == FAST_ELEMENTS || |
+ elements_kind == FAST_SMI_ONLY_ELEMENTS) { |
FixedArray* fast_elements = FixedArray::cast(elements()); |
for (int i = value; i < old_length; i++) { |
fast_elements->set_the_hole(i); |
} |
} else { |
- ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS); |
+ ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS); |
FixedDoubleArray* fast_double_elements = |
FixedDoubleArray::cast(elements()); |
for (int i = value; i < old_length; i++) { |
@@ -7584,10 +8079,17 @@ |
int new_capacity = value > min ? value : min; |
if (!ShouldConvertToSlowElements(new_capacity)) { |
MaybeObject* result; |
- if (GetElementsKind() == FAST_ELEMENTS) { |
- result = SetFastElementsCapacityAndLength(new_capacity, value); |
+ if (elements_kind == FAST_ELEMENTS || |
+ elements_kind == FAST_SMI_ONLY_ELEMENTS) { |
+ SetFastElementsCapacityMode set_capacity_mode = |
+ elements_kind == FAST_SMI_ONLY_ELEMENTS |
+ ? kAllowSmiOnlyElements |
+ : kDontAllowSmiOnlyElements; |
+ result = SetFastElementsCapacityAndLength(new_capacity, |
+ value, |
+ set_capacity_mode); |
} else { |
- ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS); |
+ ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS); |
result = SetFastDoubleElementsCapacityAndLength(new_capacity, |
value); |
} |
@@ -7644,10 +8146,13 @@ |
// len is not a number so make the array size one and |
// set only element to len. |
Object* obj; |
- { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1); |
- if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
- } |
+ MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1); |
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
FixedArray::cast(obj)->set(0, len); |
+ |
+ maybe_obj = EnsureCanContainElements(&len, 1); |
+ if (maybe_obj->IsFailure()) return maybe_obj; |
+ |
if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1)); |
set_elements(FixedArray::cast(obj)); |
return this; |
@@ -7693,7 +8198,7 @@ |
FixedArray* new_cache; |
// Grow array by factor 2 over and above what we need. |
{ MaybeObject* maybe_cache = |
- heap()->AllocateFixedArray(transitions * 2 * step + header); |
+ GetHeap()->AllocateFixedArray(transitions * 2 * step + header); |
if (!maybe_cache->To<FixedArray>(&new_cache)) return maybe_cache; |
} |
@@ -7746,7 +8251,7 @@ |
// It is sufficient to validate that the receiver is not in the new prototype |
// chain. |
for (Object* pt = value; pt != heap->null_value(); pt = pt->GetPrototype()) { |
- if (JSObject::cast(pt) == this) { |
+ if (JSReceiver::cast(pt) == this) { |
// Cycle detected. |
HandleScope scope(heap->isolate()); |
return heap->isolate()->Throw( |
@@ -7761,8 +8266,8 @@ |
// hidden and set the new prototype on that object. |
Object* current_proto = real_receiver->GetPrototype(); |
while (current_proto->IsJSObject() && |
- JSObject::cast(current_proto)->map()->is_hidden_prototype()) { |
- real_receiver = JSObject::cast(current_proto); |
+ JSReceiver::cast(current_proto)->map()->is_hidden_prototype()) { |
+ real_receiver = JSReceiver::cast(current_proto); |
current_proto = current_proto->GetPrototype(); |
} |
} |
@@ -7795,8 +8300,16 @@ |
} |
+MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, |
+ uint32_t first_arg, |
+ uint32_t arg_count) { |
+ return EnsureCanContainElements(args->arguments() - first_arg, arg_count); |
+} |
+ |
+ |
bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) { |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: { |
uint32_t length = IsJSArray() ? |
static_cast<uint32_t> |
@@ -7857,6 +8370,11 @@ |
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); |
} |
@@ -7933,6 +8451,7 @@ |
} |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: { |
uint32_t length = IsJSArray() ? |
static_cast<uint32_t> |
@@ -8047,6 +8566,7 @@ |
ElementsKind kind = GetElementsKind(); |
switch (kind) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: { |
uint32_t length = IsJSArray() ? |
static_cast<uint32_t> |
@@ -8113,6 +8633,11 @@ |
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); |
} |
@@ -8189,9 +8714,9 @@ |
// __defineGetter__ callback |
if (structure->IsFixedArray()) { |
Object* getter = FixedArray::cast(structure)->get(kGetterIndex); |
- if (getter->IsJSFunction()) { |
- return Object::GetPropertyWithDefinedGetter(receiver, |
- JSFunction::cast(getter)); |
+ if (getter->IsSpecFunction()) { |
+ // TODO(rossberg): nicer would be to cast to some JSCallable here... |
+ return GetPropertyWithDefinedGetter(receiver, JSReceiver::cast(getter)); |
} |
// Getter is not a function. |
return isolate->heap()->undefined_value(); |
@@ -8246,8 +8771,9 @@ |
if (structure->IsFixedArray()) { |
Handle<Object> setter(FixedArray::cast(structure)->get(kSetterIndex)); |
- if (setter->IsJSFunction()) { |
- return SetPropertyWithDefinedSetter(JSFunction::cast(*setter), value); |
+ if (setter->IsSpecFunction()) { |
+ // TODO(rossberg): nicer would be to cast to some JSCallable here... |
+ return SetPropertyWithDefinedSetter(JSReceiver::cast(*setter), value); |
} else { |
if (strict_mode == kNonStrictMode) { |
return value; |
@@ -8297,7 +8823,8 @@ |
Object* value, |
StrictModeFlag strict_mode, |
bool check_prototype) { |
- ASSERT(HasFastElements() || HasFastArgumentsElements()); |
+ ASSERT(HasFastTypeElements() || |
+ HasFastArgumentsElements()); |
FixedArray* backing_store = FixedArray::cast(elements()); |
if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) { |
@@ -8322,6 +8849,24 @@ |
// Check whether there is extra space in fixed array. |
if (index < length) { |
+ if (HasFastSmiOnlyElements()) { |
+ if (!value->IsSmi()) { |
+ // If the value is a number, transition from smi-only to |
+ // FastDoubleElements. |
+ if (value->IsNumber()) { |
+ MaybeObject* maybe = |
+ SetFastDoubleElementsCapacityAndLength(length, length); |
+ if (maybe->IsFailure()) return maybe; |
+ FixedDoubleArray::cast(elements())->set(index, value->Number()); |
+ return value; |
+ } |
+ // Value is not a number, transition to generic fast elements. |
+ MaybeObject* maybe_new_map = GetElementsTransitionMap(FAST_ELEMENTS); |
+ Map* new_map; |
+ if (!maybe_new_map->To<Map>(&new_map)) return maybe_new_map; |
+ set_map(new_map); |
+ } |
+ } |
backing_store->set(index, value); |
if (IsJSArray()) { |
// Update the length of the array if needed. |
@@ -8341,8 +8886,14 @@ |
if (!ShouldConvertToSlowElements(new_capacity)) { |
ASSERT(static_cast<uint32_t>(new_capacity) > index); |
Object* new_elements; |
+ SetFastElementsCapacityMode set_capacity_mode = |
+ value->IsSmi() && HasFastSmiOnlyElements() |
+ ? kAllowSmiOnlyElements |
+ : kDontAllowSmiOnlyElements; |
MaybeObject* maybe = |
- SetFastElementsCapacityAndLength(new_capacity, index + 1); |
+ SetFastElementsCapacityAndLength(new_capacity, |
+ index + 1, |
+ set_capacity_mode); |
if (!maybe->ToObject(&new_elements)) return maybe; |
FixedArray::cast(new_elements)->set(index, value); |
return value; |
@@ -8448,7 +8999,9 @@ |
} |
MaybeObject* result = CanConvertToFastDoubleElements() |
? SetFastDoubleElementsCapacityAndLength(new_length, new_length) |
- : SetFastElementsCapacityAndLength(new_length, new_length); |
+ : SetFastElementsCapacityAndLength(new_length, |
+ new_length, |
+ kDontAllowSmiOnlyElements); |
if (result->IsFailure()) return result; |
#ifdef DEBUG |
if (FLAG_trace_normalization) { |
@@ -8492,10 +9045,15 @@ |
if (IsJSArray()) { |
CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); |
} |
- MaybeObject* maybe_obj = |
- SetFastElementsCapacityAndLength(elms_length, length); |
+ MaybeObject* maybe_obj = SetFastElementsCapacityAndLength( |
+ elms_length, |
+ length, |
+ kDontAllowSmiOnlyElements); |
if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
- return SetFastElement(index, value, strict_mode, check_prototype); |
+ return SetFastElement(index, |
+ value, |
+ strict_mode, |
+ check_prototype); |
} |
double double_value = value_is_smi |
@@ -8546,6 +9104,17 @@ |
} |
+MaybeObject* JSReceiver::SetElement(uint32_t index, |
+ Object* value, |
+ StrictModeFlag strict_mode, |
+ bool check_proto) { |
+ return IsJSProxy() |
+ ? JSProxy::cast(this)->SetElementWithHandler(index, value, strict_mode) |
+ : JSObject::cast(this)->SetElement(index, value, strict_mode, check_proto) |
+ ; |
+} |
+ |
+ |
MaybeObject* JSObject::SetElement(uint32_t index, |
Object* value, |
StrictModeFlag strict_mode, |
@@ -8592,6 +9161,7 @@ |
bool check_prototype) { |
Isolate* isolate = GetIsolate(); |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
return SetFastElement(index, value, strict_mode, check_prototype); |
case FAST_DOUBLE_ELEMENTS: |
@@ -8754,6 +9324,7 @@ |
break; |
} |
// Fall through. |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: |
backing_store = FixedArray::cast(backing_store_base); |
*capacity = backing_store->length(); |
@@ -9029,6 +9600,7 @@ |
if (this->IsStringObjectWithCharacterAt(index)) return true; |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: { |
uint32_t length = IsJSArray() ? |
static_cast<uint32_t>( |
@@ -9268,6 +9840,7 @@ |
PropertyAttributes filter) { |
int counter = 0; |
switch (GetElementsKind()) { |
+ case FAST_SMI_ONLY_ELEMENTS: |
case FAST_ELEMENTS: { |
int length = IsJSArray() ? |
Smi::cast(JSArray::cast(this)->length())->value() : |
@@ -10133,8 +10706,6 @@ |
// If the object is in dictionary mode, it is converted to fast elements |
// mode. |
MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { |
- ASSERT(!HasExternalArrayElements()); |
- |
Heap* heap = GetHeap(); |
if (HasDictionaryElements()) { |
@@ -10148,7 +10719,7 @@ |
// Convert to fast elements. |
Object* obj; |
- { MaybeObject* maybe_obj = map()->GetFastElementsMap(); |
+ { MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS); |
if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
} |
Map* new_map = Map::cast(obj); |
@@ -10164,13 +10735,16 @@ |
set_map(new_map); |
set_elements(fast_elements); |
+ } else if (HasExternalArrayElements()) { |
+ // External arrays cannot have holes or undefined elements. |
+ return Smi::FromInt(ExternalArray::cast(elements())->length()); |
} else if (!HasFastDoubleElements()) { |
Object* obj; |
{ MaybeObject* maybe_obj = EnsureWritableFastElements(); |
if (!maybe_obj->ToObject(&obj)) return maybe_obj; |
} |
} |
- ASSERT(HasFastElements() || HasFastDoubleElements()); |
+ ASSERT(HasFastTypeElements() || HasFastDoubleElements()); |
// Collect holes at the end, undefined before that and the rest at the |
// start, and return the number of non-hole, non-undefined values. |
@@ -11294,9 +11868,9 @@ |
} |
-Object* ObjectHashTable::Lookup(JSObject* key) { |
+Object* ObjectHashTable::Lookup(JSReceiver* key) { |
// If the object does not have an identity hash, it was never used as a key. |
- MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::OMIT_CREATION); |
+ MaybeObject* maybe_hash = key->GetIdentityHash(OMIT_CREATION); |
if (maybe_hash->IsFailure()) return GetHeap()->undefined_value(); |
int entry = FindEntry(key); |
if (entry == kNotFound) return GetHeap()->undefined_value(); |
@@ -11304,10 +11878,10 @@ |
} |
-MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) { |
+MaybeObject* ObjectHashTable::Put(JSReceiver* key, Object* value) { |
// Make sure the key object has an identity hash code. |
int hash; |
- { MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::ALLOW_CREATION); |
+ { MaybeObject* maybe_hash = key->GetIdentityHash(ALLOW_CREATION); |
if (maybe_hash->IsFailure()) return maybe_hash; |
hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value(); |
} |
@@ -11337,7 +11911,7 @@ |
} |
-void ObjectHashTable::AddEntry(int entry, JSObject* key, Object* value) { |
+void ObjectHashTable::AddEntry(int entry, JSReceiver* key, Object* value) { |
set(EntryToIndex(entry), key); |
set(EntryToIndex(entry) + 1, value); |
ElementAdded(); |