Upgrade to V8 3.5
Merge V8 3.5.10.24
Simple merge required updates to makefiles only.
Bug: 5688872
Change-Id: I0acdb9a1a53919d84e9a7525308e8371739d2f06
diff --git a/src/objects.cc b/src/objects.cc
index 1ab5dd2..0660dba 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -33,6 +33,7 @@
#include "codegen.h"
#include "debug.h"
#include "deoptimizer.h"
+#include "elements.h"
#include "execution.h"
#include "full-codegen.h"
#include "hydrogen.h"
@@ -602,36 +603,69 @@
MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
- Object* holder = NULL;
- if (IsSmi()) {
- Context* global_context = Isolate::Current()->context()->global_context();
- holder = global_context->number_function()->instance_prototype();
- } else {
- HeapObject* heap_object = HeapObject::cast(this);
+ Heap* heap = IsSmi()
+ ? Isolate::Current()->heap()
+ : HeapObject::cast(this)->GetHeap();
+ Object* holder = this;
- if (heap_object->IsJSObject()) {
- return JSObject::cast(this)->GetElementWithReceiver(receiver, index);
- }
- Heap* heap = heap_object->GetHeap();
- 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()) {
+ // Iterate up the prototype chain until an element is found or the null
+ // prototype is encountered.
+ 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 if (heap_object->IsBoolean()) {
- holder = global_context->boolean_function()->instance_prototype();
- } else if (heap_object->IsJSProxy()) {
- 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();
+ 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()) {
+ 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();
+ }
+ }
+ }
+
+ // Inline the case for JSObjects. Doing so significantly improves the
+ // performance of fetching elements where checking the prototype chain is
+ // necessary.
+ JSObject* js_object = JSObject::cast(holder);
+
+ // Check access rights if needed.
+ if (js_object->IsAccessCheckNeeded()) {
+ Isolate* isolate = heap->isolate();
+ if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) {
+ isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET);
+ return heap->undefined_value();
+ }
+ }
+
+ if (js_object->HasIndexedInterceptor()) {
+ return js_object->GetElementWithInterceptor(receiver, index);
+ }
+
+ if (js_object->elements() != heap->empty_fixed_array()) {
+ MaybeObject* result = js_object->GetElementsAccessor()->Get(
+ js_object->elements(),
+ index,
+ js_object,
+ receiver);
+ if (result != heap->the_hole_value()) return result;
}
}
- return JSObject::cast(holder)->GetElementWithReceiver(receiver, index);
+ return heap->undefined_value();
}
@@ -962,6 +996,11 @@
accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
break;
}
+ case JS_WEAK_MAP_TYPE: {
+ int elements = JSWeakMap::cast(this)->table()->NumberOfElements();
+ accumulator->Add("<JS WeakMap[%d]>", elements);
+ break;
+ }
case JS_REGEXP_TYPE: {
accumulator->Add("<JS RegExp>");
break;
@@ -1168,6 +1207,9 @@
case kConsStringTag:
ConsString::BodyDescriptor::IterateBody(this, v);
break;
+ case kSlicedStringTag:
+ SlicedString::BodyDescriptor::IterateBody(this, v);
+ break;
case kExternalStringTag:
if ((type & kStringEncodingMask) == kAsciiStringTag) {
reinterpret_cast<ExternalAsciiString*>(this)->
@@ -1191,6 +1233,7 @@
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_ARRAY_TYPE:
+ case JS_WEAK_MAP_TYPE:
case JS_REGEXP_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
@@ -2278,7 +2321,8 @@
if (has_exception) return Failure::Exception();
Object* bool_result = result->ToBoolean();
- if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) {
+ if (mode == STRICT_DELETION &&
+ bool_result == isolate->heap()->false_value()) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
"handler_failed", HandleVector(args, ARRAY_SIZE(args)));
@@ -2330,7 +2374,7 @@
Handle<JSProxy> self(this);
isolate->factory()->BecomeJSObject(self);
- ASSERT(IsJSObject());
+ ASSERT(self->IsJSObject());
// TODO(rossberg): recognize function proxies.
}
@@ -2470,6 +2514,9 @@
// callback setter removed. The two lines looking up the LookupResult
// result are also added. If one of the functions is changed, the other
// should be.
+// Note that this method cannot be used to set the prototype of a function
+// because ConvertDescriptorToField() which is called in "case CALLBACKS:"
+// doesn't handle function prototypes correctly.
MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
String* name,
Object* value,
@@ -2895,9 +2942,12 @@
int length = IsJSArray()
? Smi::cast(JSArray::cast(this)->length())->value()
: array->length();
+ int old_capacity = 0;
+ int used_elements = 0;
+ GetElementsCapacityAndUsage(&old_capacity, &used_elements);
NumberDictionary* dictionary = NULL;
{ Object* object;
- MaybeObject* maybe = NumberDictionary::Allocate(length);
+ MaybeObject* maybe = NumberDictionary::Allocate(used_elements);
if (!maybe->ToObject(&object)) return maybe;
dictionary = NumberDictionary::cast(object);
}
@@ -2916,7 +2966,7 @@
// exceed the capacity of new space, and we would fail repeatedly
// trying to convert the FixedDoubleArray.
MaybeObject* maybe_value_object =
- GetHeap()->AllocateHeapNumber(double_array->get(i), TENURED);
+ GetHeap()->AllocateHeapNumber(double_array->get_scalar(i), TENURED);
if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
}
} else {
@@ -2960,6 +3010,98 @@
}
+MaybeObject* JSObject::GetHiddenProperties(HiddenPropertiesFlag flag) {
+ 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;
+ }
+ // 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();
+ }
+ }
+ 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;
+ 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* JSObject::DeletePropertyPostInterceptor(String* name,
DeleteMode mode) {
// Check local property, ignore interceptor.
@@ -3009,48 +3151,6 @@
}
-MaybeObject* JSObject::DeleteElementPostInterceptor(uint32_t index,
- DeleteMode mode) {
- ASSERT(!HasExternalArrayElements());
- switch (GetElementsKind()) {
- case FAST_ELEMENTS: {
- Object* obj;
- { MaybeObject* maybe_obj = EnsureWritableFastElements();
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
- uint32_t length = IsJSArray() ?
- static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) :
- static_cast<uint32_t>(FixedArray::cast(elements())->length());
- if (index < length) {
- FixedArray::cast(elements())->set_the_hole(index);
- }
- break;
- }
- case DICTIONARY_ELEMENTS: {
- NumberDictionary* dictionary = element_dictionary();
- int entry = dictionary->FindEntry(index);
- if (entry != NumberDictionary::kNotFound) {
- Object* deleted = dictionary->DeleteProperty(entry, mode);
- if (deleted == GetHeap()->true_value()) {
- MaybeObject* maybe_elements = dictionary->Shrink(index);
- FixedArray* new_elements = NULL;
- if (!maybe_elements->To(&new_elements)) {
- return maybe_elements;
- }
- set_elements(new_elements);
- }
- return deleted;
- }
- break;
- }
- default:
- UNREACHABLE();
- break;
- }
- return GetHeap()->true_value();
-}
-
-
MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
Isolate* isolate = GetIsolate();
Heap* heap = isolate->heap();
@@ -3078,100 +3178,15 @@
ASSERT(result->IsBoolean());
return *v8::Utils::OpenHandle(*result);
}
- MaybeObject* raw_result =
- this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION);
+ MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete(
+ *this_handle,
+ index,
+ NORMAL_DELETION);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
return raw_result;
}
-MaybeObject* JSObject::DeleteFastElement(uint32_t index) {
- ASSERT(HasFastElements() || HasFastArgumentsElements());
- Heap* heap = GetHeap();
- FixedArray* backing_store = FixedArray::cast(elements());
- if (backing_store->map() == heap->non_strict_arguments_elements_map()) {
- backing_store = FixedArray::cast(backing_store->get(1));
- } else {
- Object* writable;
- MaybeObject* maybe = EnsureWritableFastElements();
- if (!maybe->ToObject(&writable)) return maybe;
- backing_store = FixedArray::cast(writable);
- }
- uint32_t length = static_cast<uint32_t>(
- IsJSArray()
- ? Smi::cast(JSArray::cast(this)->length())->value()
- : backing_store->length());
- if (index < length) {
- backing_store->set_the_hole(index);
- // If an old space backing store is larger than a certain size and
- // has too few used values, normalize it.
- // To avoid doing the check on every delete we require at least
- // one adjacent hole to the value being deleted.
- Object* hole = heap->the_hole_value();
- const int kMinLengthForSparsenessCheck = 64;
- if (backing_store->length() >= kMinLengthForSparsenessCheck &&
- !heap->InNewSpace(backing_store) &&
- ((index > 0 && backing_store->get(index - 1) == hole) ||
- (index + 1 < length && backing_store->get(index + 1) == hole))) {
- int num_used = 0;
- for (int i = 0; i < backing_store->length(); ++i) {
- if (backing_store->get(i) != hole) ++num_used;
- // Bail out early if more than 1/4 is used.
- if (4 * num_used > backing_store->length()) break;
- }
- if (4 * num_used <= backing_store->length()) {
- MaybeObject* result = NormalizeElements();
- if (result->IsFailure()) return result;
- }
- }
- }
- return heap->true_value();
-}
-
-
-MaybeObject* JSObject::DeleteDictionaryElement(uint32_t index,
- DeleteMode mode) {
- Isolate* isolate = GetIsolate();
- Heap* heap = isolate->heap();
- FixedArray* backing_store = FixedArray::cast(elements());
- bool is_arguments =
- (GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS);
- if (is_arguments) {
- backing_store = FixedArray::cast(backing_store->get(1));
- }
- NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
- int entry = dictionary->FindEntry(index);
- if (entry != NumberDictionary::kNotFound) {
- Object* result = dictionary->DeleteProperty(entry, mode);
- if (result == heap->true_value()) {
- MaybeObject* maybe_elements = dictionary->Shrink(index);
- FixedArray* new_elements = NULL;
- if (!maybe_elements->To(&new_elements)) {
- return maybe_elements;
- }
- if (is_arguments) {
- FixedArray::cast(elements())->set(1, new_elements);
- } else {
- set_elements(new_elements);
- }
- }
- if (mode == STRICT_DELETION && result == heap->false_value()) {
- // In strict mode, attempting to delete a non-configurable property
- // throws an exception.
- HandleScope scope(isolate);
- Handle<Object> holder(this);
- Handle<Object> name = isolate->factory()->NewNumberFromUint(index);
- Handle<Object> args[2] = { name, holder };
- Handle<Object> error =
- isolate->factory()->NewTypeError("strict_delete_property",
- HandleVector(args, 2));
- return isolate->Throw(*error);
- }
- }
- return heap->true_value();
-}
-
-
MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
Isolate* isolate = GetIsolate();
// Check access rights if needed.
@@ -3190,62 +3205,13 @@
if (HasIndexedInterceptor()) {
// Skip interceptor if forcing deletion.
- return (mode == FORCE_DELETION)
- ? DeleteElementPostInterceptor(index, FORCE_DELETION)
- : DeleteElementWithInterceptor(index);
+ if (mode != FORCE_DELETION) {
+ return DeleteElementWithInterceptor(index);
+ }
+ mode = JSReceiver::FORCE_DELETION;
}
- switch (GetElementsKind()) {
- case FAST_ELEMENTS:
- return DeleteFastElement(index);
-
- case DICTIONARY_ELEMENTS:
- return DeleteDictionaryElement(index, mode);
-
- case FAST_DOUBLE_ELEMENTS: {
- int length = IsJSArray()
- ? Smi::cast(JSArray::cast(this)->length())->value()
- : FixedDoubleArray::cast(elements())->length();
- if (index < static_cast<uint32_t>(length)) {
- FixedDoubleArray::cast(elements())->set_the_hole(index);
- }
- break;
- }
- case EXTERNAL_PIXEL_ELEMENTS:
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS:
- // Pixel and external array elements cannot be deleted. Just
- // silently ignore here.
- break;
-
- case NON_STRICT_ARGUMENTS_ELEMENTS: {
- FixedArray* parameter_map = FixedArray::cast(elements());
- uint32_t length = parameter_map->length();
- Object* probe =
- index < (length - 2) ? parameter_map->get(index + 2) : NULL;
- if (probe != NULL && !probe->IsTheHole()) {
- // TODO(kmillikin): We could check if this was the last aliased
- // parameter, and revert to normal elements in that case. That
- // would enable GC of the context.
- parameter_map->set_the_hole(index + 2);
- } else {
- FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
- if (arguments->IsDictionary()) {
- return DeleteDictionaryElement(index, mode);
- } else {
- return DeleteFastElement(index);
- }
- }
- break;
- }
- }
- return isolate->heap()->true_value();
+ return GetElementsAccessor()->Delete(this, index, mode);
}
@@ -3666,6 +3632,7 @@
if (is_element) {
switch (GetElementsKind()) {
case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
break;
case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
@@ -3676,7 +3643,6 @@
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS:
// Ignore getters and setters on pixel and external array
// elements.
return heap->undefined_value();
@@ -3915,6 +3881,7 @@
// Accessors overwrite previous callbacks (cf. with getters/setters).
switch (GetElementsKind()) {
case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
break;
case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
@@ -3925,7 +3892,6 @@
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS:
// Ignore getters and setters on pixel and external array
// elements.
return isolate->heap()->undefined_value();
@@ -4521,20 +4487,6 @@
}
-static bool HasKey(FixedArray* array, Object* key) {
- int len0 = array->length();
- for (int i = 0; i < len0; i++) {
- Object* element = array->get(i);
- if (element->IsSmi() && key->IsSmi() && (element == key)) return true;
- if (element->IsString() &&
- key->IsString() && String::cast(element)->Equals(String::cast(key))) {
- return true;
- }
- }
- return false;
-}
-
-
MaybeObject* PolymorphicCodeCache::Update(MapList* maps,
Code::Flags flags,
Code* code) {
@@ -4694,102 +4646,37 @@
MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
- ASSERT(!array->HasExternalArrayElements());
- switch (array->GetElementsKind()) {
- case JSObject::FAST_ELEMENTS:
- return UnionOfKeys(FixedArray::cast(array->elements()));
- case JSObject::DICTIONARY_ELEMENTS: {
- NumberDictionary* dict = array->element_dictionary();
- int size = dict->NumberOfElements();
-
- // Allocate a temporary fixed array.
- Object* object;
- { MaybeObject* maybe_object = GetHeap()->AllocateFixedArray(size);
- if (!maybe_object->ToObject(&object)) return maybe_object;
- }
- FixedArray* key_array = FixedArray::cast(object);
-
- int capacity = dict->Capacity();
- int pos = 0;
- // Copy the elements from the JSArray to the temporary fixed array.
- for (int i = 0; i < capacity; i++) {
- if (dict->IsKey(dict->KeyAt(i))) {
- key_array->set(pos++, dict->ValueAt(i));
- }
- }
- // Compute the union of this and the temporary fixed array.
- return UnionOfKeys(key_array);
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ MaybeObject* maybe_result =
+ accessor->AddElementsToFixedArray(array->elements(), this, array, array);
+ FixedArray* result;
+ if (!maybe_result->To<FixedArray>(&result)) return maybe_result;
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ for (int i = 0; i < result->length(); i++) {
+ Object* current = result->get(i);
+ ASSERT(current->IsNumber() || current->IsString());
}
- case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS:
- UNIMPLEMENTED();
- break;
- case JSObject::EXTERNAL_BYTE_ELEMENTS:
- case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case JSObject::EXTERNAL_SHORT_ELEMENTS:
- case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case JSObject::EXTERNAL_INT_ELEMENTS:
- case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case JSObject::EXTERNAL_FLOAT_ELEMENTS:
- case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
- case JSObject::EXTERNAL_PIXEL_ELEMENTS:
- case JSObject::FAST_DOUBLE_ELEMENTS:
- break;
}
- UNREACHABLE();
- return GetHeap()->null_value(); // Failure case needs to "return" a value.
+#endif
+ return result;
}
MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
- int len0 = length();
+ ElementsAccessor* accessor = ElementsAccessor::ForArray(other);
+ MaybeObject* maybe_result =
+ accessor->AddElementsToFixedArray(other, this, NULL, NULL);
+ FixedArray* result;
+ if (!maybe_result->To<FixedArray>(&result)) return maybe_result;
#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
- for (int i = 0; i < len0; i++) {
- ASSERT(get(i)->IsString() || get(i)->IsNumber());
+ for (int i = 0; i < result->length(); i++) {
+ Object* current = result->get(i);
+ ASSERT(current->IsNumber() || current->IsString());
}
}
#endif
- int len1 = other->length();
- // Optimize if 'other' is empty.
- // We cannot optimize if 'this' is empty, as other may have holes
- // or non keys.
- if (len1 == 0) return this;
-
- // Compute how many elements are not in this.
- int extra = 0;
- for (int y = 0; y < len1; y++) {
- Object* value = other->get(y);
- if (!value->IsTheHole() && !HasKey(this, value)) extra++;
- }
-
- if (extra == 0) return this;
-
- // Allocate the result
- Object* obj;
- { MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(len0 + extra);
- if (!maybe_obj->ToObject(&obj)) return maybe_obj;
- }
- // Fill in the content
- AssertNoAllocation no_gc;
- FixedArray* result = FixedArray::cast(obj);
- WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
- for (int i = 0; i < len0; i++) {
- Object* e = get(i);
- ASSERT(e->IsString() || e->IsNumber());
- result->set(i, e, mode);
- }
- // Fill in the extra keys.
- int index = 0;
- for (int y = 0; y < len1; y++) {
- Object* value = other->get(y);
- if (!value->IsTheHole() && !HasKey(this, value)) {
- Object* e = other->get(y);
- ASSERT(e->IsString() || e->IsNumber());
- result->set(len0 + index, e, mode);
- index++;
- }
- }
- ASSERT(extra == index);
return result;
}
@@ -5171,55 +5058,45 @@
}
-Vector<const char> String::ToAsciiVector() {
- ASSERT(IsAsciiRepresentation());
- ASSERT(IsFlat());
-
- int offset = 0;
+String::FlatContent String::GetFlatContent() {
int length = this->length();
- StringRepresentationTag string_tag = StringShape(this).representation_tag();
+ StringShape shape(this);
String* string = this;
- if (string_tag == kConsStringTag) {
- ConsString* cons = ConsString::cast(string);
- ASSERT(cons->second()->length() == 0);
- string = cons->first();
- string_tag = StringShape(string).representation_tag();
- }
- if (string_tag == kSeqStringTag) {
- SeqAsciiString* seq = SeqAsciiString::cast(string);
- char* start = seq->GetChars();
- return Vector<const char>(start + offset, length);
- }
- ASSERT(string_tag == kExternalStringTag);
- ExternalAsciiString* ext = ExternalAsciiString::cast(string);
- const char* start = ext->resource()->data();
- return Vector<const char>(start + offset, length);
-}
-
-
-Vector<const uc16> String::ToUC16Vector() {
- ASSERT(IsTwoByteRepresentation());
- ASSERT(IsFlat());
-
int offset = 0;
- int length = this->length();
- StringRepresentationTag string_tag = StringShape(this).representation_tag();
- String* string = this;
- if (string_tag == kConsStringTag) {
+ if (shape.representation_tag() == kConsStringTag) {
ConsString* cons = ConsString::cast(string);
- ASSERT(cons->second()->length() == 0);
+ if (cons->second()->length() != 0) {
+ return FlatContent();
+ }
string = cons->first();
- string_tag = StringShape(string).representation_tag();
+ shape = StringShape(string);
}
- if (string_tag == kSeqStringTag) {
- SeqTwoByteString* seq = SeqTwoByteString::cast(string);
- return Vector<const uc16>(seq->GetChars() + offset, length);
+ if (shape.representation_tag() == kSlicedStringTag) {
+ SlicedString* slice = SlicedString::cast(string);
+ offset = slice->offset();
+ string = slice->parent();
+ shape = StringShape(string);
+ ASSERT(shape.representation_tag() != kConsStringTag &&
+ shape.representation_tag() != kSlicedStringTag);
}
- ASSERT(string_tag == kExternalStringTag);
- ExternalTwoByteString* ext = ExternalTwoByteString::cast(string);
- const uc16* start =
- reinterpret_cast<const uc16*>(ext->resource()->data());
- return Vector<const uc16>(start + offset, length);
+ if (shape.encoding_tag() == kAsciiStringTag) {
+ const char* start;
+ if (shape.representation_tag() == kSeqStringTag) {
+ start = SeqAsciiString::cast(string)->GetChars();
+ } else {
+ start = ExternalAsciiString::cast(string)->resource()->data();
+ }
+ return FlatContent(Vector<const char>(start + offset, length));
+ } else {
+ ASSERT(shape.encoding_tag() == kTwoByteStringTag);
+ const uc16* start;
+ if (shape.representation_tag() == kSeqStringTag) {
+ start = SeqTwoByteString::cast(string)->GetChars();
+ } else {
+ start = ExternalTwoByteString::cast(string)->resource()->data();
+ }
+ return FlatContent(Vector<const uc16>(start + offset, length));
+ }
}
@@ -5290,13 +5167,17 @@
const uc16* String::GetTwoByteData(unsigned start) {
- ASSERT(!IsAsciiRepresentation());
+ ASSERT(!IsAsciiRepresentationUnderneath());
switch (StringShape(this).representation_tag()) {
case kSeqStringTag:
return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
case kExternalStringTag:
return ExternalTwoByteString::cast(this)->
ExternalTwoByteStringGetData(start);
+ case kSlicedStringTag: {
+ SlicedString* slice = SlicedString::cast(this);
+ return slice->parent()->GetTwoByteData(start + slice->offset());
+ }
case kConsStringTag:
UNREACHABLE();
return NULL;
@@ -5587,6 +5468,10 @@
max_chars);
return rbb->util_buffer;
}
+ case kSlicedStringTag:
+ return SlicedString::cast(input)->SlicedStringReadBlock(rbb,
+ offset_ptr,
+ max_chars);
default:
break;
}
@@ -5669,11 +5554,13 @@
if (str_ == NULL) return;
Handle<String> str(str_);
ASSERT(str->IsFlat());
- is_ascii_ = str->IsAsciiRepresentation();
+ String::FlatContent content = str->GetFlatContent();
+ ASSERT(content.IsFlat());
+ is_ascii_ = content.IsAscii();
if (is_ascii_) {
- start_ = str->ToAsciiVector().start();
+ start_ = content.ToAsciiVector().start();
} else {
- start_ = str->ToUC16Vector().start();
+ start_ = content.ToUC16Vector().start();
}
}
@@ -5728,6 +5615,11 @@
max_chars);
}
return;
+ case kSlicedStringTag:
+ SlicedString::cast(input)->SlicedStringReadBlockIntoBuffer(rbb,
+ offset_ptr,
+ max_chars);
+ return;
default:
break;
}
@@ -5862,6 +5754,31 @@
}
+uint16_t SlicedString::SlicedStringGet(int index) {
+ return parent()->Get(offset() + index);
+}
+
+
+const unibrow::byte* SlicedString::SlicedStringReadBlock(
+ ReadBlockBuffer* buffer, unsigned* offset_ptr, unsigned chars) {
+ unsigned offset = this->offset();
+ *offset_ptr += offset;
+ const unibrow::byte* answer = String::ReadBlock(String::cast(parent()),
+ buffer, offset_ptr, chars);
+ *offset_ptr -= offset;
+ return answer;
+}
+
+
+void SlicedString::SlicedStringReadBlockIntoBuffer(
+ ReadBlockBuffer* buffer, unsigned* offset_ptr, unsigned chars) {
+ unsigned offset = this->offset();
+ *offset_ptr += offset;
+ String::ReadBlockIntoBuffer(String::cast(parent()),
+ buffer, offset_ptr, chars);
+ *offset_ptr -= offset;
+}
+
template <typename sinkchar>
void String::WriteToFlat(String* src,
sinkchar* sink,
@@ -5929,6 +5846,13 @@
}
break;
}
+ case kAsciiStringTag | kSlicedStringTag:
+ case kTwoByteStringTag | kSlicedStringTag: {
+ SlicedString* slice = SlicedString::cast(source);
+ unsigned offset = slice->offset();
+ WriteToFlat(slice->parent(), sink, from + offset, to + offset);
+ return;
+ }
}
}
}
@@ -5993,12 +5917,13 @@
static inline bool CompareStringContentsPartial(Isolate* isolate,
IteratorA* ia,
String* b) {
- if (b->IsFlat()) {
- if (b->IsAsciiRepresentation()) {
- VectorIterator<char> ib(b->ToAsciiVector());
+ String::FlatContent content = b->GetFlatContent();
+ if (content.IsFlat()) {
+ if (content.IsAscii()) {
+ VectorIterator<char> ib(content.ToAsciiVector());
return CompareStringContents(ia, &ib);
} else {
- VectorIterator<uc16> ib(b->ToUC16Vector());
+ VectorIterator<uc16> ib(content.ToUC16Vector());
return CompareStringContents(ia, &ib);
}
} else {
@@ -6037,16 +5962,18 @@
}
Isolate* isolate = GetIsolate();
- if (lhs->IsFlat()) {
- if (lhs->IsAsciiRepresentation()) {
- Vector<const char> vec1 = lhs->ToAsciiVector();
- if (rhs->IsFlat()) {
- if (rhs->IsAsciiRepresentation()) {
- Vector<const char> vec2 = rhs->ToAsciiVector();
+ String::FlatContent lhs_content = lhs->GetFlatContent();
+ String::FlatContent rhs_content = rhs->GetFlatContent();
+ if (lhs_content.IsFlat()) {
+ if (lhs_content.IsAscii()) {
+ Vector<const char> vec1 = lhs_content.ToAsciiVector();
+ if (rhs_content.IsFlat()) {
+ if (rhs_content.IsAscii()) {
+ Vector<const char> vec2 = rhs_content.ToAsciiVector();
return CompareRawStringContents(vec1, vec2);
} else {
VectorIterator<char> buf1(vec1);
- VectorIterator<uc16> ib(rhs->ToUC16Vector());
+ VectorIterator<uc16> ib(rhs_content.ToUC16Vector());
return CompareStringContents(&buf1, &ib);
}
} else {
@@ -6056,14 +5983,14 @@
isolate->objects_string_compare_buffer_b());
}
} else {
- Vector<const uc16> vec1 = lhs->ToUC16Vector();
- if (rhs->IsFlat()) {
- if (rhs->IsAsciiRepresentation()) {
+ Vector<const uc16> vec1 = lhs_content.ToUC16Vector();
+ if (rhs_content.IsFlat()) {
+ if (rhs_content.IsAscii()) {
VectorIterator<uc16> buf1(vec1);
- VectorIterator<char> ib(rhs->ToAsciiVector());
+ VectorIterator<char> ib(rhs_content.ToAsciiVector());
return CompareStringContents(&buf1, &ib);
} else {
- Vector<const uc16> vec2(rhs->ToUC16Vector());
+ Vector<const uc16> vec2(rhs_content.ToUC16Vector());
return CompareRawStringContents(vec1, vec2);
}
} else {
@@ -6116,8 +6043,10 @@
bool String::IsAsciiEqualTo(Vector<const char> str) {
int slen = length();
if (str.length() != slen) return false;
- if (IsFlat() && IsAsciiRepresentation()) {
- return CompareChars(ToAsciiVector().start(), str.start(), slen) == 0;
+ FlatContent content = GetFlatContent();
+ if (content.IsAscii()) {
+ return CompareChars(content.ToAsciiVector().start(),
+ str.start(), slen) == 0;
}
for (int i = 0; i < slen; i++) {
if (Get(i) != static_cast<uint16_t>(str[i])) return false;
@@ -6129,8 +6058,9 @@
bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
int slen = length();
if (str.length() != slen) return false;
- if (IsFlat() && IsTwoByteRepresentation()) {
- return CompareChars(ToUC16Vector().start(), str.start(), slen) == 0;
+ FlatContent content = GetFlatContent();
+ if (content.IsTwoByte()) {
+ return CompareChars(content.ToUC16Vector().start(), str.start(), slen) == 0;
}
for (int i = 0; i < slen; i++) {
if (Get(i) != str[i]) return false;
@@ -7060,126 +6990,99 @@
PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count);
if (0 == deopt_count) return;
- PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc", "commands");
+ PrintF(out, "%6s %6s %6s %12s\n", "index", "ast id", "argc",
+ FLAG_print_code_verbose ? "commands" : "");
for (int i = 0; i < deopt_count; i++) {
- int command_count = 0;
PrintF(out, "%6d %6d %6d",
i, AstId(i)->value(), ArgumentsStackHeight(i)->value());
+
+ if (!FLAG_print_code_verbose) {
+ PrintF(out, "\n");
+ continue;
+ }
+ // Print details of the frame translation.
int translation_index = TranslationIndex(i)->value();
TranslationIterator iterator(TranslationByteArray(), translation_index);
Translation::Opcode opcode =
static_cast<Translation::Opcode>(iterator.Next());
ASSERT(Translation::BEGIN == opcode);
int frame_count = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
- frame_count);
- }
+ PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
+ frame_count);
- for (int i = 0; i < frame_count; ++i) {
- opcode = static_cast<Translation::Opcode>(iterator.Next());
- ASSERT(Translation::FRAME == opcode);
- int ast_id = iterator.Next();
- int function_id = iterator.Next();
- JSFunction* function =
- JSFunction::cast(LiteralArray()->get(function_id));
- unsigned height = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "%24s %s {ast_id=%d, function=",
- "", Translation::StringFor(opcode), ast_id);
- function->PrintName(out);
- PrintF(out, ", height=%u}\n", height);
+ while (iterator.HasNext() &&
+ Translation::BEGIN !=
+ (opcode = static_cast<Translation::Opcode>(iterator.Next()))) {
+ PrintF(out, "%24s %s ", "", Translation::StringFor(opcode));
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ UNREACHABLE();
+ break;
+
+ case Translation::FRAME: {
+ int ast_id = iterator.Next();
+ int function_id = iterator.Next();
+ JSFunction* function =
+ JSFunction::cast(LiteralArray()->get(function_id));
+ unsigned height = iterator.Next();
+ PrintF(out, "{ast_id=%d, \nfunction=", ast_id);
+ function->PrintName(out);
+ PrintF(out, ", height=%u}", height);
+ break;
+ }
+
+ case Translation::DUPLICATE:
+ break;
+
+ case Translation::REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
+ break;
+ }
+
+ case Translation::INT32_REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
+ break;
+ }
+
+ case Translation::DOUBLE_REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out, "{input=%s}",
+ DoubleRegister::AllocationIndexToString(reg_code));
+ break;
+ }
+
+ case Translation::STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d}", input_slot_index);
+ break;
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d}", input_slot_index);
+ break;
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d}", input_slot_index);
+ break;
+ }
+
+ case Translation::LITERAL: {
+ unsigned literal_index = iterator.Next();
+ PrintF(out, "{literal_id=%u}", literal_index);
+ break;
+ }
+
+ case Translation::ARGUMENTS_OBJECT:
+ break;
}
-
- // Size of translation is height plus all incoming arguments including
- // receiver.
- int size = height + function->shared()->formal_parameter_count() + 1;
- command_count += size;
- for (int j = 0; j < size; ++j) {
- opcode = static_cast<Translation::Opcode>(iterator.Next());
- if (FLAG_print_code_verbose) {
- PrintF(out, "%24s %s ", "", Translation::StringFor(opcode));
- }
-
- if (opcode == Translation::DUPLICATE) {
- opcode = static_cast<Translation::Opcode>(iterator.Next());
- if (FLAG_print_code_verbose) {
- PrintF(out, "%s ", Translation::StringFor(opcode));
- }
- --j; // Two commands share the same frame index.
- }
-
- switch (opcode) {
- case Translation::BEGIN:
- case Translation::FRAME:
- case Translation::DUPLICATE:
- UNREACHABLE();
- break;
-
- case Translation::REGISTER: {
- int reg_code = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
- }
- break;
- }
-
- case Translation::INT32_REGISTER: {
- int reg_code = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
- }
- break;
- }
-
- case Translation::DOUBLE_REGISTER: {
- int reg_code = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "{input=%s}",
- DoubleRegister::AllocationIndexToString(reg_code));
- }
- break;
- }
-
- case Translation::STACK_SLOT: {
- int input_slot_index = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "{input=%d}", input_slot_index);
- }
- break;
- }
-
- case Translation::INT32_STACK_SLOT: {
- int input_slot_index = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "{input=%d}", input_slot_index);
- }
- break;
- }
-
- case Translation::DOUBLE_STACK_SLOT: {
- int input_slot_index = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "{input=%d}", input_slot_index);
- }
- break;
- }
-
- case Translation::LITERAL: {
- unsigned literal_index = iterator.Next();
- if (FLAG_print_code_verbose) {
- PrintF(out, "{literal_id=%u}", literal_index);
- }
- break;
- }
-
- case Translation::ARGUMENTS_OBJECT:
- break;
- }
- if (FLAG_print_code_verbose) PrintF(out, "\n");
- }
+ PrintF(out, "\n");
}
- if (!FLAG_print_code_verbose) PrintF(out, " %12d\n", command_count);
}
}
@@ -7217,6 +7120,7 @@
case UNARY_OP_IC: return "UNARY_OP_IC";
case BINARY_OP_IC: return "BINARY_OP_IC";
case COMPARE_IC: return "COMPARE_IC";
+ case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC";
}
UNREACHABLE();
return NULL;
@@ -7455,7 +7359,8 @@
// exceed the capacity of new space, and we would fail repeatedly
// trying to convert the FixedDoubleArray.
MaybeObject* maybe_value_object =
- GetHeap()->AllocateHeapNumber(old_elements->get(i), TENURED);
+ GetHeap()->AllocateHeapNumber(old_elements->get_scalar(i),
+ TENURED);
if (!maybe_value_object->ToObject(&obj)) return maybe_value_object;
// Force write barrier. It's not worth trying to exploit
// elems->GetWriteBarrierMode(), since it requires an
@@ -7549,9 +7454,10 @@
switch (GetElementsKind()) {
case FAST_ELEMENTS: {
+ case FAST_DOUBLE_ELEMENTS:
// Make sure we never try to shrink dense arrays into sparse arrays.
- ASSERT(static_cast<uint32_t>(FixedArray::cast(elements())->length()) <=
- new_length);
+ ASSERT(static_cast<uint32_t>(
+ FixedArrayBase::cast(elements())->length()) <= new_length);
MaybeObject* result = NormalizeElements();
if (result->IsFailure()) return result;
@@ -7580,7 +7486,6 @@
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case EXTERNAL_PIXEL_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS:
UNREACHABLE();
break;
}
@@ -7695,8 +7600,7 @@
}
int min = NewElementsCapacity(old_capacity);
int new_capacity = value > min ? value : min;
- if (new_capacity <= kMaxFastElementsLength ||
- !ShouldConvertToSlowElements(new_capacity)) {
+ if (!ShouldConvertToSlowElements(new_capacity)) {
MaybeObject* result;
if (GetElementsKind() == FAST_ELEMENTS) {
result = SetFastElementsCapacityAndLength(new_capacity, value);
@@ -7922,6 +7826,17 @@
}
break;
}
+ case FAST_DOUBLE_ELEMENTS: {
+ uint32_t length = IsJSArray() ?
+ static_cast<uint32_t>
+ (Smi::cast(JSArray::cast(this)->length())->value()) :
+ static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
+ if ((index < length) &&
+ !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
+ return true;
+ }
+ break;
+ }
case EXTERNAL_PIXEL_ELEMENTS: {
ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
if (index < static_cast<uint32_t>(pixels->length())) {
@@ -7936,8 +7851,7 @@
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS:
- case FAST_DOUBLE_ELEMENTS: {
+ case EXTERNAL_DOUBLE_ELEMENTS: {
ExternalArray* array = ExternalArray::cast(elements());
if (index < static_cast<uint32_t>(array->length())) {
return true;
@@ -8048,6 +7962,17 @@
}
break;
}
+ case FAST_DOUBLE_ELEMENTS: {
+ uint32_t length = IsJSArray() ?
+ static_cast<uint32_t>
+ (Smi::cast(JSArray::cast(this)->length())->value()) :
+ static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
+ if ((index < length) &&
+ !FixedDoubleArray::cast(elements())->is_the_hole(index)) {
+ return FAST_ELEMENT;
+ }
+ break;
+ }
case EXTERNAL_PIXEL_ELEMENTS: {
ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT;
@@ -8065,9 +7990,6 @@
if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
break;
}
- case FAST_DOUBLE_ELEMENTS:
- UNREACHABLE();
- break;
case DICTIONARY_ELEMENTS: {
if (element_dictionary()->FindEntry(index) !=
NumberDictionary::kNotFound) {
@@ -8434,8 +8356,7 @@
if ((index - length) < kMaxGap) {
// Try allocating extra space.
int new_capacity = NewElementsCapacity(index + 1);
- if (new_capacity <= kMaxFastElementsLength ||
- !ShouldConvertToSlowElements(new_capacity)) {
+ if (!ShouldConvertToSlowElements(new_capacity)) {
ASSERT(static_cast<uint32_t>(new_capacity) > index);
Object* new_elements;
MaybeObject* maybe =
@@ -8515,14 +8436,14 @@
return isolate->Throw(*error);
}
}
- Object* new_dictionary;
+ FixedArrayBase* new_dictionary;
MaybeObject* maybe = dictionary->AtNumberPut(index, value);
- if (!maybe->ToObject(&new_dictionary)) return maybe;
+ if (!maybe->To<FixedArrayBase>(&new_dictionary)) return maybe;
if (dictionary != NumberDictionary::cast(new_dictionary)) {
if (is_arguments) {
elements->set(1, new_dictionary);
} else {
- set_elements(HeapObject::cast(new_dictionary));
+ set_elements(new_dictionary);
}
dictionary = NumberDictionary::cast(new_dictionary);
}
@@ -8543,7 +8464,7 @@
} else {
new_length = dictionary->max_number_key() + 1;
}
- MaybeObject* result = ShouldConvertToFastDoubleElements()
+ MaybeObject* result = CanConvertToFastDoubleElements()
? SetFastDoubleElementsCapacityAndLength(new_length, new_length)
: SetFastElementsCapacityAndLength(new_length, new_length);
if (result->IsFailure()) return result;
@@ -8617,8 +8538,7 @@
if ((index - elms_length) < kMaxGap) {
// Try allocating extra space.
int new_capacity = NewElementsCapacity(index+1);
- if (new_capacity <= kMaxFastElementsLength ||
- !ShouldConvertToSlowElements(new_capacity)) {
+ if (!ShouldConvertToSlowElements(new_capacity)) {
ASSERT(static_cast<uint32_t>(new_capacity) > index);
Object* obj;
{ MaybeObject* maybe_obj =
@@ -8783,71 +8703,6 @@
}
-MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
- uint32_t index) {
- // Get element works for both JSObject and JSArray since
- // JSArray::length cannot change.
- switch (GetElementsKind()) {
- case FAST_ELEMENTS: {
- FixedArray* elms = FixedArray::cast(elements());
- if (index < static_cast<uint32_t>(elms->length())) {
- Object* value = elms->get(index);
- if (!value->IsTheHole()) return value;
- }
- break;
- }
- case FAST_DOUBLE_ELEMENTS: {
- FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
- if (index < static_cast<uint32_t>(elms->length())) {
- if (!elms->is_the_hole(index)) {
- return GetHeap()->NumberFromDouble(elms->get(index));
- }
- }
- break;
- }
- case EXTERNAL_PIXEL_ELEMENTS:
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS: {
- MaybeObject* maybe_value = GetExternalElement(index);
- Object* value;
- if (!maybe_value->ToObject(&value)) return maybe_value;
- if (!value->IsUndefined()) return value;
- break;
- }
- case DICTIONARY_ELEMENTS: {
- NumberDictionary* dictionary = element_dictionary();
- int entry = dictionary->FindEntry(index);
- if (entry != NumberDictionary::kNotFound) {
- Object* element = dictionary->ValueAt(entry);
- PropertyDetails details = dictionary->DetailsAt(entry);
- if (details.type() == CALLBACKS) {
- return GetElementWithCallback(receiver,
- element,
- index,
- this);
- }
- return element;
- }
- break;
- }
- case NON_STRICT_ARGUMENTS_ELEMENTS:
- UNIMPLEMENTED();
- break;
- }
-
- // Continue searching via the prototype chain.
- Object* pt = GetPrototype();
- if (pt->IsNull()) return GetHeap()->undefined_value();
- return pt->GetElementWithReceiver(receiver, index);
-}
-
-
MaybeObject* JSObject::GetElementWithInterceptor(Object* receiver,
uint32_t index) {
Isolate* isolate = GetIsolate();
@@ -8875,218 +8730,33 @@
if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
}
- MaybeObject* raw_result =
- holder_handle->GetElementPostInterceptor(*this_handle, index);
+ Heap* heap = holder_handle->GetHeap();
+ ElementsAccessor* handler = holder_handle->GetElementsAccessor();
+ MaybeObject* raw_result = handler->Get(holder_handle->elements(),
+ index,
+ *holder_handle,
+ *this_handle);
+ if (raw_result != heap->the_hole_value()) return raw_result;
+
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
- return raw_result;
-}
-
-MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
- uint32_t index) {
- // Check access rights if needed.
- if (IsAccessCheckNeeded()) {
- Heap* heap = GetHeap();
- if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_GET)) {
- heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_GET);
- return heap->undefined_value();
- }
- }
-
- if (HasIndexedInterceptor()) {
- return GetElementWithInterceptor(receiver, index);
- }
-
- // Get element works for both JSObject and JSArray since
- // JSArray::length cannot change.
- switch (GetElementsKind()) {
- case FAST_ELEMENTS: {
- FixedArray* elms = FixedArray::cast(elements());
- if (index < static_cast<uint32_t>(elms->length())) {
- Object* value = elms->get(index);
- if (!value->IsTheHole()) return value;
- }
- break;
- }
- case FAST_DOUBLE_ELEMENTS: {
- FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
- if (index < static_cast<uint32_t>(elms->length())) {
- if (!elms->is_the_hole(index)) {
- double double_value = elms->get(index);
- return GetHeap()->NumberFromDouble(double_value);
- }
- }
- break;
- }
- case EXTERNAL_PIXEL_ELEMENTS:
- case EXTERNAL_BYTE_ELEMENTS:
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
- case EXTERNAL_SHORT_ELEMENTS:
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
- case EXTERNAL_INT_ELEMENTS:
- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS: {
- MaybeObject* maybe_value = GetExternalElement(index);
- Object* value;
- if (!maybe_value->ToObject(&value)) return maybe_value;
- if (!value->IsUndefined()) return value;
- break;
- }
- case DICTIONARY_ELEMENTS: {
- NumberDictionary* dictionary = element_dictionary();
- int entry = dictionary->FindEntry(index);
- if (entry != NumberDictionary::kNotFound) {
- Object* element = dictionary->ValueAt(entry);
- PropertyDetails details = dictionary->DetailsAt(entry);
- if (details.type() == CALLBACKS) {
- return GetElementWithCallback(receiver,
- element,
- index,
- this);
- }
- return element;
- }
- break;
- }
- case NON_STRICT_ARGUMENTS_ELEMENTS: {
- FixedArray* parameter_map = FixedArray::cast(elements());
- uint32_t length = parameter_map->length();
- Object* probe =
- (index < length - 2) ? parameter_map->get(index + 2) : NULL;
- if (probe != NULL && !probe->IsTheHole()) {
- Context* context = Context::cast(parameter_map->get(0));
- int context_index = Smi::cast(probe)->value();
- ASSERT(!context->get(context_index)->IsTheHole());
- return context->get(context_index);
- } else {
- // Object is not mapped, defer to the arguments.
- FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
- if (arguments->IsDictionary()) {
- NumberDictionary* dictionary = NumberDictionary::cast(arguments);
- int entry = dictionary->FindEntry(index);
- if (entry != NumberDictionary::kNotFound) {
- Object* element = dictionary->ValueAt(entry);
- PropertyDetails details = dictionary->DetailsAt(entry);
- if (details.type() == CALLBACKS) {
- return GetElementWithCallback(receiver,
- element,
- index,
- this);
- }
- return element;
- }
- } else if (index < static_cast<uint32_t>(arguments->length())) {
- Object* value = arguments->get(index);
- if (!value->IsTheHole()) return value;
- }
- }
- break;
- }
- }
-
- Object* pt = GetPrototype();
- Heap* heap = GetHeap();
+ Object* pt = holder_handle->GetPrototype();
if (pt == heap->null_value()) return heap->undefined_value();
- return pt->GetElementWithReceiver(receiver, index);
-}
-
-
-MaybeObject* JSObject::GetExternalElement(uint32_t index) {
- // Get element works for both JSObject and JSArray since
- // JSArray::length cannot change.
- switch (GetElementsKind()) {
- case EXTERNAL_PIXEL_ELEMENTS: {
- ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
- if (index < static_cast<uint32_t>(pixels->length())) {
- uint8_t value = pixels->get(index);
- return Smi::FromInt(value);
- }
- break;
- }
- case EXTERNAL_BYTE_ELEMENTS: {
- ExternalByteArray* array = ExternalByteArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- int8_t value = array->get(index);
- return Smi::FromInt(value);
- }
- break;
- }
- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
- ExternalUnsignedByteArray* array =
- ExternalUnsignedByteArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- uint8_t value = array->get(index);
- return Smi::FromInt(value);
- }
- break;
- }
- case EXTERNAL_SHORT_ELEMENTS: {
- ExternalShortArray* array = ExternalShortArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- int16_t value = array->get(index);
- return Smi::FromInt(value);
- }
- break;
- }
- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
- ExternalUnsignedShortArray* array =
- ExternalUnsignedShortArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- uint16_t value = array->get(index);
- return Smi::FromInt(value);
- }
- break;
- }
- case EXTERNAL_INT_ELEMENTS: {
- ExternalIntArray* array = ExternalIntArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- int32_t value = array->get(index);
- return GetHeap()->NumberFromInt32(value);
- }
- break;
- }
- case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
- ExternalUnsignedIntArray* array =
- ExternalUnsignedIntArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- uint32_t value = array->get(index);
- return GetHeap()->NumberFromUint32(value);
- }
- break;
- }
- case EXTERNAL_FLOAT_ELEMENTS: {
- ExternalFloatArray* array = ExternalFloatArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- float value = array->get(index);
- return GetHeap()->AllocateHeapNumber(value);
- }
- break;
- }
- case EXTERNAL_DOUBLE_ELEMENTS: {
- ExternalDoubleArray* array = ExternalDoubleArray::cast(elements());
- if (index < static_cast<uint32_t>(array->length())) {
- double value = array->get(index);
- return GetHeap()->AllocateHeapNumber(value);
- }
- break;
- }
- case FAST_DOUBLE_ELEMENTS:
- case FAST_ELEMENTS:
- case DICTIONARY_ELEMENTS:
- UNREACHABLE();
- break;
- case NON_STRICT_ARGUMENTS_ELEMENTS:
- UNIMPLEMENTED();
- break;
- }
- return GetHeap()->undefined_value();
+ return pt->GetElementWithReceiver(*this_handle, index);
}
bool JSObject::HasDenseElements() {
int capacity = 0;
- int number_of_elements = 0;
+ int used = 0;
+ GetElementsCapacityAndUsage(&capacity, &used);
+ return (capacity == 0) || (used > (capacity / 2));
+}
+
+
+void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
+ *capacity = 0;
+ *used = 0;
FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
FixedArray* backing_store = NULL;
@@ -9097,34 +8767,33 @@
backing_store = FixedArray::cast(backing_store_base);
if (backing_store->IsDictionary()) {
NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
- capacity = dictionary->Capacity();
- number_of_elements = dictionary->NumberOfElements();
+ *capacity = dictionary->Capacity();
+ *used = dictionary->NumberOfElements();
break;
}
// Fall through.
case FAST_ELEMENTS:
backing_store = FixedArray::cast(backing_store_base);
- capacity = backing_store->length();
- for (int i = 0; i < capacity; ++i) {
- if (!backing_store->get(i)->IsTheHole()) ++number_of_elements;
+ *capacity = backing_store->length();
+ for (int i = 0; i < *capacity; ++i) {
+ if (!backing_store->get(i)->IsTheHole()) ++(*used);
}
break;
case DICTIONARY_ELEMENTS: {
NumberDictionary* dictionary =
NumberDictionary::cast(FixedArray::cast(elements()));
- capacity = dictionary->Capacity();
- number_of_elements = dictionary->NumberOfElements();
+ *capacity = dictionary->Capacity();
+ *used = dictionary->NumberOfElements();
break;
}
case FAST_DOUBLE_ELEMENTS: {
FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
- capacity = elms->length();
- for (int i = 0; i < capacity; i++) {
- if (!elms->is_the_hole(i)) number_of_elements++;
+ *capacity = elms->length();
+ for (int i = 0; i < *capacity; i++) {
+ if (!elms->is_the_hole(i)) ++(*used);
}
break;
}
- case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
case EXTERNAL_SHORT_ELEMENTS:
@@ -9132,30 +8801,34 @@
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
- case EXTERNAL_DOUBLE_ELEMENTS: {
- return true;
- }
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case EXTERNAL_PIXEL_ELEMENTS:
+ // External arrays are considered 100% used.
+ ExternalArray* external_array = ExternalArray::cast(elements());
+ *capacity = external_array->length();
+ *used = external_array->length();
+ break;
}
- return (capacity == 0) || (number_of_elements > (capacity / 2));
}
bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
- // Keep the array in fast case if the current backing storage is
- // almost filled and if the new capacity is no more than twice the
- // old capacity.
- int elements_length = 0;
- if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) {
- FixedArray* backing_store = FixedArray::cast(elements());
- elements_length = FixedArray::cast(backing_store->get(1))->length();
- } else if (HasFastElements()) {
- elements_length = FixedArray::cast(elements())->length();
- } else if (HasFastDoubleElements()) {
- elements_length = FixedDoubleArray::cast(elements())->length();
- } else {
- UNREACHABLE();
+ STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
+ kMaxUncheckedFastElementsLength);
+ if (new_capacity <= kMaxUncheckedOldFastElementsLength ||
+ (new_capacity <= kMaxUncheckedFastElementsLength &&
+ GetHeap()->InNewSpace(this))) {
+ return false;
}
- return !HasDenseElements() || ((new_capacity / 2) > elements_length);
+ // If the fast-case backing storage takes up roughly three times as
+ // much space (in machine words) as a dictionary backing storage
+ // would, the object should have slow elements.
+ int old_capacity = 0;
+ int used_elements = 0;
+ GetElementsCapacityAndUsage(&old_capacity, &used_elements);
+ int dictionary_size = NumberDictionary::ComputeCapacity(used_elements) *
+ NumberDictionary::kEntrySize;
+ return 3 * dictionary_size <= new_capacity;
}
@@ -9178,20 +8851,21 @@
// dictionary, we cannot go back to fast case.
if (dictionary->requires_slow_elements()) return false;
// If the dictionary backing storage takes up roughly half as much
- // space as a fast-case backing storage would the array should have
- // fast elements.
- uint32_t length = 0;
+ // space (in machine words) as a fast-case backing storage would,
+ // the object should have fast elements.
+ uint32_t array_size = 0;
if (IsJSArray()) {
- CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size));
} else {
- length = dictionary->max_number_key();
+ array_size = dictionary->max_number_key();
}
- return static_cast<uint32_t>(dictionary->Capacity()) >=
- (length / (2 * NumberDictionary::kEntrySize));
+ uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
+ NumberDictionary::kEntrySize;
+ return 2 * dictionary_size >= array_size;
}
-bool JSObject::ShouldConvertToFastDoubleElements() {
+bool JSObject::CanConvertToFastDoubleElements() {
if (FLAG_unbox_double_arrays) {
ASSERT(HasDictionaryElements());
NumberDictionary* dictionary = NumberDictionary::cast(elements());
@@ -9381,6 +9055,15 @@
return (index < length) &&
!FixedArray::cast(elements())->get(index)->IsTheHole();
}
+ case FAST_DOUBLE_ELEMENTS: {
+ uint32_t length = IsJSArray() ?
+ static_cast<uint32_t>(
+ Smi::cast(JSArray::cast(this)->length())->value()) :
+ static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length());
+ return (index < length) &&
+ !FixedDoubleArray::cast(elements())->is_the_hole(index);
+ break;
+ }
case EXTERNAL_PIXEL_ELEMENTS: {
ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
return index < static_cast<uint32_t>(pixels->length());
@@ -9396,9 +9079,6 @@
ExternalArray* array = ExternalArray::cast(elements());
return index < static_cast<uint32_t>(array->length());
}
- case FAST_DOUBLE_ELEMENTS:
- UNREACHABLE();
- break;
case DICTIONARY_ELEMENTS: {
return element_dictionary()->FindEntry(index)
!= NumberDictionary::kNotFound;
@@ -10118,11 +9798,8 @@
template<typename Shape, typename Key>
MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
PretenureFlag pretenure) {
- const int kMinCapacity = 32;
- int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
- if (capacity < kMinCapacity) {
- capacity = kMinCapacity; // Guarantee min capacity.
- } else if (capacity > HashTable::kMaxCapacity) {
+ int capacity = ComputeCapacity(at_least_space_for);
+ if (capacity > HashTable::kMaxCapacity) {
return Failure::OutOfMemoryException();
}
@@ -10290,6 +9967,8 @@
template class HashTable<MapCacheShape, HashTableKey*>;
+template class HashTable<ObjectHashTableShape, JSObject*>;
+
template class Dictionary<StringDictionaryShape, String*>;
template class Dictionary<NumberDictionaryShape, uint32_t>;
@@ -10504,19 +10183,19 @@
} else if (HasExternalArrayElements()) {
// External arrays cannot have holes or undefined elements.
return Smi::FromInt(ExternalArray::cast(elements())->length());
- } else {
+ } else if (!HasFastDoubleElements()) {
Object* obj;
{ MaybeObject* maybe_obj = EnsureWritableFastElements();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
}
- ASSERT(HasFastElements());
+ ASSERT(HasFastElements() || 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.
- FixedArray* elements = FixedArray::cast(this->elements());
- uint32_t elements_length = static_cast<uint32_t>(elements->length());
+ FixedArrayBase* elements_base = FixedArrayBase::cast(this->elements());
+ uint32_t elements_length = static_cast<uint32_t>(elements_base->length());
if (limit > elements_length) {
limit = elements_length ;
}
@@ -10535,47 +10214,78 @@
result_double = HeapNumber::cast(new_double);
}
- AssertNoAllocation no_alloc;
-
- // Split elements into defined, undefined and the_hole, in that order.
- // Only count locations for undefined and the hole, and fill them afterwards.
- WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
- unsigned int undefs = limit;
- unsigned int holes = limit;
- // Assume most arrays contain no holes and undefined values, so minimize the
- // number of stores of non-undefined, non-the-hole values.
- for (unsigned int i = 0; i < undefs; i++) {
- Object* current = elements->get(i);
- if (current->IsTheHole()) {
- holes--;
- undefs--;
- } else if (current->IsUndefined()) {
- undefs--;
- } else {
- continue;
+ uint32_t result = 0;
+ if (elements_base->map() == heap->fixed_double_array_map()) {
+ FixedDoubleArray* elements = FixedDoubleArray::cast(elements_base);
+ // Split elements into defined and the_hole, in that order.
+ unsigned int holes = limit;
+ // Assume most arrays contain no holes and undefined values, so minimize the
+ // number of stores of non-undefined, non-the-hole values.
+ for (unsigned int i = 0; i < holes; i++) {
+ if (elements->is_the_hole(i)) {
+ holes--;
+ } else {
+ continue;
+ }
+ // Position i needs to be filled.
+ while (holes > i) {
+ if (elements->is_the_hole(holes)) {
+ holes--;
+ } else {
+ elements->set(i, elements->get_scalar(holes));
+ break;
+ }
+ }
}
- // Position i needs to be filled.
- while (undefs > i) {
- current = elements->get(undefs);
+ result = holes;
+ while (holes < limit) {
+ elements->set_the_hole(holes);
+ holes++;
+ }
+ } else {
+ FixedArray* elements = FixedArray::cast(elements_base);
+ AssertNoAllocation no_alloc;
+
+ // Split elements into defined, undefined and the_hole, in that order. Only
+ // count locations for undefined and the hole, and fill them afterwards.
+ WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_alloc);
+ unsigned int undefs = limit;
+ unsigned int holes = limit;
+ // Assume most arrays contain no holes and undefined values, so minimize the
+ // number of stores of non-undefined, non-the-hole values.
+ for (unsigned int i = 0; i < undefs; i++) {
+ Object* current = elements->get(i);
if (current->IsTheHole()) {
holes--;
undefs--;
} else if (current->IsUndefined()) {
undefs--;
} else {
- elements->set(i, current, write_barrier);
- break;
+ continue;
+ }
+ // Position i needs to be filled.
+ while (undefs > i) {
+ current = elements->get(undefs);
+ if (current->IsTheHole()) {
+ holes--;
+ undefs--;
+ } else if (current->IsUndefined()) {
+ undefs--;
+ } else {
+ elements->set(i, current, write_barrier);
+ break;
+ }
}
}
- }
- uint32_t result = undefs;
- while (undefs < holes) {
- elements->set_undefined(undefs);
- undefs++;
- }
- while (holes < limit) {
- elements->set_the_hole(holes);
- holes++;
+ result = undefs;
+ while (undefs < holes) {
+ elements->set_undefined(undefs);
+ undefs++;
+ }
+ while (holes < limit) {
+ elements->set_the_hole(holes);
+ holes++;
+ }
}
if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
@@ -11603,6 +11313,63 @@
}
+Object* ObjectHashTable::Lookup(JSObject* 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);
+ if (maybe_hash->IsFailure()) return GetHeap()->undefined_value();
+ int entry = FindEntry(key);
+ if (entry == kNotFound) return GetHeap()->undefined_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+MaybeObject* ObjectHashTable::Put(JSObject* key, Object* value) {
+ // Make sure the key object has an identity hash code.
+ int hash;
+ { MaybeObject* maybe_hash = key->GetIdentityHash(JSObject::ALLOW_CREATION);
+ if (maybe_hash->IsFailure()) return maybe_hash;
+ hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
+ }
+ int entry = FindEntry(key);
+
+ // Check whether to perform removal operation.
+ if (value->IsUndefined()) {
+ if (entry == kNotFound) return this;
+ RemoveEntry(entry);
+ return Shrink(key);
+ }
+
+ // Key is already in table, just overwrite value.
+ if (entry != kNotFound) {
+ set(EntryToIndex(entry) + 1, value);
+ return this;
+ }
+
+ // Check whether the hash table should be extended.
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ ObjectHashTable* table = ObjectHashTable::cast(obj);
+ table->AddEntry(table->FindInsertionEntry(hash), key, value);
+ return table;
+}
+
+
+void ObjectHashTable::AddEntry(int entry, JSObject* key, Object* value) {
+ set(EntryToIndex(entry), key);
+ set(EntryToIndex(entry) + 1, value);
+ ElementAdded();
+}
+
+
+void ObjectHashTable::RemoveEntry(int entry, Heap* heap) {
+ set_null(heap, EntryToIndex(entry));
+ set_null(heap, EntryToIndex(entry) + 1);
+ ElementRemoved();
+}
+
+
#ifdef ENABLE_DEBUGGER_SUPPORT
// Check if there is a break point at this code position.
bool DebugInfo::HasBreakPoint(int code_position) {
@@ -11829,7 +11596,7 @@
Handle<Object> break_point_object) {
// No break point.
if (break_point_info->break_point_objects()->IsUndefined()) return false;
- // Single beak point.
+ // Single break point.
if (!break_point_info->break_point_objects()->IsFixedArray()) {
return break_point_info->break_point_objects() == *break_point_object;
}
@@ -11848,7 +11615,7 @@
int BreakPointInfo::GetBreakPointCount() {
// No break point.
if (break_point_objects()->IsUndefined()) return 0;
- // Single beak point.
+ // Single break point.
if (!break_point_objects()->IsFixedArray()) return 1;
// Multiple break points.
return FixedArray::cast(break_point_objects())->length();