Update V8 to r3431 as required by WebKit r51976.
Change-Id: I567392c3f8c0a0d5201a4249611ac4ccf468cd5b
diff --git a/src/objects.cc b/src/objects.cc
index af1a0e5..0f8dca3 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -37,6 +37,7 @@
#include "scanner.h"
#include "scopeinfo.h"
#include "string-stream.h"
+#include "utils.h"
#ifdef ENABLE_DISASSEMBLER
#include "disassembler.h"
@@ -683,23 +684,6 @@
#endif
switch (StringShape(this).representation_tag()) {
- case kSlicedStringTag: {
- SlicedString* ss = SlicedString::cast(this);
- // The SlicedString constructor should ensure that there are no
- // SlicedStrings that are constructed directly on top of other
- // SlicedStrings.
- String* buf = ss->buffer();
- ASSERT(!buf->IsSlicedString());
- Object* ok = buf->TryFlatten();
- if (ok->IsFailure()) return ok;
- // Under certain circumstances (TryFlattenIfNotFlat fails in
- // String::Slice) we can have a cons string under a slice.
- // In this case we need to get the flat string out of the cons!
- if (StringShape(String::cast(ok)).IsCons()) {
- ss->set_buffer(ConsString::cast(ok)->first());
- }
- return this;
- }
case kConsStringTag: {
ConsString* cs = ConsString::cast(this);
if (cs->second()->length() == 0) {
@@ -771,19 +755,21 @@
ASSERT(size >= ExternalString::kSize);
bool is_symbol = this->IsSymbol();
int length = this->length();
+ int hash_field = this->hash_field();
// Morph the object to an external string by adjusting the map and
// reinitializing the fields.
- this->set_map(ExternalTwoByteString::StringMap(length));
+ this->set_map(Heap::external_string_map());
ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
self->set_length(length);
+ self->set_hash_field(hash_field);
self->set_resource(resource);
// Additionally make the object into an external symbol if the original string
// was a symbol to start with.
if (is_symbol) {
self->Hash(); // Force regeneration of the hash value.
// Now morph this external string into a external symbol.
- self->set_map(ExternalTwoByteString::SymbolMap(length));
+ this->set_map(Heap::external_symbol_map());
}
// Fill the remainder of the string with dead wood.
@@ -815,19 +801,21 @@
ASSERT(size >= ExternalString::kSize);
bool is_symbol = this->IsSymbol();
int length = this->length();
+ int hash_field = this->hash_field();
// Morph the object to an external string by adjusting the map and
// reinitializing the fields.
- this->set_map(ExternalAsciiString::StringMap(length));
+ this->set_map(Heap::external_ascii_string_map());
ExternalAsciiString* self = ExternalAsciiString::cast(this);
self->set_length(length);
+ self->set_hash_field(hash_field);
self->set_resource(resource);
// Additionally make the object into an external symbol if the original string
// was a symbol to start with.
if (is_symbol) {
self->Hash(); // Force regeneration of the hash value.
// Now morph this external string into a external symbol.
- self->set_map(ExternalAsciiString::SymbolMap(length));
+ this->set_map(Heap::external_ascii_symbol_map());
}
// Fill the remainder of the string with dead wood.
@@ -839,7 +827,7 @@
void String::StringShortPrint(StringStream* accumulator) {
int len = length();
- if (len > kMaxMediumStringSize) {
+ if (len > kMaxShortPrintLength) {
accumulator->Add("<Very long string[%u]>", len);
return;
}
@@ -1135,8 +1123,14 @@
case kConsStringTag:
reinterpret_cast<ConsString*>(this)->ConsStringIterateBody(v);
break;
- case kSlicedStringTag:
- reinterpret_cast<SlicedString*>(this)->SlicedStringIterateBody(v);
+ case kExternalStringTag:
+ if ((type & kStringEncodingMask) == kAsciiStringTag) {
+ reinterpret_cast<ExternalAsciiString*>(this)->
+ ExternalAsciiStringIterateBody(v);
+ } else {
+ reinterpret_cast<ExternalTwoByteString*>(this)->
+ ExternalTwoByteStringIterateBody(v);
+ }
break;
}
return;
@@ -1251,7 +1245,8 @@
String* JSObject::constructor_name() {
if (IsJSFunction()) {
- return Heap::function_class_symbol();
+ return JSFunction::cast(this)->IsBoilerplate() ?
+ Heap::function_class_symbol() : Heap::closure_symbol();
}
if (map()->constructor()->IsJSFunction()) {
JSFunction* constructor = JSFunction::cast(map()->constructor());
@@ -1473,8 +1468,8 @@
Object* JSObject::ReplaceSlowProperty(String* name,
- Object* value,
- PropertyAttributes attributes) {
+ Object* value,
+ PropertyAttributes attributes) {
StringDictionary* dictionary = property_dictionary();
int old_index = dictionary->FindEntry(name);
int new_enumeration_index = 0; // 0 means "Use the next available index."
@@ -1488,6 +1483,7 @@
return SetNormalizedProperty(name, value, new_details);
}
+
Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
String* name,
Object* new_value,
@@ -1879,6 +1875,14 @@
// 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
+ // reallocating them.
+ if (!name->IsSymbol() && name->length() <= 2) {
+ Object* symbol_version = Heap::LookupSymbol(name);
+ if (!symbol_version->IsFailure()) name = String::cast(symbol_version);
+ }
+
// Check access rights if needed.
if (IsAccessCheckNeeded()
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
@@ -2629,33 +2633,24 @@
// Tests for the fast common case for property enumeration:
-// - this object has an enum cache
-// - this object has no elements
-// - no prototype has enumerable properties/elements
-// - neither this object nor any prototype has interceptors
+// - This object and all prototypes has an enum cache (which means that it has
+// no interceptors and needs no access checks).
+// - This object has no elements.
+// - No prototype has enumerable properties/elements.
bool JSObject::IsSimpleEnum() {
- JSObject* arguments_boilerplate =
- Top::context()->global_context()->arguments_boilerplate();
- JSFunction* arguments_function =
- JSFunction::cast(arguments_boilerplate->map()->constructor());
- if (IsAccessCheckNeeded()) return false;
- if (map()->constructor() == arguments_function) return false;
-
for (Object* o = this;
o != Heap::null_value();
o = JSObject::cast(o)->GetPrototype()) {
JSObject* curr = JSObject::cast(o);
- if (!curr->HasFastProperties()) return false;
if (!curr->map()->instance_descriptors()->HasEnumCache()) return false;
+ ASSERT(!curr->HasNamedInterceptor());
+ ASSERT(!curr->HasIndexedInterceptor());
+ ASSERT(!curr->IsAccessCheckNeeded());
if (curr->NumberOfEnumElements() > 0) return false;
- if (curr->HasNamedInterceptor()) return false;
- if (curr->HasIndexedInterceptor()) return false;
if (curr != this) {
FixedArray* curr_fixed_array =
FixedArray::cast(curr->map()->instance_descriptors()->GetEnumCache());
- if (curr_fixed_array->length() > 0) {
- return false;
- }
+ if (curr_fixed_array->length() > 0) return false;
}
}
return true;
@@ -3561,12 +3556,7 @@
int length = this->length();
StringRepresentationTag string_tag = StringShape(this).representation_tag();
String* string = this;
- if (string_tag == kSlicedStringTag) {
- SlicedString* sliced = SlicedString::cast(string);
- offset += sliced->start();
- string = sliced->buffer();
- string_tag = StringShape(string).representation_tag();
- } else if (string_tag == kConsStringTag) {
+ if (string_tag == kConsStringTag) {
ConsString* cons = ConsString::cast(string);
ASSERT(cons->second()->length() == 0);
string = cons->first();
@@ -3592,12 +3582,7 @@
int length = this->length();
StringRepresentationTag string_tag = StringShape(this).representation_tag();
String* string = this;
- if (string_tag == kSlicedStringTag) {
- SlicedString* sliced = SlicedString::cast(string);
- offset += sliced->start();
- string = String::cast(sliced->buffer());
- string_tag = StringShape(string).representation_tag();
- } else if (string_tag == kConsStringTag) {
+ if (string_tag == kConsStringTag) {
ConsString* cons = ConsString::cast(string);
ASSERT(cons->second()->length() == 0);
string = cons->first();
@@ -3688,17 +3673,6 @@
case kExternalStringTag:
return ExternalTwoByteString::cast(this)->
ExternalTwoByteStringGetData(start);
- case kSlicedStringTag: {
- SlicedString* sliced_string = SlicedString::cast(this);
- String* buffer = sliced_string->buffer();
- if (StringShape(buffer).IsCons()) {
- ConsString* cs = ConsString::cast(buffer);
- // Flattened string.
- ASSERT(cs->second()->length() == 0);
- buffer = cs->first();
- }
- return buffer->GetTwoByteData(start + sliced_string->start());
- }
case kConsStringTag:
UNREACHABLE();
return NULL;
@@ -3853,22 +3827,6 @@
}
-const unibrow::byte* SlicedString::SlicedStringReadBlock(ReadBlockBuffer* rbb,
- unsigned* offset_ptr,
- unsigned max_chars) {
- String* backing = buffer();
- unsigned offset = start() + *offset_ptr;
- unsigned length = backing->length();
- if (max_chars > length - offset) {
- max_chars = length - offset;
- }
- const unibrow::byte* answer =
- String::ReadBlock(backing, rbb, &offset, max_chars);
- *offset_ptr = offset - start();
- return answer;
-}
-
-
uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
ASSERT(index >= 0 && index < length());
return resource()->data()[index];
@@ -3992,10 +3950,6 @@
return ConsString::cast(input)->ConsStringReadBlock(rbb,
offset_ptr,
max_chars);
- case kSlicedStringTag:
- return SlicedString::cast(input)->SlicedStringReadBlock(rbb,
- offset_ptr,
- max_chars);
case kExternalStringTag:
if (input->IsAsciiRepresentation()) {
return ExternalAsciiString::cast(input)->ExternalAsciiStringReadBlock(
@@ -4138,20 +4092,15 @@
offset_ptr,
max_chars);
return;
- case kSlicedStringTag:
- SlicedString::cast(input)->SlicedStringReadBlockIntoBuffer(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);
+ ExternalAsciiString::cast(input)->
+ ExternalAsciiStringReadBlockIntoBuffer(rbb, offset_ptr, max_chars);
+ } else {
+ ExternalTwoByteString::cast(input)->
+ ExternalTwoByteStringReadBlockIntoBuffer(rbb,
+ offset_ptr,
+ max_chars);
}
return;
default:
@@ -4257,20 +4206,6 @@
}
-void SlicedString::SlicedStringReadBlockIntoBuffer(ReadBlockBuffer* rbb,
- unsigned* offset_ptr,
- unsigned max_chars) {
- String* backing = buffer();
- unsigned offset = start() + *offset_ptr;
- unsigned length = backing->length();
- if (max_chars > length - offset) {
- max_chars = length - offset;
- }
- String::ReadBlockIntoBuffer(backing, rbb, &offset, max_chars);
- *offset_ptr = offset - start();
-}
-
-
void ConsString::ConsStringIterateBody(ObjectVisitor* v) {
IteratePointers(v, kFirstOffset, kSecondOffset + kPointerSize);
}
@@ -4349,15 +4284,6 @@
to - from);
return;
}
- case kAsciiStringTag | kSlicedStringTag:
- case kTwoByteStringTag | kSlicedStringTag: {
- SlicedString* sliced_string = SlicedString::cast(source);
- int start = sliced_string->start();
- from += start;
- to += start;
- source = String::cast(sliced_string->buffer());
- break;
- }
case kAsciiStringTag | kConsStringTag:
case kTwoByteStringTag | kConsStringTag: {
ConsString* cons_string = ConsString::cast(source);
@@ -4393,18 +4319,23 @@
}
-void SlicedString::SlicedStringIterateBody(ObjectVisitor* v) {
- IteratePointer(v, kBufferOffset);
+#define FIELD_ADDR(p, offset) \
+ (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
+
+void ExternalAsciiString::ExternalAsciiStringIterateBody(ObjectVisitor* v) {
+ typedef v8::String::ExternalAsciiStringResource Resource;
+ v->VisitExternalAsciiString(
+ reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
}
-uint16_t SlicedString::SlicedStringGet(int index) {
- ASSERT(index >= 0 && index < this->length());
- // Delegate to the buffer string.
- String* underlying = buffer();
- return underlying->Get(start() + index);
+void ExternalTwoByteString::ExternalTwoByteStringIterateBody(ObjectVisitor* v) {
+ typedef v8::String::ExternalStringResource Resource;
+ v->VisitExternalTwoByteString(
+ reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
}
+#undef FIELD_ADDR
template <typename IteratorA, typename IteratorB>
static inline bool CompareStringContents(IteratorA* ia, IteratorB* ib) {
@@ -4549,23 +4480,11 @@
if (StringShape(this).IsSymbol()) return false;
Map* map = this->map();
- if (map == Heap::short_string_map()) {
- this->set_map(Heap::undetectable_short_string_map());
+ if (map == Heap::string_map()) {
+ this->set_map(Heap::undetectable_string_map());
return true;
- } else if (map == Heap::medium_string_map()) {
- this->set_map(Heap::undetectable_medium_string_map());
- return true;
- } else if (map == Heap::long_string_map()) {
- this->set_map(Heap::undetectable_long_string_map());
- return true;
- } else if (map == Heap::short_ascii_string_map()) {
- this->set_map(Heap::undetectable_short_ascii_string_map());
- return true;
- } else if (map == Heap::medium_ascii_string_map()) {
- this->set_map(Heap::undetectable_medium_ascii_string_map());
- return true;
- } else if (map == Heap::long_ascii_string_map()) {
- this->set_map(Heap::undetectable_long_ascii_string_map());
+ } else if (map == Heap::ascii_string_map()) {
+ this->set_map(Heap::undetectable_ascii_string_map());
return true;
}
// Rest cannot be marked as undetectable
@@ -4588,17 +4507,17 @@
uint32_t String::ComputeAndSetHash() {
// Should only be called if hash code has not yet been computed.
- ASSERT(!(length_field() & kHashComputedMask));
+ ASSERT(!(hash_field() & kHashComputedMask));
// Compute the hash code.
StringInputBuffer buffer(this);
- uint32_t field = ComputeLengthAndHashField(&buffer, length());
+ uint32_t field = ComputeHashField(&buffer, length());
// Store the hash code in the object.
- set_length_field(field);
+ set_hash_field(field);
// Check the hash code is there.
- ASSERT(length_field() & kHashComputedMask);
+ ASSERT(hash_field() & kHashComputedMask);
uint32_t result = field >> kHashShift;
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
return result;
@@ -4638,9 +4557,10 @@
bool String::SlowAsArrayIndex(uint32_t* index) {
if (length() <= kMaxCachedArrayIndexLength) {
Hash(); // force computation of hash code
- uint32_t field = length_field();
+ uint32_t field = hash_field();
if ((field & kIsArrayIndexMask) == 0) return false;
- *index = (field & ((1 << kShortLengthShift) - 1)) >> kLongLengthShift;
+ // Isolate the array index form the full hash field.
+ *index = (kArrayIndexHashMask & field) >> kHashShift;
return true;
} else {
StringInputBuffer buffer(this);
@@ -4649,37 +4569,42 @@
}
-static inline uint32_t HashField(uint32_t hash, bool is_array_index) {
+static inline uint32_t HashField(uint32_t hash,
+ bool is_array_index,
+ int length = -1) {
uint32_t result =
- (hash << String::kLongLengthShift) | String::kHashComputedMask;
- if (is_array_index) result |= String::kIsArrayIndexMask;
+ (hash << String::kHashShift) | String::kHashComputedMask;
+ if (is_array_index) {
+ // For array indexes mix the length into the hash as an array index could
+ // be zero.
+ ASSERT(length > 0);
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ result |= String::kIsArrayIndexMask;
+ result |= length << String::kArrayIndexHashLengthShift;
+ }
return result;
}
uint32_t StringHasher::GetHashField() {
ASSERT(is_valid());
- if (length_ <= String::kMaxShortStringSize) {
- uint32_t payload;
+ if (length_ <= String::kMaxHashCalcLength) {
if (is_array_index()) {
- payload = v8::internal::HashField(array_index(), true);
+ return v8::internal::HashField(array_index(), true, length_);
} else {
- payload = v8::internal::HashField(GetHash(), false);
+ return v8::internal::HashField(GetHash(), false);
}
- return (payload & ((1 << String::kShortLengthShift) - 1)) |
- (length_ << String::kShortLengthShift);
- } else if (length_ <= String::kMaxMediumStringSize) {
uint32_t payload = v8::internal::HashField(GetHash(), false);
- return (payload & ((1 << String::kMediumLengthShift) - 1)) |
- (length_ << String::kMediumLengthShift);
+ return payload;
} else {
return v8::internal::HashField(length_, false);
}
}
-uint32_t String::ComputeLengthAndHashField(unibrow::CharacterStream* buffer,
- int length) {
+uint32_t String::ComputeHashField(unibrow::CharacterStream* buffer,
+ int length) {
StringHasher hasher(length);
// Very long strings have a trivial hash that doesn't inspect the
@@ -4704,43 +4629,10 @@
}
-Object* String::Slice(int start, int end) {
+Object* String::SubString(int start, int end) {
if (start == 0 && end == length()) return this;
- if (StringShape(this).representation_tag() == kSlicedStringTag) {
- // Translate slices of a SlicedString into slices of the
- // underlying string buffer.
- SlicedString* str = SlicedString::cast(this);
- String* buf = str->buffer();
- return Heap::AllocateSlicedString(buf,
- str->start() + start,
- str->start() + end);
- }
- Object* result = Heap::AllocateSlicedString(this, start, end);
- if (result->IsFailure()) {
- return result;
- }
- // Due to the way we retry after GC on allocation failure we are not allowed
- // to fail on allocation after this point. This is the one-allocation rule.
-
- // Try to flatten a cons string that is under the sliced string.
- // This is to avoid memory leaks and possible stack overflows caused by
- // building 'towers' of sliced strings on cons strings.
- // This may fail due to an allocation failure (when a GC is needed), but it
- // will succeed often enough to avoid the problem. We only have to do this
- // if Heap::AllocateSlicedString actually returned a SlicedString. It will
- // return flat strings for small slices for efficiency reasons.
- String* answer = String::cast(result);
- if (StringShape(answer).IsSliced() &&
- StringShape(this).representation_tag() == kConsStringTag) {
- TryFlatten();
- // If the flatten succeeded we might as well make the sliced string point
- // to the flat string rather than the cons string.
- String* second = ConsString::cast(this)->second();
- if (second->length() == 0) {
- SlicedString::cast(answer)->set_buffer(ConsString::cast(this)->first());
- }
- }
- return answer;
+ Object* result = Heap::AllocateSubString(this, start, end);
+ return result;
}
@@ -4920,13 +4812,9 @@
void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
- bool only_this_property_assignments,
bool only_simple_this_property_assignments,
FixedArray* assignments) {
set_compiler_hints(BooleanBit::set(compiler_hints(),
- kHasOnlyThisPropertyAssignments,
- only_this_property_assignments));
- set_compiler_hints(BooleanBit::set(compiler_hints(),
kHasOnlySimpleThisPropertyAssignments,
only_simple_this_property_assignments));
set_this_property_assignments(assignments);
@@ -4936,9 +4824,6 @@
void SharedFunctionInfo::ClearThisPropertyAssignmentsInfo() {
set_compiler_hints(BooleanBit::set(compiler_hints(),
- kHasOnlyThisPropertyAssignments,
- false));
- set_compiler_hints(BooleanBit::set(compiler_hints(),
kHasOnlySimpleThisPropertyAssignments,
false));
set_this_property_assignments(Heap::undefined_value());
@@ -4993,7 +4878,7 @@
return;
}
- // Get the slice of the source for this function.
+ // Get the source for the script which this function came from.
// Don't use String::cast because we don't want more assertion errors while
// we are already creating a stack dump.
String* script_source =
@@ -5082,7 +4967,7 @@
}
-void Code::Relocate(int delta) {
+void Code::Relocate(intptr_t delta) {
for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
it.rinfo()->apply(delta);
}
@@ -5148,8 +5033,9 @@
// Only look at positions after the current pc.
if (it.rinfo()->pc() < pc) {
// Get position and distance.
- int dist = pc - it.rinfo()->pc();
- int pos = it.rinfo()->data();
+
+ int dist = static_cast<int>(pc - it.rinfo()->pc());
+ int pos = static_cast<int>(it.rinfo()->data());
// If this position is closer than the current candidate or if it has the
// same distance as the current candidate and the position is higher then
// this position is the new candidate.
@@ -5176,7 +5062,7 @@
RelocIterator it(this, RelocInfo::kPositionMask);
while (!it.done()) {
if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
- int p = it.rinfo()->data();
+ int p = static_cast<int>(it.rinfo()->data());
if (statement_position < p && p <= position) {
statement_position = p;
}
@@ -5353,9 +5239,7 @@
Handle<JSArray> self(this);
Handle<FixedArray> old_backing(FixedArray::cast(elements()));
int old_size = old_backing->length();
- // Doubling in size would be overkill, but leave some slack to avoid
- // constantly growing.
- int new_size = required_size + (required_size >> 3);
+ int new_size = required_size > old_size ? required_size : old_size;
Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
// Can't use this any more now because we may have had a GC!
for (int i = 0; i < old_size; i++) new_backing->set(i, old_backing->get(i));
@@ -6284,6 +6168,18 @@
}
+Object* JSObject::GetLocalPropertyPostInterceptor(
+ JSObject* receiver,
+ String* name,
+ PropertyAttributes* attributes) {
+ // Check local property in holder, ignore interceptor.
+ LookupResult result;
+ LocalLookupRealNamedProperty(name, &result);
+ if (!result.IsValid()) return Heap::undefined_value();
+ return GetProperty(receiver, &result, name, attributes);
+}
+
+
Object* JSObject::GetPropertyWithInterceptor(
JSObject* receiver,
String* name,
@@ -6573,6 +6469,15 @@
int JSObject::NumberOfEnumElements() {
+ // Fast case for objects with no elements.
+ if (!IsJSValue() && HasFastElements()) {
+ uint32_t length = IsJSArray() ?
+ static_cast<uint32_t>(
+ Smi::cast(JSArray::cast(this)->length())->value()) :
+ static_cast<uint32_t>(FixedArray::cast(elements())->length());
+ if (length == 0) return 0;
+ }
+ // Compute the number of enumerable elements.
return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
}
@@ -6832,19 +6737,19 @@
class Utf8SymbolKey : public HashTableKey {
public:
explicit Utf8SymbolKey(Vector<const char> string)
- : string_(string), length_field_(0) { }
+ : string_(string), hash_field_(0) { }
bool IsMatch(Object* string) {
return String::cast(string)->IsEqualTo(string_);
}
uint32_t Hash() {
- if (length_field_ != 0) return length_field_ >> String::kHashShift;
+ if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
unibrow::Utf8InputBuffer<> buffer(string_.start(),
static_cast<unsigned>(string_.length()));
chars_ = buffer.Length();
- length_field_ = String::ComputeLengthAndHashField(&buffer, chars_);
- uint32_t result = length_field_ >> String::kHashShift;
+ hash_field_ = String::ComputeHashField(&buffer, chars_);
+ uint32_t result = hash_field_ >> String::kHashShift;
ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
return result;
}
@@ -6854,12 +6759,12 @@
}
Object* AsObject() {
- if (length_field_ == 0) Hash();
- return Heap::AllocateSymbol(string_, chars_, length_field_);
+ if (hash_field_ == 0) Hash();
+ return Heap::AllocateSymbol(string_, chars_, hash_field_);
}
Vector<const char> string_;
- uint32_t length_field_;
+ uint32_t hash_field_;
int chars_; // Caches the number of characters when computing the hash code.
};
@@ -6900,7 +6805,7 @@
StringInputBuffer buffer(string_);
return Heap::AllocateInternalSymbol(&buffer,
string_->length(),
- string_->length_field());
+ string_->hash_field());
}
static uint32_t StringHash(Object* obj) {
@@ -7429,6 +7334,67 @@
}
+// This class is used for looking up two character strings in the symbol 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)
+ : c1_(c1), c2_(c2) {
+ // Char 1.
+ uint32_t hash = c1 + (c1 << 10);
+ hash ^= hash >> 6;
+ // Char 2.
+ hash += c2;
+ hash += hash << 10;
+ hash ^= hash >> 6;
+ // GetHash.
+ hash += hash << 3;
+ hash ^= hash >> 11;
+ hash += hash << 15;
+ if (hash == 0) hash = 27;
+#ifdef DEBUG
+ StringHasher hasher(2);
+ 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));
+#endif
+ hash_ = hash;
+ }
+
+ bool IsMatch(Object* o) {
+ if (!o->IsString()) return false;
+ String* other = String::cast(o);
+ if (other->length() != 2) return false;
+ if (other->Get(0) != c1_) return false;
+ return other->Get(1) == c2_;
+ }
+
+ uint32_t Hash() { return hash_; }
+ uint32_t HashForObject(Object* key) {
+ if (!key->IsString()) return 0;
+ return String::cast(key)->Hash();
+ }
+
+ Object* AsObject() {
+ // The TwoCharHashTableKey is only used for looking in the symbol
+ // table, not for adding to it.
+ UNREACHABLE();
+ return NULL;
+ }
+ private:
+ uint32_t c1_;
+ uint32_t c2_;
+ uint32_t hash_;
+};
+
+
bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
SymbolKey key(string);
int entry = FindEntry(&key);
@@ -7443,6 +7409,22 @@
}
+bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
+ uint32_t c2,
+ String** symbol) {
+ TwoCharHashTableKey key(c1, c2);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) {
+ return false;
+ } else {
+ String* result = String::cast(KeyAt(entry));
+ ASSERT(StringShape(result).IsSymbol());
+ *symbol = result;
+ return true;
+ }
+}
+
+
Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
Utf8SymbolKey key(str);
return LookupKey(&key, s);