Revision: 24378
Author:   [email protected]
Date:     Thu Oct  2 07:12:46 2014 UTC
Log:      Handle external strings in the code serializer.

[email protected]

Review URL: https://codereview.chromium.org/623453003
https://code.google.com/p/v8/source/detail?r=24378

Modified:
 /branches/bleeding_edge/src/serialize.cc
 /branches/bleeding_edge/src/serialize.h
 /branches/bleeding_edge/test/cctest/test-serialize.cc

=======================================
--- /branches/bleeding_edge/src/serialize.cc    Thu Oct  2 07:04:28 2014 UTC
+++ /branches/bleeding_edge/src/serialize.cc    Thu Oct  2 07:12:46 2014 UTC
@@ -833,6 +833,7 @@
     return address;
   }
 }
+

 void Deserializer::ReadChunk(Object** current,
                              Object** limit,
@@ -1514,10 +1515,8 @@
 }


-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,13 +1545,79 @@
   }

   // Serialize the map (first word of the object).
-  serializer_->SerializeObject(object_->map(), kPlain, kStartOfObject, 0);
+  serializer_->SerializeObject(map, kPlain, kStartOfObject, 0);
+}

-  // 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);
-  OutputRawData(object_->address() + size);
+
+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(map->instance_type(), size, this);
+    OutputRawData(object_->address() + size);
+  }
 }


@@ -1697,7 +1762,7 @@
     }
   }
   // 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 @@
   }

   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;
=======================================
--- /branches/bleeding_edge/src/serialize.h     Mon Sep 29 07:53:22 2014 UTC
+++ /branches/bleeding_edge/src/serialize.h     Thu Oct  2 07:12:46 2014 UTC
@@ -439,12 +439,16 @@
     }

    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_;
=======================================
--- /branches/bleeding_edge/test/cctest/test-serialize.cc Thu Sep 25 07:32:13 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-serialize.cc Thu Oct 2 07:12:46 2014 UTC
@@ -899,6 +899,163 @@
   delete cache;
   source.Dispose();
 }
+
+
+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) {

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to