• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
OneByteWrapper(const void * data,int length)28   OneByteWrapper(const void* data, int length) : data_(data), length_(length) {}
29   ~OneByteWrapper() override = default;
30 
data() const31   const char* data() const override {
32     return reinterpret_cast<const char*>(data_);
33   }
34 
length() const35   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:
TwoByteWrapper(const void * data,int length)46   TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {}
47   ~TwoByteWrapper() override = default;
48 
data() const49   const uint16_t* data() const override {
50     return reinterpret_cast<const uint16_t*>(data_);
51   }
52 
length() const53   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 
CompilerDispatcherJob(Isolate * isolate,CompilerDispatcherTracer * tracer,Handle<SharedFunctionInfo> shared,size_t max_stack_size)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 
CompilerDispatcherJob(Isolate * isolate,CompilerDispatcherTracer * tracer,Handle<Script> script,Handle<SharedFunctionInfo> shared,FunctionLiteral * literal,std::shared_ptr<Zone> parse_zone,std::shared_ptr<DeferredHandles> parse_handles,std::shared_ptr<DeferredHandles> compile_handles,size_t max_stack_size)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 
~CompilerDispatcherJob()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 
IsAssociatedWith(Handle<SharedFunctionInfo> shared) const127 bool CompilerDispatcherJob::IsAssociatedWith(
128     Handle<SharedFunctionInfo> shared) const {
129   return *shared_ == *shared;
130 }
131 
PrepareToParseOnMainThread()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 
Parse()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 
FinalizeParsingOnMainThread()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 
AnalyzeOnMainThread()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 
PrepareToCompileOnMainThread()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 
Compile()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 
FinalizeCompilingOnMainThread()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 
ResetOnMainThread()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 
EstimateRuntimeOfNextStepInMs() const442 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 
ShortPrint()476 void CompilerDispatcherJob::ShortPrint() {
477   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
478   shared_->ShortPrint();
479 }
480 
481 }  // namespace internal
482 }  // namespace v8
483