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