Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Unified Diff: src/objects.cc

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

Powered by Google App Engine
This is Rietveld 408576698