普通文本  |  380行  |  13.27 KB

// 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/unoptimized-compile-job.h"

#include "src/assert-scope.h"
#include "src/base/optional.h"
#include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
#include "src/compiler.h"
#include "src/flags.h"
#include "src/global-handles.h"
#include "src/interpreter/interpreter.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/unoptimized-compilation-info.h"
#include "src/utils.h"

namespace v8 {
namespace internal {

namespace {

class OneByteWrapper : public v8::String::ExternalOneByteStringResource {
 public:
  OneByteWrapper(const void* data, int length) : data_(data), length_(length) {}
  ~OneByteWrapper() override = default;

  const char* data() const override {
    return reinterpret_cast<const char*>(data_);
  }

  size_t length() const override { return static_cast<size_t>(length_); }

 private:
  const void* data_;
  int length_;

  DISALLOW_COPY_AND_ASSIGN(OneByteWrapper);
};

class TwoByteWrapper : public v8::String::ExternalStringResource {
 public:
  TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {}
  ~TwoByteWrapper() override = default;

  const uint16_t* data() const override {
    return reinterpret_cast<const uint16_t*>(data_);
  }

  size_t length() const override { return static_cast<size_t>(length_); }

 private:
  const void* data_;
  int length_;

  DISALLOW_COPY_AND_ASSIGN(TwoByteWrapper);
};

}  // namespace

UnoptimizedCompileJob::UnoptimizedCompileJob(Isolate* isolate,
                                             CompilerDispatcherTracer* tracer,
                                             Handle<SharedFunctionInfo> shared,
                                             size_t max_stack_size)
    : CompilerDispatcherJob(Type::kUnoptimizedCompile),
      main_thread_id_(isolate->thread_id().ToInteger()),
      tracer_(tracer),
      allocator_(isolate->allocator()),
      context_(isolate->global_handles()->Create(isolate->context())),
      shared_(isolate->global_handles()->Create(*shared)),
      max_stack_size_(max_stack_size),
      trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
  DCHECK(!shared_->is_toplevel());
  // TODO(rmcilroy): Handle functions with non-empty outer scope info.
  DCHECK(!shared_->HasOuterScopeInfo());
  HandleScope scope(isolate);
  Handle<Script> script(Script::cast(shared_->script()), isolate);
  Handle<String> source(String::cast(script->source()), isolate);
  if (trace_compiler_dispatcher_jobs_) {
    PrintF("UnoptimizedCompileJob[%p] created for ", static_cast<void*>(this));
    ShortPrintOnMainThread();
    PrintF(" in initial state.\n");
  }
}

UnoptimizedCompileJob::~UnoptimizedCompileJob() {
  DCHECK(status() == Status::kInitial || status() == Status::kDone);
  if (!shared_.is_null()) {
    DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
    i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location());
  }
  if (!context_.is_null()) {
    DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
    i::GlobalHandles::Destroy(Handle<Object>::cast(context_).location());
  }
}

bool UnoptimizedCompileJob::IsAssociatedWith(
    Handle<SharedFunctionInfo> shared) const {
  return *shared_ == *shared;
}

void UnoptimizedCompileJob::PrepareOnMainThread(Isolate* isolate) {
  DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
  DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
  DCHECK_EQ(status(), Status::kInitial);
  COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepare);

  if (trace_compiler_dispatcher_jobs_) {
    PrintF("UnoptimizedCompileJob[%p]: Preparing to parse\n",
           static_cast<void*>(this));
  }

  ParseInfo* parse_info = new ParseInfo(isolate, shared_);
  parse_info_.reset(parse_info);

  unicode_cache_.reset(new UnicodeCache());
  parse_info_->set_unicode_cache(unicode_cache_.get());
  parse_info_->set_function_literal_id(shared_->FunctionLiteralId(isolate));
  if (V8_UNLIKELY(FLAG_runtime_stats)) {
    parse_info_->set_runtime_call_stats(new (parse_info_->zone())
                                            RuntimeCallStats());
  }

  Handle<Script> script = parse_info->script();
  HandleScope scope(isolate);

  DCHECK(script->type() != Script::TYPE_NATIVE);
  Handle<String> source(String::cast(script->source()), isolate);
  if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) {
    std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
        isolate, source, shared_->StartPosition(), shared_->EndPosition()));
    parse_info_->set_character_stream(std::move(stream));
  } else {
    source = String::Flatten(isolate, source);
    const void* data;
    int offset = 0;
    int length = source->length();

    // Objects in lo_space don't move, so we can just read the contents from
    // any thread.
    if (isolate->heap()->lo_space()->Contains(*source)) {
      // We need to globalize the handle to the flattened string here, in
      // case it's not referenced from anywhere else.
      source_ = isolate->global_handles()->Create(*source);
      DisallowHeapAllocation no_allocation;
      String::FlatContent content = source->GetFlatContent();
      DCHECK(content.IsFlat());
      data =
          content.IsOneByte()
              ? reinterpret_cast<const void*>(content.ToOneByteVector().start())
              : reinterpret_cast<const void*>(content.ToUC16Vector().start());
    } else {
      // Otherwise, create a copy of the part of the string we'll parse in the
      // zone.
      length = (shared_->EndPosition() - shared_->StartPosition());
      offset = shared_->StartPosition();

      int byte_len = length * (source->IsOneByteRepresentation() ? 1 : 2);
      data = parse_info_->zone()->New(byte_len);

      DisallowHeapAllocation no_allocation;
      String::FlatContent content = source->GetFlatContent();
      DCHECK(content.IsFlat());
      if (content.IsOneByte()) {
        MemCopy(const_cast<void*>(data),
                &content.ToOneByteVector().at(shared_->StartPosition()),
                byte_len);
      } else {
        MemCopy(const_cast<void*>(data),
                &content.ToUC16Vector().at(shared_->StartPosition()), byte_len);
      }
    }
    Handle<String> wrapper;
    if (source->IsOneByteRepresentation()) {
      ExternalOneByteString::Resource* resource =
          new OneByteWrapper(data, length);
      wrapper = isolate->factory()
                    ->NewExternalStringFromOneByte(resource)
                    .ToHandleChecked();
    } else {
      ExternalTwoByteString::Resource* resource =
          new TwoByteWrapper(data, length);
      wrapper = isolate->factory()
                    ->NewExternalStringFromTwoByte(resource)
                    .ToHandleChecked();
    }
    wrapper_ = isolate->global_handles()->Create(*wrapper);
    std::unique_ptr<Utf16CharacterStream> stream(
        ScannerStream::For(isolate, wrapper_, shared_->StartPosition() - offset,
                           shared_->EndPosition() - offset));
    parse_info_->set_character_stream(std::move(stream));
  }

  parser_.reset(new Parser(parse_info_.get()));
  parser_->DeserializeScopeChain(isolate, parse_info_.get(),
                                 parse_info_->maybe_outer_scope_info());

  // Initailize the name after setting up the ast_value_factory.
  Handle<String> name(shared_->Name(), isolate);
  parse_info_->set_function_name(
      parse_info_->ast_value_factory()->GetString(name));

  set_status(Status::kPrepared);
}

void UnoptimizedCompileJob::Compile(bool on_background_thread) {
  DCHECK_EQ(status(), Status::kPrepared);
  COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
      tracer_, kCompile,
      parse_info_->end_position() - parse_info_->start_position());
  if (trace_compiler_dispatcher_jobs_) {
    PrintF("UnoptimizedCompileJob[%p]: Compiling\n", static_cast<void*>(this));
  }

  DisallowHeapAllocation no_allocation;
  DisallowHandleAllocation no_handles;
  DisallowHandleDereference no_deref;

  parse_info_->set_on_background_thread(on_background_thread);
  uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
  parser_->set_stack_limit(stack_limit);
  parse_info_->set_stack_limit(stack_limit);
  parser_->ParseOnBackground(parse_info_.get());

  if (parse_info_->literal() == nullptr) {
    // Parser sets error in pending error handler.
    set_status(Status::kHasErrorsToReport);
    return;
  }

  if (!Compiler::Analyze(parse_info_.get())) {
    parse_info_->pending_error_handler()->set_stack_overflow();
    set_status(Status::kHasErrorsToReport);
    return;
  }

  compilation_job_.reset(interpreter::Interpreter::NewCompilationJob(
      parse_info_.get(), parse_info_->literal(), allocator_, nullptr));

  if (!compilation_job_.get()) {
    parse_info_->pending_error_handler()->set_stack_overflow();
    set_status(Status::kHasErrorsToReport);
    return;
  }

  if (compilation_job_->ExecuteJob() != CompilationJob::SUCCEEDED) {
    parse_info_->pending_error_handler()->set_stack_overflow();
    set_status(Status::kHasErrorsToReport);
    return;
  }

  set_status(Status::kCompiled);
}

void UnoptimizedCompileJob::FinalizeOnMainThread(Isolate* isolate) {
  DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
  DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
  DCHECK_EQ(status(), Status::kCompiled);
  DCHECK_NOT_NULL(parse_info_->literal());
  DCHECK_NOT_NULL(compilation_job_.get());
  COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalize);
  if (trace_compiler_dispatcher_jobs_) {
    PrintF("UnoptimizedCompileJob[%p]: Finalizing compiling\n",
           static_cast<void*>(this));
  }

  Handle<Script> script(Script::cast(shared_->script()), isolate);
  DCHECK_EQ(*parse_info_->script(), shared_->script());

  parser_->UpdateStatistics(isolate, script);
  parse_info_->UpdateBackgroundParseStatisticsOnMainThread(isolate);
  parser_->HandleSourceURLComments(isolate, script);

  {
    HandleScope scope(isolate);
    // Internalize ast values onto the heap.
    parse_info_->ast_value_factory()->Internalize(isolate);
    // Allocate scope infos for the literal.
    DeclarationScope::AllocateScopeInfos(parse_info_.get(), isolate);
    if (compilation_job_->state() == CompilationJob::State::kFailed ||
        !Compiler::FinalizeCompilationJob(compilation_job_.release(), shared_,
                                          isolate)) {
      if (!isolate->has_pending_exception()) isolate->StackOverflow();
      set_status(Status::kFailed);
      return;
    }
  }

  ResetDataOnMainThread(isolate);
  set_status(Status::kDone);
}

void UnoptimizedCompileJob::ReportErrorsOnMainThread(Isolate* isolate) {
  DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
  DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
  DCHECK_EQ(status(), Status::kHasErrorsToReport);

  if (trace_compiler_dispatcher_jobs_) {
    PrintF("UnoptimizedCompileJob[%p]: Reporting Errors\n",
           static_cast<void*>(this));
  }

  // Ensure we report errors in the correct context for the job.
  SaveContext save(isolate);
  isolate->set_context(context());

  Handle<Script> script(Script::cast(shared_->script()), isolate);
  parse_info_->pending_error_handler()->ReportErrors(
      isolate, script, parse_info_->ast_value_factory());

  ResetDataOnMainThread(isolate);
  set_status(Status::kFailed);
}

void UnoptimizedCompileJob::ResetDataOnMainThread(Isolate* isolate) {
  DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
  DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);

  compilation_job_.reset();
  parser_.reset();
  unicode_cache_.reset();
  parse_info_.reset();

  if (!source_.is_null()) {
    DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
    DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
    i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
    source_ = Handle<String>::null();
  }
  if (!wrapper_.is_null()) {
    DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
    DCHECK_EQ(isolate->thread_id().ToInteger(), main_thread_id_);
    i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location());
    wrapper_ = Handle<String>::null();
  }
}

void UnoptimizedCompileJob::ResetOnMainThread(Isolate* isolate) {
  if (trace_compiler_dispatcher_jobs_) {
    PrintF("UnoptimizedCompileJob[%p]: Resetting\n", static_cast<void*>(this));
  }

  ResetDataOnMainThread(isolate);
  set_status(Status::kInitial);
}

double UnoptimizedCompileJob::EstimateRuntimeOfNextStepInMs() const {
  switch (status()) {
    case Status::kInitial:
      return tracer_->EstimatePrepareInMs();
    case Status::kPrepared:
      return tracer_->EstimateCompileInMs(parse_info_->end_position() -
                                          parse_info_->start_position());
    case Status::kCompiled:
      return tracer_->EstimateFinalizeInMs();

    case Status::kHasErrorsToReport:
    case Status::kFailed:
    case Status::kDone:
      return 0.0;
  }

  UNREACHABLE();
}

void UnoptimizedCompileJob::ShortPrintOnMainThread() {
  DCHECK_EQ(ThreadId::Current().ToInteger(), main_thread_id_);
  DCHECK(!shared_.is_null());
  shared_->ShortPrint();
}

}  // namespace internal
}  // namespace v8