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: 73 void* Allocate(size_t length) override { 74 return allocator_->Allocate(length); 75 } 76 77 void* AllocateUninitialized(size_t length) override { 78 return allocator_->AllocateUninitialized(length); 79 } 80 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: 93 void* Allocate(size_t length) override { 94 if (length >= kVMThreshold) return AllocateVM(length); 95 return ArrayBufferAllocatorBase::Allocate(length); 96 } 97 98 void* AllocateUninitialized(size_t length) override { 99 if (length >= kVMThreshold) return AllocateVM(length); 100 return ArrayBufferAllocatorBase::AllocateUninitialized(length); 101 } 102 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 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 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 { 138 void* Allocate(size_t length) override { 139 return ArrayBufferAllocatorBase::Allocate(Adjust(length)); 140 } 141 142 void* AllocateUninitialized(size_t length) override { 143 return ArrayBufferAllocatorBase::AllocateUninitialized(Adjust(length)); 144 } 145 146 void Free(void* data, size_t length) override { 147 return ArrayBufferAllocatorBase::Free(data, Adjust(length)); 148 } 149 150 private: 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: 162 explicit PredictablePlatform(std::unique_ptr<Platform> platform) 163 : platform_(std::move(platform)) { 164 DCHECK_NOT_NULL(platform_); 165 } 166 167 PageAllocator* GetPageAllocator() override { 168 return platform_->GetPageAllocator(); 169 } 170 171 void OnCriticalMemoryPressure() override { 172 platform_->OnCriticalMemoryPressure(); 173 } 174 175 bool OnCriticalMemoryPressure(size_t length) override { 176 return platform_->OnCriticalMemoryPressure(length); 177 } 178 179 std::shared_ptr<TaskRunner> GetForegroundTaskRunner( 180 v8::Isolate* isolate) override { 181 return platform_->GetForegroundTaskRunner(isolate); 182 } 183 184 int NumberOfWorkerThreads() override { return 0; } 185 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 192 void CallDelayedOnWorkerThread(std::unique_ptr<Task> task, 193 double delay_in_seconds) override { 194 // Never run delayed tasks. 195 } 196 197 void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { 198 platform_->CallOnForegroundThread(isolate, task); 199 } 200 201 void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, 202 double delay_in_seconds) override { 203 platform_->CallDelayedOnForegroundThread(isolate, task, delay_in_seconds); 204 } 205 206 void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override { 207 UNREACHABLE(); 208 } 209 210 bool IdleTasksEnabled(Isolate* isolate) override { return false; } 211 212 double MonotonicallyIncreasingTime() override { 213 return synthetic_time_in_sec_ += 0.00001; 214 } 215 216 double CurrentClockTimeMillis() override { 217 return MonotonicallyIncreasingTime() * base::Time::kMillisecondsPerSecond; 218 } 219 220 v8::TracingController* GetTracingController() override { 221 return platform_->GetTracingController(); 222 } 223 224 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 235 v8::Platform* GetDefaultPlatform() { 236 return i::FLAG_verify_predictable 237 ? static_cast<PredictablePlatform*>(g_platform.get())->platform() 238 : g_platform.get(); 239 } 240 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 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 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: 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: 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 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 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 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 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: 390 ExternalOwningOneByteStringResource() : length_(0) {} 391 ExternalOwningOneByteStringResource(std::unique_ptr<const char[]> data, 392 size_t length) 393 : data_(std::move(data)), length_(length) {} 394 const char* data() const override { return data_.get(); } 395 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: 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 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: 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 462 void Run() override { task_->Run(); } 463 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 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 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. 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 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 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 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 '/'. 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). 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: 684 explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {} 685 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: 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 709 void InitializeModuleEmbedderData(Local<Context> context) { 710 context->SetAlignedPointerInEmbedderData( 711 kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate())); 712 } 713 714 ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) { 715 return static_cast<ModuleEmbedderData*>( 716 context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex)); 717 } 718 719 void DisposeModuleEmbedderData(Local<Context> context) { 720 delete GetModuleDataFromContext(context); 721 context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr); 722 } 723 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 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 { 789 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 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 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 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 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 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 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 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 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 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 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 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 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 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(). 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. 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. 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.) 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 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 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. 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. 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. 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. 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. 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. 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. 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 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. 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. 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 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 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 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 1291 void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) { 1292 WriteAndFlush(stdout, args); 1293 } 1294 1295 void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) { 1296 WriteAndFlush(stderr, args); 1297 } 1298 1299 void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) { 1300 WriteToFile(stdout, args); 1301 } 1302 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 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 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 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 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 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 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 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 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 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 1512 void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) { 1513 SetWaitUntilDone(args.GetIsolate(), true); 1514 } 1515 1516 void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) { 1517 SetWaitUntilDone(args.GetIsolate(), false); 1518 } 1519 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 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 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 1600 void Counter::AddSample(int32_t sample) { 1601 count_++; 1602 sample_total_ += sample; 1603 } 1604 1605 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 1614 Counter* CounterCollection::GetNextCounter() { 1615 if (counters_in_use_ == kMaxCounters) return nullptr; 1616 return &counters_[counters_in_use_++]; 1617 } 1618 1619 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 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 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 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 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. 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 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 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 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 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 1982 inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) { 1983 return strcmp(lhs.key, rhs.key) < 0; 1984 } 1985 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 { 2001 int LineFromOffset(Local<debug::Script> script, int offset) { 2002 debug::Location location = script->GetSourceLocation(offset); 2003 return location.GetLineNumber(); 2004 } 2005 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 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). 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 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 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 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 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 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. 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 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: 2294 explicit InspectorFrontend(Local<Context> context) { 2295 isolate_ = context->GetIsolate(); 2296 context_.Reset(isolate_, context); 2297 } 2298 virtual ~InspectorFrontend() = default; 2299 2300 private: 2301 void sendResponse( 2302 int callId, 2303 std::unique_ptr<v8_inspector::StringBuffer> message) override { 2304 Send(message->string()); 2305 } 2306 void sendNotification( 2307 std::unique_ptr<v8_inspector::StringBuffer> message) override { 2308 Send(message->string()); 2309 } 2310 void flushProtocolNotifications() override {} 2311 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: 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: 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 2392 Local<Context> ensureDefaultContextInGroup(int group_id) override { 2393 DCHECK(isolate_); 2394 DCHECK_EQ(kContextGroupId, group_id); 2395 return context_.Get(isolate_); 2396 } 2397 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 2424 SourceGroup::~SourceGroup() { 2425 delete thread_; 2426 thread_ = nullptr; 2427 } 2428 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 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 2504 Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) { 2505 return Shell::ReadFile(isolate, name); 2506 } 2507 2508 SourceGroup::IsolateThread::IsolateThread(SourceGroup* group) 2509 : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {} 2510 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 2549 void SourceGroup::StartExecuteInThread() { 2550 if (thread_ == nullptr) { 2551 thread_ = new IsolateThread(this); 2552 thread_->Start(); 2553 } 2554 next_semaphore_.Signal(); 2555 } 2556 2557 2558 void SourceGroup::WaitForThread() { 2559 if (thread_ == nullptr) return; 2560 done_semaphore_.Wait(); 2561 } 2562 2563 2564 void SourceGroup::JoinThread() { 2565 if (thread_ == nullptr) return; 2566 thread_->Join(); 2567 } 2568 2569 ExternalizedContents::~ExternalizedContents() { 2570 if (data_ != nullptr) { 2571 deleter_(data_, length_, deleter_data_); 2572 } 2573 } 2574 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 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 2591 bool SerializationDataQueue::IsEmpty() { 2592 base::LockGuard<base::Mutex> lock_guard(&mutex_); 2593 return data_.empty(); 2594 } 2595 2596 2597 void SerializationDataQueue::Clear() { 2598 base::LockGuard<base::Mutex> lock_guard(&mutex_); 2599 data_.clear(); 2600 } 2601 2602 Worker::Worker() 2603 : in_semaphore_(0), 2604 out_semaphore_(0), 2605 thread_(nullptr), 2606 script_(nullptr), 2607 running_(false) {} 2608 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 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 2626 void Worker::PostMessage(std::unique_ptr<SerializationData> data) { 2627 in_queue_.Enqueue(std::move(data)); 2628 in_semaphore_.Signal(); 2629 } 2630 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 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 2651 void Worker::WaitForThread() { 2652 Terminate(); 2653 thread_->Join(); 2654 } 2655 2656 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 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 2767 void SetFlagsFromString(const char* flags) { 2768 v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags))); 2769 } 2770 2771 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 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 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 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 { 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 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 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: 3055 explicit Serializer(Isolate* isolate) 3056 : isolate_(isolate), 3057 serializer_(isolate, this), 3058 current_memory_usage_(0) {} 3059 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 3085 std::unique_ptr<SerializationData> Release() { return std::move(data_); } 3086 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. 3096 void ThrowDataCloneError(Local<String> message) override { 3097 isolate_->ThrowException(Exception::Error(message)); 3098 } 3099 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 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 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 3143 void FreeBufferMemory(void* buffer) override { free(buffer); } 3144 3145 private: 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> 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 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: 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 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 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 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 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 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 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 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 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