OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 23035 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
23046 IndexedAccessCounter); | 23046 IndexedAccessCounter); |
23047 | 23047 |
23048 v8::Handle<Context> context = Context::New(isolate); | 23048 v8::Handle<Context> context = Context::New(isolate); |
23049 v8::Context::Scope context_scope(context); | 23049 v8::Context::Scope context_scope(context); |
23050 | 23050 |
23051 context->Global()->Set(v8_str("o"), object_template->NewInstance()); | 23051 context->Global()->Set(v8_str("o"), object_template->NewInstance()); |
23052 CompileRun( | 23052 CompileRun( |
23053 "Object.defineProperty(o, 'key', " | 23053 "Object.defineProperty(o, 'key', " |
23054 " { get: function() {}, set: function() {} });"); | 23054 " { get: function() {}, set: function() {} });"); |
23055 } | 23055 } |
| 23056 |
| 23057 class TestSourceStream : public v8::ScriptCompiler::ExternalSourceStream { |
| 23058 public: |
| 23059 explicit TestSourceStream(const char** chunks) : chunks_(chunks), index_(0) {} |
| 23060 |
| 23061 virtual size_t GetMoreData(const uint8_t** src) { |
| 23062 // Unlike in real use cases, this function will never block. |
| 23063 if (chunks_[index_] == NULL) { |
| 23064 return 0; |
| 23065 } |
| 23066 // Copy the data, since the caller takes ownership of it. |
| 23067 size_t len = strlen(chunks_[index_]); |
| 23068 // We don't need to zero-terminate since we return the length. |
| 23069 uint8_t* copy = new uint8_t[len]; |
| 23070 memcpy(copy, chunks_[index_], len); |
| 23071 *src = copy; |
| 23072 ++index_; |
| 23073 return len; |
| 23074 } |
| 23075 |
| 23076 // Helper for constructing a string from chunks (the compilation needs it |
| 23077 // too). |
| 23078 static char* FullSourceString(const char** chunks) { |
| 23079 size_t total_len = 0; |
| 23080 for (size_t i = 0; chunks[i] != NULL; ++i) { |
| 23081 total_len += strlen(chunks[i]); |
| 23082 } |
| 23083 char* full_string = new char[total_len + 1]; |
| 23084 size_t offset = 0; |
| 23085 for (size_t i = 0; chunks[i] != NULL; ++i) { |
| 23086 size_t len = strlen(chunks[i]); |
| 23087 memcpy(full_string + offset, chunks[i], len); |
| 23088 offset += len; |
| 23089 } |
| 23090 full_string[total_len] = 0; |
| 23091 return full_string; |
| 23092 } |
| 23093 |
| 23094 private: |
| 23095 const char** chunks_; |
| 23096 unsigned index_; |
| 23097 }; |
| 23098 |
| 23099 |
| 23100 // Helper function for running streaming tests. |
| 23101 void RunStreamingTest(const char** chunks, |
| 23102 v8::ScriptCompiler::StreamedSource::Encoding encoding = |
| 23103 v8::ScriptCompiler::StreamedSource::ONE_BYTE, |
| 23104 bool expected_success = true) { |
| 23105 LocalContext env; |
| 23106 v8::Isolate* isolate = env->GetIsolate(); |
| 23107 v8::HandleScope scope(isolate); |
| 23108 v8::TryCatch try_catch; |
| 23109 |
| 23110 v8::ScriptCompiler::StreamedSource source(new TestSourceStream(chunks), |
| 23111 encoding); |
| 23112 v8::ScriptCompiler::ScriptStreamingTask* task = |
| 23113 v8::ScriptCompiler::StartStreamingScript(isolate, &source); |
| 23114 |
| 23115 // TestSourceStream::GetMoreData won't block, so it's OK to just run the |
| 23116 // task here in the main thread. |
| 23117 task->Run(); |
| 23118 delete task; |
| 23119 |
| 23120 v8::ScriptOrigin origin(v8_str("http://foo.com")); |
| 23121 char* full_source = TestSourceStream::FullSourceString(chunks); |
| 23122 |
| 23123 // The possible errors are only produced while compiling. |
| 23124 CHECK_EQ(false, try_catch.HasCaught()); |
| 23125 |
| 23126 v8::Handle<Script> script = v8::ScriptCompiler::Compile( |
| 23127 isolate, &source, v8_str(full_source), origin); |
| 23128 if (expected_success) { |
| 23129 CHECK(!script.IsEmpty()); |
| 23130 v8::Handle<Value> result(script->Run()); |
| 23131 // All scripts are supposed to return the fixed value 13 when ran. |
| 23132 CHECK_EQ(13, result->Int32Value()); |
| 23133 } else { |
| 23134 CHECK(script.IsEmpty()); |
| 23135 CHECK(try_catch.HasCaught()); |
| 23136 } |
| 23137 delete[] full_source; |
| 23138 } |
| 23139 |
| 23140 |
| 23141 TEST(StreamingSimpleScript) { |
| 23142 // This script is unrealistically small, since no one chunk is enough to fill |
| 23143 // the backing buffer of Scanner, let alone overflow it. |
| 23144 const char* chunks[] = {"function foo() { ret", "urn 13; } f", "oo(); ", |
| 23145 NULL}; |
| 23146 RunStreamingTest(chunks); |
| 23147 } |
| 23148 |
| 23149 |
| 23150 TEST(StreamingBiggerScript) { |
| 23151 const char* chunk1 = |
| 23152 "function foo() {\n" |
| 23153 " // Make this chunk sufficiently long so that it will overflow the\n" |
| 23154 " // backing buffer of the Scanner.\n" |
| 23155 " var i = 0;\n" |
| 23156 " var result = 0;\n" |
| 23157 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23158 " result = 0;\n" |
| 23159 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23160 " result = 0;\n" |
| 23161 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23162 " result = 0;\n" |
| 23163 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23164 " return result;\n" |
| 23165 "}\n"; |
| 23166 const char* chunks[] = {chunk1, "foo(); ", NULL}; |
| 23167 RunStreamingTest(chunks); |
| 23168 } |
| 23169 |
| 23170 |
| 23171 TEST(StreamingScriptWithParseError) { |
| 23172 // Test that parse errors from streamed scripts are propagated correctly. |
| 23173 { |
| 23174 char chunk1[] = |
| 23175 " // This will result in a parse error.\n" |
| 23176 " var if else then foo"; |
| 23177 char chunk2[] = " 13\n"; |
| 23178 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23179 |
| 23180 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::ONE_BYTE, |
| 23181 false); |
| 23182 } |
| 23183 // Test that the next script succeeds normally. |
| 23184 { |
| 23185 char chunk1[] = |
| 23186 " // This will be parsed successfully.\n" |
| 23187 " function foo() { return "; |
| 23188 char chunk2[] = " 13; }\n"; |
| 23189 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23190 |
| 23191 RunStreamingTest(chunks); |
| 23192 } |
| 23193 } |
| 23194 |
| 23195 |
| 23196 TEST(StreamingUtf8Script) { |
| 23197 // We'd want to write \uc481 instead of \xeb\x91\x80, but Windows compilers |
| 23198 // don't like it. |
| 23199 const char* chunk1 = |
| 23200 "function foo() {\n" |
| 23201 " // This function will contain an UTF-8 character which is not in\n" |
| 23202 " // ASCII.\n" |
| 23203 " var foob\xeb\x91\x80r = 13;\n" |
| 23204 " return foob\xeb\x91\x80r;\n" |
| 23205 "}\n"; |
| 23206 const char* chunks[] = {chunk1, "foo(); ", NULL}; |
| 23207 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 23208 } |
| 23209 |
| 23210 |
| 23211 TEST(StreamingUtf8ScriptWithSplitCharactersSanityCheck) { |
| 23212 // A sanity check to prove that the approach of splitting UTF-8 |
| 23213 // characters is correct. Here is an UTF-8 character which will take three |
| 23214 // bytes. |
| 23215 const char* reference = "\xeb\x91\x80"; |
| 23216 CHECK(3u == strlen(reference)); // NOLINT - no CHECK_EQ for unsigned. |
| 23217 |
| 23218 char chunk1[] = |
| 23219 "function foo() {\n" |
| 23220 " // This function will contain an UTF-8 character which is not in\n" |
| 23221 " // ASCII.\n" |
| 23222 " var foob"; |
| 23223 char chunk2[] = |
| 23224 "XXXr = 13;\n" |
| 23225 " return foob\xeb\x91\x80r;\n" |
| 23226 "}\n"; |
| 23227 for (int i = 0; i < 3; ++i) { |
| 23228 chunk2[i] = reference[i]; |
| 23229 } |
| 23230 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23231 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 23232 } |
| 23233 |
| 23234 |
| 23235 TEST(StreamingUtf8ScriptWithSplitCharacters) { |
| 23236 // Stream data where a multi-byte UTF-8 character is split between two data |
| 23237 // chunks. |
| 23238 const char* reference = "\xeb\x91\x80"; |
| 23239 char chunk1[] = |
| 23240 "function foo() {\n" |
| 23241 " // This function will contain an UTF-8 character which is not in\n" |
| 23242 " // ASCII.\n" |
| 23243 " var foobX"; |
| 23244 char chunk2[] = |
| 23245 "XXr = 13;\n" |
| 23246 " return foob\xeb\x91\x80r;\n" |
| 23247 "}\n"; |
| 23248 chunk1[strlen(chunk1) - 1] = reference[0]; |
| 23249 chunk2[0] = reference[1]; |
| 23250 chunk2[1] = reference[2]; |
| 23251 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23252 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 23253 } |
| 23254 |
| 23255 |
| 23256 TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) { |
| 23257 // Tests edge cases which should still be decoded correctly. |
| 23258 |
| 23259 // Case 1: a chunk contains only bytes for a split character (and no other |
| 23260 // data). This kind of a chunk would be exceptionally small, but we should |
| 23261 // still decode it correctly. |
| 23262 const char* reference = "\xeb\x91\x80"; |
| 23263 // The small chunk is at the beginning of the split character |
| 23264 { |
| 23265 char chunk1[] = |
| 23266 "function foo() {\n" |
| 23267 " // This function will contain an UTF-8 character which is not in\n" |
| 23268 " // ASCII.\n" |
| 23269 " var foob"; |
| 23270 char chunk2[] = "XX"; |
| 23271 char chunk3[] = |
| 23272 "Xr = 13;\n" |
| 23273 " return foob\xeb\x91\x80r;\n" |
| 23274 "}\n"; |
| 23275 chunk2[0] = reference[0]; |
| 23276 chunk2[1] = reference[1]; |
| 23277 chunk3[0] = reference[2]; |
| 23278 const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; |
| 23279 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 23280 } |
| 23281 // The small chunk is at the end of a character |
| 23282 { |
| 23283 char chunk1[] = |
| 23284 "function foo() {\n" |
| 23285 " // This function will contain an UTF-8 character which is not in\n" |
| 23286 " // ASCII.\n" |
| 23287 " var foobX"; |
| 23288 char chunk2[] = "XX"; |
| 23289 char chunk3[] = |
| 23290 "r = 13;\n" |
| 23291 " return foob\xeb\x91\x80r;\n" |
| 23292 "}\n"; |
| 23293 chunk1[strlen(chunk1) - 1] = reference[0]; |
| 23294 chunk2[0] = reference[1]; |
| 23295 chunk2[1] = reference[2]; |
| 23296 const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; |
| 23297 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 23298 } |
| 23299 // Case 2: the script ends with a multi-byte character. Make sure that it's |
| 23300 // decoded correctly and not just ignored. |
| 23301 { |
| 23302 char chunk1[] = |
| 23303 "var foob\xeb\x91\x80 = 13;\n" |
| 23304 "foob\xeb\x91\x80"; |
| 23305 const char* chunks[] = {chunk1, NULL}; |
| 23306 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8); |
| 23307 } |
| 23308 } |
| 23309 |
| 23310 |
| 23311 TEST(StreamingUtf8ScriptWithSplitCharactersInvalidEdgeCases) { |
| 23312 // Test cases where a UTF-8 character is split over several chunks. Those |
| 23313 // cases are not supported (the embedder should give the data in big enough |
| 23314 // chunks), but we shouldn't crash, just produce a parse error. |
| 23315 const char* reference = "\xeb\x91\x80"; |
| 23316 char chunk1[] = |
| 23317 "function foo() {\n" |
| 23318 " // This function will contain an UTF-8 character which is not in\n" |
| 23319 " // ASCII.\n" |
| 23320 " var foobX"; |
| 23321 char chunk2[] = "X"; |
| 23322 char chunk3[] = |
| 23323 "Xr = 13;\n" |
| 23324 " return foob\xeb\x91\x80r;\n" |
| 23325 "}\n"; |
| 23326 chunk1[strlen(chunk1) - 1] = reference[0]; |
| 23327 chunk2[0] = reference[1]; |
| 23328 chunk3[0] = reference[2]; |
| 23329 const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; |
| 23330 |
| 23331 RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, false); |
| 23332 } |
| 23333 |
| 23334 |
| 23335 TEST(StreamingProducesParserCache) { |
| 23336 i::FLAG_min_preparse_length = 0; |
| 23337 const char* chunks[] = {"function foo() { ret", "urn 13; } f", "oo(); ", |
| 23338 NULL}; |
| 23339 |
| 23340 LocalContext env; |
| 23341 v8::Isolate* isolate = env->GetIsolate(); |
| 23342 v8::HandleScope scope(isolate); |
| 23343 |
| 23344 v8::ScriptCompiler::StreamedSource source( |
| 23345 new TestSourceStream(chunks), |
| 23346 v8::ScriptCompiler::StreamedSource::ONE_BYTE); |
| 23347 v8::ScriptCompiler::ScriptStreamingTask* task = |
| 23348 v8::ScriptCompiler::StartStreamingScript( |
| 23349 isolate, &source, v8::ScriptCompiler::kProduceParserCache); |
| 23350 |
| 23351 // TestSourceStream::GetMoreData won't block, so it's OK to just run the |
| 23352 // task here in the main thread. |
| 23353 task->Run(); |
| 23354 delete task; |
| 23355 |
| 23356 const v8::ScriptCompiler::CachedData* cached_data = source.GetCachedData(); |
| 23357 CHECK(cached_data != NULL); |
| 23358 CHECK(cached_data->data != NULL); |
| 23359 CHECK_GT(cached_data->length, 0); |
| 23360 } |
OLD | NEW |