| Index: test/cctest/test-api.cc | 
| diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc | 
| index 75c5b8e2a330f8306bd220e561d70c5b2ed4d534..e32089398b97dae02079d53ec7eb0fd52711044f 100644 | 
| --- a/test/cctest/test-api.cc | 
| +++ b/test/cctest/test-api.cc | 
| @@ -23035,3 +23035,307 @@ TEST(GetHiddenPropertyTableAfterAccessCheck) { | 
|  | 
| obj->SetHiddenValue(v8_str("hidden key 2"), v8_str("hidden value 2")); | 
| } | 
| + | 
| + | 
| +class TestSourceStream : public v8::ScriptCompiler::ExternalSourceStream { | 
| + public: | 
| +  explicit TestSourceStream(const char** chunks) : chunks_(chunks), index_(0) {} | 
| + | 
| +  virtual size_t GetMoreData(const uint8_t** src) { | 
| +    // Unlike in real use cases, this function will never block. | 
| +    if (chunks_[index_] == NULL) { | 
| +      return 0; | 
| +    } | 
| +    // Copy the data, since the caller takes ownership of it. | 
| +    size_t len = strlen(chunks_[index_]); | 
| +    // We don't need to zero-terminate since we return the length. | 
| +    uint8_t* copy = new uint8_t[len]; | 
| +    memcpy(copy, chunks_[index_], len); | 
| +    *src = copy; | 
| +    ++index_; | 
| +    return len; | 
| +  } | 
| + | 
| +  // Helper for constructing a string from chunks (the compilation needs it | 
| +  // too). | 
| +  static char* FullSourceString(const char** chunks) { | 
| +    size_t total_len = 0; | 
| +    for (size_t i = 0; chunks[i] != NULL; ++i) { | 
| +      total_len += strlen(chunks[i]); | 
| +    } | 
| +    char* full_string = new char[total_len + 1]; | 
| +    size_t offset = 0; | 
| +    for (size_t i = 0; chunks[i] != NULL; ++i) { | 
| +      size_t len = strlen(chunks[i]); | 
| +      memcpy(full_string + offset, chunks[i], len); | 
| +      offset += len; | 
| +    } | 
| +    full_string[total_len] = 0; | 
| +    return full_string; | 
| +  } | 
| + | 
| + private: | 
| +  const char** chunks_; | 
| +  unsigned index_; | 
| +}; | 
| + | 
| + | 
| +// Helper function for running streaming tests. | 
| +void RunStreamingTest(const char** chunks, | 
| +                      v8::ScriptCompiler::StreamedSource::Encoding encoding = | 
| +                          v8::ScriptCompiler::StreamedSource::ONE_BYTE, | 
| +                      bool expected_success = true) { | 
| +  LocalContext env; | 
| +  v8::Isolate* isolate = env->GetIsolate(); | 
| +  v8::HandleScope scope(isolate); | 
| +  v8::TryCatch try_catch; | 
| + | 
| +  v8::ScriptCompiler::StreamedSource source(new TestSourceStream(chunks), | 
| +                                            encoding); | 
| +  v8::ScriptCompiler::ScriptStreamingTask* task = | 
| +      v8::ScriptCompiler::StartStreamingScript(isolate, &source); | 
| + | 
| +  // TestSourceStream::GetMoreData won't block, so it's OK to just run the | 
| +  // task here in the main thread. | 
| +  task->Run(); | 
| +  delete task; | 
| + | 
| +  v8::ScriptOrigin origin(v8_str("http://foo.com")); | 
| +  char* full_source = TestSourceStream::FullSourceString(chunks); | 
| + | 
| +  // The possible errors are only produced while compiling. | 
| +  CHECK_EQ(false, try_catch.HasCaught()); | 
| + | 
| +  v8::Handle<Script> script = v8::ScriptCompiler::Compile( | 
| +      isolate, &source, v8_str(full_source), origin); | 
| +  if (expected_success) { | 
| +    CHECK(!script.IsEmpty()); | 
| +    v8::Handle<Value> result(script->Run()); | 
| +    // All scripts are supposed to return the fixed value 13 when ran. | 
| +    CHECK_EQ(13, result->Int32Value()); | 
| +  } else { | 
| +    CHECK(script.IsEmpty()); | 
| +    CHECK(try_catch.HasCaught()); | 
| +  } | 
| +  delete[] full_source; | 
| +} | 
| + | 
| + | 
| +TEST(StreamingSimpleScript) { | 
| +  // This script is unrealistically small, since no one chunk is enough to fill | 
| +  // the backing buffer of Scanner, let alone overflow it. | 
| +  const char* chunks[] = {"function foo() { ret", "urn 13; } f", "oo(); ", | 
| +                          NULL}; | 
| +  RunStreamingTest(chunks); | 
| +} | 
| + | 
| + | 
| +TEST(StreamingBiggerScript) { | 
| +  const char* chunk1 = | 
| +      "function foo() {\n" | 
| +      "  // Make this chunk sufficiently long so that it will overflow the\n" | 
| +      "  // backing buffer of the Scanner.\n" | 
| +      "  var i = 0;\n" | 
| +      "  var result = 0;\n" | 
| +      "  for (i = 0; i < 13; ++i) { result = result + 1; }\n" | 
| +      "  result = 0;\n" | 
| +      "  for (i = 0; i < 13; ++i) { result = result + 1; }\n" | 
| +      "  result = 0;\n" | 
| +      "  for (i = 0; i < 13; ++i) { result = result + 1; }\n" | 
| +      "  result = 0;\n" | 
| +      "  for (i = 0; i < 13; ++i) { result = result + 1; }\n" | 
| +      "  return result;\n" | 
| +      "}\n"; | 
| +  const char* chunks[] = {chunk1, "foo(); ", NULL}; | 
| +  RunStreamingTest(chunks); | 
| +} | 
| + | 
| + | 
| +TEST(StreamingScriptWithParseError) { | 
| +  // Test that parse errors from streamed scripts are propagated correctly. | 
| +  { | 
| +    char chunk1[] = | 
| +        "  // This will result in a parse error.\n" | 
| +        "  var if else then foo"; | 
| +    char chunk2[] = "  13\n"; | 
| +    const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; | 
| + | 
| +    RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::ONE_BYTE, | 
| +                     false); | 
| +  } | 
| +  // Test that the next script succeeds normally. | 
| +  { | 
| +    char chunk1[] = | 
| +        "  // This will be parsed successfully.\n" | 
| +        "  function foo() { return "; | 
| +    char chunk2[] = "  13; }\n"; | 
| +    const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; | 
| + | 
| +    RunStreamingTest(chunks); | 
| +  } | 
| +} | 
| + | 
| + | 
| +TEST(StreamingUtf8Script) { | 
| +  const char* chunk1 = | 
| +      "function foo() {\n" | 
| +      "  // This function will contain an UTF-8 character which is not in\n" | 
| +      "  // ASCII.\n" | 
| +      "  var foob\uc481r = 13;\n" | 
| +      "  return foob\uc481r;\n" | 
| +      "}\n"; | 
| +  const char* chunks[] = {chunk1, "foo(); ", NULL}; | 
| +  RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); | 
| +} | 
| + | 
| + | 
| +TEST(StreamingUtf8ScriptWithSplitCharactersSanityCheck) { | 
| +  // A sanity check to prove that the approach of splitting UTF-8 | 
| +  // characters is correct. Here is an UTF-8 character which will take three | 
| +  // bytes. | 
| +  const char* reference = "\uc481"; | 
| +  CHECK_EQ(3, strlen(reference)); | 
| +  char chunk1[] = | 
| +      "function foo() {\n" | 
| +      "  // This function will contain an UTF-8 character which is not in\n" | 
| +      "  // ASCII.\n" | 
| +      "  var foob"; | 
| +  char chunk2[] = | 
| +      "XXXr = 13;\n" | 
| +      "  return foob\uc481r;\n" | 
| +      "}\n"; | 
| +  for (int i = 0; i < 3; ++i) { | 
| +    chunk2[i] = reference[i]; | 
| +  } | 
| +  const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; | 
| +  RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); | 
| +} | 
| + | 
| + | 
| +TEST(StreamingUtf8ScriptWithSplitCharacters) { | 
| +  // Stream data where a multi-byte UTF-8 character is split between two data | 
| +  // chunks. | 
| +  const char* reference = "\uc481"; | 
| +  char chunk1[] = | 
| +      "function foo() {\n" | 
| +      "  // This function will contain an UTF-8 character which is not in\n" | 
| +      "  // ASCII.\n" | 
| +      "  var foobX"; | 
| +  char chunk2[] = | 
| +      "XXr = 13;\n" | 
| +      "  return foob\uc481r;\n" | 
| +      "}\n"; | 
| +  chunk1[strlen(chunk1) - 1] = reference[0]; | 
| +  chunk2[0] = reference[1]; | 
| +  chunk2[1] = reference[2]; | 
| +  const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; | 
| +  RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); | 
| +} | 
| + | 
| + | 
| +TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) { | 
| +  // Tests edge cases which should still be decoded correctly. | 
| + | 
| +  // Case 1: a chunk contains only bytes for a split character (and no other | 
| +  // data). This kind of a chunk would be exceptionally small, but we should | 
| +  // still decode it correctly. | 
| +  const char* reference = "\uc481"; | 
| +  fprintf(stderr, "%d %d %d\n", reference[0], reference[1], reference[2]); | 
| +  // The small chunk is at the beginning of the split character | 
| +  { | 
| +    char chunk1[] = | 
| +        "function foo() {\n" | 
| +        "  // This function will contain an UTF-8 character which is not in\n" | 
| +        "  // ASCII.\n" | 
| +        "  var foob"; | 
| +    char chunk2[] = "XX"; | 
| +    char chunk3[] = | 
| +        "Xr = 13;\n" | 
| +        "  return foob\uc481r;\n" | 
| +        "}\n"; | 
| +    chunk2[0] = reference[0]; | 
| +    chunk2[1] = reference[1]; | 
| +    chunk3[0] = reference[2]; | 
| +    const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; | 
| +    RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); | 
| +  } | 
| +  // The small chunk is at the end of a character | 
| +  { | 
| +    char chunk1[] = | 
| +        "function foo() {\n" | 
| +        "  // This function will contain an UTF-8 character which is not in\n" | 
| +        "  // ASCII.\n" | 
| +        "  var foobX"; | 
| +    char chunk2[] = "XX"; | 
| +    char chunk3[] = | 
| +        "r = 13;\n" | 
| +        "  return foob\uc481r;\n" | 
| +        "}\n"; | 
| +    chunk1[strlen(chunk1) - 1] = reference[0]; | 
| +    chunk2[0] = reference[1]; | 
| +    chunk2[1] = reference[2]; | 
| +    const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; | 
| +    RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); | 
| +  } | 
| +  // Case 2: the script ends with a multi-byte character. Make sure that it's | 
| +  // decoded correctly and not just ignored. | 
| +  { | 
| +    char chunk1[] = | 
| +        "var foob\uc481 = 13;\n" | 
| +        "foob\uc481"; | 
| +    const char* chunks[] = {chunk1, NULL}; | 
| +    RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); | 
| +  } | 
| +} | 
| + | 
| + | 
| +TEST(StreamingUtf8ScriptWithSplitCharactersInvalidEdgeCases) { | 
| +  // Test cases where a UTF-8 character is split over several chunks. Those | 
| +  // cases are not supported (the embedder should give the data in big enough | 
| +  // chunks), but we shouldn't crash, just produce a parse error. | 
| +  const char* reference = "\uc481"; | 
| +  char chunk1[] = | 
| +      "function foo() {\n" | 
| +      "  // This function will contain an UTF-8 character which is not in\n" | 
| +      "  // ASCII.\n" | 
| +      "  var foobX"; | 
| +  char chunk2[] = "X"; | 
| +  char chunk3[] = | 
| +      "Xr = 13;\n" | 
| +      "  return foob\uc481r;\n" | 
| +      "}\n"; | 
| +  chunk1[strlen(chunk1) - 1] = reference[0]; | 
| +  chunk2[0] = reference[1]; | 
| +  chunk3[0] = reference[2]; | 
| +  const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; | 
| + | 
| +  RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, false); | 
| +} | 
| + | 
| + | 
| +TEST(StreamingProducesParserCache) { | 
| +  i::FLAG_min_preparse_length = 0; | 
| +  const char* chunks[] = {"function foo() { ret", "urn 13; } f", "oo(); ", | 
| +                          NULL}; | 
| + | 
| +  LocalContext env; | 
| +  v8::Isolate* isolate = env->GetIsolate(); | 
| +  v8::HandleScope scope(isolate); | 
| + | 
| +  v8::ScriptCompiler::StreamedSource source( | 
| +      new TestSourceStream(chunks), | 
| +      v8::ScriptCompiler::StreamedSource::ONE_BYTE); | 
| +  v8::ScriptCompiler::ScriptStreamingTask* task = | 
| +      v8::ScriptCompiler::StartStreamingScript( | 
| +          isolate, &source, v8::ScriptCompiler::kProduceParserCache); | 
| + | 
| +  // TestSourceStream::GetMoreData won't block, so it's OK to just run the | 
| +  // task here in the main thread. | 
| +  task->Run(); | 
| +  delete task; | 
| + | 
| +  const v8::ScriptCompiler::CachedData* cached_data = source.GetCachedData(); | 
| +  CHECK(cached_data != NULL); | 
| +  CHECK(cached_data->data != NULL); | 
| +  CHECK_GT(cached_data->length, 0); | 
| +} | 
|  |