| Index: src/objects-inl.h | 
| diff --git a/src/objects-inl.h b/src/objects-inl.h | 
| index 0d83c4794666f1ed6e4b72bd1b74e8933e0d839b..33b8f0c1a952c00046ac7f8264e35c7cdbeb4229 100644 | 
| --- a/src/objects-inl.h | 
| +++ b/src/objects-inl.h | 
| @@ -177,10 +177,14 @@ bool Object::IsSymbol() { | 
|  | 
|  | 
| bool Object::IsConsString() { | 
| -  if (!this->IsHeapObject()) return false; | 
| -  uint32_t type = HeapObject::cast(this)->map()->instance_type(); | 
| -  return (type & (kIsNotStringMask | kStringRepresentationMask)) == | 
| -         (kStringTag | kConsStringTag); | 
| +  if (!IsString()) return false; | 
| +  return StringShape(String::cast(this)).IsCons(); | 
| +} | 
| + | 
| + | 
| +bool Object::IsSlicedString() { | 
| +  if (!IsString()) return false; | 
| +  return StringShape(String::cast(this)).IsSliced(); | 
| } | 
|  | 
|  | 
| @@ -268,6 +272,38 @@ bool String::IsTwoByteRepresentation() { | 
| } | 
|  | 
|  | 
| +bool String::IsAsciiRepresentationUnderneath() { | 
| +  uint32_t type = map()->instance_type(); | 
| +  STATIC_ASSERT(kIsIndirectStringTag != 0); | 
| +  STATIC_ASSERT((kIsIndirectStringMask & kStringEncodingMask) == 0); | 
| +  ASSERT(IsFlat()); | 
| +  switch (type & (kIsIndirectStringMask | kStringEncodingMask)) { | 
| +    case kAsciiStringTag: | 
| +      return true; | 
| +    case kTwoByteStringTag: | 
| +      return false; | 
| +    default:  // Cons or sliced string.  Need to go deeper. | 
| +      return GetUnderlying()->IsAsciiRepresentation(); | 
| +  } | 
| +} | 
| + | 
| + | 
| +bool String::IsTwoByteRepresentationUnderneath() { | 
| +  uint32_t type = map()->instance_type(); | 
| +  STATIC_ASSERT(kIsIndirectStringTag != 0); | 
| +  STATIC_ASSERT((kIsIndirectStringMask & kStringEncodingMask) == 0); | 
| +  ASSERT(IsFlat()); | 
| +  switch (type & (kIsIndirectStringMask | kStringEncodingMask)) { | 
| +    case kAsciiStringTag: | 
| +      return false; | 
| +    case kTwoByteStringTag: | 
| +      return true; | 
| +    default:  // Cons or sliced string.  Need to go deeper. | 
| +      return GetUnderlying()->IsTwoByteRepresentation(); | 
| +  } | 
| +} | 
| + | 
| + | 
| bool String::HasOnlyAsciiChars() { | 
| uint32_t type = map()->instance_type(); | 
| return (type & kStringEncodingMask) == kAsciiStringTag || | 
| @@ -280,6 +316,16 @@ bool StringShape::IsCons() { | 
| } | 
|  | 
|  | 
| +bool StringShape::IsSliced() { | 
| +  return (type_ & kStringRepresentationMask) == kSlicedStringTag; | 
| +} | 
| + | 
| + | 
| +bool StringShape::IsIndirect() { | 
| +  return (type_ & kIsIndirectStringMask) == kIsIndirectStringTag; | 
| +} | 
| + | 
| + | 
| bool StringShape::IsExternal() { | 
| return (type_ & kStringRepresentationMask) == kExternalStringTag; | 
| } | 
| @@ -2030,6 +2076,7 @@ CAST_ACCESSOR(String) | 
| CAST_ACCESSOR(SeqString) | 
| CAST_ACCESSOR(SeqAsciiString) | 
| CAST_ACCESSOR(SeqTwoByteString) | 
| +CAST_ACCESSOR(SlicedString) | 
| CAST_ACCESSOR(ConsString) | 
| CAST_ACCESSOR(ExternalString) | 
| CAST_ACCESSOR(ExternalAsciiString) | 
| @@ -2116,7 +2163,7 @@ bool String::Equals(String* other) { | 
| MaybeObject* String::TryFlatten(PretenureFlag pretenure) { | 
| if (!StringShape(this).IsCons()) return this; | 
| ConsString* cons = ConsString::cast(this); | 
| -  if (cons->second()->length() == 0) return cons->first(); | 
| +  if (cons->IsFlat()) return cons->first(); | 
| return SlowTryFlatten(pretenure); | 
| } | 
|  | 
| @@ -2124,10 +2171,8 @@ MaybeObject* String::TryFlatten(PretenureFlag pretenure) { | 
| String* String::TryFlattenGetString(PretenureFlag pretenure) { | 
| MaybeObject* flat = TryFlatten(pretenure); | 
| Object* successfully_flattened; | 
| -  if (flat->ToObject(&successfully_flattened)) { | 
| -    return String::cast(successfully_flattened); | 
| -  } | 
| -  return this; | 
| +  if (!flat->ToObject(&successfully_flattened)) return this; | 
| +  return String::cast(successfully_flattened); | 
| } | 
|  | 
|  | 
| @@ -2145,6 +2190,9 @@ uint16_t String::Get(int index) { | 
| return ExternalAsciiString::cast(this)->ExternalAsciiStringGet(index); | 
| case kExternalStringTag | kTwoByteStringTag: | 
| return ExternalTwoByteString::cast(this)->ExternalTwoByteStringGet(index); | 
| +    case kSlicedStringTag | kAsciiStringTag: | 
| +    case kSlicedStringTag | kTwoByteStringTag: | 
| +      return SlicedString::cast(this)->SlicedStringGet(index); | 
| default: | 
| break; | 
| } | 
| @@ -2165,15 +2213,19 @@ void String::Set(int index, uint16_t value) { | 
|  | 
|  | 
| bool String::IsFlat() { | 
| -  switch (StringShape(this).representation_tag()) { | 
| -    case kConsStringTag: { | 
| -      String* second = ConsString::cast(this)->second(); | 
| -      // Only flattened strings have second part empty. | 
| -      return second->length() == 0; | 
| -    } | 
| -    default: | 
| -      return true; | 
| -  } | 
| +  if (!StringShape(this).IsCons()) return true; | 
| +  return ConsString::cast(this)->second()->length() == 0; | 
| +} | 
| + | 
| + | 
| +String* String::GetUnderlying() { | 
| +  // Giving direct access to underlying string only makes sense if the | 
| +  // wrapping string is already flattened. | 
| +  ASSERT(this->IsFlat()); | 
| +  ASSERT(StringShape(this).IsIndirect()); | 
| +  STATIC_ASSERT(ConsString::kFirstOffset == SlicedString::kParentOffset); | 
| +  const int kUnderlyingOffset = SlicedString::kParentOffset; | 
| +  return String::cast(READ_FIELD(this, kUnderlyingOffset)); | 
| } | 
|  | 
|  | 
| @@ -2232,6 +2284,20 @@ int SeqAsciiString::SeqAsciiStringSize(InstanceType instance_type) { | 
| } | 
|  | 
|  | 
| +String* SlicedString::parent() { | 
| +  return String::cast(READ_FIELD(this, kParentOffset)); | 
| +} | 
| + | 
| + | 
| +void SlicedString::set_parent(String* parent) { | 
| +  ASSERT(parent->IsSeqString()); | 
| +  WRITE_FIELD(this, kParentOffset, parent); | 
| +} | 
| + | 
| + | 
| +SMI_ACCESSORS(SlicedString, offset, kOffsetOffset) | 
| + | 
| + | 
| String* ConsString::first() { | 
| return String::cast(READ_FIELD(this, kFirstOffset)); | 
| } | 
|  |