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.cc1595
1 files changed, 1126 insertions, 469 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 41b4fd4db..561273230 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -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 @@ Object* Object::ToBoolean() {
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()) {
- holder = global_context->string_function()->instance_prototype();
- } else if (heap_object->IsHeapNumber()) {
+ if (IsNumber()) {
holder = global_context->number_function()->instance_prototype();
- } else if (heap_object->IsBoolean()) {
+ } else if (IsString()) {
+ holder = global_context->string_function()->instance_prototype();
+ } 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::GetPropertyWithReceiver(Object* receiver,
}
-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 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
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 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
// __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::GetPropertyWithCallback(Object* receiver,
}
-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 @@ MaybeObject* JSObject::GetPropertyWithFailedAccessCheck(
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 @@ MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) {
}
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);
@@ -537,7 +545,9 @@ MaybeObject* Object::GetProperty(Object* receiver,
// holder in the prototype chain.
// Proxy handlers do not use the proxy's prototype, so we can skip this.
if (!result->IsHandler()) {
- Object* last = result->IsProperty() ? result->holder() : heap->null_value();
+ Object* last = result->IsProperty()
+ ? result->holder()
+ : Object::cast(heap->null_value());
ASSERT(this != this->GetPrototype());
for (Object* current = this; true; current = current->GetPrototype()) {
if (current->IsAccessCheckNeeded()) {
@@ -566,30 +576,26 @@ MaybeObject* Object::GetProperty(Object* receiver,
}
*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 +619,21 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
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 +876,9 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
// 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 +925,10 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
// 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 +1004,7 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
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 +1032,7 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
// 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 +1054,6 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
global_object ? "Global Object: " : "",
vowel ? "n" : "");
accumulator->Put(str);
- accumulator->Put('>');
printed = true;
}
}
@@ -1071,7 +1075,6 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
- // if (!HEAP->InNewSpace(this)) PrintF("*", this);
Heap* heap = GetHeap();
if (!heap->Contains(this)) {
accumulator->Add("!!!INVALID POINTER!!!");
@@ -1094,7 +1097,7 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
}
switch (map()->instance_type()) {
case MAP_TYPE:
- accumulator->Add("<Map>");
+ accumulator->Add("<Map(elements=%u)>", Map::cast(this)->elements_kind());
break;
case FIXED_ARRAY_TYPE:
accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
@@ -1102,6 +1105,9 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
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 +1283,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
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 +1540,7 @@ MaybeObject* JSObject::AddConstantFunctionProperty(
// 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 +1616,7 @@ MaybeObject* JSObject::AddProperty(String* name,
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 +1665,14 @@ MaybeObject* JSObject::SetPropertyPostInterceptor(
// 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 +1711,7 @@ MaybeObject* JSObject::ConvertDescriptorToFieldAndMapTransition(
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 +1895,9 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
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,22 +1916,24 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
}
-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;
- Object** argv[] = { value_handle.location() };
- Execution::Call(fun, self, 1, argv, &has_pending_exception);
+ Handle<Object> argv[] = { value_handle };
+ Execution::Call(fun, self, ARRAY_SIZE(argv), argv, &has_pending_exception);
// Check for pending exception and return the result.
if (has_pending_exception) return Failure::Exception();
return *value_handle;
@@ -1928,6 +1946,9 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name,
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 +1969,16 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(
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,6 +2000,60 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(
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
+ // getPropertyDescriptor 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();
@@ -1985,7 +2070,8 @@ void Map::LookupInDescriptors(JSObject* holder,
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,75 +2085,239 @@ void Map::LookupInDescriptors(JSObject* holder,
}
-MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind,
- bool safe_to_add_transition) {
- Heap* current_heap = heap();
+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;
+}
+
+
+String* Map::elements_transition_sentinel_name() {
+ return GetHeap()->empty_symbol();
+}
+
+
+Object* Map::GetDescriptorContents(String* sentinel_name,
+ bool* safe_to_add_transition) {
+ // Get the cached index for the descriptors lookup, or find and cache it.
DescriptorArray* descriptors = instance_descriptors();
- String* elements_transition_sentinel_name = current_heap->empty_symbol();
+ DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache();
+ int index = cache->Lookup(descriptors, sentinel_name);
+ if (index == DescriptorLookupCache::kAbsent) {
+ index = descriptors->Search(sentinel_name);
+ cache->Update(descriptors, sentinel_name, index);
+ }
+ // If the transition already exists, return its descriptor.
+ if (index != DescriptorArray::kNotFound) {
+ PropertyDetails details(descriptors->GetDetails(index));
+ if (details.type() == ELEMENTS_TRANSITION) {
+ return descriptors->GetValue(index);
+ } else {
+ *safe_to_add_transition = false;
+ }
+ }
+ return NULL;
+}
+
+
+Map* Map::LookupElementsTransitionMap(ElementsKind elements_kind,
+ bool* safe_to_add_transition) {
+ // Special case: indirect SMI->FAST transition (cf. comment in
+ // AddElementsTransition()).
+ if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS &&
+ elements_kind == FAST_ELEMENTS) {
+ Map* double_map = this->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS,
+ safe_to_add_transition);
+ if (double_map == NULL) return double_map;
+ return double_map->LookupElementsTransitionMap(FAST_ELEMENTS,
+ safe_to_add_transition);
+ }
+ Object* descriptor_contents = GetDescriptorContents(
+ elements_transition_sentinel_name(), safe_to_add_transition);
+ if (descriptor_contents != NULL) {
+ Map* maybe_transition_map =
+ GetElementsTransitionMapFromDescriptor(descriptor_contents,
+ elements_kind);
+ ASSERT(maybe_transition_map == NULL || maybe_transition_map->IsMap());
+ return maybe_transition_map;
+ }
+ return NULL;
+}
+
+
+MaybeObject* Map::AddElementsTransition(ElementsKind elements_kind,
+ Map* transitioned_map) {
+ // The map transition graph should be a tree, therefore the transition
+ // from SMI to FAST elements is not done directly, but by going through
+ // DOUBLE elements first.
+ if (this->elements_kind() == FAST_SMI_ONLY_ELEMENTS &&
+ elements_kind == FAST_ELEMENTS) {
+ bool safe_to_add = true;
+ Map* double_map = this->LookupElementsTransitionMap(
+ FAST_DOUBLE_ELEMENTS, &safe_to_add);
+ // This method is only called when safe_to_add_transition has been found
+ // to be true earlier.
+ ASSERT(safe_to_add);
+
+ if (double_map == NULL) {
+ MaybeObject* maybe_map = this->CopyDropTransitions();
+ if (!maybe_map->To(&double_map)) return maybe_map;
+ double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS);
+ MaybeObject* maybe_double_transition = this->AddElementsTransition(
+ FAST_DOUBLE_ELEMENTS, double_map);
+ if (maybe_double_transition->IsFailure()) return maybe_double_transition;
+ }
+ return double_map->AddElementsTransition(FAST_ELEMENTS, transitioned_map);
+ }
+
+ bool safe_to_add_transition = true;
+ Object* descriptor_contents = GetDescriptorContents(
+ elements_transition_sentinel_name(), &safe_to_add_transition);
+ // This method is only called when safe_to_add_transition has been found
+ // to be true earlier.
+ ASSERT(safe_to_add_transition);
+ MaybeObject* maybe_new_contents =
+ AddElementsTransitionMapToDescriptor(descriptor_contents,
+ transitioned_map);
+ Object* new_contents;
+ if (!maybe_new_contents->ToObject(&new_contents)) {
+ return maybe_new_contents;
+ }
+
+ ElementsTransitionDescriptor desc(elements_transition_sentinel_name(),
+ new_contents);
+ Object* new_descriptors;
+ MaybeObject* maybe_new_descriptors =
+ instance_descriptors()->CopyInsert(&desc, KEEP_TRANSITIONS);
+ if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
+ return maybe_new_descriptors;
+ }
+ set_instance_descriptors(DescriptorArray::cast(new_descriptors));
+ return this;
+}
+
+
+MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind to_kind) {
+ Map* current_map = map();
+ ElementsKind from_kind = current_map->elements_kind();
+
+ if (from_kind == to_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 caused by objects
+ // with elements that switch back and forth between dictionary and fast
+ // element mode.
+ if (from_kind == DICTIONARY_ELEMENTS && to_kind == FAST_ELEMENTS) {
+ safe_to_add_transition = false;
+ }
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();
- int index = cache->Lookup(descriptors, elements_transition_sentinel_name);
- if (index == DescriptorLookupCache::kAbsent) {
- index = descriptors->Search(elements_transition_sentinel_name);
- cache->Update(descriptors,
- elements_transition_sentinel_name,
- index);
- }
-
- // If the transition already exists, check the type. If there is a match,
- // 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);
- } else {
- safe_to_add_transition = false;
- }
+ Map* maybe_transition_map = current_map->LookupElementsTransitionMap(
+ to_kind, &safe_to_add_transition);
+ if (maybe_transition_map != NULL) {
+ return maybe_transition_map;
}
}
+ Map* new_map = NULL;
+
// No transition to an existing map for the given ElementsKind. Make a new
// one.
- Object* obj;
- { MaybeObject* maybe_map = CopyDropTransitions();
- if (!maybe_map->ToObject(&obj)) return maybe_map;
+ { MaybeObject* maybe_map = current_map->CopyDropTransitions();
+ if (!maybe_map->To(&new_map)) return maybe_map;
}
- Map* new_map = Map::cast(obj);
- new_map->set_elements_kind(elements_kind);
- GetIsolate()->counters()->map_to_external_array_elements()->Increment();
+ new_map->set_elements_kind(to_kind);
// 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.
- ElementsTransitionDescriptor desc(elements_transition_sentinel_name,
- Map::cast(new_map),
- elements_kind);
- Object* new_descriptors;
- MaybeObject* maybe_new_descriptors = descriptors->CopyInsert(
- &desc,
- KEEP_TRANSITIONS);
- if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
- return maybe_new_descriptors;
- }
- descriptors = DescriptorArray::cast(new_descriptors);
- set_instance_descriptors(descriptors);
+ MaybeObject* maybe_transition =
+ current_map->AddElementsTransition(to_kind, new_map);
+ if (maybe_transition->IsFailure()) return maybe_transition;
}
-
return new_map;
}
@@ -2078,6 +2328,7 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
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 +2455,7 @@ MaybeObject* JSReceiver::SetProperty(LookupResult* result,
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,22 +2469,11 @@ bool JSProxy::HasPropertyWithHandler(String* name_raw) {
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 +2489,82 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
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();
+
+ 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(
+ "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args);
if (isolate->has_pending_exception()) return Failure::Exception();
- if (trap->IsUndefined()) {
- trap = isolate->derived_set_trap();
- }
- // 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();
+ 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;
+ Handle<Object> argv[] = { result };
+ 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.
+ }
+ // The proxy does not define the property as an accessor.
+ *found = false;
return *value;
}
@@ -2277,31 +2575,16 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
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 +2594,73 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler(
}
+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;
+ Handle<Object> argv[] = { result };
+ 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 +2669,9 @@ void JSProxy::Fix() {
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,9 +2679,42 @@ void JSProxy::Fix() {
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> argv[]) {
+ 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);
+ }
+
+ bool threw;
+ return Execution::Call(trap, handler, argc, argv, &threw);
+}
+
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
String* name,
@@ -2386,48 +2739,46 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
}
// 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 +3007,8 @@ PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver(
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 +3038,8 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver,
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 +3205,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
}
}
- 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,6 +3227,10 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
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 +3264,14 @@ MaybeObject* JSObject::NormalizeElements() {
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 +3306,8 @@ MaybeObject* JSObject::NormalizeElements() {
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 +3327,14 @@ MaybeObject* JSObject::NormalizeElements() {
// 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,69 +3348,8 @@ MaybeObject* JSObject::NormalizeElements() {
}
-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()) {
- // 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();
- if ((descriptors->number_of_descriptors() > 0) &&
- (descriptors->GetKey(0) == heap->hidden_symbol()) &&
- descriptors->IsProperty(0)) {
- ASSERT(descriptors->GetType(0) == FIELD);
- return obj->FastPropertyAt(descriptors->GetFieldIndex(0));
- }
- }
-
- // 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;
- }
- return obj->SetHiddenPropertiesObject(hidden_obj);
- } else {
- return heap->undefined_value();
- }
- }
- return obj->GetHiddenPropertiesObject();
-}
-
-
-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());
- }
- }
int hash_value;
int attempts = 0;
@@ -3067,17 +3361,209 @@ MaybeObject* JSObject::GetIdentityHash(HiddenPropertiesFlag flag) {
} 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 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() {
+ return GetPropertyAttributePostInterceptor(this,
+ GetHeap()->hidden_symbol(),
+ false) != ABSENT;
+}
+
+
+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 = this->map()->instance_descriptors();
+ if ((descriptors->number_of_descriptors() > 0) &&
+ (descriptors->GetKey(0) == GetHeap()->hidden_symbol()) &&
+ descriptors->IsProperty(0)) {
+ ASSERT(descriptors->GetType(0) == FIELD);
+ Object* hidden_store =
+ this->FastPropertyAt(descriptors->GetFieldIndex(0));
+ return StringDictionary::cast(hidden_store);
+ }
+ } 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);
+ }
+ }
+ 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::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;
+ }
+ }
+ MaybeObject* store_result =
+ SetPropertyPostInterceptor(GetHeap()->hidden_symbol(),
+ dictionary,
+ DONT_ENUM,
+ kNonStrictMode);
+ if (store_result->IsFailure()) return store_result;
+ return this;
+}
+
+
MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name,
DeleteMode mode) {
// Check local property, ignore interceptor.
@@ -3194,9 +3680,16 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
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);
}
@@ -3260,7 +3753,8 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) {
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()
@@ -3280,7 +3774,7 @@ bool JSObject::ReferencesObjectFromElements(FixedArray* elements,
// 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?
@@ -3315,6 +3809,8 @@ bool JSObject::ReferencesObject(Object* obj) {
// 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());
@@ -3502,15 +3998,6 @@ AccessorDescriptor* Map::FindAccessor(String* name) {
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();
@@ -3519,28 +4006,36 @@ void JSObject::LocalLookup(String* name, LookupResult* result) {
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);
}
@@ -3550,7 +4045,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) {
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();
@@ -3561,7 +4056,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) {
void JSObject::LookupCallback(String* name, LookupResult* result) {
Heap* heap = GetHeap();
for (Object* current = this;
- current != heap->null_value();
+ current != heap->null_value() && current->IsJSObject();
current = JSObject::cast(current)->GetPrototype()) {
JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
if (result->IsProperty() && result->type() == CALLBACKS) return;
@@ -3607,6 +4102,7 @@ MaybeObject* JSObject::DefineGetterSetter(String* name,
if (is_element) {
switch (GetElementsKind()) {
+ case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
break;
@@ -3793,7 +4289,7 @@ MaybeObject* JSObject::DefineAccessor(String* name,
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() &&
@@ -3856,6 +4352,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
// Accessors overwrite previous callbacks (cf. with getters/setters).
switch (GetElementsKind()) {
+ case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
break;
@@ -4079,7 +4576,7 @@ MaybeObject* Map::UpdateCodeCache(String* name, Code* code) {
// 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);
@@ -4121,7 +4618,7 @@ void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
// 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*>(
@@ -4142,7 +4639,7 @@ void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
// 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;
@@ -4167,23 +4664,23 @@ void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
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;
}
@@ -4399,7 +4896,7 @@ class CodeCacheHashTableKey : public HashTableKey {
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);
@@ -5988,7 +6485,7 @@ bool String::MarkAsUndetectable() {
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;
@@ -6191,29 +6688,45 @@ void String::PrintOn(FILE* file) {
}
+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);
+ }
+ }
+ }
}
}
}
@@ -6240,16 +6753,46 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
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*>(object);
+ 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 if (target->IsMap()) {
+ 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);
+ }
}
}
}
@@ -6355,7 +6898,7 @@ MaybeObject* JSFunction::SetPrototype(Object* value) {
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);
@@ -6386,7 +6929,7 @@ Object* JSFunction::RemovePrototype() {
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;
}
@@ -6679,6 +7222,8 @@ bool SharedFunctionInfo::VerifyBailoutId(int id) {
void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
ASSERT(!IsInobjectSlackTrackingInProgress());
+ if (!FLAG_clever_optimizations) return;
+
// Only initiate the tracking the first time.
if (live_objects_may_exist()) return;
set_live_objects_may_exist(true);
@@ -6694,7 +7239,7 @@ void SharedFunctionInfo::StartInobjectSlackTracking(Map* map) {
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));
@@ -6714,8 +7259,9 @@ void SharedFunctionInfo::DetachInitialMap() {
// 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));
@@ -6731,7 +7277,7 @@ void SharedFunctionInfo::AttachInitialMap(Map* map) {
// 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));
@@ -6763,7 +7309,7 @@ void SharedFunctionInfo::CompleteInobjectSlackTracking() {
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),
@@ -6825,8 +7371,14 @@ void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
}
+void ObjectVisitor::VisitEmbeddedPointer(RelocInfo* rinfo) {
+ ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT);
+ VisitPointer(rinfo->target_object_address());
+}
+
+
void Code::InvalidateRelocation() {
- set_relocation_info(heap()->empty_byte_array());
+ set_relocation_info(GetHeap()->empty_byte_array());
}
@@ -6860,7 +7412,7 @@ void Code::CopyFrom(const CodeDesc& desc) {
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
@@ -7263,8 +7815,10 @@ static void CopySlowElementsToFast(NumberDictionary* source,
}
-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());
@@ -7281,15 +7835,24 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
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 =
+ (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);
@@ -7384,13 +7947,15 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
}
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;
@@ -7428,8 +7993,9 @@ MaybeObject* JSObject::SetSlowElements(Object* len) {
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);
@@ -7495,7 +8061,7 @@ void JSArray::Expand(int required_size) {
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);
}
@@ -7518,13 +8084,15 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
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;
}
@@ -7535,7 +8103,8 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
} 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() +
@@ -7555,13 +8124,14 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
} 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++) {
@@ -7577,10 +8147,17 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
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);
}
@@ -7637,10 +8214,13 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
// 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;
@@ -7686,7 +8266,7 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
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;
}
@@ -7739,7 +8319,7 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
// 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(
@@ -7754,8 +8334,8 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
// 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();
}
}
@@ -7788,8 +8368,16 @@ MaybeObject* JSReceiver::SetPrototype(Object* value,
}
+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>
@@ -7850,6 +8438,11 @@ bool JSObject::HasElementPostInterceptor(JSReceiver* receiver, uint32_t index) {
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);
}
@@ -7926,6 +8519,7 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
}
switch (GetElementsKind()) {
+ case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS: {
uint32_t length = IsJSArray() ?
static_cast<uint32_t>
@@ -8040,6 +8634,7 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) {
ElementsKind kind = GetElementsKind();
switch (kind) {
+ case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS: {
uint32_t length = IsJSArray() ?
static_cast<uint32_t>
@@ -8106,6 +8701,11 @@ bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) {
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);
}
@@ -8182,9 +8782,9 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
// __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();
@@ -8239,8 +8839,9 @@ MaybeObject* JSObject::SetElementWithCallback(Object* structure,
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;
@@ -8290,7 +8891,8 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
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()) {
@@ -8301,10 +8903,10 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
if (!maybe->ToObject(&writable)) return maybe;
backing_store = FixedArray::cast(writable);
}
- uint32_t length = static_cast<uint32_t>(backing_store->length());
+ uint32_t capacity = static_cast<uint32_t>(backing_store->length());
if (check_prototype &&
- (index >= length || backing_store->get(index)->IsTheHole())) {
+ (index >= capacity || backing_store->get(index)->IsTheHole())) {
bool found;
MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
value,
@@ -8313,39 +8915,71 @@ MaybeObject* JSObject::SetFastElement(uint32_t index,
if (found) return result;
}
- // Check whether there is extra space in fixed array.
- if (index < length) {
- backing_store->set(index, value);
- if (IsJSArray()) {
- // Update the length of the array if needed.
- uint32_t array_length = 0;
- CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
- if (index >= array_length) {
- JSArray::cast(this)->set_length(Smi::FromInt(index + 1));
+ uint32_t new_capacity = capacity;
+ // Check if the length property of this object needs to be updated.
+ uint32_t array_length = 0;
+ bool must_update_array_length = false;
+ if (IsJSArray()) {
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
+ if (index >= array_length) {
+ must_update_array_length = true;
+ array_length = index + 1;
+ }
+ }
+ // Check if the capacity of the backing store needs to be increased, or if
+ // a transition to slow elements is necessary.
+ if (index >= capacity) {
+ bool convert_to_slow = true;
+ if ((index - capacity) < kMaxGap) {
+ new_capacity = NewElementsCapacity(index + 1);
+ ASSERT(new_capacity > index);
+ if (!ShouldConvertToSlowElements(new_capacity)) {
+ convert_to_slow = false;
}
}
+ if (convert_to_slow) {
+ MaybeObject* result = NormalizeElements();
+ if (result->IsFailure()) return result;
+ return SetDictionaryElement(index, value, strict_mode, check_prototype);
+ }
+ }
+ // Convert to fast double elements if appropriate.
+ if (HasFastSmiOnlyElements() && !value->IsSmi() && value->IsNumber()) {
+ MaybeObject* maybe =
+ SetFastDoubleElementsCapacityAndLength(new_capacity, array_length);
+ if (maybe->IsFailure()) return maybe;
+ FixedDoubleArray::cast(elements())->set(index, value->Number());
return value;
}
-
- // Allow gap in fast case.
- if ((index - length) < kMaxGap) {
- // Try allocating extra space.
- int new_capacity = NewElementsCapacity(index + 1);
- if (!ShouldConvertToSlowElements(new_capacity)) {
- ASSERT(static_cast<uint32_t>(new_capacity) > index);
- Object* new_elements;
- MaybeObject* maybe =
- SetFastElementsCapacityAndLength(new_capacity, index + 1);
- if (!maybe->ToObject(&new_elements)) return maybe;
- FixedArray::cast(new_elements)->set(index, value);
- return value;
- }
+ // Change elements kind from SMI_ONLY to generic FAST if necessary.
+ if (HasFastSmiOnlyElements() && !value->IsSmi()) {
+ 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);
}
-
- // Otherwise default to slow case.
- MaybeObject* result = NormalizeElements();
- if (result->IsFailure()) return result;
- return SetDictionaryElement(index, value, strict_mode, check_prototype);
+ // Increase backing store capacity if that's been decided previously.
+ if (new_capacity != capacity) {
+ Object* new_elements;
+ SetFastElementsCapacityMode set_capacity_mode =
+ value->IsSmi() && HasFastSmiOnlyElements()
+ ? kAllowSmiOnlyElements
+ : kDontAllowSmiOnlyElements;
+ MaybeObject* maybe =
+ SetFastElementsCapacityAndLength(new_capacity,
+ array_length,
+ set_capacity_mode);
+ if (!maybe->ToObject(&new_elements)) return maybe;
+ FixedArray::cast(new_elements)->set(index, value);
+ return value;
+ }
+ // Finally, set the new element and length.
+ ASSERT(elements()->IsFixedArray());
+ backing_store->set(index, value);
+ if (must_update_array_length) {
+ JSArray::cast(this)->set_length(Smi::FromInt(array_length));
+ }
+ return value;
}
@@ -8441,7 +9075,9 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
}
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) {
@@ -8485,10 +9121,15 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
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
@@ -8539,6 +9180,17 @@ MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
}
+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,
@@ -8585,6 +9237,7 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
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:
@@ -8747,6 +9400,7 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
break;
}
// Fall through.
+ case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
backing_store = FixedArray::cast(backing_store_base);
*capacity = backing_store->length();
@@ -9022,6 +9676,7 @@ bool JSObject::HasRealElementProperty(uint32_t index) {
if (this->IsStringObjectWithCharacterAt(index)) return true;
switch (GetElementsKind()) {
+ case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS: {
uint32_t length = IsJSArray() ?
static_cast<uint32_t>(
@@ -9261,6 +9916,7 @@ int JSObject::GetLocalElementKeys(FixedArray* storage,
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() :
@@ -9942,7 +10598,7 @@ template class HashTable<CompilationCacheShape, HashTableKey*>;
template class HashTable<MapCacheShape, HashTableKey*>;
-template class HashTable<ObjectHashTableShape, JSObject*>;
+template class HashTable<ObjectHashTableShape, JSReceiver*>;
template class Dictionary<StringDictionaryShape, String*>;
@@ -10126,8 +10782,6 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
// 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()) {
@@ -10141,7 +10795,7 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
// 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);
@@ -10157,13 +10811,16 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
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.
@@ -11287,9 +11944,9 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor(
}
-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();
@@ -11297,10 +11954,10 @@ Object* ObjectHashTable::Lookup(JSObject* key) {
}
-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();
}
@@ -11330,7 +11987,7 @@ MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) {
}
-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();
@@ -11594,7 +12251,7 @@ int BreakPointInfo::GetBreakPointCount() {
// Multiple break points.
return FixedArray::cast(break_point_objects())->length();
}
-#endif
+#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal