| Index: runtime/vm/object.h | 
| diff --git a/runtime/vm/object.h b/runtime/vm/object.h | 
| index 3bb0676ad323a7c8eb9e4e5d12b43c3ea91f0d19..d43fbaaed9fa17c68c2444756a4262322f83ad24 100644 | 
| --- a/runtime/vm/object.h | 
| +++ b/runtime/vm/object.h | 
| @@ -432,6 +432,18 @@ class Object { | 
| return *transition_sentinel_; | 
| } | 
|  | 
| +#if defined(HASH_IN_OBJECT_HEADER) | 
| +  static uint32_t GetCachedHash(const RawObject* obj) { | 
| +    uword tags = obj->ptr()->tags_; | 
| +    return tags >> 32; | 
| +  } | 
| + | 
| +  static void SetCachedHash(RawObject* obj, uintptr_t hash) { | 
| +    ASSERT(hash >> 32 == 0); | 
| +    obj->ptr()->tags_ |= hash << 32; | 
| +  } | 
| +#endif | 
| + | 
| // Compiler's constant propagation constants. | 
| static const Instance& unknown_constant() { | 
| ASSERT(unknown_constant_ != NULL); | 
| @@ -6754,8 +6766,11 @@ class String : public Instance { | 
| // All strings share the same maximum element count to keep things | 
| // simple.  We choose a value that will prevent integer overflow for | 
| // 2 byte strings, since it is the worst case. | 
| -  static const intptr_t kSizeofRawString = | 
| -      sizeof(RawInstance) + (2 * kWordSize); | 
| +#if defined(HASH_IN_OBJECT_HEADER) | 
| +  static const intptr_t kSizeofRawString = sizeof(RawInstance) + kWordSize; | 
| +#else | 
| +  static const intptr_t kSizeofRawString = sizeof(RawInstance) + 2 * kWordSize; | 
| +#endif | 
| static const intptr_t kMaxElements = kSmiMax / kTwoByteChar; | 
|  | 
| class CodePointIterator : public ValueObject { | 
| @@ -6791,28 +6806,32 @@ class String : public Instance { | 
| static intptr_t length_offset() { return OFFSET_OF(RawString, length_); } | 
|  | 
| intptr_t Hash() const { | 
| -    intptr_t result = Smi::Value(raw_ptr()->hash_); | 
| +    intptr_t result = GetCachedHash(raw()); | 
| if (result != 0) { | 
| return result; | 
| } | 
| result = String::Hash(*this, 0, this->Length()); | 
| -    this->SetHash(result); | 
| +    SetCachedHash(raw(), result); | 
| return result; | 
| } | 
|  | 
| bool HasHash() const { | 
| ASSERT(Smi::New(0) == NULL); | 
| -    return (raw_ptr()->hash_ != NULL); | 
| +    return GetCachedHash(raw()) != 0; | 
| } | 
|  | 
| +#if defined(HASH_IN_OBJECT_HEADER) | 
| +  static intptr_t hash_offset() { return kInt32Size; }  // Wrong for big-endian? | 
| +#else | 
| static intptr_t hash_offset() { return OFFSET_OF(RawString, hash_); } | 
| +#endif | 
| static intptr_t Hash(const String& str, intptr_t begin_index, intptr_t len); | 
| static intptr_t Hash(const char* characters, intptr_t len); | 
| static intptr_t Hash(const uint16_t* characters, intptr_t len); | 
| static intptr_t Hash(const int32_t* characters, intptr_t len); | 
| static intptr_t HashRawSymbol(const RawString* symbol) { | 
| ASSERT(symbol->IsCanonical()); | 
| -    intptr_t result = Smi::Value(symbol->ptr()->hash_); | 
| +    intptr_t result = GetCachedHash(symbol); | 
| ASSERT(result != 0); | 
| return result; | 
| } | 
| @@ -7031,6 +7050,16 @@ class String : public Instance { | 
| intptr_t end, | 
| double* result); | 
|  | 
| +#if !defined(HASH_IN_OBJECT_HEADER) | 
| +  static uint32_t GetCachedHash(const RawString* obj) { | 
| +    return Smi::Value(obj->ptr()->hash_); | 
| +  } | 
| + | 
| +  static void SetCachedHash(RawString* obj, uintptr_t hash) { | 
| +    obj->ptr()->hash_ = Smi::New(hash); | 
| +  } | 
| +#endif | 
| + | 
| protected: | 
| // These two operate on an array of Latin-1 encoded characters. | 
| // They are protected to avoid mistaking Latin-1 for UTF-8, but used | 
| @@ -7044,11 +7073,7 @@ class String : public Instance { | 
| StoreSmi(&raw_ptr()->length_, Smi::New(value)); | 
| } | 
|  | 
| -  void SetHash(intptr_t value) const { | 
| -    // This is only safe because we create a new Smi, which does not cause | 
| -    // heap allocation. | 
| -    StoreSmi(&raw_ptr()->hash_, Smi::New(value)); | 
| -  } | 
| +  void SetHash(intptr_t value) const { SetCachedHash(raw(), value); } | 
|  | 
| template <typename HandleType, typename ElementType, typename CallbackType> | 
| static void ReadFromImpl(SnapshotReader* reader, | 
| @@ -7107,6 +7132,12 @@ class OneByteString : public AllStatic { | 
| static intptr_t InstanceSize(intptr_t len) { | 
| ASSERT(sizeof(RawOneByteString) == String::kSizeofRawString); | 
| ASSERT(0 <= len && len <= kMaxElements); | 
| +#if defined(HASH_IN_OBJECT_HEADER) | 
| +    // We have to pad zero-length raw strings so that they can be externalized. | 
| +    // If we don't pad, then the external string object does not fit in the | 
| +    // memory allocated for the raw string. | 
| +    if (len == 0) return InstanceSize(1); | 
| +#endif | 
| return String::RoundedAllocationSize(sizeof(RawOneByteString) + | 
| (len * kBytesPerElement)); | 
| } | 
| @@ -7240,6 +7271,10 @@ class TwoByteString : public AllStatic { | 
| static intptr_t InstanceSize(intptr_t len) { | 
| ASSERT(sizeof(RawTwoByteString) == String::kSizeofRawString); | 
| ASSERT(0 <= len && len <= kMaxElements); | 
| +    // We have to pad zero-length raw strings so that they can be externalized. | 
| +    // If we don't pad, then the external string object does not fit in the | 
| +    // memory allocated for the raw string. | 
| +    if (len == 0) return InstanceSize(1); | 
| return String::RoundedAllocationSize(sizeof(RawTwoByteString) + | 
| (len * kBytesPerElement)); | 
| } | 
| @@ -8933,7 +8968,7 @@ bool String::Equals(const String& str) const { | 
|  | 
|  | 
| intptr_t Library::UrlHash() const { | 
| -  intptr_t result = Smi::Value(url()->ptr()->hash_); | 
| +  intptr_t result = String::GetCachedHash(url()); | 
| ASSERT(result != 0); | 
| return result; | 
| } | 
|  |