Handle external strings in the code serializer.

R=mvstanton@chromium.org

Review URL: https://codereview.chromium.org/623453003

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24378 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2014-10-02 07:12:46 +00:00
parent 2134c5c0ba
commit 43ddad10b3
3 changed files with 242 additions and 11 deletions

View File

@ -834,6 +834,7 @@ Address Deserializer::Allocate(int space_index, int size) {
}
}
void Deserializer::ReadChunk(Object** current,
Object** limit,
int source_space,
@ -1514,10 +1515,8 @@ void PartialSerializer::SerializeObject(
}
void Serializer::ObjectSerializer::Serialize() {
int space = Serializer::SpaceOfObject(object_);
int size = object_->Size();
void Serializer::ObjectSerializer::SerializePrologue(int space, int size,
Map* map) {
sink_->Put(kNewObject + reference_representation_ + space,
"ObjectSerialization");
sink_->PutInt(size >> kObjectAlignmentBits, "Size in words");
@ -1546,14 +1545,80 @@ void Serializer::ObjectSerializer::Serialize() {
}
// Serialize the map (first word of the object).
serializer_->SerializeObject(object_->map(), kPlain, kStartOfObject, 0);
serializer_->SerializeObject(map, kPlain, kStartOfObject, 0);
}
void Serializer::ObjectSerializer::SerializeExternalString() {
// Instead of serializing this as an external string, we serialize
// an imaginary sequential string with the same content.
DCHECK(object_->IsExternalString() && object_->IsInternalizedString());
Isolate* isolate = serializer_->isolate();
ExternalString* string = ExternalString::cast(object_);
int length = string->length();
Map* map;
int size;
const char* resource;
// Find the map and size for the imaginary sequential string.
if (object_->IsExternalOneByteString()) {
map = isolate->heap()->one_byte_internalized_string_map();
size = SeqOneByteString::SizeFor(length);
resource = ExternalOneByteString::cast(string)->resource()->data();
} else {
map = isolate->heap()->internalized_string_map();
size = SeqTwoByteString::SizeFor(length);
resource = reinterpret_cast<const char*>(
ExternalTwoByteString::cast(string)->resource()->data());
}
int space =
(size > Page::kMaxRegularHeapObjectSize) ? LO_SPACE : OLD_DATA_SPACE;
SerializePrologue(space, size, map);
// Output the rest of the imaginary string.
int bytes_to_output = size - HeapObject::kHeaderSize;
// Output raw data header. Do not bother with common raw length cases here.
sink_->Put(kRawData, "RawDataForString");
sink_->PutInt(bytes_to_output, "length");
// Serialize string header (except for map).
Address string_start = string->address();
for (int i = HeapObject::kHeaderSize; i < SeqString::kHeaderSize; i++) {
sink_->PutSection(string_start[i], "StringHeader");
}
// Serialize string content.
int content_length = size - SeqString::kHeaderSize;
for (int i = 0; i < content_length; i++) {
sink_->PutSection(resource[i], "StringContent");
}
sink_->Put(kSkip, "SkipAfterString");
sink_->PutInt(bytes_to_output, "SkipDistance");
}
void Serializer::ObjectSerializer::Serialize() {
if (object_->IsExternalString() && object_->IsInternalizedString()) {
// Native source code strings are not internalized and are handled in
// VisitExternalOneByteString. We deal with embedded external strings
// by serializing them as sequential strings on the heap.
// This can only happen with CodeSerializer.
SerializeExternalString();
} else {
int size = object_->Size();
Map* map = object_->map();
SerializePrologue(Serializer::SpaceOfObject(object_), size, map);
// Serialize the rest of the object.
CHECK_EQ(0, bytes_processed_so_far_);
bytes_processed_so_far_ = kPointerSize;
object_->IterateBody(object_->map()->instance_type(), size, this);
object_->IterateBody(map->instance_type(), size, this);
OutputRawData(object_->address() + size);
}
}
void Serializer::ObjectSerializer::VisitPointers(Object** start,
@ -1697,7 +1762,7 @@ void Serializer::ObjectSerializer::VisitExternalOneByteString(
}
}
// One of the strings in the natives cache should match the resource. We
// can't serialize any other kinds of external strings.
// don't expect any other kinds of external strings here.
UNREACHABLE();
}
@ -1876,6 +1941,11 @@ void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
}
if (address_mapper_.IsMapped(heap_object)) {
if (FLAG_trace_code_serializer) {
PrintF("Encoding back reference to: ");
heap_object->ShortPrint();
PrintF("\n");
}
SerializeReferenceToPreviousObject(heap_object, how_to_code, where_to_point,
skip);
return;

View File

@ -439,12 +439,16 @@ class Serializer : public SerializerDeserializer {
}
private:
void SerializePrologue(int space, int size, Map* map);
enum ReturnSkip { kCanReturnSkipInsteadOfSkipping, kIgnoringReturn };
// This function outputs or skips the raw data between the last pointer and
// up to the current position. It optionally can just return the number of
// bytes to skip instead of performing a skip instruction, in case the skip
// can be merged into the next instruction.
int OutputRawData(Address up_to, ReturnSkip return_skip = kIgnoringReturn);
// External strings are serialized in a way to resemble sequential strings.
void SerializeExternalString();
Serializer* serializer_;
HeapObject* object_;

View File

@ -901,6 +901,163 @@ TEST(SerializeToplevelLargeString) {
}
class OneByteResource : public v8::String::ExternalOneByteStringResource {
public:
OneByteResource(const char* data, size_t length)
: data_(data), length_(length) {}
virtual const char* data() const { return data_; }
virtual size_t length() const { return length_; }
private:
const char* data_;
size_t length_;
};
class TwoByteResource : public v8::String::ExternalStringResource {
public:
TwoByteResource(const char* data, size_t length)
: data_(AsciiToTwoByteString(data)), length_(length) {}
~TwoByteResource() { DeleteArray<const uint16_t>(data_); }
virtual const uint16_t* data() const { return data_; }
virtual size_t length() const { return length_; }
private:
const uint16_t* data_;
size_t length_;
};
TEST(SerializeToplevelExternalString) {
FLAG_serialize_toplevel = true;
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
v8::HandleScope scope(CcTest::isolate());
// Obtain external internalized one-byte string.
OneByteResource one_byte_resource("one_byte", 8);
Handle<String> one_byte_string =
isolate->factory()->NewStringFromAsciiChecked("one_byte");
one_byte_string = isolate->factory()->InternalizeString(one_byte_string);
one_byte_string->MakeExternal(&one_byte_resource);
CHECK(one_byte_string->IsExternalOneByteString());
CHECK(one_byte_string->IsInternalizedString());
// Obtain external internalized two-byte string.
TwoByteResource two_byte_resource("two_byte", 8);
Handle<String> two_byte_string =
isolate->factory()->NewStringFromAsciiChecked("two_byte");
two_byte_string = isolate->factory()->InternalizeString(two_byte_string);
two_byte_string->MakeExternal(&two_byte_resource);
CHECK(two_byte_string->IsExternalTwoByteString());
CHECK(two_byte_string->IsInternalizedString());
const char* source =
"var o = {} \n"
"o.one_byte = 7; \n"
"o.two_byte = 8; \n"
"o.one_byte + o.two_byte; \n";
Handle<String> source_string = isolate->factory()
->NewStringFromUtf8(CStrVector(source))
.ToHandleChecked();
Handle<JSObject> global(isolate->context()->global_object());
ScriptData* cache = NULL;
Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
source_string, Handle<String>(), 0, 0, false,
Handle<Context>(isolate->native_context()), NULL, &cache,
v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
Handle<SharedFunctionInfo> copy;
{
DisallowCompilation no_compile_expected(isolate);
copy = Compiler::CompileScript(
source_string, Handle<String>(), 0, 0, false,
Handle<Context>(isolate->native_context()), NULL, &cache,
v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
}
CHECK_NE(*orig, *copy);
Handle<JSFunction> copy_fun =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
copy, isolate->native_context());
Handle<Object> copy_result =
Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
CHECK_EQ(15.0f, copy_result->Number());
delete cache;
}
TEST(SerializeToplevelLargeExternalString) {
FLAG_serialize_toplevel = true;
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
Factory* f = isolate->factory();
v8::HandleScope scope(CcTest::isolate());
// Create a huge external internalized string to use as variable name.
Vector<const uint8_t> string =
ConstructSource(STATIC_CHAR_VECTOR(""), STATIC_CHAR_VECTOR("abcdef"),
STATIC_CHAR_VECTOR(""), 1000000);
Handle<String> name = f->NewStringFromOneByte(string).ToHandleChecked();
OneByteResource one_byte_resource(
reinterpret_cast<const char*>(string.start()), string.length());
name = f->InternalizeString(name);
name->MakeExternal(&one_byte_resource);
CHECK(name->IsExternalOneByteString());
CHECK(name->IsInternalizedString());
CHECK(isolate->heap()->InSpace(*name, LO_SPACE));
// Create the source, which is "var <literal> = 42; <literal>".
Handle<String> source_str =
f->NewConsString(
f->NewConsString(f->NewStringFromAsciiChecked("var "), name)
.ToHandleChecked(),
f->NewConsString(f->NewStringFromAsciiChecked(" = 42; "), name)
.ToHandleChecked()).ToHandleChecked();
Handle<JSObject> global(isolate->context()->global_object());
ScriptData* cache = NULL;
Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
source_str, Handle<String>(), 0, 0, false,
Handle<Context>(isolate->native_context()), NULL, &cache,
v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
Handle<SharedFunctionInfo> copy;
{
DisallowCompilation no_compile_expected(isolate);
copy = Compiler::CompileScript(
source_str, Handle<String>(), 0, 0, false,
Handle<Context>(isolate->native_context()), NULL, &cache,
v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
}
CHECK_NE(*orig, *copy);
Handle<JSFunction> copy_fun =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
copy, isolate->native_context());
Handle<Object> copy_result =
Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
CHECK_EQ(42.0f, copy_result->Number());
delete cache;
string.Dispose();
}
TEST(SerializeToplevelIsolates) {
FLAG_serialize_toplevel = true;