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/compiler-dispatcher-job.h" 6 7 #include "src/assert-scope.h" 8 #include "src/compilation-info.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/isolate.h" 14 #include "src/objects-inl.h" 15 #include "src/parsing/parse-info.h" 16 #include "src/parsing/parser.h" 17 #include "src/parsing/scanner-character-streams.h" 18 #include "src/unicode-cache.h" 19 #include "src/utils.h" 20 21 namespace v8 { 22 namespace internal { 23 24 namespace { 25 26 class OneByteWrapper : public v8::String::ExternalOneByteStringResource { 27 public: 28 OneByteWrapper(const void* data, int length) : data_(data), length_(length) {} 29 ~OneByteWrapper() override = default; 30 31 const char* data() const override { 32 return reinterpret_cast<const char*>(data_); 33 } 34 35 size_t length() const override { return static_cast<size_t>(length_); } 36 37 private: 38 const void* data_; 39 int length_; 40 41 DISALLOW_COPY_AND_ASSIGN(OneByteWrapper); 42 }; 43 44 class TwoByteWrapper : public v8::String::ExternalStringResource { 45 public: 46 TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {} 47 ~TwoByteWrapper() override = default; 48 49 const uint16_t* data() const override { 50 return reinterpret_cast<const uint16_t*>(data_); 51 } 52 53 size_t length() const override { return static_cast<size_t>(length_); } 54 55 private: 56 const void* data_; 57 int length_; 58 59 DISALLOW_COPY_AND_ASSIGN(TwoByteWrapper); 60 }; 61 62 } // namespace 63 64 CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate, 65 CompilerDispatcherTracer* tracer, 66 Handle<SharedFunctionInfo> shared, 67 size_t max_stack_size) 68 : status_(CompileJobStatus::kInitial), 69 isolate_(isolate), 70 tracer_(tracer), 71 context_(Handle<Context>::cast( 72 isolate_->global_handles()->Create(isolate->context()))), 73 shared_(Handle<SharedFunctionInfo>::cast( 74 isolate_->global_handles()->Create(*shared))), 75 max_stack_size_(max_stack_size), 76 trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) { 77 DCHECK(!shared_->is_toplevel()); 78 HandleScope scope(isolate_); 79 Handle<Script> script(Script::cast(shared_->script()), isolate_); 80 Handle<String> source(String::cast(script->source()), isolate_); 81 if (trace_compiler_dispatcher_jobs_) { 82 PrintF("CompilerDispatcherJob[%p] created for ", static_cast<void*>(this)); 83 shared_->ShortPrint(); 84 PrintF(" in initial state.\n"); 85 } 86 } 87 88 CompilerDispatcherJob::CompilerDispatcherJob( 89 Isolate* isolate, CompilerDispatcherTracer* tracer, Handle<Script> script, 90 Handle<SharedFunctionInfo> shared, FunctionLiteral* literal, 91 std::shared_ptr<Zone> parse_zone, 92 std::shared_ptr<DeferredHandles> parse_handles, 93 std::shared_ptr<DeferredHandles> compile_handles, size_t max_stack_size) 94 : status_(CompileJobStatus::kAnalyzed), 95 isolate_(isolate), 96 tracer_(tracer), 97 context_(Handle<Context>::cast( 98 isolate_->global_handles()->Create(isolate->context()))), 99 shared_(Handle<SharedFunctionInfo>::cast( 100 isolate_->global_handles()->Create(*shared))), 101 max_stack_size_(max_stack_size), 102 parse_info_(new ParseInfo(shared_)), 103 parse_zone_(parse_zone), 104 compile_info_(new CompilationInfo(parse_info_->zone(), parse_info_.get(), 105 Handle<JSFunction>::null())), 106 trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) { 107 parse_info_->set_literal(literal); 108 parse_info_->set_script(script); 109 parse_info_->set_deferred_handles(parse_handles); 110 compile_info_->set_deferred_handles(compile_handles); 111 112 if (trace_compiler_dispatcher_jobs_) { 113 PrintF("CompilerDispatcherJob[%p] created for ", static_cast<void*>(this)); 114 shared_->ShortPrint(); 115 PrintF(" in Analyzed state.\n"); 116 } 117 } 118 119 CompilerDispatcherJob::~CompilerDispatcherJob() { 120 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 121 DCHECK(status_ == CompileJobStatus::kInitial || 122 status_ == CompileJobStatus::kDone); 123 i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location()); 124 i::GlobalHandles::Destroy(Handle<Object>::cast(context_).location()); 125 } 126 127 bool CompilerDispatcherJob::IsAssociatedWith( 128 Handle<SharedFunctionInfo> shared) const { 129 return *shared_ == *shared; 130 } 131 132 void CompilerDispatcherJob::PrepareToParseOnMainThread() { 133 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 134 DCHECK(status() == CompileJobStatus::kInitial); 135 COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToParse); 136 if (trace_compiler_dispatcher_jobs_) { 137 PrintF("CompilerDispatcherJob[%p]: Preparing to parse\n", 138 static_cast<void*>(this)); 139 } 140 HandleScope scope(isolate_); 141 unicode_cache_.reset(new UnicodeCache()); 142 Handle<Script> script(Script::cast(shared_->script()), isolate_); 143 DCHECK(script->type() != Script::TYPE_NATIVE); 144 145 Handle<String> source(String::cast(script->source()), isolate_); 146 parse_info_.reset(new ParseInfo(isolate_->allocator())); 147 if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) { 148 character_stream_.reset(ScannerStream::For( 149 source, shared_->start_position(), shared_->end_position())); 150 } else { 151 source = String::Flatten(source); 152 const void* data; 153 int offset = 0; 154 int length = source->length(); 155 156 // Objects in lo_space don't move, so we can just read the contents from 157 // any thread. 158 if (isolate_->heap()->lo_space()->Contains(*source)) { 159 // We need to globalize the handle to the flattened string here, in 160 // case it's not referenced from anywhere else. 161 source_ = 162 Handle<String>::cast(isolate_->global_handles()->Create(*source)); 163 DisallowHeapAllocation no_allocation; 164 String::FlatContent content = source->GetFlatContent(); 165 DCHECK(content.IsFlat()); 166 data = 167 content.IsOneByte() 168 ? reinterpret_cast<const void*>(content.ToOneByteVector().start()) 169 : reinterpret_cast<const void*>(content.ToUC16Vector().start()); 170 } else { 171 // Otherwise, create a copy of the part of the string we'll parse in the 172 // zone. 173 length = (shared_->end_position() - shared_->start_position()); 174 offset = shared_->start_position(); 175 176 int byte_len = length * (source->IsOneByteRepresentation() ? 1 : 2); 177 data = parse_info_->zone()->New(byte_len); 178 179 DisallowHeapAllocation no_allocation; 180 String::FlatContent content = source->GetFlatContent(); 181 DCHECK(content.IsFlat()); 182 if (content.IsOneByte()) { 183 MemCopy(const_cast<void*>(data), 184 &content.ToOneByteVector().at(shared_->start_position()), 185 byte_len); 186 } else { 187 MemCopy(const_cast<void*>(data), 188 &content.ToUC16Vector().at(shared_->start_position()), 189 byte_len); 190 } 191 } 192 Handle<String> wrapper; 193 if (source->IsOneByteRepresentation()) { 194 ExternalOneByteString::Resource* resource = 195 new OneByteWrapper(data, length); 196 source_wrapper_.reset(resource); 197 wrapper = isolate_->factory() 198 ->NewExternalStringFromOneByte(resource) 199 .ToHandleChecked(); 200 } else { 201 ExternalTwoByteString::Resource* resource = 202 new TwoByteWrapper(data, length); 203 source_wrapper_.reset(resource); 204 wrapper = isolate_->factory() 205 ->NewExternalStringFromTwoByte(resource) 206 .ToHandleChecked(); 207 } 208 wrapper_ = 209 Handle<String>::cast(isolate_->global_handles()->Create(*wrapper)); 210 211 character_stream_.reset( 212 ScannerStream::For(wrapper_, shared_->start_position() - offset, 213 shared_->end_position() - offset)); 214 } 215 parse_info_->set_isolate(isolate_); 216 parse_info_->set_character_stream(character_stream_.get()); 217 parse_info_->set_hash_seed(isolate_->heap()->HashSeed()); 218 parse_info_->set_is_named_expression(shared_->is_named_expression()); 219 parse_info_->set_compiler_hints(shared_->compiler_hints()); 220 parse_info_->set_start_position(shared_->start_position()); 221 parse_info_->set_end_position(shared_->end_position()); 222 parse_info_->set_unicode_cache(unicode_cache_.get()); 223 parse_info_->set_language_mode(shared_->language_mode()); 224 parse_info_->set_function_literal_id(shared_->function_literal_id()); 225 226 parser_.reset(new Parser(parse_info_.get())); 227 MaybeHandle<ScopeInfo> outer_scope_info; 228 if (!shared_->outer_scope_info()->IsTheHole(isolate_) && 229 ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) { 230 outer_scope_info = handle(ScopeInfo::cast(shared_->outer_scope_info())); 231 } 232 parser_->DeserializeScopeChain(parse_info_.get(), outer_scope_info); 233 234 Handle<String> name(String::cast(shared_->name())); 235 parse_info_->set_function_name( 236 parse_info_->ast_value_factory()->GetString(name)); 237 status_ = CompileJobStatus::kReadyToParse; 238 } 239 240 void CompilerDispatcherJob::Parse() { 241 DCHECK(status() == CompileJobStatus::kReadyToParse); 242 COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM( 243 tracer_, kParse, 244 parse_info_->end_position() - parse_info_->start_position()); 245 if (trace_compiler_dispatcher_jobs_) { 246 PrintF("CompilerDispatcherJob[%p]: Parsing\n", static_cast<void*>(this)); 247 } 248 249 DisallowHeapAllocation no_allocation; 250 DisallowHandleAllocation no_handles; 251 DisallowHandleDereference no_deref; 252 253 // Nullify the Isolate temporarily so that the parser doesn't accidentally 254 // use it. 255 parse_info_->set_isolate(nullptr); 256 257 uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB; 258 259 parser_->set_stack_limit(stack_limit); 260 parser_->ParseOnBackground(parse_info_.get()); 261 262 parse_info_->set_isolate(isolate_); 263 264 status_ = CompileJobStatus::kParsed; 265 } 266 267 bool CompilerDispatcherJob::FinalizeParsingOnMainThread() { 268 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 269 DCHECK(status() == CompileJobStatus::kParsed); 270 COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeParsing); 271 if (trace_compiler_dispatcher_jobs_) { 272 PrintF("CompilerDispatcherJob[%p]: Finalizing parsing\n", 273 static_cast<void*>(this)); 274 } 275 276 if (!source_.is_null()) { 277 i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location()); 278 source_ = Handle<String>::null(); 279 } 280 if (!wrapper_.is_null()) { 281 i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location()); 282 wrapper_ = Handle<String>::null(); 283 } 284 285 Handle<Script> script(Script::cast(shared_->script()), isolate_); 286 parse_info_->set_script(script); 287 if (parse_info_->literal() == nullptr) { 288 parser_->ReportErrors(isolate_, script); 289 status_ = CompileJobStatus::kFailed; 290 } else { 291 status_ = CompileJobStatus::kReadyToAnalyze; 292 } 293 parser_->UpdateStatistics(isolate_, script); 294 295 DeferredHandleScope scope(isolate_); 296 { 297 parse_info_->ReopenHandlesInNewHandleScope(); 298 299 if (!shared_->outer_scope_info()->IsTheHole(isolate_) && 300 ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) { 301 Handle<ScopeInfo> outer_scope_info( 302 handle(ScopeInfo::cast(shared_->outer_scope_info()))); 303 parse_info_->set_outer_scope_info(outer_scope_info); 304 } 305 parse_info_->set_shared_info(shared_); 306 307 // Internalize ast values on the main thread. 308 parse_info_->ast_value_factory()->Internalize(isolate_); 309 parser_->HandleSourceURLComments(isolate_, script); 310 311 parse_info_->set_character_stream(nullptr); 312 parse_info_->set_unicode_cache(nullptr); 313 parser_.reset(); 314 unicode_cache_.reset(); 315 character_stream_.reset(); 316 } 317 parse_info_->set_deferred_handles(scope.Detach()); 318 319 return status_ != CompileJobStatus::kFailed; 320 } 321 322 bool CompilerDispatcherJob::AnalyzeOnMainThread() { 323 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 324 DCHECK(status() == CompileJobStatus::kReadyToAnalyze); 325 COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kAnalyze); 326 if (trace_compiler_dispatcher_jobs_) { 327 PrintF("CompilerDispatcherJob[%p]: Analyzing\n", static_cast<void*>(this)); 328 } 329 330 compile_info_.reset(new CompilationInfo( 331 parse_info_->zone(), parse_info_.get(), Handle<JSFunction>::null())); 332 333 DeferredHandleScope scope(isolate_); 334 { 335 if (Compiler::Analyze(parse_info_.get())) { 336 status_ = CompileJobStatus::kAnalyzed; 337 } else { 338 status_ = CompileJobStatus::kFailed; 339 if (!isolate_->has_pending_exception()) isolate_->StackOverflow(); 340 } 341 } 342 compile_info_->set_deferred_handles(scope.Detach()); 343 344 return status_ != CompileJobStatus::kFailed; 345 } 346 347 bool CompilerDispatcherJob::PrepareToCompileOnMainThread() { 348 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 349 DCHECK(status() == CompileJobStatus::kAnalyzed); 350 COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToCompile); 351 352 compile_job_.reset( 353 Compiler::PrepareUnoptimizedCompilationJob(compile_info_.get())); 354 if (!compile_job_.get()) { 355 if (!isolate_->has_pending_exception()) isolate_->StackOverflow(); 356 status_ = CompileJobStatus::kFailed; 357 return false; 358 } 359 360 CHECK(compile_job_->can_execute_on_background_thread()); 361 status_ = CompileJobStatus::kReadyToCompile; 362 return true; 363 } 364 365 void CompilerDispatcherJob::Compile() { 366 DCHECK(status() == CompileJobStatus::kReadyToCompile); 367 COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM( 368 tracer_, kCompile, parse_info_->literal()->ast_node_count()); 369 if (trace_compiler_dispatcher_jobs_) { 370 PrintF("CompilerDispatcherJob[%p]: Compiling\n", static_cast<void*>(this)); 371 } 372 373 // Disallowing of handle dereference and heap access dealt with in 374 // CompilationJob::ExecuteJob. 375 376 uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB; 377 compile_job_->set_stack_limit(stack_limit); 378 379 CompilationJob::Status status = compile_job_->ExecuteJob(); 380 USE(status); 381 382 // Always transition to kCompiled - errors will be reported by 383 // FinalizeCompilingOnMainThread. 384 status_ = CompileJobStatus::kCompiled; 385 } 386 387 bool CompilerDispatcherJob::FinalizeCompilingOnMainThread() { 388 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 389 DCHECK(status() == CompileJobStatus::kCompiled); 390 COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeCompiling); 391 if (trace_compiler_dispatcher_jobs_) { 392 PrintF("CompilerDispatcherJob[%p]: Finalizing compiling\n", 393 static_cast<void*>(this)); 394 } 395 396 { 397 HandleScope scope(isolate_); 398 if (compile_job_->state() == CompilationJob::State::kFailed || 399 !Compiler::FinalizeCompilationJob(compile_job_.release())) { 400 if (!isolate_->has_pending_exception()) isolate_->StackOverflow(); 401 status_ = CompileJobStatus::kFailed; 402 return false; 403 } 404 } 405 406 compile_job_.reset(); 407 compile_info_.reset(); 408 parse_zone_.reset(); 409 parse_info_.reset(); 410 411 status_ = CompileJobStatus::kDone; 412 return true; 413 } 414 415 void CompilerDispatcherJob::ResetOnMainThread() { 416 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 417 418 if (trace_compiler_dispatcher_jobs_) { 419 PrintF("CompilerDispatcherJob[%p]: Resetting\n", static_cast<void*>(this)); 420 } 421 422 compile_job_.reset(); 423 compile_info_.reset(); 424 parse_zone_.reset(); 425 parser_.reset(); 426 unicode_cache_.reset(); 427 character_stream_.reset(); 428 parse_info_.reset(); 429 430 if (!source_.is_null()) { 431 i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location()); 432 source_ = Handle<String>::null(); 433 } 434 if (!wrapper_.is_null()) { 435 i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location()); 436 wrapper_ = Handle<String>::null(); 437 } 438 439 status_ = CompileJobStatus::kInitial; 440 } 441 442 double CompilerDispatcherJob::EstimateRuntimeOfNextStepInMs() const { 443 switch (status_) { 444 case CompileJobStatus::kInitial: 445 return tracer_->EstimatePrepareToParseInMs(); 446 447 case CompileJobStatus::kReadyToParse: 448 return tracer_->EstimateParseInMs(parse_info_->end_position() - 449 parse_info_->start_position()); 450 451 case CompileJobStatus::kParsed: 452 return tracer_->EstimateFinalizeParsingInMs(); 453 454 case CompileJobStatus::kReadyToAnalyze: 455 return tracer_->EstimateAnalyzeInMs(); 456 457 case CompileJobStatus::kAnalyzed: 458 return tracer_->EstimatePrepareToCompileInMs(); 459 460 case CompileJobStatus::kReadyToCompile: 461 return tracer_->EstimateCompileInMs( 462 parse_info_->literal()->ast_node_count()); 463 464 case CompileJobStatus::kCompiled: 465 return tracer_->EstimateFinalizeCompilingInMs(); 466 467 case CompileJobStatus::kFailed: 468 case CompileJobStatus::kDone: 469 return 0.0; 470 } 471 472 UNREACHABLE(); 473 return 0.0; 474 } 475 476 void CompilerDispatcherJob::ShortPrint() { 477 DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); 478 shared_->ShortPrint(); 479 } 480 481 } // namespace internal 482 } // namespace v8 483