1 /// Copyright 2012 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 <errno.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9
10 #include <algorithm>
11 #include <fstream>
12 #include <unordered_map>
13 #include <utility>
14 #include <vector>
15
16 #ifdef ENABLE_VTUNE_JIT_INTERFACE
17 #include "src/third_party/vtune/v8-vtune.h"
18 #endif
19
20 #include "src/d8-console.h"
21 #include "src/d8.h"
22 #include "src/ostreams.h"
23
24 #include "include/libplatform/libplatform.h"
25 #include "include/libplatform/v8-tracing.h"
26 #include "include/v8-inspector.h"
27 #include "src/api-inl.h"
28 #include "src/base/cpu.h"
29 #include "src/base/logging.h"
30 #include "src/base/platform/platform.h"
31 #include "src/base/platform/time.h"
32 #include "src/base/sys-info.h"
33 #include "src/basic-block-profiler.h"
34 #include "src/debug/debug-interface.h"
35 #include "src/interpreter/interpreter.h"
36 #include "src/msan.h"
37 #include "src/objects-inl.h"
38 #include "src/objects.h"
39 #include "src/snapshot/natives.h"
40 #include "src/trap-handler/trap-handler.h"
41 #include "src/utils.h"
42 #include "src/v8.h"
43 #include "src/wasm/wasm-engine.h"
44
45 #if !defined(_WIN32) && !defined(_WIN64)
46 #include <unistd.h> // NOLINT
47 #else
48 #include <windows.h> // NOLINT
49 #endif // !defined(_WIN32) && !defined(_WIN64)
50
51 #ifndef DCHECK
52 #define DCHECK(condition) assert(condition)
53 #endif
54
55 #ifndef CHECK
56 #define CHECK(condition) assert(condition)
57 #endif
58
59 namespace v8 {
60
61 namespace {
62
63 const int kMB = 1024 * 1024;
64
65 const int kMaxWorkers = 50;
66 const int kMaxSerializerMemoryUsage =
67 1 * kMB; // Arbitrary maximum for testing.
68
69 // Base class for shell ArrayBuffer allocators. It forwards all opertions to
70 // the default v8 allocator.
71 class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
72 public:
Allocate(size_t length)73 void* Allocate(size_t length) override {
74 return allocator_->Allocate(length);
75 }
76
AllocateUninitialized(size_t length)77 void* AllocateUninitialized(size_t length) override {
78 return allocator_->AllocateUninitialized(length);
79 }
80
Free(void * data,size_t length)81 void Free(void* data, size_t length) override {
82 allocator_->Free(data, length);
83 }
84
85 private:
86 std::unique_ptr<Allocator> allocator_ =
87 std::unique_ptr<Allocator>(NewDefaultAllocator());
88 };
89
90 // ArrayBuffer allocator that can use virtual memory to improve performance.
91 class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
92 public:
Allocate(size_t length)93 void* Allocate(size_t length) override {
94 if (length >= kVMThreshold) return AllocateVM(length);
95 return ArrayBufferAllocatorBase::Allocate(length);
96 }
97
AllocateUninitialized(size_t length)98 void* AllocateUninitialized(size_t length) override {
99 if (length >= kVMThreshold) return AllocateVM(length);
100 return ArrayBufferAllocatorBase::AllocateUninitialized(length);
101 }
102
Free(void * data,size_t length)103 void Free(void* data, size_t length) override {
104 if (length >= kVMThreshold) {
105 FreeVM(data, length);
106 } else {
107 ArrayBufferAllocatorBase::Free(data, length);
108 }
109 }
110
111 private:
112 static constexpr size_t kVMThreshold = 65536;
113 static constexpr size_t kTwoGB = 2u * 1024u * 1024u * 1024u;
114
AllocateVM(size_t length)115 void* AllocateVM(size_t length) {
116 DCHECK_LE(kVMThreshold, length);
117 // TODO(titzer): allocations should fail if >= 2gb because array buffers
118 // store their lengths as a SMI internally.
119 if (length >= kTwoGB) return nullptr;
120
121 size_t page_size = i::AllocatePageSize();
122 size_t allocated = RoundUp(length, page_size);
123 // Rounding up could go over the limit.
124 if (allocated >= kTwoGB) return nullptr;
125 return i::AllocatePages(nullptr, allocated, page_size,
126 PageAllocator::kReadWrite);
127 }
128
FreeVM(void * data,size_t length)129 void FreeVM(void* data, size_t length) {
130 size_t page_size = i::AllocatePageSize();
131 size_t allocated = RoundUp(length, page_size);
132 CHECK(i::FreePages(data, allocated));
133 }
134 };
135
136 // ArrayBuffer allocator that never allocates over 10MB.
137 class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
Allocate(size_t length)138 void* Allocate(size_t length) override {
139 return ArrayBufferAllocatorBase::Allocate(Adjust(length));
140 }
141
AllocateUninitialized(size_t length)142 void* AllocateUninitialized(size_t length) override {
143 return ArrayBufferAllocatorBase::AllocateUninitialized(Adjust(length));
144 }
145
Free(void * data,size_t length)146 void Free(void* data, size_t length) override {
147 return ArrayBufferAllocatorBase::Free(data, Adjust(length));
148 }
149
150 private:
Adjust(size_t length)151 size_t Adjust(size_t length) {
152 const size_t kAllocationLimit = 10 * kMB;
153 return length > kAllocationLimit ? i::AllocatePageSize() : length;
154 }
155 };
156
157 // Predictable v8::Platform implementation. Worker threads are disabled, idle
158 // tasks are disallowed, and the time reported by {MonotonicallyIncreasingTime}
159 // is deterministic.
160 class PredictablePlatform : public Platform {
161 public:
PredictablePlatform(std::unique_ptr<Platform> platform)162 explicit PredictablePlatform(std::unique_ptr<Platform> platform)
163 : platform_(std::move(platform)) {
164 DCHECK_NOT_NULL(platform_);
165 }
166
GetPageAllocator()167 PageAllocator* GetPageAllocator() override {
168 return platform_->GetPageAllocator();
169 }
170
OnCriticalMemoryPressure()171 void OnCriticalMemoryPressure() override {
172 platform_->OnCriticalMemoryPressure();
173 }
174
OnCriticalMemoryPressure(size_t length)175 bool OnCriticalMemoryPressure(size_t length) override {
176 return platform_->OnCriticalMemoryPressure(length);
177 }
178
GetForegroundTaskRunner(v8::Isolate * isolate)179 std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
180 v8::Isolate* isolate) override {
181 return platform_->GetForegroundTaskRunner(isolate);
182 }
183
NumberOfWorkerThreads()184 int NumberOfWorkerThreads() override { return 0; }
185
CallOnWorkerThread(std::unique_ptr<Task> task)186 void CallOnWorkerThread(std::unique_ptr<Task> task) override {
187 // It's not defined when background tasks are being executed, so we can just
188 // execute them right away.
189 task->Run();
190 }
191
CallDelayedOnWorkerThread(std::unique_ptr<Task> task,double delay_in_seconds)192 void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
193 double delay_in_seconds) override {
194 // Never run delayed tasks.
195 }
196
CallOnForegroundThread(v8::Isolate * isolate,Task * task)197 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
198 platform_->CallOnForegroundThread(isolate, task);
199 }
200
CallDelayedOnForegroundThread(v8::Isolate * isolate,Task * task,double delay_in_seconds)201 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
202 double delay_in_seconds) override {
203 platform_->CallDelayedOnForegroundThread(isolate, task, delay_in_seconds);
204 }
205
CallIdleOnForegroundThread(Isolate * isolate,IdleTask * task)206 void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override {
207 UNREACHABLE();
208 }
209
IdleTasksEnabled(Isolate * isolate)210 bool IdleTasksEnabled(Isolate* isolate) override { return false; }
211
MonotonicallyIncreasingTime()212 double MonotonicallyIncreasingTime() override {
213 return synthetic_time_in_sec_ += 0.00001;
214 }
215
CurrentClockTimeMillis()216 double CurrentClockTimeMillis() override {
217 return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond;
218 }
219
GetTracingController()220 v8::TracingController* GetTracingController() override {
221 return platform_->GetTracingController();
222 }
223
platform() const224 Platform* platform() const { return platform_.get(); }
225
226 private:
227 double synthetic_time_in_sec_ = 0.0;
228 std::unique_ptr<Platform> platform_;
229
230 DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
231 };
232
233 std::unique_ptr<v8::Platform> g_platform;
234
GetDefaultPlatform()235 v8::Platform* GetDefaultPlatform() {
236 return i::FLAG_verify_predictable
237 ? static_cast<PredictablePlatform*>(g_platform.get())->platform()
238 : g_platform.get();
239 }
240
Throw(Isolate * isolate,const char * message)241 static Local<Value> Throw(Isolate* isolate, const char* message) {
242 return isolate->ThrowException(
243 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
244 .ToLocalChecked());
245 }
246
GetWorkerFromInternalField(Isolate * isolate,Local<Object> object)247 Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
248 if (object->InternalFieldCount() != 1) {
249 Throw(isolate, "this is not a Worker");
250 return nullptr;
251 }
252
253 Worker* worker =
254 static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0));
255 if (worker == nullptr) {
256 Throw(isolate, "Worker is defunct because main thread is terminating");
257 return nullptr;
258 }
259
260 return worker;
261 }
262
GetThreadOptions(const char * name)263 base::Thread::Options GetThreadOptions(const char* name) {
264 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
265 // which is not enough to parse the big literal expressions used in tests.
266 // The stack size should be at least StackGuard::kLimitSize + some
267 // OS-specific padding for thread startup code. 2Mbytes seems to be enough.
268 return base::Thread::Options(name, 2 * kMB);
269 }
270
271 } // namespace
272
273 namespace tracing {
274
275 namespace {
276
277 // String options that can be used to initialize TraceOptions.
278 const char kRecordUntilFull[] = "record-until-full";
279 const char kRecordContinuously[] = "record-continuously";
280 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
281
282 const char kRecordModeParam[] = "record_mode";
283 const char kEnableSystraceParam[] = "enable_systrace";
284 const char kEnableArgumentFilterParam[] = "enable_argument_filter";
285 const char kIncludedCategoriesParam[] = "included_categories";
286
287 class TraceConfigParser {
288 public:
FillTraceConfig(v8::Isolate * isolate,platform::tracing::TraceConfig * trace_config,const char * json_str)289 static void FillTraceConfig(v8::Isolate* isolate,
290 platform::tracing::TraceConfig* trace_config,
291 const char* json_str) {
292 HandleScope outer_scope(isolate);
293 Local<Context> context = Context::New(isolate);
294 Context::Scope context_scope(context);
295 HandleScope inner_scope(isolate);
296
297 Local<String> source =
298 String::NewFromUtf8(isolate, json_str, NewStringType::kNormal)
299 .ToLocalChecked();
300 Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
301 Local<v8::Object> trace_config_object = Local<v8::Object>::Cast(result);
302
303 trace_config->SetTraceRecordMode(
304 GetTraceRecordMode(isolate, context, trace_config_object));
305 if (GetBoolean(isolate, context, trace_config_object,
306 kEnableSystraceParam)) {
307 trace_config->EnableSystrace();
308 }
309 if (GetBoolean(isolate, context, trace_config_object,
310 kEnableArgumentFilterParam)) {
311 trace_config->EnableArgumentFilter();
312 }
313 UpdateIncludedCategoriesList(isolate, context, trace_config_object,
314 trace_config);
315 }
316
317 private:
GetBoolean(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,const char * property)318 static bool GetBoolean(v8::Isolate* isolate, Local<Context> context,
319 Local<v8::Object> object, const char* property) {
320 Local<Value> value = GetValue(isolate, context, object, property);
321 if (value->IsNumber()) {
322 Local<Boolean> v8_boolean = value->ToBoolean(context).ToLocalChecked();
323 return v8_boolean->Value();
324 }
325 return false;
326 }
327
UpdateIncludedCategoriesList(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,platform::tracing::TraceConfig * trace_config)328 static int UpdateIncludedCategoriesList(
329 v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
330 platform::tracing::TraceConfig* trace_config) {
331 Local<Value> value =
332 GetValue(isolate, context, object, kIncludedCategoriesParam);
333 if (value->IsArray()) {
334 Local<Array> v8_array = Local<Array>::Cast(value);
335 for (int i = 0, length = v8_array->Length(); i < length; ++i) {
336 Local<Value> v = v8_array->Get(context, i)
337 .ToLocalChecked()
338 ->ToString(context)
339 .ToLocalChecked();
340 String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
341 trace_config->AddIncludedCategory(*str);
342 }
343 return v8_array->Length();
344 }
345 return 0;
346 }
347
GetTraceRecordMode(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object)348 static platform::tracing::TraceRecordMode GetTraceRecordMode(
349 v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object) {
350 Local<Value> value = GetValue(isolate, context, object, kRecordModeParam);
351 if (value->IsString()) {
352 Local<String> v8_string = value->ToString(context).ToLocalChecked();
353 String::Utf8Value str(isolate, v8_string);
354 if (strcmp(kRecordUntilFull, *str) == 0) {
355 return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
356 } else if (strcmp(kRecordContinuously, *str) == 0) {
357 return platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY;
358 } else if (strcmp(kRecordAsMuchAsPossible, *str) == 0) {
359 return platform::tracing::TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE;
360 }
361 }
362 return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
363 }
364
GetValue(v8::Isolate * isolate,Local<Context> context,Local<v8::Object> object,const char * property)365 static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
366 Local<v8::Object> object, const char* property) {
367 Local<String> v8_str =
368 String::NewFromUtf8(isolate, property, NewStringType::kNormal)
369 .ToLocalChecked();
370 return object->Get(context, v8_str).ToLocalChecked();
371 }
372 };
373
374 } // namespace
375
CreateTraceConfigFromJSON(v8::Isolate * isolate,const char * json_str)376 static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
377 v8::Isolate* isolate, const char* json_str) {
378 platform::tracing::TraceConfig* trace_config =
379 new platform::tracing::TraceConfig();
380 TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str);
381 return trace_config;
382 }
383
384 } // namespace tracing
385
386
387 class ExternalOwningOneByteStringResource
388 : public String::ExternalOneByteStringResource {
389 public:
ExternalOwningOneByteStringResource()390 ExternalOwningOneByteStringResource() : length_(0) {}
ExternalOwningOneByteStringResource(std::unique_ptr<const char[]> data,size_t length)391 ExternalOwningOneByteStringResource(std::unique_ptr<const char[]> data,
392 size_t length)
393 : data_(std::move(data)), length_(length) {}
data() const394 const char* data() const override { return data_.get(); }
length() const395 size_t length() const override { return length_; }
396
397 private:
398 std::unique_ptr<const char[]> data_;
399 size_t length_;
400 };
401
402 CounterMap* Shell::counter_map_;
403 base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
404 CounterCollection Shell::local_counters_;
405 CounterCollection* Shell::counters_ = &local_counters_;
406 base::LazyMutex Shell::context_mutex_;
407 const base::TimeTicks Shell::kInitialTicks =
408 base::TimeTicks::HighResolutionNow();
409 Global<Function> Shell::stringify_function_;
410 base::LazyMutex Shell::workers_mutex_;
411 bool Shell::allow_new_workers_ = true;
412 std::vector<Worker*> Shell::workers_;
413 std::vector<ExternalizedContents> Shell::externalized_contents_;
414 std::atomic<bool> Shell::script_executed_{false};
415 base::LazyMutex Shell::isolate_status_lock_;
416 std::map<v8::Isolate*, bool> Shell::isolate_status_;
417 base::LazyMutex Shell::cached_code_mutex_;
418 std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
419 Shell::cached_code_map_;
420
421 Global<Context> Shell::evaluation_context_;
422 ArrayBuffer::Allocator* Shell::array_buffer_allocator;
423 ShellOptions Shell::options;
424 base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
425
426 // Dummy external source stream which returns the whole source in one go.
427 class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
428 public:
DummySourceStream(Local<String> source,Isolate * isolate)429 DummySourceStream(Local<String> source, Isolate* isolate) : done_(false) {
430 source_length_ = source->Utf8Length(isolate);
431 source_buffer_.reset(new uint8_t[source_length_]);
432 source->WriteUtf8(isolate, reinterpret_cast<char*>(source_buffer_.get()),
433 source_length_);
434 }
435
GetMoreData(const uint8_t ** src)436 virtual size_t GetMoreData(const uint8_t** src) {
437 if (done_) {
438 return 0;
439 }
440 *src = source_buffer_.release();
441 done_ = true;
442
443 return source_length_;
444 }
445
446 private:
447 int source_length_;
448 std::unique_ptr<uint8_t[]> source_buffer_;
449 bool done_;
450 };
451
452 class BackgroundCompileThread : public base::Thread {
453 public:
BackgroundCompileThread(Isolate * isolate,Local<String> source)454 BackgroundCompileThread(Isolate* isolate, Local<String> source)
455 : base::Thread(GetThreadOptions("BackgroundCompileThread")),
456 source_(source),
457 streamed_source_(new DummySourceStream(source, isolate),
458 v8::ScriptCompiler::StreamedSource::UTF8),
459 task_(v8::ScriptCompiler::StartStreamingScript(isolate,
460 &streamed_source_)) {}
461
Run()462 void Run() override { task_->Run(); }
463
streamed_source()464 v8::ScriptCompiler::StreamedSource* streamed_source() {
465 return &streamed_source_;
466 }
467
468 private:
469 Local<String> source_;
470 v8::ScriptCompiler::StreamedSource streamed_source_;
471 std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_;
472 };
473
LookupCodeCache(Isolate * isolate,Local<Value> source)474 ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
475 Local<Value> source) {
476 base::LockGuard<base::Mutex> lock_guard(cached_code_mutex_.Pointer());
477 CHECK(source->IsString());
478 v8::String::Utf8Value key(isolate, source);
479 DCHECK(*key);
480 auto entry = cached_code_map_.find(*key);
481 if (entry != cached_code_map_.end() && entry->second) {
482 int length = entry->second->length;
483 uint8_t* cache = new uint8_t[length];
484 memcpy(cache, entry->second->data, length);
485 ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
486 cache, length, ScriptCompiler::CachedData::BufferOwned);
487 return cached_data;
488 }
489 return nullptr;
490 }
491
StoreInCodeCache(Isolate * isolate,Local<Value> source,const ScriptCompiler::CachedData * cache_data)492 void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
493 const ScriptCompiler::CachedData* cache_data) {
494 base::LockGuard<base::Mutex> lock_guard(cached_code_mutex_.Pointer());
495 CHECK(source->IsString());
496 if (cache_data == nullptr) return;
497 v8::String::Utf8Value key(isolate, source);
498 DCHECK(*key);
499 int length = cache_data->length;
500 uint8_t* cache = new uint8_t[length];
501 memcpy(cache, cache_data->data, length);
502 cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
503 new ScriptCompiler::CachedData(cache, length,
504 ScriptCompiler::CachedData::BufferOwned));
505 }
506
507 // Executes a string within the current v8 context.
ExecuteString(Isolate * isolate,Local<String> source,Local<Value> name,PrintResult print_result,ReportExceptions report_exceptions,ProcessMessageQueue process_message_queue)508 bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
509 Local<Value> name, PrintResult print_result,
510 ReportExceptions report_exceptions,
511 ProcessMessageQueue process_message_queue) {
512 HandleScope handle_scope(isolate);
513 TryCatch try_catch(isolate);
514 try_catch.SetVerbose(true);
515
516 MaybeLocal<Value> maybe_result;
517 bool success = true;
518 {
519 PerIsolateData* data = PerIsolateData::Get(isolate);
520 Local<Context> realm =
521 Local<Context>::New(isolate, data->realms_[data->realm_current_]);
522 Context::Scope context_scope(realm);
523 MaybeLocal<Script> maybe_script;
524 Local<Context> context(isolate->GetCurrentContext());
525 ScriptOrigin origin(name);
526
527 DCHECK(options.compile_options != ScriptCompiler::kProduceParserCache);
528 DCHECK(options.compile_options != ScriptCompiler::kConsumeParserCache);
529 if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
530 ScriptCompiler::CachedData* cached_code =
531 LookupCodeCache(isolate, source);
532 if (cached_code != nullptr) {
533 ScriptCompiler::Source script_source(source, origin, cached_code);
534 maybe_script = ScriptCompiler::Compile(context, &script_source,
535 options.compile_options);
536 CHECK(!cached_code->rejected);
537 } else {
538 ScriptCompiler::Source script_source(source, origin);
539 maybe_script = ScriptCompiler::Compile(
540 context, &script_source, ScriptCompiler::kNoCompileOptions);
541 }
542 } else if (options.stress_background_compile) {
543 // Start a background thread compiling the script.
544 BackgroundCompileThread background_compile_thread(isolate, source);
545 background_compile_thread.Start();
546
547 // In parallel, compile on the main thread to flush out any data races.
548 {
549 TryCatch ignore_try_catch(isolate);
550 ScriptCompiler::Source script_source(source, origin);
551 USE(ScriptCompiler::Compile(context, &script_source,
552 ScriptCompiler::kNoCompileOptions));
553 }
554
555 // Join with background thread and finalize compilation.
556 background_compile_thread.Join();
557 maybe_script = v8::ScriptCompiler::Compile(
558 context, background_compile_thread.streamed_source(), source, origin);
559 } else {
560 ScriptCompiler::Source script_source(source, origin);
561 maybe_script = ScriptCompiler::Compile(context, &script_source,
562 options.compile_options);
563 }
564
565 Local<Script> script;
566 if (!maybe_script.ToLocal(&script)) {
567 // Print errors that happened during compilation.
568 if (report_exceptions) ReportException(isolate, &try_catch);
569 return false;
570 }
571
572 if (options.code_cache_options ==
573 ShellOptions::CodeCacheOptions::kProduceCache) {
574 // Serialize and store it in memory for the next execution.
575 ScriptCompiler::CachedData* cached_data =
576 ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
577 StoreInCodeCache(isolate, source, cached_data);
578 delete cached_data;
579 }
580 maybe_result = script->Run(realm);
581 if (options.code_cache_options ==
582 ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
583 // Serialize and store it in memory for the next execution.
584 ScriptCompiler::CachedData* cached_data =
585 ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
586 StoreInCodeCache(isolate, source, cached_data);
587 delete cached_data;
588 }
589 if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
590 data->realm_current_ = data->realm_switch_;
591 }
592 Local<Value> result;
593 if (!maybe_result.ToLocal(&result)) {
594 DCHECK(try_catch.HasCaught());
595 // Print errors that happened during execution.
596 if (report_exceptions) ReportException(isolate, &try_catch);
597 return false;
598 }
599 DCHECK(!try_catch.HasCaught());
600 if (print_result) {
601 if (options.test_shell) {
602 if (!result->IsUndefined()) {
603 // If all went well and the result wasn't undefined then print
604 // the returned value.
605 v8::String::Utf8Value str(isolate, result);
606 fwrite(*str, sizeof(**str), str.length(), stdout);
607 printf("\n");
608 }
609 } else {
610 v8::String::Utf8Value str(isolate, Stringify(isolate, result));
611 fwrite(*str, sizeof(**str), str.length(), stdout);
612 printf("\n");
613 }
614 }
615 return success;
616 }
617
618 namespace {
619
ToSTLString(Isolate * isolate,Local<String> v8_str)620 std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
621 String::Utf8Value utf8(isolate, v8_str);
622 // Should not be able to fail since the input is a String.
623 CHECK(*utf8);
624 return *utf8;
625 }
626
IsAbsolutePath(const std::string & path)627 bool IsAbsolutePath(const std::string& path) {
628 #if defined(_WIN32) || defined(_WIN64)
629 // TODO(adamk): This is an incorrect approximation, but should
630 // work for all our test-running cases.
631 return path.find(':') != std::string::npos;
632 #else
633 return path[0] == '/';
634 #endif
635 }
636
GetWorkingDirectory()637 std::string GetWorkingDirectory() {
638 #if defined(_WIN32) || defined(_WIN64)
639 char system_buffer[MAX_PATH];
640 // TODO(adamk): Support Unicode paths.
641 DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
642 CHECK_GT(len, 0);
643 return system_buffer;
644 #else
645 char curdir[PATH_MAX];
646 CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
647 return curdir;
648 #endif
649 }
650
651 // Returns the directory part of path, without the trailing '/'.
DirName(const std::string & path)652 std::string DirName(const std::string& path) {
653 DCHECK(IsAbsolutePath(path));
654 size_t last_slash = path.find_last_of('/');
655 DCHECK(last_slash != std::string::npos);
656 return path.substr(0, last_slash);
657 }
658
659 // Resolves path to an absolute path if necessary, and does some
660 // normalization (eliding references to the current directory
661 // and replacing backslashes with slashes).
NormalizePath(const std::string & path,const std::string & dir_name)662 std::string NormalizePath(const std::string& path,
663 const std::string& dir_name) {
664 std::string result;
665 if (IsAbsolutePath(path)) {
666 result = path;
667 } else {
668 result = dir_name + '/' + path;
669 }
670 std::replace(result.begin(), result.end(), '\\', '/');
671 size_t i;
672 while ((i = result.find("/./")) != std::string::npos) {
673 result.erase(i, 2);
674 }
675 return result;
676 }
677
678 // Per-context Module data, allowing sharing of module maps
679 // across top-level module loads.
680 class ModuleEmbedderData {
681 private:
682 class ModuleGlobalHash {
683 public:
ModuleGlobalHash(Isolate * isolate)684 explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
operator ()(const Global<Module> & module) const685 size_t operator()(const Global<Module>& module) const {
686 return module.Get(isolate_)->GetIdentityHash();
687 }
688
689 private:
690 Isolate* isolate_;
691 };
692
693 public:
ModuleEmbedderData(Isolate * isolate)694 explicit ModuleEmbedderData(Isolate* isolate)
695 : module_to_specifier_map(10, ModuleGlobalHash(isolate)) {}
696
697 // Map from normalized module specifier to Module.
698 std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
699 // Map from Module to its URL as defined in the ScriptOrigin
700 std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
701 module_to_specifier_map;
702 };
703
704 enum {
705 kModuleEmbedderDataIndex,
706 kInspectorClientIndex
707 };
708
InitializeModuleEmbedderData(Local<Context> context)709 void InitializeModuleEmbedderData(Local<Context> context) {
710 context->SetAlignedPointerInEmbedderData(
711 kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
712 }
713
GetModuleDataFromContext(Local<Context> context)714 ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
715 return static_cast<ModuleEmbedderData*>(
716 context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
717 }
718
DisposeModuleEmbedderData(Local<Context> context)719 void DisposeModuleEmbedderData(Local<Context> context) {
720 delete GetModuleDataFromContext(context);
721 context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
722 }
723
ResolveModuleCallback(Local<Context> context,Local<String> specifier,Local<Module> referrer)724 MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
725 Local<String> specifier,
726 Local<Module> referrer) {
727 Isolate* isolate = context->GetIsolate();
728 ModuleEmbedderData* d = GetModuleDataFromContext(context);
729 auto specifier_it =
730 d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
731 CHECK(specifier_it != d->module_to_specifier_map.end());
732 std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
733 DirName(specifier_it->second));
734 auto module_it = d->specifier_to_module_map.find(absolute_path);
735 CHECK(module_it != d->specifier_to_module_map.end());
736 return module_it->second.Get(isolate);
737 }
738
739 } // anonymous namespace
740
FetchModuleTree(Local<Context> context,const std::string & file_name)741 MaybeLocal<Module> Shell::FetchModuleTree(Local<Context> context,
742 const std::string& file_name) {
743 DCHECK(IsAbsolutePath(file_name));
744 Isolate* isolate = context->GetIsolate();
745 Local<String> source_text = ReadFile(isolate, file_name.c_str());
746 if (source_text.IsEmpty()) {
747 std::string msg = "Error reading: " + file_name;
748 Throw(isolate, msg.c_str());
749 return MaybeLocal<Module>();
750 }
751 ScriptOrigin origin(
752 String::NewFromUtf8(isolate, file_name.c_str(), NewStringType::kNormal)
753 .ToLocalChecked(),
754 Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
755 Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
756 ScriptCompiler::Source source(source_text, origin);
757 Local<Module> module;
758 if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
759 return MaybeLocal<Module>();
760 }
761
762 ModuleEmbedderData* d = GetModuleDataFromContext(context);
763 CHECK(d->specifier_to_module_map
764 .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
765 .second);
766 CHECK(d->module_to_specifier_map
767 .insert(std::make_pair(Global<Module>(isolate, module), file_name))
768 .second);
769
770 std::string dir_name = DirName(file_name);
771
772 for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
773 Local<String> name = module->GetModuleRequest(i);
774 std::string absolute_path =
775 NormalizePath(ToSTLString(isolate, name), dir_name);
776 if (!d->specifier_to_module_map.count(absolute_path)) {
777 if (FetchModuleTree(context, absolute_path).IsEmpty()) {
778 return MaybeLocal<Module>();
779 }
780 }
781 }
782
783 return module;
784 }
785
786 namespace {
787
788 struct DynamicImportData {
DynamicImportDatav8::__anoncd2d3b7e0511::DynamicImportData789 DynamicImportData(Isolate* isolate_, Local<String> referrer_,
790 Local<String> specifier_,
791 Local<Promise::Resolver> resolver_)
792 : isolate(isolate_) {
793 referrer.Reset(isolate, referrer_);
794 specifier.Reset(isolate, specifier_);
795 resolver.Reset(isolate, resolver_);
796 }
797
798 Isolate* isolate;
799 Global<String> referrer;
800 Global<String> specifier;
801 Global<Promise::Resolver> resolver;
802 };
803
804 } // namespace
805
HostImportModuleDynamically(Local<Context> context,Local<ScriptOrModule> referrer,Local<String> specifier)806 MaybeLocal<Promise> Shell::HostImportModuleDynamically(
807 Local<Context> context, Local<ScriptOrModule> referrer,
808 Local<String> specifier) {
809 Isolate* isolate = context->GetIsolate();
810
811 MaybeLocal<Promise::Resolver> maybe_resolver =
812 Promise::Resolver::New(context);
813 Local<Promise::Resolver> resolver;
814 if (maybe_resolver.ToLocal(&resolver)) {
815 DynamicImportData* data = new DynamicImportData(
816 isolate, Local<String>::Cast(referrer->GetResourceName()), specifier,
817 resolver);
818 isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
819 return resolver->GetPromise();
820 }
821
822 return MaybeLocal<Promise>();
823 }
824
HostInitializeImportMetaObject(Local<Context> context,Local<Module> module,Local<Object> meta)825 void Shell::HostInitializeImportMetaObject(Local<Context> context,
826 Local<Module> module,
827 Local<Object> meta) {
828 Isolate* isolate = context->GetIsolate();
829 HandleScope handle_scope(isolate);
830
831 ModuleEmbedderData* d = GetModuleDataFromContext(context);
832 auto specifier_it =
833 d->module_to_specifier_map.find(Global<Module>(isolate, module));
834 CHECK(specifier_it != d->module_to_specifier_map.end());
835
836 Local<String> url_key =
837 String::NewFromUtf8(isolate, "url", NewStringType::kNormal)
838 .ToLocalChecked();
839 Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str(),
840 NewStringType::kNormal)
841 .ToLocalChecked();
842 meta->CreateDataProperty(context, url_key, url).ToChecked();
843 }
844
DoHostImportModuleDynamically(void * import_data)845 void Shell::DoHostImportModuleDynamically(void* import_data) {
846 std::unique_ptr<DynamicImportData> import_data_(
847 static_cast<DynamicImportData*>(import_data));
848 Isolate* isolate(import_data_->isolate);
849 HandleScope handle_scope(isolate);
850
851 Local<String> referrer(import_data_->referrer.Get(isolate));
852 Local<String> specifier(import_data_->specifier.Get(isolate));
853 Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
854
855 PerIsolateData* data = PerIsolateData::Get(isolate);
856 Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
857 Context::Scope context_scope(realm);
858
859 std::string source_url = ToSTLString(isolate, referrer);
860 std::string dir_name =
861 DirName(NormalizePath(source_url, GetWorkingDirectory()));
862 std::string file_name = ToSTLString(isolate, specifier);
863 std::string absolute_path = NormalizePath(file_name, dir_name);
864
865 TryCatch try_catch(isolate);
866 try_catch.SetVerbose(true);
867
868 ModuleEmbedderData* d = GetModuleDataFromContext(realm);
869 Local<Module> root_module;
870 auto module_it = d->specifier_to_module_map.find(absolute_path);
871 if (module_it != d->specifier_to_module_map.end()) {
872 root_module = module_it->second.Get(isolate);
873 } else if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
874 CHECK(try_catch.HasCaught());
875 resolver->Reject(realm, try_catch.Exception()).ToChecked();
876 return;
877 }
878
879 MaybeLocal<Value> maybe_result;
880 if (root_module->InstantiateModule(realm, ResolveModuleCallback)
881 .FromMaybe(false)) {
882 maybe_result = root_module->Evaluate(realm);
883 EmptyMessageQueues(isolate);
884 }
885
886 Local<Value> module;
887 if (!maybe_result.ToLocal(&module)) {
888 DCHECK(try_catch.HasCaught());
889 resolver->Reject(realm, try_catch.Exception()).ToChecked();
890 return;
891 }
892
893 DCHECK(!try_catch.HasCaught());
894 Local<Value> module_namespace = root_module->GetModuleNamespace();
895 resolver->Resolve(realm, module_namespace).ToChecked();
896 }
897
ExecuteModule(Isolate * isolate,const char * file_name)898 bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
899 HandleScope handle_scope(isolate);
900
901 PerIsolateData* data = PerIsolateData::Get(isolate);
902 Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
903 Context::Scope context_scope(realm);
904
905 std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
906
907 TryCatch try_catch(isolate);
908 try_catch.SetVerbose(true);
909
910 Local<Module> root_module;
911 MaybeLocal<Value> maybe_exception;
912
913 if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
914 CHECK(try_catch.HasCaught());
915 ReportException(isolate, &try_catch);
916 return false;
917 }
918
919 MaybeLocal<Value> maybe_result;
920 if (root_module->InstantiateModule(realm, ResolveModuleCallback)
921 .FromMaybe(false)) {
922 maybe_result = root_module->Evaluate(realm);
923 EmptyMessageQueues(isolate);
924 }
925 Local<Value> result;
926 if (!maybe_result.ToLocal(&result)) {
927 DCHECK(try_catch.HasCaught());
928 // Print errors that happened during execution.
929 ReportException(isolate, &try_catch);
930 return false;
931 }
932 DCHECK(!try_catch.HasCaught());
933 return true;
934 }
935
PerIsolateData(Isolate * isolate)936 PerIsolateData::PerIsolateData(Isolate* isolate)
937 : isolate_(isolate), realms_(nullptr) {
938 isolate->SetData(0, this);
939 if (i::FLAG_expose_async_hooks) {
940 async_hooks_wrapper_ = new AsyncHooks(isolate);
941 }
942 }
943
~PerIsolateData()944 PerIsolateData::~PerIsolateData() {
945 isolate_->SetData(0, nullptr); // Not really needed, just to be sure...
946 if (i::FLAG_expose_async_hooks) {
947 delete async_hooks_wrapper_; // This uses the isolate
948 }
949 }
950
SetTimeout(Local<Function> callback,Local<Context> context)951 void PerIsolateData::SetTimeout(Local<Function> callback,
952 Local<Context> context) {
953 set_timeout_callbacks_.emplace(isolate_, callback);
954 set_timeout_contexts_.emplace(isolate_, context);
955 }
956
GetTimeoutCallback()957 MaybeLocal<Function> PerIsolateData::GetTimeoutCallback() {
958 if (set_timeout_callbacks_.empty()) return MaybeLocal<Function>();
959 Local<Function> result = set_timeout_callbacks_.front().Get(isolate_);
960 set_timeout_callbacks_.pop();
961 return result;
962 }
963
GetTimeoutContext()964 MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
965 if (set_timeout_contexts_.empty()) return MaybeLocal<Context>();
966 Local<Context> result = set_timeout_contexts_.front().Get(isolate_);
967 set_timeout_contexts_.pop();
968 return result;
969 }
970
RealmScope(PerIsolateData * data)971 PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
972 data_->realm_count_ = 1;
973 data_->realm_current_ = 0;
974 data_->realm_switch_ = 0;
975 data_->realms_ = new Global<Context>[1];
976 data_->realms_[0].Reset(data_->isolate_,
977 data_->isolate_->GetEnteredContext());
978 }
979
980
~RealmScope()981 PerIsolateData::RealmScope::~RealmScope() {
982 // Drop realms to avoid keeping them alive. We don't dispose the
983 // module embedder data for the first realm here, but instead do
984 // it in RunShell or in RunMain, if not running in interactive mode
985 for (int i = 1; i < data_->realm_count_; ++i) {
986 Global<Context>& realm = data_->realms_[i];
987 if (realm.IsEmpty()) continue;
988 DisposeModuleEmbedderData(realm.Get(data_->isolate_));
989 // TODO(adamk): No need to reset manually, Globals reset when destructed.
990 realm.Reset();
991 }
992 data_->realm_count_ = 0;
993 delete[] data_->realms_;
994 // TODO(adamk): No need to reset manually, Globals reset when destructed.
995 if (!data_->realm_shared_.IsEmpty())
996 data_->realm_shared_.Reset();
997 }
998
999
RealmFind(Local<Context> context)1000 int PerIsolateData::RealmFind(Local<Context> context) {
1001 for (int i = 0; i < realm_count_; ++i) {
1002 if (realms_[i] == context) return i;
1003 }
1004 return -1;
1005 }
1006
1007
RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value> & args,int arg_offset)1008 int PerIsolateData::RealmIndexOrThrow(
1009 const v8::FunctionCallbackInfo<v8::Value>& args,
1010 int arg_offset) {
1011 if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
1012 Throw(args.GetIsolate(), "Invalid argument");
1013 return -1;
1014 }
1015 int index = args[arg_offset]
1016 ->Int32Value(args.GetIsolate()->GetCurrentContext())
1017 .FromMaybe(-1);
1018 if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
1019 Throw(args.GetIsolate(), "Invalid realm index");
1020 return -1;
1021 }
1022 return index;
1023 }
1024
1025
1026 // performance.now() returns a time stamp as double, measured in milliseconds.
1027 // When FLAG_verify_predictable mode is enabled it returns result of
1028 // v8::Platform::MonotonicallyIncreasingTime().
PerformanceNow(const v8::FunctionCallbackInfo<v8::Value> & args)1029 void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1030 if (i::FLAG_verify_predictable) {
1031 args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
1032 } else {
1033 base::TimeDelta delta =
1034 base::TimeTicks::HighResolutionNow() - kInitialTicks;
1035 args.GetReturnValue().Set(delta.InMillisecondsF());
1036 }
1037 }
1038
1039
1040 // Realm.current() returns the index of the currently active realm.
RealmCurrent(const v8::FunctionCallbackInfo<v8::Value> & args)1041 void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
1042 Isolate* isolate = args.GetIsolate();
1043 PerIsolateData* data = PerIsolateData::Get(isolate);
1044 int index = data->RealmFind(isolate->GetEnteredContext());
1045 if (index == -1) return;
1046 args.GetReturnValue().Set(index);
1047 }
1048
1049
1050 // Realm.owner(o) returns the index of the realm that created o.
RealmOwner(const v8::FunctionCallbackInfo<v8::Value> & args)1051 void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
1052 Isolate* isolate = args.GetIsolate();
1053 PerIsolateData* data = PerIsolateData::Get(isolate);
1054 if (args.Length() < 1 || !args[0]->IsObject()) {
1055 Throw(args.GetIsolate(), "Invalid argument");
1056 return;
1057 }
1058 int index = data->RealmFind(args[0]
1059 ->ToObject(isolate->GetCurrentContext())
1060 .ToLocalChecked()
1061 ->CreationContext());
1062 if (index == -1) return;
1063 args.GetReturnValue().Set(index);
1064 }
1065
1066
1067 // Realm.global(i) returns the global object of realm i.
1068 // (Note that properties of global objects cannot be read/written cross-realm.)
RealmGlobal(const v8::FunctionCallbackInfo<v8::Value> & args)1069 void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1070 PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
1071 int index = data->RealmIndexOrThrow(args, 0);
1072 if (index == -1) return;
1073 args.GetReturnValue().Set(
1074 Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1075 }
1076
CreateRealm(const v8::FunctionCallbackInfo<v8::Value> & args,int index,v8::MaybeLocal<Value> global_object)1077 MaybeLocal<Context> Shell::CreateRealm(
1078 const v8::FunctionCallbackInfo<v8::Value>& args, int index,
1079 v8::MaybeLocal<Value> global_object) {
1080 Isolate* isolate = args.GetIsolate();
1081 TryCatch try_catch(isolate);
1082 PerIsolateData* data = PerIsolateData::Get(isolate);
1083 if (index < 0) {
1084 Global<Context>* old_realms = data->realms_;
1085 index = data->realm_count_;
1086 data->realms_ = new Global<Context>[++data->realm_count_];
1087 for (int i = 0; i < index; ++i) {
1088 data->realms_[i].Reset(isolate, old_realms[i]);
1089 old_realms[i].Reset();
1090 }
1091 delete[] old_realms;
1092 }
1093 Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1094 Local<Context> context =
1095 Context::New(isolate, nullptr, global_template, global_object);
1096 DCHECK(!try_catch.HasCaught());
1097 if (context.IsEmpty()) return MaybeLocal<Context>();
1098 InitializeModuleEmbedderData(context);
1099 data->realms_[index].Reset(isolate, context);
1100 args.GetReturnValue().Set(index);
1101 return context;
1102 }
1103
DisposeRealm(const v8::FunctionCallbackInfo<v8::Value> & args,int index)1104 void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
1105 int index) {
1106 Isolate* isolate = args.GetIsolate();
1107 PerIsolateData* data = PerIsolateData::Get(isolate);
1108 DisposeModuleEmbedderData(data->realms_[index].Get(isolate));
1109 data->realms_[index].Reset();
1110 isolate->ContextDisposedNotification();
1111 isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
1112 }
1113
1114 // Realm.create() creates a new realm with a distinct security token
1115 // and returns its index.
RealmCreate(const v8::FunctionCallbackInfo<v8::Value> & args)1116 void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1117 CreateRealm(args, -1, v8::MaybeLocal<Value>());
1118 }
1119
1120 // Realm.createAllowCrossRealmAccess() creates a new realm with the same
1121 // security token as the current realm.
RealmCreateAllowCrossRealmAccess(const v8::FunctionCallbackInfo<v8::Value> & args)1122 void Shell::RealmCreateAllowCrossRealmAccess(
1123 const v8::FunctionCallbackInfo<v8::Value>& args) {
1124 Local<Context> context;
1125 if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1126 context->SetSecurityToken(
1127 args.GetIsolate()->GetEnteredContext()->GetSecurityToken());
1128 }
1129 }
1130
1131 // Realm.navigate(i) creates a new realm with a distinct security token
1132 // in place of realm i.
RealmNavigate(const v8::FunctionCallbackInfo<v8::Value> & args)1133 void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1134 Isolate* isolate = args.GetIsolate();
1135 PerIsolateData* data = PerIsolateData::Get(isolate);
1136 int index = data->RealmIndexOrThrow(args, 0);
1137 if (index == -1) return;
1138 if (index == 0 || index == data->realm_current_ ||
1139 index == data->realm_switch_) {
1140 Throw(args.GetIsolate(), "Invalid realm index");
1141 return;
1142 }
1143
1144 Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
1145 v8::MaybeLocal<Value> global_object = context->Global();
1146 DisposeRealm(args, index);
1147 CreateRealm(args, index, global_object);
1148 }
1149
1150 // Realm.dispose(i) disposes the reference to the realm i.
RealmDispose(const v8::FunctionCallbackInfo<v8::Value> & args)1151 void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1152 Isolate* isolate = args.GetIsolate();
1153 PerIsolateData* data = PerIsolateData::Get(isolate);
1154 int index = data->RealmIndexOrThrow(args, 0);
1155 if (index == -1) return;
1156 if (index == 0 ||
1157 index == data->realm_current_ || index == data->realm_switch_) {
1158 Throw(args.GetIsolate(), "Invalid realm index");
1159 return;
1160 }
1161 DisposeRealm(args, index);
1162 }
1163
1164
1165 // Realm.switch(i) switches to the realm i for consecutive interactive inputs.
RealmSwitch(const v8::FunctionCallbackInfo<v8::Value> & args)1166 void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1167 Isolate* isolate = args.GetIsolate();
1168 PerIsolateData* data = PerIsolateData::Get(isolate);
1169 int index = data->RealmIndexOrThrow(args, 0);
1170 if (index == -1) return;
1171 data->realm_switch_ = index;
1172 }
1173
1174
1175 // Realm.eval(i, s) evaluates s in realm i and returns the result.
RealmEval(const v8::FunctionCallbackInfo<v8::Value> & args)1176 void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1177 Isolate* isolate = args.GetIsolate();
1178 PerIsolateData* data = PerIsolateData::Get(isolate);
1179 int index = data->RealmIndexOrThrow(args, 0);
1180 if (index == -1) return;
1181 if (args.Length() < 2 || !args[1]->IsString()) {
1182 Throw(args.GetIsolate(), "Invalid argument");
1183 return;
1184 }
1185 ScriptCompiler::Source script_source(
1186 args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
1187 Local<UnboundScript> script;
1188 if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
1189 .ToLocal(&script)) {
1190 return;
1191 }
1192 Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1193 realm->Enter();
1194 int previous_index = data->realm_current_;
1195 data->realm_current_ = data->realm_switch_ = index;
1196 Local<Value> result;
1197 if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
1198 realm->Exit();
1199 data->realm_current_ = data->realm_switch_ = previous_index;
1200 return;
1201 }
1202 realm->Exit();
1203 data->realm_current_ = data->realm_switch_ = previous_index;
1204 args.GetReturnValue().Set(result);
1205 }
1206
1207
1208 // Realm.shared is an accessor for a single shared value across realms.
RealmSharedGet(Local<String> property,const PropertyCallbackInfo<Value> & info)1209 void Shell::RealmSharedGet(Local<String> property,
1210 const PropertyCallbackInfo<Value>& info) {
1211 Isolate* isolate = info.GetIsolate();
1212 PerIsolateData* data = PerIsolateData::Get(isolate);
1213 if (data->realm_shared_.IsEmpty()) return;
1214 info.GetReturnValue().Set(data->realm_shared_);
1215 }
1216
RealmSharedSet(Local<String> property,Local<Value> value,const PropertyCallbackInfo<void> & info)1217 void Shell::RealmSharedSet(Local<String> property,
1218 Local<Value> value,
1219 const PropertyCallbackInfo<void>& info) {
1220 Isolate* isolate = info.GetIsolate();
1221 PerIsolateData* data = PerIsolateData::Get(isolate);
1222 data->realm_shared_.Reset(isolate, value);
1223 }
1224
1225 // async_hooks.createHook() registers functions to be called for different
1226 // lifetime events of each async operation.
AsyncHooksCreateHook(const v8::FunctionCallbackInfo<v8::Value> & args)1227 void Shell::AsyncHooksCreateHook(
1228 const v8::FunctionCallbackInfo<v8::Value>& args) {
1229 Local<Object> wrap =
1230 PerIsolateData::Get(args.GetIsolate())->GetAsyncHooks()->CreateHook(args);
1231 args.GetReturnValue().Set(wrap);
1232 }
1233
1234 // async_hooks.executionAsyncId() returns the asyncId of the current execution
1235 // context.
AsyncHooksExecutionAsyncId(const v8::FunctionCallbackInfo<v8::Value> & args)1236 void Shell::AsyncHooksExecutionAsyncId(
1237 const v8::FunctionCallbackInfo<v8::Value>& args) {
1238 Isolate* isolate = args.GetIsolate();
1239 HandleScope handle_scope(isolate);
1240 args.GetReturnValue().Set(v8::Number::New(
1241 isolate,
1242 PerIsolateData::Get(isolate)->GetAsyncHooks()->GetExecutionAsyncId()));
1243 }
1244
AsyncHooksTriggerAsyncId(const v8::FunctionCallbackInfo<v8::Value> & args)1245 void Shell::AsyncHooksTriggerAsyncId(
1246 const v8::FunctionCallbackInfo<v8::Value>& args) {
1247 Isolate* isolate = args.GetIsolate();
1248 HandleScope handle_scope(isolate);
1249 args.GetReturnValue().Set(v8::Number::New(
1250 isolate,
1251 PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
1252 }
1253
WriteToFile(FILE * file,const v8::FunctionCallbackInfo<v8::Value> & args)1254 void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1255 for (int i = 0; i < args.Length(); i++) {
1256 HandleScope handle_scope(args.GetIsolate());
1257 if (i != 0) {
1258 fprintf(file, " ");
1259 }
1260
1261 // Explicitly catch potential exceptions in toString().
1262 v8::TryCatch try_catch(args.GetIsolate());
1263 Local<Value> arg = args[i];
1264 Local<String> str_obj;
1265
1266 if (arg->IsSymbol()) {
1267 arg = Local<Symbol>::Cast(arg)->Name();
1268 }
1269 if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1270 .ToLocal(&str_obj)) {
1271 try_catch.ReThrow();
1272 return;
1273 }
1274
1275 v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1276 int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1277 if (n != str.length()) {
1278 printf("Error in fwrite\n");
1279 base::OS::ExitProcess(1);
1280 }
1281 }
1282 }
1283
WriteAndFlush(FILE * file,const v8::FunctionCallbackInfo<v8::Value> & args)1284 void WriteAndFlush(FILE* file,
1285 const v8::FunctionCallbackInfo<v8::Value>& args) {
1286 WriteToFile(file, args);
1287 fprintf(file, "\n");
1288 fflush(file);
1289 }
1290
Print(const v8::FunctionCallbackInfo<v8::Value> & args)1291 void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
1292 WriteAndFlush(stdout, args);
1293 }
1294
PrintErr(const v8::FunctionCallbackInfo<v8::Value> & args)1295 void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) {
1296 WriteAndFlush(stderr, args);
1297 }
1298
Write(const v8::FunctionCallbackInfo<v8::Value> & args)1299 void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
1300 WriteToFile(stdout, args);
1301 }
1302
Read(const v8::FunctionCallbackInfo<v8::Value> & args)1303 void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1304 String::Utf8Value file(args.GetIsolate(), args[0]);
1305 if (*file == nullptr) {
1306 Throw(args.GetIsolate(), "Error loading file");
1307 return;
1308 }
1309 if (args.Length() == 2) {
1310 String::Utf8Value format(args.GetIsolate(), args[1]);
1311 if (*format && std::strcmp(*format, "binary") == 0) {
1312 ReadBuffer(args);
1313 return;
1314 }
1315 }
1316 Local<String> source = ReadFile(args.GetIsolate(), *file);
1317 if (source.IsEmpty()) {
1318 Throw(args.GetIsolate(), "Error loading file");
1319 return;
1320 }
1321 args.GetReturnValue().Set(source);
1322 }
1323
1324
ReadFromStdin(Isolate * isolate)1325 Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1326 static const int kBufferSize = 256;
1327 char buffer[kBufferSize];
1328 Local<String> accumulator =
1329 String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked();
1330 int length;
1331 while (true) {
1332 // Continue reading if the line ends with an escape '\\' or the line has
1333 // not been fully read into the buffer yet (does not end with '\n').
1334 // If fgets gets an error, just give up.
1335 char* input = nullptr;
1336 input = fgets(buffer, kBufferSize, stdin);
1337 if (input == nullptr) return Local<String>();
1338 length = static_cast<int>(strlen(buffer));
1339 if (length == 0) {
1340 return accumulator;
1341 } else if (buffer[length-1] != '\n') {
1342 accumulator = String::Concat(
1343 isolate, accumulator,
1344 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
1345 .ToLocalChecked());
1346 } else if (length > 1 && buffer[length-2] == '\\') {
1347 buffer[length-2] = '\n';
1348 accumulator =
1349 String::Concat(isolate, accumulator,
1350 String::NewFromUtf8(isolate, buffer,
1351 NewStringType::kNormal, length - 1)
1352 .ToLocalChecked());
1353 } else {
1354 return String::Concat(
1355 isolate, accumulator,
1356 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1357 length - 1)
1358 .ToLocalChecked());
1359 }
1360 }
1361 }
1362
1363
Load(const v8::FunctionCallbackInfo<v8::Value> & args)1364 void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1365 for (int i = 0; i < args.Length(); i++) {
1366 HandleScope handle_scope(args.GetIsolate());
1367 String::Utf8Value file(args.GetIsolate(), args[i]);
1368 if (*file == nullptr) {
1369 Throw(args.GetIsolate(), "Error loading file");
1370 return;
1371 }
1372 Local<String> source = ReadFile(args.GetIsolate(), *file);
1373 if (source.IsEmpty()) {
1374 Throw(args.GetIsolate(), "Error loading file");
1375 return;
1376 }
1377 if (!ExecuteString(
1378 args.GetIsolate(), source,
1379 String::NewFromUtf8(args.GetIsolate(), *file,
1380 NewStringType::kNormal)
1381 .ToLocalChecked(),
1382 kNoPrintResult,
1383 options.quiet_load ? kNoReportExceptions : kReportExceptions,
1384 kNoProcessMessageQueue)) {
1385 Throw(args.GetIsolate(), "Error executing file");
1386 return;
1387 }
1388 }
1389 }
1390
SetTimeout(const v8::FunctionCallbackInfo<v8::Value> & args)1391 void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
1392 Isolate* isolate = args.GetIsolate();
1393 args.GetReturnValue().Set(v8::Number::New(isolate, 0));
1394 if (args.Length() == 0 || !args[0]->IsFunction()) return;
1395 Local<Function> callback = Local<Function>::Cast(args[0]);
1396 Local<Context> context = isolate->GetCurrentContext();
1397 PerIsolateData::Get(isolate)->SetTimeout(callback, context);
1398 }
1399
WorkerNew(const v8::FunctionCallbackInfo<v8::Value> & args)1400 void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
1401 Isolate* isolate = args.GetIsolate();
1402 HandleScope handle_scope(isolate);
1403 if (args.Length() < 1 || !args[0]->IsString()) {
1404 Throw(args.GetIsolate(), "1st argument must be string");
1405 return;
1406 }
1407
1408 if (!args.IsConstructCall()) {
1409 Throw(args.GetIsolate(), "Worker must be constructed with new");
1410 return;
1411 }
1412
1413 {
1414 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
1415 if (workers_.size() >= kMaxWorkers) {
1416 Throw(args.GetIsolate(), "Too many workers, I won't let you create more");
1417 return;
1418 }
1419
1420 // Initialize the embedder field to nullptr; if we return early without
1421 // creating a new Worker (because the main thread is terminating) we can
1422 // early-out from the instance calls.
1423 args.Holder()->SetAlignedPointerInInternalField(0, nullptr);
1424
1425 if (!allow_new_workers_) return;
1426
1427 Worker* worker = new Worker;
1428 args.Holder()->SetAlignedPointerInInternalField(0, worker);
1429 workers_.push_back(worker);
1430
1431 String::Utf8Value script(args.GetIsolate(), args[0]);
1432 if (!*script) {
1433 Throw(args.GetIsolate(), "Can't get worker script");
1434 return;
1435 }
1436 worker->StartExecuteInThread(*script);
1437 }
1438 }
1439
1440
WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value> & args)1441 void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1442 Isolate* isolate = args.GetIsolate();
1443 HandleScope handle_scope(isolate);
1444
1445 if (args.Length() < 1) {
1446 Throw(isolate, "Invalid argument");
1447 return;
1448 }
1449
1450 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1451 if (!worker) {
1452 return;
1453 }
1454
1455 Local<Value> message = args[0];
1456 Local<Value> transfer =
1457 args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
1458 std::unique_ptr<SerializationData> data =
1459 Shell::SerializeValue(isolate, message, transfer);
1460 if (data) {
1461 worker->PostMessage(std::move(data));
1462 }
1463 }
1464
1465
WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value> & args)1466 void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1467 Isolate* isolate = args.GetIsolate();
1468 HandleScope handle_scope(isolate);
1469 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1470 if (!worker) {
1471 return;
1472 }
1473
1474 std::unique_ptr<SerializationData> data = worker->GetMessage();
1475 if (data) {
1476 Local<Value> value;
1477 if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
1478 args.GetReturnValue().Set(value);
1479 }
1480 }
1481 }
1482
1483
WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value> & args)1484 void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1485 Isolate* isolate = args.GetIsolate();
1486 HandleScope handle_scope(isolate);
1487 Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1488 if (!worker) {
1489 return;
1490 }
1491
1492 worker->Terminate();
1493 }
1494
1495
QuitOnce(v8::FunctionCallbackInfo<v8::Value> * args)1496 void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
1497 int exit_code = (*args)[0]
1498 ->Int32Value(args->GetIsolate()->GetCurrentContext())
1499 .FromMaybe(0);
1500 CleanupWorkers();
1501 args->GetIsolate()->Exit();
1502 OnExit(args->GetIsolate());
1503 base::OS::ExitProcess(exit_code);
1504 }
1505
1506
Quit(const v8::FunctionCallbackInfo<v8::Value> & args)1507 void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
1508 base::CallOnce(&quit_once_, &QuitOnce,
1509 const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
1510 }
1511
WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value> & args)1512 void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1513 SetWaitUntilDone(args.GetIsolate(), true);
1514 }
1515
NotifyDone(const v8::FunctionCallbackInfo<v8::Value> & args)1516 void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1517 SetWaitUntilDone(args.GetIsolate(), false);
1518 }
1519
Version(const v8::FunctionCallbackInfo<v8::Value> & args)1520 void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1521 args.GetReturnValue().Set(
1522 String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
1523 NewStringType::kNormal).ToLocalChecked());
1524 }
1525
1526
ReportException(Isolate * isolate,v8::TryCatch * try_catch)1527 void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
1528 HandleScope handle_scope(isolate);
1529 Local<Context> context = isolate->GetCurrentContext();
1530 bool enter_context = context.IsEmpty();
1531 if (enter_context) {
1532 context = Local<Context>::New(isolate, evaluation_context_);
1533 context->Enter();
1534 }
1535 // Converts a V8 value to a C string.
1536 auto ToCString = [](const v8::String::Utf8Value& value) {
1537 return *value ? *value : "<string conversion failed>";
1538 };
1539
1540 v8::String::Utf8Value exception(isolate, try_catch->Exception());
1541 const char* exception_string = ToCString(exception);
1542 Local<Message> message = try_catch->Message();
1543 if (message.IsEmpty()) {
1544 // V8 didn't provide any extra information about this error; just
1545 // print the exception.
1546 printf("%s\n", exception_string);
1547 } else if (message->GetScriptOrigin().Options().IsWasm()) {
1548 // Print wasm-function[(function index)]:(offset): (message).
1549 int function_index = message->GetLineNumber(context).FromJust() - 1;
1550 int offset = message->GetStartColumn(context).FromJust();
1551 printf("wasm-function[%d]:%d: %s\n", function_index, offset,
1552 exception_string);
1553 } else {
1554 // Print (filename):(line number): (message).
1555 v8::String::Utf8Value filename(isolate,
1556 message->GetScriptOrigin().ResourceName());
1557 const char* filename_string = ToCString(filename);
1558 int linenum = message->GetLineNumber(context).FromMaybe(-1);
1559 printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1560 Local<String> sourceline;
1561 if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1562 // Print line of source code.
1563 v8::String::Utf8Value sourcelinevalue(isolate, sourceline);
1564 const char* sourceline_string = ToCString(sourcelinevalue);
1565 printf("%s\n", sourceline_string);
1566 // Print wavy underline (GetUnderline is deprecated).
1567 int start = message->GetStartColumn(context).FromJust();
1568 for (int i = 0; i < start; i++) {
1569 printf(" ");
1570 }
1571 int end = message->GetEndColumn(context).FromJust();
1572 for (int i = start; i < end; i++) {
1573 printf("^");
1574 }
1575 printf("\n");
1576 }
1577 }
1578 Local<Value> stack_trace_string;
1579 if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
1580 stack_trace_string->IsString()) {
1581 v8::String::Utf8Value stack_trace(isolate,
1582 Local<String>::Cast(stack_trace_string));
1583 printf("%s\n", ToCString(stack_trace));
1584 }
1585 printf("\n");
1586 if (enter_context) context->Exit();
1587 }
1588
1589
Bind(const char * name,bool is_histogram)1590 int32_t* Counter::Bind(const char* name, bool is_histogram) {
1591 int i;
1592 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
1593 name_[i] = static_cast<char>(name[i]);
1594 name_[i] = '\0';
1595 is_histogram_ = is_histogram;
1596 return ptr();
1597 }
1598
1599
AddSample(int32_t sample)1600 void Counter::AddSample(int32_t sample) {
1601 count_++;
1602 sample_total_ += sample;
1603 }
1604
1605
CounterCollection()1606 CounterCollection::CounterCollection() {
1607 magic_number_ = 0xDEADFACE;
1608 max_counters_ = kMaxCounters;
1609 max_name_size_ = Counter::kMaxNameSize;
1610 counters_in_use_ = 0;
1611 }
1612
1613
GetNextCounter()1614 Counter* CounterCollection::GetNextCounter() {
1615 if (counters_in_use_ == kMaxCounters) return nullptr;
1616 return &counters_[counters_in_use_++];
1617 }
1618
1619
MapCounters(v8::Isolate * isolate,const char * name)1620 void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
1621 counters_file_ = base::OS::MemoryMappedFile::create(
1622 name, sizeof(CounterCollection), &local_counters_);
1623 void* memory =
1624 (counters_file_ == nullptr) ? nullptr : counters_file_->memory();
1625 if (memory == nullptr) {
1626 printf("Could not map counters file %s\n", name);
1627 base::OS::ExitProcess(1);
1628 }
1629 counters_ = static_cast<CounterCollection*>(memory);
1630 isolate->SetCounterFunction(LookupCounter);
1631 isolate->SetCreateHistogramFunction(CreateHistogram);
1632 isolate->SetAddHistogramSampleFunction(AddHistogramSample);
1633 }
1634
GetCounter(const char * name,bool is_histogram)1635 Counter* Shell::GetCounter(const char* name, bool is_histogram) {
1636 auto map_entry = counter_map_->find(name);
1637 Counter* counter =
1638 map_entry != counter_map_->end() ? map_entry->second : nullptr;
1639
1640 if (counter == nullptr) {
1641 counter = counters_->GetNextCounter();
1642 if (counter != nullptr) {
1643 (*counter_map_)[name] = counter;
1644 counter->Bind(name, is_histogram);
1645 }
1646 } else {
1647 DCHECK(counter->is_histogram() == is_histogram);
1648 }
1649 return counter;
1650 }
1651
1652
LookupCounter(const char * name)1653 int* Shell::LookupCounter(const char* name) {
1654 Counter* counter = GetCounter(name, false);
1655
1656 if (counter != nullptr) {
1657 return counter->ptr();
1658 } else {
1659 return nullptr;
1660 }
1661 }
1662
1663
CreateHistogram(const char * name,int min,int max,size_t buckets)1664 void* Shell::CreateHistogram(const char* name,
1665 int min,
1666 int max,
1667 size_t buckets) {
1668 return GetCounter(name, true);
1669 }
1670
1671
AddHistogramSample(void * histogram,int sample)1672 void Shell::AddHistogramSample(void* histogram, int sample) {
1673 Counter* counter = reinterpret_cast<Counter*>(histogram);
1674 counter->AddSample(sample);
1675 }
1676
1677 // Turn a value into a human-readable string.
Stringify(Isolate * isolate,Local<Value> value)1678 Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
1679 v8::Local<v8::Context> context =
1680 v8::Local<v8::Context>::New(isolate, evaluation_context_);
1681 if (stringify_function_.IsEmpty()) {
1682 int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
1683 i::Vector<const char> source_string =
1684 i::NativesCollection<i::D8>::GetScriptSource(source_index);
1685 i::Vector<const char> source_name =
1686 i::NativesCollection<i::D8>::GetScriptName(source_index);
1687 Local<String> source =
1688 String::NewFromUtf8(isolate, source_string.start(),
1689 NewStringType::kNormal, source_string.length())
1690 .ToLocalChecked();
1691 Local<String> name =
1692 String::NewFromUtf8(isolate, source_name.start(),
1693 NewStringType::kNormal, source_name.length())
1694 .ToLocalChecked();
1695 ScriptOrigin origin(name);
1696 Local<Script> script =
1697 Script::Compile(context, source, &origin).ToLocalChecked();
1698 stringify_function_.Reset(
1699 isolate, script->Run(context).ToLocalChecked().As<Function>());
1700 }
1701 Local<Function> fun = Local<Function>::New(isolate, stringify_function_);
1702 Local<Value> argv[1] = {value};
1703 v8::TryCatch try_catch(isolate);
1704 MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
1705 if (result.IsEmpty()) return String::Empty(isolate);
1706 return result.ToLocalChecked().As<String>();
1707 }
1708
1709
CreateGlobalTemplate(Isolate * isolate)1710 Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
1711 Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
1712 global_template->Set(
1713 String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
1714 .ToLocalChecked(),
1715 FunctionTemplate::New(isolate, Print));
1716 global_template->Set(
1717 String::NewFromUtf8(isolate, "printErr", NewStringType::kNormal)
1718 .ToLocalChecked(),
1719 FunctionTemplate::New(isolate, PrintErr));
1720 global_template->Set(
1721 String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
1722 .ToLocalChecked(),
1723 FunctionTemplate::New(isolate, Write));
1724 global_template->Set(
1725 String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
1726 .ToLocalChecked(),
1727 FunctionTemplate::New(isolate, Read));
1728 global_template->Set(
1729 String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
1730 .ToLocalChecked(),
1731 FunctionTemplate::New(isolate, ReadBuffer));
1732 global_template->Set(
1733 String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
1734 .ToLocalChecked(),
1735 FunctionTemplate::New(isolate, ReadLine));
1736 global_template->Set(
1737 String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
1738 .ToLocalChecked(),
1739 FunctionTemplate::New(isolate, Load));
1740 global_template->Set(
1741 String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal)
1742 .ToLocalChecked(),
1743 FunctionTemplate::New(isolate, SetTimeout));
1744 // Some Emscripten-generated code tries to call 'quit', which in turn would
1745 // call C's exit(). This would lead to memory leaks, because there is no way
1746 // we can terminate cleanly then, so we need a way to hide 'quit'.
1747 if (!options.omit_quit) {
1748 global_template->Set(
1749 String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
1750 .ToLocalChecked(),
1751 FunctionTemplate::New(isolate, Quit));
1752 }
1753 Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
1754 global_template->Set(
1755 String::NewFromUtf8(isolate, "testRunner", NewStringType::kNormal)
1756 .ToLocalChecked(),
1757 test_template);
1758 test_template->Set(
1759 String::NewFromUtf8(isolate, "notifyDone", NewStringType::kNormal)
1760 .ToLocalChecked(),
1761 FunctionTemplate::New(isolate, NotifyDone));
1762 test_template->Set(
1763 String::NewFromUtf8(isolate, "waitUntilDone", NewStringType::kNormal)
1764 .ToLocalChecked(),
1765 FunctionTemplate::New(isolate, WaitUntilDone));
1766 global_template->Set(
1767 String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
1768 .ToLocalChecked(),
1769 FunctionTemplate::New(isolate, Version));
1770 global_template->Set(
1771 Symbol::GetToStringTag(isolate),
1772 String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1773 .ToLocalChecked());
1774
1775 // Bind the Realm object.
1776 Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
1777 realm_template->Set(
1778 String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
1779 .ToLocalChecked(),
1780 FunctionTemplate::New(isolate, RealmCurrent));
1781 realm_template->Set(
1782 String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
1783 .ToLocalChecked(),
1784 FunctionTemplate::New(isolate, RealmOwner));
1785 realm_template->Set(
1786 String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1787 .ToLocalChecked(),
1788 FunctionTemplate::New(isolate, RealmGlobal));
1789 realm_template->Set(
1790 String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
1791 .ToLocalChecked(),
1792 FunctionTemplate::New(isolate, RealmCreate));
1793 realm_template->Set(
1794 String::NewFromUtf8(isolate, "createAllowCrossRealmAccess",
1795 NewStringType::kNormal)
1796 .ToLocalChecked(),
1797 FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
1798 realm_template->Set(
1799 String::NewFromUtf8(isolate, "navigate", NewStringType::kNormal)
1800 .ToLocalChecked(),
1801 FunctionTemplate::New(isolate, RealmNavigate));
1802 realm_template->Set(
1803 String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
1804 .ToLocalChecked(),
1805 FunctionTemplate::New(isolate, RealmDispose));
1806 realm_template->Set(
1807 String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
1808 .ToLocalChecked(),
1809 FunctionTemplate::New(isolate, RealmSwitch));
1810 realm_template->Set(
1811 String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
1812 .ToLocalChecked(),
1813 FunctionTemplate::New(isolate, RealmEval));
1814 realm_template->SetAccessor(
1815 String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
1816 .ToLocalChecked(),
1817 RealmSharedGet, RealmSharedSet);
1818 global_template->Set(
1819 String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
1820 .ToLocalChecked(),
1821 realm_template);
1822
1823 Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
1824 performance_template->Set(
1825 String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
1826 .ToLocalChecked(),
1827 FunctionTemplate::New(isolate, PerformanceNow));
1828 global_template->Set(
1829 String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
1830 .ToLocalChecked(),
1831 performance_template);
1832
1833 Local<FunctionTemplate> worker_fun_template =
1834 FunctionTemplate::New(isolate, WorkerNew);
1835 Local<Signature> worker_signature =
1836 Signature::New(isolate, worker_fun_template);
1837 worker_fun_template->SetClassName(
1838 String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1839 .ToLocalChecked());
1840 worker_fun_template->ReadOnlyPrototype();
1841 worker_fun_template->PrototypeTemplate()->Set(
1842 String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
1843 .ToLocalChecked(),
1844 FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
1845 worker_signature));
1846 worker_fun_template->PrototypeTemplate()->Set(
1847 String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
1848 .ToLocalChecked(),
1849 FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
1850 worker_signature));
1851 worker_fun_template->PrototypeTemplate()->Set(
1852 String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
1853 .ToLocalChecked(),
1854 FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
1855 worker_signature));
1856 worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
1857 global_template->Set(
1858 String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1859 .ToLocalChecked(),
1860 worker_fun_template);
1861
1862 Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
1863 AddOSMethods(isolate, os_templ);
1864 global_template->Set(
1865 String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
1866 .ToLocalChecked(),
1867 os_templ);
1868
1869 if (i::FLAG_expose_async_hooks) {
1870 Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
1871 async_hooks_templ->Set(
1872 String::NewFromUtf8(isolate, "createHook", NewStringType::kNormal)
1873 .ToLocalChecked(),
1874 FunctionTemplate::New(isolate, AsyncHooksCreateHook));
1875 async_hooks_templ->Set(
1876 String::NewFromUtf8(isolate, "executionAsyncId", NewStringType::kNormal)
1877 .ToLocalChecked(),
1878 FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
1879 async_hooks_templ->Set(
1880 String::NewFromUtf8(isolate, "triggerAsyncId", NewStringType::kNormal)
1881 .ToLocalChecked(),
1882 FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
1883 global_template->Set(
1884 String::NewFromUtf8(isolate, "async_hooks", NewStringType::kNormal)
1885 .ToLocalChecked(),
1886 async_hooks_templ);
1887 }
1888
1889 return global_template;
1890 }
1891
PrintNonErrorsMessageCallback(Local<Message> message,Local<Value> error)1892 static void PrintNonErrorsMessageCallback(Local<Message> message,
1893 Local<Value> error) {
1894 // Nothing to do here for errors, exceptions thrown up to the shell will be
1895 // reported
1896 // separately by {Shell::ReportException} after they are caught.
1897 // Do print other kinds of messages.
1898 switch (message->ErrorLevel()) {
1899 case v8::Isolate::kMessageWarning:
1900 case v8::Isolate::kMessageLog:
1901 case v8::Isolate::kMessageInfo:
1902 case v8::Isolate::kMessageDebug: {
1903 break;
1904 }
1905
1906 case v8::Isolate::kMessageError: {
1907 // Ignore errors, printed elsewhere.
1908 return;
1909 }
1910
1911 default: {
1912 UNREACHABLE();
1913 break;
1914 }
1915 }
1916 // Converts a V8 value to a C string.
1917 auto ToCString = [](const v8::String::Utf8Value& value) {
1918 return *value ? *value : "<string conversion failed>";
1919 };
1920 Isolate* isolate = Isolate::GetCurrent();
1921 v8::String::Utf8Value msg(isolate, message->Get());
1922 const char* msg_string = ToCString(msg);
1923 // Print (filename):(line number): (message).
1924 v8::String::Utf8Value filename(isolate,
1925 message->GetScriptOrigin().ResourceName());
1926 const char* filename_string = ToCString(filename);
1927 Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
1928 int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
1929 printf("%s:%i: %s\n", filename_string, linenum, msg_string);
1930 }
1931
Initialize(Isolate * isolate)1932 void Shell::Initialize(Isolate* isolate) {
1933 // Set up counters
1934 if (i::StrLength(i::FLAG_map_counters) != 0)
1935 MapCounters(isolate, i::FLAG_map_counters);
1936 // Disable default message reporting.
1937 isolate->AddMessageListenerWithErrorLevel(
1938 PrintNonErrorsMessageCallback,
1939 v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
1940 v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
1941 v8::Isolate::kMessageLog);
1942 }
1943
1944
CreateEvaluationContext(Isolate * isolate)1945 Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
1946 // This needs to be a critical section since this is not thread-safe
1947 base::LockGuard<base::Mutex> lock_guard(context_mutex_.Pointer());
1948 // Initialize the global objects
1949 Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1950 EscapableHandleScope handle_scope(isolate);
1951 Local<Context> context = Context::New(isolate, nullptr, global_template);
1952 DCHECK(!context.IsEmpty());
1953 InitializeModuleEmbedderData(context);
1954 Context::Scope scope(context);
1955
1956 i::Factory* factory = reinterpret_cast<i::Isolate*>(isolate)->factory();
1957 i::JSArguments js_args = i::FLAG_js_arguments;
1958 i::Handle<i::FixedArray> arguments_array =
1959 factory->NewFixedArray(js_args.argc);
1960 for (int j = 0; j < js_args.argc; j++) {
1961 i::Handle<i::String> arg =
1962 factory->NewStringFromUtf8(i::CStrVector(js_args[j])).ToHandleChecked();
1963 arguments_array->set(j, *arg);
1964 }
1965 i::Handle<i::JSArray> arguments_jsarray =
1966 factory->NewJSArrayWithElements(arguments_array);
1967 context->Global()
1968 ->Set(context,
1969 String::NewFromUtf8(isolate, "arguments", NewStringType::kNormal)
1970 .ToLocalChecked(),
1971 Utils::ToLocal(arguments_jsarray))
1972 .FromJust();
1973 return handle_scope.Escape(context);
1974 }
1975
1976 struct CounterAndKey {
1977 Counter* counter;
1978 const char* key;
1979 };
1980
1981
operator <(const CounterAndKey & lhs,const CounterAndKey & rhs)1982 inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
1983 return strcmp(lhs.key, rhs.key) < 0;
1984 }
1985
WriteIgnitionDispatchCountersFile(v8::Isolate * isolate)1986 void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
1987 HandleScope handle_scope(isolate);
1988 Local<Context> context = Context::New(isolate);
1989 Context::Scope context_scope(context);
1990
1991 Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
1992 ->interpreter()
1993 ->GetDispatchCountersObject();
1994 std::ofstream dispatch_counters_stream(
1995 i::FLAG_trace_ignition_dispatches_output_file);
1996 dispatch_counters_stream << *String::Utf8Value(
1997 isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
1998 }
1999
2000 namespace {
LineFromOffset(Local<debug::Script> script,int offset)2001 int LineFromOffset(Local<debug::Script> script, int offset) {
2002 debug::Location location = script->GetSourceLocation(offset);
2003 return location.GetLineNumber();
2004 }
2005
WriteLcovDataForRange(std::vector<uint32_t> & lines,int start_line,int end_line,uint32_t count)2006 void WriteLcovDataForRange(std::vector<uint32_t>& lines, int start_line,
2007 int end_line, uint32_t count) {
2008 // Ensure space in the array.
2009 lines.resize(std::max(static_cast<size_t>(end_line + 1), lines.size()), 0);
2010 // Boundary lines could be shared between two functions with different
2011 // invocation counts. Take the maximum.
2012 lines[start_line] = std::max(lines[start_line], count);
2013 lines[end_line] = std::max(lines[end_line], count);
2014 // Invocation counts for non-boundary lines are overwritten.
2015 for (int k = start_line + 1; k < end_line; k++) lines[k] = count;
2016 }
2017
WriteLcovDataForNamedRange(std::ostream & sink,std::vector<uint32_t> & lines,std::string name,int start_line,int end_line,uint32_t count)2018 void WriteLcovDataForNamedRange(std::ostream& sink,
2019 std::vector<uint32_t>& lines, std::string name,
2020 int start_line, int end_line, uint32_t count) {
2021 WriteLcovDataForRange(lines, start_line, end_line, count);
2022 sink << "FN:" << start_line + 1 << "," << name << std::endl;
2023 sink << "FNDA:" << count << "," << name << std::endl;
2024 }
2025 } // namespace
2026
2027 // Write coverage data in LCOV format. See man page for geninfo(1).
WriteLcovData(v8::Isolate * isolate,const char * file)2028 void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
2029 if (!file) return;
2030 HandleScope handle_scope(isolate);
2031 debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2032 std::ofstream sink(file, std::ofstream::app);
2033 for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2034 debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
2035 Local<debug::Script> script = script_data.GetScript();
2036 // Skip unnamed scripts.
2037 Local<String> name;
2038 if (!script->Name().ToLocal(&name)) continue;
2039 std::string file_name = ToSTLString(isolate, name);
2040 // Skip scripts not backed by a file.
2041 if (!std::ifstream(file_name).good()) continue;
2042 sink << "SF:";
2043 sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
2044 std::vector<uint32_t> lines;
2045 for (size_t j = 0; j < script_data.FunctionCount(); j++) {
2046 debug::Coverage::FunctionData function_data =
2047 script_data.GetFunctionData(j);
2048
2049 // Write function stats.
2050 {
2051 debug::Location start =
2052 script->GetSourceLocation(function_data.StartOffset());
2053 debug::Location end =
2054 script->GetSourceLocation(function_data.EndOffset());
2055 int start_line = start.GetLineNumber();
2056 int end_line = end.GetLineNumber();
2057 uint32_t count = function_data.Count();
2058
2059 Local<String> name;
2060 std::stringstream name_stream;
2061 if (function_data.Name().ToLocal(&name)) {
2062 name_stream << ToSTLString(isolate, name);
2063 } else {
2064 name_stream << "<" << start_line + 1 << "-";
2065 name_stream << start.GetColumnNumber() << ">";
2066 }
2067
2068 WriteLcovDataForNamedRange(sink, lines, name_stream.str(), start_line,
2069 end_line, count);
2070 }
2071
2072 // Process inner blocks.
2073 for (size_t k = 0; k < function_data.BlockCount(); k++) {
2074 debug::Coverage::BlockData block_data = function_data.GetBlockData(k);
2075 int start_line = LineFromOffset(script, block_data.StartOffset());
2076 int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2077 uint32_t count = block_data.Count();
2078 WriteLcovDataForRange(lines, start_line, end_line, count);
2079 }
2080 }
2081 // Write per-line coverage. LCOV uses 1-based line numbers.
2082 for (size_t i = 0; i < lines.size(); i++) {
2083 sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
2084 }
2085 sink << "end_of_record" << std::endl;
2086 }
2087 }
2088
OnExit(v8::Isolate * isolate)2089 void Shell::OnExit(v8::Isolate* isolate) {
2090 // Dump basic block profiling data.
2091 if (i::FLAG_turbo_profiling) {
2092 i::BasicBlockProfiler* profiler = i::BasicBlockProfiler::Get();
2093 i::StdoutStream{} << *profiler;
2094 }
2095 isolate->Dispose();
2096
2097 if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2098 const int number_of_counters = static_cast<int>(counter_map_->size());
2099 CounterAndKey* counters = new CounterAndKey[number_of_counters];
2100 int j = 0;
2101 for (auto map_entry : *counter_map_) {
2102 counters[j].counter = map_entry.second;
2103 counters[j].key = map_entry.first;
2104 j++;
2105 }
2106 std::sort(counters, counters + number_of_counters);
2107
2108 if (i::FLAG_dump_counters_nvp) {
2109 // Dump counters as name-value pairs.
2110 for (j = 0; j < number_of_counters; j++) {
2111 Counter* counter = counters[j].counter;
2112 const char* key = counters[j].key;
2113 if (counter->is_histogram()) {
2114 printf("\"c:%s\"=%i\n", key, counter->count());
2115 printf("\"t:%s\"=%i\n", key, counter->sample_total());
2116 } else {
2117 printf("\"%s\"=%i\n", key, counter->count());
2118 }
2119 }
2120 } else {
2121 // Dump counters in formatted boxes.
2122 printf(
2123 "+----------------------------------------------------------------+"
2124 "-------------+\n");
2125 printf(
2126 "| Name |"
2127 " Value |\n");
2128 printf(
2129 "+----------------------------------------------------------------+"
2130 "-------------+\n");
2131 for (j = 0; j < number_of_counters; j++) {
2132 Counter* counter = counters[j].counter;
2133 const char* key = counters[j].key;
2134 if (counter->is_histogram()) {
2135 printf("| c:%-60s | %11i |\n", key, counter->count());
2136 printf("| t:%-60s | %11i |\n", key, counter->sample_total());
2137 } else {
2138 printf("| %-62s | %11i |\n", key, counter->count());
2139 }
2140 }
2141 printf(
2142 "+----------------------------------------------------------------+"
2143 "-------------+\n");
2144 }
2145 delete [] counters;
2146 }
2147
2148 delete counters_file_;
2149 delete counter_map_;
2150 }
2151
2152
FOpen(const char * path,const char * mode)2153 static FILE* FOpen(const char* path, const char* mode) {
2154 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2155 FILE* result;
2156 if (fopen_s(&result, path, mode) == 0) {
2157 return result;
2158 } else {
2159 return nullptr;
2160 }
2161 #else
2162 FILE* file = fopen(path, mode);
2163 if (file == nullptr) return nullptr;
2164 struct stat file_stat;
2165 if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2166 bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
2167 if (is_regular_file) return file;
2168 fclose(file);
2169 return nullptr;
2170 #endif
2171 }
2172
ReadChars(const char * name,int * size_out)2173 static char* ReadChars(const char* name, int* size_out) {
2174 if (Shell::options.read_from_tcp_port >= 0) {
2175 return Shell::ReadCharsFromTcpPort(name, size_out);
2176 }
2177
2178 FILE* file = FOpen(name, "rb");
2179 if (file == nullptr) return nullptr;
2180
2181 fseek(file, 0, SEEK_END);
2182 size_t size = ftell(file);
2183 rewind(file);
2184
2185 char* chars = new char[size + 1];
2186 chars[size] = '\0';
2187 for (size_t i = 0; i < size;) {
2188 i += fread(&chars[i], 1, size - i, file);
2189 if (ferror(file)) {
2190 fclose(file);
2191 delete[] chars;
2192 return nullptr;
2193 }
2194 }
2195 fclose(file);
2196 *size_out = static_cast<int>(size);
2197 return chars;
2198 }
2199
2200
2201 struct DataAndPersistent {
2202 uint8_t* data;
2203 int byte_length;
2204 Global<ArrayBuffer> handle;
2205 };
2206
2207
ReadBufferWeakCallback(const v8::WeakCallbackInfo<DataAndPersistent> & data)2208 static void ReadBufferWeakCallback(
2209 const v8::WeakCallbackInfo<DataAndPersistent>& data) {
2210 int byte_length = data.GetParameter()->byte_length;
2211 data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
2212 -static_cast<intptr_t>(byte_length));
2213
2214 delete[] data.GetParameter()->data;
2215 data.GetParameter()->handle.Reset();
2216 delete data.GetParameter();
2217 }
2218
2219
ReadBuffer(const v8::FunctionCallbackInfo<v8::Value> & args)2220 void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
2221 static_assert(sizeof(char) == sizeof(uint8_t),
2222 "char and uint8_t should both have 1 byte");
2223 Isolate* isolate = args.GetIsolate();
2224 String::Utf8Value filename(isolate, args[0]);
2225 int length;
2226 if (*filename == nullptr) {
2227 Throw(isolate, "Error loading file");
2228 return;
2229 }
2230
2231 DataAndPersistent* data = new DataAndPersistent;
2232 data->data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
2233 if (data->data == nullptr) {
2234 delete data;
2235 Throw(isolate, "Error reading file");
2236 return;
2237 }
2238 data->byte_length = length;
2239 Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
2240 data->handle.Reset(isolate, buffer);
2241 data->handle.SetWeak(data, ReadBufferWeakCallback,
2242 v8::WeakCallbackType::kParameter);
2243 isolate->AdjustAmountOfExternalAllocatedMemory(length);
2244
2245 args.GetReturnValue().Set(buffer);
2246 }
2247
2248 // Reads a file into a v8 string.
ReadFile(Isolate * isolate,const char * name)2249 Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2250 int size = 0;
2251 char* chars = ReadChars(name, &size);
2252 if (chars == nullptr) return Local<String>();
2253 Local<String> result;
2254 if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
2255 String::ExternalOneByteStringResource* resource =
2256 new ExternalOwningOneByteStringResource(
2257 std::unique_ptr<const char[]>(chars), size);
2258 result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
2259 } else {
2260 result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
2261 .ToLocalChecked();
2262 delete[] chars;
2263 }
2264 return result;
2265 }
2266
2267
RunShell(Isolate * isolate)2268 void Shell::RunShell(Isolate* isolate) {
2269 HandleScope outer_scope(isolate);
2270 v8::Local<v8::Context> context =
2271 v8::Local<v8::Context>::New(isolate, evaluation_context_);
2272 v8::Context::Scope context_scope(context);
2273 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2274 Local<String> name =
2275 String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
2276 .ToLocalChecked();
2277 printf("V8 version %s\n", V8::GetVersion());
2278 while (true) {
2279 HandleScope inner_scope(isolate);
2280 printf("d8> ");
2281 Local<String> input = Shell::ReadFromStdin(isolate);
2282 if (input.IsEmpty()) break;
2283 ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
2284 kProcessMessageQueue);
2285 }
2286 printf("\n");
2287 // We need to explicitly clean up the module embedder data for
2288 // the interative shell context.
2289 DisposeModuleEmbedderData(context);
2290 }
2291
2292 class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
2293 public:
InspectorFrontend(Local<Context> context)2294 explicit InspectorFrontend(Local<Context> context) {
2295 isolate_ = context->GetIsolate();
2296 context_.Reset(isolate_, context);
2297 }
2298 virtual ~InspectorFrontend() = default;
2299
2300 private:
sendResponse(int callId,std::unique_ptr<v8_inspector::StringBuffer> message)2301 void sendResponse(
2302 int callId,
2303 std::unique_ptr<v8_inspector::StringBuffer> message) override {
2304 Send(message->string());
2305 }
sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)2306 void sendNotification(
2307 std::unique_ptr<v8_inspector::StringBuffer> message) override {
2308 Send(message->string());
2309 }
flushProtocolNotifications()2310 void flushProtocolNotifications() override {}
2311
Send(const v8_inspector::StringView & string)2312 void Send(const v8_inspector::StringView& string) {
2313 v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2314 int length = static_cast<int>(string.length());
2315 DCHECK_LT(length, v8::String::kMaxLength);
2316 Local<String> message =
2317 (string.is8Bit()
2318 ? v8::String::NewFromOneByte(
2319 isolate_,
2320 reinterpret_cast<const uint8_t*>(string.characters8()),
2321 v8::NewStringType::kNormal, length)
2322 : v8::String::NewFromTwoByte(
2323 isolate_,
2324 reinterpret_cast<const uint16_t*>(string.characters16()),
2325 v8::NewStringType::kNormal, length))
2326 .ToLocalChecked();
2327 Local<String> callback_name =
2328 v8::String::NewFromUtf8(isolate_, "receive", v8::NewStringType::kNormal)
2329 .ToLocalChecked();
2330 Local<Context> context = context_.Get(isolate_);
2331 Local<Value> callback =
2332 context->Global()->Get(context, callback_name).ToLocalChecked();
2333 if (callback->IsFunction()) {
2334 v8::TryCatch try_catch(isolate_);
2335 Local<Value> args[] = {message};
2336 USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 1,
2337 args));
2338 #ifdef DEBUG
2339 if (try_catch.HasCaught()) {
2340 Local<Object> exception = Local<Object>::Cast(try_catch.Exception());
2341 Local<String> key = v8::String::NewFromUtf8(isolate_, "message",
2342 v8::NewStringType::kNormal)
2343 .ToLocalChecked();
2344 Local<String> expected =
2345 v8::String::NewFromUtf8(isolate_,
2346 "Maximum call stack size exceeded",
2347 v8::NewStringType::kNormal)
2348 .ToLocalChecked();
2349 Local<Value> value = exception->Get(context, key).ToLocalChecked();
2350 DCHECK(value->StrictEquals(expected));
2351 }
2352 #endif
2353 }
2354 }
2355
2356 Isolate* isolate_;
2357 Global<Context> context_;
2358 };
2359
2360 class InspectorClient : public v8_inspector::V8InspectorClient {
2361 public:
InspectorClient(Local<Context> context,bool connect)2362 InspectorClient(Local<Context> context, bool connect) {
2363 if (!connect) return;
2364 isolate_ = context->GetIsolate();
2365 channel_.reset(new InspectorFrontend(context));
2366 inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
2367 session_ =
2368 inspector_->connect(1, channel_.get(), v8_inspector::StringView());
2369 context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
2370 inspector_->contextCreated(v8_inspector::V8ContextInfo(
2371 context, kContextGroupId, v8_inspector::StringView()));
2372
2373 Local<Value> function =
2374 FunctionTemplate::New(isolate_, SendInspectorMessage)
2375 ->GetFunction(context)
2376 .ToLocalChecked();
2377 Local<String> function_name =
2378 String::NewFromUtf8(isolate_, "send", NewStringType::kNormal)
2379 .ToLocalChecked();
2380 CHECK(context->Global()->Set(context, function_name, function).FromJust());
2381
2382 context_.Reset(isolate_, context);
2383 }
2384
2385 private:
GetSession(Local<Context> context)2386 static v8_inspector::V8InspectorSession* GetSession(Local<Context> context) {
2387 InspectorClient* inspector_client = static_cast<InspectorClient*>(
2388 context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
2389 return inspector_client->session_.get();
2390 }
2391
ensureDefaultContextInGroup(int group_id)2392 Local<Context> ensureDefaultContextInGroup(int group_id) override {
2393 DCHECK(isolate_);
2394 DCHECK_EQ(kContextGroupId, group_id);
2395 return context_.Get(isolate_);
2396 }
2397
SendInspectorMessage(const v8::FunctionCallbackInfo<v8::Value> & args)2398 static void SendInspectorMessage(
2399 const v8::FunctionCallbackInfo<v8::Value>& args) {
2400 Isolate* isolate = args.GetIsolate();
2401 v8::HandleScope handle_scope(isolate);
2402 Local<Context> context = isolate->GetCurrentContext();
2403 args.GetReturnValue().Set(Undefined(isolate));
2404 Local<String> message = args[0]->ToString(context).ToLocalChecked();
2405 v8_inspector::V8InspectorSession* session =
2406 InspectorClient::GetSession(context);
2407 int length = message->Length();
2408 std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
2409 message->Write(isolate, buffer.get(), 0, length);
2410 v8_inspector::StringView message_view(buffer.get(), length);
2411 session->dispatchProtocolMessage(message_view);
2412 args.GetReturnValue().Set(True(isolate));
2413 }
2414
2415 static const int kContextGroupId = 1;
2416
2417 std::unique_ptr<v8_inspector::V8Inspector> inspector_;
2418 std::unique_ptr<v8_inspector::V8InspectorSession> session_;
2419 std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
2420 Global<Context> context_;
2421 Isolate* isolate_;
2422 };
2423
~SourceGroup()2424 SourceGroup::~SourceGroup() {
2425 delete thread_;
2426 thread_ = nullptr;
2427 }
2428
ends_with(const char * input,const char * suffix)2429 bool ends_with(const char* input, const char* suffix) {
2430 size_t input_length = strlen(input);
2431 size_t suffix_length = strlen(suffix);
2432 if (suffix_length <= input_length) {
2433 return strcmp(input + input_length - suffix_length, suffix) == 0;
2434 }
2435 return false;
2436 }
2437
Execute(Isolate * isolate)2438 void SourceGroup::Execute(Isolate* isolate) {
2439 bool exception_was_thrown = false;
2440 for (int i = begin_offset_; i < end_offset_; ++i) {
2441 const char* arg = argv_[i];
2442 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
2443 // Execute argument given to -e option directly.
2444 HandleScope handle_scope(isolate);
2445 Local<String> file_name =
2446 String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2447 .ToLocalChecked();
2448 Local<String> source =
2449 String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
2450 .ToLocalChecked();
2451 Shell::set_script_executed();
2452 if (!Shell::ExecuteString(isolate, source, file_name,
2453 Shell::kNoPrintResult, Shell::kReportExceptions,
2454 Shell::kNoProcessMessageQueue)) {
2455 exception_was_thrown = true;
2456 break;
2457 }
2458 ++i;
2459 continue;
2460 } else if (ends_with(arg, ".mjs")) {
2461 Shell::set_script_executed();
2462 if (!Shell::ExecuteModule(isolate, arg)) {
2463 exception_was_thrown = true;
2464 break;
2465 }
2466 continue;
2467 } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
2468 // Treat the next file as a module.
2469 arg = argv_[++i];
2470 Shell::set_script_executed();
2471 if (!Shell::ExecuteModule(isolate, arg)) {
2472 exception_was_thrown = true;
2473 break;
2474 }
2475 continue;
2476 } else if (arg[0] == '-') {
2477 // Ignore other options. They have been parsed already.
2478 continue;
2479 }
2480
2481 // Use all other arguments as names of files to load and run.
2482 HandleScope handle_scope(isolate);
2483 Local<String> file_name =
2484 String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
2485 .ToLocalChecked();
2486 Local<String> source = ReadFile(isolate, arg);
2487 if (source.IsEmpty()) {
2488 printf("Error reading '%s'\n", arg);
2489 base::OS::ExitProcess(1);
2490 }
2491 Shell::set_script_executed();
2492 if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
2493 Shell::kReportExceptions,
2494 Shell::kProcessMessageQueue)) {
2495 exception_was_thrown = true;
2496 break;
2497 }
2498 }
2499 if (exception_was_thrown != Shell::options.expected_to_throw) {
2500 base::OS::ExitProcess(1);
2501 }
2502 }
2503
ReadFile(Isolate * isolate,const char * name)2504 Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
2505 return Shell::ReadFile(isolate, name);
2506 }
2507
IsolateThread(SourceGroup * group)2508 SourceGroup::IsolateThread::IsolateThread(SourceGroup* group)
2509 : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {}
2510
ExecuteInThread()2511 void SourceGroup::ExecuteInThread() {
2512 Isolate::CreateParams create_params;
2513 create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2514 Isolate* isolate = Isolate::New(create_params);
2515 isolate->SetHostImportModuleDynamicallyCallback(
2516 Shell::HostImportModuleDynamically);
2517 isolate->SetHostInitializeImportMetaObjectCallback(
2518 Shell::HostInitializeImportMetaObject);
2519 Shell::SetWaitUntilDone(isolate, false);
2520 D8Console console(isolate);
2521 debug::SetConsoleDelegate(isolate, &console);
2522 for (int i = 0; i < Shell::options.stress_runs; ++i) {
2523 next_semaphore_.Wait();
2524 {
2525 Isolate::Scope iscope(isolate);
2526 PerIsolateData data(isolate);
2527 {
2528 HandleScope scope(isolate);
2529 Local<Context> context = Shell::CreateEvaluationContext(isolate);
2530 {
2531 Context::Scope cscope(context);
2532 InspectorClient inspector_client(context,
2533 Shell::options.enable_inspector);
2534 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2535 Execute(isolate);
2536 Shell::CompleteMessageLoop(isolate);
2537 }
2538 DisposeModuleEmbedderData(context);
2539 }
2540 Shell::CollectGarbage(isolate);
2541 }
2542 done_semaphore_.Signal();
2543 }
2544
2545 isolate->Dispose();
2546 }
2547
2548
StartExecuteInThread()2549 void SourceGroup::StartExecuteInThread() {
2550 if (thread_ == nullptr) {
2551 thread_ = new IsolateThread(this);
2552 thread_->Start();
2553 }
2554 next_semaphore_.Signal();
2555 }
2556
2557
WaitForThread()2558 void SourceGroup::WaitForThread() {
2559 if (thread_ == nullptr) return;
2560 done_semaphore_.Wait();
2561 }
2562
2563
JoinThread()2564 void SourceGroup::JoinThread() {
2565 if (thread_ == nullptr) return;
2566 thread_->Join();
2567 }
2568
~ExternalizedContents()2569 ExternalizedContents::~ExternalizedContents() {
2570 if (data_ != nullptr) {
2571 deleter_(data_, length_, deleter_data_);
2572 }
2573 }
2574
Enqueue(std::unique_ptr<SerializationData> data)2575 void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
2576 base::LockGuard<base::Mutex> lock_guard(&mutex_);
2577 data_.push_back(std::move(data));
2578 }
2579
Dequeue(std::unique_ptr<SerializationData> * out_data)2580 bool SerializationDataQueue::Dequeue(
2581 std::unique_ptr<SerializationData>* out_data) {
2582 out_data->reset();
2583 base::LockGuard<base::Mutex> lock_guard(&mutex_);
2584 if (data_.empty()) return false;
2585 *out_data = std::move(data_[0]);
2586 data_.erase(data_.begin());
2587 return true;
2588 }
2589
2590
IsEmpty()2591 bool SerializationDataQueue::IsEmpty() {
2592 base::LockGuard<base::Mutex> lock_guard(&mutex_);
2593 return data_.empty();
2594 }
2595
2596
Clear()2597 void SerializationDataQueue::Clear() {
2598 base::LockGuard<base::Mutex> lock_guard(&mutex_);
2599 data_.clear();
2600 }
2601
Worker()2602 Worker::Worker()
2603 : in_semaphore_(0),
2604 out_semaphore_(0),
2605 thread_(nullptr),
2606 script_(nullptr),
2607 running_(false) {}
2608
~Worker()2609 Worker::~Worker() {
2610 delete thread_;
2611 thread_ = nullptr;
2612 delete[] script_;
2613 script_ = nullptr;
2614 in_queue_.Clear();
2615 out_queue_.Clear();
2616 }
2617
2618
StartExecuteInThread(const char * script)2619 void Worker::StartExecuteInThread(const char* script) {
2620 running_ = true;
2621 script_ = i::StrDup(script);
2622 thread_ = new WorkerThread(this);
2623 thread_->Start();
2624 }
2625
PostMessage(std::unique_ptr<SerializationData> data)2626 void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
2627 in_queue_.Enqueue(std::move(data));
2628 in_semaphore_.Signal();
2629 }
2630
GetMessage()2631 std::unique_ptr<SerializationData> Worker::GetMessage() {
2632 std::unique_ptr<SerializationData> result;
2633 while (!out_queue_.Dequeue(&result)) {
2634 // If the worker is no longer running, and there are no messages in the
2635 // queue, don't expect any more messages from it.
2636 if (!base::Relaxed_Load(&running_)) break;
2637 out_semaphore_.Wait();
2638 }
2639 return result;
2640 }
2641
2642
Terminate()2643 void Worker::Terminate() {
2644 base::Relaxed_Store(&running_, false);
2645 // Post nullptr to wake the Worker thread message loop, and tell it to stop
2646 // running.
2647 PostMessage(nullptr);
2648 }
2649
2650
WaitForThread()2651 void Worker::WaitForThread() {
2652 Terminate();
2653 thread_->Join();
2654 }
2655
2656
ExecuteInThread()2657 void Worker::ExecuteInThread() {
2658 Isolate::CreateParams create_params;
2659 create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2660 Isolate* isolate = Isolate::New(create_params);
2661 isolate->SetHostImportModuleDynamicallyCallback(
2662 Shell::HostImportModuleDynamically);
2663 isolate->SetHostInitializeImportMetaObjectCallback(
2664 Shell::HostInitializeImportMetaObject);
2665 D8Console console(isolate);
2666 debug::SetConsoleDelegate(isolate, &console);
2667 {
2668 Isolate::Scope iscope(isolate);
2669 {
2670 HandleScope scope(isolate);
2671 PerIsolateData data(isolate);
2672 Local<Context> context = Shell::CreateEvaluationContext(isolate);
2673 {
2674 Context::Scope cscope(context);
2675 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2676
2677 Local<Object> global = context->Global();
2678 Local<Value> this_value = External::New(isolate, this);
2679 Local<FunctionTemplate> postmessage_fun_template =
2680 FunctionTemplate::New(isolate, PostMessageOut, this_value);
2681
2682 Local<Function> postmessage_fun;
2683 if (postmessage_fun_template->GetFunction(context)
2684 .ToLocal(&postmessage_fun)) {
2685 global->Set(context, String::NewFromUtf8(isolate, "postMessage",
2686 NewStringType::kNormal)
2687 .ToLocalChecked(),
2688 postmessage_fun).FromJust();
2689 }
2690
2691 // First run the script
2692 Local<String> file_name =
2693 String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2694 .ToLocalChecked();
2695 Local<String> source =
2696 String::NewFromUtf8(isolate, script_, NewStringType::kNormal)
2697 .ToLocalChecked();
2698 if (Shell::ExecuteString(
2699 isolate, source, file_name, Shell::kNoPrintResult,
2700 Shell::kReportExceptions, Shell::kProcessMessageQueue)) {
2701 // Get the message handler
2702 Local<Value> onmessage =
2703 global->Get(context, String::NewFromUtf8(isolate, "onmessage",
2704 NewStringType::kNormal)
2705 .ToLocalChecked()).ToLocalChecked();
2706 if (onmessage->IsFunction()) {
2707 Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
2708 // Now wait for messages
2709 while (true) {
2710 in_semaphore_.Wait();
2711 std::unique_ptr<SerializationData> data;
2712 if (!in_queue_.Dequeue(&data)) continue;
2713 if (!data) {
2714 break;
2715 }
2716 v8::TryCatch try_catch(isolate);
2717 Local<Value> value;
2718 if (Shell::DeserializeValue(isolate, std::move(data))
2719 .ToLocal(&value)) {
2720 Local<Value> argv[] = {value};
2721 MaybeLocal<Value> result =
2722 onmessage_fun->Call(context, global, 1, argv);
2723 USE(result);
2724 }
2725 if (try_catch.HasCaught()) {
2726 Shell::ReportException(isolate, &try_catch);
2727 }
2728 }
2729 }
2730 }
2731 }
2732 DisposeModuleEmbedderData(context);
2733 }
2734 Shell::CollectGarbage(isolate);
2735 }
2736 isolate->Dispose();
2737
2738 // Post nullptr to wake the thread waiting on GetMessage() if there is one.
2739 out_queue_.Enqueue(nullptr);
2740 out_semaphore_.Signal();
2741 }
2742
2743
PostMessageOut(const v8::FunctionCallbackInfo<v8::Value> & args)2744 void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
2745 Isolate* isolate = args.GetIsolate();
2746 HandleScope handle_scope(isolate);
2747
2748 if (args.Length() < 1) {
2749 Throw(isolate, "Invalid argument");
2750 return;
2751 }
2752
2753 Local<Value> message = args[0];
2754 Local<Value> transfer = Undefined(isolate);
2755 std::unique_ptr<SerializationData> data =
2756 Shell::SerializeValue(isolate, message, transfer);
2757 if (data) {
2758 DCHECK(args.Data()->IsExternal());
2759 Local<External> this_value = Local<External>::Cast(args.Data());
2760 Worker* worker = static_cast<Worker*>(this_value->Value());
2761 worker->out_queue_.Enqueue(std::move(data));
2762 worker->out_semaphore_.Signal();
2763 }
2764 }
2765
2766
SetFlagsFromString(const char * flags)2767 void SetFlagsFromString(const char* flags) {
2768 v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags)));
2769 }
2770
2771
SetOptions(int argc,char * argv[])2772 bool Shell::SetOptions(int argc, char* argv[]) {
2773 bool logfile_per_isolate = false;
2774 for (int i = 0; i < argc; i++) {
2775 if (strcmp(argv[i], "--stress-opt") == 0) {
2776 options.stress_opt = true;
2777 argv[i] = nullptr;
2778 } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
2779 strcmp(argv[i], "--no-stress-opt") == 0) {
2780 options.stress_opt = false;
2781 argv[i] = nullptr;
2782 } else if (strcmp(argv[i], "--stress-deopt") == 0) {
2783 options.stress_deopt = true;
2784 argv[i] = nullptr;
2785 } else if (strcmp(argv[i], "--stress-background-compile") == 0) {
2786 options.stress_background_compile = true;
2787 argv[i] = nullptr;
2788 } else if (strcmp(argv[i], "--nostress-background-compile") == 0 ||
2789 strcmp(argv[i], "--no-stress-background-compile") == 0) {
2790 options.stress_background_compile = false;
2791 argv[i] = nullptr;
2792 } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
2793 strcmp(argv[i], "--no-always-opt") == 0) {
2794 // No support for stressing if we can't use --always-opt.
2795 options.stress_opt = false;
2796 options.stress_deopt = false;
2797 } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
2798 logfile_per_isolate = true;
2799 argv[i] = nullptr;
2800 } else if (strcmp(argv[i], "--shell") == 0) {
2801 options.interactive_shell = true;
2802 argv[i] = nullptr;
2803 } else if (strcmp(argv[i], "--test") == 0) {
2804 options.test_shell = true;
2805 argv[i] = nullptr;
2806 } else if (strcmp(argv[i], "--notest") == 0 ||
2807 strcmp(argv[i], "--no-test") == 0) {
2808 options.test_shell = false;
2809 argv[i] = nullptr;
2810 } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
2811 options.send_idle_notification = true;
2812 argv[i] = nullptr;
2813 } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
2814 options.invoke_weak_callbacks = true;
2815 // TODO(jochen) See issue 3351
2816 options.send_idle_notification = true;
2817 argv[i] = nullptr;
2818 } else if (strcmp(argv[i], "--omit-quit") == 0) {
2819 options.omit_quit = true;
2820 argv[i] = nullptr;
2821 } else if (strcmp(argv[i], "--no-wait-for-wasm") == 0) {
2822 // TODO(herhut) Remove this flag once wasm compilation is fully
2823 // isolate-independent.
2824 options.wait_for_wasm = false;
2825 argv[i] = nullptr;
2826 } else if (strcmp(argv[i], "-f") == 0) {
2827 // Ignore any -f flags for compatibility with other stand-alone
2828 // JavaScript engines.
2829 continue;
2830 } else if (strcmp(argv[i], "--isolate") == 0) {
2831 options.num_isolates++;
2832 } else if (strcmp(argv[i], "--throws") == 0) {
2833 options.expected_to_throw = true;
2834 argv[i] = nullptr;
2835 } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
2836 options.icu_data_file = argv[i] + 16;
2837 argv[i] = nullptr;
2838 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
2839 } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) {
2840 options.natives_blob = argv[i] + 15;
2841 argv[i] = nullptr;
2842 } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
2843 options.snapshot_blob = argv[i] + 16;
2844 argv[i] = nullptr;
2845 #endif // V8_USE_EXTERNAL_STARTUP_DATA
2846 } else if (strcmp(argv[i], "--cache") == 0 ||
2847 strncmp(argv[i], "--cache=", 8) == 0) {
2848 const char* value = argv[i] + 7;
2849 if (!*value || strncmp(value, "=code", 6) == 0) {
2850 options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2851 options.code_cache_options =
2852 ShellOptions::CodeCacheOptions::kProduceCache;
2853 } else if (strncmp(value, "=none", 6) == 0) {
2854 options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2855 options.code_cache_options =
2856 ShellOptions::CodeCacheOptions::kNoProduceCache;
2857 } else if (strncmp(value, "=after-execute", 15) == 0) {
2858 options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2859 options.code_cache_options =
2860 ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute;
2861 } else if (strncmp(value, "=full-code-cache", 17) == 0) {
2862 options.compile_options = v8::ScriptCompiler::kEagerCompile;
2863 options.code_cache_options =
2864 ShellOptions::CodeCacheOptions::kProduceCache;
2865 } else {
2866 printf("Unknown option to --cache.\n");
2867 return false;
2868 }
2869 argv[i] = nullptr;
2870 } else if (strcmp(argv[i], "--enable-tracing") == 0) {
2871 options.trace_enabled = true;
2872 argv[i] = nullptr;
2873 } else if (strncmp(argv[i], "--trace-path=", 13) == 0) {
2874 options.trace_path = argv[i] + 13;
2875 argv[i] = nullptr;
2876 } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
2877 options.trace_config = argv[i] + 15;
2878 argv[i] = nullptr;
2879 } else if (strcmp(argv[i], "--enable-inspector") == 0) {
2880 options.enable_inspector = true;
2881 argv[i] = nullptr;
2882 } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
2883 options.lcov_file = argv[i] + 7;
2884 argv[i] = nullptr;
2885 } else if (strcmp(argv[i], "--disable-in-process-stack-traces") == 0) {
2886 options.disable_in_process_stack_traces = true;
2887 argv[i] = nullptr;
2888 #ifdef V8_OS_POSIX
2889 } else if (strncmp(argv[i], "--read-from-tcp-port=", 21) == 0) {
2890 options.read_from_tcp_port = atoi(argv[i] + 21);
2891 argv[i] = nullptr;
2892 #endif // V8_OS_POSIX
2893 } else if (strcmp(argv[i], "--enable-os-system") == 0) {
2894 options.enable_os_system = true;
2895 argv[i] = nullptr;
2896 } else if (strcmp(argv[i], "--quiet-load") == 0) {
2897 options.quiet_load = true;
2898 argv[i] = nullptr;
2899 } else if (strncmp(argv[i], "--thread-pool-size=", 19) == 0) {
2900 options.thread_pool_size = atoi(argv[i] + 19);
2901 argv[i] = nullptr;
2902 }
2903 }
2904
2905 v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
2906 options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator;
2907
2908 // Set up isolated source groups.
2909 options.isolate_sources = new SourceGroup[options.num_isolates];
2910 SourceGroup* current = options.isolate_sources;
2911 current->Begin(argv, 1);
2912 for (int i = 1; i < argc; i++) {
2913 const char* str = argv[i];
2914 if (strcmp(str, "--isolate") == 0) {
2915 current->End(i);
2916 current++;
2917 current->Begin(argv, i + 1);
2918 } else if (strcmp(str, "--module") == 0) {
2919 // Pass on to SourceGroup, which understands this option.
2920 } else if (strncmp(str, "--", 2) == 0) {
2921 printf("Warning: unknown flag %s.\nTry --help for options\n", str);
2922 } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
2923 set_script_executed();
2924 } else if (strncmp(str, "-", 1) != 0) {
2925 // Not a flag, so it must be a script to execute.
2926 set_script_executed();
2927 }
2928 }
2929 current->End(argc);
2930
2931 if (!logfile_per_isolate && options.num_isolates) {
2932 SetFlagsFromString("--nologfile_per_isolate");
2933 }
2934
2935 return true;
2936 }
2937
RunMain(Isolate * isolate,int argc,char * argv[],bool last_run)2938 int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
2939 for (int i = 1; i < options.num_isolates; ++i) {
2940 options.isolate_sources[i].StartExecuteInThread();
2941 }
2942 {
2943 SetWaitUntilDone(isolate, false);
2944 if (options.lcov_file) {
2945 debug::Coverage::SelectMode(isolate, debug::Coverage::kBlockCount);
2946 }
2947 HandleScope scope(isolate);
2948 Local<Context> context = CreateEvaluationContext(isolate);
2949 bool use_existing_context = last_run && use_interactive_shell();
2950 if (use_existing_context) {
2951 // Keep using the same context in the interactive shell.
2952 evaluation_context_.Reset(isolate, context);
2953 }
2954 {
2955 Context::Scope cscope(context);
2956 InspectorClient inspector_client(context, options.enable_inspector);
2957 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2958 options.isolate_sources[0].Execute(isolate);
2959 CompleteMessageLoop(isolate);
2960 }
2961 if (!use_existing_context) {
2962 DisposeModuleEmbedderData(context);
2963 }
2964 WriteLcovData(isolate, options.lcov_file);
2965 }
2966 CollectGarbage(isolate);
2967 for (int i = 1; i < options.num_isolates; ++i) {
2968 if (last_run) {
2969 options.isolate_sources[i].JoinThread();
2970 } else {
2971 options.isolate_sources[i].WaitForThread();
2972 }
2973 }
2974 CleanupWorkers();
2975 return 0;
2976 }
2977
2978
CollectGarbage(Isolate * isolate)2979 void Shell::CollectGarbage(Isolate* isolate) {
2980 if (options.send_idle_notification) {
2981 const double kLongIdlePauseInSeconds = 1.0;
2982 isolate->ContextDisposedNotification();
2983 isolate->IdleNotificationDeadline(
2984 g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
2985 }
2986 if (options.invoke_weak_callbacks) {
2987 // By sending a low memory notifications, we will try hard to collect all
2988 // garbage and will therefore also invoke all weak callbacks of actually
2989 // unreachable persistent handles.
2990 isolate->LowMemoryNotification();
2991 }
2992 }
2993
SetWaitUntilDone(Isolate * isolate,bool value)2994 void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
2995 base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer());
2996 if (isolate_status_.count(isolate) == 0) {
2997 isolate_status_.insert(std::make_pair(isolate, value));
2998 } else {
2999 isolate_status_[isolate] = value;
3000 }
3001 }
3002
3003 namespace {
ProcessMessages(Isolate * isolate,std::function<platform::MessageLoopBehavior ()> behavior)3004 bool ProcessMessages(Isolate* isolate,
3005 std::function<platform::MessageLoopBehavior()> behavior) {
3006 Platform* platform = GetDefaultPlatform();
3007 while (true) {
3008 while (v8::platform::PumpMessageLoop(platform, isolate, behavior())) {
3009 isolate->RunMicrotasks();
3010 }
3011 if (platform->IdleTasksEnabled(isolate)) {
3012 v8::platform::RunIdleTasks(platform, isolate,
3013 50.0 / base::Time::kMillisecondsPerSecond);
3014 }
3015 HandleScope handle_scope(isolate);
3016 PerIsolateData* data = PerIsolateData::Get(isolate);
3017 Local<Function> callback;
3018 if (!data->GetTimeoutCallback().ToLocal(&callback)) break;
3019 Local<Context> context;
3020 if (!data->GetTimeoutContext().ToLocal(&context)) break;
3021 TryCatch try_catch(isolate);
3022 try_catch.SetVerbose(true);
3023 Context::Scope context_scope(context);
3024 if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
3025 Shell::ReportException(isolate, &try_catch);
3026 return false;
3027 }
3028 }
3029 return true;
3030 }
3031 } // anonymous namespace
3032
CompleteMessageLoop(Isolate * isolate)3033 void Shell::CompleteMessageLoop(Isolate* isolate) {
3034 auto get_waiting_behaviour = [isolate]() {
3035 base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer());
3036 DCHECK_GT(isolate_status_.count(isolate), 0);
3037 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3038 i::wasm::WasmEngine* wasm_engine = i_isolate->wasm_engine();
3039 bool should_wait = (options.wait_for_wasm &&
3040 wasm_engine->HasRunningCompileJob(i_isolate)) ||
3041 isolate_status_[isolate];
3042 return should_wait ? platform::MessageLoopBehavior::kWaitForWork
3043 : platform::MessageLoopBehavior::kDoNotWait;
3044 };
3045 ProcessMessages(isolate, get_waiting_behaviour);
3046 }
3047
EmptyMessageQueues(Isolate * isolate)3048 bool Shell::EmptyMessageQueues(Isolate* isolate) {
3049 return ProcessMessages(
3050 isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
3051 }
3052
3053 class Serializer : public ValueSerializer::Delegate {
3054 public:
Serializer(Isolate * isolate)3055 explicit Serializer(Isolate* isolate)
3056 : isolate_(isolate),
3057 serializer_(isolate, this),
3058 current_memory_usage_(0) {}
3059
WriteValue(Local<Context> context,Local<Value> value,Local<Value> transfer)3060 Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
3061 Local<Value> transfer) {
3062 bool ok;
3063 DCHECK(!data_);
3064 data_.reset(new SerializationData);
3065 if (!PrepareTransfer(context, transfer).To(&ok)) {
3066 return Nothing<bool>();
3067 }
3068 serializer_.WriteHeader();
3069
3070 if (!serializer_.WriteValue(context, value).To(&ok)) {
3071 data_.reset();
3072 return Nothing<bool>();
3073 }
3074
3075 if (!FinalizeTransfer().To(&ok)) {
3076 return Nothing<bool>();
3077 }
3078
3079 std::pair<uint8_t*, size_t> pair = serializer_.Release();
3080 data_->data_.reset(pair.first);
3081 data_->size_ = pair.second;
3082 return Just(true);
3083 }
3084
Release()3085 std::unique_ptr<SerializationData> Release() { return std::move(data_); }
3086
AppendExternalizedContentsTo(std::vector<ExternalizedContents> * to)3087 void AppendExternalizedContentsTo(std::vector<ExternalizedContents>* to) {
3088 to->insert(to->end(),
3089 std::make_move_iterator(externalized_contents_.begin()),
3090 std::make_move_iterator(externalized_contents_.end()));
3091 externalized_contents_.clear();
3092 }
3093
3094 protected:
3095 // Implements ValueSerializer::Delegate.
ThrowDataCloneError(Local<String> message)3096 void ThrowDataCloneError(Local<String> message) override {
3097 isolate_->ThrowException(Exception::Error(message));
3098 }
3099
GetSharedArrayBufferId(Isolate * isolate,Local<SharedArrayBuffer> shared_array_buffer)3100 Maybe<uint32_t> GetSharedArrayBufferId(
3101 Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override {
3102 DCHECK_NOT_NULL(data_);
3103 for (size_t index = 0; index < shared_array_buffers_.size(); ++index) {
3104 if (shared_array_buffers_[index] == shared_array_buffer) {
3105 return Just<uint32_t>(static_cast<uint32_t>(index));
3106 }
3107 }
3108
3109 size_t index = shared_array_buffers_.size();
3110 shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
3111 data_->shared_array_buffer_contents_.push_back(
3112 MaybeExternalize(shared_array_buffer));
3113 return Just<uint32_t>(static_cast<uint32_t>(index));
3114 }
3115
GetWasmModuleTransferId(Isolate * isolate,Local<WasmCompiledModule> module)3116 Maybe<uint32_t> GetWasmModuleTransferId(
3117 Isolate* isolate, Local<WasmCompiledModule> module) override {
3118 DCHECK_NOT_NULL(data_);
3119 for (size_t index = 0; index < wasm_modules_.size(); ++index) {
3120 if (wasm_modules_[index] == module) {
3121 return Just<uint32_t>(static_cast<uint32_t>(index));
3122 }
3123 }
3124
3125 size_t index = wasm_modules_.size();
3126 wasm_modules_.emplace_back(isolate_, module);
3127 data_->transferrable_modules_.push_back(module->GetTransferrableModule());
3128 return Just<uint32_t>(static_cast<uint32_t>(index));
3129 }
3130
ReallocateBufferMemory(void * old_buffer,size_t size,size_t * actual_size)3131 void* ReallocateBufferMemory(void* old_buffer, size_t size,
3132 size_t* actual_size) override {
3133 // Not accurate, because we don't take into account reallocated buffers,
3134 // but this is fine for testing.
3135 current_memory_usage_ += size;
3136 if (current_memory_usage_ > kMaxSerializerMemoryUsage) return nullptr;
3137
3138 void* result = realloc(old_buffer, size);
3139 *actual_size = result ? size : 0;
3140 return result;
3141 }
3142
FreeBufferMemory(void * buffer)3143 void FreeBufferMemory(void* buffer) override { free(buffer); }
3144
3145 private:
PrepareTransfer(Local<Context> context,Local<Value> transfer)3146 Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
3147 if (transfer->IsArray()) {
3148 Local<Array> transfer_array = Local<Array>::Cast(transfer);
3149 uint32_t length = transfer_array->Length();
3150 for (uint32_t i = 0; i < length; ++i) {
3151 Local<Value> element;
3152 if (transfer_array->Get(context, i).ToLocal(&element)) {
3153 if (!element->IsArrayBuffer()) {
3154 Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
3155 return Nothing<bool>();
3156 }
3157
3158 Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
3159 serializer_.TransferArrayBuffer(
3160 static_cast<uint32_t>(array_buffers_.size()), array_buffer);
3161 array_buffers_.emplace_back(isolate_, array_buffer);
3162 } else {
3163 return Nothing<bool>();
3164 }
3165 }
3166 return Just(true);
3167 } else if (transfer->IsUndefined()) {
3168 return Just(true);
3169 } else {
3170 Throw(isolate_, "Transfer list must be an Array or undefined");
3171 return Nothing<bool>();
3172 }
3173 }
3174
3175 template <typename T>
MaybeExternalize(Local<T> array_buffer)3176 typename T::Contents MaybeExternalize(Local<T> array_buffer) {
3177 if (array_buffer->IsExternal()) {
3178 return array_buffer->GetContents();
3179 } else {
3180 typename T::Contents contents = array_buffer->Externalize();
3181 externalized_contents_.emplace_back(contents);
3182 return contents;
3183 }
3184 }
3185
FinalizeTransfer()3186 Maybe<bool> FinalizeTransfer() {
3187 for (const auto& global_array_buffer : array_buffers_) {
3188 Local<ArrayBuffer> array_buffer =
3189 Local<ArrayBuffer>::New(isolate_, global_array_buffer);
3190 if (!array_buffer->IsNeuterable()) {
3191 Throw(isolate_, "ArrayBuffer could not be transferred");
3192 return Nothing<bool>();
3193 }
3194
3195 ArrayBuffer::Contents contents = MaybeExternalize(array_buffer);
3196 array_buffer->Neuter();
3197 data_->array_buffer_contents_.push_back(contents);
3198 }
3199
3200 return Just(true);
3201 }
3202
3203 Isolate* isolate_;
3204 ValueSerializer serializer_;
3205 std::unique_ptr<SerializationData> data_;
3206 std::vector<Global<ArrayBuffer>> array_buffers_;
3207 std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
3208 std::vector<Global<WasmCompiledModule>> wasm_modules_;
3209 std::vector<ExternalizedContents> externalized_contents_;
3210 size_t current_memory_usage_;
3211
3212 DISALLOW_COPY_AND_ASSIGN(Serializer);
3213 };
3214
3215 class Deserializer : public ValueDeserializer::Delegate {
3216 public:
Deserializer(Isolate * isolate,std::unique_ptr<SerializationData> data)3217 Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
3218 : isolate_(isolate),
3219 deserializer_(isolate, data->data(), data->size(), this),
3220 data_(std::move(data)) {
3221 deserializer_.SetSupportsLegacyWireFormat(true);
3222 }
3223
ReadValue(Local<Context> context)3224 MaybeLocal<Value> ReadValue(Local<Context> context) {
3225 bool read_header;
3226 if (!deserializer_.ReadHeader(context).To(&read_header)) {
3227 return MaybeLocal<Value>();
3228 }
3229
3230 uint32_t index = 0;
3231 for (const auto& contents : data_->array_buffer_contents()) {
3232 Local<ArrayBuffer> array_buffer =
3233 ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
3234 deserializer_.TransferArrayBuffer(index++, array_buffer);
3235 }
3236
3237 return deserializer_.ReadValue(context);
3238 }
3239
GetSharedArrayBufferFromId(Isolate * isolate,uint32_t clone_id)3240 MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
3241 Isolate* isolate, uint32_t clone_id) override {
3242 DCHECK_NOT_NULL(data_);
3243 if (clone_id < data_->shared_array_buffer_contents().size()) {
3244 SharedArrayBuffer::Contents contents =
3245 data_->shared_array_buffer_contents().at(clone_id);
3246 return SharedArrayBuffer::New(isolate_, contents.Data(),
3247 contents.ByteLength());
3248 }
3249 return MaybeLocal<SharedArrayBuffer>();
3250 }
3251
GetWasmModuleFromId(Isolate * isolate,uint32_t transfer_id)3252 MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(
3253 Isolate* isolate, uint32_t transfer_id) override {
3254 DCHECK_NOT_NULL(data_);
3255 if (transfer_id < data_->transferrable_modules().size()) {
3256 return WasmCompiledModule::FromTransferrableModule(
3257 isolate_, data_->transferrable_modules().at(transfer_id));
3258 }
3259 return MaybeLocal<WasmCompiledModule>();
3260 }
3261
3262 private:
3263 Isolate* isolate_;
3264 ValueDeserializer deserializer_;
3265 std::unique_ptr<SerializationData> data_;
3266
3267 DISALLOW_COPY_AND_ASSIGN(Deserializer);
3268 };
3269
SerializeValue(Isolate * isolate,Local<Value> value,Local<Value> transfer)3270 std::unique_ptr<SerializationData> Shell::SerializeValue(
3271 Isolate* isolate, Local<Value> value, Local<Value> transfer) {
3272 bool ok;
3273 Local<Context> context = isolate->GetCurrentContext();
3274 Serializer serializer(isolate);
3275 std::unique_ptr<SerializationData> data;
3276 if (serializer.WriteValue(context, value, transfer).To(&ok)) {
3277 data = serializer.Release();
3278 }
3279 // Append externalized contents even when WriteValue fails.
3280 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3281 serializer.AppendExternalizedContentsTo(&externalized_contents_);
3282 return data;
3283 }
3284
DeserializeValue(Isolate * isolate,std::unique_ptr<SerializationData> data)3285 MaybeLocal<Value> Shell::DeserializeValue(
3286 Isolate* isolate, std::unique_ptr<SerializationData> data) {
3287 Local<Value> value;
3288 Local<Context> context = isolate->GetCurrentContext();
3289 Deserializer deserializer(isolate, std::move(data));
3290 return deserializer.ReadValue(context);
3291 }
3292
3293
CleanupWorkers()3294 void Shell::CleanupWorkers() {
3295 // Make a copy of workers_, because we don't want to call Worker::Terminate
3296 // while holding the workers_mutex_ lock. Otherwise, if a worker is about to
3297 // create a new Worker, it would deadlock.
3298 std::vector<Worker*> workers_copy;
3299 {
3300 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3301 allow_new_workers_ = false;
3302 workers_copy.swap(workers_);
3303 }
3304
3305 for (Worker* worker : workers_copy) {
3306 worker->WaitForThread();
3307 delete worker;
3308 }
3309
3310 // Now that all workers are terminated, we can re-enable Worker creation.
3311 base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
3312 allow_new_workers_ = true;
3313 externalized_contents_.clear();
3314 }
3315
Main(int argc,char * argv[])3316 int Shell::Main(int argc, char* argv[]) {
3317 std::ofstream trace_file;
3318 v8::base::EnsureConsoleOutput();
3319 if (!SetOptions(argc, argv)) return 1;
3320 v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
3321
3322 v8::platform::InProcessStackDumping in_process_stack_dumping =
3323 options.disable_in_process_stack_traces
3324 ? v8::platform::InProcessStackDumping::kDisabled
3325 : v8::platform::InProcessStackDumping::kEnabled;
3326
3327 std::unique_ptr<platform::tracing::TracingController> tracing;
3328 if (options.trace_enabled && !i::FLAG_verify_predictable) {
3329 tracing = base::make_unique<platform::tracing::TracingController>();
3330
3331 trace_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
3332 platform::tracing::TraceBuffer* trace_buffer =
3333 platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
3334 platform::tracing::TraceBuffer::kRingBufferChunks,
3335 platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
3336 tracing->Initialize(trace_buffer);
3337 }
3338
3339 platform::tracing::TracingController* tracing_controller = tracing.get();
3340 g_platform = v8::platform::NewDefaultPlatform(
3341 options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
3342 in_process_stack_dumping, std::move(tracing));
3343 if (i::FLAG_verify_predictable) {
3344 g_platform.reset(new PredictablePlatform(std::move(g_platform)));
3345 }
3346
3347 v8::V8::InitializePlatform(g_platform.get());
3348 v8::V8::Initialize();
3349 if (options.natives_blob || options.snapshot_blob) {
3350 v8::V8::InitializeExternalStartupData(options.natives_blob,
3351 options.snapshot_blob);
3352 } else {
3353 v8::V8::InitializeExternalStartupData(argv[0]);
3354 }
3355 if (i::FLAG_trace_turbo_cfg_file == nullptr) {
3356 SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
3357 }
3358 if (i::FLAG_redirect_code_traces_to == nullptr) {
3359 SetFlagsFromString("--redirect-code-traces-to=code.asm");
3360 }
3361 int result = 0;
3362 Isolate::CreateParams create_params;
3363 ShellArrayBufferAllocator shell_array_buffer_allocator;
3364 MockArrayBufferAllocator mock_arraybuffer_allocator;
3365 if (options.mock_arraybuffer_allocator) {
3366 Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
3367 } else {
3368 Shell::array_buffer_allocator = &shell_array_buffer_allocator;
3369 }
3370 create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3371 #ifdef ENABLE_VTUNE_JIT_INTERFACE
3372 create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
3373 #endif
3374 create_params.constraints.ConfigureDefaults(
3375 base::SysInfo::AmountOfPhysicalMemory(),
3376 base::SysInfo::AmountOfVirtualMemory());
3377
3378 Shell::counter_map_ = new CounterMap();
3379 if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp || i::FLAG_gc_stats) {
3380 create_params.counter_lookup_callback = LookupCounter;
3381 create_params.create_histogram_callback = CreateHistogram;
3382 create_params.add_histogram_sample_callback = AddHistogramSample;
3383 }
3384
3385 if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) {
3386 constexpr bool use_default_trap_handler = true;
3387 if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_trap_handler)) {
3388 FATAL("Could not register trap handler");
3389 }
3390 }
3391
3392 Isolate* isolate = Isolate::New(create_params);
3393 isolate->SetHostImportModuleDynamicallyCallback(
3394 Shell::HostImportModuleDynamically);
3395 isolate->SetHostInitializeImportMetaObjectCallback(
3396 Shell::HostInitializeImportMetaObject);
3397
3398 D8Console console(isolate);
3399 {
3400 Isolate::Scope scope(isolate);
3401 Initialize(isolate);
3402 PerIsolateData data(isolate);
3403 debug::SetConsoleDelegate(isolate, &console);
3404
3405 if (options.trace_enabled) {
3406 platform::tracing::TraceConfig* trace_config;
3407 if (options.trace_config) {
3408 int size = 0;
3409 char* trace_config_json_str = ReadChars(options.trace_config, &size);
3410 trace_config =
3411 tracing::CreateTraceConfigFromJSON(isolate, trace_config_json_str);
3412 delete[] trace_config_json_str;
3413 } else {
3414 trace_config =
3415 platform::tracing::TraceConfig::CreateDefaultTraceConfig();
3416 }
3417 tracing_controller->StartTracing(trace_config);
3418 }
3419
3420 if (options.stress_opt || options.stress_deopt) {
3421 Testing::SetStressRunType(options.stress_opt
3422 ? Testing::kStressTypeOpt
3423 : Testing::kStressTypeDeopt);
3424 options.stress_runs = Testing::GetStressRuns();
3425 for (int i = 0; i < options.stress_runs && result == 0; i++) {
3426 printf("============ Stress %d/%d ============\n", i + 1,
3427 options.stress_runs);
3428 Testing::PrepareStressRun(i);
3429 bool last_run = i == options.stress_runs - 1;
3430 result = RunMain(isolate, argc, argv, last_run);
3431 }
3432 printf("======== Full Deoptimization =======\n");
3433 Testing::DeoptimizeAll(isolate);
3434 } else if (i::FLAG_stress_runs > 0) {
3435 options.stress_runs = i::FLAG_stress_runs;
3436 for (int i = 0; i < options.stress_runs && result == 0; i++) {
3437 printf("============ Run %d/%d ============\n", i + 1,
3438 options.stress_runs);
3439 bool last_run = i == options.stress_runs - 1;
3440 result = RunMain(isolate, argc, argv, last_run);
3441 }
3442 } else if (options.code_cache_options !=
3443 ShellOptions::CodeCacheOptions::kNoProduceCache) {
3444 printf("============ Run: Produce code cache ============\n");
3445 // First run to produce the cache
3446 Isolate::CreateParams create_params;
3447 create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3448 i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
3449 Isolate* isolate2 = Isolate::New(create_params);
3450 i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
3451 isolate2->SetHostImportModuleDynamicallyCallback(
3452 Shell::HostImportModuleDynamically);
3453 isolate2->SetHostInitializeImportMetaObjectCallback(
3454 Shell::HostInitializeImportMetaObject);
3455 {
3456 D8Console console(isolate2);
3457 debug::SetConsoleDelegate(isolate2, &console);
3458 PerIsolateData data(isolate2);
3459 Isolate::Scope isolate_scope(isolate2);
3460
3461 result = RunMain(isolate2, argc, argv, false);
3462 }
3463 isolate2->Dispose();
3464
3465 // Change the options to consume cache
3466 DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
3467 options.compile_options == v8::ScriptCompiler::kNoCompileOptions);
3468 options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
3469
3470 printf("============ Run: Consume code cache ============\n");
3471 // Second run to consume the cache in current isolate
3472 result = RunMain(isolate, argc, argv, true);
3473 options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3474 } else {
3475 bool last_run = true;
3476 result = RunMain(isolate, argc, argv, last_run);
3477 }
3478
3479 // Run interactive shell if explicitly requested or if no script has been
3480 // executed, but never on --test
3481 if (use_interactive_shell()) {
3482 RunShell(isolate);
3483 }
3484
3485 if (i::FLAG_trace_ignition_dispatches &&
3486 i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
3487 WriteIgnitionDispatchCountersFile(isolate);
3488 }
3489
3490 // Shut down contexts and collect garbage.
3491 cached_code_map_.clear();
3492 evaluation_context_.Reset();
3493 stringify_function_.Reset();
3494 CollectGarbage(isolate);
3495 }
3496 OnExit(isolate);
3497 V8::Dispose();
3498 V8::ShutdownPlatform();
3499
3500 // Delete the platform explicitly here to write the tracing output to the
3501 // tracing file.
3502 g_platform.reset();
3503 return result;
3504 }
3505
3506 } // namespace v8
3507
3508
3509 #ifndef GOOGLE3
main(int argc,char * argv[])3510 int main(int argc, char* argv[]) {
3511 return v8::Shell::Main(argc, argv);
3512 }
3513 #endif
3514
3515 #undef CHECK
3516 #undef DCHECK
3517