Internalize strings in-place (reland^2)

using newly introduced ThinStrings, which store a pointer to the actual,
internalized string they represent.

BUG=v8:4520

(Previously landed as #42168 / af51befe69)
(Previously landed as #42193 / 4c699e349a)

Review-Url: https://codereview.chromium.org/2549773002
Cr-Commit-Position: refs/heads/master@{#42235}
This commit is contained in:
jkummerow 2017-01-11 06:59:35 -08:00 committed by Commit bot
parent 0befccd21b
commit ec45e6ed2e
61 changed files with 947 additions and 356 deletions

View File

@ -2313,7 +2313,7 @@ class V8_EXPORT String : public Name {
enum Encoding {
UNKNOWN_ENCODING = 0x1,
TWO_BYTE_ENCODING = 0x0,
ONE_BYTE_ENCODING = 0x4
ONE_BYTE_ENCODING = 0x8
};
/**
* Returns the number of characters in this string.
@ -8412,10 +8412,10 @@ class Internals {
static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
static const int kContextHeaderSize = 2 * kApiPointerSize;
static const int kContextEmbedderDataIndex = 5;
static const int kFullStringRepresentationMask = 0x07;
static const int kStringEncodingMask = 0x4;
static const int kFullStringRepresentationMask = 0x0f;
static const int kStringEncodingMask = 0x8;
static const int kExternalTwoByteRepresentationTag = 0x02;
static const int kExternalOneByteRepresentationTag = 0x06;
static const int kExternalOneByteRepresentationTag = 0x0a;
static const int kIsolateEmbedderDataOffset = 0 * kApiPointerSize;
static const int kExternalMemoryOffset = 4 * kApiPointerSize;

View File

@ -1298,7 +1298,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (6) External string. Make it, offset-wise, look like a sequential string.
// Go to (4).
// (7) Short external string or not a string? If yes, bail out to runtime.
// (8) Sliced string. Replace subject with parent. Go to (1).
// (8) Sliced or thin string. Replace subject with parent. Go to (1).
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
@ -1320,6 +1320,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (2) Sequential or cons? If not, go to (5).
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
__ cmp(r1, Operand(kExternalStringTag));
@ -1347,10 +1348,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ b(ls, &runtime);
__ SmiUntag(r1);
STATIC_ASSERT(4 == kOneByteStringTag);
STATIC_ASSERT(8 == kOneByteStringTag);
STATIC_ASSERT(kTwoByteStringTag == 0);
__ and_(r0, r0, Operand(kStringEncodingMask));
__ mov(r3, Operand(r0, ASR, 2), SetCC);
__ mov(r3, Operand(r0, ASR, 3), SetCC);
__ ldr(r6, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset),
ne);
__ ldr(r6, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
@ -1584,12 +1585,19 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ tst(r1, Operand(kIsNotStringMask | kShortExternalStringMask));
__ b(ne, &runtime);
// (8) Sliced string. Replace subject with parent. Go to (4).
// (8) Sliced or thin string. Replace subject with parent. Go to (4).
Label thin_string;
__ cmp(r1, Operand(kThinStringTag));
__ b(eq, &thin_string);
// Load offset into r9 and replace subject string with parent.
__ ldr(r9, FieldMemOperand(subject, SlicedString::kOffsetOffset));
__ SmiUntag(r9);
__ ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
__ jmp(&check_underlying); // Go to (4).
__ bind(&thin_string);
__ ldr(subject, FieldMemOperand(subject, ThinString::kActualOffset));
__ jmp(&check_underlying); // Go to (4).
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -322,6 +322,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Register index,
Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
@ -332,17 +335,24 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ b(eq, &check_sequential);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ tst(result, Operand(kSlicedNotConsMask));
Label cons_string, thin_string;
__ and_(result, result, Operand(kStringRepresentationMask));
__ cmp(result, Operand(kConsStringTag));
__ b(eq, &cons_string);
__ cmp(result, Operand(kThinStringTag));
__ b(eq, &thin_string);
// Handle slices.
Label indirect_string_loaded;
__ ldr(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
__ ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
__ add(index, index, Operand::SmiUntag(result));
__ jmp(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ ldr(string, FieldMemOperand(string, ThinString::kActualOffset));
__ jmp(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
@ -354,10 +364,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ b(ne, call_runtime);
// Get the first of the two strings and load its instance type.
__ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
__ jmp(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -1445,7 +1445,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (6) External string. Make it, offset-wise, look like a sequential string.
// Go to (4).
// (7) Short external string or not a string? If yes, bail out to runtime.
// (8) Sliced string. Replace subject with parent. Go to (1).
// (8) Sliced or thin string. Replace subject with parent. Go to (1).
Label check_underlying; // (1)
Label seq_string; // (4)
@ -1479,6 +1479,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (2) Sequential or cons? If not, go to (5).
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
__ Cmp(string_representation, kExternalStringTag);
@ -1506,10 +1507,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// before entering the exit frame.
__ SmiUntag(x1, x10);
// The third bit determines the string encoding in string_type.
STATIC_ASSERT(kOneByteStringTag == 0x04);
// The fourth bit determines the string encoding in string_type.
STATIC_ASSERT(kOneByteStringTag == 0x08);
STATIC_ASSERT(kTwoByteStringTag == 0x00);
STATIC_ASSERT(kStringEncodingMask == 0x04);
STATIC_ASSERT(kStringEncodingMask == 0x08);
// Find the code object based on the assumptions above.
// kDataOneByteCodeOffset and kDataUC16CodeOffset are adjacent, adds an offset
@ -1517,7 +1518,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(JSRegExp::kDataOneByteCodeOffset + kPointerSize ==
JSRegExp::kDataUC16CodeOffset);
__ Mov(x10, kPointerSize);
// We will need the encoding later: Latin1 = 0x04
// We will need the encoding later: Latin1 = 0x08
// UC16 = 0x00
__ Ands(string_encoding, string_type, kStringEncodingMask);
__ CzeroX(x10, ne);
@ -1565,10 +1566,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ Ldr(length, UntagSmiFieldMemOperand(subject, String::kLengthOffset));
// Handle UC16 encoding, two bytes make one character.
// string_encoding: if Latin1: 0x04
// string_encoding: if Latin1: 0x08
// if UC16: 0x00
STATIC_ASSERT(kStringEncodingMask == 0x04);
__ Ubfx(string_encoding, string_encoding, 2, 1);
STATIC_ASSERT(kStringEncodingMask == 0x08);
__ Ubfx(string_encoding, string_encoding, 3, 1);
__ Eor(string_encoding, string_encoding, 1);
// string_encoding: if Latin1: 0
// if UC16: 1
@ -1781,11 +1782,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
kShortExternalStringMask | kIsNotStringMask,
&runtime);
// (8) Sliced string. Replace subject with parent.
// (8) Sliced or thin string. Replace subject with parent.
Label thin_string;
__ Cmp(string_representation, kThinStringTag);
__ B(eq, &thin_string);
__ Ldr(sliced_string_offset,
UntagSmiFieldMemOperand(subject, SlicedString::kOffsetOffset));
__ Ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
__ B(&check_underlying); // Go to (1).
__ bind(&thin_string);
__ Ldr(subject, FieldMemOperand(subject, ThinString::kActualOffset));
__ B(&check_underlying); // Go to (1).
#endif
}

View File

@ -99,6 +99,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Register result,
Label* call_runtime) {
DCHECK(string.Is64Bits() && index.Is32Bits() && result.Is64Bits());
Label indirect_string_loaded;
__ Bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
@ -108,17 +111,25 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ TestAndBranchIfAllClear(result, kIsIndirectStringMask, &check_sequential);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ TestAndBranchIfAllClear(result, kSlicedNotConsMask, &cons_string);
Label cons_string, thin_string;
__ And(result, result, kStringRepresentationMask);
__ Cmp(result, kConsStringTag);
__ B(eq, &cons_string);
__ Cmp(result, kThinStringTag);
__ B(eq, &thin_string);
// Handle slices.
Label indirect_string_loaded;
__ Ldr(result.W(),
UntagSmiFieldMemOperand(string, SlicedString::kOffsetOffset));
__ Ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
__ Add(index, index, result.W());
__ B(&indirect_string_loaded);
// Handle thin strings.
__ Bind(&thin_string);
__ Ldr(string, FieldMemOperand(string, ThinString::kActualOffset));
__ B(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
@ -129,10 +140,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ JumpIfNotRoot(result, Heap::kempty_stringRootIndex, call_runtime);
// Get the first of the two strings and load its instance type.
__ Ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
__ Bind(&indirect_string_loaded);
__ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
__ B(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -157,6 +157,8 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
case ONE_BYTE_STRING_TYPE:
case CONS_STRING_TYPE:
case CONS_ONE_BYTE_STRING_TYPE:
case THIN_STRING_TYPE:
case THIN_ONE_BYTE_STRING_TYPE:
case SLICED_STRING_TYPE:
case SLICED_ONE_BYTE_STRING_TYPE:
case EXTERNAL_STRING_TYPE:

View File

@ -39,21 +39,24 @@ TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) {
Node* map = LoadMap(object);
Node* instance_type = LoadMapInstanceType(map);
Variable var_index(this, MachineType::PointerRepresentation());
{
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_unique(this, MachineRepresentation::kTagged);
Label keyisindex(this), if_iskeyunique(this);
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &call_runtime);
Label keyisindex(this), if_iskeyunique(this);
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &var_unique,
&call_runtime);
Bind(&if_iskeyunique);
TryHasOwnProperty(object, map, instance_type, key, &return_true,
&return_false, &call_runtime);
Bind(&keyisindex);
// Handle negative keys in the runtime.
GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)), &call_runtime);
TryLookupElement(object, map, instance_type, var_index.value(), &return_true,
&return_false, &call_runtime);
Bind(&if_iskeyunique);
TryHasOwnProperty(object, map, instance_type, var_unique.value(),
&return_true, &return_false, &call_runtime);
Bind(&keyisindex);
// Handle negative keys in the runtime.
GotoIf(IntPtrLessThan(var_index.value(), IntPtrConstant(0)), &call_runtime);
TryLookupElement(object, map, instance_type, var_index.value(),
&return_true, &return_false, &call_runtime);
}
Bind(&return_true);
Return(BooleanConstant(true));

View File

@ -1565,6 +1565,9 @@ Node* CodeStubAssembler::AllocateHeapNumberWithValue(Node* value,
Node* CodeStubAssembler::AllocateSeqOneByteString(int length,
AllocationFlags flags) {
Comment("AllocateSeqOneByteString");
if (length == 0) {
return LoadRoot(Heap::kempty_stringRootIndex);
}
Node* result = Allocate(SeqOneByteString::SizeFor(length), flags);
DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex));
StoreMapNoWriteBarrier(result, Heap::kOneByteStringMapRootIndex);
@ -1584,8 +1587,10 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
Variable var_result(this, MachineRepresentation::kTagged);
// Compute the SeqOneByteString size and check if it fits into new space.
Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
if_join(this);
Label if_lengthiszero(this), if_sizeissmall(this),
if_notsizeissmall(this, Label::kDeferred), if_join(this);
GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero);
Node* raw_size = GetArrayAllocationSize(
length, UINT8_ELEMENTS, mode,
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
@ -1618,6 +1623,12 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
Goto(&if_join);
}
Bind(&if_lengthiszero);
{
var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex));
Goto(&if_join);
}
Bind(&if_join);
return var_result.value();
}
@ -1625,6 +1636,9 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
Node* CodeStubAssembler::AllocateSeqTwoByteString(int length,
AllocationFlags flags) {
Comment("AllocateSeqTwoByteString");
if (length == 0) {
return LoadRoot(Heap::kempty_stringRootIndex);
}
Node* result = Allocate(SeqTwoByteString::SizeFor(length), flags);
DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex));
StoreMapNoWriteBarrier(result, Heap::kStringMapRootIndex);
@ -1644,8 +1658,10 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length,
Variable var_result(this, MachineRepresentation::kTagged);
// Compute the SeqTwoByteString size and check if it fits into new space.
Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
if_join(this);
Label if_lengthiszero(this), if_sizeissmall(this),
if_notsizeissmall(this, Label::kDeferred), if_join(this);
GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero);
Node* raw_size = GetArrayAllocationSize(
length, UINT16_ELEMENTS, mode,
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
@ -1680,6 +1696,12 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length,
Goto(&if_join);
}
Bind(&if_lengthiszero);
{
var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex));
Goto(&if_join);
}
Bind(&if_join);
return var_result.value();
}
@ -2982,7 +3004,7 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
// Translate the {index} into a Word.
index = ParameterToWord(index, parameter_mode);
// We may need to loop in case of cons or sliced strings.
// We may need to loop in case of cons, thin, or sliced strings.
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_result(this, MachineRepresentation::kWord32);
Variable var_string(this, MachineRepresentation::kTagged);
@ -3134,14 +3156,29 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
Bind(&if_stringisnotexternal);
{
// The {string} is a SlicedString, continue with its parent.
Node* string_offset =
LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
Node* string_parent =
LoadObjectField(string, SlicedString::kParentOffset);
var_index.Bind(IntPtrAdd(index, string_offset));
var_string.Bind(string_parent);
Goto(&loop);
Label if_stringissliced(this), if_stringisthin(this);
Branch(
Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringRepresentationMask)),
Int32Constant(kSlicedStringTag)),
&if_stringissliced, &if_stringisthin);
Bind(&if_stringissliced);
{
// The {string} is a SlicedString, continue with its parent.
Node* string_offset =
LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
Node* string_parent =
LoadObjectField(string, SlicedString::kParentOffset);
var_index.Bind(IntPtrAdd(index, string_offset));
var_string.Bind(string_parent);
Goto(&loop);
}
Bind(&if_stringisthin);
{
// The {string} is a ThinString, continue with its actual value.
var_string.Bind(LoadObjectField(string, ThinString::kActualOffset));
Goto(&loop);
}
}
}
}
@ -3272,11 +3309,13 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Label runtime(this);
Variable var_instance_type(this, MachineRepresentation::kWord32); // Int32.
Variable var_representation(this, MachineRepresentation::kWord32); // Int32.
Variable var_result(this, MachineRepresentation::kTagged); // String.
Variable var_from(this, MachineRepresentation::kTagged); // Smi.
Variable var_string(this, MachineRepresentation::kTagged); // String.
var_instance_type.Bind(Int32Constant(0));
var_representation.Bind(Int32Constant(0));
var_string.Bind(string);
var_from.Bind(from);
@ -3317,7 +3356,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// and put the underlying string into var_string.
// If the string is not indirect, it can only be sequential or external.
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
STATIC_ASSERT(kIsIndirectStringMask ==
(kSlicedStringTag & kConsStringTag & kThinStringTag));
STATIC_ASSERT(kIsIndirectStringMask != 0);
Label underlying_unpacked(this);
GotoIf(Word32Equal(
@ -3325,13 +3365,14 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Int32Constant(0)),
&underlying_unpacked);
// The subject string is either a sliced or cons string.
// The subject string is a sliced, cons, or thin string.
Label sliced_string(this);
GotoIf(Word32NotEqual(
Word32And(instance_type, Int32Constant(kSlicedNotConsMask)),
Int32Constant(0)),
&sliced_string);
Label sliced_string(this), thin_or_sliced(this);
var_representation.Bind(
Word32And(instance_type, Int32Constant(kStringRepresentationMask)));
GotoIf(
Word32NotEqual(var_representation.value(), Int32Constant(kConsStringTag)),
&thin_or_sliced);
// Cons string. Check whether it is flat, then fetch first part.
// Flat cons strings have an empty second part.
@ -3343,14 +3384,33 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Node* first_string_part = LoadObjectField(string, ConsString::kFirstOffset);
var_string.Bind(first_string_part);
var_instance_type.Bind(LoadInstanceType(first_string_part));
var_representation.Bind(Word32And(
var_instance_type.value(), Int32Constant(kStringRepresentationMask)));
// The loaded first part might be a thin string.
Branch(Word32Equal(Word32And(var_instance_type.value(),
Int32Constant(kIsIndirectStringMask)),
Int32Constant(0)),
&underlying_unpacked, &thin_or_sliced);
}
Bind(&thin_or_sliced);
{
GotoIf(Word32Equal(var_representation.value(),
Int32Constant(kSlicedStringTag)),
&sliced_string);
Node* actual_string =
LoadObjectField(var_string.value(), ThinString::kActualOffset);
var_string.Bind(actual_string);
var_instance_type.Bind(LoadInstanceType(actual_string));
Goto(&underlying_unpacked);
}
Bind(&sliced_string);
{
// Fetch parent and correct start index by offset.
Node* sliced_offset = LoadObjectField(string, SlicedString::kOffsetOffset);
Node* sliced_offset =
LoadObjectField(var_string.value(), SlicedString::kOffsetOffset);
var_from.Bind(SmiAdd(from, sliced_offset));
Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset);
@ -4123,45 +4183,6 @@ Node* CodeStubAssembler::ToString(Node* context, Node* input) {
return result.value();
}
Node* CodeStubAssembler::FlattenString(Node* string) {
CSA_ASSERT(this, IsString(string));
Variable var_result(this, MachineRepresentation::kTagged);
var_result.Bind(string);
Node* instance_type = LoadInstanceType(string);
// Check if the {string} is not a ConsString (i.e. already flat).
Label is_cons(this, Label::kDeferred), is_flat_in_cons(this), end(this);
{
GotoUnless(Word32Equal(Word32And(instance_type,
Int32Constant(kStringRepresentationMask)),
Int32Constant(kConsStringTag)),
&end);
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string).
Node* rhs = LoadObjectField(string, ConsString::kSecondOffset);
Branch(WordEqual(rhs, EmptyStringConstant()), &is_flat_in_cons, &is_cons);
}
// Bail out to the runtime.
Bind(&is_cons);
{
var_result.Bind(
CallRuntime(Runtime::kFlattenString, NoContextConstant(), string));
Goto(&end);
}
Bind(&is_flat_in_cons);
{
var_result.Bind(LoadObjectField(string, ConsString::kFirstOffset));
Goto(&end);
}
Bind(&end);
return var_result.value();
}
Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) {
Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this);
Variable result(this, MachineRepresentation::kTagged);
@ -4303,17 +4324,19 @@ void CodeStubAssembler::Use(Label* label) {
void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
Variable* var_index, Label* if_keyisunique,
Label* if_bailout) {
Variable* var_unique, Label* if_bailout) {
DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_unique->rep());
Comment("TryToName");
Label if_hascachedindex(this), if_keyisnotindex(this);
Label if_hascachedindex(this), if_keyisnotindex(this), if_thinstring(this);
// Handle Smi and HeapNumber keys.
var_index->Bind(TryToIntptr(key, &if_keyisnotindex));
Goto(if_keyisindex);
Bind(&if_keyisnotindex);
Node* key_map = LoadMap(key);
var_unique->Bind(key);
// Symbols are unique.
GotoIf(IsSymbolMap(key_map), if_keyisunique);
Node* key_instance_type = LoadMapInstanceType(key_map);
@ -4330,6 +4353,12 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
Node* not_an_index =
Word32And(hash, Int32Constant(Name::kIsNotArrayIndexMask));
GotoIf(Word32Equal(not_an_index, Int32Constant(0)), if_bailout);
// Check if we have a ThinString.
GotoIf(Word32Equal(key_instance_type, Int32Constant(THIN_STRING_TYPE)),
&if_thinstring);
GotoIf(
Word32Equal(key_instance_type, Int32Constant(THIN_ONE_BYTE_STRING_TYPE)),
&if_thinstring);
// Finally, check if |key| is internalized.
STATIC_ASSERT(kNotInternalizedTag != 0);
Node* not_internalized =
@ -4337,6 +4366,10 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), if_bailout);
Goto(if_keyisunique);
Bind(&if_thinstring);
var_unique->Bind(LoadObjectField(key, ThinString::kActualOffset));
Goto(if_keyisunique);
Bind(&if_hascachedindex);
var_index->Bind(DecodeWordFromWord32<Name::ArrayIndexValueBits>(hash));
Goto(if_keyisindex);
@ -5186,9 +5219,11 @@ void CodeStubAssembler::TryPrototypeChainLookup(
}
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_unique(this, MachineRepresentation::kTagged);
Label if_keyisindex(this), if_iskeyunique(this);
TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, if_bailout);
TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, &var_unique,
if_bailout);
Bind(&if_iskeyunique);
{
@ -5210,8 +5245,8 @@ void CodeStubAssembler::TryPrototypeChainLookup(
Label next_proto(this);
lookup_property_in_holder(receiver, var_holder.value(), holder_map,
holder_instance_type, key, &next_proto,
if_bailout);
holder_instance_type, var_unique.value(),
&next_proto, if_bailout);
Bind(&next_proto);
// Bailout if it can be an integer indexed exotic case.

View File

@ -729,9 +729,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Convert any object to a Primitive.
Node* JSReceiverToPrimitive(Node* context, Node* input);
// Convert a String to a flat String.
Node* FlattenString(Node* string);
enum ToIntegerTruncationMode {
kNoTruncation,
kTruncateMinusZero,
@ -844,7 +841,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Various building blocks for stubs doing property lookups.
void TryToName(Node* key, Label* if_keyisindex, Variable* var_index,
Label* if_keyisunique, Label* if_bailout);
Label* if_keyisunique, Variable* var_unique,
Label* if_bailout);
// Calculates array index for given dictionary entry and entry field.
// See Dictionary::EntryToIndex().

View File

@ -73,7 +73,7 @@ void CodeStubDescriptor::Initialize(Register stack_parameter_count,
bool CodeStub::FindCodeInCache(Code** code_out) {
UnseededNumberDictionary* stubs = isolate()->heap()->code_stubs();
int index = stubs->FindEntry(GetKey());
int index = stubs->FindEntry(isolate(), GetKey());
if (index != UnseededNumberDictionary::kNotFound) {
*code_out = Code::cast(stubs->ValueAt(index));
return true;

View File

@ -502,6 +502,15 @@ FieldAccess AccessBuilder::ForConsStringSecond() {
return access;
}
// static
FieldAccess AccessBuilder::ForThinStringActual() {
FieldAccess access = {kTaggedBase, ThinString::kActualOffset,
Handle<Name>(), MaybeHandle<Map>(),
Type::String(), MachineType::TaggedPointer(),
kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForSlicedStringOffset() {
FieldAccess access = {kTaggedBase, SlicedString::kOffsetOffset,

View File

@ -167,6 +167,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to ConsString::second() field.
static FieldAccess ForConsStringSecond();
// Provides access to ThinString::actual() field.
static FieldAccess ForThinStringActual();
// Provides access to SlicedString::offset() field.
static FieldAccess ForSlicedStringOffset();

View File

@ -152,6 +152,8 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case ONE_BYTE_STRING_TYPE:
case CONS_STRING_TYPE:
case CONS_ONE_BYTE_STRING_TYPE:
case THIN_STRING_TYPE:
case THIN_ONE_BYTE_STRING_TYPE:
case SLICED_STRING_TYPE:
case SLICED_ONE_BYTE_STRING_TYPE:
case EXTERNAL_STRING_TYPE:

View File

@ -187,7 +187,7 @@ static void CopyDictionaryToObjectElements(
: SKIP_WRITE_BARRIER;
Isolate* isolate = from->GetIsolate();
for (int i = 0; i < copy_size; i++) {
int entry = from->FindEntry(i + from_start);
int entry = from->FindEntry(isolate, i + from_start);
if (entry != SeededNumberDictionary::kNotFound) {
Object* value = from->ValueAt(entry);
DCHECK(!value->IsTheHole(isolate));
@ -417,8 +417,9 @@ static void CopyDictionaryToDoubleElements(FixedArrayBase* from_base,
if (to_start + copy_size > to_length) {
copy_size = to_length - to_start;
}
Isolate* isolate = from->GetIsolate();
for (int i = 0; i < copy_size; i++) {
int entry = from->FindEntry(i + from_start);
int entry = from->FindEntry(isolate, i + from_start);
if (entry != SeededNumberDictionary::kNotFound) {
to->set(i + to_start, from->ValueAt(entry)->Number());
} else {
@ -1628,7 +1629,7 @@ class DictionaryElementsAccessor
// Iterate through entire range, as accessing elements out of order is
// observable
for (uint32_t k = start_from; k < length; ++k) {
int entry = dictionary->FindEntry(k);
int entry = dictionary->FindEntry(isolate, k);
if (entry == SeededNumberDictionary::kNotFound) {
if (search_for_hole) return Just(true);
continue;
@ -1694,7 +1695,7 @@ class DictionaryElementsAccessor
// Iterate through entire range, as accessing elements out of order is
// observable.
for (uint32_t k = start_from; k < length; ++k) {
int entry = dictionary->FindEntry(k);
int entry = dictionary->FindEntry(isolate, k);
if (entry == SeededNumberDictionary::kNotFound) {
continue;
}

View File

@ -278,6 +278,7 @@ Handle<String> Factory::InternalizeStringWithKey(StringTableKey* key) {
MaybeHandle<String> Factory::NewStringFromOneByte(Vector<const uint8_t> string,
PretenureFlag pretenure) {
int length = string.length();
if (length == 0) return empty_string();
if (length == 1) return LookupSingleCharacterStringFromCode(string[0]);
Handle<SeqOneByteString> result;
ASSIGN_RETURN_ON_EXCEPTION(
@ -371,6 +372,7 @@ MaybeHandle<String> Factory::NewStringFromUtf8SubString(
MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string,
int length,
PretenureFlag pretenure) {
if (length == 0) return empty_string();
if (String::IsOneByte(string, length)) {
if (length == 1) return LookupSingleCharacterStringFromCode(string[0]);
Handle<SeqOneByteString> result;
@ -455,38 +457,63 @@ Handle<String> Factory::NewInternalizedStringImpl(
String);
}
namespace {
MaybeHandle<Map> GetInternalizedStringMap(Factory* f, Handle<String> string) {
switch (string->map()->instance_type()) {
case STRING_TYPE:
return f->internalized_string_map();
case ONE_BYTE_STRING_TYPE:
return f->one_byte_internalized_string_map();
case EXTERNAL_STRING_TYPE:
return f->external_internalized_string_map();
case EXTERNAL_ONE_BYTE_STRING_TYPE:
return f->external_one_byte_internalized_string_map();
case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
return f->external_internalized_string_with_one_byte_data_map();
case SHORT_EXTERNAL_STRING_TYPE:
return f->short_external_internalized_string_map();
case SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE:
return f->short_external_one_byte_internalized_string_map();
case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
return f->short_external_internalized_string_with_one_byte_data_map();
default: return MaybeHandle<Map>(); // No match found.
}
}
} // namespace
MaybeHandle<Map> Factory::InternalizedStringMapForString(
Handle<String> string) {
// If the string is in new space it cannot be used as internalized.
if (isolate()->heap()->InNewSpace(*string)) return MaybeHandle<Map>();
// Find the corresponding internalized string map for strings.
switch (string->map()->instance_type()) {
case STRING_TYPE: return internalized_string_map();
case ONE_BYTE_STRING_TYPE:
return one_byte_internalized_string_map();
case EXTERNAL_STRING_TYPE: return external_internalized_string_map();
case EXTERNAL_ONE_BYTE_STRING_TYPE:
return external_one_byte_internalized_string_map();
case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
return external_internalized_string_with_one_byte_data_map();
case SHORT_EXTERNAL_STRING_TYPE:
return short_external_internalized_string_map();
case SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE:
return short_external_one_byte_internalized_string_map();
case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
return short_external_internalized_string_with_one_byte_data_map();
default: return MaybeHandle<Map>(); // No match found.
}
return GetInternalizedStringMap(this, string);
}
template <class StringClass>
Handle<StringClass> Factory::InternalizeExternalString(Handle<String> string) {
Handle<StringClass> cast_string = Handle<StringClass>::cast(string);
Handle<Map> map = GetInternalizedStringMap(this, string).ToHandleChecked();
Handle<StringClass> external_string = New<StringClass>(map, OLD_SPACE);
external_string->set_length(cast_string->length());
external_string->set_hash_field(cast_string->hash_field());
external_string->set_resource(nullptr);
isolate()->heap()->RegisterExternalString(*external_string);
return external_string;
}
template Handle<ExternalOneByteString>
Factory::InternalizeExternalString<ExternalOneByteString>(Handle<String>);
template Handle<ExternalTwoByteString>
Factory::InternalizeExternalString<ExternalTwoByteString>(Handle<String>);
MaybeHandle<SeqOneByteString> Factory::NewRawOneByteString(
int length, PretenureFlag pretenure) {
if (length > String::kMaxLength || length < 0) {
THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqOneByteString);
}
DCHECK(length > 0); // Use Factory::empty_string() instead.
CALL_HEAP_FUNCTION(
isolate(),
isolate()->heap()->AllocateRawOneByteString(length, pretenure),
@ -499,6 +526,7 @@ MaybeHandle<SeqTwoByteString> Factory::NewRawTwoByteString(
if (length > String::kMaxLength || length < 0) {
THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqTwoByteString);
}
DCHECK(length > 0); // Use Factory::empty_string() instead.
CALL_HEAP_FUNCTION(
isolate(),
isolate()->heap()->AllocateRawTwoByteString(length, pretenure),
@ -587,6 +615,12 @@ Handle<String> ConcatStringContent(Handle<StringType> result,
MaybeHandle<String> Factory::NewConsString(Handle<String> left,
Handle<String> right) {
if (left->IsThinString()) {
left = handle(Handle<ThinString>::cast(left)->actual(), isolate());
}
if (right->IsThinString()) {
right = handle(Handle<ThinString>::cast(right)->actual(), isolate());
}
int left_length = left->length();
if (left_length == 0) return right;
int right_length = right->length();

View File

@ -227,6 +227,11 @@ class V8_EXPORT_PRIVATE Factory final {
MUST_USE_RESULT MaybeHandle<Map> InternalizedStringMapForString(
Handle<String> string);
// Creates an internalized copy of an external string. |string| must be
// of type StringClass.
template <class StringClass>
Handle<StringClass> InternalizeExternalString(Handle<String> string);
// Allocates and partially initializes an one-byte or two-byte String. The
// characters of the string are uninitialized. Currently used in regexp code
// only, where they are pretenured.

View File

@ -225,6 +225,8 @@ AllocationResult Heap::AllocateInternalizedStringImpl(T t, int chars,
AllocationResult Heap::AllocateOneByteInternalizedString(
Vector<const uint8_t> str, uint32_t hash_field) {
CHECK_GE(String::kMaxLength, str.length());
// The canonical empty_string is the only zero-length string we allow.
DCHECK_IMPLIES(str.length() == 0, roots_[kempty_stringRootIndex] == nullptr);
// Compute map and object size.
Map* map = one_byte_internalized_string_map();
int size = SeqOneByteString::SizeFor(str.length());
@ -256,6 +258,7 @@ AllocationResult Heap::AllocateOneByteInternalizedString(
AllocationResult Heap::AllocateTwoByteInternalizedString(Vector<const uc16> str,
uint32_t hash_field) {
CHECK_GE(String::kMaxLength, str.length());
DCHECK_NE(0, str.length()); // Use Heap::empty_string() instead.
// Compute map and object size.
Map* map = internalized_string_map();
int size = SeqTwoByteString::SizeFor(str.length());

View File

@ -1738,12 +1738,21 @@ String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap,
if (!first_word.IsForwardingAddress()) {
// Unreachable external string can be finalized.
heap->FinalizeExternalString(String::cast(*p));
String* string = String::cast(*p);
if (!string->IsExternalString()) {
// Original external string has been internalized.
DCHECK(string->IsThinString());
return NULL;
}
heap->FinalizeExternalString(string);
return NULL;
}
// String is still reachable.
return String::cast(first_word.ToForwardingAddress());
String* string = String::cast(first_word.ToForwardingAddress());
if (string->IsThinString()) string = ThinString::cast(string)->actual();
// Internalization can replace external strings with non-external strings.
return string->IsExternalString() ? string : nullptr;
}
@ -6413,14 +6422,19 @@ void Heap::ExternalStringTable::CleanUpNewSpaceStrings() {
int last = 0;
Isolate* isolate = heap_->isolate();
for (int i = 0; i < new_space_strings_.length(); ++i) {
if (new_space_strings_[i]->IsTheHole(isolate)) {
Object* o = new_space_strings_[i];
if (o->IsTheHole(isolate)) {
continue;
}
DCHECK(new_space_strings_[i]->IsExternalString());
if (heap_->InNewSpace(new_space_strings_[i])) {
new_space_strings_[last++] = new_space_strings_[i];
if (o->IsThinString()) {
o = ThinString::cast(o)->actual();
if (!o->IsExternalString()) continue;
}
DCHECK(o->IsExternalString());
if (heap_->InNewSpace(o)) {
new_space_strings_[last++] = o;
} else {
old_space_strings_.Add(new_space_strings_[i]);
old_space_strings_.Add(o);
}
}
new_space_strings_.Rewind(last);
@ -6432,12 +6446,17 @@ void Heap::ExternalStringTable::CleanUpAll() {
int last = 0;
Isolate* isolate = heap_->isolate();
for (int i = 0; i < old_space_strings_.length(); ++i) {
if (old_space_strings_[i]->IsTheHole(isolate)) {
Object* o = old_space_strings_[i];
if (o->IsTheHole(isolate)) {
continue;
}
DCHECK(old_space_strings_[i]->IsExternalString());
DCHECK(!heap_->InNewSpace(old_space_strings_[i]));
old_space_strings_[last++] = old_space_strings_[i];
if (o->IsThinString()) {
o = ThinString::cast(o)->actual();
if (!o->IsExternalString()) continue;
}
DCHECK(o->IsExternalString());
DCHECK(!heap_->InNewSpace(o));
old_space_strings_[last++] = o;
}
old_space_strings_.Rewind(last);
old_space_strings_.Trim();
@ -6450,11 +6469,21 @@ void Heap::ExternalStringTable::CleanUpAll() {
void Heap::ExternalStringTable::TearDown() {
for (int i = 0; i < new_space_strings_.length(); ++i) {
heap_->FinalizeExternalString(ExternalString::cast(new_space_strings_[i]));
Object* o = new_space_strings_[i];
if (o->IsThinString()) {
o = ThinString::cast(o)->actual();
if (!o->IsExternalString()) continue;
}
heap_->FinalizeExternalString(ExternalString::cast(o));
}
new_space_strings_.Free();
for (int i = 0; i < old_space_strings_.length(); ++i) {
heap_->FinalizeExternalString(ExternalString::cast(old_space_strings_[i]));
Object* o = old_space_strings_[i];
if (o->IsThinString()) {
o = ThinString::cast(o)->actual();
if (!o->IsExternalString()) continue;
}
heap_->FinalizeExternalString(ExternalString::cast(o));
}
old_space_strings_.Free();
}

View File

@ -101,6 +101,8 @@ using v8::MemoryPressureLevel;
V(Map, string_map, StringMap) \
V(Map, cons_one_byte_string_map, ConsOneByteStringMap) \
V(Map, cons_string_map, ConsStringMap) \
V(Map, thin_one_byte_string_map, ThinOneByteStringMap) \
V(Map, thin_string_map, ThinStringMap) \
V(Map, sliced_string_map, SlicedStringMap) \
V(Map, sliced_one_byte_string_map, SlicedOneByteStringMap) \
V(Map, external_string_map, ExternalStringMap) \
@ -2131,10 +2133,6 @@ class Heap {
MUST_USE_RESULT AllocationResult
AllocateCode(int object_size, bool immovable);
MUST_USE_RESULT AllocationResult InternalizeStringWithKey(HashTableKey* key);
MUST_USE_RESULT AllocationResult InternalizeString(String* str);
// ===========================================================================
void set_force_oom(bool value) { force_oom_ = value; }

View File

@ -1437,8 +1437,12 @@ class StringTableCleaner : public ObjectVisitor {
if (o->IsHeapObject()) {
if (Marking::IsWhite(ObjectMarking::MarkBitFrom(HeapObject::cast(o)))) {
if (finalize_external_strings) {
DCHECK(o->IsExternalString());
heap_->FinalizeExternalString(String::cast(*p));
if (o->IsExternalString()) {
heap_->FinalizeExternalString(String::cast(*p));
} else {
// The original external string may have been internalized.
DCHECK(o->IsThinString());
}
} else {
pointers_removed_++;
}

View File

@ -31,6 +31,10 @@ void StaticNewSpaceVisitor<StaticVisitor>::Initialize() {
kVisitConsString,
&FixedBodyVisitor<StaticVisitor, ConsString::BodyDescriptor, int>::Visit);
table_.Register(
kVisitThinString,
&FixedBodyVisitor<StaticVisitor, ThinString::BodyDescriptor, int>::Visit);
table_.Register(kVisitSlicedString,
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
int>::Visit);
@ -117,6 +121,10 @@ void StaticMarkingVisitor<StaticVisitor>::Initialize() {
&FixedBodyVisitor<StaticVisitor, ConsString::BodyDescriptor,
void>::Visit);
table_.Register(kVisitThinString,
&FixedBodyVisitor<StaticVisitor, ThinString::BodyDescriptor,
void>::Visit);
table_.Register(kVisitSlicedString,
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
void>::Visit);

View File

@ -41,6 +41,9 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case kExternalStringTag:
return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric,
instance_size, has_unboxed_fields);
case kThinStringTag:
return kVisitThinString;
}
UNREACHABLE();
}

View File

@ -79,6 +79,7 @@ class StaticVisitorBase : public AllStatic {
V(StructGeneric) \
V(ConsString) \
V(SlicedString) \
V(ThinString) \
V(Symbol) \
V(Oddball) \
V(Code) \

View File

@ -30,6 +30,7 @@ class ScavengingVisitor : public StaticVisitorBase {
table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString);
table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString);
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
table_.Register(kVisitThinString, &EvacuateThinString);
table_.Register(kVisitByteArray, &EvacuateByteArray);
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray);
@ -89,6 +90,12 @@ class ScavengingVisitor : public StaticVisitorBase {
return &table_;
}
static void EvacuateThinStringNoShortcut(Map* map, HeapObject** slot,
HeapObject* object) {
EvacuateObject<POINTER_OBJECT, kWordAligned>(map, slot, object,
ThinString::kSize);
}
private:
enum ObjectContents { DATA_OBJECT, POINTER_OBJECT };
@ -339,6 +346,22 @@ class ScavengingVisitor : public StaticVisitorBase {
object_size);
}
static inline void EvacuateThinString(Map* map, HeapObject** slot,
HeapObject* object) {
if (marks_handling == IGNORE_MARKS) {
HeapObject* actual = ThinString::cast(object)->actual();
*slot = actual;
// ThinStrings always refer to internalized strings, which are
// always in old space.
DCHECK(!map->GetHeap()->InNewSpace(actual));
object->set_map_word(MapWord::FromForwardingAddress(actual));
return;
}
EvacuateObject<POINTER_OBJECT, kWordAligned>(map, slot, object,
ThinString::kSize);
}
template <ObjectContents object_contents>
class ObjectEvacuationStrategy {
public:
@ -423,6 +446,10 @@ void Scavenger::SelectScavengingVisitorsTable() {
StaticVisitorBase::kVisitShortcutCandidate,
scavenging_visitors_table_.GetVisitorById(
StaticVisitorBase::kVisitConsString));
scavenging_visitors_table_.Register(
StaticVisitorBase::kVisitThinString,
&ScavengingVisitor<TRANSFER_MARKS, LOGGING_AND_PROFILING_DISABLED>::
EvacuateThinStringNoShortcut);
}
}
}

View File

@ -604,7 +604,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (8) Is the external string one byte? If yes, go to (5).
// (9) Two byte sequential. Load regexp code for two byte. Go to (E).
// (10) Short external string or not a string? If yes, bail out to runtime.
// (11) Sliced string. Replace subject with parent. Go to (1).
// (11) Sliced or thin string. Replace subject with parent. Go to (1).
Label seq_one_byte_string /* 5 */, seq_two_byte_string /* 9 */,
external_string /* 7 */, check_underlying /* 1 */,
@ -634,6 +634,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// have already been covered.
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
__ cmp(ebx, Immediate(kExternalStringTag));
@ -912,11 +913,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
__ j(not_zero, &runtime);
// (11) Sliced string. Replace subject with parent. Go to (1).
// (11) Sliced or thin string. Replace subject with parent. Go to (1).
Label thin_string;
__ cmp(ebx, Immediate(kThinStringTag));
__ j(equal, &thin_string, Label::kNear);
// Load offset into edi and replace subject string with parent.
__ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
__ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
__ jmp(&check_underlying); // Go to (1).
__ bind(&thin_string);
__ mov(eax, FieldOperand(eax, ThinString::kActualOffset));
__ jmp(&check_underlying); // Go to (1).
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -491,6 +491,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Register index,
Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
@ -501,17 +504,24 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ j(zero, &check_sequential, Label::kNear);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ test(result, Immediate(kSlicedNotConsMask));
__ j(zero, &cons_string, Label::kNear);
Label cons_string, thin_string;
__ and_(result, Immediate(kStringRepresentationMask));
__ cmp(result, Immediate(kConsStringTag));
__ j(equal, &cons_string, Label::kNear);
__ cmp(result, Immediate(kThinStringTag));
__ j(equal, &thin_string, Label::kNear);
// Handle slices.
Label indirect_string_loaded;
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
__ SmiUntag(result);
__ add(index, result);
__ mov(string, FieldOperand(string, SlicedString::kParentOffset));
__ jmp(&indirect_string_loaded, Label::kNear);
__ jmp(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ mov(string, FieldOperand(string, ThinString::kActualOffset));
__ jmp(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
@ -523,10 +533,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Immediate(factory->empty_string()));
__ j(not_equal, call_runtime);
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
__ jmp(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -2566,11 +2566,13 @@ void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(Register object1,
const int kFlatOneByteStringTag =
kStringTag | kOneByteStringTag | kSeqStringTag;
// Interleave bits from both instance types and compare them in one check.
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << 3));
const int kShift = 8;
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << kShift));
and_(scratch1, kFlatOneByteStringMask);
and_(scratch2, kFlatOneByteStringMask);
lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << 3));
shl(scratch2, kShift);
or_(scratch1, scratch2);
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << kShift));
j(not_equal, failure);
}

View File

@ -1394,6 +1394,8 @@ void AccessorAssemblerImpl::KeyedLoadIC(const LoadICParameters* p) {
void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_unique(this, MachineRepresentation::kTagged);
var_unique.Bind(p->name); // Dummy initialization.
Variable var_details(this, MachineRepresentation::kWord32);
Variable var_value(this, MachineRepresentation::kTagged);
Label if_index(this), if_unique_name(this), if_element_hole(this),
@ -1410,8 +1412,8 @@ void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
&slow);
Node* key = p->name;
TryToName(key, &if_index, &var_index, &if_unique_name, &slow);
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
&slow);
Bind(&if_index);
{
@ -1460,6 +1462,7 @@ void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
Bind(&if_unique_name);
{
Comment("key is unique name");
Node* key = var_unique.value();
// Check if the receiver has fast or slow properties.
properties = LoadProperties(receiver);
Node* properties_map = LoadMap(properties);
@ -1518,6 +1521,7 @@ void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
// We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
// seeing global objects here (which would need special handling).
Node* key = var_unique.value();
Variable var_name_index(this, MachineType::PointerRepresentation());
Label dictionary_found(this, &var_name_index);
NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found,

View File

@ -1578,6 +1578,8 @@ static Handle<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
}
} else if (key->IsUndefined(isolate)) {
key = isolate->factory()->undefined_string();
} else if (key->IsString()) {
key = isolate->factory()->InternalizeString(Handle<String>::cast(key));
}
return key;
}

View File

@ -461,6 +461,8 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore(
// Out-of-capacity accesses (index >= capacity) jump here. Additionally,
// an ElementsKind transition might be necessary.
// The index can also be negative at this point! Jump to the runtime in that
// case to convert it to a named property.
Bind(&if_grow);
{
Comment("Grow backing store");
@ -756,6 +758,8 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
Node* context = Parameter(Descriptor::kContext);
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_unique(this, MachineRepresentation::kTagged);
var_unique.Bind(name); // Dummy initialization.
Label if_index(this), if_unique_name(this), slow(this);
GotoIf(TaggedIsSmi(receiver), &slow);
@ -767,7 +771,7 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
&slow);
TryToName(name, &if_index, &var_index, &if_unique_name, &slow);
TryToName(name, &if_index, &var_index, &if_unique_name, &var_unique, &slow);
Bind(&if_index);
{
@ -779,8 +783,8 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
Bind(&if_unique_name);
{
Comment("key is unique name");
KeyedStoreGenericAssembler::StoreICParameters p(context, receiver, name,
value, slot, vector);
StoreICParameters p(context, receiver, var_unique.value(), value, slot,
vector);
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow, language_mode);
}

View File

@ -1423,7 +1423,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (6) External string. Make it, offset-wise, look like a sequential string.
// Go to (4).
// (7) Short external string or not a string? If yes, bail out to runtime.
// (8) Sliced string. Replace subject with parent. Go to (1).
// (8) Sliced or thin string. Replace subject with parent. Go to (1).
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
@ -1444,6 +1444,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (2) Sequential or cons? If not, go to (5).
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
// Go to (5).
@ -1470,8 +1471,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ Branch(&runtime, ls, a3, Operand(a1));
__ sra(a1, a1, kSmiTagSize); // Untag the Smi.
STATIC_ASSERT(kStringEncodingMask == 4);
STATIC_ASSERT(kOneByteStringTag == 4);
STATIC_ASSERT(kStringEncodingMask == 8);
STATIC_ASSERT(kOneByteStringTag == 8);
STATIC_ASSERT(kTwoByteStringTag == 0);
__ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for one-byte.
__ lw(t9, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset));
@ -1720,12 +1721,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask));
__ Branch(&runtime, ne, at, Operand(zero_reg));
// (8) Sliced string. Replace subject with parent. Go to (4).
// (8) Sliced or thin string. Replace subject with parent. Go to (4).
Label thin_string;
__ Branch(&thin_string, eq, a1, Operand(kThinStringTag));
// Load offset into t0 and replace subject string with parent.
__ lw(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset));
__ sra(t0, t0, kSmiTagSize);
__ lw(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
__ jmp(&check_underlying); // Go to (4).
__ bind(&thin_string);
__ lw(subject, FieldMemOperand(subject, ThinString::kActualOffset));
__ jmp(&check_underlying); // Go to (4).
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -610,6 +610,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Register index,
Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ lw(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
@ -620,18 +623,23 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ Branch(&check_sequential, eq, at, Operand(zero_reg));
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ And(at, result, Operand(kSlicedNotConsMask));
__ Branch(&cons_string, eq, at, Operand(zero_reg));
Label cons_string, thin_string;
__ And(at, result, Operand(kStringRepresentationMask));
__ Branch(&cons_string, eq, at, Operand(kConsStringTag));
__ Branch(&thin_string, eq, at, Operand(kThinStringTag));
// Handle slices.
Label indirect_string_loaded;
__ lw(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
__ lw(string, FieldMemOperand(string, SlicedString::kParentOffset));
__ sra(at, result, kSmiTagSize);
__ Addu(index, index, at);
__ jmp(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ lw(string, FieldMemOperand(string, ThinString::kActualOffset));
__ jmp(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
@ -643,10 +651,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ Branch(call_runtime, ne, result, Operand(at));
// Get the first of the two strings and load its instance type.
__ lw(string, FieldMemOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ lw(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
__ jmp(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -1420,7 +1420,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (6) External string. Make it, offset-wise, look like a sequential string.
// Go to (4).
// (7) Short external string or not a string? If yes, bail out to runtime.
// (8) Sliced string. Replace subject with parent. Go to (1).
// (8) Sliced or thin string. Replace subject with parent. Go to (1).
Label check_underlying; // (1)
Label seq_string; // (4)
@ -1444,6 +1444,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (2) Sequential or cons? If not, go to (5).
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
// Go to (5).
@ -1470,8 +1471,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ Branch(&runtime, ls, a3, Operand(a1));
__ SmiUntag(a1);
STATIC_ASSERT(kStringEncodingMask == 4);
STATIC_ASSERT(kOneByteStringTag == 4);
STATIC_ASSERT(kStringEncodingMask == 8);
STATIC_ASSERT(kOneByteStringTag == 8);
STATIC_ASSERT(kTwoByteStringTag == 0);
__ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for one_byte.
__ ld(t9, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset));
@ -1721,12 +1722,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask));
__ Branch(&runtime, ne, at, Operand(zero_reg));
// (8) Sliced string. Replace subject with parent. Go to (4).
// (8) Sliced or thin string. Replace subject with parent. Go to (4).
Label thin_string;
__ Branch(&thin_string, eq, a1, Operand(kThinStringTag));
// Load offset into t0 and replace subject string with parent.
__ ld(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset));
__ SmiUntag(t0);
__ ld(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
__ jmp(&check_underlying); // Go to (1).
__ bind(&thin_string);
__ ld(subject, FieldMemOperand(subject, ThinString::kActualOffset));
__ jmp(&check_underlying); // Go to (1).
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -612,6 +612,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Register index,
Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ ld(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
@ -622,18 +625,23 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ Branch(&check_sequential, eq, at, Operand(zero_reg));
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ And(at, result, Operand(kSlicedNotConsMask));
__ Branch(&cons_string, eq, at, Operand(zero_reg));
Label cons_string, thin_string;
__ And(at, result, Operand(kStringRepresentationMask));
__ Branch(&cons_string, eq, at, Operand(kConsStringTag));
__ Branch(&thin_string, eq, at, Operand(kThinStringTag));
// Handle slices.
Label indirect_string_loaded;
__ ld(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
__ ld(string, FieldMemOperand(string, SlicedString::kParentOffset));
__ dsra32(at, result, 0);
__ Daddu(index, index, at);
__ jmp(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ ld(string, FieldMemOperand(string, ThinString::kActualOffset));
__ jmp(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
@ -645,10 +653,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ Branch(call_runtime, ne, result, Operand(at));
// Get the first of the two strings and load its instance type.
__ ld(string, FieldMemOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ ld(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
__ jmp(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -440,6 +440,8 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3) {
return ReturnType();
case kConsStringTag:
return Op::template apply<ConsString::BodyDescriptor>(p1, p2, p3);
case kThinStringTag:
return Op::template apply<ThinString::BodyDescriptor>(p1, p2, p3);
case kSlicedStringTag:
return Op::template apply<SlicedString::BodyDescriptor>(p1, p2, p3);
case kExternalStringTag:

View File

@ -555,6 +555,8 @@ void String::StringVerify() {
ConsString::cast(this)->ConsStringVerify();
} else if (IsSlicedString()) {
SlicedString::cast(this)->SlicedStringVerify();
} else if (IsThinString()) {
ThinString::cast(this)->ThinStringVerify();
}
}
@ -566,12 +568,17 @@ void ConsString::ConsStringVerify() {
CHECK(this->length() >= ConsString::kMinLength);
CHECK(this->length() == this->first()->length() + this->second()->length());
if (this->IsFlat()) {
// A flat cons can only be created by String::SlowTryFlatten.
// Afterwards, the first part may be externalized.
CHECK(this->first()->IsSeqString() || this->first()->IsExternalString());
// A flat cons can only be created by String::SlowFlatten.
// Afterwards, the first part may be externalized or internalized.
CHECK(this->first()->IsSeqString() || this->first()->IsExternalString() ||
this->first()->IsThinString());
}
}
void ThinString::ThinStringVerify() {
CHECK(this->actual()->IsInternalizedString());
CHECK(this->actual()->IsSeqString() || this->actual()->IsExternalString());
}
void SlicedString::SlicedStringVerify() {
CHECK(!this->parent()->IsConsString());

View File

@ -275,6 +275,11 @@ bool HeapObject::IsConsString() const {
return StringShape(String::cast(this)).IsCons();
}
bool HeapObject::IsThinString() const {
if (!IsString()) return false;
return StringShape(String::cast(this)).IsThin();
}
bool HeapObject::IsSlicedString() const {
if (!IsString()) return false;
return StringShape(String::cast(this)).IsSliced();
@ -698,6 +703,7 @@ CAST_ACCESSOR(StringTable)
CAST_ACCESSOR(Struct)
CAST_ACCESSOR(Symbol)
CAST_ACCESSOR(TemplateInfo)
CAST_ACCESSOR(ThinString)
CAST_ACCESSOR(Uint16x8)
CAST_ACCESSOR(Uint32x4)
CAST_ACCESSOR(Uint8x16)
@ -844,6 +850,10 @@ bool StringShape::IsCons() {
return (type_ & kStringRepresentationMask) == kConsStringTag;
}
bool StringShape::IsThin() {
return (type_ & kStringRepresentationMask) == kThinStringTag;
}
bool StringShape::IsSliced() {
return (type_ & kStringRepresentationMask) == kSlicedStringTag;
}
@ -3700,10 +3710,19 @@ bool String::Equals(Handle<String> one, Handle<String> two) {
Handle<String> String::Flatten(Handle<String> string, PretenureFlag pretenure) {
if (!string->IsConsString()) return string;
Handle<ConsString> cons = Handle<ConsString>::cast(string);
if (cons->IsFlat()) return handle(cons->first());
return SlowFlatten(cons, pretenure);
if (string->IsConsString()) {
Handle<ConsString> cons = Handle<ConsString>::cast(string);
if (cons->IsFlat()) {
string = handle(cons->first());
} else {
return SlowFlatten(cons, pretenure);
}
}
if (string->IsThinString()) {
string = handle(Handle<ThinString>::cast(string)->actual());
DCHECK(!string->IsConsString());
}
return string;
}
@ -3724,6 +3743,9 @@ uint16_t String::Get(int index) {
case kSlicedStringTag | kOneByteStringTag:
case kSlicedStringTag | kTwoByteStringTag:
return SlicedString::cast(this)->SlicedStringGet(index);
case kThinStringTag | kOneByteStringTag:
case kThinStringTag | kTwoByteStringTag:
return ThinString::cast(this)->ThinStringGet(index);
default:
break;
}
@ -3755,6 +3777,7 @@ String* String::GetUnderlying() {
DCHECK(this->IsFlat());
DCHECK(StringShape(this).IsIndirect());
STATIC_ASSERT(ConsString::kFirstOffset == SlicedString::kParentOffset);
STATIC_ASSERT(ConsString::kFirstOffset == ThinString::kActualOffset);
const int kUnderlyingOffset = SlicedString::kParentOffset;
return String::cast(READ_FIELD(this, kUnderlyingOffset));
}
@ -3806,6 +3829,11 @@ ConsString* String::VisitFlat(Visitor* visitor,
case kConsStringTag | kTwoByteStringTag:
return ConsString::cast(string);
case kThinStringTag | kOneByteStringTag:
case kThinStringTag | kTwoByteStringTag:
string = ThinString::cast(string)->actual();
continue;
default:
UNREACHABLE();
return NULL;
@ -3937,6 +3965,7 @@ void ConsString::set_second(String* value, WriteBarrierMode mode) {
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kSecondOffset, value, mode);
}
ACCESSORS(ThinString, actual, String, kActualOffset);
bool ExternalString::is_short() {
InstanceType type = map()->instance_type();

View File

@ -858,6 +858,8 @@ void String::StringPrint(std::ostream& os) { // NOLINT
os << "#";
} else if (StringShape(this).IsCons()) {
os << "c\"";
} else if (StringShape(this).IsThin()) {
os << ">\"";
} else {
os << "\"";
}

View File

@ -2513,7 +2513,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
Heap* heap = GetHeap();
bool is_one_byte = this->IsOneByteRepresentation();
bool is_internalized = this->IsInternalizedString();
bool has_pointers = this->IsConsString() || this->IsSlicedString();
bool has_pointers = StringShape(this).IsIndirect();
// Morph the string to an external string by replacing the map and
// reinitializing the fields. This won't work if the space the existing
@ -2585,7 +2585,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) {
if (size < ExternalString::kShortSize) return false;
Heap* heap = GetHeap();
bool is_internalized = this->IsInternalizedString();
bool has_pointers = this->IsConsString() || this->IsSlicedString();
bool has_pointers = StringShape(this).IsIndirect();
// Morph the string to an external string by replacing the map and
// reinitializing the fields. This won't work if the space the existing
@ -10310,11 +10310,7 @@ Handle<String> String::Trim(Handle<String> string, TrimMode mode) {
return isolate->factory()->NewSubString(string, left, right);
}
bool String::LooksValid() {
if (!GetIsolate()->heap()->Contains(this)) return false;
return true;
}
bool String::LooksValid() { return GetIsolate()->heap()->Contains(this); }
// static
MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) {
@ -10448,8 +10444,7 @@ String::FlatContent String::GetFlatContent() {
}
string = cons->first();
shape = StringShape(string);
}
if (shape.representation_tag() == kSlicedStringTag) {
} else if (shape.representation_tag() == kSlicedStringTag) {
SlicedString* slice = SlicedString::cast(string);
offset = slice->offset();
string = slice->parent();
@ -10457,6 +10452,13 @@ String::FlatContent String::GetFlatContent() {
DCHECK(shape.representation_tag() != kConsStringTag &&
shape.representation_tag() != kSlicedStringTag);
}
if (shape.representation_tag() == kThinStringTag) {
ThinString* thin = ThinString::cast(string);
string = thin->actual();
shape = StringShape(string);
DCHECK(!shape.IsCons());
DCHECK(!shape.IsSliced());
}
if (shape.encoding_tag() == kOneByteStringTag) {
const uint8_t* start;
if (shape.representation_tag() == kSeqStringTag) {
@ -10542,6 +10544,7 @@ const uc16* String::GetTwoByteData(unsigned start) {
return slice->parent()->GetTwoByteData(start + slice->offset());
}
case kConsStringTag:
case kThinStringTag:
UNREACHABLE();
return NULL;
}
@ -10808,6 +10811,7 @@ uint16_t ConsString::ConsStringGet(int index) {
return 0;
}
uint16_t ThinString::ThinStringGet(int index) { return actual()->Get(index); }
uint16_t SlicedString::SlicedStringGet(int index) {
return parent()->Get(offset() + index);
@ -10902,6 +10906,10 @@ void String::WriteToFlat(String* src,
WriteToFlat(slice->parent(), sink, from + offset, to + offset);
return;
}
case kOneByteStringTag | kThinStringTag:
case kTwoByteStringTag | kThinStringTag:
source = ThinString::cast(source)->actual();
break;
}
}
}
@ -11123,6 +11131,17 @@ bool String::SlowEquals(String* other) {
if (len != other->length()) return false;
if (len == 0) return true;
// Fast check: if at least one ThinString is involved, dereference it/them
// and restart.
if (this->IsThinString() || other->IsThinString()) {
if (other->IsThinString()) other = ThinString::cast(other)->actual();
if (this->IsThinString()) {
return ThinString::cast(this)->actual()->Equals(other);
} else {
return this->Equals(other);
}
}
// Fast check: if hash code is computed for both strings
// a fast negative check can be performed.
if (HasHashCode() && other->HasHashCode()) {
@ -11164,6 +11183,14 @@ bool String::SlowEquals(Handle<String> one, Handle<String> two) {
if (one_length != two->length()) return false;
if (one_length == 0) return true;
// Fast check: if at least one ThinString is involved, dereference it/them
// and restart.
if (one->IsThinString() || two->IsThinString()) {
if (one->IsThinString()) one = handle(ThinString::cast(*one)->actual());
if (two->IsThinString()) two = handle(ThinString::cast(*two)->actual());
return String::Equals(one, two);
}
// Fast check: if hash code is computed for both strings
// a fast negative check can be performed.
if (one->HasHashCode() && two->HasHashCode()) {
@ -16219,6 +16246,14 @@ class InternalizedStringKey : public HashTableKey {
DCHECK(string_->IsInternalizedString());
return string_;
}
// External strings get special treatment, to avoid copying their contents.
if (string_->IsExternalOneByteString()) {
return isolate->factory()
->InternalizeExternalString<ExternalOneByteString>(string_);
} else if (string_->IsExternalTwoByteString()) {
return isolate->factory()
->InternalizeExternalString<ExternalTwoByteString>(string_);
}
// Otherwise allocate a new internalized string.
return isolate->factory()->NewInternalizedStringImpl(
string_, string_->length(), string_->hash_field());
@ -16228,6 +16263,7 @@ class InternalizedStringKey : public HashTableKey {
return String::cast(obj)->Hash();
}
private:
Handle<String> string_;
};
@ -17162,6 +17198,9 @@ MaybeHandle<String> StringTable::InternalizeStringIfExists(
if (string->IsInternalizedString()) {
return string;
}
if (string->IsThinString()) {
return handle(Handle<ThinString>::cast(string)->actual(), isolate);
}
return LookupStringIfExists(isolate, string);
}
@ -17208,31 +17247,72 @@ void StringTable::EnsureCapacityForDeserialization(Isolate* isolate,
isolate->heap()->SetRootStringTable(*table);
}
namespace {
template <class StringClass>
void MigrateExternalStringResource(Isolate* isolate, Handle<String> from,
Handle<String> to) {
Handle<StringClass> cast_from = Handle<StringClass>::cast(from);
Handle<StringClass> cast_to = Handle<StringClass>::cast(to);
const typename StringClass::Resource* to_resource = cast_to->resource();
if (to_resource == nullptr) {
// |to| is a just-created internalized copy of |from|. Migrate the resource.
cast_to->set_resource(cast_from->resource());
// Zap |from|'s resource pointer to reflect the fact that |from| has
// relinquished ownership of its resource.
cast_from->set_resource(nullptr);
} else if (to_resource != cast_from->resource()) {
// |to| already existed and has its own resource. Finalize |from|.
isolate->heap()->FinalizeExternalString(*from);
}
}
} // namespace
Handle<String> StringTable::LookupString(Isolate* isolate,
Handle<String> string) {
if (string->IsThinString()) {
DCHECK(Handle<ThinString>::cast(string)->actual()->IsInternalizedString());
return handle(Handle<ThinString>::cast(string)->actual(), isolate);
}
if (string->IsConsString() && string->IsFlat()) {
string = String::Flatten(string);
string = handle(Handle<ConsString>::cast(string)->first(), isolate);
if (string->IsInternalizedString()) return string;
}
InternalizedStringKey key(string);
Handle<String> result = LookupKey(isolate, &key);
if (string->IsConsString()) {
Handle<ConsString> cons = Handle<ConsString>::cast(string);
cons->set_first(*result);
cons->set_second(isolate->heap()->empty_string());
} else if (string->IsSlicedString()) {
STATIC_ASSERT(ConsString::kSize == SlicedString::kSize);
if (string->IsExternalString()) {
if (result->IsExternalOneByteString()) {
MigrateExternalStringResource<ExternalOneByteString>(isolate, string,
result);
} else if (result->IsExternalTwoByteString()) {
MigrateExternalStringResource<ExternalTwoByteString>(isolate, string,
result);
}
}
// The LookupKey() call above tries to internalize the string in-place.
// In cases where that wasn't possible (e.g. new-space strings), turn them
// into ThinStrings referring to their internalized versions now.
if (!string->IsInternalizedString()) {
DisallowHeapAllocation no_gc;
bool one_byte = result->IsOneByteRepresentation();
Handle<Map> map = one_byte ? isolate->factory()->cons_one_byte_string_map()
: isolate->factory()->cons_string_map();
string->set_map(*map);
Handle<ConsString> cons = Handle<ConsString>::cast(string);
cons->set_first(*result);
cons->set_second(isolate->heap()->empty_string());
Handle<Map> map = one_byte ? isolate->factory()->thin_one_byte_string_map()
: isolate->factory()->thin_string_map();
int old_size = string->Size();
DCHECK(old_size >= ThinString::kSize);
string->synchronized_set_map(*map);
Handle<ThinString> thin = Handle<ThinString>::cast(string);
thin->set_actual(*result);
Address thin_end = thin->address() + ThinString::kSize;
int size_delta = old_size - ThinString::kSize;
if (size_delta != 0) {
Heap* heap = isolate->heap();
heap->CreateFillerObjectAt(thin_end, size_delta, ClearRecordedSlots::kNo);
heap->AdjustLiveBytes(*thin, -size_delta);
}
}
return result;
}

View File

@ -109,6 +109,7 @@
// - SeqTwoByteString
// - SlicedString
// - ConsString
// - ThinString
// - ExternalString
// - ExternalOneByteString
// - ExternalTwoByteString
@ -335,10 +336,12 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(CONS_STRING_TYPE) \
V(EXTERNAL_STRING_TYPE) \
V(SLICED_STRING_TYPE) \
V(THIN_STRING_TYPE) \
V(ONE_BYTE_STRING_TYPE) \
V(CONS_ONE_BYTE_STRING_TYPE) \
V(EXTERNAL_ONE_BYTE_STRING_TYPE) \
V(SLICED_ONE_BYTE_STRING_TYPE) \
V(THIN_ONE_BYTE_STRING_TYPE) \
V(EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE) \
V(SHORT_EXTERNAL_STRING_TYPE) \
V(SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE) \
@ -522,7 +525,10 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE, \
ExternalTwoByteString::kShortSize, \
short_external_internalized_string_with_one_byte_data, \
ShortExternalInternalizedStringWithOneByteData)
ShortExternalInternalizedStringWithOneByteData) \
V(THIN_STRING_TYPE, ThinString::kSize, thin_string, ThinString) \
V(THIN_ONE_BYTE_STRING_TYPE, ThinString::kSize, thin_one_byte_string, \
ThinOneByteString)
// A struct is a simple object a set of object-valued fields. Including an
// object type in this causes the compiler to generate most of the boilerplate
@ -574,20 +580,21 @@ const uint32_t kIsNotInternalizedMask = 0x40;
const uint32_t kNotInternalizedTag = 0x40;
const uint32_t kInternalizedTag = 0x0;
// If bit 7 is clear then bit 2 indicates whether the string consists of
// If bit 7 is clear then bit 3 indicates whether the string consists of
// two-byte characters or one-byte characters.
const uint32_t kStringEncodingMask = 0x4;
const uint32_t kStringEncodingMask = 0x8;
const uint32_t kTwoByteStringTag = 0x0;
const uint32_t kOneByteStringTag = 0x4;
const uint32_t kOneByteStringTag = 0x8;
// If bit 7 is clear, the low-order 2 bits indicate the representation
// If bit 7 is clear, the low-order 3 bits indicate the representation
// of the string.
const uint32_t kStringRepresentationMask = 0x03;
const uint32_t kStringRepresentationMask = 0x07;
enum StringRepresentationTag {
kSeqStringTag = 0x0,
kConsStringTag = 0x1,
kExternalStringTag = 0x2,
kSlicedStringTag = 0x3
kSlicedStringTag = 0x3,
kThinStringTag = 0x5
};
const uint32_t kIsIndirectStringMask = 0x1;
const uint32_t kIsIndirectStringTag = 0x1;
@ -597,22 +604,17 @@ STATIC_ASSERT((kConsStringTag &
kIsIndirectStringMask) == kIsIndirectStringTag); // NOLINT
STATIC_ASSERT((kSlicedStringTag &
kIsIndirectStringMask) == kIsIndirectStringTag); // NOLINT
STATIC_ASSERT((kThinStringTag & kIsIndirectStringMask) == kIsIndirectStringTag);
// Use this mask to distinguish between cons and slice only after making
// sure that the string is one of the two (an indirect string).
const uint32_t kSlicedNotConsMask = kSlicedStringTag & ~kConsStringTag;
STATIC_ASSERT(IS_POWER_OF_TWO(kSlicedNotConsMask));
// If bit 7 is clear, then bit 3 indicates whether this two-byte
// If bit 7 is clear, then bit 4 indicates whether this two-byte
// string actually contains one byte data.
const uint32_t kOneByteDataHintMask = 0x08;
const uint32_t kOneByteDataHintTag = 0x08;
const uint32_t kOneByteDataHintMask = 0x10;
const uint32_t kOneByteDataHintTag = 0x10;
// If bit 7 is clear and string representation indicates an external string,
// then bit 4 indicates whether the data pointer is cached.
const uint32_t kShortExternalStringMask = 0x10;
const uint32_t kShortExternalStringTag = 0x10;
// then bit 5 indicates whether the data pointer is cached.
const uint32_t kShortExternalStringMask = 0x20;
const uint32_t kShortExternalStringTag = 0x20;
// A ConsString with an empty string as the right side is a candidate
// for being shortcut by the garbage collector. We don't allocate any
@ -676,6 +678,9 @@ enum InstanceType {
SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE =
SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE |
kNotInternalizedTag,
THIN_STRING_TYPE = kTwoByteStringTag | kThinStringTag | kNotInternalizedTag,
THIN_ONE_BYTE_STRING_TYPE =
kOneByteStringTag | kThinStringTag | kNotInternalizedTag,
// Non-string names
SYMBOL_TYPE = kNotStringTag, // FIRST_NONSTRING_TYPE, LAST_NAME_TYPE
@ -1030,6 +1035,7 @@ template <class C> inline bool Is(Object* obj);
V(SeqTwoByteString) \
V(SeqOneByteString) \
V(InternalizedString) \
V(ThinString) \
V(Symbol) \
\
V(FixedTypedArrayBase) \
@ -9274,6 +9280,7 @@ class StringShape BASE_EMBEDDED {
inline bool IsExternal();
inline bool IsCons();
inline bool IsSliced();
inline bool IsThin();
inline bool IsIndirect();
inline bool IsExternalOneByte();
inline bool IsExternalTwoByte();
@ -9987,6 +9994,34 @@ class ConsString: public String {
DISALLOW_IMPLICIT_CONSTRUCTORS(ConsString);
};
// The ThinString class describes string objects that are just references
// to another string object. They are used for in-place internalization when
// the original string cannot actually be internalized in-place: in these
// cases, the original string is converted to a ThinString pointing at its
// internalized version (which is allocated as a new object).
// In terms of memory layout and most algorithms operating on strings,
// ThinStrings can be thought of as "one-part cons strings".
class ThinString : public String {
public:
// Actual string that this ThinString refers to.
inline String* actual() const;
inline void set_actual(String* s,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
V8_EXPORT_PRIVATE uint16_t ThinStringGet(int index);
DECLARE_CAST(ThinString)
DECLARE_VERIFIER(ThinString)
// Layout description.
static const int kActualOffset = String::kSize;
static const int kSize = kActualOffset + kPointerSize;
typedef FixedBodyDescriptor<kActualOffset, kSize, kSize> BodyDescriptor;
private:
DISALLOW_COPY_AND_ASSIGN(ThinString);
};
// The Sliced String class describes strings that are substrings of another
// sequential string. The motivation is to save time and memory when creating

View File

@ -1374,7 +1374,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (6) External string. Make it, offset-wise, look like a sequential string.
// Go to (4).
// (7) Short external string or not a string? If yes, bail out to runtime.
// (8) Sliced string. Replace subject with parent. Go to (1).
// (8) Sliced or thin string. Replace subject with parent. Go to (1).
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
@ -1395,6 +1395,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (2) Sequential or cons? If not, go to (5).
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
STATIC_ASSERT(kExternalStringTag < 0xffffu);
@ -1671,12 +1672,19 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ andi(r0, r4, Operand(kIsNotStringMask | kShortExternalStringMask));
__ bne(&runtime, cr0);
// (8) Sliced string. Replace subject with parent. Go to (4).
// (8) Sliced or thin string. Replace subject with parent. Go to (4).
Label thin_string;
__ cmpi(r4, Operand(kThinStringTag));
__ beq(&thin_string);
// Load offset into r11 and replace subject string with parent.
__ LoadP(r11, FieldMemOperand(subject, SlicedString::kOffsetOffset));
__ SmiUntag(r11);
__ LoadP(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
__ b(&check_underlying); // Go to (4).
__ bind(&thin_string);
__ LoadP(subject, FieldMemOperand(subject, ThinString::kActualOffset));
__ b(&check_underlying); // Go to (4).
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -77,6 +77,9 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
Register index, Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbz(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
@ -86,20 +89,26 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
__ andi(r0, result, Operand(kIsIndirectStringMask));
__ beq(&check_sequential, cr0);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ mov(ip, Operand(kSlicedNotConsMask));
__ and_(r0, result, ip, SetRC);
__ beq(&cons_string, cr0);
// Dispatch on the indirect string shape: slice or cons or thin.
Label cons_string, thin_string;
__ andi(ip, result, Operand(kStringRepresentationMask));
__ cmpi(ip, Operand(kConsStringTag));
__ beq(&cons_string);
__ cmpi(ip, Operand(kThinStringTag));
__ beq(&thin_string);
// Handle slices.
Label indirect_string_loaded;
__ LoadP(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
__ LoadP(string, FieldMemOperand(string, SlicedString::kParentOffset));
__ SmiUntag(ip, result);
__ add(index, index, ip);
__ b(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ LoadP(string, FieldMemOperand(string, ThinString::kActualOffset));
__ b(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
@ -111,10 +120,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
__ bne(call_runtime);
// Get the first of the two strings and load its instance type.
__ LoadP(string, FieldMemOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbz(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
__ b(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -1165,6 +1165,10 @@ void V8HeapExplorer::ExtractStringReferences(int entry, String* string) {
SlicedString* ss = SlicedString::cast(string);
SetInternalReference(ss, entry, "parent", ss->parent(),
SlicedString::kParentOffset);
} else if (string->IsThinString()) {
ThinString* ts = ThinString::cast(string);
SetInternalReference(ts, entry, "actual", ts->actual(),
ThinString::kActualOffset);
}
}

View File

@ -133,6 +133,8 @@ const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
} else if (subject->IsSlicedString()) {
start_index += SlicedString::cast(subject)->offset();
subject = SlicedString::cast(subject)->parent();
} else if (subject->IsThinString()) {
subject = ThinString::cast(subject)->actual();
}
DCHECK(start_index >= 0);
DCHECK(start_index <= subject->length());
@ -239,6 +241,9 @@ NativeRegExpMacroAssembler::Result NativeRegExpMacroAssembler::Match(
subject_ptr = slice->parent();
slice_offset = slice->offset();
}
if (StringShape(subject_ptr).IsThin()) {
subject_ptr = ThinString::cast(subject_ptr)->actual();
}
// Ensure that an underlying string has the same representation.
bool is_one_byte = subject_ptr->IsOneByteRepresentation();
DCHECK(subject_ptr->IsExternalString() || subject_ptr->IsSeqString());

View File

@ -865,6 +865,8 @@ MUST_USE_RESULT Object* LocaleConvertCase(Handle<String> s, Isolate* isolate,
Handle<SeqTwoByteString> result;
std::unique_ptr<uc16[]> sap;
if (dest_length == 0) return isolate->heap()->empty_string();
// This is not a real loop. It'll be executed only once (no overflow) or
// twice (overflow).
for (int i = 0; i < 2; ++i) {
@ -1086,7 +1088,7 @@ RUNTIME_FUNCTION(Runtime_StringToUpperCaseI18N) {
int32_t length = s->length();
s = String::Flatten(s);
if (s->HasOnlyOneByteChars()) {
if (s->HasOnlyOneByteChars() && length > 0) {
Handle<SeqOneByteString> result =
isolate->factory()->NewRawOneByteString(length).ToHandleChecked();

View File

@ -298,6 +298,7 @@ RUNTIME_FUNCTION(Runtime_AllocateSeqOneByteString) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(length, 0);
if (length == 0) return isolate->heap()->empty_string();
Handle<SeqOneByteString> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawOneByteString(length));
@ -308,6 +309,7 @@ RUNTIME_FUNCTION(Runtime_AllocateSeqTwoByteString) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(length, 0);
if (length == 0) return isolate->heap()->empty_string();
Handle<SeqTwoByteString> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawTwoByteString(length));

View File

@ -56,6 +56,14 @@ static MaybeHandle<Object> KeyedGetObjectProperty(Isolate* isolate,
DisallowHeapAllocation no_allocation;
Handle<JSObject> receiver = Handle<JSObject>::cast(receiver_obj);
Handle<Name> key = Handle<Name>::cast(key_obj);
// Get to a ThinString's referenced internalized string, but don't
// otherwise force internalization. We assume that internalization
// (which is a dictionary lookup with a non-internalized key) is
// about as expensive as doing the property dictionary lookup with
// the non-internalized key directly.
if (key->IsThinString()) {
key = handle(Handle<ThinString>::cast(key)->actual(), isolate);
}
if (receiver->IsJSGlobalObject()) {
// Attempt dictionary lookup.
GlobalDictionary* dictionary = receiver->global_dictionary();

View File

@ -431,6 +431,9 @@ MUST_USE_RESULT static Object* StringReplaceGlobalAtomRegExpWithString(
} else {
result_len = static_cast<int>(result_len_64);
}
if (result_len == 0) {
return isolate->heap()->empty_string();
}
int subject_pos = 0;
int result_pos = 0;

View File

@ -263,6 +263,9 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
if (length == -1) {
return isolate->Throw(isolate->heap()->illegal_argument_string());
}
if (length == 0) {
return isolate->heap()->empty_string();
}
if (one_byte) {
Handle<SeqOneByteString> answer;

View File

@ -1371,7 +1371,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (6) External string. Make it, offset-wise, look like a sequential string.
// Go to (4).
// (7) Short external string or not a string? If yes, bail out to runtime.
// (8) Sliced string. Replace subject with parent. Go to (1).
// (8) Sliced or thin string. Replace subject with parent. Go to (1).
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
@ -1393,6 +1393,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (2) Sequential or cons? If not, go to (5).
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
STATIC_ASSERT(kExternalStringTag < 0xffffu);
@ -1680,12 +1681,19 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ AndP(r0, r3);
__ bne(&runtime);
// (8) Sliced string. Replace subject with parent. Go to (4).
// (8) Sliced or thin string. Replace subject with parent. Go to (4).
Label thin_string;
__ CmpP(r3, Operand(kThinStringTag));
__ beq(&thin_string);
// Load offset into ip and replace subject string with parent.
__ LoadP(ip, FieldMemOperand(subject, SlicedString::kOffsetOffset));
__ SmiUntag(ip);
__ LoadP(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
__ b(&check_underlying); // Go to (4).
__ bind(&thin_string);
__ LoadP(subject, FieldMemOperand(subject, ThinString::kActualOffset));
__ b(&check_underlying); // Go to (4).
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -70,6 +70,9 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
Register index, Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ LoadlB(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
@ -81,19 +84,25 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
__ beq(&check_sequential, Label::kNear /*, cr0*/);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ mov(ip, Operand(kSlicedNotConsMask));
__ LoadRR(r0, result);
__ AndP(r0, ip /*, SetRC*/); // Should be okay to remove RC
__ beq(&cons_string, Label::kNear /*, cr0*/);
Label cons_string, thin_string;
__ LoadRR(ip, result);
__ nilf(ip, Operand(kStringRepresentationMask));
__ CmpP(ip, Operand(kConsStringTag));
__ beq(&cons_string);
__ CmpP(ip, Operand(kThinStringTag));
__ beq(&thin_string);
// Handle slices.
Label indirect_string_loaded;
__ LoadP(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
__ LoadP(string, FieldMemOperand(string, SlicedString::kParentOffset));
__ SmiUntag(ip, result);
__ AddP(index, ip);
__ b(&indirect_string_loaded, Label::kNear);
__ b(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ LoadP(string, FieldMemOperand(string, ThinString::kActualOffset));
__ b(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
@ -106,10 +115,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
__ bne(call_runtime);
// Get the first of the two strings and load its instance type.
__ LoadP(string, FieldMemOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
__ LoadlB(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
__ b(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -1135,8 +1135,9 @@ MaybeHandle<String> ValueDeserializer::ReadUtf8String() {
if (!ReadVarint<uint32_t>().To(&utf8_length) ||
utf8_length >
static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
!ReadRawBytes(utf8_length).To(&utf8_bytes))
!ReadRawBytes(utf8_length).To(&utf8_bytes)) {
return MaybeHandle<String>();
}
return isolate_->factory()->NewStringFromUtf8(
Vector<const char>::cast(utf8_bytes), pretenure_);
}
@ -1147,16 +1148,20 @@ MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
if (!ReadVarint<uint32_t>().To(&byte_length) ||
byte_length >
static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
byte_length % sizeof(uc16) != 0 || !ReadRawBytes(byte_length).To(&bytes))
byte_length % sizeof(uc16) != 0 ||
!ReadRawBytes(byte_length).To(&bytes)) {
return MaybeHandle<String>();
}
// Allocate an uninitialized string so that we can do a raw memcpy into the
// string on the heap (regardless of alignment).
if (byte_length == 0) return isolate_->factory()->empty_string();
Handle<SeqTwoByteString> string;
if (!isolate_->factory()
->NewRawTwoByteString(byte_length / sizeof(uc16), pretenure_)
.ToHandle(&string))
.ToHandle(&string)) {
return MaybeHandle<String>();
}
// Copy the bytes directly into the new string.
// Warning: this uses host endianness.

View File

@ -484,7 +484,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (8) Is the external string one byte? If yes, go to (5).
// (9) Two byte sequential. Load regexp code for two byte. Go to (E).
// (10) Short external string or not a string? If yes, bail out to runtime.
// (11) Sliced string. Replace subject with parent. Go to (1).
// (11) Sliced or thin string. Replace subject with parent. Go to (1).
Label seq_one_byte_string /* 5 */, seq_two_byte_string /* 9 */,
external_string /* 7 */, check_underlying /* 1 */,
@ -514,6 +514,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// have already been covered.
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
__ cmpp(rbx, Immediate(kExternalStringTag));
@ -802,11 +803,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ testb(rbx, Immediate(kIsNotStringMask | kShortExternalStringMask));
__ j(not_zero, &runtime);
// (11) Sliced string. Replace subject with parent. Go to (1).
// (11) Sliced or thin string. Replace subject with parent. Go to (1).
Label thin_string;
__ cmpl(rbx, Immediate(kThinStringTag));
__ j(equal, &thin_string, Label::kNear);
// Load offset into r14 and replace subject string with parent.
__ SmiToInteger32(r14, FieldOperand(rdi, SlicedString::kOffsetOffset));
__ movp(rdi, FieldOperand(rdi, SlicedString::kParentOffset));
__ jmp(&check_underlying);
__ bind(&thin_string);
__ movp(rdi, FieldOperand(rdi, ThinString::kActualOffset));
__ jmp(&check_underlying);
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -67,6 +67,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Register index,
Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ movp(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
@ -77,16 +80,23 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ j(zero, &check_sequential, Label::kNear);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ testb(result, Immediate(kSlicedNotConsMask));
__ j(zero, &cons_string, Label::kNear);
Label cons_string, thin_string;
__ andl(result, Immediate(kStringRepresentationMask));
__ cmpl(result, Immediate(kConsStringTag));
__ j(equal, &cons_string, Label::kNear);
__ cmpl(result, Immediate(kThinStringTag));
__ j(equal, &thin_string, Label::kNear);
// Handle slices.
Label indirect_string_loaded;
__ SmiToInteger32(result, FieldOperand(string, SlicedString::kOffsetOffset));
__ addp(index, result);
__ movp(string, FieldOperand(string, SlicedString::kParentOffset));
__ jmp(&indirect_string_loaded, Label::kNear);
__ jmp(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ movp(string, FieldOperand(string, ThinString::kActualOffset));
__ jmp(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
@ -98,10 +108,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Heap::kempty_stringRootIndex);
__ j(not_equal, call_runtime);
__ movp(string, FieldOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ movp(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
__ jmp(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -2540,10 +2540,12 @@ void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(
andl(scratch1, Immediate(kFlatOneByteStringMask));
andl(scratch2, Immediate(kFlatOneByteStringMask));
// Interleave the bits to check both scratch1 and scratch2 in one test.
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << 3));
leap(scratch1, Operand(scratch1, scratch2, times_8, 0));
const int kShift = 8;
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << kShift));
shlp(scratch2, Immediate(kShift));
orp(scratch1, scratch2);
cmpl(scratch1,
Immediate(kFlatOneByteStringTag + (kFlatOneByteStringTag << 3)));
Immediate(kFlatOneByteStringTag + (kFlatOneByteStringTag << kShift)));
j(not_equal, on_fail, near_jump);
}

View File

@ -425,7 +425,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// (8) Is the external string one byte? If yes, go to (5).
// (9) Two byte sequential. Load regexp code for two byte. Go to (E).
// (10) Short external string or not a string? If yes, bail out to runtime.
// (11) Sliced string. Replace subject with parent. Go to (1).
// (11) Sliced or thin string. Replace subject with parent. Go to (1).
Label seq_one_byte_string /* 5 */, seq_two_byte_string /* 9 */,
external_string /* 7 */, check_underlying /* 1 */,
@ -455,6 +455,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// have already been covered.
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
__ cmp(ebx, Immediate(kExternalStringTag));
@ -733,11 +734,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
__ j(not_zero, &runtime);
// (11) Sliced string. Replace subject with parent. Go to (1).
// (11) Sliced or thin string. Replace subject with parent. Go to (1).
Label thin_string;
__ cmp(ebx, Immediate(kThinStringTag));
__ j(equal, &thin_string, Label::kNear);
// Load offset into edi and replace subject string with parent.
__ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
__ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
__ jmp(&check_underlying); // Go to (1).
__ bind(&thin_string);
__ mov(eax, FieldOperand(eax, ThinString::kActualOffset));
__ jmp(&check_underlying); // Go to (1).
#endif // V8_INTERPRETED_REGEXP
}

View File

@ -218,6 +218,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Register index,
Register result,
Label* call_runtime) {
Label indirect_string_loaded;
__ bind(&indirect_string_loaded);
// Fetch the instance type of the receiver into result register.
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
@ -228,17 +231,24 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
__ j(zero, &check_sequential, Label::kNear);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ test(result, Immediate(kSlicedNotConsMask));
__ j(zero, &cons_string, Label::kNear);
Label cons_string, thin_string;
__ and_(result, Immediate(kStringRepresentationMask));
__ cmp(result, Immediate(kConsStringTag));
__ j(equal, &cons_string, Label::kNear);
__ cmp(result, Immediate(kThinStringTag));
__ j(equal, &thin_string, Label::kNear);
// Handle slices.
Label indirect_string_loaded;
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
__ SmiUntag(result);
__ add(index, result);
__ mov(string, FieldOperand(string, SlicedString::kParentOffset));
__ jmp(&indirect_string_loaded, Label::kNear);
__ jmp(&indirect_string_loaded);
// Handle thin strings.
__ bind(&thin_string);
__ mov(string, FieldOperand(string, ThinString::kActualOffset));
__ jmp(&indirect_string_loaded);
// Handle cons strings.
// Check whether the right hand side is the empty string (i.e. if
@ -250,10 +260,7 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Immediate(factory->empty_string()));
__ j(not_equal, call_runtime);
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
__ jmp(&indirect_string_loaded);
// Distinguish sequential and external strings. Only these two string
// representations can reach here (slices and flat cons strings have been

View File

@ -2415,11 +2415,13 @@ void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(Register object1,
const int kFlatOneByteStringTag =
kStringTag | kOneByteStringTag | kSeqStringTag;
// Interleave bits from both instance types and compare them in one check.
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << 3));
const int kShift = 8;
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << kShift));
and_(scratch1, kFlatOneByteStringMask);
and_(scratch2, kFlatOneByteStringMask);
lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << 3));
shl(scratch2, kShift);
or_(scratch1, scratch2);
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << kShift));
j(not_equal, failure);
}

View File

@ -181,47 +181,6 @@ TEST(ToString) {
}
}
TEST(FlattenString) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 1;
CodeAssemblerTester data(isolate, kNumParams);
CodeStubAssembler m(data.state());
m.Return(m.FlattenString(m.Parameter(0)));
Handle<Code> code = data.GenerateCode();
FunctionTester ft(code, kNumParams);
Handle<FixedArray> test_cases(isolate->factory()->NewFixedArray(4));
Handle<String> expected(
isolate->factory()->InternalizeUtf8String("hello, world!"));
test_cases->set(0, *expected);
Handle<String> string(
isolate->factory()->InternalizeUtf8String("filler hello, world! filler"));
Handle<String> sub_string(
isolate->factory()->NewProperSubString(string, 7, 20));
test_cases->set(1, *sub_string);
Handle<String> hello(isolate->factory()->InternalizeUtf8String("hello,"));
Handle<String> world(isolate->factory()->InternalizeUtf8String(" world!"));
Handle<String> cons_str(
isolate->factory()->NewConsString(hello, world).ToHandleChecked());
test_cases->set(2, *cons_str);
Handle<String> empty(isolate->factory()->InternalizeUtf8String(""));
Handle<String> fake_cons_str(
isolate->factory()->NewConsString(expected, empty).ToHandleChecked());
test_cases->set(3, *fake_cons_str);
for (int i = 0; i < 4; ++i) {
Handle<String> test = handle(String::cast(test_cases->get(i)));
Handle<Object> result = ft.Call(test).ToHandleChecked();
CHECK(result->IsString());
CHECK(Handle<String>::cast(result)->IsFlat());
CHECK(String::Equals(Handle<String>::cast(result), expected));
}
}
TEST(TryToName) {
typedef CodeAssemblerLabel Label;
typedef CodeAssemblerVariable Variable;
@ -239,22 +198,26 @@ TEST(TryToName) {
Label passed(&m), failed(&m);
Label if_keyisindex(&m), if_keyisunique(&m), if_bailout(&m);
Variable var_index(&m, MachineType::PointerRepresentation());
{
Variable var_index(&m, MachineType::PointerRepresentation());
Variable var_unique(&m, MachineRepresentation::kTagged);
m.TryToName(key, &if_keyisindex, &var_index, &if_keyisunique, &if_bailout);
m.TryToName(key, &if_keyisindex, &var_index, &if_keyisunique, &var_unique,
&if_bailout);
m.Bind(&if_keyisindex);
m.GotoUnless(
m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kKeyIsIndex))),
&failed);
m.Branch(m.WordEqual(m.SmiUntag(expected_arg), var_index.value()), &passed,
&failed);
m.Bind(&if_keyisindex);
m.GotoUnless(m.WordEqual(expected_result,
m.SmiConstant(Smi::FromInt(kKeyIsIndex))),
&failed);
m.Branch(m.WordEqual(m.SmiUntag(expected_arg), var_index.value()),
&passed, &failed);
m.Bind(&if_keyisunique);
m.GotoUnless(
m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kKeyIsUnique))),
&failed);
m.Branch(m.WordEqual(expected_arg, key), &passed, &failed);
m.Bind(&if_keyisunique);
m.GotoUnless(m.WordEqual(expected_result,
m.SmiConstant(Smi::FromInt(kKeyIsUnique))),
&failed);
m.Branch(m.WordEqual(expected_arg, var_unique.value()), &passed, &failed);
}
m.Bind(&if_bailout);
m.Branch(
@ -350,6 +313,23 @@ TEST(TryToName) {
Handle<Object> key = isolate->factory()->NewStringFromAsciiChecked("test");
ft.CheckTrue(key, expect_bailout);
}
{
// TryToName(<thin string>) => internalized version.
Handle<String> s = isolate->factory()->NewStringFromAsciiChecked("foo");
Handle<String> internalized = isolate->factory()->InternalizeString(s);
ft.CheckTrue(s, expect_unique, internalized);
}
{
// TryToName(<thin two-byte string>) => internalized version.
uc16 array1[] = {2001, 2002, 2003};
Vector<const uc16> str1(array1);
Handle<String> s =
isolate->factory()->NewStringFromTwoByte(str1).ToHandleChecked();
Handle<String> internalized = isolate->factory()->InternalizeString(s);
ft.CheckTrue(s, expect_unique, internalized);
}
}
namespace {

View File

@ -1195,6 +1195,31 @@ class OneByteVectorResource : public v8::String::ExternalOneByteStringResource {
i::Vector<const char> data_;
};
TEST(InternalizeExternal) {
i::Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
// This won't leak; the external string mechanism will call Dispose() on it.
OneByteVectorResource* resource =
new OneByteVectorResource(i::Vector<const char>("prop", 4));
{
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::String> ext_string =
v8::String::NewExternalOneByte(CcTest::isolate(), resource)
.ToLocalChecked();
Handle<String> string = v8::Utils::OpenHandle(*ext_string);
CHECK(string->IsExternalString());
CHECK(!string->IsInternalizedString());
CHECK(isolate->heap()->InNewSpace(*string));
factory->InternalizeName(string);
CHECK(string->IsThinString());
CcTest::CollectGarbage(i::NEW_SPACE);
CcTest::CollectGarbage(i::NEW_SPACE);
CHECK(string->IsInternalizedString());
CHECK(!isolate->heap()->InNewSpace(*string));
}
CcTest::CollectGarbage(i::OLD_SPACE);
CcTest::CollectGarbage(i::OLD_SPACE);
}
TEST(SliceFromExternal) {
FLAG_string_slices = true;

View File

@ -0,0 +1,90 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function get_thin_string(a, b) {
var str = a + b; // Make a ConsString.
var o = {};
o[str]; // Turn it into a ThinString.
return str;
}
var str = get_thin_string("foo", "bar");
var re = /.o+ba./;
assertEquals(["foobar"], re.exec(str));
assertEquals(["foobar"], re.exec(str));
assertEquals(["foobar"], re.exec(str));
function CheckCS() {
assertEquals("o", str.substring(1, 2));
assertEquals("f".charCodeAt(0), str.charCodeAt(0));
assertEquals("f", str.split(/oo/)[0]);
}
CheckCS();
%OptimizeFunctionOnNextCall(CheckCS);
CheckCS();
function CheckTF() {
try {} catch(e) {} // Turbofan.
assertEquals("o", str.substring(1, 2));
assertEquals("f".charCodeAt(0), str.charCodeAt(0));
assertEquals("f", str.split(/oo/)[0]);
}
CheckTF();
%OptimizeFunctionOnNextCall(CheckTF);
CheckTF();
// Flat cons strings can point to a thin string.
function get_cons_thin_string(a, b) {
// Make a ConsString.
var s = a + b;
// Flatten it.
s.endsWith("a");
// Internalize the first part.
var o = {};
o[s];
return s;
}
var s = get_cons_thin_string("__________", "@@@@@@@@@@a");
s.match(/.*a/);
assertEquals("________", s.substring(0, 8));
function cc1(s) {
assertEquals(95, s.charCodeAt(0));
assertEquals(95, s.codePointAt(0));
}
cc1(s);
cc1(s);
%OptimizeFunctionOnNextCall(cc1);
cc1(s);
// Sliced strings can have a thin string as their parent.
function get_sliced_thin_string(a, b) {
// Make a long thin string.
var s = a + b;
// Slice a substring out of it.
var slice = s.substring(0, 20);
// Make the original string thin.
var o = {};
o[s];
return slice;
}
var t = get_sliced_thin_string("abcdefghijklmnopqrstuvwxyz",
"abcdefghijklmnopqrstuvwxyz");
assertEquals("abcdefghijklmnopqrst", decodeURI(t));
function cc2(s) {
assertEquals(97, s.charCodeAt(0));
assertEquals(97, s.codePointAt(0));
}
cc2(t);
cc2(t);
%OptimizeFunctionOnNextCall(cc2);
cc2(t);