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