blob: 1042ffed6ca0ea6e8b7b9cb16bbba1e135cabd1a [file] [log] [blame]
// 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,
CompilerDispatcherTracer* tracer,
Handle<SharedFunctionInfo> shared,
size_t max_stack_size)
: isolate_(isolate),
tracer_(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());
}
bool CompilerDispatcherJob::IsAssociatedWith(
Handle<SharedFunctionInfo> shared) const {
return *shared_ == *shared;
}
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());
parse_info_->set_function_literal_id(shared_->function_literal_id());
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(), LazyCompilationMode::kAlways));
}
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;
}
double CompilerDispatcherJob::EstimateRuntimeOfNextStepInMs() const {
switch (status_) {
case CompileJobStatus::kInitial:
return tracer_->EstimatePrepareToParseInMs();
case CompileJobStatus::kReadyToParse:
return tracer_->EstimateParseInMs(parse_info_->end_position() -
parse_info_->start_position());
case CompileJobStatus::kParsed:
return tracer_->EstimateFinalizeParsingInMs();
case CompileJobStatus::kReadyToAnalyse:
return tracer_->EstimatePrepareToCompileInMs();
case CompileJobStatus::kReadyToCompile:
return tracer_->EstimateCompileInMs(
parse_info_->literal()->ast_node_count());
case CompileJobStatus::kCompiled:
return tracer_->EstimateFinalizeCompilingInMs();
case CompileJobStatus::kFailed:
case CompileJobStatus::kDone:
return 0.0;
}
UNREACHABLE();
return 0.0;
}
} // namespace internal
} // namespace v8