1 // Copyright 2016 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/compiler-dispatcher/unoptimized-compile-job.h" 6 7 #include "src/assert-scope.h" 8 #include "src/base/optional.h" 9 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" 10 #include "src/compiler.h" 11 #include "src/flags.h" 12 #include "src/global-handles.h" 13 #include "src/interpreter/interpreter.h" 14 #include "src/isolate.h" 15 #include "src/objects-inl.h" 16 #include "src/parsing/parse-info.h" 17 #include "src/parsing/parser.h" 18 #include "src/parsing/scanner-character-streams.h" 19 #include "src/unicode-cache.h" 20 #include "src/unoptimized-compilation-info.h" 21 #include "src/utils.h" 22 23 namespace v8 { 24 namespace internal { 25 26 namespace { 27 28 class OneByteWrapper : public v8::String::ExternalOneByteStringResource { 29 public: 30 OneByteWrapper(const void* data, int length) : data_(data), length_(length) {} 31 ~OneByteWrapper() override = default; 32 33 const char* data() const override { 34 return reinterpret_cast<const char*>(data_); 35 } 36 37 size_t length() const override { return static_cast<size_t>(length_); } 38 39 private: 40 const void* data_; 41 int length_; 42 43 DISALLOW_COPY_AND_ASSIGN(OneByteWrapper); 44 }; 45 46 class TwoByteWrapper : public v8::String::ExternalStringResource { 47 public: 48 TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {} 49 ~TwoByteWrapper() override = default; 50 51 const uint16_t* data() const override { 52 return reinterpret_cast<const uint16_t*>(data_); 53 } 54 55 size_t length() const override { return static_cast<size_t>(length_); } 56 57 private: 58 const void* data_; 59 int length_; 60 61 DISALLOW_COPY_AND_ASSIGN(TwoByteWrapper); 62 }; 63 64 } // namespace 65 66 UnoptimizedCompileJob::UnoptimizedCompileJob(Isolate* isolate, 67 CompilerDispatcherTracer* tracer, 68 Handle<SharedFunctionInfo> shared, 69 size_t max_stack_size) 70 : CompilerDispatcherJob(Type::kUnoptimizedCompile), 71 main_thread_id_(isolate->thread_id().ToInteger()), 72 tracer_(tracer), 73 allocator_(isolate->allocator()), 74 context_(isolate->global_handles()->Create(isolate->context())), 75 shared_(isolate->global_handles()->Create(*shared)), 76 max_stack_size_(max_stack_size), 77 trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) { 78 DCHECK(!shared_->is_toplevel()); 79 // TODO(rmcilroy): Handle functions with non-empty outer scope info. 80 DCHECK(!shared_->HasOuterScopeInfo()); 81 HandleScope scope(isolate); 82 Handle<Script> script(Script::cast(shared_->script()), isolate); 83 Handle<String> source(String::cast(script->source()), isolate); 84 if (trace_compiler_dispatcher_jobs_) { 85 PrintF("UnoptimizedCompileJob[%p] created for ", static_cast<void*>(this)); 86 ShortPrintOnMainThread(); 87 PrintF(" in initial state.\n"); 88 } 89 } 90 91 UnoptimizedCompileJob::~UnoptimizedCompileJob() { 92 DCHECK(status() == Status::kInitial || status() == Status::kDone); 93 if (!shared_.is_null()) { 94 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 95 i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location()); 96 } 97 if (!context_.is_null()) { 98 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 99 i::GlobalHandles::Destroy(Handle<Object>::cast(context_).location()); 100 } 101 } 102 103 bool UnoptimizedCompileJob::IsAssociatedWith( 104 Handle<SharedFunctionInfo> shared) const { 105 return *shared_ == *shared; 106 } 107 108 void UnoptimizedCompileJob::PrepareOnMainThread(Isolate* isolate) { 109 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 110 DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_); 111 DCHECK_EQ(status(), Status::kInitial); 112 COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepare); 113 114 if (trace_compiler_dispatcher_jobs_) { 115 PrintF("UnoptimizedCompileJob[%p]: Preparing to parse\n", 116 static_cast<void*>(this)); 117 } 118 119 ParseInfo* parse_info = new ParseInfo(isolate, shared_); 120 parse_info_.reset(parse_info); 121 122 unicode_cache_.reset(new UnicodeCache()); 123 parse_info_->set_unicode_cache(unicode_cache_.get()); 124 parse_info_->set_function_literal_id(shared_->FunctionLiteralId(isolate)); 125 if (V8_UNLIKELY(FLAG_runtime_stats)) { 126 parse_info_->set_runtime_call_stats(new (parse_info_->zone()) 127 RuntimeCallStats()); 128 } 129 130 Handle<Script> script = parse_info->script(); 131 HandleScope scope(isolate); 132 133 DCHECK(script->type() != Script::TYPE_NATIVE); 134 Handle<String> source(String::cast(script->source()), isolate); 135 if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) { 136 std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For( 137 isolate, source, shared_->StartPosition(), shared_->EndPosition())); 138 parse_info_->set_character_stream(std::move(stream)); 139 } else { 140 source = String::Flatten(isolate, source); 141 const void* data; 142 int offset = 0; 143 int length = source->length(); 144 145 // Objects in lo_space don't move, so we can just read the contents from 146 // any thread. 147 if (isolate->heap()->lo_space()->Contains(*source)) { 148 // We need to globalize the handle to the flattened string here, in 149 // case it's not referenced from anywhere else. 150 source_ = isolate->global_handles()->Create(*source); 151 DisallowHeapAllocation no_allocation; 152 String::FlatContent content = source->GetFlatContent(); 153 DCHECK(content.IsFlat()); 154 data = 155 content.IsOneByte() 156 ? reinterpret_cast<const void*>(content.ToOneByteVector().start()) 157 : reinterpret_cast<const void*>(content.ToUC16Vector().start()); 158 } else { 159 // Otherwise, create a copy of the part of the string we'll parse in the 160 // zone. 161 length = (shared_->EndPosition() - shared_->StartPosition()); 162 offset = shared_->StartPosition(); 163 164 int byte_len = length * (source->IsOneByteRepresentation() ? 1 : 2); 165 data = parse_info_->zone()->New(byte_len); 166 167 DisallowHeapAllocation no_allocation; 168 String::FlatContent content = source->GetFlatContent(); 169 DCHECK(content.IsFlat()); 170 if (content.IsOneByte()) { 171 MemCopy(const_cast<void*>(data), 172 &content.ToOneByteVector().at(shared_->StartPosition()), 173 byte_len); 174 } else { 175 MemCopy(const_cast<void*>(data), 176 &content.ToUC16Vector().at(shared_->StartPosition()), byte_len); 177 } 178 } 179 Handle<String> wrapper; 180 if (source->IsOneByteRepresentation()) { 181 ExternalOneByteString::Resource* resource = 182 new OneByteWrapper(data, length); 183 wrapper = isolate->factory() 184 ->NewExternalStringFromOneByte(resource) 185 .ToHandleChecked(); 186 } else { 187 ExternalTwoByteString::Resource* resource = 188 new TwoByteWrapper(data, length); 189 wrapper = isolate->factory() 190 ->NewExternalStringFromTwoByte(resource) 191 .ToHandleChecked(); 192 } 193 wrapper_ = isolate->global_handles()->Create(*wrapper); 194 std::unique_ptr<Utf16CharacterStream> stream( 195 ScannerStream::For(isolate, wrapper_, shared_->StartPosition() - offset, 196 shared_->EndPosition() - offset)); 197 parse_info_->set_character_stream(std::move(stream)); 198 } 199 200 parser_.reset(new Parser(parse_info_.get())); 201 parser_->DeserializeScopeChain(isolate, parse_info_.get(), 202 parse_info_->maybe_outer_scope_info()); 203 204 // Initailize the name after setting up the ast_value_factory. 205 Handle<String> name(shared_->Name(), isolate); 206 parse_info_->set_function_name( 207 parse_info_->ast_value_factory()->GetString(name)); 208 209 set_status(Status::kPrepared); 210 } 211 212 void UnoptimizedCompileJob::Compile(bool on_background_thread) { 213 DCHECK_EQ(status(), Status::kPrepared); 214 COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM( 215 tracer_, kCompile, 216 parse_info_->end_position() - parse_info_->start_position()); 217 if (trace_compiler_dispatcher_jobs_) { 218 PrintF("UnoptimizedCompileJob[%p]: Compiling\n", static_cast<void*>(this)); 219 } 220 221 DisallowHeapAllocation no_allocation; 222 DisallowHandleAllocation no_handles; 223 DisallowHandleDereference no_deref; 224 225 parse_info_->set_on_background_thread(on_background_thread); 226 uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB; 227 parser_->set_stack_limit(stack_limit); 228 parse_info_->set_stack_limit(stack_limit); 229 parser_->ParseOnBackground(parse_info_.get()); 230 231 if (parse_info_->literal() == nullptr) { 232 // Parser sets error in pending error handler. 233 set_status(Status::kHasErrorsToReport); 234 return; 235 } 236 237 if (!Compiler::Analyze(parse_info_.get())) { 238 parse_info_->pending_error_handler()->set_stack_overflow(); 239 set_status(Status::kHasErrorsToReport); 240 return; 241 } 242 243 compilation_job_.reset(interpreter::Interpreter::NewCompilationJob( 244 parse_info_.get(), parse_info_->literal(), allocator_, nullptr)); 245 246 if (!compilation_job_.get()) { 247 parse_info_->pending_error_handler()->set_stack_overflow(); 248 set_status(Status::kHasErrorsToReport); 249 return; 250 } 251 252 if (compilation_job_->ExecuteJob() != CompilationJob::SUCCEEDED) { 253 parse_info_->pending_error_handler()->set_stack_overflow(); 254 set_status(Status::kHasErrorsToReport); 255 return; 256 } 257 258 set_status(Status::kCompiled); 259 } 260 261 void UnoptimizedCompileJob::FinalizeOnMainThread(Isolate* isolate) { 262 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 263 DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_); 264 DCHECK_EQ(status(), Status::kCompiled); 265 DCHECK_NOT_NULL(parse_info_->literal()); 266 DCHECK_NOT_NULL(compilation_job_.get()); 267 COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalize); 268 if (trace_compiler_dispatcher_jobs_) { 269 PrintF("UnoptimizedCompileJob[%p]: Finalizing compiling\n", 270 static_cast<void*>(this)); 271 } 272 273 Handle<Script> script(Script::cast(shared_->script()), isolate); 274 DCHECK_EQ(*parse_info_->script(), shared_->script()); 275 276 parser_->UpdateStatistics(isolate, script); 277 parse_info_->UpdateBackgroundParseStatisticsOnMainThread(isolate); 278 parser_->HandleSourceURLComments(isolate, script); 279 280 { 281 HandleScope scope(isolate); 282 // Internalize ast values onto the heap. 283 parse_info_->ast_value_factory()->Internalize(isolate); 284 // Allocate scope infos for the literal. 285 DeclarationScope::AllocateScopeInfos(parse_info_.get(), isolate); 286 if (compilation_job_->state() == CompilationJob::State::kFailed || 287 !Compiler::FinalizeCompilationJob(compilation_job_.release(), shared_, 288 isolate)) { 289 if (!isolate->has_pending_exception()) isolate->StackOverflow(); 290 set_status(Status::kFailed); 291 return; 292 } 293 } 294 295 ResetDataOnMainThread(isolate); 296 set_status(Status::kDone); 297 } 298 299 void UnoptimizedCompileJob::ReportErrorsOnMainThread(Isolate* isolate) { 300 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 301 DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_); 302 DCHECK_EQ(status(), Status::kHasErrorsToReport); 303 304 if (trace_compiler_dispatcher_jobs_) { 305 PrintF("UnoptimizedCompileJob[%p]: Reporting Errors\n", 306 static_cast<void*>(this)); 307 } 308 309 // Ensure we report errors in the correct context for the job. 310 SaveContext save(isolate); 311 isolate->set_context(context()); 312 313 Handle<Script> script(Script::cast(shared_->script()), isolate); 314 parse_info_->pending_error_handler()->ReportErrors( 315 isolate, script, parse_info_->ast_value_factory()); 316 317 ResetDataOnMainThread(isolate); 318 set_status(Status::kFailed); 319 } 320 321 void UnoptimizedCompileJob::ResetDataOnMainThread(Isolate* isolate) { 322 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 323 DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_); 324 325 compilation_job_.reset(); 326 parser_.reset(); 327 unicode_cache_.reset(); 328 parse_info_.reset(); 329 330 if (!source_.is_null()) { 331 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 332 DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_); 333 i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location()); 334 source_ = Handle<String>::null(); 335 } 336 if (!wrapper_.is_null()) { 337 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 338 DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_); 339 i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location()); 340 wrapper_ = Handle<String>::null(); 341 } 342 } 343 344 void UnoptimizedCompileJob::ResetOnMainThread(Isolate* isolate) { 345 if (trace_compiler_dispatcher_jobs_) { 346 PrintF("UnoptimizedCompileJob[%p]: Resetting\n", static_cast<void*>(this)); 347 } 348 349 ResetDataOnMainThread(isolate); 350 set_status(Status::kInitial); 351 } 352 353 double UnoptimizedCompileJob::EstimateRuntimeOfNextStepInMs() const { 354 switch (status()) { 355 case Status::kInitial: 356 return tracer_->EstimatePrepareInMs(); 357 case Status::kPrepared: 358 return tracer_->EstimateCompileInMs(parse_info_->end_position() - 359 parse_info_->start_position()); 360 case Status::kCompiled: 361 return tracer_->EstimateFinalizeInMs(); 362 363 case Status::kHasErrorsToReport: 364 case Status::kFailed: 365 case Status::kDone: 366 return 0.0; 367 } 368 369 UNREACHABLE(); 370 } 371 372 void UnoptimizedCompileJob::ShortPrintOnMainThread() { 373 DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_); 374 DCHECK(!shared_.is_null()); 375 shared_->ShortPrint(); 376 } 377 378 } // namespace internal 379 } // namespace v8 380