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