diff options
Diffstat (limited to 'deps/v8/src/objects.cc')
-rw-r--r-- | deps/v8/src/objects.cc | 4250 |
1 files changed, 2533 insertions, 1717 deletions
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 37f8361d8..00d00d5ee 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1,4 +1,4 @@ -// Copyright 2012 the V8 project authors. All rights reserved. +// Copyright 2013 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -27,6 +27,7 @@ #include "v8.h" +#include "accessors.h" #include "api.h" #include "arguments.h" #include "bootstrapper.h" @@ -104,43 +105,31 @@ MaybeObject* Object::ToObject() { } -Object* Object::ToBoolean() { - if (IsTrue()) return this; - if (IsFalse()) return this; - if (IsSmi()) { - return Isolate::Current()->heap()->ToBoolean(Smi::cast(this)->value() != 0); - } - HeapObject* heap_object = HeapObject::cast(this); - if (heap_object->IsUndefined() || heap_object->IsNull()) { - return heap_object->GetHeap()->false_value(); - } - // Undetectable object is false - if (heap_object->IsUndetectableObject()) { - return heap_object->GetHeap()->false_value(); - } - if (heap_object->IsString()) { - return heap_object->GetHeap()->ToBoolean( - String::cast(this)->length() != 0); - } - if (heap_object->IsHeapNumber()) { - return HeapNumber::cast(this)->HeapNumberToBoolean(); - } - return heap_object->GetHeap()->true_value(); +bool Object::BooleanValue() { + if (IsBoolean()) return IsTrue(); + if (IsSmi()) return Smi::cast(this)->value() != 0; + if (IsUndefined() || IsNull()) return false; + if (IsUndetectableObject()) return false; // Undetectable object is false. + if (IsString()) return String::cast(this)->length() != 0; + if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue(); + return true; } -void Object::Lookup(String* name, LookupResult* result) { +void Object::Lookup(Name* name, LookupResult* result) { Object* holder = NULL; if (IsJSReceiver()) { holder = this; } else { - Context* native_context = Isolate::Current()->context()->native_context(); + Context* native_context = result->isolate()->context()->native_context(); if (IsNumber()) { holder = native_context->number_function()->instance_prototype(); } else if (IsString()) { holder = native_context->string_function()->instance_prototype(); } else if (IsBoolean()) { holder = native_context->boolean_function()->instance_prototype(); + } else if (IsSymbol()) { + holder = native_context->symbol_delegate(); } else { Isolate::Current()->PushStackTraceAndDie( 0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001); @@ -152,7 +141,7 @@ void Object::Lookup(String* name, LookupResult* result) { MaybeObject* Object::GetPropertyWithReceiver(Object* receiver, - String* name, + Name* name, PropertyAttributes* attributes) { LookupResult result(name->GetIsolate()); Lookup(name, &result); @@ -162,9 +151,149 @@ MaybeObject* Object::GetPropertyWithReceiver(Object* receiver, } +template<typename To> +static inline To* CheckedCast(void *from) { + uintptr_t temp = reinterpret_cast<uintptr_t>(from); + ASSERT(temp % sizeof(To) == 0); + return reinterpret_cast<To*>(temp); +} + + +static MaybeObject* PerformCompare(const BitmaskCompareDescriptor& descriptor, + char* ptr, + Heap* heap) { + uint32_t bitmask = descriptor.bitmask; + uint32_t compare_value = descriptor.compare_value; + uint32_t value; + switch (descriptor.size) { + case 1: + value = static_cast<uint32_t>(*CheckedCast<uint8_t>(ptr)); + compare_value &= 0xff; + bitmask &= 0xff; + break; + case 2: + value = static_cast<uint32_t>(*CheckedCast<uint16_t>(ptr)); + compare_value &= 0xffff; + bitmask &= 0xffff; + break; + case 4: + value = *CheckedCast<uint32_t>(ptr); + break; + default: + UNREACHABLE(); + return NULL; + } + return heap->ToBoolean((bitmask & value) == (bitmask & compare_value)); +} + + +static MaybeObject* PerformCompare(const PointerCompareDescriptor& descriptor, + char* ptr, + Heap* heap) { + uintptr_t compare_value = + reinterpret_cast<uintptr_t>(descriptor.compare_value); + uintptr_t value = *CheckedCast<uintptr_t>(ptr); + return heap->ToBoolean(compare_value == value); +} + + +static MaybeObject* GetPrimitiveValue( + const PrimitiveValueDescriptor& descriptor, + char* ptr, + Heap* heap) { + int32_t int32_value = 0; + switch (descriptor.data_type) { + case kDescriptorInt8Type: + int32_value = *CheckedCast<int8_t>(ptr); + break; + case kDescriptorUint8Type: + int32_value = *CheckedCast<uint8_t>(ptr); + break; + case kDescriptorInt16Type: + int32_value = *CheckedCast<int16_t>(ptr); + break; + case kDescriptorUint16Type: + int32_value = *CheckedCast<uint16_t>(ptr); + break; + case kDescriptorInt32Type: + int32_value = *CheckedCast<int32_t>(ptr); + break; + case kDescriptorUint32Type: { + uint32_t value = *CheckedCast<uint32_t>(ptr); + return heap->NumberFromUint32(value); + } + case kDescriptorBoolType: { + uint8_t byte = *CheckedCast<uint8_t>(ptr); + return heap->ToBoolean(byte & (0x1 << descriptor.bool_offset)); + } + case kDescriptorFloatType: { + float value = *CheckedCast<float>(ptr); + return heap->NumberFromDouble(value); + } + case kDescriptorDoubleType: { + double value = *CheckedCast<double>(ptr); + return heap->NumberFromDouble(value); + } + } + return heap->NumberFromInt32(int32_value); +} + + +static MaybeObject* GetDeclaredAccessorProperty(Object* receiver, + DeclaredAccessorInfo* info, + Isolate* isolate) { + char* current = reinterpret_cast<char*>(receiver); + DeclaredAccessorDescriptorIterator iterator(info->descriptor()); + while (true) { + const DeclaredAccessorDescriptorData* data = iterator.Next(); + switch (data->type) { + case kDescriptorReturnObject: { + ASSERT(iterator.Complete()); + current = *CheckedCast<char*>(current); + return *CheckedCast<Object*>(current); + } + case kDescriptorPointerDereference: + ASSERT(!iterator.Complete()); + current = *reinterpret_cast<char**>(current); + break; + case kDescriptorPointerShift: + ASSERT(!iterator.Complete()); + current += data->pointer_shift_descriptor.byte_offset; + break; + case kDescriptorObjectDereference: { + ASSERT(!iterator.Complete()); + Object* object = CheckedCast<Object>(current); + int field = data->object_dereference_descriptor.internal_field; + Object* smi = JSObject::cast(object)->GetInternalField(field); + ASSERT(smi->IsSmi()); + current = reinterpret_cast<char*>(smi); + break; + } + case kDescriptorBitmaskCompare: + ASSERT(iterator.Complete()); + return PerformCompare(data->bitmask_compare_descriptor, + current, + isolate->heap()); + case kDescriptorPointerCompare: + ASSERT(iterator.Complete()); + return PerformCompare(data->pointer_compare_descriptor, + current, + isolate->heap()); + case kDescriptorPrimitiveValue: + ASSERT(iterator.Complete()); + return GetPrimitiveValue(data->primitive_value_descriptor, + current, + isolate->heap()); + } + } + UNREACHABLE(); + return NULL; +} + + MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, Object* structure, - String* name) { + Name* 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 @@ -180,10 +309,9 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, // api style callbacks. if (structure->IsAccessorInfo()) { - AccessorInfo* data = AccessorInfo::cast(structure); - if (!data->IsCompatibleReceiver(receiver)) { - Handle<Object> name_handle(name); - Handle<Object> receiver_handle(receiver); + if (!AccessorInfo::cast(structure)->IsCompatibleReceiver(receiver)) { + Handle<Object> name_handle(name, isolate); + Handle<Object> receiver_handle(receiver, isolate); Handle<Object> args[2] = { name_handle, receiver_handle }; Handle<Object> error = isolate->factory()->NewTypeError("incompatible_method_receiver", @@ -191,12 +319,21 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, ARRAY_SIZE(args))); return isolate->Throw(*error); } + // TODO(rossberg): Handling symbols in the API requires changing the API, + // so we do not support it for now. + if (name->IsSymbol()) return isolate->heap()->undefined_value(); + if (structure->IsDeclaredAccessorInfo()) { + return GetDeclaredAccessorProperty(receiver, + DeclaredAccessorInfo::cast(structure), + isolate); + } + ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure); Object* fun_obj = data->getter(); v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); if (call_fun == NULL) return isolate->heap()->undefined_value(); HandleScope scope(isolate); JSObject* self = JSObject::cast(receiver); - Handle<String> key(name); + Handle<String> key(String::cast(name)); LOG(isolate, ApiNamedPropertyAccess("load", self, name)); CustomArguments args(isolate, data->data(), self, this); v8::AccessorInfo info(args.end()); @@ -232,11 +369,11 @@ MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver, MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw, - String* name_raw) { + Name* name_raw) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); - Handle<Object> receiver(receiver_raw); - Handle<Object> name(name_raw); + Handle<Object> receiver(receiver_raw, isolate); + Handle<Object> name(name_raw, isolate); Handle<Object> args[] = { receiver, name }; Handle<Object> result = CallTrap( @@ -247,6 +384,19 @@ MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw, } +Handle<Object> Object::GetProperty(Handle<Object> object, Handle<Name> name) { + // TODO(rossberg): The index test should not be here but in the GetProperty + // method (or somewhere else entirely). Needs more global clean-up. + uint32_t index; + if (name->AsArrayIndex(&index)) + return GetElement(object, index); + Isolate* isolate = object->IsHeapObject() + ? Handle<HeapObject>::cast(object)->GetIsolate() + : Isolate::Current(); + CALL_HEAP_FUNCTION(isolate, object->GetProperty(*name), Object); +} + + Handle<Object> Object::GetElement(Handle<Object> object, uint32_t index) { Isolate* isolate = object->IsHeapObject() ? Handle<HeapObject>::cast(object)->GetIsolate() @@ -285,11 +435,12 @@ bool JSProxy::HasElementWithHandler(uint32_t index) { MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver, JSReceiver* getter) { - HandleScope scope; + Isolate* isolate = getter->GetIsolate(); + HandleScope scope(isolate); Handle<JSReceiver> fun(getter); - Handle<Object> self(receiver); + Handle<Object> self(receiver, isolate); #ifdef ENABLE_DEBUGGER_SUPPORT - Debug* debug = fun->GetHeap()->isolate()->debug(); + Debug* debug = isolate->debug(); // Handle stepping into a getter if step into is active. // TODO(rossberg): should this apply to getters that are function proxies? if (debug->StepInActive() && fun->IsJSFunction()) { @@ -311,7 +462,7 @@ MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver, MaybeObject* JSObject::GetPropertyWithFailedAccessCheck( Object* receiver, LookupResult* result, - String* name, + Name* name, PropertyAttributes* attributes) { if (result->IsProperty()) { switch (result->type()) { @@ -371,7 +522,7 @@ MaybeObject* JSObject::GetPropertyWithFailedAccessCheck( PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( Object* receiver, LookupResult* result, - String* name, + Name* name, bool continue_search) { if (result->IsProperty()) { switch (result->type()) { @@ -457,7 +608,7 @@ Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) { Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object, - Handle<String> key, + Handle<Name> key, Handle<Object> value, PropertyDetails details) { CALL_HEAP_FUNCTION(object->GetIsolate(), @@ -466,12 +617,12 @@ Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object, } -MaybeObject* JSObject::SetNormalizedProperty(String* name, +MaybeObject* JSObject::SetNormalizedProperty(Name* name, Object* value, PropertyDetails details) { ASSERT(!HasFastProperties()); int entry = property_dictionary()->FindEntry(name); - if (entry == StringDictionary::kNotFound) { + if (entry == NameDictionary::kNotFound) { Object* store_value = value; if (IsGlobalObject()) { Heap* heap = name->GetHeap(); @@ -484,7 +635,7 @@ MaybeObject* JSObject::SetNormalizedProperty(String* name, property_dictionary()->Add(name, store_value, details); if (!maybe_dict->ToObject(&dict)) return maybe_dict; } - set_properties(StringDictionary::cast(dict)); + set_properties(NameDictionary::cast(dict)); return value; } @@ -515,11 +666,11 @@ MaybeObject* JSObject::SetNormalizedProperty(String* name, } -MaybeObject* JSObject::DeleteNormalizedProperty(String* name, DeleteMode mode) { +MaybeObject* JSObject::DeleteNormalizedProperty(Name* name, DeleteMode mode) { ASSERT(!HasFastProperties()); - StringDictionary* dictionary = property_dictionary(); + NameDictionary* dictionary = property_dictionary(); int entry = dictionary->FindEntry(name); - if (entry != StringDictionary::kNotFound) { + if (entry != NameDictionary::kNotFound) { // If we have a global object set the cell to the hole. if (IsGlobalObject()) { PropertyDetails details = dictionary->DetailsAt(entry); @@ -575,7 +726,7 @@ bool JSObject::IsDirty() { Handle<Object> Object::GetProperty(Handle<Object> object, Handle<Object> receiver, LookupResult* result, - Handle<String> key, + Handle<Name> key, PropertyAttributes* attributes) { Isolate* isolate = object->IsHeapObject() ? Handle<HeapObject>::cast(object)->GetIsolate() @@ -589,12 +740,13 @@ Handle<Object> Object::GetProperty(Handle<Object> object, MaybeObject* Object::GetProperty(Object* receiver, LookupResult* result, - String* name, + Name* name, PropertyAttributes* attributes) { // Make sure that the top context does not change when doing // callbacks or interceptor calls. AssertNoContextChange ncc; - Heap* heap = name->GetHeap(); + Isolate* isolate = name->GetIsolate(); + Heap* heap = isolate->heap(); // Traverse the prototype chain from the current object (this) to // the holder and check for access rights. This avoids traversing the @@ -604,11 +756,13 @@ MaybeObject* Object::GetProperty(Object* receiver, // holder in the prototype chain. // Proxy handlers do not use the proxy's prototype, so we can skip this. if (!result->IsHandler()) { - Object* last = result->IsProperty() + Object* last = result->IsProperty() && !receiver->IsSymbol() ? result->holder() : Object::cast(heap->null_value()); - ASSERT(this != this->GetPrototype()); - for (Object* current = this; true; current = current->GetPrototype()) { + ASSERT(this != this->GetPrototype(isolate)); + for (Object* current = this; + true; + current = current->GetPrototype(isolate)) { if (current->IsAccessCheckNeeded()) { // Check if we're allowed to read from the current object. Note // that even though we may not actually end up loading the named @@ -641,7 +795,8 @@ MaybeObject* Object::GetProperty(Object* receiver, ASSERT(!value->IsTheHole() || result->IsReadOnly()); return value->IsTheHole() ? heap->undefined_value() : value; case FIELD: - value = result->holder()->FastPropertyAt(result->GetFieldIndex()); + value = result->holder()->FastPropertyAt( + result->GetFieldIndex().field_index()); ASSERT(!value->IsTheHole() || result->IsReadOnly()); return value->IsTheHole() ? heap->undefined_value() : value; case CONSTANT_FUNCTION: @@ -665,18 +820,18 @@ MaybeObject* Object::GetProperty(Object* receiver, MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { - Heap* heap = IsSmi() - ? Isolate::Current()->heap() - : HeapObject::cast(this)->GetHeap(); + Isolate* isolate = IsSmi() + ? Isolate::Current() + : HeapObject::cast(this)->GetIsolate(); + Heap* heap = isolate->heap(); Object* holder = this; // 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()) { + holder = holder->GetPrototype(isolate)) { if (!holder->IsJSObject()) { - Isolate* isolate = heap->isolate(); Context* native_context = isolate->context()->native_context(); if (holder->IsNumber()) { holder = native_context->number_function()->instance_prototype(); @@ -684,6 +839,8 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { holder = native_context->string_function()->instance_prototype(); } else if (holder->IsBoolean()) { holder = native_context->boolean_function()->instance_prototype(); + } else if (holder->IsSymbol()) { + holder = native_context->symbol_delegate(); } else if (holder->IsJSProxy()) { return JSProxy::cast(holder)->GetElementWithHandler(receiver, index); } else { @@ -722,10 +879,9 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { } -Object* Object::GetPrototype() { +Object* Object::GetPrototype(Isolate* isolate) { if (IsSmi()) { - Heap* heap = Isolate::Current()->heap(); - Context* context = heap->isolate()->context()->native_context(); + Context* context = isolate->context()->native_context(); return context->number_function()->instance_prototype(); } @@ -736,8 +892,7 @@ Object* Object::GetPrototype() { if (heap_object->IsJSReceiver()) { return heap_object->map()->prototype(); } - Heap* heap = heap_object->GetHeap(); - Context* context = heap->isolate()->context()->native_context(); + Context* context = isolate->context()->native_context(); if (heap_object->IsHeapNumber()) { return context->number_function()->instance_prototype(); @@ -748,20 +903,30 @@ Object* Object::GetPrototype() { if (heap_object->IsBoolean()) { return context->boolean_function()->instance_prototype(); } else { - return heap->null_value(); + return isolate->heap()->null_value(); + } +} + + +Object* Object::GetDelegate(Isolate* isolate) { + if (IsSymbol()) { + Heap* heap = Symbol::cast(this)->GetHeap(); + Context* context = heap->isolate()->context()->native_context(); + return context->symbol_delegate(); } + return GetPrototype(isolate); } MaybeObject* Object::GetHash(CreationFlag flag) { - // The object is either a number, a string, an odd-ball, + // The object is either a number, a name, an odd-ball, // a real JS object, or a Harmony proxy. if (IsNumber()) { uint32_t hash = ComputeLongHash(double_to_uint64(Number())); return Smi::FromInt(hash & Smi::kMaxValue); } - if (IsString()) { - uint32_t hash = String::cast(this)->Hash(); + if (IsName()) { + uint32_t hash = Name::cast(this)->Hash(); return Smi::FromInt(hash); } if (IsOddball()) { @@ -780,7 +945,7 @@ MaybeObject* Object::GetHash(CreationFlag flag) { bool Object::SameValue(Object* other) { if (other == this) return true; - // The object is either a number, a string, an odd-ball, + // The object is either a number, a name, an odd-ball, // a real JS object, or a Harmony proxy. if (IsNumber() && other->IsNumber()) { double this_value = Number(); @@ -881,14 +1046,15 @@ MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) { int len = length(); Object* object; String* result; - if (IsAsciiRepresentation()) { - { MaybeObject* maybe_object = heap->AllocateRawAsciiString(len, tenure); + if (IsOneByteRepresentation()) { + { MaybeObject* maybe_object = + heap->AllocateRawOneByteString(len, tenure); if (!maybe_object->ToObject(&object)) return maybe_object; } result = String::cast(object); String* first = cs->first(); int first_length = first->length(); - char* dest = SeqAsciiString::cast(result)->GetChars(); + uint8_t* dest = SeqOneByteString::cast(result)->GetChars(); WriteToFlat(first, dest, 0, first_length); String* second = cs->second(); WriteToFlat(second, @@ -941,29 +1107,34 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { if (size < ExternalString::kShortSize) { return false; } - bool is_ascii = this->IsAsciiRepresentation(); - bool is_symbol = this->IsSymbol(); + bool is_ascii = this->IsOneByteRepresentation(); + bool is_internalized = this->IsInternalizedString(); // Morph the object to an external string by adjusting the map and // reinitializing the fields. if (size >= ExternalString::kSize) { this->set_map_no_write_barrier( - is_symbol - ? (is_ascii ? heap->external_symbol_with_ascii_data_map() - : heap->external_symbol_map()) - : (is_ascii ? heap->external_string_with_ascii_data_map() - : heap->external_string_map())); + is_internalized + ? (is_ascii + ? heap->external_internalized_string_with_ascii_data_map() + : heap->external_internalized_string_map()) + : (is_ascii + ? heap->external_string_with_ascii_data_map() + : heap->external_string_map())); } else { this->set_map_no_write_barrier( - is_symbol - ? (is_ascii ? heap->short_external_symbol_with_ascii_data_map() - : heap->short_external_symbol_map()) - : (is_ascii ? heap->short_external_string_with_ascii_data_map() - : heap->short_external_string_map())); + is_internalized + ? (is_ascii + ? heap-> + short_external_internalized_string_with_ascii_data_map() + : heap->short_external_internalized_string_map()) + : (is_ascii + ? heap->short_external_string_with_ascii_data_map() + : heap->short_external_string_map())); } ExternalTwoByteString* self = ExternalTwoByteString::cast(this); self->set_resource(resource); - if (is_symbol) self->Hash(); // Force regeneration of the hash value. + if (is_internalized) self->Hash(); // Force regeneration of the hash value. // Fill the remainder of the string with dead wood. int new_size = this->Size(); // Byte size of the external String object. @@ -993,22 +1164,22 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { if (size < ExternalString::kShortSize) { return false; } - bool is_symbol = this->IsSymbol(); + bool is_internalized = this->IsInternalizedString(); // Morph the object to an external string by adjusting the map and // reinitializing the fields. Use short version if space is limited. if (size >= ExternalString::kSize) { this->set_map_no_write_barrier( - is_symbol ? heap->external_ascii_symbol_map() - : heap->external_ascii_string_map()); + is_internalized ? heap->external_ascii_internalized_string_map() + : heap->external_ascii_string_map()); } else { this->set_map_no_write_barrier( - is_symbol ? heap->short_external_ascii_symbol_map() - : heap->short_external_ascii_string_map()); + is_internalized ? heap->short_external_ascii_internalized_string_map() + : heap->short_external_ascii_string_map()); } ExternalAsciiString* self = ExternalAsciiString::cast(this); self->set_resource(resource); - if (is_symbol) self->Hash(); // Force regeneration of the hash value. + if (is_internalized) self->Hash(); // Force regeneration of the hash value. // Fill the remainder of the string with dead wood. int new_size = this->Size(); // Byte size of the external String object. @@ -1033,7 +1204,8 @@ void String::StringShortPrint(StringStream* accumulator) { return; } - StringInputBuffer buf(this); + ConsStringIteratorOp op; + StringCharacterStream stream(this, &op); bool truncated = false; if (len > kMaxShortPrintLength) { @@ -1042,17 +1214,17 @@ void String::StringShortPrint(StringStream* accumulator) { } bool ascii = true; for (int i = 0; i < len; i++) { - int c = buf.GetNext(); + uint16_t c = stream.GetNext(); if (c < 32 || c >= 127) { ascii = false; } } - buf.Reset(this); + stream.Reset(this); if (ascii) { accumulator->Add("<String[%u]: ", length()); for (int i = 0; i < len; i++) { - accumulator->Put(buf.GetNext()); + accumulator->Put(static_cast<char>(stream.GetNext())); } accumulator->Put('>'); } else { @@ -1060,7 +1232,7 @@ void String::StringShortPrint(StringStream* accumulator) { // characters and that backslashes are therefore escaped. accumulator->Add("<String[%u]\\: ", length()); for (int i = 0; i < len; i++) { - int c = buf.GetNext(); + uint16_t c = stream.GetNext(); if (c == '\n') { accumulator->Add("\\n"); } else if (c == '\r') { @@ -1070,7 +1242,7 @@ void String::StringShortPrint(StringStream* accumulator) { } else if (c < 32 || c > 126) { accumulator->Add("\\x%02x", c); } else { - accumulator->Put(c); + accumulator->Put(static_cast<char>(c)); } } if (truncated) { @@ -1118,6 +1290,10 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { } break; } + case JS_MODULE_TYPE: { + accumulator->Add("<JS Module>"); + break; + } // All other JSObjects are rather similar to each other (JSObject, // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue). default: { @@ -1173,7 +1349,7 @@ void JSObject::PrintElementsTransition( PrintF(file, " -> "); PrintElementsKind(file, to_kind); PrintF(file, "] in "); - JavaScriptFrame::PrintTop(file, false, true); + JavaScriptFrame::PrintTop(GetIsolate(), file, false, true); PrintF(file, " for "); ShortPrint(file); PrintF(file, " from "); @@ -1291,6 +1467,9 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { accumulator->Add("<Odd Oddball>"); break; } + case SYMBOL_TYPE: + accumulator->Add("<Symbol: %d>", Symbol::cast(this)->Hash()); + break; case HEAP_NUMBER_TYPE: accumulator->Add("<Number: "); HeapNumber::cast(this)->HeapNumberPrint(accumulator); @@ -1340,7 +1519,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, SlicedString::BodyDescriptor::IterateBody(this, v); break; case kExternalStringTag: - if ((type & kStringEncodingMask) == kAsciiStringTag) { + if ((type & kStringEncodingMask) == kOneByteStringTag) { reinterpret_cast<ExternalAsciiString*>(this)-> ExternalAsciiStringIterateBody(v); } else { @@ -1399,6 +1578,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, case JS_GLOBAL_PROPERTY_CELL_TYPE: JSGlobalPropertyCell::BodyDescriptor::IterateBody(this, v); break; + case SYMBOL_TYPE: case HEAP_NUMBER_TYPE: case FILLER_TYPE: case BYTE_ARRAY_TYPE: @@ -1431,7 +1611,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size, } -Object* HeapNumber::HeapNumberToBoolean() { +bool HeapNumber::HeapNumberBooleanValue() { // NaN, +0, and -0 should return the false object #if __BYTE_ORDER == __LITTLE_ENDIAN union IeeeDoubleLittleEndianArchType u; @@ -1441,15 +1621,13 @@ Object* HeapNumber::HeapNumberToBoolean() { u.d = value(); if (u.bits.exp == 2047) { // Detect NaN for IEEE double precision floating point. - if ((u.bits.man_low | u.bits.man_high) != 0) - return GetHeap()->false_value(); + if ((u.bits.man_low | u.bits.man_high) != 0) return false; } if (u.bits.exp == 0) { // Detect +0, and -0 for IEEE double precision floating point. - if ((u.bits.man_low | u.bits.man_high) == 0) - return GetHeap()->false_value(); + if ((u.bits.man_low | u.bits.man_high) == 0) return false; } - return GetHeap()->true_value(); + return true; } @@ -1473,14 +1651,14 @@ void HeapNumber::HeapNumberPrint(StringStream* accumulator) { String* JSReceiver::class_name() { if (IsJSFunction() && IsJSFunctionProxy()) { - return GetHeap()->function_class_symbol(); + return GetHeap()->function_class_string(); } if (map()->constructor()->IsJSFunction()) { JSFunction* constructor = JSFunction::cast(map()->constructor()); return String::cast(constructor->shared()->instance_class_name()); } // If the constructor is not present, return "Object". - return GetHeap()->Object_symbol(); + return GetHeap()->Object_string(); } @@ -1496,12 +1674,12 @@ String* JSReceiver::constructor_name() { } // TODO(rossberg): what about proxies? // If the constructor is not present, return "Object". - return GetHeap()->Object_symbol(); + return GetHeap()->Object_string(); } MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map, - String* name, + Name* name, Object* value, int field_index) { if (map()->unused_property_fields() == 0) { @@ -1518,15 +1696,18 @@ MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map, } -static bool IsIdentifier(UnicodeCache* cache, - unibrow::CharacterStream* buffer) { +static bool IsIdentifier(UnicodeCache* cache, Name* name) { // Checks whether the buffer contains an identifier (no escape). - if (!buffer->has_more()) return false; - if (!cache->IsIdentifierStart(buffer->GetNext())) { + if (!name->IsString()) return false; + String* string = String::cast(name); + if (string->length() == 0) return false; + ConsStringIteratorOp op; + StringCharacterStream stream(string, &op); + if (!cache->IsIdentifierStart(stream.GetNext())) { return false; } - while (buffer->has_more()) { - if (!cache->IsIdentifierPart(buffer->GetNext())) { + while (stream.HasMore()) { + if (!cache->IsIdentifierPart(stream.GetNext())) { return false; } } @@ -1534,7 +1715,7 @@ static bool IsIdentifier(UnicodeCache* cache, } -MaybeObject* JSObject::AddFastProperty(String* name, +MaybeObject* JSObject::AddFastProperty(Name* name, Object* value, PropertyAttributes attributes, StoreFromKeyed store_mode) { @@ -1543,13 +1724,12 @@ MaybeObject* JSObject::AddFastProperty(String* name, map()->instance_descriptors()->Search( name, map()->NumberOfOwnDescriptors())); - // Normalize the object if the name is an actual string (not the - // hidden symbols) and is not a real identifier. + // Normalize the object if the name is an actual name (not the + // hidden strings) and is not a real identifier. // Normalize the object if it will have too many fast properties. Isolate* isolate = GetHeap()->isolate(); - StringInputBuffer buffer(name); - if ((!IsIdentifier(isolate->unicode_cache(), &buffer) - && name != isolate->heap()->hidden_symbol()) || + if ((!IsIdentifier(isolate->unicode_cache(), name) + && name != isolate->heap()->hidden_string()) || (map()->unused_property_fields() == 0 && TooManyFastProperties(properties()->length(), store_mode))) { Object* obj; @@ -1579,10 +1759,7 @@ MaybeObject* JSObject::AddFastProperty(String* name, if (!maybe_values->To(&values)) return maybe_values; } - // Only allow map transition if the object isn't the global object. - TransitionFlag flag = isolate->empty_object_map() != map() - ? INSERT_TRANSITION - : OMIT_TRANSITION; + TransitionFlag flag = INSERT_TRANSITION; Map* new_map; MaybeObject* maybe_new_map = map()->CopyAddDescriptor(&new_field, flag); @@ -1602,21 +1779,17 @@ MaybeObject* JSObject::AddFastProperty(String* name, MaybeObject* JSObject::AddConstantFunctionProperty( - String* name, + Name* name, JSFunction* function, PropertyAttributes attributes) { // Allocate new instance descriptors with (name, function) added ConstantFunctionDescriptor d(name, function, attributes, 0); - Heap* heap = GetHeap(); TransitionFlag flag = - // Do not add transitions to the empty object map (map of "new Object()"), - // nor to global objects. - (map() == heap->isolate()->empty_object_map() || IsGlobalObject() || + // Do not add transitions to global objects. + (IsGlobalObject() || // Don't add transitions to special properties with non-trivial // attributes. - // TODO(verwaest): Once we support attribute changes, these transitions - // should be kept as well. attributes != NONE) ? OMIT_TRANSITION : INSERT_TRANSITION; @@ -1631,16 +1804,16 @@ MaybeObject* JSObject::AddConstantFunctionProperty( // Add property in slow mode -MaybeObject* JSObject::AddSlowProperty(String* name, +MaybeObject* JSObject::AddSlowProperty(Name* name, Object* value, PropertyAttributes attributes) { ASSERT(!HasFastProperties()); - StringDictionary* dict = property_dictionary(); + NameDictionary* dict = property_dictionary(); Object* store_value = value; if (IsGlobalObject()) { // In case name is an orphaned property reuse the cell. int entry = dict->FindEntry(name); - if (entry != StringDictionary::kNotFound) { + if (entry != NameDictionary::kNotFound) { store_value = dict->ValueAt(entry); JSGlobalPropertyCell::cast(store_value)->set_value(value); // Assign an enumeration index to the property and update @@ -1663,12 +1836,12 @@ MaybeObject* JSObject::AddSlowProperty(String* name, { MaybeObject* maybe_result = dict->Add(name, store_value, details); if (!maybe_result->ToObject(&result)) return maybe_result; } - if (dict != result) set_properties(StringDictionary::cast(result)); + if (dict != result) set_properties(NameDictionary::cast(result)); return value; } -MaybeObject* JSObject::AddProperty(String* name, +MaybeObject* JSObject::AddProperty(Name* name, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode, @@ -1677,44 +1850,93 @@ MaybeObject* JSObject::AddProperty(String* name, ASSERT(!IsJSGlobalProxy()); Map* map_of_this = map(); Heap* heap = GetHeap(); + Isolate* isolate = heap->isolate(); + MaybeObject* result; if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK && !map_of_this->is_extensible()) { if (strict_mode == kNonStrictMode) { return value; } else { - Handle<Object> args[1] = {Handle<String>(name)}; - return heap->isolate()->Throw( + Handle<Object> args[1] = {Handle<Name>(name)}; + return isolate->Throw( *FACTORY->NewTypeError("object_not_extensible", HandleVector(args, 1))); } } + if (HasFastProperties()) { // Ensure the descriptor array does not get too big. if (map_of_this->NumberOfOwnDescriptors() < DescriptorArray::kMaxNumberOfDescriptors) { if (value->IsJSFunction()) { - return AddConstantFunctionProperty(name, - JSFunction::cast(value), - attributes); + result = AddConstantFunctionProperty(name, + JSFunction::cast(value), + attributes); } else { - return AddFastProperty(name, value, attributes, store_mode); + result = AddFastProperty(name, value, attributes, store_mode); } } else { // Normalize the object to prevent very large instance descriptors. // This eliminates unwanted N^2 allocation and lookup behavior. Object* obj; - { MaybeObject* maybe_obj = - NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); + if (!maybe->To(&obj)) return maybe; + result = AddSlowProperty(name, value, attributes); } + } else { + result = AddSlowProperty(name, value, attributes); + } + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (FLAG_harmony_observation && map()->is_observed()) { + EnqueueChangeRecord(handle(this, isolate), + "new", + handle(name, isolate), + handle(heap->the_hole_value(), isolate)); + } + + return *hresult; +} + + +void JSObject::EnqueueChangeRecord(Handle<JSObject> object, + const char* type_str, + Handle<Name> name, + Handle<Object> old_value) { + Isolate* isolate = object->GetIsolate(); + HandleScope scope(isolate); + Handle<String> type = isolate->factory()->InternalizeUtf8String(type_str); + if (object->IsJSGlobalObject()) { + object = handle(JSGlobalObject::cast(*object)->global_receiver(), isolate); } - return AddSlowProperty(name, value, attributes); + Handle<Object> args[] = { type, object, name, old_value }; + bool threw; + Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()), + isolate->factory()->undefined_value(), + old_value->IsTheHole() ? 3 : 4, args, + &threw); + ASSERT(!threw); +} + + +void JSObject::DeliverChangeRecords(Isolate* isolate) { + ASSERT(isolate->observer_delivery_pending()); + bool threw = false; + Execution::Call( + isolate->observers_deliver_changes(), + isolate->factory()->undefined_value(), + 0, + NULL, + &threw); + ASSERT(!threw); + isolate->set_observer_delivery_pending(false); } MaybeObject* JSObject::SetPropertyPostInterceptor( - String* name, + Name* name, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode, @@ -1739,10 +1961,10 @@ MaybeObject* JSObject::SetPropertyPostInterceptor( } -MaybeObject* JSObject::ReplaceSlowProperty(String* name, +MaybeObject* JSObject::ReplaceSlowProperty(Name* name, Object* value, PropertyAttributes attributes) { - StringDictionary* dictionary = property_dictionary(); + NameDictionary* dictionary = property_dictionary(); int old_index = dictionary->FindEntry(name); int new_enumeration_index = 0; // 0 means "Use the next available index." if (old_index != -1) { @@ -1757,7 +1979,7 @@ MaybeObject* JSObject::ReplaceSlowProperty(String* name, MaybeObject* JSObject::ConvertTransitionToMapTransition( int transition_index, - String* name, + Name* name, Object* new_value, PropertyAttributes attributes) { Map* old_map = map(); @@ -1770,10 +1992,8 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition( if (!HasFastProperties()) return result; - // This method should only be used to convert existing transitions. Objects - // with the map of "new Object()" cannot have transitions in the first place. + // This method should only be used to convert existing transitions. Map* new_map = map(); - ASSERT(new_map != GetIsolate()->empty_object_map()); // TODO(verwaest): From here on we lose existing map transitions, causing // invalid back pointers. This will change once we can store multiple @@ -1805,7 +2025,7 @@ MaybeObject* JSObject::ConvertTransitionToMapTransition( } -MaybeObject* JSObject::ConvertDescriptorToField(String* name, +MaybeObject* JSObject::ConvertDescriptorToField(Name* name, Object* new_value, PropertyAttributes attributes) { if (map()->unused_property_fields() == 0 && @@ -1848,14 +2068,16 @@ MaybeObject* JSObject::ConvertDescriptorToField(String* name, MaybeObject* JSObject::SetPropertyWithInterceptor( - String* name, + Name* name, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode) { + // TODO(rossberg): Support symbols in the API. + if (name->IsSymbol()) return value; Isolate* isolate = GetIsolate(); HandleScope scope(isolate); Handle<JSObject> this_handle(this); - Handle<String> name_handle(name); + Handle<String> name_handle(String::cast(name)); Handle<Object> value_handle(value, isolate); Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); if (!interceptor->setter()->IsUndefined()) { @@ -1891,7 +2113,7 @@ MaybeObject* JSObject::SetPropertyWithInterceptor( Handle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object, - Handle<String> key, + Handle<Name> key, Handle<Object> value, PropertyAttributes attributes, StrictModeFlag strict_mode) { @@ -1901,13 +2123,13 @@ Handle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object, } -MaybeObject* JSReceiver::SetProperty(String* name, +MaybeObject* JSReceiver::SetProperty(Name* name, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode, JSReceiver::StoreFromKeyed store_mode) { LookupResult result(GetIsolate()); - LocalLookup(name, &result); + LocalLookup(name, &result, true); if (!result.IsFound()) { map()->LookupTransition(JSObject::cast(this), name, &result); } @@ -1916,7 +2138,7 @@ MaybeObject* JSReceiver::SetProperty(String* name, MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, - String* name, + Name* name, Object* value, JSObject* holder, StrictModeFlag strict_mode) { @@ -1941,12 +2163,12 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, return *value_handle; } - if (structure->IsAccessorInfo()) { + if (structure->IsExecutableAccessorInfo()) { // api style callbacks - AccessorInfo* data = AccessorInfo::cast(structure); + ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure); if (!data->IsCompatibleReceiver(this)) { - Handle<Object> name_handle(name); - Handle<Object> receiver_handle(this); + Handle<Object> name_handle(name, isolate); + Handle<Object> receiver_handle(this, isolate); Handle<Object> args[2] = { name_handle, receiver_handle }; Handle<Object> error = isolate->factory()->NewTypeError("incompatible_method_receiver", @@ -1954,10 +2176,12 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, ARRAY_SIZE(args))); return isolate->Throw(*error); } + // TODO(rossberg): Support symbols in the API. + if (name->IsSymbol()) return value; Object* call_obj = data->setter(); v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj); if (call_fun == NULL) return value; - Handle<String> key(name); + Handle<String> key(String::cast(name)); LOG(isolate, ApiNamedPropertyAccess("store", this, name)); CustomArguments args(isolate, data->data(), this, JSObject::cast(holder)); v8::AccessorInfo info(args.end()); @@ -1981,7 +2205,7 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, if (strict_mode == kNonStrictMode) { return value; } - Handle<String> key(name); + Handle<Name> key(name); Handle<Object> holder_handle(holder, isolate); Handle<Object> args[2] = { key, holder_handle }; return isolate->Throw( @@ -1990,6 +2214,11 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure, } } + // TODO(dcarney): Handle correctly. + if (structure->IsDeclaredAccessorInfo()) { + return value; + } + UNREACHABLE(); return NULL; } @@ -2027,10 +2256,10 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( Heap* heap = GetHeap(); for (Object* pt = GetPrototype(); pt != heap->null_value(); - pt = pt->GetPrototype()) { + pt = pt->GetPrototype(GetIsolate())) { if (pt->IsJSProxy()) { String* name; - MaybeObject* maybe = GetHeap()->Uint32ToString(index); + MaybeObject* maybe = heap->Uint32ToString(index); if (!maybe->To<String>(&name)) { *found = true; // Force abort return maybe; @@ -2061,7 +2290,7 @@ MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes( } MaybeObject* JSObject::SetPropertyViaPrototypes( - String* name, + Name* name, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode, @@ -2110,7 +2339,8 @@ MaybeObject* JSObject::SetPropertyViaPrototypes( if (!FLAG_es5_readonly) *done = false; if (*done) { if (strict_mode == kNonStrictMode) return value; - Handle<Object> args[] = { Handle<Object>(name), Handle<Object>(this)}; + Handle<Object> args[] = { Handle<Object>(name, isolate), + Handle<Object>(this, isolate)}; return isolate->Throw(*isolate->factory()->NewTypeError( "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); } @@ -2193,15 +2423,17 @@ void Map::AppendCallbackDescriptors(Handle<Map> map, ASSERT(array->NumberOfSlackDescriptors() >= nof_callbacks); - // Ensure the keys are symbols before writing them into the instance - // descriptor. Since it may cause a GC, it has to be done before we + // Ensure the keys are unique names before writing them into the + // instance descriptor. Since it may cause a GC, it has to be done before we // temporarily put the heap in an invalid state while appending descriptors. for (int i = 0; i < nof_callbacks; ++i) { Handle<AccessorInfo> entry(AccessorInfo::cast(callbacks.get(i))); - Handle<String> key = - isolate->factory()->SymbolFromString( - Handle<String>(String::cast(entry->name()))); - entry->set_name(*key); + if (!entry->name()->IsUniqueName()) { + Handle<String> key = + isolate->factory()->InternalizedStringFromString( + Handle<String>(String::cast(entry->name()))); + entry->set_name(*key); + } } int nof = map->NumberOfOwnDescriptors(); @@ -2211,7 +2443,7 @@ void Map::AppendCallbackDescriptors(Handle<Map> map, // precedence over previously added callbacks with that name. for (int i = nof_callbacks - 1; i >= 0; i--) { AccessorInfo* entry = AccessorInfo::cast(callbacks.get(i)); - String* key = String::cast(entry->name()); + Name* key = Name::cast(entry->name()); // Check if a descriptor with this name already exists before writing. if (array->Search(key, nof) == DescriptorArray::kNotFound) { CallbacksDescriptor desc(key, entry, entry->property_attributes()); @@ -2344,10 +2576,8 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { } bool allow_store_transition = - // 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 + // Only remember the map transition if there is not an already existing // non-matching element transition. - (GetIsolate()->empty_object_map() != map()) && !start_map->IsUndefined() && !start_map->is_shared() && IsFastElementsKind(from_kind); @@ -2372,8 +2602,7 @@ MaybeObject* JSObject::GetElementsTransitionMapSlow(ElementsKind to_kind) { } -void JSObject::LocalLookupRealNamedProperty(String* name, - LookupResult* result) { +void JSObject::LocalLookupRealNamedProperty(Name* name, LookupResult* result) { if (IsJSGlobalProxy()) { Object* proto = GetPrototype(); if (proto->IsNull()) return result->NotFound(); @@ -2393,14 +2622,14 @@ void JSObject::LocalLookupRealNamedProperty(String* name, // occur as fields. if (result->IsField() && result->IsReadOnly() && - FastPropertyAt(result->GetFieldIndex())->IsTheHole()) { + FastPropertyAt(result->GetFieldIndex().field_index())->IsTheHole()) { result->DisallowCaching(); } return; } int entry = property_dictionary()->FindEntry(name); - if (entry != StringDictionary::kNotFound) { + if (entry != NameDictionary::kNotFound) { Object* value = property_dictionary()->ValueAt(entry); if (IsGlobalObject()) { PropertyDetails d = property_dictionary()->DetailsAt(entry); @@ -2421,7 +2650,7 @@ void JSObject::LocalLookupRealNamedProperty(String* name, } -void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) { +void JSObject::LookupRealNamedProperty(Name* name, LookupResult* result) { LocalLookupRealNamedProperty(name, result); if (result->IsFound()) return; @@ -2429,12 +2658,13 @@ void JSObject::LookupRealNamedProperty(String* name, LookupResult* result) { } -void JSObject::LookupRealNamedPropertyInPrototypes(String* name, +void JSObject::LookupRealNamedPropertyInPrototypes(Name* name, LookupResult* result) { - Heap* heap = GetHeap(); + Isolate* isolate = GetIsolate(); + Heap* heap = isolate->heap(); for (Object* pt = GetPrototype(); pt != heap->null_value(); - pt = pt->GetPrototype()) { + pt = pt->GetPrototype(isolate)) { if (pt->IsJSProxy()) { return result->HandlerResult(JSProxy::cast(pt)); } @@ -2449,7 +2679,7 @@ void JSObject::LookupRealNamedPropertyInPrototypes(String* name, // We only need to deal with CALLBACKS and INTERCEPTORS MaybeObject* JSObject::SetPropertyWithFailedAccessCheck( LookupResult* result, - String* name, + Name* name, Object* value, bool check_prototype, StrictModeFlag strict_mode) { @@ -2497,14 +2727,14 @@ MaybeObject* JSObject::SetPropertyWithFailedAccessCheck( Isolate* isolate = GetIsolate(); HandleScope scope(isolate); - Handle<Object> value_handle(value); + Handle<Object> value_handle(value, isolate); isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); return *value_handle; } MaybeObject* JSReceiver::SetProperty(LookupResult* result, - String* key, + Name* key, Object* value, PropertyAttributes attributes, StrictModeFlag strict_mode, @@ -2519,32 +2749,32 @@ MaybeObject* JSReceiver::SetProperty(LookupResult* result, } -bool JSProxy::HasPropertyWithHandler(String* name_raw) { +bool JSProxy::HasPropertyWithHandler(Name* name_raw) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); - Handle<Object> receiver(this); - Handle<Object> name(name_raw); + Handle<Object> receiver(this, isolate); + Handle<Object> name(name_raw, isolate); Handle<Object> args[] = { name }; Handle<Object> result = CallTrap( "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args); if (isolate->has_pending_exception()) return false; - return result->ToBoolean()->IsTrue(); + return result->BooleanValue(); } MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler( JSReceiver* receiver_raw, - String* name_raw, + Name* name_raw, Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); Handle<JSReceiver> receiver(receiver_raw); - Handle<Object> name(name_raw); - Handle<Object> value(value_raw); + Handle<Object> name(name_raw, isolate); + Handle<Object> value(value_raw, isolate); Handle<Object> args[] = { receiver, name, value }; CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args); @@ -2556,7 +2786,7 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler( MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( JSReceiver* receiver_raw, - String* name_raw, + Name* name_raw, Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode, @@ -2564,9 +2794,9 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( Isolate* isolate = GetIsolate(); Handle<JSProxy> proxy(this); Handle<JSReceiver> receiver(receiver_raw); - Handle<String> name(name_raw); - Handle<Object> value(value_raw); - Handle<Object> handler(this->handler()); // Trap might morph proxy. + Handle<Name> name(name_raw); + Handle<Object> value(value_raw, isolate); + Handle<Object> handler(this->handler(), isolate); // Trap might morph proxy. *done = true; // except where redefined... Handle<Object> args[] = { name }; @@ -2589,14 +2819,16 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( // [[GetProperty]] requires to check that all properties are configurable. Handle<String> configurable_name = - isolate->factory()->LookupAsciiSymbol("configurable_"); + isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("configurable_")); Handle<Object> configurable( - v8::internal::GetProperty(desc, configurable_name)); + v8::internal::GetProperty(isolate, desc, configurable_name)); ASSERT(!isolate->has_pending_exception()); ASSERT(configurable->IsTrue() || configurable->IsFalse()); if (configurable->IsFalse()) { Handle<String> trap = - isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); + isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("getPropertyDescriptor")); Handle<Object> args[] = { handler, trap, name }; Handle<Object> error = isolate->factory()->NewTypeError( "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); @@ -2606,14 +2838,18 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( // Check for DataDescriptor. Handle<String> hasWritable_name = - isolate->factory()->LookupAsciiSymbol("hasWritable_"); - Handle<Object> hasWritable(v8::internal::GetProperty(desc, hasWritable_name)); + isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("hasWritable_")); + Handle<Object> hasWritable( + v8::internal::GetProperty(isolate, desc, hasWritable_name)); ASSERT(!isolate->has_pending_exception()); ASSERT(hasWritable->IsTrue() || hasWritable->IsFalse()); if (hasWritable->IsTrue()) { Handle<String> writable_name = - isolate->factory()->LookupAsciiSymbol("writable_"); - Handle<Object> writable(v8::internal::GetProperty(desc, writable_name)); + isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("writable_")); + Handle<Object> writable( + v8::internal::GetProperty(isolate, desc, writable_name)); ASSERT(!isolate->has_pending_exception()); ASSERT(writable->IsTrue() || writable->IsFalse()); *done = writable->IsFalse(); @@ -2626,8 +2862,9 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( } // We have an AccessorDescriptor. - Handle<String> set_name = isolate->factory()->LookupAsciiSymbol("set_"); - Handle<Object> setter(v8::internal::GetProperty(desc, set_name)); + Handle<String> set_name = isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("set_")); + Handle<Object> setter(v8::internal::GetProperty(isolate, desc, set_name)); ASSERT(!isolate->has_pending_exception()); if (!setter->IsUndefined()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... @@ -2644,28 +2881,29 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler( MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler( - String* name_raw, DeleteMode mode) { + Name* name_raw, DeleteMode mode) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); Handle<JSProxy> receiver(this); - Handle<Object> name(name_raw); + Handle<Object> name(name_raw, isolate); Handle<Object> args[] = { name }; Handle<Object> result = CallTrap( "delete", Handle<Object>(), ARRAY_SIZE(args), args); if (isolate->has_pending_exception()) return Failure::Exception(); - Object* bool_result = result->ToBoolean(); - if (mode == STRICT_DELETION && bool_result == GetHeap()->false_value()) { - Handle<Object> handler(receiver->handler()); - Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete"); + bool result_bool = result->BooleanValue(); + if (mode == STRICT_DELETION && !result_bool) { + Handle<Object> handler(receiver->handler(), isolate); + Handle<String> trap_name = isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("delete")); Handle<Object> args[] = { handler, trap_name }; Handle<Object> error = isolate->factory()->NewTypeError( "handler_failed", HandleVector(args, ARRAY_SIZE(args))); isolate->Throw(*error); return Failure::Exception(); } - return bool_result; + return isolate->heap()->ToBoolean(result_bool); } @@ -2681,13 +2919,13 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeleteElementWithHandler( MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( JSReceiver* receiver_raw, - String* name_raw) { + Name* name_raw) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); Handle<JSProxy> proxy(this); - Handle<Object> handler(this->handler()); // Trap might morph proxy. + Handle<Object> handler(this->handler(), isolate); // Trap might morph proxy. Handle<JSReceiver> receiver(receiver_raw); - Handle<Object> name(name_raw); + Handle<Object> name(name_raw, isolate); Handle<Object> args[] = { name }; Handle<Object> result = CallTrap( @@ -2704,19 +2942,22 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( 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)); + Handle<String> enum_n = isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("enumerable")); + Handle<Object> enumerable(v8::internal::GetProperty(isolate, 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)); + Handle<String> conf_n = isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("configurable")); + Handle<Object> configurable(v8::internal::GetProperty(isolate, 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)); + Handle<String> writ_n = isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("writable")); + Handle<Object> writable(v8::internal::GetProperty(isolate, desc, writ_n)); if (isolate->has_pending_exception()) return NONE; if (configurable->IsFalse()) { - Handle<String> trap = - isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor"); + Handle<String> trap = isolate->factory()->InternalizeOneByteString( + STATIC_ASCII_VECTOR("getPropertyDescriptor")); Handle<Object> args[] = { handler, trap, name }; Handle<Object> error = isolate->factory()->NewTypeError( "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args))); @@ -2725,20 +2966,22 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( } int attributes = NONE; - if (enumerable->ToBoolean()->IsFalse()) attributes |= DONT_ENUM; - if (configurable->ToBoolean()->IsFalse()) attributes |= DONT_DELETE; - if (writable->ToBoolean()->IsFalse()) attributes |= READ_ONLY; + if (!enumerable->BooleanValue()) attributes |= DONT_ENUM; + if (!configurable->BooleanValue()) attributes |= DONT_DELETE; + if (!writable->BooleanValue()) attributes |= READ_ONLY; return static_cast<PropertyAttributes>(attributes); } MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler( - JSReceiver* receiver, + JSReceiver* receiver_raw, uint32_t index) { Isolate* isolate = GetIsolate(); HandleScope scope(isolate); + Handle<JSProxy> proxy(this); + Handle<JSReceiver> receiver(receiver_raw); Handle<String> name = isolate->factory()->Uint32ToString(index); - return GetPropertyAttributeWithHandler(receiver, *name); + return proxy->GetPropertyAttributeWithHandler(*receiver, *name); } @@ -2772,10 +3015,10 @@ MUST_USE_RESULT Handle<Object> JSProxy::CallTrap(const char* name, int argc, Handle<Object> argv[]) { Isolate* isolate = GetIsolate(); - Handle<Object> handler(this->handler()); + Handle<Object> handler(this->handler(), isolate); - Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol(name); - Handle<Object> trap(v8::internal::GetProperty(handler, trap_name)); + Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name); + Handle<Object> trap(v8::internal::GetProperty(isolate, handler, trap_name)); if (isolate->has_pending_exception()) return trap; if (trap->IsUndefined()) { @@ -2802,34 +3045,37 @@ void JSObject::AddFastPropertyUsingMap(Handle<JSObject> object, } -MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, - String* name_raw, +MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup, + Name* name_raw, Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode, StoreFromKeyed store_mode) { Heap* heap = GetHeap(); + Isolate* isolate = heap->isolate(); // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; // Optimization for 2-byte strings often used as keys in a decompression - // dictionary. We make these short keys into symbols to avoid constantly + // dictionary. We internalize these short keys to avoid constantly // reallocating them. - if (!name_raw->IsSymbol() && name_raw->length() <= 2) { - Object* symbol_version; - { MaybeObject* maybe_symbol_version = heap->LookupSymbol(name_raw); - if (maybe_symbol_version->ToObject(&symbol_version)) { - name_raw = String::cast(symbol_version); + if (name_raw->IsString() && !name_raw->IsInternalizedString() && + String::cast(name_raw)->length() <= 2) { + Object* internalized_version; + { MaybeObject* maybe_string_version = + heap->InternalizeString(String::cast(name_raw)); + if (maybe_string_version->ToObject(&internalized_version)) { + name_raw = String::cast(internalized_version); } } } // Check access rights if needed. if (IsAccessCheckNeeded()) { - if (!heap->isolate()->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { + if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { return SetPropertyWithFailedAccessCheck( - result, name_raw, value_raw, true, strict_mode); + lookup, name_raw, value_raw, true, strict_mode); } } @@ -2838,66 +3084,78 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, if (proto->IsNull()) return value_raw; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->SetPropertyForResult( - result, name_raw, value_raw, attributes, strict_mode, store_mode); + lookup, name_raw, value_raw, attributes, strict_mode, store_mode); } + ASSERT(!lookup->IsFound() || lookup->holder() == this || + lookup->holder()->map()->is_hidden_prototype()); + // From this point on everything needs to be handlified, because // SetPropertyViaPrototypes might call back into JavaScript. - HandleScope scope(GetIsolate()); + HandleScope scope(isolate); Handle<JSObject> self(this); - Handle<String> name(name_raw); - Handle<Object> value(value_raw); + Handle<Name> name(name_raw); + Handle<Object> value(value_raw, isolate); - if (!result->IsProperty() && !self->IsJSContextExtensionObject()) { + if (!lookup->IsProperty() && !self->IsJSContextExtensionObject()) { bool done = false; MaybeObject* result_object = self->SetPropertyViaPrototypes( *name, *value, attributes, strict_mode, &done); if (done) return result_object; } - if (!result->IsFound()) { + if (!lookup->IsFound()) { // Neither properties nor transitions found. return self->AddProperty( *name, *value, attributes, strict_mode, store_mode); } - if (result->IsProperty() && result->IsReadOnly()) { + + if (lookup->IsProperty() && lookup->IsReadOnly()) { if (strict_mode == kStrictMode) { Handle<Object> args[] = { name, self }; - return heap->isolate()->Throw(*heap->isolate()->factory()->NewTypeError( + return isolate->Throw(*isolate->factory()->NewTypeError( "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)))); } else { return *value; } } + Handle<Object> old_value(heap->the_hole_value(), isolate); + if (FLAG_harmony_observation && + map()->is_observed() && lookup->IsDataProperty()) { + old_value = Object::GetProperty(self, name); + } + // This is a real property that is not read-only, or it is a // transition or null descriptor and there are no setters in the prototypes. - switch (result->type()) { + MaybeObject* result = *value; + switch (lookup->type()) { case NORMAL: - return self->SetNormalizedProperty(result, *value); + result = lookup->holder()->SetNormalizedProperty(lookup, *value); + break; case FIELD: - return self->FastPropertyAtPut(result->GetFieldIndex(), *value); + result = lookup->holder()->FastPropertyAtPut( + lookup->GetFieldIndex().field_index(), *value); + break; case CONSTANT_FUNCTION: // Only replace the function if necessary. - if (*value == result->GetConstantFunction()) return *value; + if (*value == lookup->GetConstantFunction()) return *value; // Preserve the attributes of this existing property. - attributes = result->GetAttributes(); - return self->ConvertDescriptorToField(*name, *value, attributes); + attributes = lookup->GetAttributes(); + result = + lookup->holder()->ConvertDescriptorToField(*name, *value, attributes); + break; case CALLBACKS: { - Object* callback_object = result->GetCallbackObject(); - return self->SetPropertyWithCallback(callback_object, - *name, - *value, - result->holder(), - strict_mode); + Object* callback_object = lookup->GetCallbackObject(); + return self->SetPropertyWithCallback( + callback_object, *name, *value, lookup->holder(), strict_mode); } case INTERCEPTOR: - return self->SetPropertyWithInterceptor(*name, - *value, - attributes, - strict_mode); + result = lookup->holder()->SetPropertyWithInterceptor( + *name, *value, attributes, strict_mode); + break; case TRANSITION: { - Map* transition_map = result->GetTransitionTarget(); + Map* transition_map = lookup->GetTransitionTarget(); int descriptor = transition_map->LastAdded(); DescriptorArray* descriptors = transition_map->instance_descriptors(); @@ -2906,37 +3164,55 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, if (details.type() == FIELD) { if (attributes == details.attributes()) { int field_index = descriptors->GetFieldIndex(descriptor); - return self->AddFastPropertyUsingMap(transition_map, - *name, - *value, - field_index); + result = lookup->holder()->AddFastPropertyUsingMap( + transition_map, *name, *value, field_index); + } else { + result = lookup->holder()->ConvertDescriptorToField( + *name, *value, attributes); } - return self->ConvertDescriptorToField(*name, *value, attributes); } else if (details.type() == CALLBACKS) { - return ConvertDescriptorToField(*name, *value, attributes); - } - - ASSERT(details.type() == CONSTANT_FUNCTION); - - Object* constant_function = descriptors->GetValue(descriptor); - // If the same constant function is being added we can simply - // transition to the target map. - if (constant_function == *value) { - self->set_map(transition_map); - return constant_function; + result = lookup->holder()->ConvertDescriptorToField( + *name, *value, attributes); + } else { + ASSERT(details.type() == CONSTANT_FUNCTION); + + Object* constant_function = descriptors->GetValue(descriptor); + if (constant_function == *value) { + // If the same constant function is being added we can simply + // transition to the target map. + lookup->holder()->set_map(transition_map); + result = constant_function; + } else { + // Otherwise, replace with a map transition to a new map with a FIELD, + // even if the value is a constant function. + result = lookup->holder()->ConvertTransitionToMapTransition( + lookup->GetTransitionIndex(), *name, *value, attributes); + } } - // Otherwise, replace with a map transition to a new map with a FIELD, - // even if the value is a constant function. - return ConvertTransitionToMapTransition( - result->GetTransitionIndex(), *name, *value, attributes); + break; } case HANDLER: case NONEXISTENT: UNREACHABLE(); - return *value; } - UNREACHABLE(); // keep the compiler happy - return *value; + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (FLAG_harmony_observation && map()->is_observed()) { + if (lookup->IsTransition()) { + EnqueueChangeRecord(self, "new", name, old_value); + } else { + LookupResult new_lookup(isolate); + self->LocalLookup(*name, &new_lookup, true); + if (new_lookup.IsDataProperty() && + !Object::GetProperty(self, name)->SameValue(*old_value)) { + EnqueueChangeRecord(self, "updated", name, old_value); + } + } + } + + return *hresult; } @@ -2951,7 +3227,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, // doesn't handle function prototypes correctly. Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes( Handle<JSObject> object, - Handle<String> key, + Handle<Name> key, Handle<Object> value, PropertyAttributes attributes) { CALL_HEAP_FUNCTION( @@ -2962,22 +3238,22 @@ Handle<Object> JSObject::SetLocalPropertyIgnoreAttributes( MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( - String* name, - Object* value, + Name* name_raw, + Object* value_raw, PropertyAttributes attributes) { // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; Isolate* isolate = GetIsolate(); - LookupResult result(isolate); - LocalLookup(name, &result); - if (!result.IsFound()) map()->LookupTransition(this, name, &result); + LookupResult lookup(isolate); + LocalLookup(name_raw, &lookup, true); + if (!lookup.IsFound()) map()->LookupTransition(this, name_raw, &lookup); // Check access rights if needed. if (IsAccessCheckNeeded()) { - if (!isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { - return SetPropertyWithFailedAccessCheck(&result, - name, - value, + if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { + return SetPropertyWithFailedAccessCheck(&lookup, + name_raw, + value_raw, false, kNonStrictMode); } @@ -2985,40 +3261,61 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( if (IsJSGlobalProxy()) { Object* proto = GetPrototype(); - if (proto->IsNull()) return value; + if (proto->IsNull()) return value_raw; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes( - name, - value, + name_raw, + value_raw, attributes); } // Check for accessor in prototype chain removed here in clone. - if (!result.IsFound()) { + if (!lookup.IsFound()) { // Neither properties nor transitions found. - return AddProperty(name, value, attributes, kNonStrictMode); + return AddProperty(name_raw, value_raw, attributes, kNonStrictMode); + } + + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + Handle<Name> name(name_raw); + Handle<Object> value(value_raw, isolate); + + Handle<Object> old_value(isolate->heap()->the_hole_value(), isolate); + PropertyAttributes old_attributes = ABSENT; + bool is_observed = FLAG_harmony_observation && self->map()->is_observed(); + if (is_observed) { + if (lookup.IsDataProperty()) old_value = Object::GetProperty(self, name); + old_attributes = lookup.GetAttributes(); } // Check of IsReadOnly removed from here in clone. - switch (result.type()) { + MaybeObject* result = *value; + switch (lookup.type()) { case NORMAL: { PropertyDetails details = PropertyDetails(attributes, NORMAL); - return SetNormalizedProperty(name, value, details); + result = self->SetNormalizedProperty(*name, *value, details); + break; } case FIELD: - return FastPropertyAtPut(result.GetFieldIndex(), value); + result = self->FastPropertyAtPut( + lookup.GetFieldIndex().field_index(), *value); + break; case CONSTANT_FUNCTION: // Only replace the function if necessary. - if (value == result.GetConstantFunction()) return value; - // Preserve the attributes of this existing property. - attributes = result.GetAttributes(); - return ConvertDescriptorToField(name, value, attributes); + if (*value != lookup.GetConstantFunction()) { + // Preserve the attributes of this existing property. + attributes = lookup.GetAttributes(); + result = self->ConvertDescriptorToField(*name, *value, attributes); + } + break; case CALLBACKS: case INTERCEPTOR: // Override callback in clone - return ConvertDescriptorToField(name, value, attributes); + result = self->ConvertDescriptorToField(*name, *value, attributes); + break; case TRANSITION: { - Map* transition_map = result.GetTransitionTarget(); + Map* transition_map = lookup.GetTransitionTarget(); int descriptor = transition_map->LastAdded(); DescriptorArray* descriptors = transition_map->instance_descriptors(); @@ -3027,35 +3324,57 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( if (details.type() == FIELD) { if (attributes == details.attributes()) { int field_index = descriptors->GetFieldIndex(descriptor); - return AddFastPropertyUsingMap(transition_map, - name, - value, - field_index); + result = self->AddFastPropertyUsingMap( + transition_map, *name, *value, field_index); + } else { + result = self->ConvertDescriptorToField(*name, *value, attributes); } - return ConvertDescriptorToField(name, value, attributes); } else if (details.type() == CALLBACKS) { - return ConvertDescriptorToField(name, value, attributes); - } - - ASSERT(details.type() == CONSTANT_FUNCTION); + result = self->ConvertDescriptorToField(*name, *value, attributes); + } else { + ASSERT(details.type() == CONSTANT_FUNCTION); - // Replace transition to CONSTANT FUNCTION with a map transition to a new - // map with a FIELD, even if the value is a function. - return ConvertTransitionToMapTransition( - result.GetTransitionIndex(), name, value, attributes); + // Replace transition to CONSTANT FUNCTION with a map transition to a + // new map with a FIELD, even if the value is a function. + result = self->ConvertTransitionToMapTransition( + lookup.GetTransitionIndex(), *name, *value, attributes); + } + break; } case HANDLER: case NONEXISTENT: UNREACHABLE(); } - UNREACHABLE(); // keep the compiler happy - return value; + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (is_observed) { + if (lookup.IsTransition()) { + EnqueueChangeRecord(self, "new", name, old_value); + } else if (old_value->IsTheHole()) { + EnqueueChangeRecord(self, "reconfigured", name, old_value); + } else { + LookupResult new_lookup(isolate); + self->LocalLookup(*name, &new_lookup, true); + bool value_changed = new_lookup.IsDataProperty() && + !old_value->SameValue(*Object::GetProperty(self, name)); + if (new_lookup.GetAttributes() != old_attributes) { + if (!value_changed) old_value = isolate->factory()->the_hole_value(); + EnqueueChangeRecord(self, "reconfigured", name, old_value); + } else if (value_changed) { + EnqueueChangeRecord(self, "updated", name, old_value); + } + } + } + + return *hresult; } PropertyAttributes JSObject::GetPropertyAttributePostInterceptor( JSObject* receiver, - String* name, + Name* name, bool continue_search) { // Check local property, ignore interceptor. LookupResult result(GetIsolate()); @@ -3076,8 +3395,11 @@ PropertyAttributes JSObject::GetPropertyAttributePostInterceptor( PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor( JSObject* receiver, - String* name, + Name* name, bool continue_search) { + // TODO(rossberg): Support symbols in the API. + if (name->IsSymbol()) return ABSENT; + Isolate* isolate = GetIsolate(); // Make sure that the top context does not change when doing @@ -3088,7 +3410,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor( Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); Handle<JSObject> receiver_handle(receiver); Handle<JSObject> holder_handle(this); - Handle<String> name_handle(name); + Handle<String> name_handle(String::cast(name)); CustomArguments args(isolate, interceptor->data(), receiver, this); v8::AccessorInfo info(args.end()); if (!interceptor->query()->IsUndefined()) { @@ -3127,45 +3449,46 @@ PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor( PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver( JSReceiver* receiver, - String* key) { + Name* key) { uint32_t index = 0; if (IsJSObject() && key->AsArrayIndex(&index)) { - return JSObject::cast(this)->HasElementWithReceiver(receiver, index) - ? NONE : ABSENT; + return JSObject::cast(this)->GetElementAttributeWithReceiver( + receiver, index, true); } // Named property. - LookupResult result(GetIsolate()); - Lookup(key, &result); - return GetPropertyAttribute(receiver, &result, key, true); + LookupResult lookup(GetIsolate()); + Lookup(key, &lookup); + return GetPropertyAttributeForResult(receiver, &lookup, key, true); } -PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver, - LookupResult* result, - String* name, - bool continue_search) { +PropertyAttributes JSReceiver::GetPropertyAttributeForResult( + JSReceiver* receiver, + LookupResult* lookup, + Name* name, + bool continue_search) { // Check access rights if needed. if (IsAccessCheckNeeded()) { JSObject* this_obj = JSObject::cast(this); Heap* heap = GetHeap(); if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) { return this_obj->GetPropertyAttributeWithFailedAccessCheck( - receiver, result, name, continue_search); + receiver, lookup, name, continue_search); } } - if (result->IsFound()) { - switch (result->type()) { + if (lookup->IsFound()) { + switch (lookup->type()) { case NORMAL: // fall through case FIELD: case CONSTANT_FUNCTION: case CALLBACKS: - return result->GetAttributes(); + return lookup->GetAttributes(); case HANDLER: { - return JSProxy::cast(result->proxy())->GetPropertyAttributeWithHandler( + return JSProxy::cast(lookup->proxy())->GetPropertyAttributeWithHandler( receiver, name); } case INTERCEPTOR: - return result->holder()->GetPropertyAttributeWithInterceptor( + return lookup->holder()->GetPropertyAttributeWithInterceptor( JSObject::cast(receiver), name, continue_search); case TRANSITION: case NONEXISTENT: @@ -3176,17 +3499,114 @@ PropertyAttributes JSReceiver::GetPropertyAttribute(JSReceiver* receiver, } -PropertyAttributes JSReceiver::GetLocalPropertyAttribute(String* name) { +PropertyAttributes JSReceiver::GetLocalPropertyAttribute(Name* name) { // Check whether the name is an array index. uint32_t index = 0; if (IsJSObject() && name->AsArrayIndex(&index)) { - if (JSObject::cast(this)->HasLocalElement(index)) return NONE; - return ABSENT; + return GetLocalElementAttribute(index); } // Named property. - LookupResult result(GetIsolate()); - LocalLookup(name, &result); - return GetPropertyAttribute(this, &result, name, false); + LookupResult lookup(GetIsolate()); + LocalLookup(name, &lookup, true); + return GetPropertyAttributeForResult(this, &lookup, name, false); +} + + +PropertyAttributes JSObject::GetElementAttributeWithReceiver( + JSReceiver* receiver, uint32_t index, bool continue_search) { + Isolate* isolate = GetIsolate(); + + // Check access rights if needed. + if (IsAccessCheckNeeded()) { + if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { + isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS); + return ABSENT; + } + } + + if (IsJSGlobalProxy()) { + Object* proto = GetPrototype(); + if (proto->IsNull()) return ABSENT; + ASSERT(proto->IsJSGlobalObject()); + return JSObject::cast(proto)->GetElementAttributeWithReceiver( + receiver, index, continue_search); + } + + // Check for lookup interceptor except when bootstrapping. + if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) { + return GetElementAttributeWithInterceptor(receiver, index, continue_search); + } + + return GetElementAttributeWithoutInterceptor( + receiver, index, continue_search); +} + + +PropertyAttributes JSObject::GetElementAttributeWithInterceptor( + JSReceiver* receiver, uint32_t index, bool continue_search) { + Isolate* isolate = GetIsolate(); + // Make sure that the top context does not change when doing + // callbacks or interceptor calls. + AssertNoContextChange ncc; + HandleScope scope(isolate); + Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); + Handle<JSReceiver> hreceiver(receiver); + Handle<JSObject> holder(this); + CustomArguments args(isolate, interceptor->data(), receiver, this); + v8::AccessorInfo info(args.end()); + if (!interceptor->query()->IsUndefined()) { + v8::IndexedPropertyQuery query = + v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); + LOG(isolate, + ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); + v8::Handle<v8::Integer> result; + { + // Leaving JavaScript. + VMState state(isolate, EXTERNAL); + result = query(index, info); + } + if (!result.IsEmpty()) + return static_cast<PropertyAttributes>(result->Int32Value()); + } else if (!interceptor->getter()->IsUndefined()) { + v8::IndexedPropertyGetter getter = + v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); + LOG(isolate, + ApiIndexedPropertyAccess("interceptor-indexed-get-has", this, index)); + v8::Handle<v8::Value> result; + { + // Leaving JavaScript. + VMState state(isolate, EXTERNAL); + result = getter(index, info); + } + if (!result.IsEmpty()) return NONE; + } + + return holder->GetElementAttributeWithoutInterceptor( + *hreceiver, index, continue_search); +} + + +PropertyAttributes JSObject::GetElementAttributeWithoutInterceptor( + JSReceiver* receiver, uint32_t index, bool continue_search) { + PropertyAttributes attr = GetElementsAccessor()->GetAttributes( + receiver, this, index); + if (attr != ABSENT) return attr; + + // Handle [] on String objects. + if (IsStringObjectWithCharacterAt(index)) { + return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); + } + + if (!continue_search) return ABSENT; + + Object* pt = GetPrototype(); + if (pt->IsJSProxy()) { + // We need to follow the spec and simulate a call to [[GetOwnProperty]]. + return JSProxy::cast(pt)->GetElementAttributeWithHandler(receiver, index); + } + if (pt->IsNull()) return ABSENT; + return JSObject::cast(pt)->GetElementAttributeWithReceiver( + receiver, index, true); } @@ -3215,7 +3635,9 @@ MaybeObject* NormalizedMapCache::Get(JSObject* obj, ASSERT(memcmp(Map::cast(fresh)->address(), Map::cast(result)->address(), Map::kCodeCacheOffset) == 0); - int offset = Map::kCodeCacheOffset + kPointerSize; + STATIC_ASSERT(Map::kDependentCodeOffset == + Map::kCodeCacheOffset + kPointerSize); + int offset = Map::kDependentCodeOffset + kPointerSize; ASSERT(memcmp(Map::cast(fresh)->address() + offset, Map::cast(result)->address() + offset, Map::kSize - offset) == 0); @@ -3246,7 +3668,7 @@ void NormalizedMapCache::Clear() { void JSObject::UpdateMapCodeCache(Handle<JSObject> object, - Handle<String> name, + Handle<Name> name, Handle<Code> code) { Isolate* isolate = object->GetIsolate(); CALL_HEAP_FUNCTION_VOID(isolate, @@ -3254,7 +3676,7 @@ void JSObject::UpdateMapCodeCache(Handle<JSObject> object, } -MaybeObject* JSObject::UpdateMapCodeCache(String* name, Code* code) { +MaybeObject* JSObject::UpdateMapCodeCache(Name* name, Code* code) { if (map()->is_shared()) { // Fast case maps are never marked as shared. ASSERT(!HasFastProperties()); @@ -3300,8 +3722,9 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, } else { property_count += 2; // Make space for two more properties. } - StringDictionary* dictionary; - MaybeObject* maybe_dictionary = StringDictionary::Allocate(property_count); + NameDictionary* dictionary; + MaybeObject* maybe_dictionary = + NameDictionary::Allocate(GetHeap(), property_count); if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary; DescriptorArray* descs = map_of_this->instance_descriptors(); @@ -3374,6 +3797,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, } set_map(new_map); + map_of_this->NotifyLeafMapLayoutChange(); set_properties(dictionary); @@ -3438,7 +3862,8 @@ MaybeObject* JSObject::NormalizeElements() { GetElementsCapacityAndUsage(&old_capacity, &used_elements); SeededNumberDictionary* dictionary = NULL; { Object* object; - MaybeObject* maybe = SeededNumberDictionary::Allocate(used_elements); + MaybeObject* maybe = + SeededNumberDictionary::Allocate(GetHeap(), used_elements); if (!maybe->ToObject(&object)) return maybe; dictionary = SeededNumberDictionary::cast(object); } @@ -3521,7 +3946,7 @@ Smi* JSReceiver::GenerateIdentityHash() { MaybeObject* JSObject::SetIdentityHash(Smi* hash, CreationFlag flag) { - MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_symbol(), + MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_string(), hash); if (maybe->IsFailure()) return maybe; return this; @@ -3537,14 +3962,14 @@ int JSObject::GetIdentityHash(Handle<JSObject> obj) { MaybeObject* JSObject::GetIdentityHash(CreationFlag flag) { - Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_symbol()); + Object* stored_value = GetHiddenProperty(GetHeap()->identity_hash_string()); if (stored_value->IsSmi()) return stored_value; // Do not generate permanent identity hash code if not requested. if (flag == OMIT_CREATION) return GetHeap()->undefined_value(); Smi* hash = GenerateIdentityHash(); - MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_symbol(), + MaybeObject* result = SetHiddenProperty(GetHeap()->identity_hash_string(), hash); if (result->IsFailure()) return result; if (result->ToObjectUnchecked()->IsUndefined()) { @@ -3565,8 +3990,8 @@ MaybeObject* JSProxy::GetIdentityHash(CreationFlag flag) { } -Object* JSObject::GetHiddenProperty(String* key) { - ASSERT(key->IsSymbol()); +Object* JSObject::GetHiddenProperty(Name* key) { + ASSERT(key->IsUniqueName()); if (IsJSGlobalProxy()) { // For a proxy, use the prototype as target object. Object* proxy_parent = GetPrototype(); @@ -3582,7 +4007,7 @@ Object* JSObject::GetHiddenProperty(String* key) { if (inline_value->IsSmi()) { // Handle inline-stored identity hash. - if (key == GetHeap()->identity_hash_symbol()) { + if (key == GetHeap()->identity_hash_string()) { return inline_value; } else { return GetHeap()->undefined_value(); @@ -3599,7 +4024,7 @@ Object* JSObject::GetHiddenProperty(String* key) { Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj, - Handle<String> key, + Handle<Name> key, Handle<Object> value) { CALL_HEAP_FUNCTION(obj->GetIsolate(), obj->SetHiddenProperty(*key, *value), @@ -3607,8 +4032,8 @@ Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj, } -MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { - ASSERT(key->IsSymbol()); +MaybeObject* JSObject::SetHiddenProperty(Name* key, Object* value) { + ASSERT(key->IsUniqueName()); if (IsJSGlobalProxy()) { // For a proxy, use the prototype as target object. Object* proxy_parent = GetPrototype(); @@ -3624,7 +4049,7 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { // If there is no backing store yet, store the identity hash inline. if (value->IsSmi() && - key == GetHeap()->identity_hash_symbol() && + key == GetHeap()->identity_hash_string() && (inline_value->IsUndefined() || inline_value->IsSmi())) { return SetHiddenPropertiesHashTable(value); } @@ -3648,8 +4073,8 @@ MaybeObject* JSObject::SetHiddenProperty(String* key, Object* value) { } -void JSObject::DeleteHiddenProperty(String* key) { - ASSERT(key->IsSymbol()); +void JSObject::DeleteHiddenProperty(Name* key) { + ASSERT(key->IsUniqueName()); if (IsJSGlobalProxy()) { // For a proxy, use the prototype as target object. Object* proxy_parent = GetPrototype(); @@ -3665,7 +4090,7 @@ void JSObject::DeleteHiddenProperty(String* key) { Object* inline_value = hidden_lookup->ToObjectUnchecked(); // We never delete (inline-stored) identity hashes. - ASSERT(key != GetHeap()->identity_hash_symbol()); + ASSERT(key != GetHeap()->identity_hash_string()); if (inline_value->IsUndefined() || inline_value->IsSmi()) return; ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value); @@ -3677,7 +4102,7 @@ void JSObject::DeleteHiddenProperty(String* key) { bool JSObject::HasHiddenProperties() { return GetPropertyAttributePostInterceptor(this, - GetHeap()->hidden_symbol(), + GetHeap()->hidden_string(), false) != ABSENT; } @@ -3688,13 +4113,13 @@ MaybeObject* JSObject::GetHiddenPropertiesHashTable( Object* inline_value; 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 + // in the descriptor array matches the hidden string. Since the + // hidden strings hash code is zero (and no other name 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) { int sorted_index = descriptors->GetSortedKeyIndex(0); - if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() && + if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() && sorted_index < map()->NumberOfOwnDescriptors()) { ASSERT(descriptors->GetType(sorted_index) == FIELD); inline_value = @@ -3707,12 +4132,12 @@ MaybeObject* JSObject::GetHiddenPropertiesHashTable( } } else { PropertyAttributes attributes; - // You can't install a getter on a property indexed by the hidden symbol, + // You can't install a getter on a property indexed by the hidden string, // so we can be sure that GetLocalPropertyPostInterceptor returns a real // object. inline_value = GetLocalPropertyPostInterceptor(this, - GetHeap()->hidden_symbol(), + GetHeap()->hidden_string(), &attributes)->ToObjectUnchecked(); } @@ -3724,7 +4149,8 @@ MaybeObject* JSObject::GetHiddenPropertiesHashTable( ObjectHashTable* hashtable; static const int kInitialCapacity = 4; MaybeObject* maybe_obj = - ObjectHashTable::Allocate(kInitialCapacity, + ObjectHashTable::Allocate(GetHeap(), + kInitialCapacity, ObjectHashTable::USE_CUSTOM_MINIMUM_CAPACITY); if (!maybe_obj->To<ObjectHashTable>(&hashtable)) return maybe_obj; @@ -3732,7 +4158,7 @@ MaybeObject* JSObject::GetHiddenPropertiesHashTable( // We were storing the identity hash inline and now allocated an actual // dictionary. Put the identity hash into the new dictionary. MaybeObject* insert_result = - hashtable->Put(GetHeap()->identity_hash_symbol(), inline_value); + hashtable->Put(GetHeap()->identity_hash_string(), inline_value); ObjectHashTable* new_table; if (!insert_result->To(&new_table)) return insert_result; // We expect no resizing for the first insert. @@ -3740,7 +4166,7 @@ MaybeObject* JSObject::GetHiddenPropertiesHashTable( } MaybeObject* store_result = - SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), + SetPropertyPostInterceptor(GetHeap()->hidden_string(), hashtable, DONT_ENUM, kNonStrictMode, @@ -3757,13 +4183,13 @@ MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) { ASSERT(HasHiddenProperties() != value->IsSmi()); 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 + // in the descriptor array matches the hidden string. Since the + // hidden strings hash code is zero (and no other name 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) { int sorted_index = descriptors->GetSortedKeyIndex(0); - if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_symbol() && + if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() && sorted_index < map()->NumberOfOwnDescriptors()) { ASSERT(descriptors->GetType(sorted_index) == FIELD); this->FastPropertyAtPut(descriptors->GetFieldIndex(sorted_index), @@ -3773,7 +4199,7 @@ MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) { } } MaybeObject* store_result = - SetPropertyPostInterceptor(GetHeap()->hidden_symbol(), + SetPropertyPostInterceptor(GetHeap()->hidden_string(), value, DONT_ENUM, kNonStrictMode, @@ -3783,7 +4209,7 @@ MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) { } -MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name, +MaybeObject* JSObject::DeletePropertyPostInterceptor(Name* name, DeleteMode mode) { // Check local property, ignore interceptor. LookupResult result(GetIsolate()); @@ -3800,11 +4226,14 @@ MaybeObject* JSObject::DeletePropertyPostInterceptor(String* name, } -MaybeObject* JSObject::DeletePropertyWithInterceptor(String* name) { +MaybeObject* JSObject::DeletePropertyWithInterceptor(Name* name) { + // TODO(rossberg): Support symbols in the API. + if (name->IsSymbol()) return GetHeap()->false_value(); + Isolate* isolate = GetIsolate(); HandleScope scope(isolate); Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); - Handle<String> name_handle(name); + Handle<String> name_handle(String::cast(name)); Handle<JSObject> this_handle(this); if (!interceptor->deleter()->IsUndefined()) { v8::NamedPropertyDeleter deleter = @@ -3893,7 +4322,7 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { if (mode == STRICT_DELETION) { // Deleting a non-configurable property in strict mode. HandleScope scope(isolate); - Handle<Object> holder(this); + Handle<Object> holder(this, isolate); Handle<Object> name = isolate->factory()->NewNumberFromUint(index); Handle<Object> args[2] = { name, holder }; Handle<Object> error = @@ -3911,30 +4340,53 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { return JSGlobalObject::cast(proto)->DeleteElement(index, mode); } - if (HasIndexedInterceptor()) { - // Skip interceptor if forcing deletion. - if (mode != FORCE_DELETION) { - return DeleteElementWithInterceptor(index); + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + + Handle<Object> old_value; + bool should_enqueue_change_record = false; + if (FLAG_harmony_observation && self->map()->is_observed()) { + should_enqueue_change_record = self->HasLocalElement(index); + if (should_enqueue_change_record) { + old_value = self->GetLocalElementAccessorPair(index) != NULL + ? Handle<Object>::cast(isolate->factory()->the_hole_value()) + : Object::GetElement(self, index); } - mode = JSReceiver::FORCE_DELETION; } - return GetElementsAccessor()->Delete(this, index, mode); + MaybeObject* result; + // Skip interceptor if forcing deletion. + if (self->HasIndexedInterceptor() && mode != FORCE_DELETION) { + result = self->DeleteElementWithInterceptor(index); + } else { + result = self->GetElementsAccessor()->Delete(*self, index, mode); + } + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (should_enqueue_change_record && !self->HasLocalElement(index)) { + Handle<String> name = isolate->factory()->Uint32ToString(index); + EnqueueChangeRecord(self, "deleted", name, old_value); + } + + return *hresult; } Handle<Object> JSObject::DeleteProperty(Handle<JSObject> obj, - Handle<String> prop) { + Handle<Name> prop) { CALL_HEAP_FUNCTION(obj->GetIsolate(), obj->DeleteProperty(*prop, JSObject::NORMAL_DELETION), Object); } -MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { +MaybeObject* JSObject::DeleteProperty(Name* name, DeleteMode mode) { Isolate* isolate = GetIsolate(); // ECMA-262, 3rd, 8.6.2.5 - ASSERT(name->IsString()); + ASSERT(name->IsName()); // Check access rights if needed. if (IsAccessCheckNeeded() && @@ -3953,38 +4405,61 @@ MaybeObject* JSObject::DeleteProperty(String* name, DeleteMode mode) { uint32_t index = 0; if (name->AsArrayIndex(&index)) { return DeleteElement(index, mode); - } else { - LookupResult result(isolate); - LocalLookup(name, &result); - if (!result.IsFound()) return isolate->heap()->true_value(); - // Ignore attributes if forcing a deletion. - if (result.IsDontDelete() && mode != FORCE_DELETION) { - if (mode == STRICT_DELETION) { - // Deleting a non-configurable property in strict mode. - HandleScope scope(isolate); - Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) }; - return isolate->Throw(*isolate->factory()->NewTypeError( - "strict_delete_property", HandleVector(args, 2))); - } - return isolate->heap()->false_value(); - } - // Check for interceptor. - if (result.IsInterceptor()) { - // Skip interceptor if forcing a deletion. - if (mode == FORCE_DELETION) { - return DeletePropertyPostInterceptor(name, mode); - } - return DeletePropertyWithInterceptor(name); + } + + LookupResult lookup(isolate); + LocalLookup(name, &lookup, true); + if (!lookup.IsFound()) return isolate->heap()->true_value(); + // Ignore attributes if forcing a deletion. + if (lookup.IsDontDelete() && mode != FORCE_DELETION) { + if (mode == STRICT_DELETION) { + // Deleting a non-configurable property in strict mode. + HandleScope scope(isolate); + Handle<Object> args[2] = { Handle<Object>(name, isolate), + Handle<Object>(this, isolate) }; + return isolate->Throw(*isolate->factory()->NewTypeError( + "strict_delete_property", HandleVector(args, 2))); + } + return isolate->heap()->false_value(); + } + + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + Handle<Name> hname(name); + + Handle<Object> old_value = isolate->factory()->the_hole_value(); + bool is_observed = FLAG_harmony_observation && self->map()->is_observed(); + if (is_observed && lookup.IsDataProperty()) { + old_value = Object::GetProperty(self, hname); + } + MaybeObject* result; + + // Check for interceptor. + if (lookup.IsInterceptor()) { + // Skip interceptor if forcing a deletion. + if (mode == FORCE_DELETION) { + result = self->DeletePropertyPostInterceptor(*hname, mode); + } else { + result = self->DeletePropertyWithInterceptor(*hname); } + } else { // Normalize object if needed. Object* obj; - { MaybeObject* maybe_obj = - NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + result = self->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); + if (!result->To(&obj)) return result; // Make sure the properties are normalized before removing the entry. - return DeleteNormalizedProperty(name, mode); + result = self->DeleteNormalizedProperty(*hname, mode); } + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (is_observed && !self->HasLocalProperty(*hname)) { + EnqueueChangeRecord(self, "deleted", hname, old_value); + } + + return *hresult; } @@ -3996,7 +4471,7 @@ MaybeObject* JSReceiver::DeleteElement(uint32_t index, DeleteMode mode) { } -MaybeObject* JSReceiver::DeleteProperty(String* name, DeleteMode mode) { +MaybeObject* JSReceiver::DeleteProperty(Name* name, DeleteMode mode) { if (IsJSProxy()) { return JSProxy::cast(this)->DeletePropertyWithHandler(name, mode); } @@ -4160,7 +4635,7 @@ MaybeObject* JSObject::PreventExtensions() { // It's not possible to seal objects with external array elements if (HasExternalArrayElements()) { HandleScope scope(isolate); - Handle<Object> object(this); + Handle<Object> object(this, isolate); Handle<Object> error = isolate->factory()->NewTypeError( "cant_prevent_ext_external_array_elements", @@ -4222,13 +4697,16 @@ int Map::NumberOfDescribedProperties(DescriptorFlag which, ? descs->number_of_descriptors() : NumberOfOwnDescriptors(); for (int i = 0; i < limit; i++) { - if ((descs->GetDetails(i).attributes() & filter) == 0) result++; + if ((descs->GetDetails(i).attributes() & filter) == 0 && + ((filter & SYMBOLIC) == 0 || !descs->GetKey(i)->IsSymbol())) { + result++; + } } return result; } -int Map::PropertyIndexFor(String* name) { +int Map::PropertyIndexFor(Name* name) { DescriptorArray* descs = instance_descriptors(); int limit = NumberOfOwnDescriptors(); for (int i = 0; i < limit; i++) { @@ -4252,7 +4730,7 @@ int Map::NextFreePropertyIndex() { } -AccessorDescriptor* Map::FindAccessor(String* name) { +AccessorDescriptor* Map::FindAccessor(Name* name) { DescriptorArray* descs = instance_descriptors(); int number_of_own_descriptors = NumberOfOwnDescriptors(); for (int i = 0; i < number_of_own_descriptors; i++) { @@ -4264,8 +4742,9 @@ AccessorDescriptor* Map::FindAccessor(String* name) { } -void JSReceiver::LocalLookup(String* name, LookupResult* result) { - ASSERT(name->IsString()); +void JSReceiver::LocalLookup( + Name* name, LookupResult* result, bool search_hidden_prototypes) { + ASSERT(name->IsName()); Heap* heap = GetHeap(); @@ -4273,7 +4752,8 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) { Object* proto = GetPrototype(); if (proto->IsNull()) return result->NotFound(); ASSERT(proto->IsJSGlobalObject()); - return JSReceiver::cast(proto)->LocalLookup(name, result); + return JSReceiver::cast(proto)->LocalLookup( + name, result, search_hidden_prototypes); } if (IsJSProxy()) { @@ -4289,12 +4769,6 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) { JSObject* js_object = JSObject::cast(this); - // Check __proto__ before interceptor. - if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) { - result->ConstantResult(js_object); - return; - } - // Check for lookup interceptor except when bootstrapping. if (js_object->HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) { @@ -4303,16 +4777,24 @@ void JSReceiver::LocalLookup(String* name, LookupResult* result) { } js_object->LocalLookupRealNamedProperty(name, result); + if (result->IsFound() || !search_hidden_prototypes) return; + + Object* proto = js_object->GetPrototype(); + if (!proto->IsJSReceiver()) return; + JSReceiver* receiver = JSReceiver::cast(proto); + if (receiver->map()->is_hidden_prototype()) { + receiver->LocalLookup(name, result, search_hidden_prototypes); + } } -void JSReceiver::Lookup(String* name, LookupResult* result) { +void JSReceiver::Lookup(Name* name, LookupResult* result) { // Ecma-262 3rd 8.6.2.4 Heap* heap = GetHeap(); for (Object* current = this; current != heap->null_value(); current = JSObject::cast(current)->GetPrototype()) { - JSReceiver::cast(current)->LocalLookup(name, result); + JSReceiver::cast(current)->LocalLookup(name, result, false); if (result->IsFound()) return; } result->NotFound(); @@ -4320,7 +4802,7 @@ void JSReceiver::Lookup(String* name, LookupResult* result) { // Search object and its prototype chain for callback properties. -void JSObject::LookupCallbackProperty(String* name, LookupResult* result) { +void JSObject::LookupCallbackProperty(Name* name, LookupResult* result) { Heap* heap = GetHeap(); for (Object* current = this; current != heap->null_value() && current->IsJSObject(); @@ -4426,7 +4908,7 @@ MaybeObject* JSObject::DefineElementAccessor(uint32_t index, } -MaybeObject* JSObject::CreateAccessorPairFor(String* name) { +MaybeObject* JSObject::CreateAccessorPairFor(Name* name) { LookupResult result(GetHeap()->isolate()); LocalLookupRealNamedProperty(name, &result); if (result.IsPropertyCallbacks()) { @@ -4445,7 +4927,7 @@ MaybeObject* JSObject::CreateAccessorPairFor(String* name) { } -MaybeObject* JSObject::DefinePropertyAccessor(String* name, +MaybeObject* JSObject::DefinePropertyAccessor(Name* name, Object* getter, Object* setter, PropertyAttributes attributes) { @@ -4453,7 +4935,9 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name, // to do a lookup, which seems to be a bit of overkill. Heap* heap = GetHeap(); bool only_attribute_changes = getter->IsNull() && setter->IsNull(); - if (HasFastProperties() && !only_attribute_changes) { + if (HasFastProperties() && !only_attribute_changes && + (map()->NumberOfOwnDescriptors() < + DescriptorArray::kMaxNumberOfDescriptors)) { MaybeObject* getterOk = heap->undefined_value(); if (!getter->IsNull()) { getterOk = DefineFastAccessor(name, ACCESSOR_GETTER, getter, attributes); @@ -4480,7 +4964,7 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name, } -bool JSObject::CanSetCallback(String* name) { +bool JSObject::CanSetCallback(Name* name) { ASSERT(!IsAccessCheckNeeded() || GetIsolate()->MayNamedAccess(this, name, v8::ACCESS_SET)); @@ -4542,7 +5026,7 @@ MaybeObject* JSObject::SetElementCallback(uint32_t index, } -MaybeObject* JSObject::SetPropertyCallback(String* name, +MaybeObject* JSObject::SetPropertyCallback(Name* name, Object* structure, PropertyAttributes attributes) { // Normalize object to make this operation simple. @@ -4574,7 +5058,7 @@ MaybeObject* JSObject::SetPropertyCallback(String* name, void JSObject::DefineAccessor(Handle<JSObject> object, - Handle<String> name, + Handle<Name> name, Handle<Object> getter, Handle<Object> setter, PropertyAttributes attributes) { @@ -4583,14 +5067,14 @@ void JSObject::DefineAccessor(Handle<JSObject> object, object->DefineAccessor(*name, *getter, *setter, attributes)); } -MaybeObject* JSObject::DefineAccessor(String* name, - Object* getter, - Object* setter, +MaybeObject* JSObject::DefineAccessor(Name* name_raw, + Object* getter_raw, + Object* setter_raw, PropertyAttributes attributes) { Isolate* isolate = GetIsolate(); // Check access rights if needed. if (IsAccessCheckNeeded() && - !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { + !isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) { isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); return isolate->heap()->undefined_value(); } @@ -4600,7 +5084,7 @@ MaybeObject* JSObject::DefineAccessor(String* name, if (proto->IsNull()) return this; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->DefineAccessor( - name, getter, setter, attributes); + name_raw, getter_raw, setter_raw, attributes); } // Make sure that the top context does not change when doing callbacks or @@ -4608,14 +5092,52 @@ MaybeObject* JSObject::DefineAccessor(String* name, AssertNoContextChange ncc; // Try to flatten before operating on the string. - name->TryFlatten(); + if (name_raw->IsString()) String::cast(name_raw)->TryFlatten(); - if (!CanSetCallback(name)) return isolate->heap()->undefined_value(); + if (!CanSetCallback(name_raw)) return isolate->heap()->undefined_value(); + + // From this point on everything needs to be handlified. + HandleScope scope(isolate); + Handle<JSObject> self(this); + Handle<Name> name(name_raw); + Handle<Object> getter(getter_raw, isolate); + Handle<Object> setter(setter_raw, isolate); uint32_t index = 0; - return name->AsArrayIndex(&index) ? - DefineElementAccessor(index, getter, setter, attributes) : - DefinePropertyAccessor(name, getter, setter, attributes); + bool is_element = name->AsArrayIndex(&index); + + Handle<Object> old_value = isolate->factory()->the_hole_value(); + bool is_observed = FLAG_harmony_observation && self->map()->is_observed(); + bool preexists = false; + if (is_observed) { + if (is_element) { + preexists = HasLocalElement(index); + if (preexists && self->GetLocalElementAccessorPair(index) == NULL) { + old_value = Object::GetElement(self, index); + } + } else { + LookupResult lookup(isolate); + LocalLookup(*name, &lookup, true); + preexists = lookup.IsProperty(); + if (preexists && lookup.IsDataProperty()) { + old_value = Object::GetProperty(self, name); + } + } + } + + MaybeObject* result = is_element ? + self->DefineElementAccessor(index, *getter, *setter, attributes) : + self->DefinePropertyAccessor(*name, *getter, *setter, attributes); + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + if (is_observed) { + const char* type = preexists ? "reconfigured" : "new"; + EnqueueChangeRecord(self, type, name, old_value); + } + + return *hresult; } @@ -4648,7 +5170,7 @@ static MaybeObject* TryAccessorTransition(JSObject* self, } -MaybeObject* JSObject::DefineFastAccessor(String* name, +MaybeObject* JSObject::DefineFastAccessor(Name* name, AccessorComponent component, Object* accessor, PropertyAttributes attributes) { @@ -4695,7 +5217,8 @@ MaybeObject* JSObject::DefineFastAccessor(String* name, if (result.IsFound()) { Map* target = result.GetTransitionTarget(); int descriptor_number = target->LastAdded(); - ASSERT(target->instance_descriptors()->GetKey(descriptor_number) == name); + ASSERT(target->instance_descriptors()->GetKey(descriptor_number) + ->Equals(name)); return TryAccessorTransition( this, target, descriptor_number, component, accessor, attributes); } @@ -4730,7 +5253,7 @@ MaybeObject* JSObject::DefineFastAccessor(String* name, MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { Isolate* isolate = GetIsolate(); - String* name = String::cast(info->name()); + Name* name = Name::cast(info->name()); // Check access rights if needed. if (IsAccessCheckNeeded() && !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) { @@ -4750,7 +5273,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { AssertNoContextChange ncc; // Try to flatten before operating on the string. - name->TryFlatten(); + if (name->IsString()) String::cast(name)->TryFlatten(); if (!CanSetCallback(name)) return isolate->heap()->undefined_value(); @@ -4794,7 +5317,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { } else { // Lookup the name. LookupResult result(isolate); - LocalLookup(name, &result); + LocalLookup(name, &result, true); // ES5 forbids turning a property into an accessor if it's not // configurable (that is IsDontDelete in ES3 and v8), see 8.6.1 (Table 5). if (result.IsFound() && (result.IsReadOnly() || result.IsDontDelete())) { @@ -4810,7 +5333,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) { } -Object* JSObject::LookupAccessor(String* name, AccessorComponent component) { +Object* JSObject::LookupAccessor(Name* name, AccessorComponent component) { Heap* heap = GetHeap(); // Make sure that the top context does not change when doing callbacks or @@ -4896,7 +5419,6 @@ MaybeObject* Map::RawCopy(int instance_size) { result->set_constructor(constructor()); result->set_bit_field(bit_field()); result->set_bit_field2(bit_field2()); - result->set_bit_field3(bit_field3()); int new_bit_field3 = bit_field3(); new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true); new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0); @@ -4921,7 +5443,6 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode, result->set_inobject_properties(inobject_properties()); } - result->set_code_cache(code_cache()); result->set_is_shared(sharing == SHARED_NORMALIZED_MAP); result->set_dictionary_map(true); @@ -4947,6 +5468,7 @@ MaybeObject* Map::CopyDropDescriptors() { result->set_pre_allocated_property_fields(pre_allocated_property_fields()); result->set_is_shared(false); result->ClearCodeCache(GetHeap()); + NotifyLeafMapLayoutChange(); return result; } @@ -4962,7 +5484,7 @@ MaybeObject* Map::ShareDescriptor(DescriptorArray* descriptors, MaybeObject* maybe_result = CopyDropDescriptors(); if (!maybe_result->To(&result)) return maybe_result; - String* name = descriptor->GetKey(); + Name* name = descriptor->GetKey(); TransitionArray* transitions; MaybeObject* maybe_transitions = @@ -5027,7 +5549,7 @@ MaybeObject* Map::ShareDescriptor(DescriptorArray* descriptors, MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors, - String* name, + Name* name, TransitionFlag flag, int descriptor_index) { ASSERT(descriptors->IsSortedNoDuplicates()); @@ -5143,8 +5665,8 @@ MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor, TransitionFlag flag) { DescriptorArray* descriptors = instance_descriptors(); - // Ensure the key is a symbol. - MaybeObject* maybe_failure = descriptor->KeyToSymbol(); + // Ensure the key is unique. + MaybeObject* maybe_failure = descriptor->KeyToUniqueName(); if (maybe_failure->IsFailure()) return maybe_failure; int old_size = NumberOfOwnDescriptors(); @@ -5176,7 +5698,7 @@ MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor, new_descriptors->Append(descriptor, witness); } - String* key = descriptor->GetKey(); + Name* key = descriptor->GetKey(); int insertion_index = new_descriptors->number_of_descriptors() - 1; return CopyReplaceDescriptors(new_descriptors, key, flag, insertion_index); @@ -5187,8 +5709,8 @@ MaybeObject* Map::CopyInsertDescriptor(Descriptor* descriptor, TransitionFlag flag) { DescriptorArray* old_descriptors = instance_descriptors(); - // Ensure the key is a symbol. - MaybeObject* maybe_result = descriptor->KeyToSymbol(); + // Ensure the key is unique. + MaybeObject* maybe_result = descriptor->KeyToUniqueName(); if (maybe_result->IsFailure()) return maybe_result; // We replace the key if it is already present. @@ -5224,11 +5746,11 @@ MaybeObject* Map::CopyReplaceDescriptor(DescriptorArray* descriptors, Descriptor* descriptor, int insertion_index, TransitionFlag flag) { - // Ensure the key is a symbol. - MaybeObject* maybe_failure = descriptor->KeyToSymbol(); + // Ensure the key is unique. + MaybeObject* maybe_failure = descriptor->KeyToUniqueName(); if (maybe_failure->IsFailure()) return maybe_failure; - String* key = descriptor->GetKey(); + Name* key = descriptor->GetKey(); ASSERT(key == descriptors->GetKey(insertion_index)); int new_size = NumberOfOwnDescriptors(); @@ -5259,7 +5781,7 @@ MaybeObject* Map::CopyReplaceDescriptor(DescriptorArray* descriptors, void Map::UpdateCodeCache(Handle<Map> map, - Handle<String> name, + Handle<Name> name, Handle<Code> code) { Isolate* isolate = map->GetIsolate(); CALL_HEAP_FUNCTION_VOID(isolate, @@ -5267,7 +5789,7 @@ void Map::UpdateCodeCache(Handle<Map> map, } -MaybeObject* Map::UpdateCodeCache(String* name, Code* code) { +MaybeObject* Map::UpdateCodeCache(Name* name, Code* code) { ASSERT(!is_shared() || code->allowed_in_shared_map_code_cache()); // Allocate the code cache if not present. @@ -5284,7 +5806,7 @@ MaybeObject* Map::UpdateCodeCache(String* name, Code* code) { } -Object* Map::FindInCodeCache(String* name, Code::Flags flags) { +Object* Map::FindInCodeCache(Name* name, Code::Flags flags) { // Do a lookup if a code cache exists. if (!code_cache()->IsFixedArray()) { return CodeCache::cast(code_cache())->Lookup(name, flags); @@ -5303,7 +5825,7 @@ int Map::IndexInCodeCache(Object* name, Code* code) { } -void Map::RemoveFromCodeCache(String* name, Code* code, int index) { +void Map::RemoveFromCodeCache(Name* name, Code* code, int index) { // No GC is supposed to happen between a call to IndexInCodeCache and // RemoveFromCodeCache so the code cache must be there. ASSERT(!code_cache()->IsFixedArray()); @@ -5506,7 +6028,7 @@ void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { } -MaybeObject* CodeCache::Update(String* name, Code* code) { +MaybeObject* CodeCache::Update(Name* name, Code* code) { // The number of monomorphic stubs for normal load/store/call IC's can grow to // a large number and therefore they need to go into a hash table. They are // used to load global properties from cells. @@ -5515,7 +6037,8 @@ MaybeObject* CodeCache::Update(String* name, Code* code) { if (normal_type_cache()->IsUndefined()) { Object* result; { MaybeObject* maybe_result = - CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize); + CodeCacheHashTable::Allocate(GetHeap(), + CodeCacheHashTable::kInitialSize); if (!maybe_result->ToObject(&result)) return maybe_result; } set_normal_type_cache(result); @@ -5528,7 +6051,7 @@ MaybeObject* CodeCache::Update(String* name, Code* code) { } -MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) { +MaybeObject* CodeCache::UpdateDefaultCache(Name* name, Code* code) { // When updating the default code cache we disregard the type encoded in the // flags. This allows call constant stubs to overwrite call field // stubs, etc. @@ -5551,7 +6074,7 @@ MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) { cache->set(i + kCodeCacheEntryCodeOffset, code); return this; } - if (name->Equals(String::cast(key))) { + if (name->Equals(Name::cast(key))) { Code::Flags found = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags(); if (Code::RemoveTypeFromFlags(found) == flags) { @@ -5588,7 +6111,7 @@ MaybeObject* CodeCache::UpdateDefaultCache(String* name, Code* code) { } -MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) { +MaybeObject* CodeCache::UpdateNormalTypeCache(Name* name, Code* code) { // Adding a new entry can cause a new cache to be allocated. CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); Object* new_cache; @@ -5600,7 +6123,7 @@ MaybeObject* CodeCache::UpdateNormalTypeCache(String* name, Code* code) { } -Object* CodeCache::Lookup(String* name, Code::Flags flags) { +Object* CodeCache::Lookup(Name* name, Code::Flags flags) { if (Code::ExtractTypeFromFlags(flags) == Code::NORMAL) { return LookupNormalTypeCache(name, flags); } else { @@ -5609,7 +6132,7 @@ Object* CodeCache::Lookup(String* name, Code::Flags flags) { } -Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) { +Object* CodeCache::LookupDefaultCache(Name* name, Code::Flags flags) { FixedArray* cache = default_cache(); int length = cache->length(); for (int i = 0; i < length; i += kCodeCacheEntrySize) { @@ -5617,7 +6140,7 @@ Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) { // Skip deleted elements. if (key->IsNull()) continue; if (key->IsUndefined()) return key; - if (name->Equals(String::cast(key))) { + if (name->Equals(Name::cast(key))) { Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset)); if (code->flags() == flags) { return code; @@ -5628,7 +6151,7 @@ Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) { } -Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) { +Object* CodeCache::LookupNormalTypeCache(Name* name, Code::Flags flags) { if (!normal_type_cache()->IsUndefined()) { CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); return cache->Lookup(name, flags); @@ -5642,7 +6165,7 @@ int CodeCache::GetIndex(Object* name, Code* code) { if (code->type() == Code::NORMAL) { if (normal_type_cache()->IsUndefined()) return -1; CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); - return cache->GetIndex(String::cast(name), code->flags()); + return cache->GetIndex(Name::cast(name), code->flags()); } FixedArray* array = default_cache(); @@ -5658,7 +6181,7 @@ void CodeCache::RemoveByIndex(Object* name, Code* code, int index) { if (code->type() == Code::NORMAL) { ASSERT(!normal_type_cache()->IsUndefined()); CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache()); - ASSERT(cache->GetIndex(String::cast(name), code->flags()) == index); + ASSERT(cache->GetIndex(Name::cast(name), code->flags()) == index); cache->RemoveByIndex(index); } else { FixedArray* array = default_cache(); @@ -5679,10 +6202,10 @@ void CodeCache::RemoveByIndex(Object* name, Code* code, int index) { // lookup not to create a new entry. class CodeCacheHashTableKey : public HashTableKey { public: - CodeCacheHashTableKey(String* name, Code::Flags flags) + CodeCacheHashTableKey(Name* name, Code::Flags flags) : name_(name), flags_(flags), code_(NULL) { } - CodeCacheHashTableKey(String* name, Code* code) + CodeCacheHashTableKey(Name* name, Code* code) : name_(name), flags_(code->flags()), code_(code) { } @@ -5691,7 +6214,7 @@ class CodeCacheHashTableKey : public HashTableKey { bool IsMatch(Object* other) { if (!other->IsFixedArray()) return false; FixedArray* pair = FixedArray::cast(other); - String* name = String::cast(pair->get(0)); + Name* name = Name::cast(pair->get(0)); Code::Flags flags = Code::cast(pair->get(1))->flags(); if (flags != flags_) { return false; @@ -5699,7 +6222,7 @@ class CodeCacheHashTableKey : public HashTableKey { return name_->Equals(name); } - static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) { + static uint32_t NameFlagsHashHelper(Name* name, Code::Flags flags) { return name->Hash() ^ flags; } @@ -5707,15 +6230,15 @@ class CodeCacheHashTableKey : public HashTableKey { uint32_t HashForObject(Object* obj) { FixedArray* pair = FixedArray::cast(obj); - String* name = String::cast(pair->get(0)); + Name* name = Name::cast(pair->get(0)); Code* code = Code::cast(pair->get(1)); return NameFlagsHashHelper(name, code->flags()); } - MUST_USE_RESULT MaybeObject* AsObject() { + MUST_USE_RESULT MaybeObject* AsObject(Heap* heap) { ASSERT(code_ != NULL); Object* obj; - { MaybeObject* maybe_obj = code_->GetHeap()->AllocateFixedArray(2); + { MaybeObject* maybe_obj = heap->AllocateFixedArray(2); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } FixedArray* pair = FixedArray::cast(obj); @@ -5725,14 +6248,14 @@ class CodeCacheHashTableKey : public HashTableKey { } private: - String* name_; + Name* name_; Code::Flags flags_; // TODO(jkummerow): We should be able to get by without this. Code* code_; }; -Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) { +Object* CodeCacheHashTable::Lookup(Name* name, Code::Flags flags) { CodeCacheHashTableKey key(name, flags); int entry = FindEntry(&key); if (entry == kNotFound) return GetHeap()->undefined_value(); @@ -5740,7 +6263,7 @@ Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) { } -MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) { +MaybeObject* CodeCacheHashTable::Put(Name* name, Code* code) { CodeCacheHashTableKey key(name, code); Object* obj; { MaybeObject* maybe_obj = EnsureCapacity(1, &key); @@ -5752,7 +6275,7 @@ MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) { int entry = cache->FindInsertionEntry(key.Hash()); Object* k; - { MaybeObject* maybe_k = key.AsObject(); + { MaybeObject* maybe_k = key.AsObject(GetHeap()); if (!maybe_k->ToObject(&k)) return maybe_k; } @@ -5763,7 +6286,7 @@ MaybeObject* CodeCacheHashTable::Put(String* name, Code* code) { } -int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) { +int CodeCacheHashTable::GetIndex(Name* name, Code::Flags flags) { CodeCacheHashTableKey key(name, flags); int entry = FindEntry(&key); return (entry == kNotFound) ? -1 : entry; @@ -5796,6 +6319,7 @@ MaybeObject* PolymorphicCodeCache::Update(MapHandleList* maps, Object* result; { MaybeObject* maybe_result = PolymorphicCodeCacheHashTable::Allocate( + GetHeap(), PolymorphicCodeCacheHashTable::kInitialSize); if (!maybe_result->ToObject(&result)) return maybe_result; } @@ -5821,7 +6345,7 @@ Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps, if (!cache()->IsUndefined()) { PolymorphicCodeCacheHashTable* hash_table = PolymorphicCodeCacheHashTable::cast(cache()); - return Handle<Object>(hash_table->Lookup(maps, flags)); + return Handle<Object>(hash_table->Lookup(maps, flags), GetIsolate()); } else { return GetIsolate()->factory()->undefined_value(); } @@ -5884,13 +6408,13 @@ class PolymorphicCodeCacheHashTableKey : public HashTableKey { return MapsHashHelper(&other_maps, other_flags); } - MUST_USE_RESULT MaybeObject* AsObject() { + MUST_USE_RESULT MaybeObject* AsObject(Heap* heap) { Object* obj; // The maps in |maps_| must be copied to a newly allocated FixedArray, // both because the referenced MapList is short-lived, and because C++ // objects can't be stored in the heap anyway. { MaybeObject* maybe_obj = - HEAP->AllocateUninitializedFixedArray(maps_->length() + 1); + heap->AllocateUninitializedFixedArray(maps_->length() + 1); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } FixedArray* list = FixedArray::cast(obj); @@ -5940,7 +6464,7 @@ MaybeObject* PolymorphicCodeCacheHashTable::Put(MapHandleList* maps, PolymorphicCodeCacheHashTable* cache = reinterpret_cast<PolymorphicCodeCacheHashTable*>(obj); int entry = cache->FindInsertionEntry(key.Hash()); - { MaybeObject* maybe_obj = key.AsObject(); + { MaybeObject* maybe_obj = key.AsObject(GetHeap()); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } cache->set(EntryToIndex(entry), obj); @@ -5960,7 +6484,7 @@ MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) { if (FLAG_enable_slow_asserts) { for (int i = 0; i < result->length(); i++) { Object* current = result->get(i); - ASSERT(current->IsNumber() || current->IsString()); + ASSERT(current->IsNumber() || current->IsName()); } } #endif @@ -5978,7 +6502,7 @@ MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) { if (FLAG_enable_slow_asserts) { for (int i = 0; i < result->length(); i++) { Object* current = result->get(i); - ASSERT(current->IsNumber() || current->IsString()); + ASSERT(current->IsNumber() || current->IsName()); } } #endif @@ -6209,14 +6733,14 @@ String::FlatContent String::GetFlatContent() { ASSERT(shape.representation_tag() != kConsStringTag && shape.representation_tag() != kSlicedStringTag); } - if (shape.encoding_tag() == kAsciiStringTag) { - const char* start; + if (shape.encoding_tag() == kOneByteStringTag) { + const uint8_t* start; if (shape.representation_tag() == kSeqStringTag) { - start = SeqAsciiString::cast(string)->GetChars(); + start = SeqOneByteString::cast(string)->GetChars(); } else { start = ExternalAsciiString::cast(string)->GetChars(); } - return FlatContent(Vector<const char>(start + offset, length)); + return FlatContent(Vector<const uint8_t>(start + offset, length)); } else { ASSERT(shape.encoding_tag() == kTwoByteStringTag); const uc16* start; @@ -6244,14 +6768,14 @@ SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, if (length < 0) length = kMaxInt - offset; // Compute the size of the UTF-8 string. Start at the specified offset. - Access<StringInputBuffer> buffer( - heap->isolate()->objects_string_input_buffer()); - buffer->Reset(offset, this); + Access<ConsStringIteratorOp> op( + heap->isolate()->objects_string_iterator()); + StringCharacterStream stream(this, op.value(), offset); int character_position = offset; int utf8_bytes = 0; int last = unibrow::Utf16::kNoPreviousCharacter; - while (buffer->has_more() && character_position++ < offset + length) { - uint16_t character = buffer->GetNext(); + while (stream.HasMore() && character_position++ < offset + length) { + uint16_t character = stream.GetNext(); utf8_bytes += unibrow::Utf8::Length(character, last); last = character; } @@ -6263,13 +6787,12 @@ SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, char* result = NewArray<char>(utf8_bytes + 1); // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset. - buffer->Rewind(); - buffer->Seek(offset); + stream.Reset(this, offset); character_position = offset; int utf8_byte_position = 0; last = unibrow::Utf16::kNoPreviousCharacter; - while (buffer->has_more() && character_position++ < offset + length) { - uint16_t character = buffer->GetNext(); + while (stream.HasMore() && character_position++ < offset + length) { + uint16_t character = stream.GetNext(); if (allow_nulls == DISALLOW_NULLS && character == 0) { character = ' '; } @@ -6295,7 +6818,7 @@ const uc16* String::GetTwoByteData() { const uc16* String::GetTwoByteData(unsigned start) { - ASSERT(!IsAsciiRepresentationUnderneath()); + ASSERT(!IsOneByteRepresentationUnderneath()); switch (StringShape(this).representation_tag()) { case kSeqStringTag: return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start); @@ -6321,15 +6844,15 @@ SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { } Heap* heap = GetHeap(); - Access<StringInputBuffer> buffer( - heap->isolate()->objects_string_input_buffer()); - buffer->Reset(this); + Access<ConsStringIteratorOp> op( + heap->isolate()->objects_string_iterator()); + StringCharacterStream stream(this, op.value()); uc16* result = NewArray<uc16>(length() + 1); int i = 0; - while (buffer->has_more()) { - uint16_t character = buffer->GetNext(); + while (stream.HasMore()) { + uint16_t character = stream.GetNext(); result[i++] = character; } result[i] = 0; @@ -6343,252 +6866,6 @@ const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) { } -void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - unsigned chars_read = 0; - unsigned offset = *offset_ptr; - while (chars_read < max_chars) { - uint16_t c = *reinterpret_cast<uint16_t*>( - reinterpret_cast<char*>(this) - - kHeapObjectTag + kHeaderSize + offset * kShortSize); - if (c <= kMaxAsciiCharCode) { - // Fast case for ASCII characters. Cursor is an input output argument. - if (!unibrow::CharacterStream::EncodeAsciiCharacter(c, - rbb->util_buffer, - rbb->capacity, - rbb->cursor)) { - break; - } - } else { - if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c, - rbb->util_buffer, - rbb->capacity, - rbb->cursor)) { - break; - } - } - offset++; - chars_read++; - } - *offset_ptr = offset; - rbb->remaining += chars_read; -} - - -const unibrow::byte* SeqAsciiString::SeqAsciiStringReadBlock( - unsigned* remaining, - unsigned* offset_ptr, - unsigned max_chars) { - const unibrow::byte* b = reinterpret_cast<unibrow::byte*>(this) - - kHeapObjectTag + kHeaderSize + *offset_ptr * kCharSize; - *remaining = max_chars; - *offset_ptr += max_chars; - return b; -} - - -// This will iterate unless the block of string data spans two 'halves' of -// a ConsString, in which case it will recurse. Since the block of string -// data to be read has a maximum size this limits the maximum recursion -// depth to something sane. Since C++ does not have tail call recursion -// elimination, the iteration must be explicit. Since this is not an -// -IntoBuffer method it can delegate to one of the efficient -// *AsciiStringReadBlock routines. -const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - ConsString* current = this; - unsigned offset = *offset_ptr; - int offset_correction = 0; - - while (true) { - String* left = current->first(); - unsigned left_length = (unsigned)left->length(); - if (left_length > offset && - (max_chars <= left_length - offset || - (rbb->capacity <= left_length - offset && - (max_chars = left_length - offset, true)))) { // comma operator! - // Left hand side only - iterate unless we have reached the bottom of - // the cons tree. The assignment on the left of the comma operator is - // in order to make use of the fact that the -IntoBuffer routines can - // produce at most 'capacity' characters. This enables us to postpone - // the point where we switch to the -IntoBuffer routines (below) in order - // to maximize the chances of delegating a big chunk of work to the - // efficient *AsciiStringReadBlock routines. - if (StringShape(left).IsCons()) { - current = ConsString::cast(left); - continue; - } else { - const unibrow::byte* answer = - String::ReadBlock(left, rbb, &offset, max_chars); - *offset_ptr = offset + offset_correction; - return answer; - } - } else if (left_length <= offset) { - // Right hand side only - iterate unless we have reached the bottom of - // the cons tree. - String* right = current->second(); - offset -= left_length; - offset_correction += left_length; - if (StringShape(right).IsCons()) { - current = ConsString::cast(right); - continue; - } else { - const unibrow::byte* answer = - String::ReadBlock(right, rbb, &offset, max_chars); - *offset_ptr = offset + offset_correction; - return answer; - } - } else { - // The block to be read spans two sides of the ConsString, so we call the - // -IntoBuffer version, which will recurse. The -IntoBuffer methods - // are able to assemble data from several part strings because they use - // the util_buffer to store their data and never return direct pointers - // to their storage. We don't try to read more than the buffer capacity - // here or we can get too much recursion. - ASSERT(rbb->remaining == 0); - ASSERT(rbb->cursor == 0); - current->ConsStringReadBlockIntoBuffer( - rbb, - &offset, - max_chars > rbb->capacity ? rbb->capacity : max_chars); - *offset_ptr = offset + offset_correction; - return rbb->util_buffer; - } - } -} - - -const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock( - unsigned* remaining, - unsigned* offset_ptr, - unsigned max_chars) { - // Cast const char* to unibrow::byte* (signedness difference). - const unibrow::byte* b = - reinterpret_cast<const unibrow::byte*>(GetChars()) + *offset_ptr; - *remaining = max_chars; - *offset_ptr += max_chars; - return b; -} - - -void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer( - ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - unsigned chars_read = 0; - unsigned offset = *offset_ptr; - const uint16_t* data = GetChars(); - while (chars_read < max_chars) { - uint16_t c = data[offset]; - if (c <= kMaxAsciiCharCode) { - // Fast case for ASCII characters. Cursor is an input output argument. - if (!unibrow::CharacterStream::EncodeAsciiCharacter(c, - rbb->util_buffer, - rbb->capacity, - rbb->cursor)) - break; - } else { - if (!unibrow::CharacterStream::EncodeNonAsciiCharacter(c, - rbb->util_buffer, - rbb->capacity, - rbb->cursor)) - break; - } - offset++; - chars_read++; - } - *offset_ptr = offset; - rbb->remaining += chars_read; -} - - -void SeqAsciiString::SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - unsigned capacity = rbb->capacity - rbb->cursor; - if (max_chars > capacity) max_chars = capacity; - memcpy(rbb->util_buffer + rbb->cursor, - reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize + - *offset_ptr * kCharSize, - max_chars); - rbb->remaining += max_chars; - *offset_ptr += max_chars; - rbb->cursor += max_chars; -} - - -void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer( - ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - unsigned capacity = rbb->capacity - rbb->cursor; - if (max_chars > capacity) max_chars = capacity; - memcpy(rbb->util_buffer + rbb->cursor, GetChars() + *offset_ptr, max_chars); - rbb->remaining += max_chars; - *offset_ptr += max_chars; - rbb->cursor += max_chars; -} - - -// This method determines the type of string involved and then copies -// a whole chunk of characters into a buffer, or returns a pointer to a buffer -// where they can be found. The pointer is not necessarily valid across a GC -// (see AsciiStringReadBlock). -const unibrow::byte* String::ReadBlock(String* input, - ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - ASSERT(*offset_ptr <= static_cast<unsigned>(input->length())); - if (max_chars == 0) { - rbb->remaining = 0; - return NULL; - } - switch (StringShape(input).representation_tag()) { - case kSeqStringTag: - if (input->IsAsciiRepresentation()) { - SeqAsciiString* str = SeqAsciiString::cast(input); - return str->SeqAsciiStringReadBlock(&rbb->remaining, - offset_ptr, - max_chars); - } else { - SeqTwoByteString* str = SeqTwoByteString::cast(input); - str->SeqTwoByteStringReadBlockIntoBuffer(rbb, - offset_ptr, - max_chars); - return rbb->util_buffer; - } - case kConsStringTag: - return ConsString::cast(input)->ConsStringReadBlock(rbb, - offset_ptr, - max_chars); - case kExternalStringTag: - if (input->IsAsciiRepresentation()) { - return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock( - &rbb->remaining, - offset_ptr, - max_chars); - } else { - ExternalTwoByteString::cast(input)-> - ExternalTwoByteStringReadBlockIntoBuffer(rbb, - offset_ptr, - max_chars); - return rbb->util_buffer; - } - case kSlicedStringTag: - return SlicedString::cast(input)->SlicedStringReadBlock(rbb, - offset_ptr, - max_chars); - default: - break; - } - - UNREACHABLE(); - return 0; -} - - void Relocatable::PostGarbageCollectionProcessing() { Isolate* isolate = Isolate::Current(); Relocatable* current = isolate->relocatable_top(); @@ -6666,168 +6943,145 @@ void FlatStringReader::PostGarbageCollection() { ASSERT(content.IsFlat()); is_ascii_ = content.IsAscii(); if (is_ascii_) { - start_ = content.ToAsciiVector().start(); + start_ = content.ToOneByteVector().start(); } else { start_ = content.ToUC16Vector().start(); } } -void StringInputBuffer::Seek(unsigned pos) { - Reset(pos, input_); -} - - -void SafeStringInputBuffer::Seek(unsigned pos) { - Reset(pos, input_); +String* ConsStringIteratorOp::Operate(String* string, + unsigned* offset_out, + int32_t* type_out, + unsigned* length_out) { + ASSERT(string->IsConsString()); + ConsString* cons_string = ConsString::cast(string); + // Set up search data. + root_ = cons_string; + consumed_ = *offset_out; + // Now search. + return Search(offset_out, type_out, length_out); } -// This method determines the type of string involved and then copies -// a whole chunk of characters into a buffer. It can be used with strings -// that have been glued together to form a ConsString and which must cooperate -// to fill up a buffer. -void String::ReadBlockIntoBuffer(String* input, - ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - ASSERT(*offset_ptr <= (unsigned)input->length()); - if (max_chars == 0) return; - - switch (StringShape(input).representation_tag()) { - case kSeqStringTag: - if (input->IsAsciiRepresentation()) { - SeqAsciiString::cast(input)->SeqAsciiStringReadBlockIntoBuffer(rbb, - offset_ptr, - max_chars); - return; - } else { - SeqTwoByteString::cast(input)->SeqTwoByteStringReadBlockIntoBuffer(rbb, - offset_ptr, - max_chars); - return; +String* ConsStringIteratorOp::Search(unsigned* offset_out, + int32_t* type_out, + unsigned* length_out) { + ConsString* cons_string = root_; + // Reset the stack, pushing the root string. + depth_ = 1; + maximum_depth_ = 1; + frames_[0] = cons_string; + const unsigned consumed = consumed_; + unsigned offset = 0; + while (true) { + // Loop until the string is found which contains the target offset. + String* string = cons_string->first(); + unsigned length = string->length(); + int32_t type; + if (consumed < offset + length) { + // Target offset is in the left branch. + // Keep going if we're still in a ConString. + type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) == kConsStringTag) { + cons_string = ConsString::cast(string); + PushLeft(cons_string); + continue; } - case kConsStringTag: - ConsString::cast(input)->ConsStringReadBlockIntoBuffer(rbb, - offset_ptr, - max_chars); - return; - case kExternalStringTag: - if (input->IsAsciiRepresentation()) { - ExternalAsciiString::cast(input)-> - ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars); - } else { - ExternalTwoByteString::cast(input)-> - ExternalTwoByteStringReadBlockIntoBuffer(rbb, - offset_ptr, - max_chars); - } - return; - case kSlicedStringTag: - SlicedString::cast(input)->SlicedStringReadBlockIntoBuffer(rbb, - offset_ptr, - max_chars); - return; - default: - break; + // Tell the stack we're done decending. + AdjustMaximumDepth(); + } else { + // Descend right. + // Update progress through the string. + offset += length; + // Keep going if we're still in a ConString. + string = cons_string->second(); + type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) == kConsStringTag) { + cons_string = ConsString::cast(string); + PushRight(cons_string); + // TODO(dcarney) Add back root optimization. + continue; + } + // Need this to be updated for the current string. + length = string->length(); + // Account for the possibility of an empty right leaf. + // This happens only if we have asked for an offset outside the string. + if (length == 0) { + // Reset depth so future operations will return null immediately. + Reset(); + return NULL; + } + // Tell the stack we're done decending. + AdjustMaximumDepth(); + // Pop stack so next iteration is in correct place. + Pop(); + } + ASSERT(length != 0); + // Adjust return values and exit. + consumed_ = offset + length; + *offset_out = consumed - offset; + *type_out = type; + *length_out = length; + return string; } - UNREACHABLE(); - return; + return NULL; } -const unibrow::byte* String::ReadBlock(String* input, - unibrow::byte* util_buffer, - unsigned capacity, - unsigned* remaining, - unsigned* offset_ptr) { - ASSERT(*offset_ptr <= (unsigned)input->length()); - unsigned chars = input->length() - *offset_ptr; - ReadBlockBuffer rbb(util_buffer, 0, capacity, 0); - const unibrow::byte* answer = ReadBlock(input, &rbb, offset_ptr, chars); - ASSERT(rbb.remaining <= static_cast<unsigned>(input->length())); - *remaining = rbb.remaining; - return answer; -} - - -const unibrow::byte* String::ReadBlock(String** raw_input, - unibrow::byte* util_buffer, - unsigned capacity, - unsigned* remaining, - unsigned* offset_ptr) { - Handle<String> input(raw_input); - ASSERT(*offset_ptr <= (unsigned)input->length()); - unsigned chars = input->length() - *offset_ptr; - if (chars > capacity) chars = capacity; - ReadBlockBuffer rbb(util_buffer, 0, capacity, 0); - ReadBlockIntoBuffer(*input, &rbb, offset_ptr, chars); - ASSERT(rbb.remaining <= static_cast<unsigned>(input->length())); - *remaining = rbb.remaining; - return rbb.util_buffer; -} - - -// This will iterate unless the block of string data spans two 'halves' of -// a ConsString, in which case it will recurse. Since the block of string -// data to be read has a maximum size this limits the maximum recursion -// depth to something sane. Since C++ does not have tail call recursion -// elimination, the iteration must be explicit. -void ConsString::ConsStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { - ConsString* current = this; - unsigned offset = *offset_ptr; - int offset_correction = 0; - +String* ConsStringIteratorOp::NextLeaf(bool* blew_stack, + int32_t* type_out, + unsigned* length_out) { while (true) { - String* left = current->first(); - unsigned left_length = (unsigned)left->length(); - if (left_length > offset && - max_chars <= left_length - offset) { - // Left hand side only - iterate unless we have reached the bottom of - // the cons tree. - if (StringShape(left).IsCons()) { - current = ConsString::cast(left); - continue; - } else { - String::ReadBlockIntoBuffer(left, rbb, &offset, max_chars); - *offset_ptr = offset + offset_correction; - return; - } - } else if (left_length <= offset) { - // Right hand side only - iterate unless we have reached the bottom of - // the cons tree. - offset -= left_length; - offset_correction += left_length; - String* right = current->second(); - if (StringShape(right).IsCons()) { - current = ConsString::cast(right); - continue; - } else { - String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars); - *offset_ptr = offset + offset_correction; - return; - } - } else { - // The block to be read spans two sides of the ConsString, so we recurse. - // First recurse on the left. - max_chars -= left_length - offset; - String::ReadBlockIntoBuffer(left, rbb, &offset, left_length - offset); - // We may have reached the max or there may not have been enough space - // in the buffer for the characters in the left hand side. - if (offset == left_length) { - // Recurse on the right. - String* right = String::cast(current->second()); - offset -= left_length; - offset_correction += left_length; - String::ReadBlockIntoBuffer(right, rbb, &offset, max_chars); - } - *offset_ptr = offset + offset_correction; - return; + // Tree traversal complete. + if (depth_ == 0) { + *blew_stack = false; + return NULL; + } + // We've lost track of higher nodes. + if (maximum_depth_ - depth_ == kStackSize) { + *blew_stack = true; + return NULL; + } + // Go right. + ConsString* cons_string = frames_[OffsetForDepth(depth_ - 1)]; + String* string = cons_string->second(); + int32_t type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) != kConsStringTag) { + // Pop stack so next iteration is in correct place. + Pop(); + unsigned length = static_cast<unsigned>(string->length()); + // Could be a flattened ConsString. + if (length == 0) continue; + *length_out = length; + *type_out = type; + consumed_ += length; + return string; + } + cons_string = ConsString::cast(string); + // TODO(dcarney) Add back root optimization. + PushRight(cons_string); + // Need to traverse all the way left. + while (true) { + // Continue left. + string = cons_string->first(); + type = string->map()->instance_type(); + if ((type & kStringRepresentationMask) != kConsStringTag) { + AdjustMaximumDepth(); + unsigned length = static_cast<unsigned>(string->length()); + ASSERT(length != 0); + *length_out = length; + *type_out = type; + consumed_ += length; + return string; + } + cons_string = ConsString::cast(string); + PushLeft(cons_string); } } + UNREACHABLE(); + return NULL; } @@ -6867,26 +7121,6 @@ uint16_t SlicedString::SlicedStringGet(int 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, @@ -6898,7 +7132,7 @@ void String::WriteToFlat(String* src, while (true) { ASSERT(0 <= from && from <= to && to <= source->length()); switch (StringShape(source).full_representation_tag()) { - case kAsciiStringTag | kExternalStringTag: { + case kOneByteStringTag | kExternalStringTag: { CopyChars(sink, ExternalAsciiString::cast(source)->GetChars() + from, to - from); @@ -6912,9 +7146,9 @@ void String::WriteToFlat(String* src, to - from); return; } - case kAsciiStringTag | kSeqStringTag: { + case kOneByteStringTag | kSeqStringTag: { CopyChars(sink, - SeqAsciiString::cast(source)->GetChars() + from, + SeqOneByteString::cast(source)->GetChars() + from, to - from); return; } @@ -6924,7 +7158,7 @@ void String::WriteToFlat(String* src, to - from); return; } - case kAsciiStringTag | kConsStringTag: + case kOneByteStringTag | kConsStringTag: case kTwoByteStringTag | kConsStringTag: { ConsString* cons_string = ConsString::cast(source); String* first = cons_string->first(); @@ -6949,9 +7183,9 @@ void String::WriteToFlat(String* src, // common case of sequential ascii right child. if (to - boundary == 1) { sink[boundary - from] = static_cast<sinkchar>(second->Get(0)); - } else if (second->IsSeqAsciiString()) { + } else if (second->IsSeqOneByteString()) { CopyChars(sink + boundary - from, - SeqAsciiString::cast(second)->GetChars(), + SeqOneByteString::cast(second)->GetChars(), to - boundary); } else { WriteToFlat(second, @@ -6965,7 +7199,7 @@ void String::WriteToFlat(String* src, } break; } - case kAsciiStringTag | kSlicedStringTag: + case kOneByteStringTag | kSlicedStringTag: case kTwoByteStringTag | kSlicedStringTag: { SlicedString* slice = SlicedString::cast(source); unsigned offset = slice->offset(); @@ -6977,46 +7211,28 @@ void String::WriteToFlat(String* src, } -template <typename IteratorA, typename IteratorB> -static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) { - // General slow case check. We know that the ia and ib iterators - // have the same length. - while (ia->has_more()) { - uint32_t ca = ia->GetNext(); - uint32_t cb = ib->GetNext(); - ASSERT(ca <= unibrow::Utf16::kMaxNonSurrogateCharCode); - ASSERT(cb <= unibrow::Utf16::kMaxNonSurrogateCharCode); - if (ca != cb) - return false; - } - return true; -} - - // Compares the contents of two strings by reading and comparing // int-sized blocks of characters. template <typename Char> -static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) { - int length = a.length(); - ASSERT_EQ(length, b.length()); - const Char* pa = a.start(); - const Char* pb = b.start(); +static inline bool CompareRawStringContents(const Char* const a, + const Char* const b, + int length) { int i = 0; #ifndef V8_HOST_CAN_READ_UNALIGNED // If this architecture isn't comfortable reading unaligned ints // then we have to check that the strings are aligned before // comparing them blockwise. const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT - uint32_t pa_addr = reinterpret_cast<uint32_t>(pa); - uint32_t pb_addr = reinterpret_cast<uint32_t>(pb); + uint32_t pa_addr = reinterpret_cast<uint32_t>(a); + uint32_t pb_addr = reinterpret_cast<uint32_t>(b); if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) { #endif const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT int endpoint = length - kStepSize; // Compare blocks until we reach near the end of the string. for (; i <= endpoint; i += kStepSize) { - uint32_t wa = *reinterpret_cast<const uint32_t*>(pa + i); - uint32_t wb = *reinterpret_cast<const uint32_t*>(pb + i); + uint32_t wa = *reinterpret_cast<const uint32_t*>(a + i); + uint32_t wb = *reinterpret_cast<const uint32_t*>(b + i); if (wa != wb) { return false; } @@ -7034,25 +7250,145 @@ static inline bool CompareRawStringContents(Vector<Char> a, Vector<Char> b) { } -template <typename IteratorA> -static inline bool CompareStringContentsPartial(Isolate* isolate, - IteratorA* ia, - String* b) { - String::FlatContent content = b->GetFlatContent(); - if (content.IsFlat()) { - if (content.IsAscii()) { - VectorIterator<char> ib(content.ToAsciiVector()); - return CompareStringContents(ia, &ib); - } else { - VectorIterator<uc16> ib(content.ToUC16Vector()); - return CompareStringContents(ia, &ib); +template<typename Chars1, typename Chars2> +class RawStringComparator : public AllStatic { + public: + static inline bool compare(const Chars1* a, const Chars2* b, int len) { + ASSERT(sizeof(Chars1) != sizeof(Chars2)); + for (int i = 0; i < len; i++) { + if (a[i] != b[i]) { + return false; + } } - } else { - isolate->objects_string_compare_buffer_b()->Reset(0, b); - return CompareStringContents(ia, - isolate->objects_string_compare_buffer_b()); + return true; } -} +}; + + +template<> +class RawStringComparator<uint16_t, uint16_t> { + public: + static inline bool compare(const uint16_t* a, const uint16_t* b, int len) { + return CompareRawStringContents(a, b, len); + } +}; + + +template<> +class RawStringComparator<uint8_t, uint8_t> { + public: + static inline bool compare(const uint8_t* a, const uint8_t* b, int len) { + return CompareRawStringContents(a, b, len); + } +}; + + +class StringComparator { + class State { + public: + explicit inline State(ConsStringIteratorOp* op) + : op_(op), is_one_byte_(true), length_(0), buffer8_(NULL) {} + + inline void Init(String* string, unsigned len) { + op_->Reset(); + int32_t type = string->map()->instance_type(); + String::Visit(string, 0, *this, *op_, type, len); + } + + inline void VisitOneByteString(const uint8_t* chars, unsigned length) { + is_one_byte_ = true; + buffer8_ = chars; + length_ = length; + } + + inline void VisitTwoByteString(const uint16_t* chars, unsigned length) { + is_one_byte_ = false; + buffer16_ = chars; + length_ = length; + } + + void Advance(unsigned consumed) { + ASSERT(consumed <= length_); + // Still in buffer. + if (length_ != consumed) { + if (is_one_byte_) { + buffer8_ += consumed; + } else { + buffer16_ += consumed; + } + length_ -= consumed; + return; + } + // Advance state. + ASSERT(op_->HasMore()); + int32_t type = 0; + unsigned length = 0; + String* next = op_->ContinueOperation(&type, &length); + ASSERT(next != NULL); + ConsStringNullOp null_op; + String::Visit(next, 0, *this, null_op, type, length); + } + + ConsStringIteratorOp* const op_; + bool is_one_byte_; + unsigned length_; + union { + const uint8_t* buffer8_; + const uint16_t* buffer16_; + }; + DISALLOW_IMPLICIT_CONSTRUCTORS(State); + }; + + public: + inline StringComparator(ConsStringIteratorOp* op_1, + ConsStringIteratorOp* op_2) + : state_1_(op_1), + state_2_(op_2) { + } + + template<typename Chars1, typename Chars2> + static inline bool Equals(State* state_1, State* state_2, unsigned to_check) { + const Chars1* a = reinterpret_cast<const Chars1*>(state_1->buffer8_); + const Chars2* b = reinterpret_cast<const Chars2*>(state_2->buffer8_); + return RawStringComparator<Chars1, Chars2>::compare(a, b, to_check); + } + + bool Equals(unsigned length, String* string_1, String* string_2) { + ASSERT(length != 0); + state_1_.Init(string_1, length); + state_2_.Init(string_2, length); + while (true) { + unsigned to_check = Min(state_1_.length_, state_2_.length_); + ASSERT(to_check > 0 && to_check <= length); + bool is_equal; + if (state_1_.is_one_byte_) { + if (state_2_.is_one_byte_) { + is_equal = Equals<uint8_t, uint8_t>(&state_1_, &state_2_, to_check); + } else { + is_equal = Equals<uint8_t, uint16_t>(&state_1_, &state_2_, to_check); + } + } else { + if (state_2_.is_one_byte_) { + is_equal = Equals<uint16_t, uint8_t>(&state_1_, &state_2_, to_check); + } else { + is_equal = Equals<uint16_t, uint16_t>(&state_1_, &state_2_, to_check); + } + } + // Looping done. + if (!is_equal) return false; + length -= to_check; + // Exit condition. Strings are equal. + if (length == 0) return true; + state_1_.Advance(to_check); + state_2_.Advance(to_check); + } + } + + private: + State state_1_; + State state_2_; + DISALLOW_IMPLICIT_CONSTRUCTORS(StringComparator); +}; bool String::SlowEquals(String* other) { @@ -7088,63 +7424,24 @@ bool String::SlowEquals(String* other) { String* lhs = this->TryFlattenGetString(); String* rhs = other->TryFlattenGetString(); + // TODO(dcarney): Compare all types of flat strings with a Visitor. if (StringShape(lhs).IsSequentialAscii() && StringShape(rhs).IsSequentialAscii()) { - const char* str1 = SeqAsciiString::cast(lhs)->GetChars(); - const char* str2 = SeqAsciiString::cast(rhs)->GetChars(); - return CompareRawStringContents(Vector<const char>(str1, len), - Vector<const char>(str2, len)); + const uint8_t* str1 = SeqOneByteString::cast(lhs)->GetChars(); + const uint8_t* str2 = SeqOneByteString::cast(rhs)->GetChars(); + return CompareRawStringContents(str1, str2, len); } Isolate* isolate = GetIsolate(); - 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_content.ToUC16Vector()); - return CompareStringContents(&buf1, &ib); - } - } else { - VectorIterator<char> buf1(vec1); - isolate->objects_string_compare_buffer_b()->Reset(0, rhs); - return CompareStringContents(&buf1, - isolate->objects_string_compare_buffer_b()); - } - } else { - Vector<const uc16> vec1 = lhs_content.ToUC16Vector(); - if (rhs_content.IsFlat()) { - if (rhs_content.IsAscii()) { - VectorIterator<uc16> buf1(vec1); - VectorIterator<char> ib(rhs_content.ToAsciiVector()); - return CompareStringContents(&buf1, &ib); - } else { - Vector<const uc16> vec2(rhs_content.ToUC16Vector()); - return CompareRawStringContents(vec1, vec2); - } - } else { - VectorIterator<uc16> buf1(vec1); - isolate->objects_string_compare_buffer_b()->Reset(0, rhs); - return CompareStringContents(&buf1, - isolate->objects_string_compare_buffer_b()); - } - } - } else { - isolate->objects_string_compare_buffer_a()->Reset(0, lhs); - return CompareStringContentsPartial(isolate, - isolate->objects_string_compare_buffer_a(), rhs); - } + StringComparator comparator(isolate->objects_string_compare_iterator_a(), + isolate->objects_string_compare_iterator_b()); + + return comparator.Equals(static_cast<unsigned>(len), lhs, rhs); } bool String::MarkAsUndetectable() { - if (StringShape(this).IsSymbol()) return false; + if (StringShape(this).IsInternalized()) return false; Map* map = this->map(); Heap* heap = GetHeap(); @@ -7160,15 +7457,21 @@ bool String::MarkAsUndetectable() { } -bool String::IsEqualTo(Vector<const char> str) { - Isolate* isolate = GetIsolate(); +bool String::IsUtf8EqualTo(Vector<const char> str) { int slen = length(); - Access<UnicodeCache::Utf8Decoder> - decoder(isolate->unicode_cache()->utf8_decoder()); - decoder->Reset(str.start(), str.length()); + // Can't check exact length equality, but we can check bounds. + int str_len = str.length(); + if (str_len < slen || + str_len > slen*static_cast<int>(unibrow::Utf8::kMaxEncodedSize)) { + return false; + } int i; - for (i = 0; i < slen && decoder->has_more(); i++) { - uint32_t r = decoder->GetNext(); + unsigned remaining_in_str = static_cast<unsigned>(str_len); + const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(str.start()); + for (i = 0; i < slen && remaining_in_str > 0; i++) { + unsigned cursor = 0; + uint32_t r = unibrow::Utf8::ValueOf(utf8_data, remaining_in_str, &cursor); + ASSERT(cursor > 0 && cursor <= remaining_in_str); if (r > unibrow::Utf16::kMaxNonSurrogateCharCode) { if (i > slen - 1) return false; if (Get(i++) != unibrow::Utf16::LeadSurrogate(r)) return false; @@ -7176,17 +7479,19 @@ bool String::IsEqualTo(Vector<const char> str) { } else { if (Get(i) != r) return false; } + utf8_data += cursor; + remaining_in_str -= cursor; } - return i == slen && !decoder->has_more(); + return i == slen && remaining_in_str == 0; } -bool String::IsAsciiEqualTo(Vector<const char> str) { +bool String::IsOneByteEqualTo(Vector<const uint8_t> str) { int slen = length(); if (str.length() != slen) return false; FlatContent content = GetFlatContent(); if (content.IsAscii()) { - return CompareChars(content.ToAsciiVector().start(), + return CompareChars(content.ToOneByteVector().start(), str.start(), slen) == 0; } for (int i = 0; i < slen; i++) { @@ -7210,28 +7515,62 @@ bool String::IsTwoByteEqualTo(Vector<const uc16> str) { } +class IteratingStringHasher: public StringHasher { + public: + static inline uint32_t Hash(String* string, uint32_t seed) { + const unsigned len = static_cast<unsigned>(string->length()); + IteratingStringHasher hasher(len, seed); + if (hasher.has_trivial_hash()) { + return hasher.GetHashField(); + } + int32_t type = string->map()->instance_type(); + ConsStringNullOp null_op; + String::Visit(string, 0, hasher, null_op, type, len); + // Flat strings terminate immediately. + if (hasher.consumed_ == len) { + ASSERT(!string->IsConsString()); + return hasher.GetHashField(); + } + ASSERT(string->IsConsString()); + // This is a ConsString, iterate across it. + ConsStringIteratorOp op; + unsigned offset = 0; + unsigned leaf_length = len; + string = op.Operate(string, &offset, &type, &leaf_length); + while (true) { + ASSERT(hasher.consumed_ < len); + String::Visit(string, 0, hasher, null_op, type, leaf_length); + if (hasher.consumed_ == len) break; + string = op.ContinueOperation(&type, &leaf_length); + // This should be taken care of by the length check. + ASSERT(string != NULL); + } + return hasher.GetHashField(); + } + inline void VisitOneByteString(const uint8_t* chars, unsigned length) { + AddCharacters(chars, static_cast<int>(length)); + consumed_ += length; + } + inline void VisitTwoByteString(const uint16_t* chars, unsigned length) { + AddCharacters(chars, static_cast<int>(length)); + consumed_ += length; + } + + private: + inline IteratingStringHasher(int len, uint32_t seed) + : StringHasher(len, seed), + consumed_(0) {} + unsigned consumed_; + DISALLOW_COPY_AND_ASSIGN(IteratingStringHasher); +}; + + uint32_t String::ComputeAndSetHash() { // Should only be called if hash code has not yet been computed. ASSERT(!HasHashCode()); - const int len = length(); - - // Compute the hash code. - uint32_t field = 0; - if (StringShape(this).IsSequentialAscii()) { - field = HashSequentialString(SeqAsciiString::cast(this)->GetChars(), - len, - GetHeap()->HashSeed()); - } else if (StringShape(this).IsSequentialTwoByte()) { - field = HashSequentialString(SeqTwoByteString::cast(this)->GetChars(), - len, - GetHeap()->HashSeed()); - } else { - StringInputBuffer buffer(this); - field = ComputeHashField(&buffer, len, GetHeap()->HashSeed()); - } - // Store the hash code in the object. + uint32_t field = IteratingStringHasher::Hash(this, GetHeap()->HashSeed()); set_hash_field(field); // Check the hash code is there. @@ -7242,11 +7581,12 @@ uint32_t String::ComputeAndSetHash() { } -bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer, - uint32_t* index, - int length) { +bool String::ComputeArrayIndex(uint32_t* index) { + int length = this->length(); if (length == 0 || length > kMaxArrayIndexSize) return false; - uc32 ch = buffer->GetNext(); + ConsStringIteratorOp op; + StringCharacterStream stream(this, &op); + uint16_t ch = stream.GetNext(); // If the string begins with a '0' character, it must only consist // of it to be a legal array index. @@ -7259,8 +7599,8 @@ bool String::ComputeArrayIndex(unibrow::CharacterStream* buffer, int d = ch - '0'; if (d < 0 || d > 9) return false; uint32_t result = d; - while (buffer->has_more()) { - d = buffer->GetNext() - '0'; + while (stream.HasMore()) { + d = stream.GetNext() - '0'; if (d < 0 || d > 9) return false; // Check that the new result is below the 32 bit limit. if (result > 429496729U - ((d > 5) ? 1 : 0)) return false; @@ -7281,12 +7621,106 @@ bool String::SlowAsArrayIndex(uint32_t* index) { *index = (kArrayIndexHashMask & field) >> kHashShift; return true; } else { - StringInputBuffer buffer(this); - return ComputeArrayIndex(&buffer, index, length()); + return ComputeArrayIndex(index); } } +String* SeqString::Truncate(int new_length) { + Heap* heap = GetHeap(); + if (new_length <= 0) return heap->empty_string(); + + int string_size, allocated_string_size; + int old_length = length(); + if (old_length <= new_length) return this; + + if (IsSeqOneByteString()) { + allocated_string_size = SeqOneByteString::SizeFor(old_length); + string_size = SeqOneByteString::SizeFor(new_length); + } else { + allocated_string_size = SeqTwoByteString::SizeFor(old_length); + string_size = SeqTwoByteString::SizeFor(new_length); + } + + int delta = allocated_string_size - string_size; + set_length(new_length); + + // String sizes are pointer size aligned, so that we can use filler objects + // that are a multiple of pointer size. + Address end_of_string = address() + string_size; + heap->CreateFillerObjectAt(end_of_string, delta); + if (Marking::IsBlack(Marking::MarkBitFrom(this))) { + MemoryChunk::IncrementLiveBytesFromMutator(address(), -delta); + } + return this; +} + + +AllocationSiteInfo* AllocationSiteInfo::FindForJSObject(JSObject* object) { + // Currently, AllocationSiteInfo objects are only allocated immediately + // after JSArrays in NewSpace, and detecting whether a JSArray has one + // involves carefully checking the object immediately after the JSArray + // (if there is one) to see if it's an AllocationSiteInfo. + if (FLAG_track_allocation_sites && object->GetHeap()->InNewSpace(object)) { + Address ptr_end = (reinterpret_cast<Address>(object) - kHeapObjectTag) + + object->Size(); + if ((ptr_end + AllocationSiteInfo::kSize) <= + object->GetHeap()->NewSpaceTop()) { + // There is room in newspace for allocation info. Do we have some? + Map** possible_allocation_site_info_map = + reinterpret_cast<Map**>(ptr_end); + if (*possible_allocation_site_info_map == + object->GetHeap()->allocation_site_info_map()) { + AllocationSiteInfo* info = AllocationSiteInfo::cast( + reinterpret_cast<Object*>(ptr_end + 1)); + return info; + } + } + } + return NULL; +} + + +bool AllocationSiteInfo::GetElementsKindPayload(ElementsKind* kind) { + ASSERT(kind != NULL); + if (payload()->IsJSGlobalPropertyCell()) { + JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(payload()); + Object* cell_contents = cell->value(); + if (cell_contents->IsSmi()) { + *kind = static_cast<ElementsKind>( + Smi::cast(cell_contents)->value()); + return true; + } + } + return false; +} + + +// Heuristic: We only need to create allocation site info if the boilerplate +// elements kind is the initial elements kind. +AllocationSiteMode AllocationSiteInfo::GetMode( + ElementsKind boilerplate_elements_kind) { + if (FLAG_track_allocation_sites && + IsFastSmiElementsKind(boilerplate_elements_kind)) { + return TRACK_ALLOCATION_SITE; + } + + return DONT_TRACK_ALLOCATION_SITE; +} + + +AllocationSiteMode AllocationSiteInfo::GetMode(ElementsKind from, + ElementsKind to) { + if (FLAG_track_allocation_sites && + IsFastSmiElementsKind(from) && + (IsFastObjectElementsKind(to) || IsFastDoubleElementsKind(to))) { + return TRACK_ALLOCATION_SITE; + } + + return DONT_TRACK_ALLOCATION_SITE; +} + + uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { // For array indexes mix the length into the hash as an array index could // be zero. @@ -7305,57 +7739,64 @@ uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) { } -void StringHasher::AddSurrogatePair(uc32 c) { - uint16_t lead = unibrow::Utf16::LeadSurrogate(c); - AddCharacter(lead); - uint16_t trail = unibrow::Utf16::TrailSurrogate(c); - AddCharacter(trail); -} - - -void StringHasher::AddSurrogatePairNoIndex(uc32 c) { - uint16_t lead = unibrow::Utf16::LeadSurrogate(c); - AddCharacterNoIndex(lead); - uint16_t trail = unibrow::Utf16::TrailSurrogate(c); - AddCharacterNoIndex(trail); -} - - uint32_t StringHasher::GetHashField() { if (length_ <= String::kMaxHashCalcLength) { - if (is_array_index()) { - return MakeArrayIndexHash(array_index(), length_); + if (is_array_index_) { + return MakeArrayIndexHash(array_index_, length_); } - return (GetHash() << String::kHashShift) | String::kIsNotArrayIndexMask; + return (GetHashCore(raw_running_hash_) << String::kHashShift) | + String::kIsNotArrayIndexMask; } else { return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask; } } -uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer, - int length, - uint32_t seed) { - StringHasher hasher(length, seed); - - // Very long strings have a trivial hash that doesn't inspect the - // string contents. - if (hasher.has_trivial_hash()) { - return hasher.GetHashField(); - } - - // Do the iterative array index computation as long as there is a - // chance this is an array index. - while (buffer->has_more() && hasher.is_array_index()) { - hasher.AddCharacter(buffer->GetNext()); - } - - // Process the remaining characters without updating the array - // index. - while (buffer->has_more()) { - hasher.AddCharacterNoIndex(buffer->GetNext()); +uint32_t StringHasher::ComputeUtf8Hash(Vector<const char> chars, + uint32_t seed, + int* utf16_length_out) { + int vector_length = chars.length(); + // Handle some edge cases + if (vector_length <= 1) { + ASSERT(vector_length == 0 || + static_cast<uint8_t>(chars.start()[0]) <= + unibrow::Utf8::kMaxOneByteChar); + *utf16_length_out = vector_length; + return HashSequentialString(chars.start(), vector_length, seed); + } + // Start with a fake length which won't affect computation. + // It will be updated later. + StringHasher hasher(String::kMaxArrayIndexSize, seed); + unsigned remaining = static_cast<unsigned>(vector_length); + const uint8_t* stream = reinterpret_cast<const uint8_t*>(chars.start()); + int utf16_length = 0; + bool is_index = true; + ASSERT(hasher.is_array_index_); + while (remaining > 0) { + unsigned consumed = 0; + uint32_t c = unibrow::Utf8::ValueOf(stream, remaining, &consumed); + ASSERT(consumed > 0 && consumed <= remaining); + stream += consumed; + remaining -= consumed; + bool is_two_characters = c > unibrow::Utf16::kMaxNonSurrogateCharCode; + utf16_length += is_two_characters ? 2 : 1; + // No need to keep hashing. But we do need to calculate utf16_length. + if (utf16_length > String::kMaxHashCalcLength) continue; + if (is_two_characters) { + uint16_t c1 = unibrow::Utf16::LeadSurrogate(c); + uint16_t c2 = unibrow::Utf16::TrailSurrogate(c); + hasher.AddCharacter(c1); + hasher.AddCharacter(c2); + if (is_index) is_index = hasher.UpdateIndex(c1); + if (is_index) is_index = hasher.UpdateIndex(c2); + } else { + hasher.AddCharacter(c); + if (is_index) is_index = hasher.UpdateIndex(c); + } } - + *utf16_length_out = static_cast<int>(utf16_length); + // Must set length here so that hash computation is correct. + hasher.length_ = utf16_length; return hasher.GetHashField(); } @@ -7399,11 +7840,12 @@ static void TrimDescriptorArray(Heap* heap, Map* map, DescriptorArray* descriptors, int number_of_own_descriptors) { - int number_of_descriptors = descriptors->number_of_descriptors(); + int number_of_descriptors = descriptors->number_of_descriptors_storage(); int to_trim = number_of_descriptors - number_of_own_descriptors; - if (to_trim <= 0) return; + if (to_trim == 0) return; - RightTrimFixedArray<FROM_GC>(heap, descriptors, to_trim); + RightTrimFixedArray<FROM_GC>( + heap, descriptors, to_trim * DescriptorArray::kDescriptorSize); descriptors->SetNumberOfDescriptors(number_of_own_descriptors); if (descriptors->HasEnumCache()) TrimEnumCache(heap, map, descriptors); @@ -7443,11 +7885,10 @@ void Map::ClearNonLiveTransitions(Heap* heap) { if (ClearBackPointer(heap, target)) { if (target->instance_descriptors() == descriptors) { descriptors_owner_died = true; - descriptors_owner_died = true; } } else { if (i != transition_index) { - String* key = t->GetKey(i); + Name* key = t->GetKey(i); t->SetKey(transition_index, key); Object** key_slot = t->GetKeySlot(transition_index); collector->RecordSlot(key_slot, key_slot, key); @@ -7519,6 +7960,7 @@ bool Map::EquivalentToForNormalization(Map* other, instance_type() == other->instance_type() && bit_field() == other->bit_field() && bit_field2() == other->bit_field2() && + is_observed() == other->is_observed() && function_with_prototype() == other->function_with_prototype(); } @@ -7536,23 +7978,52 @@ void JSFunction::MarkForLazyRecompilation() { ASSERT(is_compiled() && !IsOptimized()); ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); - Builtins* builtins = GetIsolate()->builtins(); - ReplaceCode(builtins->builtin(Builtins::kLazyRecompile)); + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kLazyRecompile)); + // No write barrier required, since the builtin is part of the root set. } + void JSFunction::MarkForParallelRecompilation() { ASSERT(is_compiled() && !IsOptimized()); ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); - Builtins* builtins = GetIsolate()->builtins(); - ReplaceCode(builtins->builtin(Builtins::kParallelRecompile)); + ASSERT(FLAG_parallel_recompilation); + if (FLAG_trace_parallel_recompilation) { + PrintF(" ** Marking "); + PrintName(); + PrintF(" for parallel recompilation.\n"); + } + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kParallelRecompile)); + // No write barrier required, since the builtin is part of the root set. +} + - // Unlike MarkForLazyRecompilation, after queuing a function for - // recompilation on the compiler thread, we actually tail-call into - // the full code. We reset the profiler ticks here so that the - // function doesn't bother the runtime profiler too much. - shared()->code()->set_profiler_ticks(0); +void JSFunction::MarkForInstallingRecompiledCode() { + ASSERT(is_compiled() && !IsOptimized()); + ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); + ASSERT(FLAG_parallel_recompilation); + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kInstallRecompiledCode)); + // No write barrier required, since the builtin is part of the root set. } + +void JSFunction::MarkInRecompileQueue() { + ASSERT(is_compiled() && !IsOptimized()); + ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); + ASSERT(FLAG_parallel_recompilation); + if (FLAG_trace_parallel_recompilation) { + PrintF(" ** Queueing "); + PrintName(); + PrintF(" for parallel recompilation.\n"); + } + set_code_no_write_barrier( + GetIsolate()->builtins()->builtin(Builtins::kInRecompileQueue)); + // No write barrier required, since the builtin is part of the root set. +} + + static bool CompileLazyHelper(CompilationInfo* info, ClearExceptionFlag flag) { // Compile the source information to a code object. @@ -7575,11 +8046,6 @@ bool SharedFunctionInfo::CompileLazy(Handle<SharedFunctionInfo> shared, } -void SharedFunctionInfo::ClearOptimizedCodeMap() { - set_optimized_code_map(Smi::FromInt(0)); -} - - void SharedFunctionInfo::AddToOptimizedCodeMap( Handle<SharedFunctionInfo> shared, Handle<Context> native_context, @@ -7843,12 +8309,13 @@ Context* JSFunction::NativeContextFromLiterals(FixedArray* literals) { MaybeObject* Oddball::Initialize(const char* to_string, Object* to_number, byte kind) { - String* symbol; - { MaybeObject* maybe_symbol = - Isolate::Current()->heap()->LookupAsciiSymbol(to_string); - if (!maybe_symbol->To(&symbol)) return maybe_symbol; + String* internalized_to_string; + { MaybeObject* maybe_string = + Isolate::Current()->heap()->InternalizeUtf8String( + CStrVector(to_string)); + if (!maybe_string->To(&internalized_to_string)) return maybe_string; } - set_to_string(symbol); + set_to_string(internalized_to_string); set_to_number(to_number); set_kind(kind); return this; @@ -7904,13 +8371,14 @@ bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) { return false; } - Heap* heap = GetHeap(); + Isolate* isolate = GetIsolate(); + Heap* heap = isolate->heap(); // Traverse the proposed prototype chain looking for properties of the // same names as are set by the inline constructor. for (Object* obj = prototype; obj != heap->null_value(); - obj = obj->GetPrototype()) { + obj = obj->GetPrototype(isolate)) { JSReceiver* receiver = JSReceiver::cast(obj); for (int i = 0; i < this_property_assignments_count(); i++) { LookupResult result(heap->isolate()); @@ -8069,7 +8537,7 @@ void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) { // old code, we have to replace it. We should try to avoid this // altogether because it flushes valuable type feedback by // effectively resetting all IC state. - set_code(recompiled); + ReplaceCode(recompiled); } ASSERT(has_deoptimization_support()); } @@ -8149,7 +8617,7 @@ void SharedFunctionInfo::DetachInitialMap() { // constructor is called. The countdown will continue and (possibly after // several more GCs) CompleteInobjectSlackTracking will eventually be called. Heap* heap = map->GetHeap(); - set_initial_map(heap->raw_unchecked_undefined_value()); + set_initial_map(heap->undefined_value()); Builtins* builtins = heap->isolate()->builtins(); ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown), *RawField(this, kConstructStubOffset)); @@ -8277,6 +8745,15 @@ void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) { } +void ObjectVisitor::VisitCodeAgeSequence(RelocInfo* rinfo) { + ASSERT(RelocInfo::IsCodeAgeSequence(rinfo->rmode())); + Object* stub = rinfo->code_age_stub(); + if (stub) { + VisitPointer(&stub); + } +} + + void ObjectVisitor::VisitCodeEntry(Address entry_address) { Object* code = Code::GetObjectFromEntryAddress(entry_address); Object* old_code = code; @@ -8336,9 +8813,16 @@ void Code::CopyFrom(const CodeDesc& desc) { ASSERT(Marking::Color(this) == Marking::WHITE_OBJECT); // copy code + CHECK(IsCode()); + CHECK(relocation_info()->IsByteArray()); + CHECK(reinterpret_cast<intptr_t>(instruction_start()) == + reinterpret_cast<intptr_t>(this) + Code::kHeaderSize - kHeapObjectTag); memmove(instruction_start(), desc.buffer, desc.instr_size); // copy reloc info + // TODO(mstarzinger): Remove once we found the bug. + CHECK(IsCode()); + CHECK(relocation_info()->IsByteArray()); memmove(relocation_start(), desc.buffer + desc.buffer_size - desc.reloc_size, desc.reloc_size); @@ -8348,8 +8832,10 @@ void Code::CopyFrom(const CodeDesc& desc) { int mode_mask = RelocInfo::kCodeTargetMask | RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | RelocInfo::ModeMask(RelocInfo::GLOBAL_PROPERTY_CELL) | + RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) | RelocInfo::kApplyMask; - Assembler* origin = desc.origin; // Needed to find target_object on X64. + // Needed to find target_object and runtime_entry on X64 + Assembler* origin = desc.origin; for (RelocIterator it(this, mode_mask); !it.done(); it.next()) { RelocInfo::Mode mode = it.rinfo()->rmode(); if (mode == RelocInfo::EMBEDDED_OBJECT) { @@ -8365,6 +8851,9 @@ void Code::CopyFrom(const CodeDesc& desc) { Code* code = Code::cast(*p); it.rinfo()->set_target_address(code->instruction_start(), SKIP_WRITE_BARRIER); + } else if (RelocInfo::IsRuntimeEntry(mode)) { + Address p = it.rinfo()->target_runtime_entry(origin); + it.rinfo()->set_target_runtime_entry(p, SKIP_WRITE_BARRIER); } else { it.rinfo()->apply(delta); } @@ -8455,6 +8944,46 @@ Map* Code::FindFirstMap() { } +void Code::FindAllMaps(MapHandleList* maps) { + ASSERT(is_inline_cache_stub()); + AssertNoAllocation no_allocation; + int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); + for (RelocIterator it(this, mask); !it.done(); it.next()) { + RelocInfo* info = it.rinfo(); + Object* object = info->target_object(); + if (object->IsMap()) maps->Add(Handle<Map>(Map::cast(object))); + } +} + + +Code* Code::FindFirstCode() { + ASSERT(is_inline_cache_stub()); + AssertNoAllocation no_allocation; + int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); + for (RelocIterator it(this, mask); !it.done(); it.next()) { + RelocInfo* info = it.rinfo(); + return Code::GetCodeFromTargetAddress(info->target_address()); + } + return NULL; +} + + +void Code::FindAllCode(CodeHandleList* code_list, int length) { + ASSERT(is_inline_cache_stub()); + AssertNoAllocation no_allocation; + int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); + int i = 0; + for (RelocIterator it(this, mask); !it.done(); it.next()) { + if (i++ == length) return; + RelocInfo* info = it.rinfo(); + Code* code = Code::GetCodeFromTargetAddress(info->target_address()); + ASSERT(code->kind() == Code::STUB); + code_list->Add(Handle<Code>(code)); + } + UNREACHABLE(); +} + + void Code::ClearInlineCaches() { int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) | @@ -8471,6 +9000,7 @@ void Code::ClearInlineCaches() { void Code::ClearTypeFeedbackCells(Heap* heap) { + if (kind() != FUNCTION) return; Object* raw_info = type_feedback_info(); if (raw_info->IsTypeFeedbackInfo()) { TypeFeedbackCells* type_feedback_cells = @@ -8485,7 +9015,135 @@ void Code::ClearTypeFeedbackCells(Heap* heap) { bool Code::allowed_in_shared_map_code_cache() { return is_keyed_load_stub() || is_keyed_store_stub() || - (is_compare_ic_stub() && compare_state() == CompareIC::KNOWN_OBJECTS); + (is_compare_ic_stub() && + ICCompareStub::CompareState(stub_info()) == CompareIC::KNOWN_OBJECT); +} + + +void Code::MakeCodeAgeSequenceYoung(byte* sequence) { + PatchPlatformCodeAge(sequence, kNoAge, NO_MARKING_PARITY); +} + + +void Code::MakeOlder(MarkingParity current_parity) { + byte* sequence = FindCodeAgeSequence(); + if (sequence != NULL) { + Age age; + MarkingParity code_parity; + GetCodeAgeAndParity(sequence, &age, &code_parity); + if (age != kLastCodeAge && code_parity != current_parity) { + PatchPlatformCodeAge(sequence, static_cast<Age>(age + 1), + current_parity); + } + } +} + + +bool Code::IsOld() { + byte* sequence = FindCodeAgeSequence(); + if (sequence == NULL) return false; + Age age; + MarkingParity parity; + GetCodeAgeAndParity(sequence, &age, &parity); + return age >= kSexagenarianCodeAge; +} + + +byte* Code::FindCodeAgeSequence() { + return FLAG_age_code && + prologue_offset() != kPrologueOffsetNotSet && + (kind() == OPTIMIZED_FUNCTION || + (kind() == FUNCTION && !has_debug_break_slots())) + ? instruction_start() + prologue_offset() + : NULL; +} + + +void Code::GetCodeAgeAndParity(Code* code, Age* age, + MarkingParity* parity) { + Isolate* isolate = Isolate::Current(); + Builtins* builtins = isolate->builtins(); + Code* stub = NULL; +#define HANDLE_CODE_AGE(AGE) \ + stub = *builtins->Make##AGE##CodeYoungAgainEvenMarking(); \ + if (code == stub) { \ + *age = k##AGE##CodeAge; \ + *parity = EVEN_MARKING_PARITY; \ + return; \ + } \ + stub = *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ + if (code == stub) { \ + *age = k##AGE##CodeAge; \ + *parity = ODD_MARKING_PARITY; \ + return; \ + } + CODE_AGE_LIST(HANDLE_CODE_AGE) +#undef HANDLE_CODE_AGE + UNREACHABLE(); +} + + +Code* Code::GetCodeAgeStub(Age age, MarkingParity parity) { + Isolate* isolate = Isolate::Current(); + Builtins* builtins = isolate->builtins(); + switch (age) { +#define HANDLE_CODE_AGE(AGE) \ + case k##AGE##CodeAge: { \ + Code* stub = parity == EVEN_MARKING_PARITY \ + ? *builtins->Make##AGE##CodeYoungAgainEvenMarking() \ + : *builtins->Make##AGE##CodeYoungAgainOddMarking(); \ + return stub; \ + } + CODE_AGE_LIST(HANDLE_CODE_AGE) +#undef HANDLE_CODE_AGE + default: + UNREACHABLE(); + break; + } + return NULL; +} + + +void Code::PrintDeoptLocation(int bailout_id) { + const char* last_comment = NULL; + int mask = RelocInfo::ModeMask(RelocInfo::COMMENT) + | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); + for (RelocIterator it(this, mask); !it.done(); it.next()) { + RelocInfo* info = it.rinfo(); + if (info->rmode() == RelocInfo::COMMENT) { + last_comment = reinterpret_cast<const char*>(info->data()); + } else if (last_comment != NULL && + bailout_id == Deoptimizer::GetDeoptimizationId( + GetIsolate(), info->target_address(), Deoptimizer::EAGER)) { + CHECK(RelocInfo::IsRuntimeEntry(info->rmode())); + PrintF(" %s\n", last_comment); + return; + } + } +} + + +// Identify kind of code. +const char* Code::Kind2String(Kind kind) { + switch (kind) { + case FUNCTION: return "FUNCTION"; + case OPTIMIZED_FUNCTION: return "OPTIMIZED_FUNCTION"; + case COMPILED_STUB: return "COMPILED_STUB"; + case STUB: return "STUB"; + case BUILTIN: return "BUILTIN"; + case LOAD_IC: return "LOAD_IC"; + case KEYED_LOAD_IC: return "KEYED_LOAD_IC"; + case STORE_IC: return "STORE_IC"; + case KEYED_STORE_IC: return "KEYED_STORE_IC"; + case CALL_IC: return "CALL_IC"; + case KEYED_CALL_IC: return "KEYED_CALL_IC"; + 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; } @@ -8548,6 +9206,12 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { break; } + case Translation::COMPILED_STUB_FRAME: { + Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next()); + PrintF(out, "{kind=%d}", stub_kind); + break; + } + case Translation::ARGUMENTS_ADAPTOR_FRAME: case Translation::CONSTRUCT_STUB_FRAME: { int function_id = iterator.Next(); @@ -8588,8 +9252,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { case Translation::UINT32_REGISTER: { int reg_code = iterator.Next(); - PrintF(out, - "{input=%s (unsigned)}", + PrintF(out, "{input=%s (unsigned)}", converter.NameOfCPURegister(reg_code)); break; } @@ -8631,8 +9294,14 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { break; } - case Translation::ARGUMENTS_OBJECT: + case Translation::ARGUMENTS_OBJECT: { + bool args_known = iterator.Next(); + int args_index = iterator.Next(); + int args_length = iterator.Next(); + PrintF(out, "{index=%d, length=%d, known=%d}", + args_index, args_length, args_known); break; + } } PrintF(out, "\n"); } @@ -8657,38 +9326,16 @@ void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) { } -// Identify kind of code. -const char* Code::Kind2String(Kind kind) { - switch (kind) { - case FUNCTION: return "FUNCTION"; - case OPTIMIZED_FUNCTION: return "OPTIMIZED_FUNCTION"; - case STUB: return "STUB"; - case BUILTIN: return "BUILTIN"; - case LOAD_IC: return "LOAD_IC"; - case KEYED_LOAD_IC: return "KEYED_LOAD_IC"; - case STORE_IC: return "STORE_IC"; - case KEYED_STORE_IC: return "KEYED_STORE_IC"; - case CALL_IC: return "CALL_IC"; - case KEYED_CALL_IC: return "KEYED_CALL_IC"; - 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; -} - - const char* Code::ICState2String(InlineCacheState state) { switch (state) { case UNINITIALIZED: return "UNINITIALIZED"; case PREMONOMORPHIC: return "PREMONOMORPHIC"; case MONOMORPHIC: return "MONOMORPHIC"; case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE"; + case POLYMORPHIC: return "POLYMORPHIC"; case MEGAMORPHIC: return "MEGAMORPHIC"; - case DEBUG_BREAK: return "DEBUG_BREAK"; - case DEBUG_PREPARE_STEP_IN: return "DEBUG_PREPARE_STEP_IN"; + case GENERIC: return "GENERIC"; + case DEBUG_STUB: return "DEBUG_STUB"; } UNREACHABLE(); return NULL; @@ -8747,11 +9394,15 @@ void Code::Disassemble(const char* name, FILE* out) { PrintF(out, "argc = %d\n", arguments_count()); } if (is_compare_ic_stub()) { - CompareIC::State state = CompareIC::ComputeState(this); - PrintF(out, "compare_state = %s\n", CompareIC::GetStateName(state)); - } - if (is_compare_ic_stub() && major_key() == CodeStub::CompareIC) { - Token::Value op = CompareIC::ComputeOperation(this); + ASSERT(major_key() == CodeStub::CompareIC); + CompareIC::State left_state, right_state, handler_state; + Token::Value op; + ICCompareStub::DecodeMinorKey(stub_info(), &left_state, &right_state, + &handler_state, &op); + PrintF(out, "compare_state = %s*%s -> %s\n", + CompareIC::GetStateName(left_state), + CompareIC::GetStateName(right_state), + CompareIC::GetStateName(handler_state)); PrintF(out, "compare_operation = %s\n", Token::Name(op)); } } @@ -8777,7 +9428,7 @@ void Code::Disassemble(const char* name, FILE* out) { } PrintF("\n"); - if (kind() == OPTIMIZED_FUNCTION) { + if (kind() == OPTIMIZED_FUNCTION || kind() == COMPILED_STUB) { SafepointTable table(this); PrintF(out, "Safepoints (size = %u)\n", table.size()); for (unsigned i = 0; i < table.length(); i++) { @@ -8797,8 +9448,6 @@ void Code::Disassemble(const char* name, FILE* out) { PrintF(out, "\n"); } PrintF(out, "\n"); - // Just print if type feedback info is ever used for optimized code. - ASSERT(type_feedback_info()->IsUndefined()); } else if (kind() == FUNCTION) { unsigned offset = stack_check_table_offset(); // If there is no stack check table, the "table start" will at or after @@ -8824,7 +9473,9 @@ void Code::Disassemble(const char* name, FILE* out) { } PrintF("RelocInfo (size = %d)\n", relocation_size()); - for (RelocIterator it(this); !it.done(); it.next()) it.rinfo()->Print(out); + for (RelocIterator it(this); !it.done(); it.next()) { + it.rinfo()->Print(GetIsolate(), out); + } PrintF(out, "\n"); } #endif // ENABLE_DISASSEMBLER @@ -8837,12 +9488,12 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( Heap* heap = GetHeap(); // We should never end in here with a pixel or external array. ASSERT(!HasExternalArrayElements()); + ASSERT(!map()->is_observed()); // Allocate a new fast elements backing store. FixedArray* new_elements; - { MaybeObject* maybe = heap->AllocateFixedArrayWithHoles(capacity); - if (!maybe->To(&new_elements)) return maybe; - } + MaybeObject* maybe = heap->AllocateUninitializedFixedArray(capacity); + if (!maybe->To(&new_elements)) return maybe; ElementsKind elements_kind = GetElementsKind(); ElementsKind new_elements_kind; @@ -8865,11 +9516,11 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength( } } FixedArrayBase* old_elements = elements(); - ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); - { MaybeObject* maybe_obj = - accessor->CopyElements(this, new_elements, new_elements_kind); - if (maybe_obj->IsFailure()) return maybe_obj; - } + ElementsAccessor* accessor = ElementsAccessor::ForKind(new_elements_kind); + MaybeObject* maybe_obj = + accessor->CopyElements(this, new_elements, elements_kind); + if (maybe_obj->IsFailure()) return maybe_obj; + if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { Map* new_map = map(); if (new_elements_kind != elements_kind) { @@ -8902,6 +9553,7 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( Heap* heap = GetHeap(); // We should never end in here with a pixel or external array. ASSERT(!HasExternalArrayElements()); + ASSERT(!map()->is_observed()); FixedArrayBase* elems; { MaybeObject* maybe_obj = @@ -8924,9 +9576,9 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( } FixedArrayBase* old_elements = elements(); - ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind); + ElementsAccessor* accessor = ElementsAccessor::ForKind(FAST_DOUBLE_ELEMENTS); { MaybeObject* maybe_obj = - accessor->CopyElements(this, elems, FAST_DOUBLE_ELEMENTS); + accessor->CopyElements(this, elems, elements_kind); if (maybe_obj->IsFailure()) return maybe_obj; } if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) { @@ -8950,19 +9602,10 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength( } -MaybeObject* JSArray::Initialize(int capacity) { - Heap* heap = GetHeap(); +MaybeObject* JSArray::Initialize(int capacity, int length) { ASSERT(capacity >= 0); - set_length(Smi::FromInt(0)); - FixedArray* new_elements; - if (capacity == 0) { - new_elements = heap->empty_fixed_array(); - } else { - MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(capacity); - if (!maybe_obj->To(&new_elements)) return maybe_obj; - } - set_elements(new_elements); - return this; + return GetHeap()->AllocateJSArrayStorage(this, length, capacity, + INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE); } @@ -8972,10 +9615,84 @@ void JSArray::Expand(int required_size) { } +// Returns false if the passed-in index is marked non-configurable, +// which will cause the ES5 truncation operation to halt, and thus +// no further old values need be collected. +static bool GetOldValue(Isolate* isolate, + Handle<JSObject> object, + uint32_t index, + List<Handle<Object> >* old_values, + List<Handle<String> >* indices) { + PropertyAttributes attributes = object->GetLocalElementAttribute(index); + ASSERT(attributes != ABSENT); + if (attributes == DONT_DELETE) return false; + old_values->Add(object->GetLocalElementAccessorPair(index) == NULL + ? Object::GetElement(object, index) + : Handle<Object>::cast(isolate->factory()->the_hole_value())); + indices->Add(isolate->factory()->Uint32ToString(index)); + return true; +} + + MaybeObject* JSArray::SetElementsLength(Object* len) { // We should never end in here with a pixel or external array. ASSERT(AllowsSetElementsLength()); - return GetElementsAccessor()->SetLength(this, len); + if (!(FLAG_harmony_observation && map()->is_observed())) + return GetElementsAccessor()->SetLength(this, len); + + Isolate* isolate = GetIsolate(); + HandleScope scope(isolate); + Handle<JSArray> self(this); + List<Handle<String> > indices; + List<Handle<Object> > old_values; + Handle<Object> old_length_handle(self->length(), isolate); + Handle<Object> new_length_handle(len, isolate); + uint32_t old_length = 0; + CHECK(old_length_handle->ToArrayIndex(&old_length)); + uint32_t new_length = 0; + if (!new_length_handle->ToArrayIndex(&new_length)) + return Failure::InternalError(); + + // Observed arrays should always be in dictionary mode; + // if they were in fast mode, the below is slower than necessary + // as it iterates over the array backing store multiple times. + ASSERT(self->HasDictionaryElements()); + static const PropertyAttributes kNoAttrFilter = NONE; + int num_elements = self->NumberOfLocalElements(kNoAttrFilter); + if (num_elements > 0) { + if (old_length == static_cast<uint32_t>(num_elements)) { + // Simple case for arrays without holes. + for (uint32_t i = old_length - 1; i + 1 > new_length; --i) { + if (!GetOldValue(isolate, self, i, &old_values, &indices)) break; + } + } else { + // For sparse arrays, only iterate over existing elements. + Handle<FixedArray> keys = isolate->factory()->NewFixedArray(num_elements); + self->GetLocalElementKeys(*keys, kNoAttrFilter); + while (num_elements-- > 0) { + uint32_t index = NumberToUint32(keys->get(num_elements)); + if (index < new_length) break; + if (!GetOldValue(isolate, self, index, &old_values, &indices)) break; + } + } + } + + MaybeObject* result = + self->GetElementsAccessor()->SetLength(*self, *new_length_handle); + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + CHECK(self->length()->ToArrayIndex(&new_length)); + if (old_length != new_length) { + for (int i = 0; i < indices.length(); ++i) { + JSObject::EnqueueChangeRecord( + self, "deleted", indices[i], old_values[i]); + } + JSObject::EnqueueChangeRecord( + self, "updated", isolate->factory()->length_string(), + old_length_handle); + } + return *hresult; } @@ -9055,13 +9772,117 @@ void Map::ZapPrototypeTransitions() { } +DependentCode::GroupStartIndexes::GroupStartIndexes(DependentCode* entries) { + Recompute(entries); +} + + +void DependentCode::GroupStartIndexes::Recompute(DependentCode* entries) { + start_indexes_[0] = 0; + for (int g = 1; g <= kGroupCount; g++) { + int count = entries->number_of_entries(static_cast<DependencyGroup>(g - 1)); + start_indexes_[g] = start_indexes_[g - 1] + count; + } +} + + +Handle<DependentCode> DependentCode::Insert(Handle<DependentCode> entries, + DependencyGroup group, + Handle<Code> value) { + GroupStartIndexes starts(*entries); + int start = starts.at(group); + int end = starts.at(group + 1); + int number_of_entries = starts.number_of_entries(); + if (start < end && entries->code_at(end - 1) == *value) { + // Do not append the code if it is already in the array. + // It is sufficient to just check only the last element because + // we process embedded maps of an optimized code in one batch. + return entries; + } + if (entries->length() < kCodesStartIndex + number_of_entries + 1) { + Factory* factory = entries->GetIsolate()->factory(); + int capacity = kCodesStartIndex + number_of_entries + 1; + if (capacity > 5) capacity = capacity * 5 / 4; + Handle<DependentCode> new_entries = Handle<DependentCode>::cast( + factory->CopySizeFixedArray(entries, capacity)); + // The number of codes can change after GC. + starts.Recompute(*entries); + start = starts.at(group); + end = starts.at(group + 1); + number_of_entries = starts.number_of_entries(); + for (int i = 0; i < number_of_entries; i++) { + entries->clear_code_at(i); + } + // If the old fixed array was empty, we need to reset counters of the + // new array. + if (number_of_entries == 0) { + for (int g = 0; g < kGroupCount; g++) { + new_entries->set_number_of_entries(static_cast<DependencyGroup>(g), 0); + } + } + entries = new_entries; + } + entries->ExtendGroup(group); + entries->set_code_at(end, *value); + entries->set_number_of_entries(group, end + 1 - start); + return entries; +} + + +bool DependentCode::Contains(DependencyGroup group, Code* code) { + GroupStartIndexes starts(this); + int number_of_entries = starts.at(kGroupCount); + for (int i = 0; i < number_of_entries; i++) { + if (code_at(i) == code) return true; + } + return false; +} + + +class DeoptimizeDependentCodeFilter : public OptimizedFunctionFilter { + public: + virtual bool TakeFunction(JSFunction* function) { + return function->code()->marked_for_deoptimization(); + } +}; + + +void DependentCode::DeoptimizeDependentCodeGroup( + Isolate* isolate, + DependentCode::DependencyGroup group) { + AssertNoAllocation no_allocation_scope; + DependentCode::GroupStartIndexes starts(this); + int start = starts.at(group); + int end = starts.at(group + 1); + int number_of_entries = starts.at(DependentCode::kGroupCount); + if (start == end) return; + for (int i = start; i < end; i++) { + Code* code = code_at(i); + code->set_marked_for_deoptimization(true); + } + // Compact the array by moving all subsequent groups to fill in the new holes. + for (int src = end, dst = start; src < number_of_entries; src++, dst++) { + set_code_at(dst, code_at(src)); + } + // Now the holes are at the end of the array, zap them for heap-verifier. + int removed = end - start; + for (int i = number_of_entries - removed; i < number_of_entries; i++) { + clear_code_at(i); + } + set_number_of_entries(group, 0); + DeoptimizeDependentCodeFilter filter; + Deoptimizer::DeoptimizeAllFunctionsWith(isolate, &filter); +} + + MaybeObject* JSReceiver::SetPrototype(Object* value, bool skip_hidden_prototypes) { #ifdef DEBUG int size = Size(); #endif - Heap* heap = GetHeap(); + Isolate* isolate = GetIsolate(); + Heap* heap = isolate->heap(); // Silently ignore the change if value is not a JSObject or null. // SpiderMonkey behaves this way. if (!value->IsJSReceiver() && !value->IsNull()) return value; @@ -9075,22 +9896,24 @@ MaybeObject* JSReceiver::SetPrototype(Object* value, // or [[Extensible]] must not violate the invariants defined in the preceding // paragraph. if (!this->map()->is_extensible()) { - HandleScope scope(heap->isolate()); - Handle<Object> handle(this, heap->isolate()); - return heap->isolate()->Throw( - *FACTORY->NewTypeError("non_extensible_proto", - HandleVector<Object>(&handle, 1))); + HandleScope scope(isolate); + Handle<Object> handle(this, isolate); + return isolate->Throw( + *isolate->factory()->NewTypeError("non_extensible_proto", + HandleVector<Object>(&handle, 1))); } // Before we can set the prototype we need to be sure // prototype cycles are prevented. // 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()) { + for (Object* pt = value; + pt != heap->null_value(); + pt = pt->GetPrototype(isolate)) { if (JSReceiver::cast(pt) == this) { // Cycle detected. - HandleScope scope(heap->isolate()); - return heap->isolate()->Throw( + HandleScope scope(isolate); + return isolate->Throw( *FACTORY->NewError("cyclic_proto", HandleVector<Object>(NULL, 0))); } } @@ -9104,7 +9927,7 @@ MaybeObject* JSReceiver::SetPrototype(Object* value, while (current_proto->IsJSObject() && JSReceiver::cast(current_proto)->map()->is_hidden_prototype()) { real_receiver = JSReceiver::cast(current_proto); - current_proto = current_proto->GetPrototype(); + current_proto = current_proto->GetPrototype(isolate); } } @@ -9152,203 +9975,51 @@ MaybeObject* JSObject::EnsureCanContainElements(Arguments* args, } -bool JSObject::HasElementWithInterceptor(JSReceiver* receiver, uint32_t index) { - Isolate* isolate = GetIsolate(); - // Make sure that the top context does not change when doing - // callbacks or interceptor calls. - AssertNoContextChange ncc; - HandleScope scope(isolate); - Handle<InterceptorInfo> interceptor(GetIndexedInterceptor()); - Handle<JSReceiver> receiver_handle(receiver); - Handle<JSObject> holder_handle(this); - CustomArguments args(isolate, interceptor->data(), receiver, this); - v8::AccessorInfo info(args.end()); - if (!interceptor->query()->IsUndefined()) { - v8::IndexedPropertyQuery query = - v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query()); - LOG(isolate, - ApiIndexedPropertyAccess("interceptor-indexed-has", this, index)); - v8::Handle<v8::Integer> result; - { - // Leaving JavaScript. - VMState state(isolate, EXTERNAL); - result = query(index, info); - } - if (!result.IsEmpty()) { - ASSERT(result->IsInt32()); - return true; // absence of property is signaled by empty handle. - } - } else if (!interceptor->getter()->IsUndefined()) { - v8::IndexedPropertyGetter getter = - v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter()); - LOG(isolate, - ApiIndexedPropertyAccess("interceptor-indexed-has-get", this, index)); - v8::Handle<v8::Value> result; - { - // Leaving JavaScript. - VMState state(isolate, EXTERNAL); - result = getter(index, info); - } - if (!result.IsEmpty()) return true; - } - - if (holder_handle->GetElementsAccessor()->HasElement( - *receiver_handle, *holder_handle, index)) { - return true; - } - - if (holder_handle->IsStringObjectWithCharacterAt(index)) return true; - Object* pt = holder_handle->GetPrototype(); - if (pt->IsJSProxy()) { - // We need to follow the spec and simulate a call to [[GetOwnProperty]]. - return JSProxy::cast(pt)->GetElementAttributeWithHandler( - receiver, index) != ABSENT; +PropertyType JSObject::GetLocalPropertyType(Name* name) { + uint32_t index = 0; + if (name->AsArrayIndex(&index)) { + return GetLocalElementType(index); } - if (pt->IsNull()) return false; - return JSObject::cast(pt)->HasElementWithReceiver(*receiver_handle, index); + LookupResult lookup(GetIsolate()); + LocalLookup(name, &lookup, true); + return lookup.type(); } -JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) { - // Check access rights if needed. - if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); - return UNDEFINED_ELEMENT; - } - } +PropertyType JSObject::GetLocalElementType(uint32_t index) { + return GetElementsAccessor()->GetType(this, this, index); +} - if (IsJSGlobalProxy()) { - Object* proto = GetPrototype(); - if (proto->IsNull()) return UNDEFINED_ELEMENT; - ASSERT(proto->IsJSGlobalObject()); - return JSObject::cast(proto)->HasLocalElement(index); - } - // Check for lookup interceptor - if (HasIndexedInterceptor()) { - return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT - : UNDEFINED_ELEMENT; +AccessorPair* JSObject::GetLocalPropertyAccessorPair(Name* name) { + uint32_t index = 0; + if (name->AsArrayIndex(&index)) { + return GetLocalElementAccessorPair(index); } - // Handle [] on String objects. - if (this->IsStringObjectWithCharacterAt(index)) { - return STRING_CHARACTER_ELEMENT; - } + LookupResult lookup(GetIsolate()); + LocalLookupRealNamedProperty(name, &lookup); - switch (GetElementsKind()) { - case FAST_SMI_ELEMENTS: - case FAST_ELEMENTS: - case FAST_HOLEY_SMI_ELEMENTS: - case FAST_HOLEY_ELEMENTS: { - uint32_t length = IsJSArray() ? - static_cast<uint32_t> - (Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast<uint32_t>(FixedArray::cast(elements())->length()); - if ((index < length) && - !FixedArray::cast(elements())->get(index)->IsTheHole()) { - return FAST_ELEMENT; - } - break; - } - case FAST_DOUBLE_ELEMENTS: - case FAST_HOLEY_DOUBLE_ELEMENTS: { - uint32_t length = IsJSArray() ? - static_cast<uint32_t> - (Smi::cast(JSArray::cast(this)->length())->value()) : - static_cast<uint32_t>(FixedDoubleArray::cast(elements())->length()); - if ((index < length) && - !FixedDoubleArray::cast(elements())->is_the_hole(index)) { - return FAST_ELEMENT; - } - break; - } - case EXTERNAL_PIXEL_ELEMENTS: { - ExternalPixelArray* pixels = ExternalPixelArray::cast(elements()); - if (index < static_cast<uint32_t>(pixels->length())) return FAST_ELEMENT; - break; - } - case EXTERNAL_BYTE_ELEMENTS: - case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case EXTERNAL_SHORT_ELEMENTS: - case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case EXTERNAL_INT_ELEMENTS: - case EXTERNAL_UNSIGNED_INT_ELEMENTS: - case EXTERNAL_FLOAT_ELEMENTS: - case EXTERNAL_DOUBLE_ELEMENTS: { - ExternalArray* array = ExternalArray::cast(elements()); - if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT; - break; - } - case DICTIONARY_ELEMENTS: { - if (element_dictionary()->FindEntry(index) != - SeededNumberDictionary::kNotFound) { - return DICTIONARY_ELEMENT; - } - break; - } - case NON_STRICT_ARGUMENTS_ELEMENTS: { - // Aliased parameters and non-aliased elements in a fast backing store - // behave as FAST_ELEMENT. Non-aliased elements in a dictionary - // backing store behave as DICTIONARY_ELEMENT. - FixedArray* parameter_map = FixedArray::cast(elements()); - uint32_t length = parameter_map->length(); - Object* probe = - index < (length - 2) ? parameter_map->get(index + 2) : NULL; - if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT; - // If not aliased, check the arguments. - FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); - if (arguments->IsDictionary()) { - SeededNumberDictionary* dictionary = - SeededNumberDictionary::cast(arguments); - if (dictionary->FindEntry(index) != SeededNumberDictionary::kNotFound) { - return DICTIONARY_ELEMENT; - } - } else { - length = arguments->length(); - probe = (index < length) ? arguments->get(index) : NULL; - if (probe != NULL && !probe->IsTheHole()) return FAST_ELEMENT; - } - break; - } + if (lookup.IsPropertyCallbacks() && + lookup.GetCallbackObject()->IsAccessorPair()) { + return AccessorPair::cast(lookup.GetCallbackObject()); } - - return UNDEFINED_ELEMENT; + return NULL; } -bool JSObject::HasElementWithReceiver(JSReceiver* receiver, uint32_t index) { - // Check access rights if needed. - if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_HAS)) { - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS); - return false; - } - } - - // Check for lookup interceptor - if (HasIndexedInterceptor()) { - return HasElementWithInterceptor(receiver, index); - } - - ElementsAccessor* accessor = GetElementsAccessor(); - if (accessor->HasElement(receiver, this, index)) { - return true; +AccessorPair* JSObject::GetLocalElementAccessorPair(uint32_t index) { + if (IsJSGlobalProxy()) { + Object* proto = GetPrototype(); + if (proto->IsNull()) return NULL; + ASSERT(proto->IsJSGlobalObject()); + return JSObject::cast(proto)->GetLocalElementAccessorPair(index); } - // Handle [] on String objects. - if (this->IsStringObjectWithCharacterAt(index)) return true; + // Check for lookup interceptor. + if (HasIndexedInterceptor()) return NULL; - Object* pt = GetPrototype(); - if (pt->IsNull()) return false; - if (pt->IsJSProxy()) { - // We need to follow the spec and simulate a call to [[GetOwnProperty]]. - return JSProxy::cast(pt)->GetElementAttributeWithHandler( - receiver, index) != ABSENT; - } - return JSObject::cast(pt)->HasElementWithReceiver(receiver, index); + return GetElementsAccessor()->GetAccessorPair(this, this, index); } @@ -9402,8 +10073,9 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver, ASSERT(!structure->IsForeign()); // api style callbacks. - if (structure->IsAccessorInfo()) { - Handle<AccessorInfo> data(AccessorInfo::cast(structure)); + if (structure->IsExecutableAccessorInfo()) { + Handle<ExecutableAccessorInfo> data( + ExecutableAccessorInfo::cast(structure)); Object* fun_obj = data->getter(); v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj); if (call_fun == NULL) return isolate->heap()->undefined_value(); @@ -9439,6 +10111,12 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver, return isolate->heap()->undefined_value(); } + if (structure->IsDeclaredAccessorInfo()) { + return GetDeclaredAccessorProperty(receiver, + DeclaredAccessorInfo::cast(structure), + isolate); + } + UNREACHABLE(); return NULL; } @@ -9462,11 +10140,12 @@ MaybeObject* JSObject::SetElementWithCallback(Object* structure, // callbacks should be phased out. ASSERT(!structure->IsForeign()); - if (structure->IsAccessorInfo()) { + if (structure->IsExecutableAccessorInfo()) { // api style callbacks Handle<JSObject> self(this); Handle<JSObject> holder_handle(JSObject::cast(holder)); - Handle<AccessorInfo> data(AccessorInfo::cast(structure)); + Handle<ExecutableAccessorInfo> data( + ExecutableAccessorInfo::cast(structure)); Object* call_obj = data->setter(); v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj); if (call_fun == NULL) return value; @@ -9487,7 +10166,7 @@ MaybeObject* JSObject::SetElementWithCallback(Object* structure, } if (structure->IsAccessorPair()) { - Handle<Object> setter(AccessorPair::cast(structure)->setter()); + Handle<Object> setter(AccessorPair::cast(structure)->setter(), isolate); if (setter->IsSpecFunction()) { // TODO(rossberg): nicer would be to cast to some JSCallable here... return SetPropertyWithDefinedSetter(JSReceiver::cast(*setter), value); @@ -9504,6 +10183,9 @@ MaybeObject* JSObject::SetElementWithCallback(Object* structure, } } + // TODO(dcarney): Handle correctly. + if (structure->IsDeclaredAccessorInfo()) return value; + UNREACHABLE(); return NULL; } @@ -9609,6 +10291,14 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, } // Convert to fast double elements if appropriate. if (HasFastSmiElements() && !value->IsSmi() && value->IsNumber()) { + // Consider fixing the boilerplate as well if we have one. + ElementsKind to_kind = IsHoleyElementsKind(elements_kind) + ? FAST_HOLEY_DOUBLE_ELEMENTS + : FAST_DOUBLE_ELEMENTS; + + MaybeObject* maybe_failure = UpdateAllocationSiteInfo(to_kind); + if (maybe_failure->IsFailure()) return maybe_failure; + MaybeObject* maybe = SetFastDoubleElementsCapacityAndLength(new_capacity, array_length); if (maybe->IsFailure()) return maybe; @@ -9622,6 +10312,10 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, ElementsKind kind = HasFastHoleyElements() ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + + MaybeObject* maybe_failure = UpdateAllocationSiteInfo(kind); + if (maybe_failure->IsFailure()) return maybe_failure; + MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(), kind); if (!maybe_new_map->To(&new_map)) return maybe_new_map; @@ -9657,7 +10351,7 @@ MaybeObject* JSObject::SetFastElement(uint32_t index, MaybeObject* JSObject::SetDictionaryElement(uint32_t index, - Object* value, + Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode, bool check_prototype, @@ -9665,24 +10359,23 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements()); Isolate* isolate = GetIsolate(); Heap* heap = isolate->heap(); + Handle<JSObject> self(this); + Handle<Object> value(value_raw, isolate); // Insert element in the dictionary. - FixedArray* elements = FixedArray::cast(this->elements()); + Handle<FixedArray> elements(FixedArray::cast(this->elements())); bool is_arguments = (elements->map() == heap->non_strict_arguments_elements_map()); - SeededNumberDictionary* dictionary = NULL; - if (is_arguments) { - dictionary = SeededNumberDictionary::cast(elements->get(1)); - } else { - dictionary = SeededNumberDictionary::cast(elements); - } + Handle<SeededNumberDictionary> dictionary(is_arguments + ? SeededNumberDictionary::cast(elements->get(1)) + : SeededNumberDictionary::cast(*elements)); int entry = dictionary->FindEntry(index); if (entry != SeededNumberDictionary::kNotFound) { Object* element = dictionary->ValueAt(entry); PropertyDetails details = dictionary->DetailsAt(entry); if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) { - return SetElementWithCallback(element, index, value, this, strict_mode); + return SetElementWithCallback(element, index, *value, this, strict_mode); } else { dictionary->UpdateMaxNumberKey(index); // If a value has not been initialized we allow writing to it even if it @@ -9696,7 +10389,7 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, if (strict_mode == kNonStrictMode) { return isolate->heap()->undefined_value(); } else { - Handle<Object> holder(this); + Handle<Object> holder(this, isolate); Handle<Object> number = isolate->factory()->NewNumberFromUint(index); Handle<Object> args[2] = { number, holder }; Handle<Object> error = @@ -9711,24 +10404,24 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, Context* context = Context::cast(elements->get(0)); int context_index = entry->aliased_context_slot(); ASSERT(!context->get(context_index)->IsTheHole()); - context->set(context_index, value); + context->set(context_index, *value); // For elements that are still writable we keep slow aliasing. - if (!details.IsReadOnly()) value = element; + if (!details.IsReadOnly()) value = handle(element, isolate); } - dictionary->ValueAtPut(entry, value); + dictionary->ValueAtPut(entry, *value); } } else { // Index not already used. Look for an accessor in the prototype chain. + // Can cause GC! if (check_prototype) { bool found; - MaybeObject* result = - SetElementWithCallbackSetterInPrototypes( - index, value, &found, strict_mode); + MaybeObject* result = SetElementWithCallbackSetterInPrototypes( + index, *value, &found, strict_mode); if (found) return result; } // When we set the is_extensible flag to false we always force the // element into dictionary mode (and force them to stay there). - if (!map()->is_extensible()) { + if (!self->map()->is_extensible()) { if (strict_mode == kNonStrictMode) { return isolate->heap()->undefined_value(); } else { @@ -9743,30 +10436,31 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, } FixedArrayBase* new_dictionary; PropertyDetails details = PropertyDetails(attributes, NORMAL); - MaybeObject* maybe = dictionary->AddNumberEntry(index, value, details); + MaybeObject* maybe = dictionary->AddNumberEntry(index, *value, details); if (!maybe->To(&new_dictionary)) return maybe; - if (dictionary != SeededNumberDictionary::cast(new_dictionary)) { + if (*dictionary != SeededNumberDictionary::cast(new_dictionary)) { if (is_arguments) { elements->set(1, new_dictionary); } else { - set_elements(new_dictionary); + self->set_elements(new_dictionary); } - dictionary = SeededNumberDictionary::cast(new_dictionary); + dictionary = + handle(SeededNumberDictionary::cast(new_dictionary), isolate); } } // Update the array length if this JSObject is an array. - if (IsJSArray()) { + if (self->IsJSArray()) { MaybeObject* result = - JSArray::cast(this)->JSArrayUpdateLengthFromIndex(index, value); + JSArray::cast(*self)->JSArrayUpdateLengthFromIndex(index, *value); if (result->IsFailure()) return result; } // Attempt to put this object back in fast case. - if (ShouldConvertToFastElements()) { + if (self->ShouldConvertToFastElements()) { uint32_t new_length = 0; - if (IsJSArray()) { - CHECK(JSArray::cast(this)->length()->ToArrayIndex(&new_length)); + if (self->IsJSArray()) { + CHECK(JSArray::cast(*self)->length()->ToArrayIndex(&new_length)); } else { new_length = dictionary->max_number_key() + 1; } @@ -9775,16 +10469,15 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, : kDontAllowSmiElements; bool has_smi_only_elements = false; bool should_convert_to_fast_double_elements = - ShouldConvertToFastDoubleElements(&has_smi_only_elements); + self->ShouldConvertToFastDoubleElements(&has_smi_only_elements); if (has_smi_only_elements) { smi_mode = kForceSmiElements; } MaybeObject* result = should_convert_to_fast_double_elements - ? SetFastDoubleElementsCapacityAndLength(new_length, new_length) - : SetFastElementsCapacityAndLength(new_length, - new_length, - smi_mode); - ValidateElements(); + ? self->SetFastDoubleElementsCapacityAndLength(new_length, new_length) + : self->SetFastElementsCapacityAndLength( + new_length, new_length, smi_mode); + self->ValidateElements(); if (result->IsFailure()) return result; #ifdef DEBUG if (FLAG_trace_normalization) { @@ -9793,7 +10486,7 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, } #endif } - return value; + return *value; } @@ -9949,28 +10642,27 @@ Handle<Object> JSObject::SetElement(Handle<JSObject> object, MaybeObject* JSObject::SetElement(uint32_t index, - Object* value, + Object* value_raw, PropertyAttributes attributes, StrictModeFlag strict_mode, bool check_prototype, SetPropertyMode set_mode) { + Isolate* isolate = GetIsolate(); + // Check access rights if needed. if (IsAccessCheckNeeded()) { - Heap* heap = GetHeap(); - if (!heap->isolate()->MayIndexedAccess(this, index, v8::ACCESS_SET)) { - HandleScope scope(heap->isolate()); - Handle<Object> value_handle(value); - heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_SET); - return *value_handle; + if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_SET)) { + isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET); + return value_raw; } } if (IsJSGlobalProxy()) { Object* proto = GetPrototype(); - if (proto->IsNull()) return value; + if (proto->IsNull()) return value_raw; ASSERT(proto->IsJSGlobalObject()); return JSObject::cast(proto)->SetElement(index, - value, + value_raw, attributes, strict_mode, check_prototype, @@ -9979,10 +10671,8 @@ MaybeObject* JSObject::SetElement(uint32_t index, // Don't allow element properties to be redefined for external arrays. if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) { - Isolate* isolate = GetHeap()->isolate(); - Handle<Object> receiver(this); Handle<Object> number = isolate->factory()->NewNumberFromUint(index); - Handle<Object> args[] = { receiver, number }; + Handle<Object> args[] = { handle(this, isolate), number }; Handle<Object> error = isolate->factory()->NewTypeError( "redef_external_array_element", HandleVector(args, ARRAY_SIZE(args))); return isolate->Throw(*error); @@ -9997,22 +10687,62 @@ MaybeObject* JSObject::SetElement(uint32_t index, dictionary->set_requires_slow_elements(); } + if (!(FLAG_harmony_observation && map()->is_observed())) { + return HasIndexedInterceptor() + ? SetElementWithInterceptor( + index, value_raw, attributes, strict_mode, check_prototype, set_mode) + : SetElementWithoutInterceptor( + index, value_raw, attributes, strict_mode, check_prototype, set_mode); + } + + // From here on, everything has to be handlified. + Handle<JSObject> self(this); + Handle<Object> value(value_raw, isolate); + PropertyAttributes old_attributes = self->GetLocalElementAttribute(index); + Handle<Object> old_value = isolate->factory()->the_hole_value(); + Handle<Object> old_length; + + if (old_attributes != ABSENT) { + if (self->GetLocalElementAccessorPair(index) == NULL) + old_value = Object::GetElement(self, index); + } else if (self->IsJSArray()) { + // Store old array length in case adding an element grows the array. + old_length = handle(Handle<JSArray>::cast(self)->length(), isolate); + } + // Check for lookup interceptor - if (HasIndexedInterceptor()) { - return SetElementWithInterceptor(index, - value, - attributes, - strict_mode, - check_prototype, - set_mode); + MaybeObject* result = self->HasIndexedInterceptor() + ? self->SetElementWithInterceptor( + index, *value, attributes, strict_mode, check_prototype, set_mode) + : self->SetElementWithoutInterceptor( + index, *value, attributes, strict_mode, check_prototype, set_mode); + + Handle<Object> hresult; + if (!result->ToHandle(&hresult, isolate)) return result; + + Handle<String> name = isolate->factory()->Uint32ToString(index); + PropertyAttributes new_attributes = self->GetLocalElementAttribute(index); + if (old_attributes == ABSENT) { + EnqueueChangeRecord(self, "new", name, old_value); + if (self->IsJSArray() && + !old_length->SameValue(Handle<JSArray>::cast(self)->length())) { + EnqueueChangeRecord( + self, "updated", isolate->factory()->length_string(), old_length); + } + } else if (old_value->IsTheHole()) { + EnqueueChangeRecord(self, "reconfigured", name, old_value); + } else { + bool value_changed = + !old_value->SameValue(*Object::GetElement(self, index)); + if (old_attributes != new_attributes) { + if (!value_changed) old_value = isolate->factory()->the_hole_value(); + EnqueueChangeRecord(self, "reconfigured", name, old_value); + } else if (value_changed) { + EnqueueChangeRecord(self, "updated", name, old_value); + } } - return SetElementWithoutInterceptor(index, - value, - attributes, - strict_mode, - check_prototype, - set_mode); + return *hresult; } @@ -10026,6 +10756,16 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index, HasDictionaryArgumentsElements() || (attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0); Isolate* isolate = GetIsolate(); + if (FLAG_trace_external_array_abuse && + IsExternalArrayElementsKind(GetElementsKind())) { + CheckArrayAbuse(this, "external elements write", index); + } + if (FLAG_trace_js_array_abuse && + !IsExternalArrayElementsKind(GetElementsKind())) { + if (IsJSArray()) { + CheckArrayAbuse(this, "elements write", index, true); + } + } switch (GetElementsKind()) { case FAST_SMI_ELEMENTS: case FAST_ELEMENTS: @@ -10121,13 +10861,69 @@ Handle<Object> JSObject::TransitionElementsKind(Handle<JSObject> object, } +MaybeObject* JSObject::UpdateAllocationSiteInfo(ElementsKind to_kind) { + if (!FLAG_track_allocation_sites || !IsJSArray()) { + return this; + } + + AllocationSiteInfo* info = AllocationSiteInfo::FindForJSObject(this); + if (info == NULL) { + return this; + } + + if (info->payload()->IsJSArray()) { + JSArray* payload = JSArray::cast(info->payload()); + ElementsKind kind = payload->GetElementsKind(); + if (AllocationSiteInfo::GetMode(kind, to_kind) == TRACK_ALLOCATION_SITE) { + // If the array is huge, it's not likely to be defined in a local + // function, so we shouldn't make new instances of it very often. + uint32_t length = 0; + CHECK(payload->length()->ToArrayIndex(&length)); + if (length <= AllocationSiteInfo::kMaximumArrayBytesToPretransition) { + if (FLAG_trace_track_allocation_sites) { + PrintF( + "AllocationSiteInfo: JSArray %p boilerplate updated %s->%s\n", + reinterpret_cast<void*>(this), + ElementsKindToString(kind), + ElementsKindToString(to_kind)); + } + return payload->TransitionElementsKind(to_kind); + } + } + } else if (info->payload()->IsJSGlobalPropertyCell()) { + JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(info->payload()); + Object* cell_contents = cell->value(); + if (cell_contents->IsSmi()) { + ElementsKind kind = static_cast<ElementsKind>( + Smi::cast(cell_contents)->value()); + if (AllocationSiteInfo::GetMode(kind, to_kind) == TRACK_ALLOCATION_SITE) { + if (FLAG_trace_track_allocation_sites) { + PrintF("AllocationSiteInfo: JSArray %p info updated %s->%s\n", + reinterpret_cast<void*>(this), + ElementsKindToString(kind), + ElementsKindToString(to_kind)); + } + cell->set_value(Smi::FromInt(to_kind)); + } + } + } + return this; +} + + MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) { + ASSERT(!map()->is_observed()); ElementsKind from_kind = map()->elements_kind(); if (IsFastHoleyElementsKind(from_kind)) { to_kind = GetHoleyElementsKind(to_kind); } + if (from_kind == to_kind) return this; + + MaybeObject* maybe_failure = UpdateAllocationSiteInfo(to_kind); + if (maybe_failure->IsFailure()) return maybe_failure; + Isolate* isolate = GetIsolate(); if (elements() == isolate->heap()->empty_fixed_array() || (IsFastSmiOrObjectElementsKind(from_kind) && @@ -10376,6 +11172,9 @@ bool JSObject::ShouldConvertToFastElements() { // An object requiring access checks is never allowed to have fast // elements. If it had fast elements we would skip security checks. if (IsAccessCheckNeeded()) return false; + // Observed objects may not go to fast mode because they rely on map checks, + // and for fast element accesses we sometimes check element kinds only. + if (FLAG_harmony_observation && map()->is_observed()) return false; FixedArray* elements = FixedArray::cast(this->elements()); SeededNumberDictionary* dictionary = NULL; @@ -10493,7 +11292,7 @@ InterceptorInfo* JSObject::GetIndexedInterceptor() { MaybeObject* JSObject::GetPropertyPostInterceptor( Object* receiver, - String* name, + Name* name, PropertyAttributes* attributes) { // Check local property in holder, ignore interceptor. LookupResult result(GetIsolate()); @@ -10511,7 +11310,7 @@ MaybeObject* JSObject::GetPropertyPostInterceptor( MaybeObject* JSObject::GetLocalPropertyPostInterceptor( Object* receiver, - String* name, + Name* name, PropertyAttributes* attributes) { // Check local property in holder, ignore interceptor. LookupResult result(GetIsolate()); @@ -10525,14 +11324,17 @@ MaybeObject* JSObject::GetLocalPropertyPostInterceptor( MaybeObject* JSObject::GetPropertyWithInterceptor( Object* receiver, - String* name, + Name* name, PropertyAttributes* attributes) { + // TODO(rossberg): Support symbols in the API. + if (name->IsSymbol()) return GetHeap()->undefined_value(); + Isolate* isolate = GetIsolate(); InterceptorInfo* interceptor = GetNamedInterceptor(); HandleScope scope(isolate); - Handle<Object> receiver_handle(receiver); + Handle<Object> receiver_handle(receiver, isolate); Handle<JSObject> holder_handle(this); - Handle<String> name_handle(name); + Handle<String> name_handle(String::cast(name)); if (!interceptor->getter()->IsUndefined()) { v8::NamedPropertyGetter getter = @@ -10565,7 +11367,7 @@ MaybeObject* JSObject::GetPropertyWithInterceptor( } -bool JSObject::HasRealNamedProperty(String* key) { +bool JSObject::HasRealNamedProperty(Name* key) { // Check access rights if needed. Isolate* isolate = GetIsolate(); if (IsAccessCheckNeeded()) { @@ -10645,7 +11447,7 @@ bool JSObject::HasRealElementProperty(uint32_t index) { } -bool JSObject::HasRealNamedCallbackProperty(String* key) { +bool JSObject::HasRealNamedCallbackProperty(Name* key) { // Check access rights if needed. Isolate* isolate = GetIsolate(); if (IsAccessCheckNeeded()) { @@ -10665,7 +11467,7 @@ int JSObject::NumberOfLocalProperties(PropertyAttributes filter) { if (HasFastProperties()) { Map* map = this->map(); if (filter == NONE) return map->NumberOfOwnDescriptors(); - if (filter == DONT_ENUM) { + if (filter & DONT_ENUM) { int result = map->EnumLength(); if (result != Map::kInvalidEnumCache) return result; } @@ -10803,7 +11605,7 @@ void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) { } else { property_dictionary()->CopyKeysTo(storage, index, - StringDictionary::UNSORTED); + NameDictionary::UNSORTED); } } @@ -10989,7 +11791,7 @@ class StringKey : public HashTableKey { uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); } - Object* AsObject() { return string_; } + Object* AsObject(Heap* heap) { return string_; } String* string_; uint32_t hash_; @@ -11064,9 +11866,9 @@ class StringSharedKey : public HashTableKey { source, shared, language_mode, scope_position); } - MUST_USE_RESULT MaybeObject* AsObject() { + MUST_USE_RESULT MaybeObject* AsObject(Heap* heap) { Object* obj; - { MaybeObject* maybe_obj = source_->GetHeap()->AllocateFixedArray(4); + { MaybeObject* maybe_obj = heap->AllocateFixedArray(4); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } FixedArray* other_array = FixedArray::cast(obj); @@ -11104,7 +11906,7 @@ class RegExpKey : public HashTableKey { uint32_t Hash() { return RegExpHash(string_, flags_); } - Object* AsObject() { + Object* AsObject(Heap* heap) { // Plain hash maps, which is where regexp keys are used, don't // use this function. UNREACHABLE(); @@ -11125,22 +11927,19 @@ class RegExpKey : public HashTableKey { Smi* flags_; }; -// Utf8SymbolKey carries a vector of chars as key. -class Utf8SymbolKey : public HashTableKey { +// Utf8StringKey carries a vector of chars as key. +class Utf8StringKey : public HashTableKey { public: - explicit Utf8SymbolKey(Vector<const char> string, uint32_t seed) + explicit Utf8StringKey(Vector<const char> string, uint32_t seed) : string_(string), hash_field_(0), seed_(seed) { } bool IsMatch(Object* string) { - return String::cast(string)->IsEqualTo(string_); + return String::cast(string)->IsUtf8EqualTo(string_); } uint32_t Hash() { if (hash_field_ != 0) return hash_field_ >> String::kHashShift; - unibrow::Utf8InputBuffer<> buffer(string_.start(), - static_cast<unsigned>(string_.length())); - chars_ = buffer.Utf16Length(); - hash_field_ = String::ComputeHashField(&buffer, chars_, seed_); + hash_field_ = StringHasher::ComputeUtf8Hash(string_, seed_, &chars_); uint32_t result = hash_field_ >> String::kHashShift; ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. return result; @@ -11150,10 +11949,11 @@ class Utf8SymbolKey : public HashTableKey { return String::cast(other)->Hash(); } - MaybeObject* AsObject() { + MaybeObject* AsObject(Heap* heap) { if (hash_field_ == 0) Hash(); - return Isolate::Current()->heap()->AllocateSymbol( - string_, chars_, hash_field_); + return heap->AllocateInternalizedStringFromUtf8(string_, + chars_, + hash_field_); } Vector<const char> string_; @@ -11164,35 +11964,15 @@ class Utf8SymbolKey : public HashTableKey { template <typename Char> -class SequentialSymbolKey : public HashTableKey { +class SequentialStringKey : public HashTableKey { public: - explicit SequentialSymbolKey(Vector<const Char> string, uint32_t seed) + explicit SequentialStringKey(Vector<const Char> string, uint32_t seed) : string_(string), hash_field_(0), seed_(seed) { } uint32_t Hash() { - StringHasher hasher(string_.length(), seed_); - - // Very long strings have a trivial hash that doesn't inspect the - // string contents. - if (hasher.has_trivial_hash()) { - hash_field_ = hasher.GetHashField(); - } else { - int i = 0; - // Do the iterative array index computation as long as there is a - // chance this is an array index. - while (i < string_.length() && hasher.is_array_index()) { - hasher.AddCharacter(static_cast<uc32>(string_[i])); - i++; - } - - // Process the remaining characters without updating the array - // index. - while (i < string_.length()) { - hasher.AddCharacterNoIndex(static_cast<uc32>(string_[i])); - i++; - } - hash_field_ = hasher.GetHashField(); - } + hash_field_ = StringHasher::HashSequentialString<Char>(string_.start(), + string_.length(), + seed_); uint32_t result = hash_field_ >> String::kHashShift; ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. @@ -11211,59 +11991,35 @@ class SequentialSymbolKey : public HashTableKey { -class AsciiSymbolKey : public SequentialSymbolKey<char> { +class OneByteStringKey : public SequentialStringKey<uint8_t> { public: - AsciiSymbolKey(Vector<const char> str, uint32_t seed) - : SequentialSymbolKey<char>(str, seed) { } + OneByteStringKey(Vector<const uint8_t> str, uint32_t seed) + : SequentialStringKey<uint8_t>(str, seed) { } bool IsMatch(Object* string) { - return String::cast(string)->IsAsciiEqualTo(string_); + return String::cast(string)->IsOneByteEqualTo(string_); } - MaybeObject* AsObject() { + MaybeObject* AsObject(Heap* heap) { if (hash_field_ == 0) Hash(); - return HEAP->AllocateAsciiSymbol(string_, hash_field_); + return heap->AllocateOneByteInternalizedString(string_, hash_field_); } }; -class SubStringAsciiSymbolKey : public HashTableKey { +class SubStringOneByteStringKey : public HashTableKey { public: - explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string, - int from, - int length, - uint32_t seed) - : string_(string), from_(from), length_(length), seed_(seed) { } + explicit SubStringOneByteStringKey(Handle<SeqOneByteString> string, + int from, + int length) + : string_(string), from_(from), length_(length) { } uint32_t Hash() { ASSERT(length_ >= 0); ASSERT(from_ + length_ <= string_->length()); - StringHasher hasher(length_, string_->GetHeap()->HashSeed()); - - // Very long strings have a trivial hash that doesn't inspect the - // string contents. - if (hasher.has_trivial_hash()) { - hash_field_ = hasher.GetHashField(); - } else { - int i = 0; - // Do the iterative array index computation as long as there is a - // chance this is an array index. - while (i < length_ && hasher.is_array_index()) { - hasher.AddCharacter(static_cast<uc32>( - string_->SeqAsciiStringGet(i + from_))); - i++; - } - - // Process the remaining characters without updating the array - // index. - while (i < length_) { - hasher.AddCharacterNoIndex(static_cast<uc32>( - string_->SeqAsciiStringGet(i + from_))); - i++; - } - hash_field_ = hasher.GetHashField(); - } - + uint8_t* chars = string_->GetChars() + from_; + hash_field_ = StringHasher::HashSequentialString( + chars, length_, string_->GetHeap()->HashSeed()); uint32_t result = hash_field_ >> String::kHashShift; ASSERT(result != 0); // Ensure that the hash value of 0 is never computed. return result; @@ -11275,45 +12031,44 @@ class SubStringAsciiSymbolKey : public HashTableKey { } bool IsMatch(Object* string) { - Vector<const char> chars(string_->GetChars() + from_, length_); - return String::cast(string)->IsAsciiEqualTo(chars); + Vector<const uint8_t> chars(string_->GetChars() + from_, length_); + return String::cast(string)->IsOneByteEqualTo(chars); } - MaybeObject* AsObject() { + MaybeObject* AsObject(Heap* heap) { if (hash_field_ == 0) Hash(); - Vector<const char> chars(string_->GetChars() + from_, length_); - return HEAP->AllocateAsciiSymbol(chars, hash_field_); + Vector<const uint8_t> chars(string_->GetChars() + from_, length_); + return heap->AllocateOneByteInternalizedString(chars, hash_field_); } private: - Handle<SeqAsciiString> string_; + Handle<SeqOneByteString> string_; int from_; int length_; uint32_t hash_field_; - uint32_t seed_; }; -class TwoByteSymbolKey : public SequentialSymbolKey<uc16> { +class TwoByteStringKey : public SequentialStringKey<uc16> { public: - explicit TwoByteSymbolKey(Vector<const uc16> str, uint32_t seed) - : SequentialSymbolKey<uc16>(str, seed) { } + explicit TwoByteStringKey(Vector<const uc16> str, uint32_t seed) + : SequentialStringKey<uc16>(str, seed) { } bool IsMatch(Object* string) { return String::cast(string)->IsTwoByteEqualTo(string_); } - MaybeObject* AsObject() { + MaybeObject* AsObject(Heap* heap) { if (hash_field_ == 0) Hash(); - return HEAP->AllocateTwoByteSymbol(string_, hash_field_); + return heap->AllocateTwoByteInternalizedString(string_, hash_field_); } }; -// SymbolKey carries a string/symbol object as key. -class SymbolKey : public HashTableKey { +// InternalizedStringKey carries a string/internalized-string object as key. +class InternalizedStringKey : public HashTableKey { public: - explicit SymbolKey(String* string) + explicit InternalizedStringKey(String* string) : string_(string) { } bool IsMatch(Object* string) { @@ -11326,23 +12081,20 @@ class SymbolKey : public HashTableKey { return String::cast(other)->Hash(); } - MaybeObject* AsObject() { - // Attempt to flatten the string, so that symbols will most often - // be flat strings. + MaybeObject* AsObject(Heap* heap) { + // Attempt to flatten the string, so that internalized strings will most + // often be flat strings. string_ = string_->TryFlattenGetString(); - Heap* heap = string_->GetHeap(); - // Transform string to symbol if possible. - Map* map = heap->SymbolMapForString(string_); + // Internalize the string if possible. + Map* map = heap->InternalizedStringMapForString(string_); if (map != NULL) { string_->set_map_no_write_barrier(map); - ASSERT(string_->IsSymbol()); + ASSERT(string_->IsInternalizedString()); return string_; } - // Otherwise allocate a new symbol. - StringInputBuffer buffer(string_); - return heap->AllocateInternalSymbol(&buffer, - string_->length(), - string_->hash_field()); + // Otherwise allocate a new internalized string. + return heap->AllocateInternalizedStringImpl( + string_, string_->length(), string_->hash_field()); } static uint32_t StringHash(Object* obj) { @@ -11368,7 +12120,8 @@ void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) { template<typename Shape, typename Key> -MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for, +MaybeObject* HashTable<Shape, Key>::Allocate(Heap* heap, + int at_least_space_for, MinimumCapacity capacity_option, PretenureFlag pretenure) { ASSERT(!capacity_option || IS_POWER_OF_TWO(at_least_space_for)); @@ -11376,12 +12129,12 @@ MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for, ? at_least_space_for : ComputeCapacity(at_least_space_for); if (capacity > HashTable::kMaxCapacity) { - return Failure::OutOfMemoryException(); + return Failure::OutOfMemoryException(0x10); } Object* obj; - { MaybeObject* maybe_obj = Isolate::Current()->heap()-> - AllocateHashTable(EntryToIndex(capacity), pretenure); + { MaybeObject* maybe_obj = + heap-> AllocateHashTable(EntryToIndex(capacity), pretenure); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } HashTable::cast(obj)->SetNumberOfElements(0); @@ -11392,19 +12145,19 @@ MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for, // Find entry for key otherwise return kNotFound. -int StringDictionary::FindEntry(String* key) { - if (!key->IsSymbol()) { - return HashTable<StringDictionaryShape, String*>::FindEntry(key); +int NameDictionary::FindEntry(Name* key) { + if (!key->IsUniqueName()) { + return HashTable<NameDictionaryShape, Name*>::FindEntry(key); } - // Optimized for symbol key. Knowledge of the key type allows: - // 1. Move the check if the key is a symbol out of the loop. - // 2. Avoid comparing hash codes in symbol to symbol comparison. - // 3. Detect a case when a dictionary key is not a symbol but the key is. - // In case of positive result the dictionary key may be replaced by - // the symbol with minimal performance penalty. It gives a chance to - // perform further lookups in code stubs (and significant performance boost - // a certain style of code). + // Optimized for unique names. Knowledge of the key type allows: + // 1. Move the check if the key is unique out of the loop. + // 2. Avoid comparing hash codes in unique-to-unique comparison. + // 3. Detect a case when a dictionary key is not unique but the key is. + // In case of positive result the dictionary key may be replaced by the + // internalized string with minimal performance penalty. It gives a chance + // to perform further lookups in code stubs (and significant performance + // boost a certain style of code). // EnsureCapacity will guarantee the hash table is never full. uint32_t capacity = Capacity(); @@ -11416,15 +12169,15 @@ int StringDictionary::FindEntry(String* key) { Object* element = get(index); if (element->IsUndefined()) break; // Empty entry. if (key == element) return entry; - if (!element->IsSymbol() && + if (!element->IsUniqueName() && !element->IsTheHole() && - String::cast(element)->Equals(key)) { - // Replace a non-symbol key by the equivalent symbol for faster further - // lookups. + Name::cast(element)->Equals(key)) { + // Replace a key that is a non-internalized string by the equivalent + // internalized string for faster further lookups. set(index, key); return entry; } - ASSERT(element->IsTheHole() || !String::cast(element)->Equals(key)); + ASSERT(element->IsTheHole() || !Name::cast(element)->Equals(key)); entry = NextProbe(entry, count++, capacity); } return kNotFound; @@ -11483,7 +12236,8 @@ MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) { (capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this); Object* obj; { MaybeObject* maybe_obj = - Allocate(nof * 2, + Allocate(GetHeap(), + nof * 2, USE_DEFAULT_MINIMUM_CAPACITY, pretenure ? TENURED : NOT_TENURED); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -11514,7 +12268,8 @@ MaybeObject* HashTable<Shape, Key>::Shrink(Key key) { !GetHeap()->InNewSpace(this); Object* obj; { MaybeObject* maybe_obj = - Allocate(at_least_room_for, + Allocate(GetHeap(), + at_least_room_for, USE_DEFAULT_MINIMUM_CAPACITY, pretenure ? TENURED : NOT_TENURED); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -11541,7 +12296,7 @@ uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) { // Force instantiation of template instances class. // Please note this list is compiler dependent. -template class HashTable<SymbolTableShape, HashTableKey*>; +template class HashTable<StringTableShape, HashTableKey*>; template class HashTable<CompilationCacheShape, HashTableKey*>; @@ -11551,20 +12306,20 @@ template class HashTable<ObjectHashTableShape<1>, Object*>; template class HashTable<ObjectHashTableShape<2>, Object*>; -template class Dictionary<StringDictionaryShape, String*>; +template class Dictionary<NameDictionaryShape, Name*>; template class Dictionary<SeededNumberDictionaryShape, uint32_t>; template class Dictionary<UnseededNumberDictionaryShape, uint32_t>; template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>:: - Allocate(int at_least_space_for); + Allocate(Heap* heap, int at_least_space_for); template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: - Allocate(int at_least_space_for); + Allocate(Heap* heap, int at_least_space_for); -template MaybeObject* Dictionary<StringDictionaryShape, String*>::Allocate( - int); +template MaybeObject* Dictionary<NameDictionaryShape, Name*>:: + Allocate(Heap* heap, int n); template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::AtPut( uint32_t, Object*); @@ -11578,7 +12333,7 @@ template Object* Dictionary<SeededNumberDictionaryShape, uint32_t>:: template Object* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: SlowReverseLookup(Object* value); -template Object* Dictionary<StringDictionaryShape, String*>::SlowReverseLookup( +template Object* Dictionary<NameDictionaryShape, Name*>::SlowReverseLookup( Object*); template void Dictionary<SeededNumberDictionaryShape, uint32_t>::CopyKeysTo( @@ -11586,32 +12341,31 @@ template void Dictionary<SeededNumberDictionaryShape, uint32_t>::CopyKeysTo( PropertyAttributes, Dictionary<SeededNumberDictionaryShape, uint32_t>::SortMode); -template Object* Dictionary<StringDictionaryShape, String*>::DeleteProperty( +template Object* Dictionary<NameDictionaryShape, Name*>::DeleteProperty( int, JSObject::DeleteMode); template Object* Dictionary<SeededNumberDictionaryShape, uint32_t>:: DeleteProperty(int, JSObject::DeleteMode); -template MaybeObject* Dictionary<StringDictionaryShape, String*>::Shrink( - String*); +template MaybeObject* Dictionary<NameDictionaryShape, Name*>::Shrink(Name* n); template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::Shrink( uint32_t); -template void Dictionary<StringDictionaryShape, String*>::CopyKeysTo( +template void Dictionary<NameDictionaryShape, Name*>::CopyKeysTo( FixedArray*, int, - Dictionary<StringDictionaryShape, String*>::SortMode); + Dictionary<NameDictionaryShape, Name*>::SortMode); template int -Dictionary<StringDictionaryShape, String*>::NumberOfElementsFilterAttributes( +Dictionary<NameDictionaryShape, Name*>::NumberOfElementsFilterAttributes( PropertyAttributes); -template MaybeObject* Dictionary<StringDictionaryShape, String*>::Add( - String*, Object*, PropertyDetails); +template MaybeObject* Dictionary<NameDictionaryShape, Name*>::Add( + Name*, Object*, PropertyDetails); template MaybeObject* -Dictionary<StringDictionaryShape, String*>::GenerateNewEnumerationIndices(); +Dictionary<NameDictionaryShape, Name*>::GenerateNewEnumerationIndices(); template int Dictionary<SeededNumberDictionaryShape, uint32_t>:: @@ -11629,8 +12383,8 @@ template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>:: template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: EnsureCapacity(int, uint32_t); -template MaybeObject* Dictionary<StringDictionaryShape, String*>:: - EnsureCapacity(int, String*); +template MaybeObject* Dictionary<NameDictionaryShape, Name*>:: + EnsureCapacity(int, Name*); template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>:: AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); @@ -11638,14 +12392,14 @@ template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>:: template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>:: AddEntry(uint32_t, Object*, PropertyDetails, uint32_t); -template MaybeObject* Dictionary<StringDictionaryShape, String*>::AddEntry( - String*, Object*, PropertyDetails, uint32_t); +template MaybeObject* Dictionary<NameDictionaryShape, Name*>::AddEntry( + Name*, Object*, PropertyDetails, uint32_t); template int Dictionary<SeededNumberDictionaryShape, uint32_t>::NumberOfEnumElements(); template -int Dictionary<StringDictionaryShape, String*>::NumberOfEnumElements(); +int Dictionary<NameDictionaryShape, Name*>::NumberOfEnumElements(); template int HashTable<SeededNumberDictionaryShape, uint32_t>::FindEntry(uint32_t); @@ -11671,7 +12425,7 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { Object* obj; { MaybeObject* maybe_obj = - SeededNumberDictionary::Allocate(dict->NumberOfElements()); + SeededNumberDictionary::Allocate(GetHeap(), dict->NumberOfElements()); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } SeededNumberDictionary* new_dict = SeededNumberDictionary::cast(obj); @@ -11690,8 +12444,9 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32); Object* value = dict->ValueAt(i); PropertyDetails details = dict->DetailsAt(i); - if (details.type() == CALLBACKS) { + if (details.type() == CALLBACKS || details.IsReadOnly()) { // Bail out and do the sorting of undefineds and array holes in JS. + // Also bail out if the element is not supposed to be moved. return Smi::FromInt(-1); } uint32_t key = NumberToUint32(k); @@ -12062,7 +12817,7 @@ JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) { Handle<JSGlobalPropertyCell> GlobalObject::EnsurePropertyCell( Handle<GlobalObject> global, - Handle<String> name) { + Handle<Name> name) { Isolate* isolate = global->GetIsolate(); CALL_HEAP_FUNCTION(isolate, global->EnsurePropertyCell(*name), @@ -12070,10 +12825,10 @@ Handle<JSGlobalPropertyCell> GlobalObject::EnsurePropertyCell( } -MaybeObject* GlobalObject::EnsurePropertyCell(String* name) { +MaybeObject* GlobalObject::EnsurePropertyCell(Name* name) { ASSERT(!HasFastProperties()); int entry = property_dictionary()->FindEntry(name); - if (entry == StringDictionary::kNotFound) { + if (entry == NameDictionary::kNotFound) { Heap* heap = GetHeap(); Object* cell; { MaybeObject* maybe_cell = @@ -12087,7 +12842,7 @@ MaybeObject* GlobalObject::EnsurePropertyCell(String* name) { property_dictionary()->Add(name, cell, details); if (!maybe_dictionary->ToObject(&dictionary)) return maybe_dictionary; } - set_properties(StringDictionary::cast(dictionary)); + set_properties(NameDictionary::cast(dictionary)); return cell; } else { Object* value = property_dictionary()->ValueAt(entry); @@ -12097,20 +12852,20 @@ MaybeObject* GlobalObject::EnsurePropertyCell(String* name) { } -MaybeObject* SymbolTable::LookupString(String* string, Object** s) { - SymbolKey key(string); +MaybeObject* StringTable::LookupString(String* string, Object** s) { + InternalizedStringKey key(string); return LookupKey(&key, s); } -// This class is used for looking up two character strings in the symbol table. +// This class is used for looking up two character strings in the string table. // If we don't have a hit we don't want to waste much time so we unroll the // string hash calculation loop here for speed. Doesn't work if the two // characters form a decimal integer, since such strings have a different hash // algorithm. class TwoCharHashTableKey : public HashTableKey { public: - TwoCharHashTableKey(uint32_t c1, uint32_t c2, uint32_t seed) + TwoCharHashTableKey(uint16_t c1, uint16_t c2, uint32_t seed) : c1_(c1), c2_(c2) { // Char 1. uint32_t hash = seed; @@ -12126,17 +12881,17 @@ class TwoCharHashTableKey : public HashTableKey { hash ^= hash >> 11; hash += hash << 15; if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash; + hash_ = hash; #ifdef DEBUG - StringHasher hasher(2, seed); - hasher.AddCharacter(c1); - hasher.AddCharacter(c2); // If this assert fails then we failed to reproduce the two-character // version of the string hashing algorithm above. One reason could be // that we were passed two digits as characters, since the hash // algorithm is different in that case. - ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash)); + uint16_t chars[2] = {c1, c2}; + uint32_t check_hash = StringHasher::HashSequentialString(chars, 2, seed); + hash = (hash << String::kHashShift) | String::kIsNotArrayIndexMask; + ASSERT_EQ(static_cast<int32_t>(hash), static_cast<int32_t>(check_hash)); #endif - hash_ = hash; } bool IsMatch(Object* o) { @@ -12153,110 +12908,109 @@ class TwoCharHashTableKey : public HashTableKey { return String::cast(key)->Hash(); } - Object* AsObject() { - // The TwoCharHashTableKey is only used for looking in the symbol + Object* AsObject(Heap* heap) { + // The TwoCharHashTableKey is only used for looking in the string // table, not for adding to it. UNREACHABLE(); return NULL; } private: - uint32_t c1_; - uint32_t c2_; + uint16_t c1_; + uint16_t c2_; uint32_t hash_; }; -bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) { - SymbolKey key(string); +bool StringTable::LookupStringIfExists(String* string, String** result) { + InternalizedStringKey key(string); int entry = FindEntry(&key); if (entry == kNotFound) { return false; } else { - String* result = String::cast(KeyAt(entry)); - ASSERT(StringShape(result).IsSymbol()); - *symbol = result; + *result = String::cast(KeyAt(entry)); + ASSERT(StringShape(*result).IsInternalized()); return true; } } -bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1, - uint32_t c2, - String** symbol) { +bool StringTable::LookupTwoCharsStringIfExists(uint16_t c1, + uint16_t c2, + String** result) { TwoCharHashTableKey key(c1, c2, GetHeap()->HashSeed()); int entry = FindEntry(&key); if (entry == kNotFound) { return false; } else { - String* result = String::cast(KeyAt(entry)); - ASSERT(StringShape(result).IsSymbol()); - *symbol = result; + *result = String::cast(KeyAt(entry)); + ASSERT(StringShape(*result).IsInternalized()); return true; } } -MaybeObject* SymbolTable::LookupSymbol(Vector<const char> str, - Object** s) { - Utf8SymbolKey key(str, GetHeap()->HashSeed()); +MaybeObject* StringTable::LookupUtf8String(Vector<const char> str, + Object** s) { + Utf8StringKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } -MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str, - Object** s) { - AsciiSymbolKey key(str, GetHeap()->HashSeed()); +MaybeObject* StringTable::LookupOneByteString(Vector<const uint8_t> str, + Object** s) { + OneByteStringKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } -MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle<SeqAsciiString> str, - int from, - int length, - Object** s) { - SubStringAsciiSymbolKey key(str, from, length, GetHeap()->HashSeed()); +MaybeObject* StringTable::LookupSubStringOneByteString( + Handle<SeqOneByteString> str, + int from, + int length, + Object** s) { + SubStringOneByteStringKey key(str, from, length); return LookupKey(&key, s); } -MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str, +MaybeObject* StringTable::LookupTwoByteString(Vector<const uc16> str, Object** s) { - TwoByteSymbolKey key(str, GetHeap()->HashSeed()); + TwoByteStringKey key(str, GetHeap()->HashSeed()); return LookupKey(&key, s); } -MaybeObject* SymbolTable::LookupKey(HashTableKey* key, Object** s) { +MaybeObject* StringTable::LookupKey(HashTableKey* key, Object** s) { int entry = FindEntry(key); - // Symbol already in table. + // String already in table. if (entry != kNotFound) { *s = KeyAt(entry); return this; } - // Adding new symbol. Grow table if needed. + // Adding new string. Grow table if needed. Object* obj; { MaybeObject* maybe_obj = EnsureCapacity(1, key); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } - // Create symbol object. - Object* symbol; - { MaybeObject* maybe_symbol = key->AsObject(); - if (!maybe_symbol->ToObject(&symbol)) return maybe_symbol; + // Create string object. + Object* string; + { MaybeObject* maybe_string = key->AsObject(GetHeap()); + if (!maybe_string->ToObject(&string)) return maybe_string; } - // If the symbol table grew as part of EnsureCapacity, obj is not - // the current symbol table and therefore we cannot use - // SymbolTable::cast here. - SymbolTable* table = reinterpret_cast<SymbolTable*>(obj); + // If the string table grew as part of EnsureCapacity, obj is not + // the current string table and therefore we cannot use + // StringTable::cast here. + StringTable* table = reinterpret_cast<StringTable*>(obj); - // Add the new symbol and return it along with the symbol table. + // Add the new string and return it along with the string table. entry = table->FindInsertionEntry(key->Hash()); - table->set(EntryToIndex(entry), symbol); + table->set(EntryToIndex(entry), string); table->ElementAdded(); - *s = symbol; + *s = string; return table; } @@ -12320,7 +13074,7 @@ MaybeObject* CompilationCacheTable::Put(String* src, if (!maybe_cache->To(&cache)) return maybe_cache; Object* k; - MaybeObject* maybe_k = key.AsObject(); + MaybeObject* maybe_k = key.AsObject(GetHeap()); if (!maybe_k->To(&k)) return maybe_k; int entry = cache->FindInsertionEntry(key.Hash()); @@ -12349,7 +13103,7 @@ MaybeObject* CompilationCacheTable::PutEval(String* src, int entry = cache->FindInsertionEntry(key.Hash()); Object* k; - { MaybeObject* maybe_k = key.AsObject(); + { MaybeObject* maybe_k = key.AsObject(GetHeap()); if (!maybe_k->ToObject(&k)) return maybe_k; } @@ -12396,42 +13150,42 @@ void CompilationCacheTable::Remove(Object* value) { } -// SymbolsKey used for HashTable where key is array of symbols. -class SymbolsKey : public HashTableKey { +// StringsKey used for HashTable where key is array of internalized strings. +class StringsKey : public HashTableKey { public: - explicit SymbolsKey(FixedArray* symbols) : symbols_(symbols) { } + explicit StringsKey(FixedArray* strings) : strings_(strings) { } - bool IsMatch(Object* symbols) { - FixedArray* o = FixedArray::cast(symbols); - int len = symbols_->length(); + bool IsMatch(Object* strings) { + FixedArray* o = FixedArray::cast(strings); + int len = strings_->length(); if (o->length() != len) return false; for (int i = 0; i < len; i++) { - if (o->get(i) != symbols_->get(i)) return false; + if (o->get(i) != strings_->get(i)) return false; } return true; } - uint32_t Hash() { return HashForObject(symbols_); } + uint32_t Hash() { return HashForObject(strings_); } uint32_t HashForObject(Object* obj) { - FixedArray* symbols = FixedArray::cast(obj); - int len = symbols->length(); + FixedArray* strings = FixedArray::cast(obj); + int len = strings->length(); uint32_t hash = 0; for (int i = 0; i < len; i++) { - hash ^= String::cast(symbols->get(i))->Hash(); + hash ^= String::cast(strings->get(i))->Hash(); } return hash; } - Object* AsObject() { return symbols_; } + Object* AsObject(Heap* heap) { return strings_; } private: - FixedArray* symbols_; + FixedArray* strings_; }; Object* MapCache::Lookup(FixedArray* array) { - SymbolsKey key(array); + StringsKey key(array); int entry = FindEntry(&key); if (entry == kNotFound) return GetHeap()->undefined_value(); return get(EntryToIndex(entry) + 1); @@ -12439,7 +13193,7 @@ Object* MapCache::Lookup(FixedArray* array) { MaybeObject* MapCache::Put(FixedArray* array, Map* value) { - SymbolsKey key(array); + StringsKey key(array); Object* obj; { MaybeObject* maybe_obj = EnsureCapacity(1, &key); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -12455,10 +13209,11 @@ MaybeObject* MapCache::Put(FixedArray* array, Map* value) { template<typename Shape, typename Key> -MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) { +MaybeObject* Dictionary<Shape, Key>::Allocate(Heap* heap, + int at_least_space_for) { Object* obj; { MaybeObject* maybe_obj = - HashTable<Shape, Key>::Allocate(at_least_space_for); + HashTable<Shape, Key>::Allocate(heap, at_least_space_for); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } // Initialize the next enumeration index. @@ -12468,8 +13223,8 @@ MaybeObject* Dictionary<Shape, Key>::Allocate(int at_least_space_for) { } -void StringDictionary::DoGenerateNewEnumerationIndices( - Handle<StringDictionary> dictionary) { +void NameDictionary::DoGenerateNewEnumerationIndices( + Handle<NameDictionary> dictionary) { CALL_HEAP_FUNCTION_VOID(dictionary->GetIsolate(), dictionary->GenerateNewEnumerationIndices()); } @@ -12586,7 +13341,7 @@ MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) { } Object* k; - { MaybeObject* maybe_k = Shape::AsObject(key); + { MaybeObject* maybe_k = Shape::AsObject(this->GetHeap(), key); if (!maybe_k->ToObject(&k)) return maybe_k; } PropertyDetails details = PropertyDetails(NONE, NORMAL); @@ -12623,7 +13378,7 @@ MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key, uint32_t hash) { // Compute the key object. Object* k; - { MaybeObject* maybe_k = Shape::AsObject(key); + { MaybeObject* maybe_k = Shape::AsObject(this->GetHeap(), key); if (!maybe_k->ToObject(&k)) return maybe_k; } @@ -12639,8 +13394,8 @@ MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key, SetNextEnumerationIndex(index + 1); } SetEntry(entry, k, value, details); - ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber() - || Dictionary<Shape, Key>::KeyAt(entry)->IsString())); + ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber() || + Dictionary<Shape, Key>::KeyAt(entry)->IsName())); HashTable<Shape, Key>::ElementAdded(); return this; } @@ -12723,7 +13478,8 @@ MaybeObject* SeededNumberDictionary::Set(uint32_t key, details = PropertyDetails(details.attributes(), details.type(), DetailsAt(entry).dictionary_index()); - MaybeObject* maybe_object_key = SeededNumberDictionaryShape::AsObject(key); + MaybeObject* maybe_object_key = + SeededNumberDictionaryShape::AsObject(GetHeap(), key); Object* object_key; if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; SetEntry(entry, object_key, value, details); @@ -12735,7 +13491,8 @@ MaybeObject* UnseededNumberDictionary::Set(uint32_t key, Object* value) { int entry = FindEntry(key); if (entry == kNotFound) return AddNumberEntry(key, value); - MaybeObject* maybe_object_key = UnseededNumberDictionaryShape::AsObject(key); + MaybeObject* maybe_object_key = + UnseededNumberDictionaryShape::AsObject(GetHeap(), key); Object* object_key; if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key; SetEntry(entry, object_key, value); @@ -12751,7 +13508,8 @@ int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes( int result = 0; for (int i = 0; i < capacity; i++) { Object* k = HashTable<Shape, Key>::KeyAt(i); - if (HashTable<Shape, Key>::IsKey(k)) { + if (HashTable<Shape, Key>::IsKey(k) && + ((filter & SYMBOLIC) == 0 || !k->IsSymbol())) { PropertyDetails details = DetailsAt(i); if (details.IsDeleted()) continue; PropertyAttributes attr = details.attributes(); @@ -12793,7 +13551,7 @@ void Dictionary<Shape, Key>::CopyKeysTo( } -FixedArray* StringDictionary::CopyEnumKeysTo(FixedArray* storage) { +FixedArray* NameDictionary::CopyEnumKeysTo(FixedArray* storage) { int length = storage->length(); ASSERT(length >= NumberOfEnumElements()); Heap* heap = GetHeap(); @@ -12806,7 +13564,7 @@ FixedArray* StringDictionary::CopyEnumKeysTo(FixedArray* storage) { // that are deleted or not enumerable. for (int i = 0; i < capacity; i++) { Object* k = KeyAt(i); - if (IsKey(k)) { + if (IsKey(k) && !k->IsSymbol()) { PropertyDetails details = DetailsAt(i); if (details.IsDeleted() || details.IsDontEnum()) continue; properties++; @@ -12877,7 +13635,7 @@ Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) { } -MaybeObject* StringDictionary::TransformPropertiesToFastFor( +MaybeObject* NameDictionary::TransformPropertiesToFastFor( JSObject* obj, int unused_property_fields) { // Make sure we preserve dictionary representation if there are too many // descriptors. @@ -12903,8 +13661,7 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( PropertyType type = DetailsAt(i).type(); ASSERT(type != FIELD); instance_descriptor_length++; - if (type == NORMAL && - (!value->IsJSFunction() || heap->InNewSpace(value))) { + if (type == NORMAL && !value->IsJSFunction()) { number_of_fields += 1; } } @@ -12959,17 +13716,22 @@ MaybeObject* StringDictionary::TransformPropertiesToFastFor( Object* k = KeyAt(i); if (IsKey(k)) { Object* value = ValueAt(i); - // Ensure the key is a symbol before writing into the instance descriptor. - String* key; - MaybeObject* maybe_key = heap->LookupSymbol(String::cast(k)); - if (!maybe_key->To(&key)) return maybe_key; + Name* key; + if (k->IsSymbol()) { + key = Symbol::cast(k); + } else { + // Ensure the key is a unique name before writing into the + // instance descriptor. + MaybeObject* maybe_key = heap->InternalizeString(String::cast(k)); + if (!maybe_key->To(&key)) return maybe_key; + } PropertyDetails details = DetailsAt(i); ASSERT(details.descriptor_index() == details.dictionary_index()); int enumeration_index = details.descriptor_index(); PropertyType type = details.type(); - if (value->IsJSFunction() && !heap->InNewSpace(value)) { + if (value->IsJSFunction()) { ConstantFunctionDescriptor d(key, JSFunction::cast(value), details.attributes(), @@ -13141,6 +13903,58 @@ void ObjectHashTable::RemoveEntry(int entry) { } +DeclaredAccessorDescriptorIterator::DeclaredAccessorDescriptorIterator( + DeclaredAccessorDescriptor* descriptor) + : array_(descriptor->serialized_data()->GetDataStartAddress()), + length_(descriptor->serialized_data()->length()), + offset_(0) { +} + + +const DeclaredAccessorDescriptorData* + DeclaredAccessorDescriptorIterator::Next() { + ASSERT(offset_ < length_); + uint8_t* ptr = &array_[offset_]; + ASSERT(reinterpret_cast<uintptr_t>(ptr) % sizeof(uintptr_t) == 0); + const DeclaredAccessorDescriptorData* data = + reinterpret_cast<const DeclaredAccessorDescriptorData*>(ptr); + offset_ += sizeof(*data); + ASSERT(offset_ <= length_); + return data; +} + + +Handle<DeclaredAccessorDescriptor> DeclaredAccessorDescriptor::Create( + Isolate* isolate, + const DeclaredAccessorDescriptorData& descriptor, + Handle<DeclaredAccessorDescriptor> previous) { + int previous_length = + previous.is_null() ? 0 : previous->serialized_data()->length(); + int length = sizeof(descriptor) + previous_length; + Handle<ByteArray> serialized_descriptor = + isolate->factory()->NewByteArray(length); + Handle<DeclaredAccessorDescriptor> value = + isolate->factory()->NewDeclaredAccessorDescriptor(); + value->set_serialized_data(*serialized_descriptor); + // Copy in the data. + { + AssertNoAllocation no_allocation; + uint8_t* array = serialized_descriptor->GetDataStartAddress(); + if (previous_length != 0) { + uint8_t* previous_array = + previous->serialized_data()->GetDataStartAddress(); + memcpy(array, previous_array, previous_length); + array += previous_length; + } + ASSERT(reinterpret_cast<uintptr_t>(array) % sizeof(uintptr_t) == 0); + DeclaredAccessorDescriptorData* data = + reinterpret_cast<DeclaredAccessorDescriptorData*>(array); + *data = descriptor; + } + return value; +} + + #ifdef ENABLE_DEBUGGER_SUPPORT // Check if there is a break point at this code position. bool DebugInfo::HasBreakPoint(int code_position) { @@ -13169,7 +13983,8 @@ Object* DebugInfo::GetBreakPointInfo(int code_position) { void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info, int code_position, Handle<Object> break_point_object) { - Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position)); + Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position), + Isolate::Current()); if (break_point_info->IsUndefined()) return; BreakPointInfo::ClearBreakPoint( Handle<BreakPointInfo>::cast(break_point_info), @@ -13183,7 +13998,8 @@ void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info, int statement_position, Handle<Object> break_point_object) { Isolate* isolate = Isolate::Current(); - Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position)); + Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position), + isolate); if (!break_point_info->IsUndefined()) { BreakPointInfo::SetBreakPoint( Handle<BreakPointInfo>::cast(break_point_info), |