Improved string hash-code distribution by excluding bit-field bits from the hash-code.
Changed string search algorithm used in indexOf from KMP to Boyer-Moore.
Improved the generated code for the instanceof operator.
Improved performance of slow-case string equality checks by specializing the code based on the string representation.
Improve the handling of out-of-memory situations (issue 70).
Improved performance of strict equality checks.
Improved profiler output to make it easier to see anonymous functions.
Improved performance of slow-case keyed loads.
Improved property access performance by allocating a number of properties in the front object.
Changed the toString behavior on the built-in object constructors to print [native code] instead of the actual source. Some web applications do not like constructors with complex toString results.
git-svn-id: http://v8.googlecode.com/svn/trunk@511 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 2645363..ce2da12 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -119,13 +119,25 @@
}
-bool Object::IsAsciiString() {
- return IsString() && (String::cast(this)->is_ascii());
+bool Object::IsSeqAsciiString() {
+ return IsSeqString()
+ && String::cast(this)->IsAsciiRepresentation();
}
-bool Object::IsTwoByteString() {
- return IsString() && (!String::cast(this)->is_ascii());
+bool Object::IsSeqTwoByteString() {
+ return IsSeqString()
+ && !String::cast(this)->IsAsciiRepresentation();
+}
+
+
+bool Object::IsAsciiStringRepresentation() {
+ return IsString() && (String::cast(this)->is_ascii_representation());
+}
+
+
+bool Object::IsTwoByteStringRepresentation() {
+ return IsString() && (!String::cast(this)->is_ascii_representation());
}
@@ -148,12 +160,12 @@
bool Object::IsExternalAsciiString() {
- return IsExternalString() && (String::cast(this)->is_ascii());
+ return IsExternalString() && (String::cast(this)->is_ascii_representation());
}
bool Object::IsExternalTwoByteString() {
- return IsExternalString() && (!String::cast(this)->is_ascii());
+ return IsExternalString() && (!String::cast(this)->is_ascii_representation());
}
@@ -199,6 +211,12 @@
}
+bool Object::IsOutOfMemoryFailure() {
+ return HAS_FAILURE_TAG(this)
+ && Failure::cast(this)->IsOutOfMemoryException();
+}
+
+
bool Object::IsException() {
return this == Failure::Exception();
}
@@ -484,6 +502,12 @@
#define WRITE_INT_FIELD(p, offset, value) \
(*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value)
+#define READ_UINT32_FIELD(p, offset) \
+ (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset)))
+
+#define WRITE_UINT32_FIELD(p, offset, value) \
+ (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset)) = value)
+
#define READ_SHORT_FIELD(p, offset) \
(*reinterpret_cast<uint16_t*>(FIELD_ADDR(p, offset)))
@@ -603,23 +627,19 @@
bool MapWord::IsForwardingAddress() {
- // This function only works for map words that are heap object pointers.
- // Since it is a heap object, it has a map. We use that map's instance
- // type to detect if this map word is not actually a map (ie, it is a
- // forwarding address during a scavenge collection).
- return reinterpret_cast<HeapObject*>(value_)->map()->instance_type() !=
- MAP_TYPE;
+ return HAS_SMI_TAG(reinterpret_cast<Object*>(value_));
}
MapWord MapWord::FromForwardingAddress(HeapObject* object) {
- return MapWord(reinterpret_cast<uintptr_t>(object));
+ Address raw = reinterpret_cast<Address>(object) - kHeapObjectTag;
+ return MapWord(reinterpret_cast<uintptr_t>(raw));
}
HeapObject* MapWord::ToForwardingAddress() {
ASSERT(IsForwardingAddress());
- return reinterpret_cast<HeapObject*>(value_);
+ return HeapObject::FromAddress(reinterpret_cast<Address>(value_));
}
@@ -874,24 +894,64 @@
int JSObject::GetInternalFieldCount() {
ASSERT(1 << kPointerSizeLog2 == kPointerSize);
- return (Size() - GetHeaderSize()) >> kPointerSizeLog2;
+ // Make sure to adjust for the number of in-object properties. These
+ // properties do contribute to the size, but are not internal fields.
+ return ((Size() - GetHeaderSize()) >> kPointerSizeLog2) -
+ map()->inobject_properties();
}
Object* JSObject::GetInternalField(int index) {
ASSERT(index < GetInternalFieldCount() && index >= 0);
+ // Internal objects do follow immediately after the header, whereas in-object
+ // properties are at the end of the object. Therefore there is no need
+ // to adjust the index here.
return READ_FIELD(this, GetHeaderSize() + (kPointerSize * index));
}
void JSObject::SetInternalField(int index, Object* value) {
ASSERT(index < GetInternalFieldCount() && index >= 0);
+ // Internal objects do follow immediately after the header, whereas in-object
+ // properties are at the end of the object. Therefore there is no need
+ // to adjust the index here.
int offset = GetHeaderSize() + (kPointerSize * index);
WRITE_FIELD(this, offset, value);
WRITE_BARRIER(this, offset);
}
+// Access fast-case object properties at index. The use of these routines
+// is needed to correctly distinguish between properties stored in-object and
+// properties stored in the properties array.
+inline Object* JSObject::FastPropertyAt(int index) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ if (index < 0) {
+ int offset = map()->instance_size() + (index * kPointerSize);
+ return READ_FIELD(this, offset);
+ } else {
+ ASSERT(index < properties()->length());
+ return properties()->get(index);
+ }
+}
+
+
+inline Object* JSObject::FastPropertyAtPut(int index, Object* value) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ if (index < 0) {
+ int offset = map()->instance_size() + (index * kPointerSize);
+ WRITE_FIELD(this, offset, value);
+ WRITE_BARRIER(this, offset);
+ } else {
+ ASSERT(index < properties()->length());
+ properties()->set(index, value);
+ }
+ return value;
+}
+
+
void JSObject::InitializeBody(int object_size) {
for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
WRITE_FIELD(this, offset, Heap::undefined_value());
@@ -1120,8 +1180,8 @@
CAST_ACCESSOR(MapCache)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
-CAST_ACCESSOR(AsciiString)
-CAST_ACCESSOR(TwoByteString)
+CAST_ACCESSOR(SeqAsciiString)
+CAST_ACCESSOR(SeqTwoByteString)
CAST_ACCESSOR(ConsString)
CAST_ACCESSOR(SlicedString)
CAST_ACCESSOR(ExternalString)
@@ -1204,13 +1264,13 @@
}
-int String::length_field() {
- return READ_INT_FIELD(this, kLengthOffset);
+uint32_t String::length_field() {
+ return READ_UINT32_FIELD(this, kLengthOffset);
}
-void String::set_length_field(int value) {
- WRITE_INT_FIELD(this, kLengthOffset, value);
+void String::set_length_field(uint32_t value) {
+ WRITE_UINT32_FIELD(this, kLengthOffset, value);
}
@@ -1228,15 +1288,15 @@
ASSERT(index >= 0 && index < length());
switch (representation_tag()) {
case kSeqStringTag:
- return is_ascii()
- ? AsciiString::cast(this)->AsciiStringGet(index)
- : TwoByteString::cast(this)->TwoByteStringGet(index);
+ return is_ascii_representation()
+ ? SeqAsciiString::cast(this)->SeqAsciiStringGet(index)
+ : SeqTwoByteString::cast(this)->SeqTwoByteStringGet(index);
case kConsStringTag:
return ConsString::cast(this)->ConsStringGet(index);
case kSlicedStringTag:
return SlicedString::cast(this)->SlicedStringGet(index);
case kExternalStringTag:
- return is_ascii()
+ return is_ascii_representation()
? ExternalAsciiString::cast(this)->ExternalAsciiStringGet(index)
: ExternalTwoByteString::cast(this)->ExternalTwoByteStringGet(index);
default:
@@ -1252,14 +1312,14 @@
ASSERT(index >= 0 && index < length());
ASSERT(IsSeqString());
- return is_ascii()
- ? AsciiString::cast(this)->AsciiStringSet(index, value)
- : TwoByteString::cast(this)->TwoByteStringSet(index, value);
+ return is_ascii_representation()
+ ? SeqAsciiString::cast(this)->SeqAsciiStringSet(index, value)
+ : SeqTwoByteString::cast(this)->SeqTwoByteStringSet(index, value);
}
-bool String::IsAscii() {
- return is_ascii();
+bool String::IsAsciiRepresentation() {
+ return is_ascii_representation();
}
@@ -1293,12 +1353,12 @@
}
-bool String::is_ascii() {
- return is_ascii_map(map());
+bool String::is_ascii_representation() {
+ return is_ascii_representation_map(map());
}
-bool String::is_ascii_map(Map* map) {
+bool String::is_ascii_representation_map(Map* map) {
return (map->instance_type() & kStringEncodingMask) != 0;
}
@@ -1315,52 +1375,57 @@
bool String::IsFlat() {
- String* current = this;
- while (true) {
- switch (current->representation_tag()) {
- case kConsStringTag:
- return String::cast(ConsString::cast(current)->second())->length() == 0;
- case kSlicedStringTag:
- current = String::cast(SlicedString::cast(this)->buffer());
- break;
- default:
- return true;
+ switch (this->representation_tag()) {
+ case kConsStringTag:
+ // Only flattened strings have second part empty.
+ return String::cast(ConsString::cast(this)->second())->length() == 0;
+ case kSlicedStringTag: {
+ String* slice = String::cast(SlicedString::cast(this)->buffer());
+ StringRepresentationTag tag = slice->representation_tag();
+ return tag == kSeqStringTag || tag == kExternalStringTag;
}
+ default:
+ return true;
}
}
-uint16_t AsciiString::AsciiStringGet(int index) {
+uint16_t SeqAsciiString::SeqAsciiStringGet(int index) {
ASSERT(index >= 0 && index < length());
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
}
-void AsciiString::AsciiStringSet(int index, uint16_t value) {
+void SeqAsciiString::SeqAsciiStringSet(int index, uint16_t value) {
ASSERT(index >= 0 && index < length() && value <= kMaxAsciiCharCode);
WRITE_BYTE_FIELD(this, kHeaderSize + index * kCharSize,
static_cast<byte>(value));
}
-Address AsciiString::GetCharsAddress() {
+Address SeqAsciiString::GetCharsAddress() {
return FIELD_ADDR(this, kHeaderSize);
}
-uint16_t TwoByteString::TwoByteStringGet(int index) {
+Address SeqTwoByteString::GetCharsAddress() {
+ return FIELD_ADDR(this, kHeaderSize);
+}
+
+
+uint16_t SeqTwoByteString::SeqTwoByteStringGet(int index) {
ASSERT(index >= 0 && index < length());
return READ_SHORT_FIELD(this, kHeaderSize + index * kShortSize);
}
-void TwoByteString::TwoByteStringSet(int index, uint16_t value) {
+void SeqTwoByteString::SeqTwoByteStringSet(int index, uint16_t value) {
ASSERT(index >= 0 && index < length());
WRITE_SHORT_FIELD(this, kHeaderSize + index * kShortSize, value);
}
-int TwoByteString::TwoByteStringSize(Map* map) {
+int SeqTwoByteString::SeqTwoByteStringSize(Map* map) {
uint32_t length = READ_INT_FIELD(this, kLengthOffset);
// Use the map (and not 'this') to compute the size tag, since
@@ -1382,7 +1447,7 @@
}
-int AsciiString::AsciiStringSize(Map* map) {
+int SeqAsciiString::SeqAsciiStringSize(Map* map) {
uint32_t length = READ_INT_FIELD(this, kLengthOffset);
// Use the map (and not 'this') to compute the size tag, since
@@ -1500,7 +1565,12 @@
int Map::instance_size() {
- return READ_BYTE_FIELD(this, kInstanceSizeOffset);
+ return READ_BYTE_FIELD(this, kInstanceSizeOffset) << kPointerSizeLog2;
+}
+
+
+int Map::inobject_properties() {
+ return READ_BYTE_FIELD(this, kInObjectPropertiesOffset);
}
@@ -1517,11 +1587,19 @@
void Map::set_instance_size(int value) {
+ ASSERT((value & ~(kPointerSize - 1)) == value);
+ value >>= kPointerSizeLog2;
ASSERT(0 <= value && value < 256);
WRITE_BYTE_FIELD(this, kInstanceSizeOffset, static_cast<byte>(value));
}
+void Map::set_inobject_properties(int value) {
+ ASSERT(0 <= value && value < 256);
+ WRITE_BYTE_FIELD(this, kInObjectPropertiesOffset, static_cast<byte>(value));
+}
+
+
InstanceType Map::instance_type() {
return static_cast<InstanceType>(READ_BYTE_FIELD(this, kInstanceTypeOffset));
}
@@ -2070,16 +2148,75 @@
uint32_t String::Hash() {
// Fast case: has hash code already been computed?
- int hash = length_field();
- if (hash & kHashComputedMask) return hash;
+ uint32_t field = length_field();
+ if (field & kHashComputedMask) return field >> kHashShift;
// Slow case: compute hash code and set it..
return ComputeAndSetHash();
}
+StringHasher::StringHasher(int length)
+ : length_(length),
+ raw_running_hash_(0),
+ array_index_(0),
+ is_array_index_(0 < length_ && length_ <= String::kMaxArrayIndexSize),
+ is_first_char_(true),
+ is_valid_(true) { }
+
+
+bool StringHasher::has_trivial_hash() {
+ return length_ > String::kMaxMediumStringSize;
+}
+
+
+void StringHasher::AddCharacter(uc32 c) {
+ // Note: the Jenkins one-at-a-time hash function
+ raw_running_hash_ += c;
+ raw_running_hash_ += (raw_running_hash_ << 10);
+ raw_running_hash_ ^= (raw_running_hash_ >> 6);
+ // Incremental array index computation
+ if (is_array_index_) {
+ if (c < '0' || c > '9') {
+ is_array_index_ = false;
+ } else {
+ int d = c - '0';
+ if (is_first_char_) {
+ is_first_char_ = false;
+ if (c == '0' && length_ > 1) {
+ is_array_index_ = false;
+ return;
+ }
+ }
+ if (array_index_ > 429496729U - ((d + 2) >> 3)) {
+ is_array_index_ = false;
+ } else {
+ array_index_ = array_index_ * 10 + d;
+ }
+ }
+ }
+}
+
+
+void StringHasher::AddCharacterNoIndex(uc32 c) {
+ ASSERT(!is_array_index());
+ raw_running_hash_ += c;
+ raw_running_hash_ += (raw_running_hash_ << 10);
+ raw_running_hash_ ^= (raw_running_hash_ >> 6);
+}
+
+
+uint32_t StringHasher::GetHash() {
+ uint32_t result = raw_running_hash_;
+ result += (result << 3);
+ result ^= (result >> 11);
+ result += (result << 15);
+ return result;
+}
+
+
bool String::AsArrayIndex(uint32_t* index) {
- int hash = length_field();
- if ((hash & kHashComputedMask) && !(hash & kIsArrayIndexMask)) return false;
+ uint32_t field = length_field();
+ if ((field & kHashComputedMask) && !(field & kIsArrayIndexMask)) return false;
return SlowAsArrayIndex(index);
}
@@ -2152,6 +2289,12 @@
}
+void JSArray::SetContent(FixedArray* storage) {
+ set_length(Smi::FromInt(storage->length()));
+ set_elements(storage);
+}
+
+
#undef CAST_ACCESSOR
#undef INT_ACCESSORS
#undef SMI_ACCESSORS