• 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/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:
OneByteWrapper(const void * data,int length)30   OneByteWrapper(const void* data, int length) : data_(data), length_(length) {}
31   ~OneByteWrapper() override = default;
32 
data() const33   const char* data() const override {
34     return reinterpret_cast<const char*>(data_);
35   }
36 
length() const37   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:
TwoByteWrapper(const void * data,int length)48   TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {}
49   ~TwoByteWrapper() override = default;
50 
data() const51   const uint16_t* data() const override {
52     return reinterpret_cast<const uint16_t*>(data_);
53   }
54 
length() const55   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 
UnoptimizedCompileJob(Isolate * isolate,CompilerDispatcherTracer * tracer,Handle<SharedFunctionInfo> shared,size_t max_stack_size)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 
~UnoptimizedCompileJob()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 
IsAssociatedWith(Handle<SharedFunctionInfo> shared) const103 bool UnoptimizedCompileJob::IsAssociatedWith(
104     Handle<SharedFunctionInfo> shared) const {
105   return *shared_ == *shared;
106 }
107 
PrepareOnMainThread(Isolate * isolate)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 
Compile(bool on_background_thread)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 
FinalizeOnMainThread(Isolate * isolate)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 
ReportErrorsOnMainThread(Isolate * isolate)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 
ResetDataOnMainThread(Isolate * isolate)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 
ResetOnMainThread(Isolate * isolate)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 
EstimateRuntimeOfNextStepInMs() const353 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 
ShortPrintOnMainThread()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