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