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