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:
parent
be923eed32
commit
5a82d78948
@ -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_
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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++) {
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user