Line data Source code
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:
28 24 : OneByteWrapper(const void* data, int length) : data_(data), length_(length) {}
29 48 : ~OneByteWrapper() override = default;
30 :
31 48 : const char* data() const override {
32 48 : return reinterpret_cast<const char*>(data_);
33 : }
34 :
35 24 : 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:
46 0 : TwoByteWrapper(const void* data, int length) : data_(data), length_(length) {}
47 0 : ~TwoByteWrapper() override = default;
48 :
49 0 : const uint16_t* data() const override {
50 0 : return reinterpret_cast<const uint16_t*>(data_);
51 : }
52 :
53 0 : 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 :
64 3 : CompilerDispatcherJob::CompilerDispatcherJob(
65 : CompilerDispatcherTracer* tracer, size_t max_stack_size,
66 : Handle<String> source, int start_position, int end_position,
67 : LanguageMode language_mode, int function_literal_id, bool native,
68 : bool module, bool is_named_expression, uint32_t hash_seed,
69 : AccountingAllocator* zone_allocator, int compiler_hints,
70 : const AstStringConstants* ast_string_constants,
71 : CompileJobFinishCallback* finish_callback)
72 : : status_(CompileJobStatus::kReadyToParse),
73 : isolate_(nullptr),
74 : tracer_(tracer),
75 : max_stack_size_(max_stack_size),
76 : finish_callback_(finish_callback),
77 6 : trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
78 3 : parse_info_.reset(new ParseInfo(zone_allocator));
79 : DCHECK(source->IsExternalTwoByteString() ||
80 : source->IsExternalOneByteString());
81 : character_stream_.reset(
82 3 : ScannerStream::For(source, start_position, end_position));
83 : parse_info_->set_character_stream(character_stream_.get());
84 : parse_info_->set_hash_seed(hash_seed);
85 : parse_info_->set_compiler_hints(compiler_hints);
86 : parse_info_->set_start_position(start_position);
87 : parse_info_->set_end_position(end_position);
88 3 : unicode_cache_.reset(new UnicodeCache());
89 : parse_info_->set_unicode_cache(unicode_cache_.get());
90 : parse_info_->set_language_mode(language_mode);
91 : parse_info_->set_function_literal_id(function_literal_id);
92 : parse_info_->set_ast_string_constants(ast_string_constants);
93 :
94 : parse_info_->set_native(native);
95 : parse_info_->set_module(module);
96 : parse_info_->set_is_named_expression(is_named_expression);
97 :
98 3 : parser_.reset(new Parser(parse_info_.get()));
99 3 : parser_->DeserializeScopeChain(parse_info_.get(), MaybeHandle<ScopeInfo>());
100 :
101 3 : if (trace_compiler_dispatcher_jobs_) {
102 0 : PrintF("CompilerDispatcherJob[%p] created for ", static_cast<void*>(this));
103 0 : ShortPrint();
104 0 : PrintF(" in ready to parse state.\n");
105 : }
106 3 : }
107 :
108 68 : CompilerDispatcherJob::CompilerDispatcherJob(Isolate* isolate,
109 : CompilerDispatcherTracer* tracer,
110 : Handle<SharedFunctionInfo> shared,
111 : size_t max_stack_size)
112 : : status_(CompileJobStatus::kInitial),
113 : isolate_(isolate),
114 : tracer_(tracer),
115 68 : context_(isolate_->global_handles()->Create(isolate->context())),
116 34 : shared_(isolate_->global_handles()->Create(*shared)),
117 : max_stack_size_(max_stack_size),
118 136 : trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
119 : DCHECK(!shared_->is_toplevel());
120 34 : HandleScope scope(isolate_);
121 : Handle<Script> script(Script::cast(shared_->script()), isolate_);
122 34 : Handle<String> source(String::cast(script->source()), isolate_);
123 34 : if (trace_compiler_dispatcher_jobs_) {
124 0 : PrintF("CompilerDispatcherJob[%p] created for ", static_cast<void*>(this));
125 0 : ShortPrint();
126 0 : PrintF(" in initial state.\n");
127 : }
128 34 : }
129 :
130 5 : CompilerDispatcherJob::CompilerDispatcherJob(
131 5 : Isolate* isolate, CompilerDispatcherTracer* tracer, Handle<Script> script,
132 : Handle<SharedFunctionInfo> shared, FunctionLiteral* literal,
133 : std::shared_ptr<Zone> parse_zone,
134 : std::shared_ptr<DeferredHandles> parse_handles,
135 : std::shared_ptr<DeferredHandles> compile_handles, size_t max_stack_size)
136 : : status_(CompileJobStatus::kAnalyzed),
137 : isolate_(isolate),
138 : tracer_(tracer),
139 10 : context_(isolate_->global_handles()->Create(isolate->context())),
140 5 : shared_(isolate_->global_handles()->Create(*shared)),
141 : max_stack_size_(max_stack_size),
142 5 : parse_info_(new ParseInfo(shared_)),
143 : parse_zone_(parse_zone),
144 : compile_info_(new CompilationInfo(parse_info_->zone(), parse_info_.get(),
145 5 : isolate_, Handle<JSFunction>::null())),
146 35 : trace_compiler_dispatcher_jobs_(FLAG_trace_compiler_dispatcher_jobs) {
147 : parse_info_->set_literal(literal);
148 : parse_info_->set_script(script);
149 10 : parse_info_->set_deferred_handles(parse_handles);
150 10 : compile_info_->set_deferred_handles(compile_handles);
151 :
152 5 : if (trace_compiler_dispatcher_jobs_) {
153 0 : PrintF("CompilerDispatcherJob[%p] created for ", static_cast<void*>(this));
154 0 : ShortPrint();
155 0 : PrintF(" in Analyzed state.\n");
156 : }
157 5 : }
158 :
159 42 : CompilerDispatcherJob::~CompilerDispatcherJob() {
160 : DCHECK(status_ == CompileJobStatus::kInitial ||
161 : (status_ == CompileJobStatus::kReadyToParse && finish_callback_) ||
162 : status_ == CompileJobStatus::kDone);
163 42 : if (!shared_.is_null()) {
164 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
165 39 : i::GlobalHandles::Destroy(Handle<Object>::cast(shared_).location());
166 : }
167 42 : if (!context_.is_null()) {
168 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
169 39 : i::GlobalHandles::Destroy(Handle<Object>::cast(context_).location());
170 : }
171 42 : }
172 :
173 0 : bool CompilerDispatcherJob::IsAssociatedWith(
174 : Handle<SharedFunctionInfo> shared) const {
175 0 : return *shared_ == *shared;
176 : }
177 :
178 28 : void CompilerDispatcherJob::PrepareToParseOnMainThread() {
179 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
180 : DCHECK(status() == CompileJobStatus::kInitial);
181 112 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToParse);
182 28 : if (trace_compiler_dispatcher_jobs_) {
183 : PrintF("CompilerDispatcherJob[%p]: Preparing to parse\n",
184 0 : static_cast<void*>(this));
185 : }
186 80 : HandleScope scope(isolate_);
187 28 : unicode_cache_.reset(new UnicodeCache());
188 28 : Handle<Script> script(Script::cast(shared_->script()), isolate_);
189 : DCHECK(script->type() != Script::TYPE_NATIVE);
190 :
191 28 : Handle<String> source(String::cast(script->source()), isolate_);
192 56 : parse_info_.reset(new ParseInfo(isolate_->allocator()));
193 56 : if (source->IsExternalTwoByteString() || source->IsExternalOneByteString()) {
194 : character_stream_.reset(ScannerStream::For(
195 4 : source, shared_->start_position(), shared_->end_position()));
196 : } else {
197 24 : source = String::Flatten(source);
198 : const void* data;
199 : int offset = 0;
200 : int length = source->length();
201 :
202 : // Objects in lo_space don't move, so we can just read the contents from
203 : // any thread.
204 24 : if (isolate_->heap()->lo_space()->Contains(*source)) {
205 : // We need to globalize the handle to the flattened string here, in
206 : // case it's not referenced from anywhere else.
207 0 : source_ = isolate_->global_handles()->Create(*source);
208 : DisallowHeapAllocation no_allocation;
209 0 : String::FlatContent content = source->GetFlatContent();
210 : DCHECK(content.IsFlat());
211 : data =
212 0 : content.IsOneByte()
213 : ? reinterpret_cast<const void*>(content.ToOneByteVector().start())
214 0 : : reinterpret_cast<const void*>(content.ToUC16Vector().start());
215 : } else {
216 : // Otherwise, create a copy of the part of the string we'll parse in the
217 : // zone.
218 24 : length = (shared_->end_position() - shared_->start_position());
219 : offset = shared_->start_position();
220 :
221 24 : int byte_len = length * (source->IsOneByteRepresentation() ? 1 : 2);
222 48 : data = parse_info_->zone()->New(byte_len);
223 :
224 : DisallowHeapAllocation no_allocation;
225 24 : String::FlatContent content = source->GetFlatContent();
226 : DCHECK(content.IsFlat());
227 24 : if (content.IsOneByte()) {
228 : MemCopy(const_cast<void*>(data),
229 : &content.ToOneByteVector().at(shared_->start_position()),
230 : byte_len);
231 : } else {
232 : MemCopy(const_cast<void*>(data),
233 : &content.ToUC16Vector().at(shared_->start_position()),
234 : byte_len);
235 : }
236 : }
237 : Handle<String> wrapper;
238 24 : if (source->IsOneByteRepresentation()) {
239 : ExternalOneByteString::Resource* resource =
240 24 : new OneByteWrapper(data, length);
241 : source_wrapper_.reset(resource);
242 : wrapper = isolate_->factory()
243 : ->NewExternalStringFromOneByte(resource)
244 48 : .ToHandleChecked();
245 : } else {
246 : ExternalTwoByteString::Resource* resource =
247 0 : new TwoByteWrapper(data, length);
248 : source_wrapper_.reset(resource);
249 : wrapper = isolate_->factory()
250 : ->NewExternalStringFromTwoByte(resource)
251 0 : .ToHandleChecked();
252 : }
253 48 : wrapper_ = isolate_->global_handles()->Create(*wrapper);
254 :
255 : character_stream_.reset(
256 : ScannerStream::For(wrapper_, shared_->start_position() - offset,
257 48 : shared_->end_position() - offset));
258 : }
259 56 : parse_info_->InitFromIsolate(isolate_);
260 : parse_info_->set_character_stream(character_stream_.get());
261 28 : parse_info_->set_hash_seed(isolate_->heap()->HashSeed());
262 : parse_info_->set_is_named_expression(shared_->is_named_expression());
263 : parse_info_->set_compiler_hints(shared_->compiler_hints());
264 : parse_info_->set_start_position(shared_->start_position());
265 : parse_info_->set_end_position(shared_->end_position());
266 : parse_info_->set_unicode_cache(unicode_cache_.get());
267 : parse_info_->set_language_mode(shared_->language_mode());
268 : parse_info_->set_function_literal_id(shared_->function_literal_id());
269 :
270 28 : parser_.reset(new Parser(parse_info_.get()));
271 : MaybeHandle<ScopeInfo> outer_scope_info;
272 82 : if (!shared_->outer_scope_info()->IsTheHole(isolate_) &&
273 : ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
274 : outer_scope_info = handle(ScopeInfo::cast(shared_->outer_scope_info()));
275 : }
276 56 : parser_->DeserializeScopeChain(parse_info_.get(), outer_scope_info);
277 :
278 : Handle<String> name(String::cast(shared_->name()));
279 : parse_info_->set_function_name(
280 28 : parse_info_->ast_value_factory()->GetString(name));
281 56 : status_ = CompileJobStatus::kReadyToParse;
282 28 : }
283 :
284 28 : void CompilerDispatcherJob::Parse() {
285 : DCHECK(status() == CompileJobStatus::kReadyToParse);
286 112 : COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
287 : tracer_, kParse,
288 : parse_info_->end_position() - parse_info_->start_position());
289 28 : if (trace_compiler_dispatcher_jobs_) {
290 0 : PrintF("CompilerDispatcherJob[%p]: Parsing\n", static_cast<void*>(this));
291 : }
292 :
293 : DisallowHeapAllocation no_allocation;
294 : DisallowHandleAllocation no_handles;
295 : DisallowHandleDereference no_deref;
296 :
297 28 : uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
298 :
299 : parser_->set_stack_limit(stack_limit);
300 28 : parser_->ParseOnBackground(parse_info_.get());
301 :
302 28 : if (finish_callback_) {
303 3 : finish_callback_->ParseFinished(std::move(parse_info_));
304 1 : status_ = CompileJobStatus::kDone;
305 : } else {
306 27 : status_ = CompileJobStatus::kParsed;
307 28 : }
308 28 : }
309 :
310 27 : bool CompilerDispatcherJob::FinalizeParsingOnMainThread() {
311 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
312 : DCHECK(status() == CompileJobStatus::kParsed);
313 108 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeParsing);
314 27 : if (trace_compiler_dispatcher_jobs_) {
315 : PrintF("CompilerDispatcherJob[%p]: Finalizing parsing\n",
316 0 : static_cast<void*>(this));
317 : }
318 :
319 27 : if (!source_.is_null()) {
320 0 : i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
321 0 : source_ = Handle<String>::null();
322 : }
323 27 : if (!wrapper_.is_null()) {
324 23 : i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location());
325 23 : wrapper_ = Handle<String>::null();
326 : }
327 :
328 27 : Handle<Script> script(Script::cast(shared_->script()), isolate_);
329 : parse_info_->set_script(script);
330 27 : if (parse_info_->literal() == nullptr) {
331 2 : parser_->ReportErrors(isolate_, script);
332 1 : status_ = CompileJobStatus::kFailed;
333 : } else {
334 26 : status_ = CompileJobStatus::kReadyToAnalyze;
335 : }
336 54 : parser_->UpdateStatistics(isolate_, script);
337 :
338 54 : DeferredHandleScope scope(isolate_);
339 : {
340 27 : parse_info_->ReopenHandlesInNewHandleScope();
341 :
342 79 : if (!shared_->outer_scope_info()->IsTheHole(isolate_) &&
343 : ScopeInfo::cast(shared_->outer_scope_info())->length() > 0) {
344 : Handle<ScopeInfo> outer_scope_info(
345 : handle(ScopeInfo::cast(shared_->outer_scope_info())));
346 : parse_info_->set_outer_scope_info(outer_scope_info);
347 : }
348 : parse_info_->set_shared_info(shared_);
349 :
350 : // Internalize ast values on the main thread.
351 54 : parse_info_->ast_value_factory()->Internalize(isolate_);
352 54 : parser_->HandleSourceURLComments(isolate_, script);
353 :
354 : parse_info_->set_character_stream(nullptr);
355 : parse_info_->set_unicode_cache(nullptr);
356 : parser_.reset();
357 : unicode_cache_.reset();
358 : character_stream_.reset();
359 : }
360 54 : parse_info_->set_deferred_handles(scope.Detach());
361 :
362 54 : return status_ != CompileJobStatus::kFailed;
363 : }
364 :
365 26 : bool CompilerDispatcherJob::AnalyzeOnMainThread() {
366 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
367 : DCHECK(status() == CompileJobStatus::kReadyToAnalyze);
368 104 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kAnalyze);
369 26 : if (trace_compiler_dispatcher_jobs_) {
370 0 : PrintF("CompilerDispatcherJob[%p]: Analyzing\n", static_cast<void*>(this));
371 : }
372 :
373 : compile_info_.reset(new CompilationInfo(parse_info_->zone(),
374 : parse_info_.get(), isolate_,
375 52 : Handle<JSFunction>::null()));
376 :
377 52 : DeferredHandleScope scope(isolate_);
378 : {
379 26 : if (Compiler::Analyze(compile_info_.get())) {
380 25 : status_ = CompileJobStatus::kAnalyzed;
381 : } else {
382 1 : status_ = CompileJobStatus::kFailed;
383 1 : if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
384 : }
385 : }
386 52 : compile_info_->set_deferred_handles(scope.Detach());
387 :
388 52 : return status_ != CompileJobStatus::kFailed;
389 : }
390 :
391 29 : bool CompilerDispatcherJob::PrepareToCompileOnMainThread() {
392 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
393 : DCHECK(status() == CompileJobStatus::kAnalyzed);
394 116 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kPrepareToCompile);
395 :
396 : compile_job_.reset(
397 29 : Compiler::PrepareUnoptimizedCompilationJob(compile_info_.get()));
398 29 : if (!compile_job_.get()) {
399 0 : if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
400 0 : status_ = CompileJobStatus::kFailed;
401 0 : return false;
402 : }
403 :
404 29 : CHECK(compile_job_->can_execute_on_background_thread());
405 29 : status_ = CompileJobStatus::kReadyToCompile;
406 58 : return true;
407 : }
408 :
409 25 : void CompilerDispatcherJob::Compile() {
410 : DCHECK(status() == CompileJobStatus::kReadyToCompile);
411 125 : COMPILER_DISPATCHER_TRACE_SCOPE_WITH_NUM(
412 : tracer_, kCompile, parse_info_->literal()->ast_node_count());
413 25 : if (trace_compiler_dispatcher_jobs_) {
414 0 : PrintF("CompilerDispatcherJob[%p]: Compiling\n", static_cast<void*>(this));
415 : }
416 :
417 : // Disallowing of handle dereference and heap access dealt with in
418 : // CompilationJob::ExecuteJob.
419 :
420 25 : uintptr_t stack_limit = GetCurrentStackPosition() - max_stack_size_ * KB;
421 : compile_job_->set_stack_limit(stack_limit);
422 :
423 25 : CompilationJob::Status status = compile_job_->ExecuteJob();
424 : USE(status);
425 :
426 : // Always transition to kCompiled - errors will be reported by
427 : // FinalizeCompilingOnMainThread.
428 50 : status_ = CompileJobStatus::kCompiled;
429 25 : }
430 :
431 24 : bool CompilerDispatcherJob::FinalizeCompilingOnMainThread() {
432 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
433 : DCHECK(status() == CompileJobStatus::kCompiled);
434 96 : COMPILER_DISPATCHER_TRACE_SCOPE(tracer_, kFinalizeCompiling);
435 24 : if (trace_compiler_dispatcher_jobs_) {
436 : PrintF("CompilerDispatcherJob[%p]: Finalizing compiling\n",
437 0 : static_cast<void*>(this));
438 : }
439 :
440 : {
441 24 : HandleScope scope(isolate_);
442 45 : if (compile_job_->state() == CompilationJob::State::kFailed ||
443 21 : !Compiler::FinalizeCompilationJob(compile_job_.release())) {
444 3 : if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
445 3 : status_ = CompileJobStatus::kFailed;
446 : return false;
447 : }
448 : }
449 :
450 : compile_job_.reset();
451 : compile_info_.reset();
452 : parse_zone_.reset();
453 : parse_info_.reset();
454 :
455 21 : status_ = CompileJobStatus::kDone;
456 45 : return true;
457 : }
458 :
459 40 : void CompilerDispatcherJob::ResetOnMainThread() {
460 40 : if (trace_compiler_dispatcher_jobs_) {
461 0 : PrintF("CompilerDispatcherJob[%p]: Resetting\n", static_cast<void*>(this));
462 : }
463 :
464 : compile_job_.reset();
465 : compile_info_.reset();
466 : parse_zone_.reset();
467 : parser_.reset();
468 : unicode_cache_.reset();
469 : character_stream_.reset();
470 : parse_info_.reset();
471 40 : finish_callback_ = nullptr;
472 :
473 40 : if (!source_.is_null()) {
474 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
475 0 : i::GlobalHandles::Destroy(Handle<Object>::cast(source_).location());
476 0 : source_ = Handle<String>::null();
477 : }
478 40 : if (!wrapper_.is_null()) {
479 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
480 1 : i::GlobalHandles::Destroy(Handle<Object>::cast(wrapper_).location());
481 1 : wrapper_ = Handle<String>::null();
482 : }
483 :
484 40 : status_ = CompileJobStatus::kInitial;
485 40 : }
486 :
487 96 : double CompilerDispatcherJob::EstimateRuntimeOfNextStepInMs() const {
488 96 : switch (status_) {
489 : case CompileJobStatus::kInitial:
490 12 : return tracer_->EstimatePrepareToParseInMs();
491 :
492 : case CompileJobStatus::kReadyToParse:
493 24 : return tracer_->EstimateParseInMs(parse_info_->end_position() -
494 24 : parse_info_->start_position());
495 :
496 : case CompileJobStatus::kParsed:
497 12 : return tracer_->EstimateFinalizeParsingInMs();
498 :
499 : case CompileJobStatus::kReadyToAnalyze:
500 12 : return tracer_->EstimateAnalyzeInMs();
501 :
502 : case CompileJobStatus::kAnalyzed:
503 14 : return tracer_->EstimatePrepareToCompileInMs();
504 :
505 : case CompileJobStatus::kReadyToCompile:
506 : return tracer_->EstimateCompileInMs(
507 28 : parse_info_->literal()->ast_node_count());
508 :
509 : case CompileJobStatus::kCompiled:
510 10 : return tracer_->EstimateFinalizeCompilingInMs();
511 :
512 : case CompileJobStatus::kFailed:
513 : case CompileJobStatus::kDone:
514 : return 0.0;
515 : }
516 :
517 0 : UNREACHABLE();
518 : return 0.0;
519 : }
520 :
521 0 : void CompilerDispatcherJob::ShortPrint() {
522 0 : if (isolate_) {
523 : DCHECK(ThreadId::Current().Equals(isolate_->thread_id()));
524 : DCHECK(!shared_.is_null());
525 0 : shared_->ShortPrint();
526 : } else {
527 : // TODO(wiktorg) more useful info in those cases
528 0 : if (parse_info_) {
529 0 : PrintF("function at %d", parse_info_->start_position());
530 : } else {
531 0 : PrintF("parsed function");
532 : }
533 : }
534 0 : }
535 :
536 : } // namespace internal
537 : } // namespace v8
|