Home | History | Annotate | Download | only in compiler-dispatcher
      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