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 22983 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
22994 desc = x->GetOwnPropertyDescriptor(v8_str("p1")); | 22994 desc = x->GetOwnPropertyDescriptor(v8_str("p1")); |
22995 Local<Function> set = | 22995 Local<Function> set = |
22996 Local<Function>::Cast(Local<Object>::Cast(desc)->Get(v8_str("set"))); | 22996 Local<Function>::Cast(Local<Object>::Cast(desc)->Get(v8_str("set"))); |
22997 Local<Function> get = | 22997 Local<Function> get = |
22998 Local<Function>::Cast(Local<Object>::Cast(desc)->Get(v8_str("get"))); | 22998 Local<Function>::Cast(Local<Object>::Cast(desc)->Get(v8_str("get"))); |
22999 CHECK_EQ(v8_num(13), get->Call(x, 0, NULL)); | 22999 CHECK_EQ(v8_num(13), get->Call(x, 0, NULL)); |
23000 Handle<Value> args[] = { v8_num(14) }; | 23000 Handle<Value> args[] = { v8_num(14) }; |
23001 set->Call(x, 1, args); | 23001 set->Call(x, 1, args); |
23002 CHECK_EQ(v8_num(14), get->Call(x, 0, NULL)); | 23002 CHECK_EQ(v8_num(14), get->Call(x, 0, NULL)); |
23003 } | 23003 } |
| 23004 |
| 23005 class TestSourceStream : public v8::ScriptCompiler::ExternalSourceStream { |
| 23006 public: |
| 23007 explicit TestSourceStream( |
| 23008 const char** chunks, |
| 23009 v8::ScriptCompiler::ExternalSourceStream::Encoding encoding) |
| 23010 : v8::ScriptCompiler::ExternalSourceStream(encoding), |
| 23011 chunks_(chunks), |
| 23012 index_(0) {} |
| 23013 |
| 23014 virtual size_t GetMoreData(const uint8_t** src) { |
| 23015 // Unlike in real use cases, this function will never block. |
| 23016 if (chunks_[index_] == NULL) { |
| 23017 return 0; |
| 23018 } |
| 23019 // Copy the data, since the caller takes ownership of it. |
| 23020 size_t len = strlen(chunks_[index_]); |
| 23021 // We don't need to zero-terminate since we return the length. |
| 23022 uint8_t* copy = new uint8_t[len]; |
| 23023 memcpy(copy, chunks_[index_], len); |
| 23024 *src = copy; |
| 23025 ++index_; |
| 23026 return len; |
| 23027 } |
| 23028 |
| 23029 // Helper for constructing a string from chunks (the compilation needs it |
| 23030 // too). |
| 23031 static char* FullSourceString(const char** chunks) { |
| 23032 size_t total_len = 0; |
| 23033 for (size_t i = 0; chunks[i] != NULL; ++i) { |
| 23034 total_len += strlen(chunks[i]); |
| 23035 } |
| 23036 char* full_string = new char[total_len + 1]; |
| 23037 size_t offset = 0; |
| 23038 for (size_t i = 0; chunks[i] != NULL; ++i) { |
| 23039 size_t len = strlen(chunks[i]); |
| 23040 memcpy(full_string + offset, chunks[i], len); |
| 23041 offset += len; |
| 23042 } |
| 23043 full_string[total_len] = 0; |
| 23044 return full_string; |
| 23045 } |
| 23046 |
| 23047 private: |
| 23048 const char** chunks_; |
| 23049 unsigned index_; |
| 23050 }; |
| 23051 |
| 23052 |
| 23053 // Helper function for running streaming tests. |
| 23054 void RunStreamingTest( |
| 23055 const char** chunks, |
| 23056 v8::ScriptCompiler::ExternalSourceStream::Encoding encoding = |
| 23057 v8::ScriptCompiler::ExternalSourceStream::ONE_BYTE, |
| 23058 bool expected_success = true) { |
| 23059 LocalContext env; |
| 23060 v8::Isolate* isolate = env->GetIsolate(); |
| 23061 v8::HandleScope scope(isolate); |
| 23062 v8::TryCatch try_catch; |
| 23063 |
| 23064 v8::ScriptCompiler::StreamedSource source( |
| 23065 new TestSourceStream(chunks, encoding)); |
| 23066 v8::ScriptCompiler::ScriptStreamingTask* task = |
| 23067 v8::ScriptCompiler::StartStreamingScript(isolate, &source); |
| 23068 |
| 23069 // TestSourceStream::GetMoreData won't block, so it's okay to just run the |
| 23070 // task here in the main thread. |
| 23071 task->Run(); |
| 23072 delete task; |
| 23073 |
| 23074 v8::ScriptOrigin origin(v8_str("http://foo.com")); |
| 23075 char* full_source = TestSourceStream::FullSourceString(chunks); |
| 23076 |
| 23077 // The possible errors are only produced while compiling. |
| 23078 CHECK_EQ(false, try_catch.HasCaught()); |
| 23079 |
| 23080 v8::Handle<Script> script = v8::ScriptCompiler::Compile( |
| 23081 isolate, &source, v8_str(full_source), origin); |
| 23082 if (expected_success) { |
| 23083 CHECK(!script.IsEmpty()); |
| 23084 v8::Handle<Value> result(script->Run()); |
| 23085 // All scripts are supposed to return the fixed value 13 when ran. |
| 23086 CHECK_EQ(13, result->Int32Value()); |
| 23087 } else { |
| 23088 CHECK(script.IsEmpty()); |
| 23089 CHECK(try_catch.HasCaught()); |
| 23090 } |
| 23091 delete[] full_source; |
| 23092 } |
| 23093 |
| 23094 |
| 23095 TEST(StreamingSimpleScript) { |
| 23096 // This script is unrealistically small, since no one chunk is enough to fill |
| 23097 // the backing buffer of Scanner, let alone overflow it. |
| 23098 const char* chunks[] = {"function foo() { ret", "urn 13; } f", "oo(); ", |
| 23099 NULL}; |
| 23100 RunStreamingTest(chunks); |
| 23101 } |
| 23102 |
| 23103 |
| 23104 TEST(StreamingBiggerScript) { |
| 23105 const char* chunk1 = |
| 23106 "function foo() {\n" |
| 23107 " // Make this chunk sufficiently long so that it will overflow the\n" |
| 23108 " // backing buffer of the Scanner.\n" |
| 23109 " var i = 0;\n" |
| 23110 " var result = 0;\n" |
| 23111 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23112 " result = 0;\n" |
| 23113 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23114 " result = 0;\n" |
| 23115 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23116 " result = 0;\n" |
| 23117 " for (i = 0; i < 13; ++i) { result = result + 1; }\n" |
| 23118 " return result;\n" |
| 23119 "}\n"; |
| 23120 const char* chunks[] = {chunk1, "foo(); ", NULL}; |
| 23121 RunStreamingTest(chunks); |
| 23122 } |
| 23123 |
| 23124 |
| 23125 TEST(StreamingScriptWithParseError) { |
| 23126 // Test that parse errors from streamed scripts are propagated correctly. |
| 23127 { |
| 23128 char chunk1[] = |
| 23129 " // This will result in a parse error.\n" |
| 23130 " var if else then foo"; |
| 23131 char chunk2[] = " 13\n"; |
| 23132 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23133 |
| 23134 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::ONE_BYTE, |
| 23135 false); |
| 23136 } |
| 23137 // Test that the next script succeeds normally. |
| 23138 { |
| 23139 char chunk1[] = |
| 23140 " // This will be parsed successfully.\n" |
| 23141 " function foo() { return "; |
| 23142 char chunk2[] = " 13; }\n"; |
| 23143 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23144 |
| 23145 RunStreamingTest(chunks); |
| 23146 } |
| 23147 } |
| 23148 |
| 23149 |
| 23150 TEST(StreamingUtf8Script) { |
| 23151 const char* chunk1 = |
| 23152 "function foo() {\n" |
| 23153 " // This function will contain an UTF-8 character which is not in\n" |
| 23154 " // ASCII.\n" |
| 23155 " var foob\uc481r = 13;\n" |
| 23156 " return foob\uc481r;\n" |
| 23157 "}\n"; |
| 23158 const char* chunks[] = {chunk1, "foo(); ", NULL}; |
| 23159 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::UTF8); |
| 23160 } |
| 23161 |
| 23162 |
| 23163 TEST(StreamingUtf8ScriptWithSplitCharactersSanityCheck) { |
| 23164 // A sanity check to prove that the approach of splitting UTF-8 |
| 23165 // characters is correct. Here is an UTF-8 character which will take three |
| 23166 // bytes. |
| 23167 const char* reference = "\uc481"; |
| 23168 CHECK_EQ(3, strlen(reference)); |
| 23169 char chunk1[] = |
| 23170 "function foo() {\n" |
| 23171 " // This function will contain an UTF-8 character which is not in\n" |
| 23172 " // ASCII.\n" |
| 23173 " var foob"; |
| 23174 char chunk2[] = |
| 23175 "XXXr = 13;\n" |
| 23176 " return foob\uc481r;\n" |
| 23177 "}\n"; |
| 23178 for (int i = 0; i < 3; ++i) { |
| 23179 chunk2[i] = reference[i]; |
| 23180 } |
| 23181 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23182 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::UTF8); |
| 23183 } |
| 23184 |
| 23185 |
| 23186 TEST(StreamingUtf8ScriptWithSplitCharacters) { |
| 23187 // Stream data where a multi-byte UTF-8 character is split between two data |
| 23188 // chunks. |
| 23189 const char* reference = "\uc481"; |
| 23190 char chunk1[] = |
| 23191 "function foo() {\n" |
| 23192 " // This function will contain an UTF-8 character which is not in\n" |
| 23193 " // ASCII.\n" |
| 23194 " var foobX"; |
| 23195 char chunk2[] = |
| 23196 "XXr = 13;\n" |
| 23197 " return foob\uc481r;\n" |
| 23198 "}\n"; |
| 23199 chunk1[strlen(chunk1) - 1] = reference[0]; |
| 23200 chunk2[0] = reference[1]; |
| 23201 chunk2[1] = reference[2]; |
| 23202 const char* chunks[] = {chunk1, chunk2, "foo();", NULL}; |
| 23203 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::UTF8); |
| 23204 } |
| 23205 |
| 23206 |
| 23207 TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) { |
| 23208 // Tests edge cases which should still be decoded correctly. |
| 23209 |
| 23210 // Case 1: a chunk contains only bytes for a split character (and no other |
| 23211 // data). This kind of a chunk would be exceptionally small, but we should |
| 23212 // still decode it correctly. |
| 23213 const char* reference = "\uc481"; |
| 23214 fprintf(stderr, "%d %d %d\n", reference[0], reference[1], reference[2]); |
| 23215 // The small chunk is at the beginning of the split character |
| 23216 { |
| 23217 char chunk1[] = |
| 23218 "function foo() {\n" |
| 23219 " // This function will contain an UTF-8 character which is not in\n" |
| 23220 " // ASCII.\n" |
| 23221 " var foob"; |
| 23222 char chunk2[] = "XX"; |
| 23223 char chunk3[] = |
| 23224 "Xr = 13;\n" |
| 23225 " return foob\uc481r;\n" |
| 23226 "}\n"; |
| 23227 chunk2[0] = reference[0]; |
| 23228 chunk2[1] = reference[1]; |
| 23229 chunk3[0] = reference[2]; |
| 23230 const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; |
| 23231 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::UTF8); |
| 23232 } |
| 23233 // The small chunk is at the end of a character |
| 23234 { |
| 23235 char chunk1[] = |
| 23236 "function foo() {\n" |
| 23237 " // This function will contain an UTF-8 character which is not in\n" |
| 23238 " // ASCII.\n" |
| 23239 " var foobX"; |
| 23240 char chunk2[] = "XX"; |
| 23241 char chunk3[] = |
| 23242 "r = 13;\n" |
| 23243 " return foob\uc481r;\n" |
| 23244 "}\n"; |
| 23245 chunk1[strlen(chunk1) - 1] = reference[0]; |
| 23246 chunk2[0] = reference[1]; |
| 23247 chunk2[1] = reference[2]; |
| 23248 const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; |
| 23249 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::UTF8); |
| 23250 } |
| 23251 // Case 2: the script ends with a multi-byte character. Make sure that it's |
| 23252 // decoded correctly and not just ignored. |
| 23253 { |
| 23254 char chunk1[] = |
| 23255 "var foob\uc481 = 13;\n" |
| 23256 "foob\uc481"; |
| 23257 const char* chunks[] = {chunk1, NULL}; |
| 23258 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::UTF8); |
| 23259 } |
| 23260 } |
| 23261 |
| 23262 |
| 23263 TEST(StreamingUtf8ScriptWithSplitCharactersInvalidEdgeCases) { |
| 23264 // Test cases where a UTF-8 character is split over several chunks. Those |
| 23265 // cases are not supported (the embedder should give the data in big enough |
| 23266 // chunks), but we shouldn't crash, just produce a parse error. |
| 23267 const char* reference = "\uc481"; |
| 23268 char chunk1[] = |
| 23269 "function foo() {\n" |
| 23270 " // This function will contain an UTF-8 character which is not in\n" |
| 23271 " // ASCII.\n" |
| 23272 " var foobX"; |
| 23273 char chunk2[] = "X"; |
| 23274 char chunk3[] = |
| 23275 "Xr = 13;\n" |
| 23276 " return foob\uc481r;\n" |
| 23277 "}\n"; |
| 23278 chunk1[strlen(chunk1) - 1] = reference[0]; |
| 23279 chunk2[0] = reference[1]; |
| 23280 chunk3[0] = reference[2]; |
| 23281 const char* chunks[] = {chunk1, chunk2, chunk3, "foo();", NULL}; |
| 23282 |
| 23283 RunStreamingTest(chunks, v8::ScriptCompiler::ExternalSourceStream::UTF8, |
| 23284 false); |
| 23285 } |
| 23286 |
| 23287 |
| 23288 TEST(StreamingProducesParserCache) { |
| 23289 i::FLAG_min_preparse_length = 0; |
| 23290 const char* chunks[] = {"function foo() { ret", "urn 13; } f", "oo(); ", |
| 23291 NULL}; |
| 23292 |
| 23293 LocalContext env; |
| 23294 v8::Isolate* isolate = env->GetIsolate(); |
| 23295 v8::HandleScope scope(isolate); |
| 23296 |
| 23297 v8::ScriptCompiler::StreamedSource source(new TestSourceStream( |
| 23298 chunks, v8::ScriptCompiler::ExternalSourceStream::ONE_BYTE)); |
| 23299 v8::ScriptCompiler::ScriptStreamingTask* task = |
| 23300 v8::ScriptCompiler::StartStreamingScript( |
| 23301 isolate, &source, v8::ScriptCompiler::kProduceParserCache); |
| 23302 |
| 23303 // TestSourceStream::GetMoreData won't block, so it's okay to just run the |
| 23304 // task here in the main thread. |
| 23305 task->Run(); |
| 23306 delete task; |
| 23307 |
| 23308 const v8::ScriptCompiler::CachedData* cached_data = source.GetCachedData(); |
| 23309 CHECK(cached_data != NULL); |
| 23310 CHECK(cached_data->data != NULL); |
| 23311 CHECK_GT(cached_data->length, 0); |
| 23312 } |
OLD | NEW |