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/global-handles.h"
12 #include "src/isolate.h"
13 #include "src/objects-inl.h"
14 #include "src/parsing/parse-info.h"
15 #include "src/parsing/parser.h"
16 #include "src/parsing/scanner-character-streams.h"
17 #include "src/unicode-cache.h"
18 #include "src/zone/zone.h"
19 
20 namespace v8 {
21 namespace internal {
22 
CompilerDispatcherJob(Isolate * isolate,Handle<SharedFunctionInfo> shared,size_t max_stack_size)23 CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate,
24                                              Handle<SharedFunctionInfo> shared,
25                                              size_t max_stack_size)
26     : isolate_(isolate),
27       tracer_(isolate_->compiler_dispatcher_tracer()),
28       shared_(Handle<SharedFunctionInfo>::cast(
29           isolate_->global_handles()->Create(*shared))),
30       max_stack_size_(max_stack_size),
31       can_compile_on_background_thread_(false) {
32   HandleScope scope(isolate_);
33   DCHECK(!shared_->outer_scope_info()->IsTheHole(isolate_));
34   Handle<Script> script(Script::cast(shared_->script()), isolate_);
35   Handle<String> source(String::cast(script->source()), isolate_);
36   can_parse_on_background_thread_ =
37       source->IsExternalTwoByteString() || source->IsExternalOneByteString();
38 }
39 
~CompilerDispatcherJob()40 CompilerDispatcherJob::~CompilerDispatcherJob() {
41   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
42   DCHECK(status_ == CompileJobStatus::kInitial ||
43          status_ == CompileJobStatus::kDone);
44   i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location());
45 }
46 
PrepareToParseOnMainThread()47 void CompilerDispatcherJob::PrepareToParseOnMainThread() {
48   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
49   DCHECK(status() == CompileJobStatus::kInitial);
50   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToParse);
51   HandleScope scope(isolate_);
52   unicode_cache_.reset(new UnicodeCache());
53   zone_.reset(new Zone(isolate_->allocator(), ZONE_NAME));
54   Handle<Script> script(Script::cast(shared_->script()), isolate_);
55   DCHECK(script->type() != Script::TYPE_NATIVE);
56 
57   Handle<String> source(String::cast(script->source()), isolate_);
58   if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) {
59     character_stream_.reset(ScannerStream::For(
60         source, shared_->start_position(), shared_->end_position()));
61   } else {
62     source = String::Flatten(source);
63     // Have to globalize the reference here, so it survives between function
64     // calls.
65     source_ = Handle<String>::cast(isolate_->global_handles()->Create(*source));
66     character_stream_.reset(ScannerStream::For(
67         source_, shared_->start_position(), shared_->end_position()));
68   }
69   parse_info_.reset(new ParseInfo(zone_.get()));
70   parse_info_->set_isolate(isolate_);
71   parse_info_->set_character_stream(character_stream_.get());
72   parse_info_->set_hash_seed(isolate_->heap()->HashSeed());
73   parse_info_->set_is_named_expression(shared_->is_named_expression());
74   parse_info_->set_compiler_hints(shared_->compiler_hints());
75   parse_info_->set_start_position(shared_->start_position());
76   parse_info_->set_end_position(shared_->end_position());
77   parse_info_->set_unicode_cache(unicode_cache_.get());
78   parse_info_->set_language_mode(shared_->language_mode());
79 
80   parser_.reset(new Parser(parse_info_.get()));
81   Handle<ScopeInfo> outer_scope_info(
82       handle(ScopeInfo::cast(shared_->outer_scope_info())));
83   parser_->DeserializeScopeChain(parse_info_.get(),
84                                  outer_scope_info->length() > 0
85                                      ? MaybeHandle<ScopeInfo>(outer_scope_info)
86                                      : MaybeHandle<ScopeInfo>());
87 
88   Handle<String> name(String::cast(shared_->name()));
89   parse_info_->set_function_name(
90       parse_info_->ast_value_factory()->GetString(name));
91   status_ = CompileJobStatus::kReadyToParse;
92 }
93 
Parse()94 void CompilerDispatcherJob::Parse() {
95   DCHECK(can_parse_on_background_thread_ ||
96          ThreadId::Current().Equals(isolate_->thread_id()));
97   DCHECK(status() == CompileJobStatus::kReadyToParse);
98   COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
99       tracer_, kParse,
100       parse_info_->end_position() - parse_info_->start_position());
101 
102   DisallowHeapAllocation no_allocation;
103   DisallowHandleAllocation no_handles;
104   std::unique_ptr<DisallowHandleDereference> no_deref;
105   // If we can't parse on a background thread, we need to be able to deref the
106   // source string.
107   if (can_parse_on_background_thread_) {
108     no_deref.reset(new DisallowHandleDereference());
109   }
110 
111   // Nullify the Isolate temporarily so that the parser doesn't accidentally
112   // use it.
113   parse_info_->set_isolate(nullptr);
114 
115   uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
116 
117   parser_->set_stack_limit(stack_limit);
118   parser_->ParseOnBackground(parse_info_.get());
119 
120   parse_info_->set_isolate(isolate_);
121 
122   status_ = CompileJobStatus::kParsed;
123 }
124 
FinalizeParsingOnMainThread()125 bool CompilerDispatcherJob::FinalizeParsingOnMainThread() {
126   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
127   DCHECK(status() == CompileJobStatus::kParsed);
128   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeParsing);
129 
130   if (!source_.is_null()) {
131     i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
132     source_ = Handle<String>::null();
133   }
134 
135   if (parse_info_->literal() == nullptr) {
136     status_ = CompileJobStatus::kFailed;
137   } else {
138     status_ = CompileJobStatus::kReadyToAnalyse;
139   }
140 
141   DeferredHandleScope scope(isolate_);
142   {
143     Handle<Script> script(Script::cast(shared_->script()), isolate_);
144 
145     parse_info_->set_script(script);
146     Handle<ScopeInfo> outer_scope_info(
147         handle(ScopeInfo::cast(shared_->outer_scope_info())));
148     if (outer_scope_info->length() > 0) {
149       parse_info_->set_outer_scope_info(outer_scope_info);
150     }
151     parse_info_->set_shared_info(shared_);
152 
153     // Do the parsing tasks which need to be done on the main thread. This
154     // will also handle parse errors.
155     parser_->Internalize(isolate_, script, parse_info_->literal() == nullptr);
156     parser_->HandleSourceURLComments(isolate_, script);
157 
158     parse_info_->set_character_stream(nullptr);
159     parse_info_->set_unicode_cache(nullptr);
160     parser_.reset();
161     unicode_cache_.reset();
162     character_stream_.reset();
163   }
164   handles_from_parsing_.reset(scope.Detach());
165 
166   return status_ != CompileJobStatus::kFailed;
167 }
168 
PrepareToCompileOnMainThread()169 bool CompilerDispatcherJob::PrepareToCompileOnMainThread() {
170   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
171   DCHECK(status() == CompileJobStatus::kReadyToAnalyse);
172   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToCompile);
173 
174   compile_info_.reset(
175       new CompilationInfo(parse_info_.get(), Handle<JSFunction>::null()));
176 
177   DeferredHandleScope scope(isolate_);
178   if (Compiler::Analyze(parse_info_.get())) {
179     compile_job_.reset(
180         Compiler::PrepareUnoptimizedCompilationJob(compile_info_.get()));
181   }
182   compile_info_->set_deferred_handles(scope.Detach());
183 
184   if (!compile_job_.get()) {
185     if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
186     status_ = CompileJobStatus::kFailed;
187     return false;
188   }
189 
190   can_compile_on_background_thread_ =
191       compile_job_->can_execute_on_background_thread();
192   status_ = CompileJobStatus::kReadyToCompile;
193   return true;
194 }
195 
Compile()196 void CompilerDispatcherJob::Compile() {
197   DCHECK(status() == CompileJobStatus::kReadyToCompile);
198   DCHECK(can_compile_on_background_thread_ ||
199          ThreadId::Current().Equals(isolate_->thread_id()));
200   COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
201       tracer_, kCompile, parse_info_->literal()->ast_node_count());
202 
203   // Disallowing of handle dereference and heap access dealt with in
204   // CompilationJob::ExecuteJob.
205 
206   uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
207   compile_job_->set_stack_limit(stack_limit);
208 
209   CompilationJob::Status status = compile_job_->ExecuteJob();
210   USE(status);
211 
212   // Always transition to kCompiled - errors will be reported by
213   // FinalizeCompilingOnMainThread.
214   status_ = CompileJobStatus::kCompiled;
215 }
216 
FinalizeCompilingOnMainThread()217 bool CompilerDispatcherJob::FinalizeCompilingOnMainThread() {
218   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
219   DCHECK(status() == CompileJobStatus::kCompiled);
220   COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeCompiling);
221 
222   if (compile_job_->state() == CompilationJob::State::kFailed ||
223       !Compiler::FinalizeCompilationJob(compile_job_.release())) {
224     if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
225     status_ = CompileJobStatus::kFailed;
226     return false;
227   }
228 
229   zone_.reset();
230   parse_info_.reset();
231   compile_info_.reset();
232   compile_job_.reset();
233   handles_from_parsing_.reset();
234 
235   status_ = CompileJobStatus::kDone;
236   return true;
237 }
238 
ResetOnMainThread()239 void CompilerDispatcherJob::ResetOnMainThread() {
240   DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
241 
242   parser_.reset();
243   unicode_cache_.reset();
244   character_stream_.reset();
245   parse_info_.reset();
246   zone_.reset();
247   handles_from_parsing_.reset();
248   compile_info_.reset();
249   compile_job_.reset();
250 
251   if (!source_.is_null()) {
252     i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
253     source_ = Handle<String>::null();
254   }
255 
256   status_ = CompileJobStatus::kInitial;
257 }
258 
259 }  // namespace internal
260 }  // namespace v8
261