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