Add pointer cache field to external string for access in generated code.

TEST=test/mjsunit/string-externalize.js

Review URL: http://codereview.chromium.org/8513010

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10023 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2011-11-17 17:05:12 +00:00
parent be923eed32
commit 5a82d78948
13 changed files with 237 additions and 190 deletions

View File

@ -94,6 +94,23 @@ class ElementsTransitionGenerator : public AllStatic {
DISALLOW_COPY_AND_ASSIGN(ElementsTransitionGenerator);
};
class StringCharLoadGenerator : public AllStatic {
public:
// Generates the code for handling different string types and loading the
// indexed character into |result|. We expect |index| as untagged input and
// |result| as untagged output.
static void Generate(MacroAssembler* masm,
Factory* factory,
Register string,
Register index,
Register result,
Label* call_runtime);
private:
DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator);
};
} } // namespace v8::internal
#endif // V8_CODEGEN_H_

View File

@ -253,6 +253,9 @@ void Heap::FinalizeExternalString(String* string) {
ExternalString::kResourceOffset -
kHeapObjectTag);
// Clear pointer cache.
ExternalString::cast(string)->clear_data_cache();
// Dispose of the C++ object if it has not already been disposed.
if (*resource_addr != NULL) {
(*resource_addr)->Dispose();

View File

@ -2847,14 +2847,14 @@ MaybeObject* Heap::AllocateConsString(String* first, String* second) {
// Copy first part.
const char* src;
if (first->IsExternalString()) {
src = ExternalAsciiString::cast(first)->resource()->data();
src = ExternalAsciiString::cast(first)->GetChars();
} else {
src = SeqAsciiString::cast(first)->GetChars();
}
for (int i = 0; i < first_length; i++) *dest++ = src[i];
// Copy second part.
if (second->IsExternalString()) {
src = ExternalAsciiString::cast(second)->resource()->data();
src = ExternalAsciiString::cast(second)->GetChars();
} else {
src = SeqAsciiString::cast(second)->GetChars();
}

View File

@ -35,6 +35,7 @@
#include "jsregexp.h"
#include "regexp-macro-assembler.h"
#include "stub-cache.h"
#include "codegen.h"
namespace v8 {
namespace internal {
@ -5125,11 +5126,6 @@ void CompareStub::PrintName(StringStream* stream) {
// StringCharCodeAtGenerator
void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
Label flat_string;
Label ascii_string;
Label got_char_code;
Label sliced_string;
// If the receiver is a smi trigger the non-string case.
STATIC_ASSERT(kSmiTag == 0);
__ JumpIfSmi(object_, receiver_not_string_);
@ -5150,71 +5146,12 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
__ cmp(index_, FieldOperand(object_, String::kLengthOffset));
__ j(above_equal, index_out_of_range_);
// We need special handling for non-flat strings.
STATIC_ASSERT(kSeqStringTag == 0);
__ test(result_, Immediate(kStringRepresentationMask));
__ j(zero, &flat_string);
// Handle non-flat strings.
__ and_(result_, kStringRepresentationMask);
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
__ cmp(result_, kExternalStringTag);
__ j(greater, &sliced_string, Label::kNear);
__ j(equal, &call_runtime_);
// ConsString.
// 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
// the case we would rather go to the runtime system now to flatten
// the string.
Label assure_seq_string;
__ cmp(FieldOperand(object_, ConsString::kSecondOffset),
Immediate(masm->isolate()->factory()->empty_string()));
__ j(not_equal, &call_runtime_);
// Get the first of the two parts.
__ mov(object_, FieldOperand(object_, ConsString::kFirstOffset));
__ jmp(&assure_seq_string, Label::kNear);
// SlicedString, unpack and add offset.
__ bind(&sliced_string);
__ add(index_, FieldOperand(object_, SlicedString::kOffsetOffset));
__ mov(object_, FieldOperand(object_, SlicedString::kParentOffset));
// Assure that we are dealing with a sequential string. Go to runtime if not.
// Note that if the original string is a cons or slice with an external
// string as underlying string, we pass that unpacked underlying string with
// the adjusted index to the runtime function.
__ bind(&assure_seq_string);
__ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
__ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
STATIC_ASSERT(kSeqStringTag == 0);
__ test(result_, Immediate(kStringRepresentationMask));
__ j(not_zero, &call_runtime_);
// Check for 1-byte or 2-byte string.
__ bind(&flat_string);
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
__ test(result_, Immediate(kStringEncodingMask));
__ j(not_zero, &ascii_string, Label::kNear);
// 2-byte string.
// Load the 2-byte character code into the result register.
STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
__ movzx_w(result_, FieldOperand(object_,
index_, times_1, // Scratch is smi-tagged.
SeqTwoByteString::kHeaderSize));
__ jmp(&got_char_code, Label::kNear);
// ASCII string.
// Load the byte into the result register.
__ bind(&ascii_string);
__ SmiUntag(index_);
__ movzx_b(result_, FieldOperand(object_,
index_, times_1,
SeqAsciiString::kHeaderSize));
__ bind(&got_char_code);
Factory* factory = masm->isolate()->factory();
StringCharLoadGenerator::Generate(
masm, factory, object_, index_, result_, &call_runtime_);
__ SmiTag(result_);
__ bind(&exit_);
}
@ -5264,6 +5201,7 @@ void StringCharCodeAtGenerator::GenerateSlow(
__ bind(&call_runtime_);
call_helper.BeforeCall(masm);
__ push(object_);
__ SmiTag(index_);
__ push(index_);
__ CallRuntime(Runtime::kStringCharCodeAt, 2);
if (!result_.is(eax)) {

View File

@ -524,6 +524,113 @@ void ElementsTransitionGenerator::GenerateDoubleToObject(
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
}
void StringCharLoadGenerator::Generate(MacroAssembler* masm,
Factory* factory,
Register string,
Register index,
Register result,
Label* call_runtime) {
// Fetch the instance type of the receiver into result register.
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
// We need special handling for indirect strings.
Label check_sequential;
__ test(result, Immediate(kIsIndirectStringMask));
__ j(zero, &check_sequential);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ test(result, Immediate(kSlicedNotConsMask));
__ j(zero, &cons_string);
// Handle slices.
Label indirect_string_loaded;
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
__ SmiUntag(result);
__ add(index, result);
__ mov(string, FieldOperand(string, SlicedString::kParentOffset));
__ jmp(&indirect_string_loaded);
// Handle external strings.
Label external_string, ascii_external, done;
__ bind(&external_string);
if (FLAG_debug_code) {
// Assert that we do not have a cons or slice (indirect strings) here.
// Sequential strings have already been ruled out.
__ test(result, Immediate(kIsIndirectStringMask));
__ Assert(zero, "external string expected, but not found");
}
__ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset));
// Assert that the external string has not been finalized yet.
__ test(result, result);
__ j(zero, call_runtime);
Register scratch = string;
__ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ cmp(scratch, Immediate(factory->external_ascii_string_map()));
__ j(equal, &ascii_external, Label::kNear);
__ cmp(scratch, Immediate(factory->external_ascii_symbol_map()));
__ j(equal, &ascii_external, Label::kNear);
// Two-byte string.
__ movzx_w(result, Operand(result, index, times_2, 0));
__ jmp(&done);
__ bind(&ascii_external);
// Ascii string.
__ movzx_b(result, Operand(result, index, times_1, 0));
__ jmp(&done);
// Handle conses.
// 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
// the case we would rather go to the runtime system now to flatten
// the string.
__ bind(&cons_string);
__ cmp(FieldOperand(string, ConsString::kSecondOffset),
Immediate(factory->empty_string()));
__ j(not_equal, call_runtime);
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
// Check whether the string is sequential. The only non-sequential
// shapes we support have just been unwrapped above.
// Note that if the original string is a cons or slice with an external
// string as underlying string, we pass that unpacked underlying string with
// the adjusted index to the runtime function.
__ bind(&check_sequential);
STATIC_ASSERT(kSeqStringTag == 0);
__ test(result, Immediate(kStringRepresentationMask));
__ j(not_zero, &external_string);
// Dispatch on the encoding: ASCII or two-byte.
Label ascii_string;
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
__ test(result, Immediate(kStringEncodingMask));
__ j(not_zero, &ascii_string, Label::kNear);
// Two-byte string.
// Load the two-byte character code into the result register.
STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
__ movzx_w(result, FieldOperand(string,
index,
times_2,
SeqTwoByteString::kHeaderSize));
__ jmp(&done, Label::kNear);
// Ascii string.
// Load the byte into the result register.
__ bind(&ascii_string);
__ movzx_b(result, FieldOperand(string,
index,
times_1,
SeqAsciiString::kHeaderSize));
__ bind(&done);
}
#undef __
} } // namespace v8::internal

View File

@ -33,6 +33,7 @@
#include "code-stubs.h"
#include "deoptimizer.h"
#include "stub-cache.h"
#include "codegen.h"
namespace v8 {
namespace internal {
@ -3411,85 +3412,15 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
LStringCharCodeAt* instr_;
};
Register string = ToRegister(instr->string());
Register index = ToRegister(instr->index());
Register result = ToRegister(instr->result());
DeferredStringCharCodeAt* deferred =
new DeferredStringCharCodeAt(this, instr);
// Fetch the instance type of the receiver into result register.
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
// We need special handling for indirect strings.
Label check_sequential;
__ test(result, Immediate(kIsIndirectStringMask));
__ j(zero, &check_sequential, Label::kNear);
// Dispatch on the indirect string shape: slice or cons.
Label cons_string;
__ test(result, Immediate(kSlicedNotConsMask));
__ j(zero, &cons_string, Label::kNear);
// Handle slices.
Label indirect_string_loaded;
__ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
__ SmiUntag(result);
__ add(index, Operand(result));
__ mov(string, FieldOperand(string, SlicedString::kParentOffset));
__ jmp(&indirect_string_loaded, Label::kNear);
// Handle conses.
// 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
// the case we would rather go to the runtime system now to flatten
// the string.
__ bind(&cons_string);
__ cmp(FieldOperand(string, ConsString::kSecondOffset),
Immediate(factory()->empty_string()));
__ j(not_equal, deferred->entry());
__ mov(string, FieldOperand(string, ConsString::kFirstOffset));
__ bind(&indirect_string_loaded);
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
// Check whether the string is sequential. The only non-sequential
// shapes we support have just been unwrapped above.
// Note that if the original string is a cons or slice with an external
// string as underlying string, we pass that unpacked underlying string with
// the adjusted index to the runtime function.
__ bind(&check_sequential);
STATIC_ASSERT(kSeqStringTag == 0);
__ test(result, Immediate(kStringRepresentationMask));
__ j(not_zero, deferred->entry());
// Dispatch on the encoding: ASCII or two-byte.
Label ascii_string;
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
__ test(result, Immediate(kStringEncodingMask));
__ j(not_zero, &ascii_string, Label::kNear);
// Two-byte string.
// Load the two-byte character code into the result register.
Label done;
STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
__ movzx_w(result, FieldOperand(string,
index,
times_2,
SeqTwoByteString::kHeaderSize));
__ jmp(&done, Label::kNear);
// ASCII string.
// Load the byte into the result register.
__ bind(&ascii_string);
__ movzx_b(result, FieldOperand(string,
index,
times_1,
SeqAsciiString::kHeaderSize));
__ bind(&done);
StringCharLoadGenerator::Generate(masm(),
factory(),
ToRegister(instr->string()),
ToRegister(instr->index()),
ToRegister(instr->result()),
deferred->entry());
__ bind(deferred->exit());
}

View File

@ -2297,6 +2297,11 @@ void ConsString::set_second(String* value, WriteBarrierMode mode) {
}
void ExternalString::clear_data_cache() {
WRITE_INTPTR_FIELD(this, kResourceDataOffset, NULL);
}
const ExternalAsciiString::Resource* ExternalAsciiString::resource() {
return *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset));
}
@ -2306,6 +2311,21 @@ void ExternalAsciiString::set_resource(
const ExternalAsciiString::Resource* resource) {
*reinterpret_cast<const Resource**>(
FIELD_ADDR(this, kResourceOffset)) = resource;
clear_data_cache();
}
const char* ExternalAsciiString::GetChars() {
const char** data_field =
reinterpret_cast<const char**>(FIELD_ADDR(this, kResourceDataOffset));
if (*data_field == NULL) *data_field = resource()->data();
return *data_field;
}
uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
ASSERT(index >= 0 && index < length());
return GetChars()[index];
}
@ -2318,6 +2338,27 @@ void ExternalTwoByteString::set_resource(
const ExternalTwoByteString::Resource* resource) {
*reinterpret_cast<const Resource**>(
FIELD_ADDR(this, kResourceOffset)) = resource;
clear_data_cache();
}
const uint16_t* ExternalTwoByteString::GetChars() {
const uint16_t** data_field =
reinterpret_cast<const uint16_t**>(FIELD_ADDR(this, kResourceDataOffset));
if (*data_field == NULL) *data_field = resource()->data();
return *data_field;
}
uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
ASSERT(index >= 0 && index < length());
return GetChars()[index];
}
const uint16_t* ExternalTwoByteString::ExternalTwoByteStringGetData(
unsigned start) {
return GetChars() + start;
}

View File

@ -953,8 +953,6 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
Heap* heap = GetHeap();
int size = this->Size(); // Byte size of the original string.
if (size < ExternalString::kSize) {
// The string is too small to fit an external String in its place. This can
// only happen for zero length strings.
return false;
}
ASSERT(size >= ExternalString::kSize);
@ -1007,8 +1005,6 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
Heap* heap = GetHeap();
int size = this->Size(); // Byte size of the original string.
if (size < ExternalString::kSize) {
// The string is too small to fit an external String in its place. This can
// only happen for zero length strings.
return false;
}
ASSERT(size >= ExternalString::kSize);
@ -5790,7 +5786,7 @@ String::FlatContent String::GetFlatContent() {
if (shape.representation_tag() == kSeqStringTag) {
start = SeqAsciiString::cast(string)->GetChars();
} else {
start = ExternalAsciiString::cast(string)->resource()->data();
start = ExternalAsciiString::cast(string)->GetChars();
}
return FlatContent(Vector<const char>(start + offset, length));
} else {
@ -5799,7 +5795,7 @@ String::FlatContent String::GetFlatContent() {
if (shape.representation_tag() == kSeqStringTag) {
start = SeqTwoByteString::cast(string)->GetChars();
} else {
start = ExternalTwoByteString::cast(string)->resource()->data();
start = ExternalTwoByteString::cast(string)->GetChars();
}
return FlatContent(Vector<const uc16>(start + offset, length));
}
@ -6032,44 +6028,26 @@ const unibrow::byte* ConsString::ConsStringReadBlock(ReadBlockBuffer* rbb,
}
uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
ASSERT(index >= 0 && index < length());
return resource()->data()[index];
}
const unibrow::byte* ExternalAsciiString::ExternalAsciiStringReadBlock(
unsigned* remaining,
unsigned* offset_ptr,
unsigned max_chars) {
// Cast const char* to unibrow::byte* (signedness difference).
const unibrow::byte* b =
reinterpret_cast<const unibrow::byte*>(resource()->data()) + *offset_ptr;
reinterpret_cast<const unibrow::byte*>(GetChars()) + *offset_ptr;
*remaining = max_chars;
*offset_ptr += max_chars;
return b;
}
const uc16* ExternalTwoByteString::ExternalTwoByteStringGetData(
unsigned start) {
return resource()->data() + start;
}
uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(int index) {
ASSERT(index >= 0 && index < length());
return resource()->data()[index];
}
void ExternalTwoByteString::ExternalTwoByteStringReadBlockIntoBuffer(
ReadBlockBuffer* rbb,
unsigned* offset_ptr,
unsigned max_chars) {
unsigned chars_read = 0;
unsigned offset = *offset_ptr;
const uint16_t* data = resource()->data();
const uint16_t* data = GetChars();
while (chars_read < max_chars) {
uint16_t c = data[offset];
if (c <= kMaxAsciiCharCode) {
@ -6115,9 +6093,7 @@ void ExternalAsciiString::ExternalAsciiStringReadBlockIntoBuffer(
unsigned max_chars) {
unsigned capacity = rbb->capacity - rbb->cursor;
if (max_chars > capacity) max_chars = capacity;
memcpy(rbb->util_buffer + rbb->cursor,
resource()->data() + *offset_ptr,
max_chars);
memcpy(rbb->util_buffer + rbb->cursor, GetChars() + *offset_ptr, max_chars);
rbb->remaining += max_chars;
*offset_ptr += max_chars;
rbb->cursor += max_chars;
@ -6559,13 +6535,13 @@ void String::WriteToFlat(String* src,
switch (StringShape(source).full_representation_tag()) {
case kAsciiStringTag | kExternalStringTag: {
CopyChars(sink,
ExternalAsciiString::cast(source)->resource()->data() + from,
ExternalAsciiString::cast(source)->GetChars() + from,
to - from);
return;
}
case kTwoByteStringTag | kExternalStringTag: {
const uc16* data =
ExternalTwoByteString::cast(source)->resource()->data();
ExternalTwoByteString::cast(source)->GetChars();
CopyChars(sink,
data + from,
to - from);

View File

@ -6758,7 +6758,12 @@ class ExternalString: public String {
// Layout description.
static const int kResourceOffset = POINTER_SIZE_ALIGN(String::kSize);
static const int kSize = kResourceOffset + kPointerSize;
static const int kResourceDataOffset = kResourceOffset + kPointerSize;
static const int kSize = kResourceDataOffset + kPointerSize;
// Clear the cached pointer to the character array provided by the resource.
// This cache is updated the first time the character array is accessed.
inline void clear_data_cache();
STATIC_CHECK(kResourceOffset == Internals::kStringResourceOffset);
@ -6779,8 +6784,10 @@ class ExternalAsciiString: public ExternalString {
inline const Resource* resource();
inline void set_resource(const Resource* buffer);
inline const char* GetChars();
// Dispatched behavior.
uint16_t ExternalAsciiStringGet(int index);
inline uint16_t ExternalAsciiStringGet(int index);
// Casting.
static inline ExternalAsciiString* cast(Object* obj);
@ -6816,11 +6823,13 @@ class ExternalTwoByteString: public ExternalString {
inline const Resource* resource();
inline void set_resource(const Resource* buffer);
inline const uint16_t* GetChars();
// Dispatched behavior.
uint16_t ExternalTwoByteStringGet(int index);
inline uint16_t ExternalTwoByteStringGet(int index);
// For regexp code.
const uint16_t* ExternalTwoByteStringGetData(unsigned start);
inline const uint16_t* ExternalTwoByteStringGetData(unsigned start);
// Casting.
static inline ExternalTwoByteString* cast(Object* obj);

View File

@ -81,7 +81,7 @@ const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
if (subject->IsAsciiRepresentation()) {
const byte* address;
if (StringShape(subject).IsExternal()) {
const char* data = ExternalAsciiString::cast(subject)->resource()->data();
const char* data = ExternalAsciiString::cast(subject)->GetChars();
address = reinterpret_cast<const byte*>(data);
} else {
ASSERT(subject->IsSeqAsciiString());
@ -92,7 +92,7 @@ const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
}
const uc16* data;
if (StringShape(subject).IsExternal()) {
data = ExternalTwoByteString::cast(subject)->resource()->data();
data = ExternalTwoByteString::cast(subject)->GetChars();
} else {
ASSERT(subject->IsSeqTwoByteString());
data = SeqTwoByteString::cast(subject)->GetChars();

View File

@ -1564,6 +1564,7 @@ void Serializer::ObjectSerializer::VisitExternalAsciiString(
sink_->Put(kNativesStringResource, "NativesStringResource");
sink_->PutSection(i, "NativesStringResourceEnd");
bytes_processed_so_far_ += sizeof(resource);
string->clear_data_cache();
return;
}
}

View File

@ -491,7 +491,7 @@ TEST(MakingExternalStringConditions) {
HEAP->CollectGarbage(i::NEW_SPACE);
HEAP->CollectGarbage(i::NEW_SPACE);
uint16_t* two_byte_string = AsciiToTwoByteString("small");
uint16_t* two_byte_string = AsciiToTwoByteString("abcdefghi");
Local<String> small_string = String::New(two_byte_string);
i::DeleteArray(two_byte_string);
@ -503,7 +503,7 @@ TEST(MakingExternalStringConditions) {
// Old space strings should be accepted.
CHECK(small_string->CanMakeExternal());
two_byte_string = AsciiToTwoByteString("small 2");
two_byte_string = AsciiToTwoByteString("abcdefghi");
small_string = String::New(two_byte_string);
i::DeleteArray(two_byte_string);
@ -537,7 +537,7 @@ TEST(MakingExternalAsciiStringConditions) {
HEAP->CollectGarbage(i::NEW_SPACE);
HEAP->CollectGarbage(i::NEW_SPACE);
Local<String> small_string = String::New("small");
Local<String> small_string = String::New("abcdefghi");
// We should refuse to externalize newly created small string.
CHECK(!small_string->CanMakeExternal());
// Trigger GCs so that the newly allocated string moves to old gen.
@ -546,7 +546,7 @@ TEST(MakingExternalAsciiStringConditions) {
// Old space strings should be accepted.
CHECK(small_string->CanMakeExternal());
small_string = String::New("small 2");
small_string = String::New("abcdefghi");
// We should refuse externalizing newly created small string.
CHECK(!small_string->CanMakeExternal());
for (int i = 0; i < 100; i++) {

View File

@ -39,12 +39,12 @@ function test() {
assertTrue(isAsciiString(str));
var twoByteExternalWithAsciiData =
"AA" + (function() { return "A"; })();
"AAAAAAAA" + (function() { return "A"; })();
externalizeString(twoByteExternalWithAsciiData, true /* force two-byte */);
assertFalse(isAsciiString(twoByteExternalWithAsciiData));
var realTwoByteExternalString =
"\u1234\u1234" + (function() { return "\u1234"; })();
"\u1234\u1234\u1234\u1234" + (function() { return "\u1234"; })();
externalizeString(realTwoByteExternalString);
assertFalse(isAsciiString(realTwoByteExternalString));
@ -87,6 +87,30 @@ function test() {
// Flattened string should still be two-byte.
assertFalse(isAsciiString(str2));
// Test buffered external strings.
var charat_str = new Array(5);
charat_str[0] = "0123456789ABCDEF0123456789ABCDEF\
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
charat_str[1] = "0123456789ABCDEF";
for (var i = 0; i < 6; i++) charat_str[1] += charat_str[1];
try { // String can only be externalized once
externalizeString(charat_str[0], false);
externalizeString(charat_str[1], true);
} catch (ex) { }
charat_str[2] = charat_str[0].slice(0, -1);
charat_str[3] = charat_str[1].slice(0, -1);
charat_str[4] = charat_str[0] + charat_str[0];
for (var i = 0; i < 5; i++) {
assertEquals('B', charat_str[i].charAt(6*16 + 11));
assertEquals('C', charat_str[i].charAt(6*16 + 12));
assertEquals('A', charat_str[i].charAt(3*16 + 10));
assertEquals('B', charat_str[i].charAt(3*16 + 11));
}
}
// Run the test many times to ensure IC-s don't break things.