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 #ifndef V8_D8_H_ 6 #define V8_D8_H_ 7 8 #include <iterator> 9 #include <map> 10 #include <memory> 11 #include <queue> 12 #include <string> 13 #include <unordered_map> 14 #include <vector> 15 16 #include "src/allocation.h" 17 #include "src/async-hooks-wrapper.h" 18 #include "src/base/platform/time.h" 19 #include "src/string-hasher.h" 20 #include "src/utils.h" 21 22 #include "src/base/once.h" 23 24 25 namespace v8 { 26 27 28 // A single counter in a counter collection. 29 class Counter { 30 public: 31 static const int kMaxNameSize = 64; 32 int32_t* Bind(const char* name, bool histogram); 33 int32_t* ptr() { return &count_; } 34 int32_t count() { return count_; } 35 int32_t sample_total() { return sample_total_; } 36 bool is_histogram() { return is_histogram_; } 37 void AddSample(int32_t sample); 38 private: 39 int32_t count_; 40 int32_t sample_total_; 41 bool is_histogram_; 42 uint8_t name_[kMaxNameSize]; 43 }; 44 45 46 // A set of counters and associated information. An instance of this 47 // class is stored directly in the memory-mapped counters file if 48 // the --map-counters options is used 49 class CounterCollection { 50 public: 51 CounterCollection(); 52 Counter* GetNextCounter(); 53 private: 54 static const unsigned kMaxCounters = 512; 55 uint32_t magic_number_; 56 uint32_t max_counters_; 57 uint32_t max_name_size_; 58 uint32_t counters_in_use_; 59 Counter counters_[kMaxCounters]; 60 }; 61 62 struct CStringHasher { 63 std::size_t operator()(const char* name) const { 64 size_t h = 0; 65 size_t c; 66 while ((c = *name++) != 0) { 67 h += h << 5; 68 h += c; 69 } 70 return h; 71 } 72 }; 73 74 typedef std::unordered_map<const char*, Counter*, CStringHasher, 75 i::StringEquals> 76 CounterMap; 77 78 class SourceGroup { 79 public: 80 SourceGroup() 81 : next_semaphore_(0), 82 done_semaphore_(0), 83 thread_(nullptr), 84 argv_(nullptr), 85 begin_offset_(0), 86 end_offset_(0) {} 87 88 ~SourceGroup(); 89 90 void Begin(char** argv, int offset) { 91 argv_ = const_cast<const char**>(argv); 92 begin_offset_ = offset; 93 } 94 95 void End(int offset) { end_offset_ = offset; } 96 97 void Execute(Isolate* isolate); 98 99 void StartExecuteInThread(); 100 void WaitForThread(); 101 void JoinThread(); 102 103 private: 104 class IsolateThread : public base::Thread { 105 public: 106 explicit IsolateThread(SourceGroup* group); 107 108 virtual void Run() { 109 group_->ExecuteInThread(); 110 } 111 112 private: 113 SourceGroup* group_; 114 }; 115 116 void ExecuteInThread(); 117 118 base::Semaphore next_semaphore_; 119 base::Semaphore done_semaphore_; 120 base::Thread* thread_; 121 122 void ExitShell(int exit_code); 123 Local<String> ReadFile(Isolate* isolate, const char* name); 124 125 const char** argv_; 126 int begin_offset_; 127 int end_offset_; 128 }; 129 130 // The backing store of an ArrayBuffer or SharedArrayBuffer, after 131 // Externalize() has been called on it. 132 class ExternalizedContents { 133 public: 134 explicit ExternalizedContents(const ArrayBuffer::Contents& contents) 135 : data_(contents.Data()), 136 length_(contents.ByteLength()), 137 deleter_(contents.Deleter()), 138 deleter_data_(contents.DeleterData()) {} 139 explicit ExternalizedContents(const SharedArrayBuffer::Contents& contents) 140 : data_(contents.Data()), 141 length_(contents.ByteLength()), 142 deleter_(contents.Deleter()), 143 deleter_data_(contents.DeleterData()) {} 144 ExternalizedContents(ExternalizedContents&& other) V8_NOEXCEPT 145 : data_(other.data_), 146 length_(other.length_), 147 deleter_(other.deleter_), 148 deleter_data_(other.deleter_data_) { 149 other.data_ = nullptr; 150 other.length_ = 0; 151 other.deleter_ = nullptr; 152 other.deleter_data_ = nullptr; 153 } 154 ExternalizedContents& operator=(ExternalizedContents&& other) V8_NOEXCEPT { 155 if (this != &other) { 156 data_ = other.data_; 157 length_ = other.length_; 158 deleter_ = other.deleter_; 159 deleter_data_ = other.deleter_data_; 160 other.data_ = nullptr; 161 other.length_ = 0; 162 other.deleter_ = nullptr; 163 other.deleter_data_ = nullptr; 164 } 165 return *this; 166 } 167 ~ExternalizedContents(); 168 169 private: 170 void* data_; 171 size_t length_; 172 ArrayBuffer::Contents::DeleterCallback deleter_; 173 void* deleter_data_; 174 175 DISALLOW_COPY_AND_ASSIGN(ExternalizedContents); 176 }; 177 178 class SerializationData { 179 public: 180 SerializationData() : size_(0) {} 181 182 uint8_t* data() { return data_.get(); } 183 size_t size() { return size_; } 184 const std::vector<ArrayBuffer::Contents>& array_buffer_contents() { 185 return array_buffer_contents_; 186 } 187 const std::vector<SharedArrayBuffer::Contents>& 188 shared_array_buffer_contents() { 189 return shared_array_buffer_contents_; 190 } 191 const std::vector<WasmCompiledModule::TransferrableModule>& 192 transferrable_modules() { 193 return transferrable_modules_; 194 } 195 196 private: 197 struct DataDeleter { 198 void operator()(uint8_t* p) const { free(p); } 199 }; 200 201 std::unique_ptr<uint8_t, DataDeleter> data_; 202 size_t size_; 203 std::vector<ArrayBuffer::Contents> array_buffer_contents_; 204 std::vector<SharedArrayBuffer::Contents> shared_array_buffer_contents_; 205 std::vector<WasmCompiledModule::TransferrableModule> transferrable_modules_; 206 207 private: 208 friend class Serializer; 209 210 DISALLOW_COPY_AND_ASSIGN(SerializationData); 211 }; 212 213 214 class SerializationDataQueue { 215 public: 216 void Enqueue(std::unique_ptr<SerializationData> data); 217 bool Dequeue(std::unique_ptr<SerializationData>* data); 218 bool IsEmpty(); 219 void Clear(); 220 221 private: 222 base::Mutex mutex_; 223 std::vector<std::unique_ptr<SerializationData>> data_; 224 }; 225 226 227 class Worker { 228 public: 229 Worker(); 230 ~Worker(); 231 232 // Run the given script on this Worker. This function should only be called 233 // once, and should only be called by the thread that created the Worker. 234 void StartExecuteInThread(const char* script); 235 // Post a message to the worker's incoming message queue. The worker will 236 // take ownership of the SerializationData. 237 // This function should only be called by the thread that created the Worker. 238 void PostMessage(std::unique_ptr<SerializationData> data); 239 // Synchronously retrieve messages from the worker's outgoing message queue. 240 // If there is no message in the queue, block until a message is available. 241 // If there are no messages in the queue and the worker is no longer running, 242 // return nullptr. 243 // This function should only be called by the thread that created the Worker. 244 std::unique_ptr<SerializationData> GetMessage(); 245 // Terminate the worker's event loop. Messages from the worker that have been 246 // queued can still be read via GetMessage(). 247 // This function can be called by any thread. 248 void Terminate(); 249 // Terminate and join the thread. 250 // This function can be called by any thread. 251 void WaitForThread(); 252 253 private: 254 class WorkerThread : public base::Thread { 255 public: 256 explicit WorkerThread(Worker* worker) 257 : base::Thread(base::Thread::Options("WorkerThread")), 258 worker_(worker) {} 259 260 virtual void Run() { worker_->ExecuteInThread(); } 261 262 private: 263 Worker* worker_; 264 }; 265 266 void ExecuteInThread(); 267 static void PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args); 268 269 base::Semaphore in_semaphore_; 270 base::Semaphore out_semaphore_; 271 SerializationDataQueue in_queue_; 272 SerializationDataQueue out_queue_; 273 base::Thread* thread_; 274 char* script_; 275 base::Atomic32 running_; 276 }; 277 278 class PerIsolateData { 279 public: 280 explicit PerIsolateData(Isolate* isolate); 281 282 ~PerIsolateData(); 283 284 inline static PerIsolateData* Get(Isolate* isolate) { 285 return reinterpret_cast<PerIsolateData*>(isolate->GetData(0)); 286 } 287 288 class RealmScope { 289 public: 290 explicit RealmScope(PerIsolateData* data); 291 ~RealmScope(); 292 293 private: 294 PerIsolateData* data_; 295 }; 296 297 inline void SetTimeout(Local<Function> callback, Local<Context> context); 298 inline MaybeLocal<Function> GetTimeoutCallback(); 299 inline MaybeLocal<Context> GetTimeoutContext(); 300 301 AsyncHooks* GetAsyncHooks() { return async_hooks_wrapper_; } 302 303 private: 304 friend class Shell; 305 friend class RealmScope; 306 Isolate* isolate_; 307 int realm_count_; 308 int realm_current_; 309 int realm_switch_; 310 Global<Context>* realms_; 311 Global<Value> realm_shared_; 312 std::queue<Global<Function>> set_timeout_callbacks_; 313 std::queue<Global<Context>> set_timeout_contexts_; 314 AsyncHooks* async_hooks_wrapper_; 315 316 int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args, 317 int arg_offset); 318 int RealmFind(Local<Context> context); 319 }; 320 321 class ShellOptions { 322 public: 323 enum CodeCacheOptions { 324 kNoProduceCache, 325 kProduceCache, 326 kProduceCacheAfterExecute 327 }; 328 329 ShellOptions() 330 : send_idle_notification(false), 331 invoke_weak_callbacks(false), 332 omit_quit(false), 333 wait_for_wasm(true), 334 stress_opt(false), 335 stress_deopt(false), 336 stress_runs(1), 337 interactive_shell(false), 338 test_shell(false), 339 expected_to_throw(false), 340 mock_arraybuffer_allocator(false), 341 enable_inspector(false), 342 num_isolates(1), 343 compile_options(v8::ScriptCompiler::kNoCompileOptions), 344 stress_background_compile(false), 345 code_cache_options(CodeCacheOptions::kNoProduceCache), 346 isolate_sources(nullptr), 347 icu_data_file(nullptr), 348 natives_blob(nullptr), 349 snapshot_blob(nullptr), 350 trace_enabled(false), 351 trace_path(nullptr), 352 trace_config(nullptr), 353 lcov_file(nullptr), 354 disable_in_process_stack_traces(false), 355 read_from_tcp_port(-1) {} 356 357 ~ShellOptions() { 358 delete[] isolate_sources; 359 } 360 361 bool send_idle_notification; 362 bool invoke_weak_callbacks; 363 bool omit_quit; 364 bool wait_for_wasm; 365 bool stress_opt; 366 bool stress_deopt; 367 int stress_runs; 368 bool interactive_shell; 369 bool test_shell; 370 bool expected_to_throw; 371 bool mock_arraybuffer_allocator; 372 bool enable_inspector; 373 int num_isolates; 374 v8::ScriptCompiler::CompileOptions compile_options; 375 bool stress_background_compile; 376 CodeCacheOptions code_cache_options; 377 SourceGroup* isolate_sources; 378 const char* icu_data_file; 379 const char* natives_blob; 380 const char* snapshot_blob; 381 bool trace_enabled; 382 const char* trace_path; 383 const char* trace_config; 384 const char* lcov_file; 385 bool disable_in_process_stack_traces; 386 int read_from_tcp_port; 387 bool enable_os_system = false; 388 bool quiet_load = false; 389 int thread_pool_size = 0; 390 }; 391 392 class Shell : public i::AllStatic { 393 public: 394 enum PrintResult : bool { kPrintResult = true, kNoPrintResult = false }; 395 enum ReportExceptions : bool { 396 kReportExceptions = true, 397 kNoReportExceptions = false 398 }; 399 enum ProcessMessageQueue : bool { 400 kProcessMessageQueue = true, 401 kNoProcessMessageQueue = false 402 }; 403 404 static bool ExecuteString(Isolate* isolate, Local<String> source, 405 Local<Value> name, PrintResult print_result, 406 ReportExceptions report_exceptions, 407 ProcessMessageQueue process_message_queue); 408 static bool ExecuteModule(Isolate* isolate, const char* file_name); 409 static void ReportException(Isolate* isolate, TryCatch* try_catch); 410 static Local<String> ReadFile(Isolate* isolate, const char* name); 411 static Local<Context> CreateEvaluationContext(Isolate* isolate); 412 static int RunMain(Isolate* isolate, int argc, char* argv[], bool last_run); 413 static int Main(int argc, char* argv[]); 414 static void Exit(int exit_code); 415 static void OnExit(Isolate* isolate); 416 static void CollectGarbage(Isolate* isolate); 417 static bool EmptyMessageQueues(Isolate* isolate); 418 static void CompleteMessageLoop(Isolate* isolate); 419 420 static std::unique_ptr<SerializationData> SerializeValue( 421 Isolate* isolate, Local<Value> value, Local<Value> transfer); 422 static MaybeLocal<Value> DeserializeValue( 423 Isolate* isolate, std::unique_ptr<SerializationData> data); 424 static void CleanupWorkers(); 425 static int* LookupCounter(const char* name); 426 static void* CreateHistogram(const char* name, 427 int min, 428 int max, 429 size_t buckets); 430 static void AddHistogramSample(void* histogram, int sample); 431 static void MapCounters(v8::Isolate* isolate, const char* name); 432 433 static void PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args); 434 435 static void RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args); 436 static void RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args); 437 static void RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args); 438 static void RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args); 439 static void RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args); 440 static void RealmCreateAllowCrossRealmAccess( 441 const v8::FunctionCallbackInfo<v8::Value>& args); 442 static void RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args); 443 static void RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args); 444 static void RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args); 445 static void RealmSharedGet(Local<String> property, 446 const PropertyCallbackInfo<Value>& info); 447 static void RealmSharedSet(Local<String> property, 448 Local<Value> value, 449 const PropertyCallbackInfo<void>& info); 450 451 static void AsyncHooksCreateHook( 452 const v8::FunctionCallbackInfo<v8::Value>& args); 453 static void AsyncHooksExecutionAsyncId( 454 const v8::FunctionCallbackInfo<v8::Value>& args); 455 static void AsyncHooksTriggerAsyncId( 456 const v8::FunctionCallbackInfo<v8::Value>& args); 457 458 static void Print(const v8::FunctionCallbackInfo<v8::Value>& args); 459 static void PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args); 460 static void Write(const v8::FunctionCallbackInfo<v8::Value>& args); 461 static void WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args); 462 static void NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args); 463 static void QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args); 464 static void Quit(const v8::FunctionCallbackInfo<v8::Value>& args); 465 static void Version(const v8::FunctionCallbackInfo<v8::Value>& args); 466 static void Read(const v8::FunctionCallbackInfo<v8::Value>& args); 467 static void ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args); 468 static Local<String> ReadFromStdin(Isolate* isolate); 469 static void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args) { 470 args.GetReturnValue().Set(ReadFromStdin(args.GetIsolate())); 471 } 472 static void Load(const v8::FunctionCallbackInfo<v8::Value>& args); 473 static void SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args); 474 static void WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args); 475 static void WorkerPostMessage( 476 const v8::FunctionCallbackInfo<v8::Value>& args); 477 static void WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args); 478 static void WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args); 479 // The OS object on the global object contains methods for performing 480 // operating system calls: 481 // 482 // os.system("program_name", ["arg1", "arg2", ...], timeout1, timeout2) will 483 // run the command, passing the arguments to the program. The standard output 484 // of the program will be picked up and returned as a multiline string. If 485 // timeout1 is present then it should be a number. -1 indicates no timeout 486 // and a positive number is used as a timeout in milliseconds that limits the 487 // time spent waiting between receiving output characters from the program. 488 // timeout2, if present, should be a number indicating the limit in 489 // milliseconds on the total running time of the program. Exceptions are 490 // thrown on timeouts or other errors or if the exit status of the program 491 // indicates an error. 492 // 493 // os.chdir(dir) changes directory to the given directory. Throws an 494 // exception/ on error. 495 // 496 // os.setenv(variable, value) sets an environment variable. Repeated calls to 497 // this method leak memory due to the API of setenv in the standard C library. 498 // 499 // os.umask(alue) calls the umask system call and returns the old umask. 500 // 501 // os.mkdirp(name, mask) creates a directory. The mask (if present) is anded 502 // with the current umask. Intermediate directories are created if necessary. 503 // An exception is not thrown if the directory already exists. Analogous to 504 // the "mkdir -p" command. 505 static void System(const v8::FunctionCallbackInfo<v8::Value>& args); 506 static void ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args); 507 static void SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args); 508 static void UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args); 509 static void SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args); 510 static void MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args); 511 static void RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args); 512 static MaybeLocal<Promise> HostImportModuleDynamically( 513 Local<Context> context, Local<ScriptOrModule> referrer, 514 Local<String> specifier); 515 static void HostInitializeImportMetaObject(Local<Context> context, 516 Local<Module> module, 517 Local<Object> meta); 518 519 // Data is of type DynamicImportData*. We use void* here to be able 520 // to conform with MicrotaskCallback interface and enqueue this 521 // function in the microtask queue. 522 static void DoHostImportModuleDynamically(void* data); 523 static void AddOSMethods(v8::Isolate* isolate, 524 Local<ObjectTemplate> os_template); 525 526 static const char* kPrompt; 527 static ShellOptions options; 528 static ArrayBuffer::Allocator* array_buffer_allocator; 529 530 static void SetWaitUntilDone(Isolate* isolate, bool value); 531 532 static char* ReadCharsFromTcpPort(const char* name, int* size_out); 533 534 static void set_script_executed() { script_executed_.store(true); } 535 static bool use_interactive_shell() { 536 return (options.interactive_shell || !script_executed_.load()) && 537 !options.test_shell; 538 } 539 540 private: 541 static Global<Context> evaluation_context_; 542 static base::OnceType quit_once_; 543 static Global<Function> stringify_function_; 544 static CounterMap* counter_map_; 545 // We statically allocate a set of local counters to be used if we 546 // don't want to store the stats in a memory-mapped file 547 static CounterCollection local_counters_; 548 static CounterCollection* counters_; 549 static base::OS::MemoryMappedFile* counters_file_; 550 static base::LazyMutex context_mutex_; 551 static const base::TimeTicks kInitialTicks; 552 553 static base::LazyMutex workers_mutex_; // Guards the following members. 554 static bool allow_new_workers_; 555 static std::vector<Worker*> workers_; 556 static std::vector<ExternalizedContents> externalized_contents_; 557 558 // Multiple isolates may update this flag concurrently. 559 static std::atomic<bool> script_executed_; 560 561 static void WriteIgnitionDispatchCountersFile(v8::Isolate* isolate); 562 // Append LCOV coverage data to file. 563 static void WriteLcovData(v8::Isolate* isolate, const char* file); 564 static Counter* GetCounter(const char* name, bool is_histogram); 565 static Local<String> Stringify(Isolate* isolate, Local<Value> value); 566 static void Initialize(Isolate* isolate); 567 static void RunShell(Isolate* isolate); 568 static bool SetOptions(int argc, char* argv[]); 569 static Local<ObjectTemplate> CreateGlobalTemplate(Isolate* isolate); 570 static MaybeLocal<Context> CreateRealm( 571 const v8::FunctionCallbackInfo<v8::Value>& args, int index, 572 v8::MaybeLocal<Value> global_object); 573 static void DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args, 574 int index); 575 static MaybeLocal<Module> FetchModuleTree(v8::Local<v8::Context> context, 576 const std::string& file_name); 577 static ScriptCompiler::CachedData* LookupCodeCache(Isolate* isolate, 578 Local<Value> name); 579 static void StoreInCodeCache(Isolate* isolate, Local<Value> name, 580 const ScriptCompiler::CachedData* data); 581 // We may have multiple isolates running concurrently, so the access to 582 // the isolate_status_ needs to be concurrency-safe. 583 static base::LazyMutex isolate_status_lock_; 584 static std::map<Isolate*, bool> isolate_status_; 585 586 static base::LazyMutex cached_code_mutex_; 587 static std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>> 588 cached_code_map_; 589 }; 590 591 592 } // namespace v8 593 594 595 #endif // V8_D8_H_ 596