// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler-dispatcher/compiler-dispatcher-job.h" #include "src/assert-scope.h" #include "src/compilation-info.h" #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" #include "src/compiler.h" #include "src/global-handles.h" #include "src/isolate.h" #include "src/objects-inl.h" #include "src/parsing/parse-info.h" #include "src/parsing/parser.h" #include "src/parsing/scanner-character-streams.h" #include "src/unicode-cache.h" #include "src/zone/zone.h" namespace v8 { namespace internal { CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate, Handle<SharedFunctionInfo> shared, size_t max_stack_size) : isolate_(isolate), tracer_(isolate_->compiler_dispatcher_tracer()), shared_(Handle<SharedFunctionInfo>::cast( isolate_->global_handles()->Create(*shared))), max_stack_size_(max_stack_size), can_compile_on_background_thread_(false) { HandleScope scope(isolate_); DCHECK(!shared_->outer_scope_info()->IsTheHole(isolate_)); Handle<Script> script(Script::cast(shared_->script()), isolate_); Handle<String> source(String::cast(script->source()), isolate_); can_parse_on_background_thread_ = source->IsExternalTwoByteString() || source->IsExternalOneByteString(); } CompilerDispatcherJob::~CompilerDispatcherJob() { DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); DCHECK(status_ == CompileJobStatus::kInitial || status_ == CompileJobStatus::kDone); i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location()); } void CompilerDispatcherJob::PrepareToParseOnMainThread() { DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); DCHECK(status() == CompileJobStatus::kInitial); COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToParse); HandleScope scope(isolate_); unicode_cache_.reset(new UnicodeCache()); zone_.reset(new Zone(isolate_->allocator(), ZONE_NAME)); Handle<Script> script(Script::cast(shared_->script()), isolate_); DCHECK(script->type() != Script::TYPE_NATIVE); Handle<String> source(String::cast(script->source()), isolate_); if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) { character_stream_.reset(ScannerStream::For( source, shared_->start_position(), shared_->end_position())); } else { source = String::Flatten(source); // Have to globalize the reference here, so it survives between function // calls. source_ = Handle<String>::cast(isolate_->global_handles()->Create(*source)); character_stream_.reset(ScannerStream::For( source_, shared_->start_position(), shared_->end_position())); } parse_info_.reset(new ParseInfo(zone_.get())); parse_info_->set_isolate(isolate_); parse_info_->set_character_stream(character_stream_.get()); parse_info_->set_hash_seed(isolate_->heap()->HashSeed()); parse_info_->set_is_named_expression(shared_->is_named_expression()); parse_info_->set_compiler_hints(shared_->compiler_hints()); parse_info_->set_start_position(shared_->start_position()); parse_info_->set_end_position(shared_->end_position()); parse_info_->set_unicode_cache(unicode_cache_.get()); parse_info_->set_language_mode(shared_->language_mode()); parser_.reset(new Parser(parse_info_.get())); Handle<ScopeInfo> outer_scope_info( handle(ScopeInfo::cast(shared_->outer_scope_info()))); parser_->DeserializeScopeChain(parse_info_.get(), outer_scope_info->length() > 0 ? MaybeHandle<ScopeInfo>(outer_scope_info) : MaybeHandle<ScopeInfo>()); Handle<String> name(String::cast(shared_->name())); parse_info_->set_function_name( parse_info_->ast_value_factory()->GetString(name)); status_ = CompileJobStatus::kReadyToParse; } void CompilerDispatcherJob::Parse() { DCHECK(can_parse_on_background_thread_ || ThreadId::Current().Equals(isolate_->thread_id())); DCHECK(status() == CompileJobStatus::kReadyToParse); COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM( tracer_, kParse, parse_info_->end_position() - parse_info_->start_position()); DisallowHeapAllocation no_allocation; DisallowHandleAllocation no_handles; std::unique_ptr<DisallowHandleDereference> no_deref; // If we can't parse on a background thread, we need to be able to deref the // source string. if (can_parse_on_background_thread_) { no_deref.reset(new DisallowHandleDereference()); } // Nullify the Isolate temporarily so that the parser doesn't accidentally // use it. parse_info_->set_isolate(nullptr); uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB; parser_->set_stack_limit(stack_limit); parser_->ParseOnBackground(parse_info_.get()); parse_info_->set_isolate(isolate_); status_ = CompileJobStatus::kParsed; } bool CompilerDispatcherJob::FinalizeParsingOnMainThread() { DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); DCHECK(status() == CompileJobStatus::kParsed); COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeParsing); if (!source_.is_null()) { i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location()); source_ = Handle<String>::null(); } if (parse_info_->literal() == nullptr) { status_ = CompileJobStatus::kFailed; } else { status_ = CompileJobStatus::kReadyToAnalyse; } DeferredHandleScope scope(isolate_); { Handle<Script> script(Script::cast(shared_->script()), isolate_); parse_info_->set_script(script); Handle<ScopeInfo> outer_scope_info( handle(ScopeInfo::cast(shared_->outer_scope_info()))); if (outer_scope_info->length() > 0) { parse_info_->set_outer_scope_info(outer_scope_info); } parse_info_->set_shared_info(shared_); // Do the parsing tasks which need to be done on the main thread. This // will also handle parse errors. parser_->Internalize(isolate_, script, parse_info_->literal() == nullptr); parser_->HandleSourceURLComments(isolate_, script); parse_info_->set_character_stream(nullptr); parse_info_->set_unicode_cache(nullptr); parser_.reset(); unicode_cache_.reset(); character_stream_.reset(); } handles_from_parsing_.reset(scope.Detach()); return status_ != CompileJobStatus::kFailed; } bool CompilerDispatcherJob::PrepareToCompileOnMainThread() { DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); DCHECK(status() == CompileJobStatus::kReadyToAnalyse); COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToCompile); compile_info_.reset( new CompilationInfo(parse_info_.get(), Handle<JSFunction>::null())); DeferredHandleScope scope(isolate_); if (Compiler::Analyze(parse_info_.get())) { compile_job_.reset( Compiler::PrepareUnoptimizedCompilationJob(compile_info_.get())); } compile_info_->set_deferred_handles(scope.Detach()); if (!compile_job_.get()) { if (!isolate_->has_pending_exception()) isolate_->StackOverflow(); status_ = CompileJobStatus::kFailed; return false; } can_compile_on_background_thread_ = compile_job_->can_execute_on_background_thread(); status_ = CompileJobStatus::kReadyToCompile; return true; } void CompilerDispatcherJob::Compile() { DCHECK(status() == CompileJobStatus::kReadyToCompile); DCHECK(can_compile_on_background_thread_ || ThreadId::Current().Equals(isolate_->thread_id())); COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM( tracer_, kCompile, parse_info_->literal()->ast_node_count()); // Disallowing of handle dereference and heap access dealt with in // CompilationJob::ExecuteJob. uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB; compile_job_->set_stack_limit(stack_limit); CompilationJob::Status status = compile_job_->ExecuteJob(); USE(status); // Always transition to kCompiled - errors will be reported by // FinalizeCompilingOnMainThread. status_ = CompileJobStatus::kCompiled; } bool CompilerDispatcherJob::FinalizeCompilingOnMainThread() { DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); DCHECK(status() == CompileJobStatus::kCompiled); COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeCompiling); if (compile_job_->state() == CompilationJob::State::kFailed || !Compiler::FinalizeCompilationJob(compile_job_.release())) { if (!isolate_->has_pending_exception()) isolate_->StackOverflow(); status_ = CompileJobStatus::kFailed; return false; } zone_.reset(); parse_info_.reset(); compile_info_.reset(); compile_job_.reset(); handles_from_parsing_.reset(); status_ = CompileJobStatus::kDone; return true; } void CompilerDispatcherJob::ResetOnMainThread() { DCHECK(ThreadId::Current().Equals(isolate_->thread_id())); parser_.reset(); unicode_cache_.reset(); character_stream_.reset(); parse_info_.reset(); zone_.reset(); handles_from_parsing_.reset(); compile_info_.reset(); compile_job_.reset(); if (!source_.is_null()) { i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location()); source_ = Handle<String>::null(); } status_ = CompileJobStatus::kInitial; } } // namespace internal } // namespace v8