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:
parent
0befccd21b
commit
ec45e6ed2e
@ -2313,7 +2313,7 @@ class V8_EXPORT String : public Name {
|
|||||||
enum Encoding {
|
enum Encoding {
|
||||||
UNKNOWN_ENCODING = 0x1,
|
UNKNOWN_ENCODING = 0x1,
|
||||||
TWO_BYTE_ENCODING = 0x0,
|
TWO_BYTE_ENCODING = 0x0,
|
||||||
ONE_BYTE_ENCODING = 0x4
|
ONE_BYTE_ENCODING = 0x8
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Returns the number of characters in this string.
|
* Returns the number of characters in this string.
|
||||||
@ -8412,10 +8412,10 @@ class Internals {
|
|||||||
static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
|
static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
|
||||||
static const int kContextHeaderSize = 2 * kApiPointerSize;
|
static const int kContextHeaderSize = 2 * kApiPointerSize;
|
||||||
static const int kContextEmbedderDataIndex = 5;
|
static const int kContextEmbedderDataIndex = 5;
|
||||||
static const int kFullStringRepresentationMask = 0x07;
|
static const int kFullStringRepresentationMask = 0x0f;
|
||||||
static const int kStringEncodingMask = 0x4;
|
static const int kStringEncodingMask = 0x8;
|
||||||
static const int kExternalTwoByteRepresentationTag = 0x02;
|
static const int kExternalTwoByteRepresentationTag = 0x02;
|
||||||
static const int kExternalOneByteRepresentationTag = 0x06;
|
static const int kExternalOneByteRepresentationTag = 0x0a;
|
||||||
|
|
||||||
static const int kIsolateEmbedderDataOffset = 0 * kApiPointerSize;
|
static const int kIsolateEmbedderDataOffset = 0 * kApiPointerSize;
|
||||||
static const int kExternalMemoryOffset = 4 * kApiPointerSize;
|
static const int kExternalMemoryOffset = 4 * kApiPointerSize;
|
||||||
|
@ -1298,7 +1298,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (6) External string. Make it, offset-wise, look like a sequential string.
|
// (6) External string. Make it, offset-wise, look like a sequential string.
|
||||||
// Go to (4).
|
// Go to (4).
|
||||||
// (7) Short external string or not a string? If yes, bail out to runtime.
|
// (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 */,
|
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
|
||||||
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
|
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).
|
// (2) Sequential or cons? If not, go to (5).
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
__ cmp(r1, Operand(kExternalStringTag));
|
__ cmp(r1, Operand(kExternalStringTag));
|
||||||
@ -1347,10 +1348,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ b(ls, &runtime);
|
__ b(ls, &runtime);
|
||||||
__ SmiUntag(r1);
|
__ SmiUntag(r1);
|
||||||
|
|
||||||
STATIC_ASSERT(4 == kOneByteStringTag);
|
STATIC_ASSERT(8 == kOneByteStringTag);
|
||||||
STATIC_ASSERT(kTwoByteStringTag == 0);
|
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||||
__ and_(r0, r0, Operand(kStringEncodingMask));
|
__ 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),
|
__ ldr(r6, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset),
|
||||||
ne);
|
ne);
|
||||||
__ ldr(r6, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
|
__ ldr(r6, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
|
||||||
@ -1584,12 +1585,19 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ tst(r1, Operand(kIsNotStringMask | kShortExternalStringMask));
|
__ tst(r1, Operand(kIsNotStringMask | kShortExternalStringMask));
|
||||||
__ b(ne, &runtime);
|
__ 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.
|
// Load offset into r9 and replace subject string with parent.
|
||||||
__ ldr(r9, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
__ ldr(r9, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
||||||
__ SmiUntag(r9);
|
__ SmiUntag(r9);
|
||||||
__ ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
__ ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
||||||
__ jmp(&check_underlying); // Go to (4).
|
__ 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
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,6 +322,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
Register index,
|
Register index,
|
||||||
Register result,
|
Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
__ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||||
__ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
__ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -332,17 +335,24 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
__ b(eq, &check_sequential);
|
__ b(eq, &check_sequential);
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ tst(result, Operand(kSlicedNotConsMask));
|
__ and_(result, result, Operand(kStringRepresentationMask));
|
||||||
|
__ cmp(result, Operand(kConsStringTag));
|
||||||
__ b(eq, &cons_string);
|
__ b(eq, &cons_string);
|
||||||
|
__ cmp(result, Operand(kThinStringTag));
|
||||||
|
__ b(eq, &thin_string);
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ ldr(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
__ ldr(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
__ ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
||||||
__ add(index, index, Operand::SmiUntag(result));
|
__ add(index, index, Operand::SmiUntag(result));
|
||||||
__ jmp(&indirect_string_loaded);
|
__ jmp(&indirect_string_loaded);
|
||||||
|
|
||||||
|
// Handle thin strings.
|
||||||
|
__ bind(&thin_string);
|
||||||
|
__ ldr(string, FieldMemOperand(string, ThinString::kActualOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
|
|
||||||
// Handle cons strings.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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
|
// 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);
|
__ b(ne, call_runtime);
|
||||||
// Get the first of the two strings and load its instance type.
|
// Get the first of the two strings and load its instance type.
|
||||||
__ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
__ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -1445,7 +1445,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (6) External string. Make it, offset-wise, look like a sequential string.
|
// (6) External string. Make it, offset-wise, look like a sequential string.
|
||||||
// Go to (4).
|
// Go to (4).
|
||||||
// (7) Short external string or not a string? If yes, bail out to runtime.
|
// (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 check_underlying; // (1)
|
||||||
Label seq_string; // (4)
|
Label seq_string; // (4)
|
||||||
@ -1479,6 +1479,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (2) Sequential or cons? If not, go to (5).
|
// (2) Sequential or cons? If not, go to (5).
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
__ Cmp(string_representation, kExternalStringTag);
|
__ Cmp(string_representation, kExternalStringTag);
|
||||||
@ -1506,10 +1507,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// before entering the exit frame.
|
// before entering the exit frame.
|
||||||
__ SmiUntag(x1, x10);
|
__ SmiUntag(x1, x10);
|
||||||
|
|
||||||
// The third bit determines the string encoding in string_type.
|
// The fourth bit determines the string encoding in string_type.
|
||||||
STATIC_ASSERT(kOneByteStringTag == 0x04);
|
STATIC_ASSERT(kOneByteStringTag == 0x08);
|
||||||
STATIC_ASSERT(kTwoByteStringTag == 0x00);
|
STATIC_ASSERT(kTwoByteStringTag == 0x00);
|
||||||
STATIC_ASSERT(kStringEncodingMask == 0x04);
|
STATIC_ASSERT(kStringEncodingMask == 0x08);
|
||||||
|
|
||||||
// Find the code object based on the assumptions above.
|
// Find the code object based on the assumptions above.
|
||||||
// kDataOneByteCodeOffset and kDataUC16CodeOffset are adjacent, adds an offset
|
// kDataOneByteCodeOffset and kDataUC16CodeOffset are adjacent, adds an offset
|
||||||
@ -1517,7 +1518,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
STATIC_ASSERT(JSRegExp::kDataOneByteCodeOffset + kPointerSize ==
|
STATIC_ASSERT(JSRegExp::kDataOneByteCodeOffset + kPointerSize ==
|
||||||
JSRegExp::kDataUC16CodeOffset);
|
JSRegExp::kDataUC16CodeOffset);
|
||||||
__ Mov(x10, kPointerSize);
|
__ Mov(x10, kPointerSize);
|
||||||
// We will need the encoding later: Latin1 = 0x04
|
// We will need the encoding later: Latin1 = 0x08
|
||||||
// UC16 = 0x00
|
// UC16 = 0x00
|
||||||
__ Ands(string_encoding, string_type, kStringEncodingMask);
|
__ Ands(string_encoding, string_type, kStringEncodingMask);
|
||||||
__ CzeroX(x10, ne);
|
__ CzeroX(x10, ne);
|
||||||
@ -1565,10 +1566,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ Ldr(length, UntagSmiFieldMemOperand(subject, String::kLengthOffset));
|
__ Ldr(length, UntagSmiFieldMemOperand(subject, String::kLengthOffset));
|
||||||
|
|
||||||
// Handle UC16 encoding, two bytes make one character.
|
// Handle UC16 encoding, two bytes make one character.
|
||||||
// string_encoding: if Latin1: 0x04
|
// string_encoding: if Latin1: 0x08
|
||||||
// if UC16: 0x00
|
// if UC16: 0x00
|
||||||
STATIC_ASSERT(kStringEncodingMask == 0x04);
|
STATIC_ASSERT(kStringEncodingMask == 0x08);
|
||||||
__ Ubfx(string_encoding, string_encoding, 2, 1);
|
__ Ubfx(string_encoding, string_encoding, 3, 1);
|
||||||
__ Eor(string_encoding, string_encoding, 1);
|
__ Eor(string_encoding, string_encoding, 1);
|
||||||
// string_encoding: if Latin1: 0
|
// string_encoding: if Latin1: 0
|
||||||
// if UC16: 1
|
// if UC16: 1
|
||||||
@ -1781,11 +1782,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
kShortExternalStringMask | kIsNotStringMask,
|
kShortExternalStringMask | kIsNotStringMask,
|
||||||
&runtime);
|
&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,
|
__ Ldr(sliced_string_offset,
|
||||||
UntagSmiFieldMemOperand(subject, SlicedString::kOffsetOffset));
|
UntagSmiFieldMemOperand(subject, SlicedString::kOffsetOffset));
|
||||||
__ Ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
__ Ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
||||||
__ B(&check_underlying); // Go to (1).
|
__ B(&check_underlying); // Go to (1).
|
||||||
|
|
||||||
|
__ bind(&thin_string);
|
||||||
|
__ Ldr(subject, FieldMemOperand(subject, ThinString::kActualOffset));
|
||||||
|
__ B(&check_underlying); // Go to (1).
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
Register result,
|
Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
DCHECK(string.Is64Bits() && index.Is32Bits() && result.Is64Bits());
|
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.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
__ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||||
__ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
__ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -108,17 +111,25 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
__ TestAndBranchIfAllClear(result, kIsIndirectStringMask, &check_sequential);
|
__ TestAndBranchIfAllClear(result, kIsIndirectStringMask, &check_sequential);
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ TestAndBranchIfAllClear(result, kSlicedNotConsMask, &cons_string);
|
__ And(result, result, kStringRepresentationMask);
|
||||||
|
__ Cmp(result, kConsStringTag);
|
||||||
|
__ B(eq, &cons_string);
|
||||||
|
__ Cmp(result, kThinStringTag);
|
||||||
|
__ B(eq, &thin_string);
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ Ldr(result.W(),
|
__ Ldr(result.W(),
|
||||||
UntagSmiFieldMemOperand(string, SlicedString::kOffsetOffset));
|
UntagSmiFieldMemOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ Ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
__ Ldr(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
||||||
__ Add(index, index, result.W());
|
__ Add(index, index, result.W());
|
||||||
__ B(&indirect_string_loaded);
|
__ B(&indirect_string_loaded);
|
||||||
|
|
||||||
|
// Handle thin strings.
|
||||||
|
__ Bind(&thin_string);
|
||||||
|
__ Ldr(string, FieldMemOperand(string, ThinString::kActualOffset));
|
||||||
|
__ B(&indirect_string_loaded);
|
||||||
|
|
||||||
// Handle cons strings.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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
|
// 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);
|
__ JumpIfNotRoot(result, Heap::kempty_stringRootIndex, call_runtime);
|
||||||
// Get the first of the two strings and load its instance type.
|
// Get the first of the two strings and load its instance type.
|
||||||
__ Ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
__ Ldr(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ B(&indirect_string_loaded);
|
||||||
__ Bind(&indirect_string_loaded);
|
|
||||||
__ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -157,6 +157,8 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
|
|||||||
case ONE_BYTE_STRING_TYPE:
|
case ONE_BYTE_STRING_TYPE:
|
||||||
case CONS_STRING_TYPE:
|
case CONS_STRING_TYPE:
|
||||||
case CONS_ONE_BYTE_STRING_TYPE:
|
case CONS_ONE_BYTE_STRING_TYPE:
|
||||||
|
case THIN_STRING_TYPE:
|
||||||
|
case THIN_ONE_BYTE_STRING_TYPE:
|
||||||
case SLICED_STRING_TYPE:
|
case SLICED_STRING_TYPE:
|
||||||
case SLICED_ONE_BYTE_STRING_TYPE:
|
case SLICED_ONE_BYTE_STRING_TYPE:
|
||||||
case EXTERNAL_STRING_TYPE:
|
case EXTERNAL_STRING_TYPE:
|
||||||
|
@ -39,21 +39,24 @@ TF_BUILTIN(ObjectHasOwnProperty, ObjectBuiltinsAssembler) {
|
|||||||
Node* map = LoadMap(object);
|
Node* map = LoadMap(object);
|
||||||
Node* instance_type = LoadMapInstanceType(map);
|
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);
|
Label keyisindex(this), if_iskeyunique(this);
|
||||||
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &call_runtime);
|
TryToName(key, &keyisindex, &var_index, &if_iskeyunique, &var_unique,
|
||||||
|
&call_runtime);
|
||||||
|
|
||||||
Bind(&if_iskeyunique);
|
Bind(&if_iskeyunique);
|
||||||
TryHasOwnProperty(object, map, instance_type, key, &return_true,
|
TryHasOwnProperty(object, map, instance_type, var_unique.value(),
|
||||||
&return_false, &call_runtime);
|
&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(&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);
|
Bind(&return_true);
|
||||||
Return(BooleanConstant(true));
|
Return(BooleanConstant(true));
|
||||||
|
|
||||||
|
@ -1565,6 +1565,9 @@ Node* CodeStubAssembler::AllocateHeapNumberWithValue(Node* value,
|
|||||||
Node* CodeStubAssembler::AllocateSeqOneByteString(int length,
|
Node* CodeStubAssembler::AllocateSeqOneByteString(int length,
|
||||||
AllocationFlags flags) {
|
AllocationFlags flags) {
|
||||||
Comment("AllocateSeqOneByteString");
|
Comment("AllocateSeqOneByteString");
|
||||||
|
if (length == 0) {
|
||||||
|
return LoadRoot(Heap::kempty_stringRootIndex);
|
||||||
|
}
|
||||||
Node* result = Allocate(SeqOneByteString::SizeFor(length), flags);
|
Node* result = Allocate(SeqOneByteString::SizeFor(length), flags);
|
||||||
DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex));
|
DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex));
|
||||||
StoreMapNoWriteBarrier(result, Heap::kOneByteStringMapRootIndex);
|
StoreMapNoWriteBarrier(result, Heap::kOneByteStringMapRootIndex);
|
||||||
@ -1584,8 +1587,10 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
|
|||||||
Variable var_result(this, MachineRepresentation::kTagged);
|
Variable var_result(this, MachineRepresentation::kTagged);
|
||||||
|
|
||||||
// Compute the SeqOneByteString size and check if it fits into new space.
|
// Compute the SeqOneByteString size and check if it fits into new space.
|
||||||
Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
|
Label if_lengthiszero(this), if_sizeissmall(this),
|
||||||
if_join(this);
|
if_notsizeissmall(this, Label::kDeferred), if_join(this);
|
||||||
|
GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero);
|
||||||
|
|
||||||
Node* raw_size = GetArrayAllocationSize(
|
Node* raw_size = GetArrayAllocationSize(
|
||||||
length, UINT8_ELEMENTS, mode,
|
length, UINT8_ELEMENTS, mode,
|
||||||
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
|
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
|
||||||
@ -1618,6 +1623,12 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
|
|||||||
Goto(&if_join);
|
Goto(&if_join);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bind(&if_lengthiszero);
|
||||||
|
{
|
||||||
|
var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex));
|
||||||
|
Goto(&if_join);
|
||||||
|
}
|
||||||
|
|
||||||
Bind(&if_join);
|
Bind(&if_join);
|
||||||
return var_result.value();
|
return var_result.value();
|
||||||
}
|
}
|
||||||
@ -1625,6 +1636,9 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
|
|||||||
Node* CodeStubAssembler::AllocateSeqTwoByteString(int length,
|
Node* CodeStubAssembler::AllocateSeqTwoByteString(int length,
|
||||||
AllocationFlags flags) {
|
AllocationFlags flags) {
|
||||||
Comment("AllocateSeqTwoByteString");
|
Comment("AllocateSeqTwoByteString");
|
||||||
|
if (length == 0) {
|
||||||
|
return LoadRoot(Heap::kempty_stringRootIndex);
|
||||||
|
}
|
||||||
Node* result = Allocate(SeqTwoByteString::SizeFor(length), flags);
|
Node* result = Allocate(SeqTwoByteString::SizeFor(length), flags);
|
||||||
DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex));
|
DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex));
|
||||||
StoreMapNoWriteBarrier(result, Heap::kStringMapRootIndex);
|
StoreMapNoWriteBarrier(result, Heap::kStringMapRootIndex);
|
||||||
@ -1644,8 +1658,10 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length,
|
|||||||
Variable var_result(this, MachineRepresentation::kTagged);
|
Variable var_result(this, MachineRepresentation::kTagged);
|
||||||
|
|
||||||
// Compute the SeqTwoByteString size and check if it fits into new space.
|
// Compute the SeqTwoByteString size and check if it fits into new space.
|
||||||
Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
|
Label if_lengthiszero(this), if_sizeissmall(this),
|
||||||
if_join(this);
|
if_notsizeissmall(this, Label::kDeferred), if_join(this);
|
||||||
|
GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero);
|
||||||
|
|
||||||
Node* raw_size = GetArrayAllocationSize(
|
Node* raw_size = GetArrayAllocationSize(
|
||||||
length, UINT16_ELEMENTS, mode,
|
length, UINT16_ELEMENTS, mode,
|
||||||
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
|
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
|
||||||
@ -1680,6 +1696,12 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length,
|
|||||||
Goto(&if_join);
|
Goto(&if_join);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bind(&if_lengthiszero);
|
||||||
|
{
|
||||||
|
var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex));
|
||||||
|
Goto(&if_join);
|
||||||
|
}
|
||||||
|
|
||||||
Bind(&if_join);
|
Bind(&if_join);
|
||||||
return var_result.value();
|
return var_result.value();
|
||||||
}
|
}
|
||||||
@ -2982,7 +3004,7 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
|
|||||||
// Translate the {index} into a Word.
|
// Translate the {index} into a Word.
|
||||||
index = ParameterToWord(index, parameter_mode);
|
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_index(this, MachineType::PointerRepresentation());
|
||||||
Variable var_result(this, MachineRepresentation::kWord32);
|
Variable var_result(this, MachineRepresentation::kWord32);
|
||||||
Variable var_string(this, MachineRepresentation::kTagged);
|
Variable var_string(this, MachineRepresentation::kTagged);
|
||||||
@ -3134,14 +3156,29 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
|
|||||||
|
|
||||||
Bind(&if_stringisnotexternal);
|
Bind(&if_stringisnotexternal);
|
||||||
{
|
{
|
||||||
// The {string} is a SlicedString, continue with its parent.
|
Label if_stringissliced(this), if_stringisthin(this);
|
||||||
Node* string_offset =
|
Branch(
|
||||||
LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
|
Word32Equal(Word32And(string_instance_type,
|
||||||
Node* string_parent =
|
Int32Constant(kStringRepresentationMask)),
|
||||||
LoadObjectField(string, SlicedString::kParentOffset);
|
Int32Constant(kSlicedStringTag)),
|
||||||
var_index.Bind(IntPtrAdd(index, string_offset));
|
&if_stringissliced, &if_stringisthin);
|
||||||
var_string.Bind(string_parent);
|
Bind(&if_stringissliced);
|
||||||
Goto(&loop);
|
{
|
||||||
|
// 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);
|
Label runtime(this);
|
||||||
|
|
||||||
Variable var_instance_type(this, MachineRepresentation::kWord32); // Int32.
|
Variable var_instance_type(this, MachineRepresentation::kWord32); // Int32.
|
||||||
|
Variable var_representation(this, MachineRepresentation::kWord32); // Int32.
|
||||||
Variable var_result(this, MachineRepresentation::kTagged); // String.
|
Variable var_result(this, MachineRepresentation::kTagged); // String.
|
||||||
Variable var_from(this, MachineRepresentation::kTagged); // Smi.
|
Variable var_from(this, MachineRepresentation::kTagged); // Smi.
|
||||||
Variable var_string(this, MachineRepresentation::kTagged); // String.
|
Variable var_string(this, MachineRepresentation::kTagged); // String.
|
||||||
|
|
||||||
var_instance_type.Bind(Int32Constant(0));
|
var_instance_type.Bind(Int32Constant(0));
|
||||||
|
var_representation.Bind(Int32Constant(0));
|
||||||
var_string.Bind(string);
|
var_string.Bind(string);
|
||||||
var_from.Bind(from);
|
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.
|
// and put the underlying string into var_string.
|
||||||
|
|
||||||
// If the string is not indirect, it can only be sequential or external.
|
// 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);
|
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||||
Label underlying_unpacked(this);
|
Label underlying_unpacked(this);
|
||||||
GotoIf(Word32Equal(
|
GotoIf(Word32Equal(
|
||||||
@ -3325,13 +3365,14 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
|
|||||||
Int32Constant(0)),
|
Int32Constant(0)),
|
||||||
&underlying_unpacked);
|
&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);
|
Label sliced_string(this), thin_or_sliced(this);
|
||||||
GotoIf(Word32NotEqual(
|
var_representation.Bind(
|
||||||
Word32And(instance_type, Int32Constant(kSlicedNotConsMask)),
|
Word32And(instance_type, Int32Constant(kStringRepresentationMask)));
|
||||||
Int32Constant(0)),
|
GotoIf(
|
||||||
&sliced_string);
|
Word32NotEqual(var_representation.value(), Int32Constant(kConsStringTag)),
|
||||||
|
&thin_or_sliced);
|
||||||
|
|
||||||
// Cons string. Check whether it is flat, then fetch first part.
|
// Cons string. Check whether it is flat, then fetch first part.
|
||||||
// Flat cons strings have an empty second 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);
|
Node* first_string_part = LoadObjectField(string, ConsString::kFirstOffset);
|
||||||
var_string.Bind(first_string_part);
|
var_string.Bind(first_string_part);
|
||||||
var_instance_type.Bind(LoadInstanceType(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);
|
Goto(&underlying_unpacked);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bind(&sliced_string);
|
Bind(&sliced_string);
|
||||||
{
|
{
|
||||||
// Fetch parent and correct start index by offset.
|
// 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));
|
var_from.Bind(SmiAdd(from, sliced_offset));
|
||||||
|
|
||||||
Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset);
|
Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset);
|
||||||
@ -4123,45 +4183,6 @@ Node* CodeStubAssembler::ToString(Node* context, Node* input) {
|
|||||||
return result.value();
|
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) {
|
Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) {
|
||||||
Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this);
|
Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this);
|
||||||
Variable result(this, MachineRepresentation::kTagged);
|
Variable result(this, MachineRepresentation::kTagged);
|
||||||
@ -4303,17 +4324,19 @@ void CodeStubAssembler::Use(Label* label) {
|
|||||||
|
|
||||||
void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
|
void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
|
||||||
Variable* var_index, Label* if_keyisunique,
|
Variable* var_index, Label* if_keyisunique,
|
||||||
Label* if_bailout) {
|
Variable* var_unique, Label* if_bailout) {
|
||||||
DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep());
|
DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep());
|
||||||
|
DCHECK_EQ(MachineRepresentation::kTagged, var_unique->rep());
|
||||||
Comment("TryToName");
|
Comment("TryToName");
|
||||||
|
|
||||||
Label if_hascachedindex(this), if_keyisnotindex(this);
|
Label if_hascachedindex(this), if_keyisnotindex(this), if_thinstring(this);
|
||||||
// Handle Smi and HeapNumber keys.
|
// Handle Smi and HeapNumber keys.
|
||||||
var_index->Bind(TryToIntptr(key, &if_keyisnotindex));
|
var_index->Bind(TryToIntptr(key, &if_keyisnotindex));
|
||||||
Goto(if_keyisindex);
|
Goto(if_keyisindex);
|
||||||
|
|
||||||
Bind(&if_keyisnotindex);
|
Bind(&if_keyisnotindex);
|
||||||
Node* key_map = LoadMap(key);
|
Node* key_map = LoadMap(key);
|
||||||
|
var_unique->Bind(key);
|
||||||
// Symbols are unique.
|
// Symbols are unique.
|
||||||
GotoIf(IsSymbolMap(key_map), if_keyisunique);
|
GotoIf(IsSymbolMap(key_map), if_keyisunique);
|
||||||
Node* key_instance_type = LoadMapInstanceType(key_map);
|
Node* key_instance_type = LoadMapInstanceType(key_map);
|
||||||
@ -4330,6 +4353,12 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
|
|||||||
Node* not_an_index =
|
Node* not_an_index =
|
||||||
Word32And(hash, Int32Constant(Name::kIsNotArrayIndexMask));
|
Word32And(hash, Int32Constant(Name::kIsNotArrayIndexMask));
|
||||||
GotoIf(Word32Equal(not_an_index, Int32Constant(0)), if_bailout);
|
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.
|
// Finally, check if |key| is internalized.
|
||||||
STATIC_ASSERT(kNotInternalizedTag != 0);
|
STATIC_ASSERT(kNotInternalizedTag != 0);
|
||||||
Node* not_internalized =
|
Node* not_internalized =
|
||||||
@ -4337,6 +4366,10 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
|
|||||||
GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), if_bailout);
|
GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), if_bailout);
|
||||||
Goto(if_keyisunique);
|
Goto(if_keyisunique);
|
||||||
|
|
||||||
|
Bind(&if_thinstring);
|
||||||
|
var_unique->Bind(LoadObjectField(key, ThinString::kActualOffset));
|
||||||
|
Goto(if_keyisunique);
|
||||||
|
|
||||||
Bind(&if_hascachedindex);
|
Bind(&if_hascachedindex);
|
||||||
var_index->Bind(DecodeWordFromWord32<Name::ArrayIndexValueBits>(hash));
|
var_index->Bind(DecodeWordFromWord32<Name::ArrayIndexValueBits>(hash));
|
||||||
Goto(if_keyisindex);
|
Goto(if_keyisindex);
|
||||||
@ -5186,9 +5219,11 @@ void CodeStubAssembler::TryPrototypeChainLookup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Variable var_index(this, MachineType::PointerRepresentation());
|
Variable var_index(this, MachineType::PointerRepresentation());
|
||||||
|
Variable var_unique(this, MachineRepresentation::kTagged);
|
||||||
|
|
||||||
Label if_keyisindex(this), if_iskeyunique(this);
|
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);
|
Bind(&if_iskeyunique);
|
||||||
{
|
{
|
||||||
@ -5210,8 +5245,8 @@ void CodeStubAssembler::TryPrototypeChainLookup(
|
|||||||
|
|
||||||
Label next_proto(this);
|
Label next_proto(this);
|
||||||
lookup_property_in_holder(receiver, var_holder.value(), holder_map,
|
lookup_property_in_holder(receiver, var_holder.value(), holder_map,
|
||||||
holder_instance_type, key, &next_proto,
|
holder_instance_type, var_unique.value(),
|
||||||
if_bailout);
|
&next_proto, if_bailout);
|
||||||
Bind(&next_proto);
|
Bind(&next_proto);
|
||||||
|
|
||||||
// Bailout if it can be an integer indexed exotic case.
|
// Bailout if it can be an integer indexed exotic case.
|
||||||
|
@ -729,9 +729,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
// Convert any object to a Primitive.
|
// Convert any object to a Primitive.
|
||||||
Node* JSReceiverToPrimitive(Node* context, Node* input);
|
Node* JSReceiverToPrimitive(Node* context, Node* input);
|
||||||
|
|
||||||
// Convert a String to a flat String.
|
|
||||||
Node* FlattenString(Node* string);
|
|
||||||
|
|
||||||
enum ToIntegerTruncationMode {
|
enum ToIntegerTruncationMode {
|
||||||
kNoTruncation,
|
kNoTruncation,
|
||||||
kTruncateMinusZero,
|
kTruncateMinusZero,
|
||||||
@ -844,7 +841,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
|||||||
|
|
||||||
// Various building blocks for stubs doing property lookups.
|
// Various building blocks for stubs doing property lookups.
|
||||||
void TryToName(Node* key, Label* if_keyisindex, Variable* var_index,
|
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.
|
// Calculates array index for given dictionary entry and entry field.
|
||||||
// See Dictionary::EntryToIndex().
|
// See Dictionary::EntryToIndex().
|
||||||
|
@ -73,7 +73,7 @@ void CodeStubDescriptor::Initialize(Register stack_parameter_count,
|
|||||||
|
|
||||||
bool CodeStub::FindCodeInCache(Code** code_out) {
|
bool CodeStub::FindCodeInCache(Code** code_out) {
|
||||||
UnseededNumberDictionary* stubs = isolate()->heap()->code_stubs();
|
UnseededNumberDictionary* stubs = isolate()->heap()->code_stubs();
|
||||||
int index = stubs->FindEntry(GetKey());
|
int index = stubs->FindEntry(isolate(), GetKey());
|
||||||
if (index != UnseededNumberDictionary::kNotFound) {
|
if (index != UnseededNumberDictionary::kNotFound) {
|
||||||
*code_out = Code::cast(stubs->ValueAt(index));
|
*code_out = Code::cast(stubs->ValueAt(index));
|
||||||
return true;
|
return true;
|
||||||
|
@ -502,6 +502,15 @@ FieldAccess AccessBuilder::ForConsStringSecond() {
|
|||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
FieldAccess AccessBuilder::ForThinStringActual() {
|
||||||
|
FieldAccess access = {kTaggedBase, ThinString::kActualOffset,
|
||||||
|
Handle<Name>(), MaybeHandle<Map>(),
|
||||||
|
Type::String(), MachineType::TaggedPointer(),
|
||||||
|
kPointerWriteBarrier};
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
FieldAccess AccessBuilder::ForSlicedStringOffset() {
|
FieldAccess AccessBuilder::ForSlicedStringOffset() {
|
||||||
FieldAccess access = {kTaggedBase, SlicedString::kOffsetOffset,
|
FieldAccess access = {kTaggedBase, SlicedString::kOffsetOffset,
|
||||||
|
@ -167,6 +167,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
|
|||||||
// Provides access to ConsString::second() field.
|
// Provides access to ConsString::second() field.
|
||||||
static FieldAccess ForConsStringSecond();
|
static FieldAccess ForConsStringSecond();
|
||||||
|
|
||||||
|
// Provides access to ThinString::actual() field.
|
||||||
|
static FieldAccess ForThinStringActual();
|
||||||
|
|
||||||
// Provides access to SlicedString::offset() field.
|
// Provides access to SlicedString::offset() field.
|
||||||
static FieldAccess ForSlicedStringOffset();
|
static FieldAccess ForSlicedStringOffset();
|
||||||
|
|
||||||
|
@ -152,6 +152,8 @@ Type::bitset BitsetType::Lub(i::Map* map) {
|
|||||||
case ONE_BYTE_STRING_TYPE:
|
case ONE_BYTE_STRING_TYPE:
|
||||||
case CONS_STRING_TYPE:
|
case CONS_STRING_TYPE:
|
||||||
case CONS_ONE_BYTE_STRING_TYPE:
|
case CONS_ONE_BYTE_STRING_TYPE:
|
||||||
|
case THIN_STRING_TYPE:
|
||||||
|
case THIN_ONE_BYTE_STRING_TYPE:
|
||||||
case SLICED_STRING_TYPE:
|
case SLICED_STRING_TYPE:
|
||||||
case SLICED_ONE_BYTE_STRING_TYPE:
|
case SLICED_ONE_BYTE_STRING_TYPE:
|
||||||
case EXTERNAL_STRING_TYPE:
|
case EXTERNAL_STRING_TYPE:
|
||||||
|
@ -187,7 +187,7 @@ static void CopyDictionaryToObjectElements(
|
|||||||
: SKIP_WRITE_BARRIER;
|
: SKIP_WRITE_BARRIER;
|
||||||
Isolate* isolate = from->GetIsolate();
|
Isolate* isolate = from->GetIsolate();
|
||||||
for (int i = 0; i < copy_size; i++) {
|
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) {
|
if (entry != SeededNumberDictionary::kNotFound) {
|
||||||
Object* value = from->ValueAt(entry);
|
Object* value = from->ValueAt(entry);
|
||||||
DCHECK(!value->IsTheHole(isolate));
|
DCHECK(!value->IsTheHole(isolate));
|
||||||
@ -417,8 +417,9 @@ static void CopyDictionaryToDoubleElements(FixedArrayBase* from_base,
|
|||||||
if (to_start + copy_size > to_length) {
|
if (to_start + copy_size > to_length) {
|
||||||
copy_size = to_length - to_start;
|
copy_size = to_length - to_start;
|
||||||
}
|
}
|
||||||
|
Isolate* isolate = from->GetIsolate();
|
||||||
for (int i = 0; i < copy_size; i++) {
|
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) {
|
if (entry != SeededNumberDictionary::kNotFound) {
|
||||||
to->set(i + to_start, from->ValueAt(entry)->Number());
|
to->set(i + to_start, from->ValueAt(entry)->Number());
|
||||||
} else {
|
} else {
|
||||||
@ -1628,7 +1629,7 @@ class DictionaryElementsAccessor
|
|||||||
// Iterate through entire range, as accessing elements out of order is
|
// Iterate through entire range, as accessing elements out of order is
|
||||||
// observable
|
// observable
|
||||||
for (uint32_t k = start_from; k < length; ++k) {
|
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 (entry == SeededNumberDictionary::kNotFound) {
|
||||||
if (search_for_hole) return Just(true);
|
if (search_for_hole) return Just(true);
|
||||||
continue;
|
continue;
|
||||||
@ -1694,7 +1695,7 @@ class DictionaryElementsAccessor
|
|||||||
// Iterate through entire range, as accessing elements out of order is
|
// Iterate through entire range, as accessing elements out of order is
|
||||||
// observable.
|
// observable.
|
||||||
for (uint32_t k = start_from; k < length; ++k) {
|
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 (entry == SeededNumberDictionary::kNotFound) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -278,6 +278,7 @@ Handle<String> Factory::InternalizeStringWithKey(StringTableKey* key) {
|
|||||||
MaybeHandle<String> Factory::NewStringFromOneByte(Vector<const uint8_t> string,
|
MaybeHandle<String> Factory::NewStringFromOneByte(Vector<const uint8_t> string,
|
||||||
PretenureFlag pretenure) {
|
PretenureFlag pretenure) {
|
||||||
int length = string.length();
|
int length = string.length();
|
||||||
|
if (length == 0) return empty_string();
|
||||||
if (length == 1) return LookupSingleCharacterStringFromCode(string[0]);
|
if (length == 1) return LookupSingleCharacterStringFromCode(string[0]);
|
||||||
Handle<SeqOneByteString> result;
|
Handle<SeqOneByteString> result;
|
||||||
ASSIGN_RETURN_ON_EXCEPTION(
|
ASSIGN_RETURN_ON_EXCEPTION(
|
||||||
@ -371,6 +372,7 @@ MaybeHandle<String> Factory::NewStringFromUtf8SubString(
|
|||||||
MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string,
|
MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string,
|
||||||
int length,
|
int length,
|
||||||
PretenureFlag pretenure) {
|
PretenureFlag pretenure) {
|
||||||
|
if (length == 0) return empty_string();
|
||||||
if (String::IsOneByte(string, length)) {
|
if (String::IsOneByte(string, length)) {
|
||||||
if (length == 1) return LookupSingleCharacterStringFromCode(string[0]);
|
if (length == 1) return LookupSingleCharacterStringFromCode(string[0]);
|
||||||
Handle<SeqOneByteString> result;
|
Handle<SeqOneByteString> result;
|
||||||
@ -455,38 +457,63 @@ Handle<String> Factory::NewInternalizedStringImpl(
|
|||||||
String);
|
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(
|
MaybeHandle<Map> Factory::InternalizedStringMapForString(
|
||||||
Handle<String> string) {
|
Handle<String> string) {
|
||||||
// If the string is in new space it cannot be used as internalized.
|
// If the string is in new space it cannot be used as internalized.
|
||||||
if (isolate()->heap()->InNewSpace(*string)) return MaybeHandle<Map>();
|
if (isolate()->heap()->InNewSpace(*string)) return MaybeHandle<Map>();
|
||||||
|
|
||||||
// Find the corresponding internalized string map for strings.
|
return GetInternalizedStringMap(this, string);
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
MaybeHandle<SeqOneByteString> Factory::NewRawOneByteString(
|
||||||
int length, PretenureFlag pretenure) {
|
int length, PretenureFlag pretenure) {
|
||||||
if (length > String::kMaxLength || length < 0) {
|
if (length > String::kMaxLength || length < 0) {
|
||||||
THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqOneByteString);
|
THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqOneByteString);
|
||||||
}
|
}
|
||||||
|
DCHECK(length > 0); // Use Factory::empty_string() instead.
|
||||||
CALL_HEAP_FUNCTION(
|
CALL_HEAP_FUNCTION(
|
||||||
isolate(),
|
isolate(),
|
||||||
isolate()->heap()->AllocateRawOneByteString(length, pretenure),
|
isolate()->heap()->AllocateRawOneByteString(length, pretenure),
|
||||||
@ -499,6 +526,7 @@ MaybeHandle<SeqTwoByteString> Factory::NewRawTwoByteString(
|
|||||||
if (length > String::kMaxLength || length < 0) {
|
if (length > String::kMaxLength || length < 0) {
|
||||||
THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqTwoByteString);
|
THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqTwoByteString);
|
||||||
}
|
}
|
||||||
|
DCHECK(length > 0); // Use Factory::empty_string() instead.
|
||||||
CALL_HEAP_FUNCTION(
|
CALL_HEAP_FUNCTION(
|
||||||
isolate(),
|
isolate(),
|
||||||
isolate()->heap()->AllocateRawTwoByteString(length, pretenure),
|
isolate()->heap()->AllocateRawTwoByteString(length, pretenure),
|
||||||
@ -587,6 +615,12 @@ Handle<String> ConcatStringContent(Handle<StringType> result,
|
|||||||
|
|
||||||
MaybeHandle<String> Factory::NewConsString(Handle<String> left,
|
MaybeHandle<String> Factory::NewConsString(Handle<String> left,
|
||||||
Handle<String> right) {
|
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();
|
int left_length = left->length();
|
||||||
if (left_length == 0) return right;
|
if (left_length == 0) return right;
|
||||||
int right_length = right->length();
|
int right_length = right->length();
|
||||||
|
@ -227,6 +227,11 @@ class V8_EXPORT_PRIVATE Factory final {
|
|||||||
MUST_USE_RESULT MaybeHandle<Map> InternalizedStringMapForString(
|
MUST_USE_RESULT MaybeHandle<Map> InternalizedStringMapForString(
|
||||||
Handle<String> string);
|
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
|
// Allocates and partially initializes an one-byte or two-byte String. The
|
||||||
// characters of the string are uninitialized. Currently used in regexp code
|
// characters of the string are uninitialized. Currently used in regexp code
|
||||||
// only, where they are pretenured.
|
// only, where they are pretenured.
|
||||||
|
@ -225,6 +225,8 @@ AllocationResult Heap::AllocateInternalizedStringImpl(T t, int chars,
|
|||||||
AllocationResult Heap::AllocateOneByteInternalizedString(
|
AllocationResult Heap::AllocateOneByteInternalizedString(
|
||||||
Vector<const uint8_t> str, uint32_t hash_field) {
|
Vector<const uint8_t> str, uint32_t hash_field) {
|
||||||
CHECK_GE(String::kMaxLength, str.length());
|
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.
|
// Compute map and object size.
|
||||||
Map* map = one_byte_internalized_string_map();
|
Map* map = one_byte_internalized_string_map();
|
||||||
int size = SeqOneByteString::SizeFor(str.length());
|
int size = SeqOneByteString::SizeFor(str.length());
|
||||||
@ -256,6 +258,7 @@ AllocationResult Heap::AllocateOneByteInternalizedString(
|
|||||||
AllocationResult Heap::AllocateTwoByteInternalizedString(Vector<const uc16> str,
|
AllocationResult Heap::AllocateTwoByteInternalizedString(Vector<const uc16> str,
|
||||||
uint32_t hash_field) {
|
uint32_t hash_field) {
|
||||||
CHECK_GE(String::kMaxLength, str.length());
|
CHECK_GE(String::kMaxLength, str.length());
|
||||||
|
DCHECK_NE(0, str.length()); // Use Heap::empty_string() instead.
|
||||||
// Compute map and object size.
|
// Compute map and object size.
|
||||||
Map* map = internalized_string_map();
|
Map* map = internalized_string_map();
|
||||||
int size = SeqTwoByteString::SizeFor(str.length());
|
int size = SeqTwoByteString::SizeFor(str.length());
|
||||||
|
@ -1738,12 +1738,21 @@ String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap,
|
|||||||
|
|
||||||
if (!first_word.IsForwardingAddress()) {
|
if (!first_word.IsForwardingAddress()) {
|
||||||
// Unreachable external string can be finalized.
|
// 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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// String is still reachable.
|
// 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;
|
int last = 0;
|
||||||
Isolate* isolate = heap_->isolate();
|
Isolate* isolate = heap_->isolate();
|
||||||
for (int i = 0; i < new_space_strings_.length(); ++i) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
DCHECK(new_space_strings_[i]->IsExternalString());
|
if (o->IsThinString()) {
|
||||||
if (heap_->InNewSpace(new_space_strings_[i])) {
|
o = ThinString::cast(o)->actual();
|
||||||
new_space_strings_[last++] = new_space_strings_[i];
|
if (!o->IsExternalString()) continue;
|
||||||
|
}
|
||||||
|
DCHECK(o->IsExternalString());
|
||||||
|
if (heap_->InNewSpace(o)) {
|
||||||
|
new_space_strings_[last++] = o;
|
||||||
} else {
|
} else {
|
||||||
old_space_strings_.Add(new_space_strings_[i]);
|
old_space_strings_.Add(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
new_space_strings_.Rewind(last);
|
new_space_strings_.Rewind(last);
|
||||||
@ -6432,12 +6446,17 @@ void Heap::ExternalStringTable::CleanUpAll() {
|
|||||||
int last = 0;
|
int last = 0;
|
||||||
Isolate* isolate = heap_->isolate();
|
Isolate* isolate = heap_->isolate();
|
||||||
for (int i = 0; i < old_space_strings_.length(); ++i) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
DCHECK(old_space_strings_[i]->IsExternalString());
|
if (o->IsThinString()) {
|
||||||
DCHECK(!heap_->InNewSpace(old_space_strings_[i]));
|
o = ThinString::cast(o)->actual();
|
||||||
old_space_strings_[last++] = old_space_strings_[i];
|
if (!o->IsExternalString()) continue;
|
||||||
|
}
|
||||||
|
DCHECK(o->IsExternalString());
|
||||||
|
DCHECK(!heap_->InNewSpace(o));
|
||||||
|
old_space_strings_[last++] = o;
|
||||||
}
|
}
|
||||||
old_space_strings_.Rewind(last);
|
old_space_strings_.Rewind(last);
|
||||||
old_space_strings_.Trim();
|
old_space_strings_.Trim();
|
||||||
@ -6450,11 +6469,21 @@ void Heap::ExternalStringTable::CleanUpAll() {
|
|||||||
|
|
||||||
void Heap::ExternalStringTable::TearDown() {
|
void Heap::ExternalStringTable::TearDown() {
|
||||||
for (int i = 0; i < new_space_strings_.length(); ++i) {
|
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();
|
new_space_strings_.Free();
|
||||||
for (int i = 0; i < old_space_strings_.length(); ++i) {
|
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();
|
old_space_strings_.Free();
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,8 @@ using v8::MemoryPressureLevel;
|
|||||||
V(Map, string_map, StringMap) \
|
V(Map, string_map, StringMap) \
|
||||||
V(Map, cons_one_byte_string_map, ConsOneByteStringMap) \
|
V(Map, cons_one_byte_string_map, ConsOneByteStringMap) \
|
||||||
V(Map, cons_string_map, ConsStringMap) \
|
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_string_map, SlicedStringMap) \
|
||||||
V(Map, sliced_one_byte_string_map, SlicedOneByteStringMap) \
|
V(Map, sliced_one_byte_string_map, SlicedOneByteStringMap) \
|
||||||
V(Map, external_string_map, ExternalStringMap) \
|
V(Map, external_string_map, ExternalStringMap) \
|
||||||
@ -2131,10 +2133,6 @@ class Heap {
|
|||||||
MUST_USE_RESULT AllocationResult
|
MUST_USE_RESULT AllocationResult
|
||||||
AllocateCode(int object_size, bool immovable);
|
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; }
|
void set_force_oom(bool value) { force_oom_ = value; }
|
||||||
|
@ -1437,8 +1437,12 @@ class StringTableCleaner : public ObjectVisitor {
|
|||||||
if (o->IsHeapObject()) {
|
if (o->IsHeapObject()) {
|
||||||
if (Marking::IsWhite(ObjectMarking::MarkBitFrom(HeapObject::cast(o)))) {
|
if (Marking::IsWhite(ObjectMarking::MarkBitFrom(HeapObject::cast(o)))) {
|
||||||
if (finalize_external_strings) {
|
if (finalize_external_strings) {
|
||||||
DCHECK(o->IsExternalString());
|
if (o->IsExternalString()) {
|
||||||
heap_->FinalizeExternalString(String::cast(*p));
|
heap_->FinalizeExternalString(String::cast(*p));
|
||||||
|
} else {
|
||||||
|
// The original external string may have been internalized.
|
||||||
|
DCHECK(o->IsThinString());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pointers_removed_++;
|
pointers_removed_++;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,10 @@ void StaticNewSpaceVisitor<StaticVisitor>::Initialize() {
|
|||||||
kVisitConsString,
|
kVisitConsString,
|
||||||
&FixedBodyVisitor<StaticVisitor, ConsString::BodyDescriptor, int>::Visit);
|
&FixedBodyVisitor<StaticVisitor, ConsString::BodyDescriptor, int>::Visit);
|
||||||
|
|
||||||
|
table_.Register(
|
||||||
|
kVisitThinString,
|
||||||
|
&FixedBodyVisitor<StaticVisitor, ThinString::BodyDescriptor, int>::Visit);
|
||||||
|
|
||||||
table_.Register(kVisitSlicedString,
|
table_.Register(kVisitSlicedString,
|
||||||
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
|
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
|
||||||
int>::Visit);
|
int>::Visit);
|
||||||
@ -117,6 +121,10 @@ void StaticMarkingVisitor<StaticVisitor>::Initialize() {
|
|||||||
&FixedBodyVisitor<StaticVisitor, ConsString::BodyDescriptor,
|
&FixedBodyVisitor<StaticVisitor, ConsString::BodyDescriptor,
|
||||||
void>::Visit);
|
void>::Visit);
|
||||||
|
|
||||||
|
table_.Register(kVisitThinString,
|
||||||
|
&FixedBodyVisitor<StaticVisitor, ThinString::BodyDescriptor,
|
||||||
|
void>::Visit);
|
||||||
|
|
||||||
table_.Register(kVisitSlicedString,
|
table_.Register(kVisitSlicedString,
|
||||||
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
|
&FixedBodyVisitor<StaticVisitor, SlicedString::BodyDescriptor,
|
||||||
void>::Visit);
|
void>::Visit);
|
||||||
|
@ -41,6 +41,9 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
|
|||||||
case kExternalStringTag:
|
case kExternalStringTag:
|
||||||
return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric,
|
return GetVisitorIdForSize(kVisitDataObject, kVisitDataObjectGeneric,
|
||||||
instance_size, has_unboxed_fields);
|
instance_size, has_unboxed_fields);
|
||||||
|
|
||||||
|
case kThinStringTag:
|
||||||
|
return kVisitThinString;
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,7 @@ class StaticVisitorBase : public AllStatic {
|
|||||||
V(StructGeneric) \
|
V(StructGeneric) \
|
||||||
V(ConsString) \
|
V(ConsString) \
|
||||||
V(SlicedString) \
|
V(SlicedString) \
|
||||||
|
V(ThinString) \
|
||||||
V(Symbol) \
|
V(Symbol) \
|
||||||
V(Oddball) \
|
V(Oddball) \
|
||||||
V(Code) \
|
V(Code) \
|
||||||
|
@ -30,6 +30,7 @@ class ScavengingVisitor : public StaticVisitorBase {
|
|||||||
table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString);
|
table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString);
|
||||||
table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString);
|
table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString);
|
||||||
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
|
table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
|
||||||
|
table_.Register(kVisitThinString, &EvacuateThinString);
|
||||||
table_.Register(kVisitByteArray, &EvacuateByteArray);
|
table_.Register(kVisitByteArray, &EvacuateByteArray);
|
||||||
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
|
table_.Register(kVisitFixedArray, &EvacuateFixedArray);
|
||||||
table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray);
|
table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray);
|
||||||
@ -89,6 +90,12 @@ class ScavengingVisitor : public StaticVisitorBase {
|
|||||||
return &table_;
|
return &table_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void EvacuateThinStringNoShortcut(Map* map, HeapObject** slot,
|
||||||
|
HeapObject* object) {
|
||||||
|
EvacuateObject<POINTER_OBJECT, kWordAligned>(map, slot, object,
|
||||||
|
ThinString::kSize);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum ObjectContents { DATA_OBJECT, POINTER_OBJECT };
|
enum ObjectContents { DATA_OBJECT, POINTER_OBJECT };
|
||||||
|
|
||||||
@ -339,6 +346,22 @@ class ScavengingVisitor : public StaticVisitorBase {
|
|||||||
object_size);
|
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>
|
template <ObjectContents object_contents>
|
||||||
class ObjectEvacuationStrategy {
|
class ObjectEvacuationStrategy {
|
||||||
public:
|
public:
|
||||||
@ -423,6 +446,10 @@ void Scavenger::SelectScavengingVisitorsTable() {
|
|||||||
StaticVisitorBase::kVisitShortcutCandidate,
|
StaticVisitorBase::kVisitShortcutCandidate,
|
||||||
scavenging_visitors_table_.GetVisitorById(
|
scavenging_visitors_table_.GetVisitorById(
|
||||||
StaticVisitorBase::kVisitConsString));
|
StaticVisitorBase::kVisitConsString));
|
||||||
|
scavenging_visitors_table_.Register(
|
||||||
|
StaticVisitorBase::kVisitThinString,
|
||||||
|
&ScavengingVisitor<TRANSFER_MARKS, LOGGING_AND_PROFILING_DISABLED>::
|
||||||
|
EvacuateThinStringNoShortcut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,7 +604,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (8) Is the external string one byte? If yes, go to (5).
|
// (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).
|
// (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.
|
// (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 */,
|
Label seq_one_byte_string /* 5 */, seq_two_byte_string /* 9 */,
|
||||||
external_string /* 7 */, check_underlying /* 1 */,
|
external_string /* 7 */, check_underlying /* 1 */,
|
||||||
@ -634,6 +634,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// have already been covered.
|
// have already been covered.
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
__ cmp(ebx, Immediate(kExternalStringTag));
|
__ cmp(ebx, Immediate(kExternalStringTag));
|
||||||
@ -912,11 +913,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
|
__ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
|
||||||
__ j(not_zero, &runtime);
|
__ 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.
|
// Load offset into edi and replace subject string with parent.
|
||||||
__ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
|
__ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
|
||||||
__ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
|
__ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
|
||||||
__ jmp(&check_underlying); // Go to (1).
|
__ 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
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,6 +491,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
Register index,
|
Register index,
|
||||||
Register result,
|
Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
|
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
|
||||||
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -501,17 +504,24 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
__ j(zero, &check_sequential, Label::kNear);
|
__ j(zero, &check_sequential, Label::kNear);
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ test(result, Immediate(kSlicedNotConsMask));
|
__ and_(result, Immediate(kStringRepresentationMask));
|
||||||
__ j(zero, &cons_string, Label::kNear);
|
__ cmp(result, Immediate(kConsStringTag));
|
||||||
|
__ j(equal, &cons_string, Label::kNear);
|
||||||
|
__ cmp(result, Immediate(kThinStringTag));
|
||||||
|
__ j(equal, &thin_string, Label::kNear);
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
|
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ SmiUntag(result);
|
__ SmiUntag(result);
|
||||||
__ add(index, result);
|
__ add(index, result);
|
||||||
__ mov(string, FieldOperand(string, SlicedString::kParentOffset));
|
__ 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.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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()));
|
Immediate(factory->empty_string()));
|
||||||
__ j(not_equal, call_runtime);
|
__ j(not_equal, call_runtime);
|
||||||
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
|
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -2566,11 +2566,13 @@ void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(Register object1,
|
|||||||
const int kFlatOneByteStringTag =
|
const int kFlatOneByteStringTag =
|
||||||
kStringTag | kOneByteStringTag | kSeqStringTag;
|
kStringTag | kOneByteStringTag | kSeqStringTag;
|
||||||
// Interleave bits from both instance types and compare them in one check.
|
// 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_(scratch1, kFlatOneByteStringMask);
|
||||||
and_(scratch2, kFlatOneByteStringMask);
|
and_(scratch2, kFlatOneByteStringMask);
|
||||||
lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
|
shl(scratch2, kShift);
|
||||||
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << 3));
|
or_(scratch1, scratch2);
|
||||||
|
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << kShift));
|
||||||
j(not_equal, failure);
|
j(not_equal, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1394,6 +1394,8 @@ void AccessorAssemblerImpl::KeyedLoadIC(const LoadICParameters* p) {
|
|||||||
|
|
||||||
void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
|
void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
|
||||||
Variable var_index(this, MachineType::PointerRepresentation());
|
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_details(this, MachineRepresentation::kWord32);
|
||||||
Variable var_value(this, MachineRepresentation::kTagged);
|
Variable var_value(this, MachineRepresentation::kTagged);
|
||||||
Label if_index(this), if_unique_name(this), if_element_hole(this),
|
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)),
|
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
|
||||||
&slow);
|
&slow);
|
||||||
|
|
||||||
Node* key = p->name;
|
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
|
||||||
TryToName(key, &if_index, &var_index, &if_unique_name, &slow);
|
&slow);
|
||||||
|
|
||||||
Bind(&if_index);
|
Bind(&if_index);
|
||||||
{
|
{
|
||||||
@ -1460,6 +1462,7 @@ void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
|
|||||||
Bind(&if_unique_name);
|
Bind(&if_unique_name);
|
||||||
{
|
{
|
||||||
Comment("key is unique name");
|
Comment("key is unique name");
|
||||||
|
Node* key = var_unique.value();
|
||||||
// Check if the receiver has fast or slow properties.
|
// Check if the receiver has fast or slow properties.
|
||||||
properties = LoadProperties(receiver);
|
properties = LoadProperties(receiver);
|
||||||
Node* properties_map = LoadMap(properties);
|
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
|
// We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
|
||||||
// seeing global objects here (which would need special handling).
|
// seeing global objects here (which would need special handling).
|
||||||
|
|
||||||
|
Node* key = var_unique.value();
|
||||||
Variable var_name_index(this, MachineType::PointerRepresentation());
|
Variable var_name_index(this, MachineType::PointerRepresentation());
|
||||||
Label dictionary_found(this, &var_name_index);
|
Label dictionary_found(this, &var_name_index);
|
||||||
NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found,
|
NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found,
|
||||||
|
@ -1578,6 +1578,8 @@ static Handle<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
|
|||||||
}
|
}
|
||||||
} else if (key->IsUndefined(isolate)) {
|
} else if (key->IsUndefined(isolate)) {
|
||||||
key = isolate->factory()->undefined_string();
|
key = isolate->factory()->undefined_string();
|
||||||
|
} else if (key->IsString()) {
|
||||||
|
key = isolate->factory()->InternalizeString(Handle<String>::cast(key));
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
@ -461,6 +461,8 @@ void KeyedStoreGenericAssembler::EmitGenericElementStore(
|
|||||||
|
|
||||||
// Out-of-capacity accesses (index >= capacity) jump here. Additionally,
|
// Out-of-capacity accesses (index >= capacity) jump here. Additionally,
|
||||||
// an ElementsKind transition might be necessary.
|
// 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);
|
Bind(&if_grow);
|
||||||
{
|
{
|
||||||
Comment("Grow backing store");
|
Comment("Grow backing store");
|
||||||
@ -756,6 +758,8 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
|
|||||||
Node* context = Parameter(Descriptor::kContext);
|
Node* context = Parameter(Descriptor::kContext);
|
||||||
|
|
||||||
Variable var_index(this, MachineType::PointerRepresentation());
|
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);
|
Label if_index(this), if_unique_name(this), slow(this);
|
||||||
|
|
||||||
GotoIf(TaggedIsSmi(receiver), &slow);
|
GotoIf(TaggedIsSmi(receiver), &slow);
|
||||||
@ -767,7 +771,7 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
|
|||||||
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
|
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
|
||||||
&slow);
|
&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);
|
Bind(&if_index);
|
||||||
{
|
{
|
||||||
@ -779,8 +783,8 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
|
|||||||
Bind(&if_unique_name);
|
Bind(&if_unique_name);
|
||||||
{
|
{
|
||||||
Comment("key is unique name");
|
Comment("key is unique name");
|
||||||
KeyedStoreGenericAssembler::StoreICParameters p(context, receiver, name,
|
StoreICParameters p(context, receiver, var_unique.value(), value, slot,
|
||||||
value, slot, vector);
|
vector);
|
||||||
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow, language_mode);
|
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow, language_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1423,7 +1423,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (6) External string. Make it, offset-wise, look like a sequential string.
|
// (6) External string. Make it, offset-wise, look like a sequential string.
|
||||||
// Go to (4).
|
// Go to (4).
|
||||||
// (7) Short external string or not a string? If yes, bail out to runtime.
|
// (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 */,
|
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
|
||||||
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
|
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).
|
// (2) Sequential or cons? If not, go to (5).
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
// Go to (5).
|
// Go to (5).
|
||||||
@ -1470,8 +1471,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ Branch(&runtime, ls, a3, Operand(a1));
|
__ Branch(&runtime, ls, a3, Operand(a1));
|
||||||
__ sra(a1, a1, kSmiTagSize); // Untag the Smi.
|
__ sra(a1, a1, kSmiTagSize); // Untag the Smi.
|
||||||
|
|
||||||
STATIC_ASSERT(kStringEncodingMask == 4);
|
STATIC_ASSERT(kStringEncodingMask == 8);
|
||||||
STATIC_ASSERT(kOneByteStringTag == 4);
|
STATIC_ASSERT(kOneByteStringTag == 8);
|
||||||
STATIC_ASSERT(kTwoByteStringTag == 0);
|
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||||
__ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for one-byte.
|
__ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for one-byte.
|
||||||
__ lw(t9, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset));
|
__ lw(t9, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset));
|
||||||
@ -1720,12 +1721,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask));
|
__ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask));
|
||||||
__ Branch(&runtime, ne, at, Operand(zero_reg));
|
__ 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.
|
// Load offset into t0 and replace subject string with parent.
|
||||||
__ lw(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
__ lw(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
||||||
__ sra(t0, t0, kSmiTagSize);
|
__ sra(t0, t0, kSmiTagSize);
|
||||||
__ lw(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
__ lw(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
||||||
__ jmp(&check_underlying); // Go to (4).
|
__ 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
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,6 +610,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
Register index,
|
Register index,
|
||||||
Register result,
|
Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ lw(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
__ lw(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||||
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -620,18 +623,23 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
__ Branch(&check_sequential, eq, at, Operand(zero_reg));
|
__ Branch(&check_sequential, eq, at, Operand(zero_reg));
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ And(at, result, Operand(kSlicedNotConsMask));
|
__ And(at, result, Operand(kStringRepresentationMask));
|
||||||
__ Branch(&cons_string, eq, at, Operand(zero_reg));
|
__ Branch(&cons_string, eq, at, Operand(kConsStringTag));
|
||||||
|
__ Branch(&thin_string, eq, at, Operand(kThinStringTag));
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ lw(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
__ lw(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ lw(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
__ lw(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
||||||
__ sra(at, result, kSmiTagSize);
|
__ sra(at, result, kSmiTagSize);
|
||||||
__ Addu(index, index, at);
|
__ Addu(index, index, at);
|
||||||
__ jmp(&indirect_string_loaded);
|
__ jmp(&indirect_string_loaded);
|
||||||
|
|
||||||
|
// Handle thin strings.
|
||||||
|
__ bind(&thin_string);
|
||||||
|
__ lw(string, FieldMemOperand(string, ThinString::kActualOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
|
|
||||||
// Handle cons strings.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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
|
// 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));
|
__ Branch(call_runtime, ne, result, Operand(at));
|
||||||
// Get the first of the two strings and load its instance type.
|
// Get the first of the two strings and load its instance type.
|
||||||
__ lw(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
__ lw(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ lw(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -1420,7 +1420,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (6) External string. Make it, offset-wise, look like a sequential string.
|
// (6) External string. Make it, offset-wise, look like a sequential string.
|
||||||
// Go to (4).
|
// Go to (4).
|
||||||
// (7) Short external string or not a string? If yes, bail out to runtime.
|
// (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 check_underlying; // (1)
|
||||||
Label seq_string; // (4)
|
Label seq_string; // (4)
|
||||||
@ -1444,6 +1444,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (2) Sequential or cons? If not, go to (5).
|
// (2) Sequential or cons? If not, go to (5).
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
// Go to (5).
|
// Go to (5).
|
||||||
@ -1470,8 +1471,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ Branch(&runtime, ls, a3, Operand(a1));
|
__ Branch(&runtime, ls, a3, Operand(a1));
|
||||||
__ SmiUntag(a1);
|
__ SmiUntag(a1);
|
||||||
|
|
||||||
STATIC_ASSERT(kStringEncodingMask == 4);
|
STATIC_ASSERT(kStringEncodingMask == 8);
|
||||||
STATIC_ASSERT(kOneByteStringTag == 4);
|
STATIC_ASSERT(kOneByteStringTag == 8);
|
||||||
STATIC_ASSERT(kTwoByteStringTag == 0);
|
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||||
__ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for one_byte.
|
__ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for one_byte.
|
||||||
__ ld(t9, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset));
|
__ ld(t9, FieldMemOperand(regexp_data, JSRegExp::kDataOneByteCodeOffset));
|
||||||
@ -1721,12 +1722,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask));
|
__ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask));
|
||||||
__ Branch(&runtime, ne, at, Operand(zero_reg));
|
__ 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.
|
// Load offset into t0 and replace subject string with parent.
|
||||||
__ ld(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
__ ld(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
||||||
__ SmiUntag(t0);
|
__ SmiUntag(t0);
|
||||||
__ ld(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
__ ld(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
||||||
__ jmp(&check_underlying); // Go to (1).
|
__ 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
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,6 +612,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
Register index,
|
Register index,
|
||||||
Register result,
|
Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ ld(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
__ ld(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||||
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -622,18 +625,23 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
__ Branch(&check_sequential, eq, at, Operand(zero_reg));
|
__ Branch(&check_sequential, eq, at, Operand(zero_reg));
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ And(at, result, Operand(kSlicedNotConsMask));
|
__ And(at, result, Operand(kStringRepresentationMask));
|
||||||
__ Branch(&cons_string, eq, at, Operand(zero_reg));
|
__ Branch(&cons_string, eq, at, Operand(kConsStringTag));
|
||||||
|
__ Branch(&thin_string, eq, at, Operand(kThinStringTag));
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ ld(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
__ ld(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ ld(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
__ ld(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
||||||
__ dsra32(at, result, 0);
|
__ dsra32(at, result, 0);
|
||||||
__ Daddu(index, index, at);
|
__ Daddu(index, index, at);
|
||||||
__ jmp(&indirect_string_loaded);
|
__ jmp(&indirect_string_loaded);
|
||||||
|
|
||||||
|
// Handle thin strings.
|
||||||
|
__ bind(&thin_string);
|
||||||
|
__ ld(string, FieldMemOperand(string, ThinString::kActualOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
|
|
||||||
// Handle cons strings.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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
|
// 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));
|
__ Branch(call_runtime, ne, result, Operand(at));
|
||||||
// Get the first of the two strings and load its instance type.
|
// Get the first of the two strings and load its instance type.
|
||||||
__ ld(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
__ ld(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ ld(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -440,6 +440,8 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3) {
|
|||||||
return ReturnType();
|
return ReturnType();
|
||||||
case kConsStringTag:
|
case kConsStringTag:
|
||||||
return Op::template apply<ConsString::BodyDescriptor>(p1, p2, p3);
|
return Op::template apply<ConsString::BodyDescriptor>(p1, p2, p3);
|
||||||
|
case kThinStringTag:
|
||||||
|
return Op::template apply<ThinString::BodyDescriptor>(p1, p2, p3);
|
||||||
case kSlicedStringTag:
|
case kSlicedStringTag:
|
||||||
return Op::template apply<SlicedString::BodyDescriptor>(p1, p2, p3);
|
return Op::template apply<SlicedString::BodyDescriptor>(p1, p2, p3);
|
||||||
case kExternalStringTag:
|
case kExternalStringTag:
|
||||||
|
@ -555,6 +555,8 @@ void String::StringVerify() {
|
|||||||
ConsString::cast(this)->ConsStringVerify();
|
ConsString::cast(this)->ConsStringVerify();
|
||||||
} else if (IsSlicedString()) {
|
} else if (IsSlicedString()) {
|
||||||
SlicedString::cast(this)->SlicedStringVerify();
|
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() >= ConsString::kMinLength);
|
||||||
CHECK(this->length() == this->first()->length() + this->second()->length());
|
CHECK(this->length() == this->first()->length() + this->second()->length());
|
||||||
if (this->IsFlat()) {
|
if (this->IsFlat()) {
|
||||||
// A flat cons can only be created by String::SlowTryFlatten.
|
// A flat cons can only be created by String::SlowFlatten.
|
||||||
// Afterwards, the first part may be externalized.
|
// Afterwards, the first part may be externalized or internalized.
|
||||||
CHECK(this->first()->IsSeqString() || this->first()->IsExternalString());
|
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() {
|
void SlicedString::SlicedStringVerify() {
|
||||||
CHECK(!this->parent()->IsConsString());
|
CHECK(!this->parent()->IsConsString());
|
||||||
|
@ -275,6 +275,11 @@ bool HeapObject::IsConsString() const {
|
|||||||
return StringShape(String::cast(this)).IsCons();
|
return StringShape(String::cast(this)).IsCons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HeapObject::IsThinString() const {
|
||||||
|
if (!IsString()) return false;
|
||||||
|
return StringShape(String::cast(this)).IsThin();
|
||||||
|
}
|
||||||
|
|
||||||
bool HeapObject::IsSlicedString() const {
|
bool HeapObject::IsSlicedString() const {
|
||||||
if (!IsString()) return false;
|
if (!IsString()) return false;
|
||||||
return StringShape(String::cast(this)).IsSliced();
|
return StringShape(String::cast(this)).IsSliced();
|
||||||
@ -698,6 +703,7 @@ CAST_ACCESSOR(StringTable)
|
|||||||
CAST_ACCESSOR(Struct)
|
CAST_ACCESSOR(Struct)
|
||||||
CAST_ACCESSOR(Symbol)
|
CAST_ACCESSOR(Symbol)
|
||||||
CAST_ACCESSOR(TemplateInfo)
|
CAST_ACCESSOR(TemplateInfo)
|
||||||
|
CAST_ACCESSOR(ThinString)
|
||||||
CAST_ACCESSOR(Uint16x8)
|
CAST_ACCESSOR(Uint16x8)
|
||||||
CAST_ACCESSOR(Uint32x4)
|
CAST_ACCESSOR(Uint32x4)
|
||||||
CAST_ACCESSOR(Uint8x16)
|
CAST_ACCESSOR(Uint8x16)
|
||||||
@ -844,6 +850,10 @@ bool StringShape::IsCons() {
|
|||||||
return (type_ & kStringRepresentationMask) == kConsStringTag;
|
return (type_ & kStringRepresentationMask) == kConsStringTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StringShape::IsThin() {
|
||||||
|
return (type_ & kStringRepresentationMask) == kThinStringTag;
|
||||||
|
}
|
||||||
|
|
||||||
bool StringShape::IsSliced() {
|
bool StringShape::IsSliced() {
|
||||||
return (type_ & kStringRepresentationMask) == kSlicedStringTag;
|
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) {
|
Handle<String> String::Flatten(Handle<String> string, PretenureFlag pretenure) {
|
||||||
if (!string->IsConsString()) return string;
|
if (string->IsConsString()) {
|
||||||
Handle<ConsString> cons = Handle<ConsString>::cast(string);
|
Handle<ConsString> cons = Handle<ConsString>::cast(string);
|
||||||
if (cons->IsFlat()) return handle(cons->first());
|
if (cons->IsFlat()) {
|
||||||
return SlowFlatten(cons, pretenure);
|
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 | kOneByteStringTag:
|
||||||
case kSlicedStringTag | kTwoByteStringTag:
|
case kSlicedStringTag | kTwoByteStringTag:
|
||||||
return SlicedString::cast(this)->SlicedStringGet(index);
|
return SlicedString::cast(this)->SlicedStringGet(index);
|
||||||
|
case kThinStringTag | kOneByteStringTag:
|
||||||
|
case kThinStringTag | kTwoByteStringTag:
|
||||||
|
return ThinString::cast(this)->ThinStringGet(index);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3755,6 +3777,7 @@ String* String::GetUnderlying() {
|
|||||||
DCHECK(this->IsFlat());
|
DCHECK(this->IsFlat());
|
||||||
DCHECK(StringShape(this).IsIndirect());
|
DCHECK(StringShape(this).IsIndirect());
|
||||||
STATIC_ASSERT(ConsString::kFirstOffset == SlicedString::kParentOffset);
|
STATIC_ASSERT(ConsString::kFirstOffset == SlicedString::kParentOffset);
|
||||||
|
STATIC_ASSERT(ConsString::kFirstOffset == ThinString::kActualOffset);
|
||||||
const int kUnderlyingOffset = SlicedString::kParentOffset;
|
const int kUnderlyingOffset = SlicedString::kParentOffset;
|
||||||
return String::cast(READ_FIELD(this, kUnderlyingOffset));
|
return String::cast(READ_FIELD(this, kUnderlyingOffset));
|
||||||
}
|
}
|
||||||
@ -3806,6 +3829,11 @@ ConsString* String::VisitFlat(Visitor* visitor,
|
|||||||
case kConsStringTag | kTwoByteStringTag:
|
case kConsStringTag | kTwoByteStringTag:
|
||||||
return ConsString::cast(string);
|
return ConsString::cast(string);
|
||||||
|
|
||||||
|
case kThinStringTag | kOneByteStringTag:
|
||||||
|
case kThinStringTag | kTwoByteStringTag:
|
||||||
|
string = ThinString::cast(string)->actual();
|
||||||
|
continue;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -3937,6 +3965,7 @@ void ConsString::set_second(String* value, WriteBarrierMode mode) {
|
|||||||
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kSecondOffset, value, mode);
|
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kSecondOffset, value, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ACCESSORS(ThinString, actual, String, kActualOffset);
|
||||||
|
|
||||||
bool ExternalString::is_short() {
|
bool ExternalString::is_short() {
|
||||||
InstanceType type = map()->instance_type();
|
InstanceType type = map()->instance_type();
|
||||||
|
@ -858,6 +858,8 @@ void String::StringPrint(std::ostream& os) { // NOLINT
|
|||||||
os << "#";
|
os << "#";
|
||||||
} else if (StringShape(this).IsCons()) {
|
} else if (StringShape(this).IsCons()) {
|
||||||
os << "c\"";
|
os << "c\"";
|
||||||
|
} else if (StringShape(this).IsThin()) {
|
||||||
|
os << ">\"";
|
||||||
} else {
|
} else {
|
||||||
os << "\"";
|
os << "\"";
|
||||||
}
|
}
|
||||||
|
124
src/objects.cc
124
src/objects.cc
@ -2513,7 +2513,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
|
|||||||
Heap* heap = GetHeap();
|
Heap* heap = GetHeap();
|
||||||
bool is_one_byte = this->IsOneByteRepresentation();
|
bool is_one_byte = this->IsOneByteRepresentation();
|
||||||
bool is_internalized = this->IsInternalizedString();
|
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
|
// Morph the string to an external string by replacing the map and
|
||||||
// reinitializing the fields. This won't work if the space the existing
|
// 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;
|
if (size < ExternalString::kShortSize) return false;
|
||||||
Heap* heap = GetHeap();
|
Heap* heap = GetHeap();
|
||||||
bool is_internalized = this->IsInternalizedString();
|
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
|
// Morph the string to an external string by replacing the map and
|
||||||
// reinitializing the fields. This won't work if the space the existing
|
// 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);
|
return isolate->factory()->NewSubString(string, left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool String::LooksValid() {
|
bool String::LooksValid() { return GetIsolate()->heap()->Contains(this); }
|
||||||
if (!GetIsolate()->heap()->Contains(this)) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) {
|
MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) {
|
||||||
@ -10448,8 +10444,7 @@ String::FlatContent String::GetFlatContent() {
|
|||||||
}
|
}
|
||||||
string = cons->first();
|
string = cons->first();
|
||||||
shape = StringShape(string);
|
shape = StringShape(string);
|
||||||
}
|
} else if (shape.representation_tag() == kSlicedStringTag) {
|
||||||
if (shape.representation_tag() == kSlicedStringTag) {
|
|
||||||
SlicedString* slice = SlicedString::cast(string);
|
SlicedString* slice = SlicedString::cast(string);
|
||||||
offset = slice->offset();
|
offset = slice->offset();
|
||||||
string = slice->parent();
|
string = slice->parent();
|
||||||
@ -10457,6 +10452,13 @@ String::FlatContent String::GetFlatContent() {
|
|||||||
DCHECK(shape.representation_tag() != kConsStringTag &&
|
DCHECK(shape.representation_tag() != kConsStringTag &&
|
||||||
shape.representation_tag() != kSlicedStringTag);
|
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) {
|
if (shape.encoding_tag() == kOneByteStringTag) {
|
||||||
const uint8_t* start;
|
const uint8_t* start;
|
||||||
if (shape.representation_tag() == kSeqStringTag) {
|
if (shape.representation_tag() == kSeqStringTag) {
|
||||||
@ -10542,6 +10544,7 @@ const uc16* String::GetTwoByteData(unsigned start) {
|
|||||||
return slice->parent()->GetTwoByteData(start + slice->offset());
|
return slice->parent()->GetTwoByteData(start + slice->offset());
|
||||||
}
|
}
|
||||||
case kConsStringTag:
|
case kConsStringTag:
|
||||||
|
case kThinStringTag:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -10808,6 +10811,7 @@ uint16_t ConsString::ConsStringGet(int index) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t ThinString::ThinStringGet(int index) { return actual()->Get(index); }
|
||||||
|
|
||||||
uint16_t SlicedString::SlicedStringGet(int index) {
|
uint16_t SlicedString::SlicedStringGet(int index) {
|
||||||
return parent()->Get(offset() + index);
|
return parent()->Get(offset() + index);
|
||||||
@ -10902,6 +10906,10 @@ void String::WriteToFlat(String* src,
|
|||||||
WriteToFlat(slice->parent(), sink, from + offset, to + offset);
|
WriteToFlat(slice->parent(), sink, from + offset, to + offset);
|
||||||
return;
|
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 != other->length()) return false;
|
||||||
if (len == 0) return true;
|
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
|
// Fast check: if hash code is computed for both strings
|
||||||
// a fast negative check can be performed.
|
// a fast negative check can be performed.
|
||||||
if (HasHashCode() && other->HasHashCode()) {
|
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 != two->length()) return false;
|
||||||
if (one_length == 0) return true;
|
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
|
// Fast check: if hash code is computed for both strings
|
||||||
// a fast negative check can be performed.
|
// a fast negative check can be performed.
|
||||||
if (one->HasHashCode() && two->HasHashCode()) {
|
if (one->HasHashCode() && two->HasHashCode()) {
|
||||||
@ -16219,6 +16246,14 @@ class InternalizedStringKey : public HashTableKey {
|
|||||||
DCHECK(string_->IsInternalizedString());
|
DCHECK(string_->IsInternalizedString());
|
||||||
return string_;
|
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.
|
// Otherwise allocate a new internalized string.
|
||||||
return isolate->factory()->NewInternalizedStringImpl(
|
return isolate->factory()->NewInternalizedStringImpl(
|
||||||
string_, string_->length(), string_->hash_field());
|
string_, string_->length(), string_->hash_field());
|
||||||
@ -16228,6 +16263,7 @@ class InternalizedStringKey : public HashTableKey {
|
|||||||
return String::cast(obj)->Hash();
|
return String::cast(obj)->Hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
Handle<String> string_;
|
Handle<String> string_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17162,6 +17198,9 @@ MaybeHandle<String> StringTable::InternalizeStringIfExists(
|
|||||||
if (string->IsInternalizedString()) {
|
if (string->IsInternalizedString()) {
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
if (string->IsThinString()) {
|
||||||
|
return handle(Handle<ThinString>::cast(string)->actual(), isolate);
|
||||||
|
}
|
||||||
return LookupStringIfExists(isolate, string);
|
return LookupStringIfExists(isolate, string);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17208,31 +17247,72 @@ void StringTable::EnsureCapacityForDeserialization(Isolate* isolate,
|
|||||||
isolate->heap()->SetRootStringTable(*table);
|
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> StringTable::LookupString(Isolate* isolate,
|
||||||
Handle<String> string) {
|
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()) {
|
if (string->IsConsString() && string->IsFlat()) {
|
||||||
string = String::Flatten(string);
|
string = handle(Handle<ConsString>::cast(string)->first(), isolate);
|
||||||
if (string->IsInternalizedString()) return string;
|
if (string->IsInternalizedString()) return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalizedStringKey key(string);
|
InternalizedStringKey key(string);
|
||||||
Handle<String> result = LookupKey(isolate, &key);
|
Handle<String> result = LookupKey(isolate, &key);
|
||||||
|
|
||||||
if (string->IsConsString()) {
|
if (string->IsExternalString()) {
|
||||||
Handle<ConsString> cons = Handle<ConsString>::cast(string);
|
if (result->IsExternalOneByteString()) {
|
||||||
cons->set_first(*result);
|
MigrateExternalStringResource<ExternalOneByteString>(isolate, string,
|
||||||
cons->set_second(isolate->heap()->empty_string());
|
result);
|
||||||
} else if (string->IsSlicedString()) {
|
} else if (result->IsExternalTwoByteString()) {
|
||||||
STATIC_ASSERT(ConsString::kSize == SlicedString::kSize);
|
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;
|
DisallowHeapAllocation no_gc;
|
||||||
bool one_byte = result->IsOneByteRepresentation();
|
bool one_byte = result->IsOneByteRepresentation();
|
||||||
Handle<Map> map = one_byte ? isolate->factory()->cons_one_byte_string_map()
|
Handle<Map> map = one_byte ? isolate->factory()->thin_one_byte_string_map()
|
||||||
: isolate->factory()->cons_string_map();
|
: isolate->factory()->thin_string_map();
|
||||||
string->set_map(*map);
|
int old_size = string->Size();
|
||||||
Handle<ConsString> cons = Handle<ConsString>::cast(string);
|
DCHECK(old_size >= ThinString::kSize);
|
||||||
cons->set_first(*result);
|
string->synchronized_set_map(*map);
|
||||||
cons->set_second(isolate->heap()->empty_string());
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@
|
|||||||
// - SeqTwoByteString
|
// - SeqTwoByteString
|
||||||
// - SlicedString
|
// - SlicedString
|
||||||
// - ConsString
|
// - ConsString
|
||||||
|
// - ThinString
|
||||||
// - ExternalString
|
// - ExternalString
|
||||||
// - ExternalOneByteString
|
// - ExternalOneByteString
|
||||||
// - ExternalTwoByteString
|
// - ExternalTwoByteString
|
||||||
@ -335,10 +336,12 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
|
|||||||
V(CONS_STRING_TYPE) \
|
V(CONS_STRING_TYPE) \
|
||||||
V(EXTERNAL_STRING_TYPE) \
|
V(EXTERNAL_STRING_TYPE) \
|
||||||
V(SLICED_STRING_TYPE) \
|
V(SLICED_STRING_TYPE) \
|
||||||
|
V(THIN_STRING_TYPE) \
|
||||||
V(ONE_BYTE_STRING_TYPE) \
|
V(ONE_BYTE_STRING_TYPE) \
|
||||||
V(CONS_ONE_BYTE_STRING_TYPE) \
|
V(CONS_ONE_BYTE_STRING_TYPE) \
|
||||||
V(EXTERNAL_ONE_BYTE_STRING_TYPE) \
|
V(EXTERNAL_ONE_BYTE_STRING_TYPE) \
|
||||||
V(SLICED_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(EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE) \
|
||||||
V(SHORT_EXTERNAL_STRING_TYPE) \
|
V(SHORT_EXTERNAL_STRING_TYPE) \
|
||||||
V(SHORT_EXTERNAL_ONE_BYTE_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, \
|
V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE, \
|
||||||
ExternalTwoByteString::kShortSize, \
|
ExternalTwoByteString::kShortSize, \
|
||||||
short_external_internalized_string_with_one_byte_data, \
|
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
|
// 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
|
// 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 kNotInternalizedTag = 0x40;
|
||||||
const uint32_t kInternalizedTag = 0x0;
|
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.
|
// 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 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.
|
// of the string.
|
||||||
const uint32_t kStringRepresentationMask = 0x03;
|
const uint32_t kStringRepresentationMask = 0x07;
|
||||||
enum StringRepresentationTag {
|
enum StringRepresentationTag {
|
||||||
kSeqStringTag = 0x0,
|
kSeqStringTag = 0x0,
|
||||||
kConsStringTag = 0x1,
|
kConsStringTag = 0x1,
|
||||||
kExternalStringTag = 0x2,
|
kExternalStringTag = 0x2,
|
||||||
kSlicedStringTag = 0x3
|
kSlicedStringTag = 0x3,
|
||||||
|
kThinStringTag = 0x5
|
||||||
};
|
};
|
||||||
const uint32_t kIsIndirectStringMask = 0x1;
|
const uint32_t kIsIndirectStringMask = 0x1;
|
||||||
const uint32_t kIsIndirectStringTag = 0x1;
|
const uint32_t kIsIndirectStringTag = 0x1;
|
||||||
@ -597,22 +604,17 @@ STATIC_ASSERT((kConsStringTag &
|
|||||||
kIsIndirectStringMask) == kIsIndirectStringTag); // NOLINT
|
kIsIndirectStringMask) == kIsIndirectStringTag); // NOLINT
|
||||||
STATIC_ASSERT((kSlicedStringTag &
|
STATIC_ASSERT((kSlicedStringTag &
|
||||||
kIsIndirectStringMask) == kIsIndirectStringTag); // NOLINT
|
kIsIndirectStringMask) == kIsIndirectStringTag); // NOLINT
|
||||||
|
STATIC_ASSERT((kThinStringTag & kIsIndirectStringMask) == kIsIndirectStringTag);
|
||||||
|
|
||||||
// Use this mask to distinguish between cons and slice only after making
|
// If bit 7 is clear, then bit 4 indicates whether this two-byte
|
||||||
// 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
|
|
||||||
// string actually contains one byte data.
|
// string actually contains one byte data.
|
||||||
const uint32_t kOneByteDataHintMask = 0x08;
|
const uint32_t kOneByteDataHintMask = 0x10;
|
||||||
const uint32_t kOneByteDataHintTag = 0x08;
|
const uint32_t kOneByteDataHintTag = 0x10;
|
||||||
|
|
||||||
// If bit 7 is clear and string representation indicates an external string,
|
// If bit 7 is clear and string representation indicates an external string,
|
||||||
// then bit 4 indicates whether the data pointer is cached.
|
// then bit 5 indicates whether the data pointer is cached.
|
||||||
const uint32_t kShortExternalStringMask = 0x10;
|
const uint32_t kShortExternalStringMask = 0x20;
|
||||||
const uint32_t kShortExternalStringTag = 0x10;
|
const uint32_t kShortExternalStringTag = 0x20;
|
||||||
|
|
||||||
|
|
||||||
// A ConsString with an empty string as the right side is a candidate
|
// 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
|
// 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_STRING_WITH_ONE_BYTE_DATA_TYPE =
|
||||||
SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE |
|
SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE |
|
||||||
kNotInternalizedTag,
|
kNotInternalizedTag,
|
||||||
|
THIN_STRING_TYPE = kTwoByteStringTag | kThinStringTag | kNotInternalizedTag,
|
||||||
|
THIN_ONE_BYTE_STRING_TYPE =
|
||||||
|
kOneByteStringTag | kThinStringTag | kNotInternalizedTag,
|
||||||
|
|
||||||
// Non-string names
|
// Non-string names
|
||||||
SYMBOL_TYPE = kNotStringTag, // FIRST_NONSTRING_TYPE, LAST_NAME_TYPE
|
SYMBOL_TYPE = kNotStringTag, // FIRST_NONSTRING_TYPE, LAST_NAME_TYPE
|
||||||
@ -1030,6 +1035,7 @@ template <class C> inline bool Is(Object* obj);
|
|||||||
V(SeqTwoByteString) \
|
V(SeqTwoByteString) \
|
||||||
V(SeqOneByteString) \
|
V(SeqOneByteString) \
|
||||||
V(InternalizedString) \
|
V(InternalizedString) \
|
||||||
|
V(ThinString) \
|
||||||
V(Symbol) \
|
V(Symbol) \
|
||||||
\
|
\
|
||||||
V(FixedTypedArrayBase) \
|
V(FixedTypedArrayBase) \
|
||||||
@ -9274,6 +9280,7 @@ class StringShape BASE_EMBEDDED {
|
|||||||
inline bool IsExternal();
|
inline bool IsExternal();
|
||||||
inline bool IsCons();
|
inline bool IsCons();
|
||||||
inline bool IsSliced();
|
inline bool IsSliced();
|
||||||
|
inline bool IsThin();
|
||||||
inline bool IsIndirect();
|
inline bool IsIndirect();
|
||||||
inline bool IsExternalOneByte();
|
inline bool IsExternalOneByte();
|
||||||
inline bool IsExternalTwoByte();
|
inline bool IsExternalTwoByte();
|
||||||
@ -9987,6 +9994,34 @@ class ConsString: public String {
|
|||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(ConsString);
|
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
|
// The Sliced String class describes strings that are substrings of another
|
||||||
// sequential string. The motivation is to save time and memory when creating
|
// sequential string. The motivation is to save time and memory when creating
|
||||||
|
@ -1374,7 +1374,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (6) External string. Make it, offset-wise, look like a sequential string.
|
// (6) External string. Make it, offset-wise, look like a sequential string.
|
||||||
// Go to (4).
|
// Go to (4).
|
||||||
// (7) Short external string or not a string? If yes, bail out to runtime.
|
// (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 */,
|
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
|
||||||
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
|
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).
|
// (2) Sequential or cons? If not, go to (5).
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kExternalStringTag < 0xffffu);
|
STATIC_ASSERT(kExternalStringTag < 0xffffu);
|
||||||
@ -1671,12 +1672,19 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ andi(r0, r4, Operand(kIsNotStringMask | kShortExternalStringMask));
|
__ andi(r0, r4, Operand(kIsNotStringMask | kShortExternalStringMask));
|
||||||
__ bne(&runtime, cr0);
|
__ 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.
|
// Load offset into r11 and replace subject string with parent.
|
||||||
__ LoadP(r11, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
__ LoadP(r11, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
||||||
__ SmiUntag(r11);
|
__ SmiUntag(r11);
|
||||||
__ LoadP(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
__ LoadP(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
||||||
__ b(&check_underlying); // Go to (4).
|
__ 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
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,9 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
|
|||||||
void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
|
void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
|
||||||
Register index, Register result,
|
Register index, Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||||
__ lbz(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
__ lbz(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -86,20 +89,26 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
|
|||||||
__ andi(r0, result, Operand(kIsIndirectStringMask));
|
__ andi(r0, result, Operand(kIsIndirectStringMask));
|
||||||
__ beq(&check_sequential, cr0);
|
__ beq(&check_sequential, cr0);
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons or thin.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ mov(ip, Operand(kSlicedNotConsMask));
|
__ andi(ip, result, Operand(kStringRepresentationMask));
|
||||||
__ and_(r0, result, ip, SetRC);
|
__ cmpi(ip, Operand(kConsStringTag));
|
||||||
__ beq(&cons_string, cr0);
|
__ beq(&cons_string);
|
||||||
|
__ cmpi(ip, Operand(kThinStringTag));
|
||||||
|
__ beq(&thin_string);
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ LoadP(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
__ LoadP(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ LoadP(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
__ LoadP(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
||||||
__ SmiUntag(ip, result);
|
__ SmiUntag(ip, result);
|
||||||
__ add(index, index, ip);
|
__ add(index, index, ip);
|
||||||
__ b(&indirect_string_loaded);
|
__ b(&indirect_string_loaded);
|
||||||
|
|
||||||
|
// Handle thin strings.
|
||||||
|
__ bind(&thin_string);
|
||||||
|
__ LoadP(string, FieldMemOperand(string, ThinString::kActualOffset));
|
||||||
|
__ b(&indirect_string_loaded);
|
||||||
|
|
||||||
// Handle cons strings.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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
|
// 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);
|
__ bne(call_runtime);
|
||||||
// Get the first of the two strings and load its instance type.
|
// Get the first of the two strings and load its instance type.
|
||||||
__ LoadP(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
__ LoadP(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ b(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ lbz(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -1165,6 +1165,10 @@ void V8HeapExplorer::ExtractStringReferences(int entry, String* string) {
|
|||||||
SlicedString* ss = SlicedString::cast(string);
|
SlicedString* ss = SlicedString::cast(string);
|
||||||
SetInternalReference(ss, entry, "parent", ss->parent(),
|
SetInternalReference(ss, entry, "parent", ss->parent(),
|
||||||
SlicedString::kParentOffset);
|
SlicedString::kParentOffset);
|
||||||
|
} else if (string->IsThinString()) {
|
||||||
|
ThinString* ts = ThinString::cast(string);
|
||||||
|
SetInternalReference(ts, entry, "actual", ts->actual(),
|
||||||
|
ThinString::kActualOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +133,8 @@ const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
|
|||||||
} else if (subject->IsSlicedString()) {
|
} else if (subject->IsSlicedString()) {
|
||||||
start_index += SlicedString::cast(subject)->offset();
|
start_index += SlicedString::cast(subject)->offset();
|
||||||
subject = SlicedString::cast(subject)->parent();
|
subject = SlicedString::cast(subject)->parent();
|
||||||
|
} else if (subject->IsThinString()) {
|
||||||
|
subject = ThinString::cast(subject)->actual();
|
||||||
}
|
}
|
||||||
DCHECK(start_index >= 0);
|
DCHECK(start_index >= 0);
|
||||||
DCHECK(start_index <= subject->length());
|
DCHECK(start_index <= subject->length());
|
||||||
@ -239,6 +241,9 @@ NativeRegExpMacroAssembler::Result NativeRegExpMacroAssembler::Match(
|
|||||||
subject_ptr = slice->parent();
|
subject_ptr = slice->parent();
|
||||||
slice_offset = slice->offset();
|
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.
|
// Ensure that an underlying string has the same representation.
|
||||||
bool is_one_byte = subject_ptr->IsOneByteRepresentation();
|
bool is_one_byte = subject_ptr->IsOneByteRepresentation();
|
||||||
DCHECK(subject_ptr->IsExternalString() || subject_ptr->IsSeqString());
|
DCHECK(subject_ptr->IsExternalString() || subject_ptr->IsSeqString());
|
||||||
|
@ -865,6 +865,8 @@ MUST_USE_RESULT Object* LocaleConvertCase(Handle<String> s, Isolate* isolate,
|
|||||||
Handle<SeqTwoByteString> result;
|
Handle<SeqTwoByteString> result;
|
||||||
std::unique_ptr<uc16[]> sap;
|
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
|
// This is not a real loop. It'll be executed only once (no overflow) or
|
||||||
// twice (overflow).
|
// twice (overflow).
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (int i = 0; i < 2; ++i) {
|
||||||
@ -1086,7 +1088,7 @@ RUNTIME_FUNCTION(Runtime_StringToUpperCaseI18N) {
|
|||||||
int32_t length = s->length();
|
int32_t length = s->length();
|
||||||
s = String::Flatten(s);
|
s = String::Flatten(s);
|
||||||
|
|
||||||
if (s->HasOnlyOneByteChars()) {
|
if (s->HasOnlyOneByteChars() && length > 0) {
|
||||||
Handle<SeqOneByteString> result =
|
Handle<SeqOneByteString> result =
|
||||||
isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
|
isolate->factory()->NewRawOneByteString(length).ToHandleChecked();
|
||||||
|
|
||||||
|
@ -298,6 +298,7 @@ RUNTIME_FUNCTION(Runtime_AllocateSeqOneByteString) {
|
|||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
DCHECK_EQ(1, args.length());
|
DCHECK_EQ(1, args.length());
|
||||||
CONVERT_SMI_ARG_CHECKED(length, 0);
|
CONVERT_SMI_ARG_CHECKED(length, 0);
|
||||||
|
if (length == 0) return isolate->heap()->empty_string();
|
||||||
Handle<SeqOneByteString> result;
|
Handle<SeqOneByteString> result;
|
||||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||||
isolate, result, isolate->factory()->NewRawOneByteString(length));
|
isolate, result, isolate->factory()->NewRawOneByteString(length));
|
||||||
@ -308,6 +309,7 @@ RUNTIME_FUNCTION(Runtime_AllocateSeqTwoByteString) {
|
|||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
DCHECK_EQ(1, args.length());
|
DCHECK_EQ(1, args.length());
|
||||||
CONVERT_SMI_ARG_CHECKED(length, 0);
|
CONVERT_SMI_ARG_CHECKED(length, 0);
|
||||||
|
if (length == 0) return isolate->heap()->empty_string();
|
||||||
Handle<SeqTwoByteString> result;
|
Handle<SeqTwoByteString> result;
|
||||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||||
isolate, result, isolate->factory()->NewRawTwoByteString(length));
|
isolate, result, isolate->factory()->NewRawTwoByteString(length));
|
||||||
|
@ -56,6 +56,14 @@ static MaybeHandle<Object> KeyedGetObjectProperty(Isolate* isolate,
|
|||||||
DisallowHeapAllocation no_allocation;
|
DisallowHeapAllocation no_allocation;
|
||||||
Handle<JSObject> receiver = Handle<JSObject>::cast(receiver_obj);
|
Handle<JSObject> receiver = Handle<JSObject>::cast(receiver_obj);
|
||||||
Handle<Name> key = Handle<Name>::cast(key_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()) {
|
if (receiver->IsJSGlobalObject()) {
|
||||||
// Attempt dictionary lookup.
|
// Attempt dictionary lookup.
|
||||||
GlobalDictionary* dictionary = receiver->global_dictionary();
|
GlobalDictionary* dictionary = receiver->global_dictionary();
|
||||||
|
@ -431,6 +431,9 @@ MUST_USE_RESULT static Object* StringReplaceGlobalAtomRegExpWithString(
|
|||||||
} else {
|
} else {
|
||||||
result_len = static_cast<int>(result_len_64);
|
result_len = static_cast<int>(result_len_64);
|
||||||
}
|
}
|
||||||
|
if (result_len == 0) {
|
||||||
|
return isolate->heap()->empty_string();
|
||||||
|
}
|
||||||
|
|
||||||
int subject_pos = 0;
|
int subject_pos = 0;
|
||||||
int result_pos = 0;
|
int result_pos = 0;
|
||||||
|
@ -263,6 +263,9 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
|
|||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
return isolate->Throw(isolate->heap()->illegal_argument_string());
|
return isolate->Throw(isolate->heap()->illegal_argument_string());
|
||||||
}
|
}
|
||||||
|
if (length == 0) {
|
||||||
|
return isolate->heap()->empty_string();
|
||||||
|
}
|
||||||
|
|
||||||
if (one_byte) {
|
if (one_byte) {
|
||||||
Handle<SeqOneByteString> answer;
|
Handle<SeqOneByteString> answer;
|
||||||
|
@ -1371,7 +1371,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (6) External string. Make it, offset-wise, look like a sequential string.
|
// (6) External string. Make it, offset-wise, look like a sequential string.
|
||||||
// Go to (4).
|
// Go to (4).
|
||||||
// (7) Short external string or not a string? If yes, bail out to runtime.
|
// (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 */,
|
Label seq_string /* 4 */, external_string /* 6 */, check_underlying /* 1 */,
|
||||||
not_seq_nor_cons /* 5 */, not_long_external /* 7 */;
|
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).
|
// (2) Sequential or cons? If not, go to (5).
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kExternalStringTag < 0xffffu);
|
STATIC_ASSERT(kExternalStringTag < 0xffffu);
|
||||||
@ -1680,12 +1681,19 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ AndP(r0, r3);
|
__ AndP(r0, r3);
|
||||||
__ bne(&runtime);
|
__ 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.
|
// Load offset into ip and replace subject string with parent.
|
||||||
__ LoadP(ip, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
__ LoadP(ip, FieldMemOperand(subject, SlicedString::kOffsetOffset));
|
||||||
__ SmiUntag(ip);
|
__ SmiUntag(ip);
|
||||||
__ LoadP(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
__ LoadP(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
|
||||||
__ b(&check_underlying); // Go to (4).
|
__ 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
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,9 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
|
|||||||
void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
|
void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
|
||||||
Register index, Register result,
|
Register index, Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||||
__ LoadlB(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
__ LoadlB(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -81,19 +84,25 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string,
|
|||||||
__ beq(&check_sequential, Label::kNear /*, cr0*/);
|
__ beq(&check_sequential, Label::kNear /*, cr0*/);
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ mov(ip, Operand(kSlicedNotConsMask));
|
__ LoadRR(ip, result);
|
||||||
__ LoadRR(r0, result);
|
__ nilf(ip, Operand(kStringRepresentationMask));
|
||||||
__ AndP(r0, ip /*, SetRC*/); // Should be okay to remove RC
|
__ CmpP(ip, Operand(kConsStringTag));
|
||||||
__ beq(&cons_string, Label::kNear /*, cr0*/);
|
__ beq(&cons_string);
|
||||||
|
__ CmpP(ip, Operand(kThinStringTag));
|
||||||
|
__ beq(&thin_string);
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ LoadP(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
__ LoadP(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ LoadP(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
__ LoadP(string, FieldMemOperand(string, SlicedString::kParentOffset));
|
||||||
__ SmiUntag(ip, result);
|
__ SmiUntag(ip, result);
|
||||||
__ AddP(index, ip);
|
__ 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.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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);
|
__ bne(call_runtime);
|
||||||
// Get the first of the two strings and load its instance type.
|
// Get the first of the two strings and load its instance type.
|
||||||
__ LoadP(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
__ LoadP(string, FieldMemOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ b(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ LoadlB(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -1135,8 +1135,9 @@ MaybeHandle<String> ValueDeserializer::ReadUtf8String() {
|
|||||||
if (!ReadVarint<uint32_t>().To(&utf8_length) ||
|
if (!ReadVarint<uint32_t>().To(&utf8_length) ||
|
||||||
utf8_length >
|
utf8_length >
|
||||||
static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
|
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 MaybeHandle<String>();
|
||||||
|
}
|
||||||
return isolate_->factory()->NewStringFromUtf8(
|
return isolate_->factory()->NewStringFromUtf8(
|
||||||
Vector<const char>::cast(utf8_bytes), pretenure_);
|
Vector<const char>::cast(utf8_bytes), pretenure_);
|
||||||
}
|
}
|
||||||
@ -1147,16 +1148,20 @@ MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
|
|||||||
if (!ReadVarint<uint32_t>().To(&byte_length) ||
|
if (!ReadVarint<uint32_t>().To(&byte_length) ||
|
||||||
byte_length >
|
byte_length >
|
||||||
static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
|
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>();
|
return MaybeHandle<String>();
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate an uninitialized string so that we can do a raw memcpy into the
|
// Allocate an uninitialized string so that we can do a raw memcpy into the
|
||||||
// string on the heap (regardless of alignment).
|
// string on the heap (regardless of alignment).
|
||||||
|
if (byte_length == 0) return isolate_->factory()->empty_string();
|
||||||
Handle<SeqTwoByteString> string;
|
Handle<SeqTwoByteString> string;
|
||||||
if (!isolate_->factory()
|
if (!isolate_->factory()
|
||||||
->NewRawTwoByteString(byte_length / sizeof(uc16), pretenure_)
|
->NewRawTwoByteString(byte_length / sizeof(uc16), pretenure_)
|
||||||
.ToHandle(&string))
|
.ToHandle(&string)) {
|
||||||
return MaybeHandle<String>();
|
return MaybeHandle<String>();
|
||||||
|
}
|
||||||
|
|
||||||
// Copy the bytes directly into the new string.
|
// Copy the bytes directly into the new string.
|
||||||
// Warning: this uses host endianness.
|
// Warning: this uses host endianness.
|
||||||
|
@ -484,7 +484,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (8) Is the external string one byte? If yes, go to (5).
|
// (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).
|
// (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.
|
// (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 */,
|
Label seq_one_byte_string /* 5 */, seq_two_byte_string /* 9 */,
|
||||||
external_string /* 7 */, check_underlying /* 1 */,
|
external_string /* 7 */, check_underlying /* 1 */,
|
||||||
@ -514,6 +514,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// have already been covered.
|
// have already been covered.
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
__ cmpp(rbx, Immediate(kExternalStringTag));
|
__ cmpp(rbx, Immediate(kExternalStringTag));
|
||||||
@ -802,11 +803,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ testb(rbx, Immediate(kIsNotStringMask | kShortExternalStringMask));
|
__ testb(rbx, Immediate(kIsNotStringMask | kShortExternalStringMask));
|
||||||
__ j(not_zero, &runtime);
|
__ 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.
|
// Load offset into r14 and replace subject string with parent.
|
||||||
__ SmiToInteger32(r14, FieldOperand(rdi, SlicedString::kOffsetOffset));
|
__ SmiToInteger32(r14, FieldOperand(rdi, SlicedString::kOffsetOffset));
|
||||||
__ movp(rdi, FieldOperand(rdi, SlicedString::kParentOffset));
|
__ movp(rdi, FieldOperand(rdi, SlicedString::kParentOffset));
|
||||||
__ jmp(&check_underlying);
|
__ jmp(&check_underlying);
|
||||||
|
|
||||||
|
__ bind(&thin_string);
|
||||||
|
__ movp(rdi, FieldOperand(rdi, ThinString::kActualOffset));
|
||||||
|
__ jmp(&check_underlying);
|
||||||
#endif // V8_INTERPRETED_REGEXP
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
Register index,
|
Register index,
|
||||||
Register result,
|
Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ movp(result, FieldOperand(string, HeapObject::kMapOffset));
|
__ movp(result, FieldOperand(string, HeapObject::kMapOffset));
|
||||||
__ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
__ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -77,16 +80,23 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
__ j(zero, &check_sequential, Label::kNear);
|
__ j(zero, &check_sequential, Label::kNear);
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ testb(result, Immediate(kSlicedNotConsMask));
|
__ andl(result, Immediate(kStringRepresentationMask));
|
||||||
__ j(zero, &cons_string, Label::kNear);
|
__ cmpl(result, Immediate(kConsStringTag));
|
||||||
|
__ j(equal, &cons_string, Label::kNear);
|
||||||
|
__ cmpl(result, Immediate(kThinStringTag));
|
||||||
|
__ j(equal, &thin_string, Label::kNear);
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ SmiToInteger32(result, FieldOperand(string, SlicedString::kOffsetOffset));
|
__ SmiToInteger32(result, FieldOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ addp(index, result);
|
__ addp(index, result);
|
||||||
__ movp(string, FieldOperand(string, SlicedString::kParentOffset));
|
__ 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.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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);
|
Heap::kempty_stringRootIndex);
|
||||||
__ j(not_equal, call_runtime);
|
__ j(not_equal, call_runtime);
|
||||||
__ movp(string, FieldOperand(string, ConsString::kFirstOffset));
|
__ movp(string, FieldOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ movp(result, FieldOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -2540,10 +2540,12 @@ void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(
|
|||||||
andl(scratch1, Immediate(kFlatOneByteStringMask));
|
andl(scratch1, Immediate(kFlatOneByteStringMask));
|
||||||
andl(scratch2, Immediate(kFlatOneByteStringMask));
|
andl(scratch2, Immediate(kFlatOneByteStringMask));
|
||||||
// Interleave the bits to check both scratch1 and scratch2 in one test.
|
// Interleave the bits to check both scratch1 and scratch2 in one test.
|
||||||
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << 3));
|
const int kShift = 8;
|
||||||
leap(scratch1, Operand(scratch1, scratch2, times_8, 0));
|
DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << kShift));
|
||||||
|
shlp(scratch2, Immediate(kShift));
|
||||||
|
orp(scratch1, scratch2);
|
||||||
cmpl(scratch1,
|
cmpl(scratch1,
|
||||||
Immediate(kFlatOneByteStringTag + (kFlatOneByteStringTag << 3)));
|
Immediate(kFlatOneByteStringTag + (kFlatOneByteStringTag << kShift)));
|
||||||
j(not_equal, on_fail, near_jump);
|
j(not_equal, on_fail, near_jump);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +425,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// (8) Is the external string one byte? If yes, go to (5).
|
// (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).
|
// (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.
|
// (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 */,
|
Label seq_one_byte_string /* 5 */, seq_two_byte_string /* 9 */,
|
||||||
external_string /* 7 */, check_underlying /* 1 */,
|
external_string /* 7 */, check_underlying /* 1 */,
|
||||||
@ -455,6 +455,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
// have already been covered.
|
// have already been covered.
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
||||||
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
|
||||||
|
STATIC_ASSERT(kThinStringTag > kExternalStringTag);
|
||||||
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
|
||||||
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
|
||||||
__ cmp(ebx, Immediate(kExternalStringTag));
|
__ cmp(ebx, Immediate(kExternalStringTag));
|
||||||
@ -733,11 +734,18 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
|||||||
__ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
|
__ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
|
||||||
__ j(not_zero, &runtime);
|
__ 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.
|
// Load offset into edi and replace subject string with parent.
|
||||||
__ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
|
__ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
|
||||||
__ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
|
__ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
|
||||||
__ jmp(&check_underlying); // Go to (1).
|
__ 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
|
#endif // V8_INTERPRETED_REGEXP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,6 +218,9 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
Register index,
|
Register index,
|
||||||
Register result,
|
Register result,
|
||||||
Label* call_runtime) {
|
Label* call_runtime) {
|
||||||
|
Label indirect_string_loaded;
|
||||||
|
__ bind(&indirect_string_loaded);
|
||||||
|
|
||||||
// Fetch the instance type of the receiver into result register.
|
// Fetch the instance type of the receiver into result register.
|
||||||
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
|
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
|
||||||
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
||||||
@ -228,17 +231,24 @@ void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
|||||||
__ j(zero, &check_sequential, Label::kNear);
|
__ j(zero, &check_sequential, Label::kNear);
|
||||||
|
|
||||||
// Dispatch on the indirect string shape: slice or cons.
|
// Dispatch on the indirect string shape: slice or cons.
|
||||||
Label cons_string;
|
Label cons_string, thin_string;
|
||||||
__ test(result, Immediate(kSlicedNotConsMask));
|
__ and_(result, Immediate(kStringRepresentationMask));
|
||||||
__ j(zero, &cons_string, Label::kNear);
|
__ cmp(result, Immediate(kConsStringTag));
|
||||||
|
__ j(equal, &cons_string, Label::kNear);
|
||||||
|
__ cmp(result, Immediate(kThinStringTag));
|
||||||
|
__ j(equal, &thin_string, Label::kNear);
|
||||||
|
|
||||||
// Handle slices.
|
// Handle slices.
|
||||||
Label indirect_string_loaded;
|
|
||||||
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
|
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
|
||||||
__ SmiUntag(result);
|
__ SmiUntag(result);
|
||||||
__ add(index, result);
|
__ add(index, result);
|
||||||
__ mov(string, FieldOperand(string, SlicedString::kParentOffset));
|
__ 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.
|
// Handle cons strings.
|
||||||
// Check whether the right hand side is the empty string (i.e. if
|
// 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()));
|
Immediate(factory->empty_string()));
|
||||||
__ j(not_equal, call_runtime);
|
__ j(not_equal, call_runtime);
|
||||||
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
|
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
|
||||||
|
__ jmp(&indirect_string_loaded);
|
||||||
__ bind(&indirect_string_loaded);
|
|
||||||
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
|
|
||||||
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// Distinguish sequential and external strings. Only these two string
|
// Distinguish sequential and external strings. Only these two string
|
||||||
// representations can reach here (slices and flat cons strings have been
|
// representations can reach here (slices and flat cons strings have been
|
||||||
|
@ -2415,11 +2415,13 @@ void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(Register object1,
|
|||||||
const int kFlatOneByteStringTag =
|
const int kFlatOneByteStringTag =
|
||||||
kStringTag | kOneByteStringTag | kSeqStringTag;
|
kStringTag | kOneByteStringTag | kSeqStringTag;
|
||||||
// Interleave bits from both instance types and compare them in one check.
|
// 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_(scratch1, kFlatOneByteStringMask);
|
||||||
and_(scratch2, kFlatOneByteStringMask);
|
and_(scratch2, kFlatOneByteStringMask);
|
||||||
lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
|
shl(scratch2, kShift);
|
||||||
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << 3));
|
or_(scratch1, scratch2);
|
||||||
|
cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << kShift));
|
||||||
j(not_equal, failure);
|
j(not_equal, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
TEST(TryToName) {
|
||||||
typedef CodeAssemblerLabel Label;
|
typedef CodeAssemblerLabel Label;
|
||||||
typedef CodeAssemblerVariable Variable;
|
typedef CodeAssemblerVariable Variable;
|
||||||
@ -239,22 +198,26 @@ TEST(TryToName) {
|
|||||||
|
|
||||||
Label passed(&m), failed(&m);
|
Label passed(&m), failed(&m);
|
||||||
Label if_keyisindex(&m), if_keyisunique(&m), if_bailout(&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.Bind(&if_keyisindex);
|
||||||
m.GotoUnless(
|
m.GotoUnless(m.WordEqual(expected_result,
|
||||||
m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kKeyIsIndex))),
|
m.SmiConstant(Smi::FromInt(kKeyIsIndex))),
|
||||||
&failed);
|
&failed);
|
||||||
m.Branch(m.WordEqual(m.SmiUntag(expected_arg), var_index.value()), &passed,
|
m.Branch(m.WordEqual(m.SmiUntag(expected_arg), var_index.value()),
|
||||||
&failed);
|
&passed, &failed);
|
||||||
|
|
||||||
m.Bind(&if_keyisunique);
|
m.Bind(&if_keyisunique);
|
||||||
m.GotoUnless(
|
m.GotoUnless(m.WordEqual(expected_result,
|
||||||
m.WordEqual(expected_result, m.SmiConstant(Smi::FromInt(kKeyIsUnique))),
|
m.SmiConstant(Smi::FromInt(kKeyIsUnique))),
|
||||||
&failed);
|
&failed);
|
||||||
m.Branch(m.WordEqual(expected_arg, key), &passed, &failed);
|
m.Branch(m.WordEqual(expected_arg, var_unique.value()), &passed, &failed);
|
||||||
|
}
|
||||||
|
|
||||||
m.Bind(&if_bailout);
|
m.Bind(&if_bailout);
|
||||||
m.Branch(
|
m.Branch(
|
||||||
@ -350,6 +313,23 @@ TEST(TryToName) {
|
|||||||
Handle<Object> key = isolate->factory()->NewStringFromAsciiChecked("test");
|
Handle<Object> key = isolate->factory()->NewStringFromAsciiChecked("test");
|
||||||
ft.CheckTrue(key, expect_bailout);
|
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 {
|
namespace {
|
||||||
|
@ -1195,6 +1195,31 @@ class OneByteVectorResource : public v8::String::ExternalOneByteStringResource {
|
|||||||
i::Vector<const char> data_;
|
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) {
|
TEST(SliceFromExternal) {
|
||||||
FLAG_string_slices = true;
|
FLAG_string_slices = true;
|
||||||
|
90
test/mjsunit/thin-strings.js
Normal file
90
test/mjsunit/thin-strings.js
Normal 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);
|
Loading…
Reference in New Issue
Block a user