Line data Source code
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 "include/libplatform/libplatform.h"
21 : #include "include/libplatform/v8-tracing.h"
22 : #include "include/v8-inspector.h"
23 : #include "src/api-inl.h"
24 : #include "src/base/cpu.h"
25 : #include "src/base/logging.h"
26 : #include "src/base/platform/platform.h"
27 : #include "src/base/platform/time.h"
28 : #include "src/base/sys-info.h"
29 : #include "src/basic-block-profiler.h"
30 : #include "src/d8-console.h"
31 : #include "src/d8-platforms.h"
32 : #include "src/d8.h"
33 : #include "src/debug/debug-interface.h"
34 : #include "src/interpreter/interpreter.h"
35 : #include "src/msan.h"
36 : #include "src/objects-inl.h"
37 : #include "src/objects.h"
38 : #include "src/ostreams.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 = 100;
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 172638 : class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
72 : public:
73 0 : void* Allocate(size_t length) override {
74 231418 : return allocator_->Allocate(length);
75 : }
76 :
77 0 : void* AllocateUninitialized(size_t length) override {
78 13474 : return allocator_->AllocateUninitialized(length);
79 : }
80 :
81 0 : void Free(void* data, size_t length) override {
82 244911 : allocator_->Free(data, length);
83 0 : }
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 57546 : class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
92 : public:
93 232196 : void* Allocate(size_t length) override {
94 232196 : if (length >= kVMThreshold) return AllocateVM(length);
95 231334 : return ArrayBufferAllocatorBase::Allocate(length);
96 : }
97 :
98 13492 : void* AllocateUninitialized(size_t length) override {
99 13492 : if (length >= kVMThreshold) return AllocateVM(length);
100 13474 : return ArrayBufferAllocatorBase::AllocateUninitialized(length);
101 : }
102 :
103 245662 : void Free(void* data, size_t length) override {
104 245662 : if (length >= kVMThreshold) {
105 850 : FreeVM(data, length);
106 : } else {
107 : ArrayBufferAllocatorBase::Free(data, length);
108 : }
109 245663 : }
110 :
111 : private:
112 : static constexpr size_t kVMThreshold = 65536;
113 : static constexpr size_t kTwoGB = 2u * 1024u * 1024u * 1024u;
114 :
115 895 : 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 895 : if (length >= kTwoGB) return nullptr;
120 :
121 859 : v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
122 859 : size_t page_size = page_allocator->AllocatePageSize();
123 859 : size_t allocated = RoundUp(length, page_size);
124 : // Rounding up could go over the limit.
125 859 : if (allocated >= kTwoGB) return nullptr;
126 : return i::AllocatePages(page_allocator, nullptr, allocated, page_size,
127 850 : PageAllocator::kReadWrite);
128 : }
129 :
130 850 : void FreeVM(void* data, size_t length) {
131 850 : v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
132 850 : size_t page_size = page_allocator->AllocatePageSize();
133 850 : size_t allocated = RoundUp(length, page_size);
134 850 : CHECK(i::FreePages(page_allocator, data, allocated));
135 850 : }
136 : };
137 :
138 : // ArrayBuffer allocator that never allocates over 10MB.
139 115092 : class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
140 : protected:
141 99 : void* Allocate(size_t length) override {
142 99 : return ArrayBufferAllocatorBase::Allocate(Adjust(length));
143 : }
144 :
145 0 : void* AllocateUninitialized(size_t length) override {
146 0 : return ArrayBufferAllocatorBase::AllocateUninitialized(Adjust(length));
147 : }
148 :
149 99 : void Free(void* data, size_t length) override {
150 99 : return ArrayBufferAllocatorBase::Free(data, Adjust(length));
151 : }
152 :
153 : private:
154 : size_t Adjust(size_t length) {
155 : const size_t kAllocationLimit = 10 * kMB;
156 198 : return length > kAllocationLimit ? i::AllocatePageSize() : length;
157 : }
158 : };
159 :
160 : // ArrayBuffer allocator that can be equipped with a limit to simulate system
161 : // OOM.
162 28773 : class MockArrayBufferAllocatiorWithLimit : public MockArrayBufferAllocator {
163 : public:
164 : explicit MockArrayBufferAllocatiorWithLimit(size_t allocation_limit)
165 28773 : : space_left_(allocation_limit) {}
166 :
167 : protected:
168 0 : void* Allocate(size_t length) override {
169 0 : if (length > space_left_) {
170 : return nullptr;
171 : }
172 : space_left_ -= length;
173 0 : return MockArrayBufferAllocator::Allocate(length);
174 : }
175 :
176 0 : void* AllocateUninitialized(size_t length) override {
177 0 : if (length > space_left_) {
178 : return nullptr;
179 : }
180 : space_left_ -= length;
181 0 : return MockArrayBufferAllocator::AllocateUninitialized(length);
182 : }
183 :
184 0 : void Free(void* data, size_t length) override {
185 : space_left_ += length;
186 0 : return MockArrayBufferAllocator::Free(data, length);
187 : }
188 :
189 : private:
190 : std::atomic<size_t> space_left_;
191 : };
192 :
193 : v8::Platform* g_default_platform;
194 28773 : std::unique_ptr<v8::Platform> g_platform;
195 :
196 4280 : static Local<Value> Throw(Isolate* isolate, const char* message) {
197 : return isolate->ThrowException(
198 : String::NewFromUtf8(isolate, message, NewStringType::kNormal)
199 8560 : .ToLocalChecked());
200 : }
201 :
202 631 : static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
203 : Local<v8::Object> object, const char* property) {
204 : Local<String> v8_str =
205 : String::NewFromUtf8(isolate, property, NewStringType::kNormal)
206 631 : .ToLocalChecked();
207 1262 : return object->Get(context, v8_str).ToLocalChecked();
208 : }
209 :
210 2366 : Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
211 2366 : if (object->InternalFieldCount() != 1) {
212 0 : Throw(isolate, "this is not a Worker");
213 0 : return nullptr;
214 : }
215 :
216 : Worker* worker =
217 : static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0));
218 2366 : if (worker == nullptr) {
219 9 : Throw(isolate, "Worker is defunct because main thread is terminating");
220 9 : return nullptr;
221 : }
222 :
223 : return worker;
224 : }
225 :
226 : base::Thread::Options GetThreadOptions(const char* name) {
227 : // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
228 : // which is not enough to parse the big literal expressions used in tests.
229 : // The stack size should be at least StackGuard::kLimitSize + some
230 : // OS-specific padding for thread startup code. 2Mbytes seems to be enough.
231 : return base::Thread::Options(name, 2 * kMB);
232 : }
233 :
234 : } // namespace
235 :
236 : namespace tracing {
237 :
238 : namespace {
239 :
240 : // String options that can be used to initialize TraceOptions.
241 : const char kRecordUntilFull[] = "record-until-full";
242 : const char kRecordContinuously[] = "record-continuously";
243 : const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
244 :
245 : const char kRecordModeParam[] = "record_mode";
246 : const char kEnableSystraceParam[] = "enable_systrace";
247 : const char kEnableArgumentFilterParam[] = "enable_argument_filter";
248 : const char kIncludedCategoriesParam[] = "included_categories";
249 :
250 : class TraceConfigParser {
251 : public:
252 0 : static void FillTraceConfig(v8::Isolate* isolate,
253 : platform::tracing::TraceConfig* trace_config,
254 : const char* json_str) {
255 0 : HandleScope outer_scope(isolate);
256 0 : Local<Context> context = Context::New(isolate);
257 : Context::Scope context_scope(context);
258 0 : HandleScope inner_scope(isolate);
259 :
260 : Local<String> source =
261 : String::NewFromUtf8(isolate, json_str, NewStringType::kNormal)
262 0 : .ToLocalChecked();
263 0 : Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
264 0 : Local<v8::Object> trace_config_object = Local<v8::Object>::Cast(result);
265 :
266 : trace_config->SetTraceRecordMode(
267 0 : GetTraceRecordMode(isolate, context, trace_config_object));
268 0 : if (GetBoolean(isolate, context, trace_config_object,
269 : kEnableSystraceParam)) {
270 : trace_config->EnableSystrace();
271 : }
272 0 : if (GetBoolean(isolate, context, trace_config_object,
273 : kEnableArgumentFilterParam)) {
274 : trace_config->EnableArgumentFilter();
275 : }
276 : UpdateIncludedCategoriesList(isolate, context, trace_config_object,
277 0 : trace_config);
278 0 : }
279 :
280 : private:
281 0 : static bool GetBoolean(v8::Isolate* isolate, Local<Context> context,
282 : Local<v8::Object> object, const char* property) {
283 0 : Local<Value> value = GetValue(isolate, context, object, property);
284 0 : if (value->IsNumber()) {
285 0 : return value->BooleanValue(isolate);
286 : }
287 : return false;
288 : }
289 :
290 0 : static int UpdateIncludedCategoriesList(
291 : v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
292 : platform::tracing::TraceConfig* trace_config) {
293 : Local<Value> value =
294 0 : GetValue(isolate, context, object, kIncludedCategoriesParam);
295 0 : if (value->IsArray()) {
296 : Local<Array> v8_array = Local<Array>::Cast(value);
297 0 : for (int i = 0, length = v8_array->Length(); i < length; ++i) {
298 0 : Local<Value> v = v8_array->Get(context, i)
299 0 : .ToLocalChecked()
300 : ->ToString(context)
301 0 : .ToLocalChecked();
302 0 : String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
303 0 : trace_config->AddIncludedCategory(*str);
304 0 : }
305 0 : return v8_array->Length();
306 : }
307 : return 0;
308 : }
309 :
310 0 : static platform::tracing::TraceRecordMode GetTraceRecordMode(
311 : v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object) {
312 0 : Local<Value> value = GetValue(isolate, context, object, kRecordModeParam);
313 0 : if (value->IsString()) {
314 0 : Local<String> v8_string = value->ToString(context).ToLocalChecked();
315 0 : String::Utf8Value str(isolate, v8_string);
316 0 : if (strcmp(kRecordUntilFull, *str) == 0) {
317 0 : return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
318 0 : } else if (strcmp(kRecordContinuously, *str) == 0) {
319 : return platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY;
320 0 : } else if (strcmp(kRecordAsMuchAsPossible, *str) == 0) {
321 : return platform::tracing::TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE;
322 0 : }
323 : }
324 : return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
325 : }
326 : };
327 :
328 : } // namespace
329 :
330 0 : static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
331 : v8::Isolate* isolate, const char* json_str) {
332 : platform::tracing::TraceConfig* trace_config =
333 0 : new platform::tracing::TraceConfig();
334 0 : TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str);
335 0 : return trace_config;
336 : }
337 :
338 : } // namespace tracing
339 :
340 :
341 36 : class ExternalOwningOneByteStringResource
342 : : public String::ExternalOneByteStringResource {
343 : public:
344 : ExternalOwningOneByteStringResource() {}
345 : ExternalOwningOneByteStringResource(
346 : std::unique_ptr<base::OS::MemoryMappedFile> file)
347 18 : : file_(std::move(file)) {}
348 111 : const char* data() const override {
349 111 : return static_cast<char*>(file_->memory());
350 : }
351 144 : size_t length() const override { return file_->size(); }
352 :
353 : private:
354 : std::unique_ptr<base::OS::MemoryMappedFile> file_;
355 : };
356 :
357 : CounterMap* Shell::counter_map_;
358 : base::OS::MemoryMappedFile* Shell::counters_file_ = nullptr;
359 : CounterCollection Shell::local_counters_;
360 : CounterCollection* Shell::counters_ = &local_counters_;
361 : base::LazyMutex Shell::context_mutex_;
362 28773 : const base::TimeTicks Shell::kInitialTicks =
363 : base::TimeTicks::HighResolutionNow();
364 28773 : Global<Function> Shell::stringify_function_;
365 : base::LazyMutex Shell::workers_mutex_;
366 : bool Shell::allow_new_workers_ = true;
367 28773 : std::vector<Worker*> Shell::workers_;
368 28773 : std::vector<ExternalizedContents> Shell::externalized_contents_;
369 : std::atomic<bool> Shell::script_executed_{false};
370 : base::LazyMutex Shell::isolate_status_lock_;
371 28773 : std::map<v8::Isolate*, bool> Shell::isolate_status_;
372 : base::LazyMutex Shell::cached_code_mutex_;
373 : std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
374 28773 : Shell::cached_code_map_;
375 :
376 28773 : Global<Context> Shell::evaluation_context_;
377 : ArrayBuffer::Allocator* Shell::array_buffer_allocator;
378 28773 : ShellOptions Shell::options;
379 : base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
380 :
381 : // Dummy external source stream which returns the whole source in one go.
382 38838 : class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
383 : public:
384 25892 : DummySourceStream(Local<String> source, Isolate* isolate) : done_(false) {
385 12946 : source_length_ = source->Utf8Length(isolate);
386 12946 : source_buffer_.reset(new uint8_t[source_length_]);
387 : source->WriteUtf8(isolate, reinterpret_cast<char*>(source_buffer_.get()),
388 25892 : source_length_);
389 12946 : }
390 :
391 25883 : size_t GetMoreData(const uint8_t** src) override {
392 25883 : if (done_) {
393 : return 0;
394 : }
395 12946 : *src = source_buffer_.release();
396 12946 : done_ = true;
397 :
398 12946 : return source_length_;
399 : }
400 :
401 : private:
402 : int source_length_;
403 : std::unique_ptr<uint8_t[]> source_buffer_;
404 : bool done_;
405 : };
406 :
407 25892 : class BackgroundCompileThread : public base::Thread {
408 : public:
409 12946 : BackgroundCompileThread(Isolate* isolate, Local<String> source)
410 : : base::Thread(GetThreadOptions("BackgroundCompileThread")),
411 : source_(source),
412 12946 : streamed_source_(new DummySourceStream(source, isolate),
413 : v8::ScriptCompiler::StreamedSource::UTF8),
414 : task_(v8::ScriptCompiler::StartStreamingScript(isolate,
415 38838 : &streamed_source_)) {}
416 :
417 25892 : void Run() override { task_->Run(); }
418 :
419 : v8::ScriptCompiler::StreamedSource* streamed_source() {
420 : return &streamed_source_;
421 : }
422 :
423 : private:
424 : Local<String> source_;
425 : v8::ScriptCompiler::StreamedSource streamed_source_;
426 : std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask> task_;
427 : };
428 :
429 108 : ScriptCompiler::CachedData* Shell::LookupCodeCache(Isolate* isolate,
430 : Local<Value> source) {
431 : base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
432 108 : CHECK(source->IsString());
433 216 : v8::String::Utf8Value key(isolate, source);
434 : DCHECK(*key);
435 216 : auto entry = cached_code_map_.find(*key);
436 212 : if (entry != cached_code_map_.end() && entry->second) {
437 104 : int length = entry->second->length;
438 104 : uint8_t* cache = new uint8_t[length];
439 104 : memcpy(cache, entry->second->data, length);
440 : ScriptCompiler::CachedData* cached_data = new ScriptCompiler::CachedData(
441 104 : cache, length, ScriptCompiler::CachedData::BufferOwned);
442 104 : return cached_data;
443 : }
444 : return nullptr;
445 : }
446 :
447 243 : void Shell::StoreInCodeCache(Isolate* isolate, Local<Value> source,
448 : const ScriptCompiler::CachedData* cache_data) {
449 : base::MutexGuard lock_guard(cached_code_mutex_.Pointer());
450 243 : CHECK(source->IsString());
451 486 : if (cache_data == nullptr) return;
452 468 : v8::String::Utf8Value key(isolate, source);
453 : DCHECK(*key);
454 234 : int length = cache_data->length;
455 234 : uint8_t* cache = new uint8_t[length];
456 234 : memcpy(cache, cache_data->data, length);
457 702 : cached_code_map_[*key] = std::unique_ptr<ScriptCompiler::CachedData>(
458 : new ScriptCompiler::CachedData(cache, length,
459 234 : ScriptCompiler::CachedData::BufferOwned));
460 : }
461 :
462 : // Executes a string within the current v8 context.
463 116250 : bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
464 : Local<Value> name, PrintResult print_result,
465 : ReportExceptions report_exceptions,
466 : ProcessMessageQueue process_message_queue) {
467 116250 : HandleScope handle_scope(isolate);
468 232500 : TryCatch try_catch(isolate);
469 116250 : try_catch.SetVerbose(true);
470 :
471 : MaybeLocal<Value> maybe_result;
472 : bool success = true;
473 : {
474 : PerIsolateData* data = PerIsolateData::Get(isolate);
475 : Local<Context> realm =
476 116250 : Local<Context>::New(isolate, data->realms_[data->realm_current_]);
477 : Context::Scope context_scope(realm);
478 : MaybeLocal<Script> maybe_script;
479 116250 : Local<Context> context(isolate->GetCurrentContext());
480 : ScriptOrigin origin(name);
481 :
482 116250 : if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
483 : ScriptCompiler::CachedData* cached_code =
484 108 : LookupCodeCache(isolate, source);
485 108 : if (cached_code != nullptr) {
486 : ScriptCompiler::Source script_source(source, origin, cached_code);
487 : maybe_script = ScriptCompiler::Compile(context, &script_source,
488 104 : options.compile_options);
489 104 : CHECK(!cached_code->rejected);
490 : } else {
491 : ScriptCompiler::Source script_source(source, origin);
492 : maybe_script = ScriptCompiler::Compile(
493 4 : context, &script_source, ScriptCompiler::kNoCompileOptions);
494 : }
495 116142 : } else if (options.stress_background_compile) {
496 : // Start a background thread compiling the script.
497 12946 : BackgroundCompileThread background_compile_thread(isolate, source);
498 12946 : background_compile_thread.Start();
499 :
500 : // In parallel, compile on the main thread to flush out any data races.
501 : {
502 12946 : TryCatch ignore_try_catch(isolate);
503 : ScriptCompiler::Source script_source(source, origin);
504 12946 : USE(ScriptCompiler::Compile(context, &script_source,
505 12946 : ScriptCompiler::kNoCompileOptions));
506 : }
507 :
508 : // Join with background thread and finalize compilation.
509 12946 : background_compile_thread.Join();
510 : maybe_script = v8::ScriptCompiler::Compile(
511 12946 : context, background_compile_thread.streamed_source(), source, origin);
512 : } else {
513 : ScriptCompiler::Source script_source(source, origin);
514 : maybe_script = ScriptCompiler::Compile(context, &script_source,
515 103196 : options.compile_options);
516 : }
517 :
518 : Local<Script> script;
519 116250 : if (!maybe_script.ToLocal(&script)) {
520 : // Print errors that happened during compilation.
521 1341 : if (report_exceptions) ReportException(isolate, &try_catch);
522 : return false;
523 : }
524 :
525 114909 : if (options.code_cache_options ==
526 : ShellOptions::CodeCacheOptions::kProduceCache) {
527 : // Serialize and store it in memory for the next execution.
528 : ScriptCompiler::CachedData* cached_data =
529 225 : ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
530 225 : StoreInCodeCache(isolate, source, cached_data);
531 225 : delete cached_data;
532 : }
533 114909 : maybe_result = script->Run(realm);
534 114909 : if (options.code_cache_options ==
535 : ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
536 : // Serialize and store it in memory for the next execution.
537 : ScriptCompiler::CachedData* cached_data =
538 18 : ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
539 18 : StoreInCodeCache(isolate, source, cached_data);
540 18 : delete cached_data;
541 : }
542 114909 : if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
543 114909 : data->realm_current_ = data->realm_switch_;
544 : }
545 : Local<Value> result;
546 114909 : if (!maybe_result.ToLocal(&result)) {
547 : DCHECK(try_catch.HasCaught());
548 : // Print errors that happened during execution.
549 3996 : if (report_exceptions) ReportException(isolate, &try_catch);
550 : return false;
551 : }
552 : DCHECK(!try_catch.HasCaught());
553 110913 : if (print_result) {
554 0 : if (options.test_shell) {
555 0 : if (!result->IsUndefined()) {
556 : // If all went well and the result wasn't undefined then print
557 : // the returned value.
558 0 : v8::String::Utf8Value str(isolate, result);
559 0 : fwrite(*str, sizeof(**str), str.length(), stdout);
560 0 : printf("\n");
561 : }
562 : } else {
563 0 : v8::String::Utf8Value str(isolate, Stringify(isolate, result));
564 0 : fwrite(*str, sizeof(**str), str.length(), stdout);
565 0 : printf("\n");
566 : }
567 : }
568 227163 : return success;
569 : }
570 :
571 : namespace {
572 :
573 2467 : std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
574 2467 : String::Utf8Value utf8(isolate, v8_str);
575 : // Should not be able to fail since the input is a String.
576 2467 : CHECK(*utf8);
577 2467 : return *utf8;
578 : }
579 :
580 : bool IsAbsolutePath(const std::string& path) {
581 : #if defined(_WIN32) || defined(_WIN64)
582 : // TODO(adamk): This is an incorrect approximation, but should
583 : // work for all our test-running cases.
584 : return path.find(':') != std::string::npos;
585 : #else
586 2958 : return path[0] == '/';
587 : #endif
588 : }
589 :
590 896 : std::string GetWorkingDirectory() {
591 : #if defined(_WIN32) || defined(_WIN64)
592 : char system_buffer[MAX_PATH];
593 : // TODO(adamk): Support Unicode paths.
594 : DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
595 : CHECK_GT(len, 0);
596 : return system_buffer;
597 : #else
598 : char curdir[PATH_MAX];
599 896 : CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
600 896 : return curdir;
601 : #endif
602 : }
603 :
604 : // Returns the directory part of path, without the trailing '/'.
605 2449 : std::string DirName(const std::string& path) {
606 : DCHECK(IsAbsolutePath(path));
607 : size_t last_slash = path.find_last_of('/');
608 : DCHECK(last_slash != std::string::npos);
609 2449 : return path.substr(0, last_slash);
610 : }
611 :
612 : // Resolves path to an absolute path if necessary, and does some
613 : // normalization (eliding references to the current directory
614 : // and replacing backslashes with slashes).
615 2958 : std::string NormalizePath(const std::string& path,
616 : const std::string& dir_name) {
617 : std::string result;
618 2958 : if (IsAbsolutePath(path)) {
619 : result = path;
620 : } else {
621 4106 : result = dir_name + '/' + path;
622 : }
623 2958 : std::replace(result.begin(), result.end(), '\\', '/');
624 : size_t i;
625 3319 : while ((i = result.find("/./")) != std::string::npos) {
626 361 : result.erase(i, 2);
627 : }
628 2958 : return result;
629 : }
630 :
631 : // Per-context Module data, allowing sharing of module maps
632 : // across top-level module loads.
633 102546 : class ModuleEmbedderData {
634 : private:
635 : class ModuleGlobalHash {
636 : public:
637 51273 : explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
638 2062 : size_t operator()(const Global<Module>& module) const {
639 4124 : return module.Get(isolate_)->GetIdentityHash();
640 : }
641 :
642 : private:
643 : Isolate* isolate_;
644 : };
645 :
646 : public:
647 51273 : explicit ModuleEmbedderData(Isolate* isolate)
648 102546 : : module_to_specifier_map(10, ModuleGlobalHash(isolate)) {}
649 :
650 : // Map from normalized module specifier to Module.
651 : std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
652 : // Map from Module to its URL as defined in the ScriptOrigin
653 : std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
654 : module_to_specifier_map;
655 : };
656 :
657 : enum {
658 : kModuleEmbedderDataIndex,
659 : kInspectorClientIndex
660 : };
661 :
662 51273 : void InitializeModuleEmbedderData(Local<Context> context) {
663 : context->SetAlignedPointerInEmbedderData(
664 51273 : kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
665 51273 : }
666 :
667 : ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
668 : return static_cast<ModuleEmbedderData*>(
669 : context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
670 : }
671 :
672 51273 : void DisposeModuleEmbedderData(Local<Context> context) {
673 51273 : delete GetModuleDataFromContext(context);
674 51273 : context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
675 51273 : }
676 :
677 833 : MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
678 : Local<String> specifier,
679 : Local<Module> referrer) {
680 833 : Isolate* isolate = context->GetIsolate();
681 : ModuleEmbedderData* d = GetModuleDataFromContext(context);
682 : auto specifier_it =
683 833 : d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
684 833 : CHECK(specifier_it != d->module_to_specifier_map.end());
685 : std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
686 2499 : DirName(specifier_it->second));
687 : auto module_it = d->specifier_to_module_map.find(absolute_path);
688 833 : CHECK(module_it != d->specifier_to_module_map.end());
689 1666 : return module_it->second.Get(isolate);
690 : }
691 :
692 : } // anonymous namespace
693 :
694 1337 : MaybeLocal<Module> Shell::FetchModuleTree(Local<Context> context,
695 : const std::string& file_name) {
696 : DCHECK(IsAbsolutePath(file_name));
697 1337 : Isolate* isolate = context->GetIsolate();
698 1337 : Local<String> source_text = ReadFile(isolate, file_name.c_str());
699 1337 : if (source_text.IsEmpty()) {
700 63 : std::string msg = "Error reading: " + file_name;
701 63 : Throw(isolate, msg.c_str());
702 63 : return MaybeLocal<Module>();
703 : }
704 : ScriptOrigin origin(
705 : String::NewFromUtf8(isolate, file_name.c_str(), NewStringType::kNormal)
706 : .ToLocalChecked(),
707 : Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
708 1274 : Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
709 : ScriptCompiler::Source source(source_text, origin);
710 : Local<Module> module;
711 2548 : if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
712 63 : return MaybeLocal<Module>();
713 : }
714 :
715 : ModuleEmbedderData* d = GetModuleDataFromContext(context);
716 1211 : CHECK(d->specifier_to_module_map
717 : .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
718 : .second);
719 1211 : CHECK(d->module_to_specifier_map
720 : .insert(std::make_pair(Global<Module>(isolate, module), file_name))
721 : .second);
722 :
723 1211 : std::string dir_name = DirName(file_name);
724 :
725 2035 : for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
726 824 : Local<String> name = module->GetModuleRequest(i);
727 : std::string absolute_path =
728 1648 : NormalizePath(ToSTLString(isolate, name), dir_name);
729 824 : if (!d->specifier_to_module_map.count(absolute_path)) {
730 1026 : if (FetchModuleTree(context, absolute_path).IsEmpty()) {
731 0 : return MaybeLocal<Module>();
732 : }
733 : }
734 : }
735 :
736 1211 : return module;
737 : }
738 :
739 : namespace {
740 :
741 810 : struct DynamicImportData {
742 405 : DynamicImportData(Isolate* isolate_, Local<String> referrer_,
743 : Local<String> specifier_,
744 : Local<Promise::Resolver> resolver_)
745 405 : : isolate(isolate_) {
746 : referrer.Reset(isolate, referrer_);
747 405 : specifier.Reset(isolate, specifier_);
748 405 : resolver.Reset(isolate, resolver_);
749 405 : }
750 :
751 : Isolate* isolate;
752 : Global<String> referrer;
753 : Global<String> specifier;
754 : Global<Promise::Resolver> resolver;
755 : };
756 :
757 : } // namespace
758 :
759 405 : MaybeLocal<Promise> Shell::HostImportModuleDynamically(
760 : Local<Context> context, Local<ScriptOrModule> referrer,
761 : Local<String> specifier) {
762 405 : Isolate* isolate = context->GetIsolate();
763 :
764 : MaybeLocal<Promise::Resolver> maybe_resolver =
765 405 : Promise::Resolver::New(context);
766 : Local<Promise::Resolver> resolver;
767 405 : if (maybe_resolver.ToLocal(&resolver)) {
768 : DynamicImportData* data = new DynamicImportData(
769 405 : isolate, Local<String>::Cast(referrer->GetResourceName()), specifier,
770 405 : resolver);
771 405 : isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
772 405 : return resolver->GetPromise();
773 : }
774 :
775 0 : return MaybeLocal<Promise>();
776 : }
777 :
778 18 : void Shell::HostInitializeImportMetaObject(Local<Context> context,
779 : Local<Module> module,
780 : Local<Object> meta) {
781 18 : Isolate* isolate = context->GetIsolate();
782 18 : HandleScope handle_scope(isolate);
783 :
784 : ModuleEmbedderData* d = GetModuleDataFromContext(context);
785 : auto specifier_it =
786 18 : d->module_to_specifier_map.find(Global<Module>(isolate, module));
787 18 : CHECK(specifier_it != d->module_to_specifier_map.end());
788 :
789 : Local<String> url_key =
790 : String::NewFromUtf8(isolate, "url", NewStringType::kNormal)
791 18 : .ToLocalChecked();
792 : Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str(),
793 : NewStringType::kNormal)
794 18 : .ToLocalChecked();
795 36 : meta->CreateDataProperty(context, url_key, url).ToChecked();
796 18 : }
797 :
798 405 : void Shell::DoHostImportModuleDynamically(void* import_data) {
799 : std::unique_ptr<DynamicImportData> import_data_(
800 : static_cast<DynamicImportData*>(import_data));
801 405 : Isolate* isolate(import_data_->isolate);
802 621 : HandleScope handle_scope(isolate);
803 :
804 405 : Local<String> referrer(import_data_->referrer.Get(isolate));
805 405 : Local<String> specifier(import_data_->specifier.Get(isolate));
806 : Local<Promise::Resolver> resolver(import_data_->resolver.Get(isolate));
807 :
808 : PerIsolateData* data = PerIsolateData::Get(isolate);
809 405 : Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
810 : Context::Scope context_scope(realm);
811 :
812 405 : std::string source_url = ToSTLString(isolate, referrer);
813 : std::string dir_name =
814 1215 : DirName(NormalizePath(source_url, GetWorkingDirectory()));
815 405 : std::string file_name = ToSTLString(isolate, specifier);
816 405 : std::string absolute_path = NormalizePath(file_name, dir_name);
817 :
818 621 : TryCatch try_catch(isolate);
819 405 : try_catch.SetVerbose(true);
820 :
821 : ModuleEmbedderData* d = GetModuleDataFromContext(realm);
822 : Local<Module> root_module;
823 : auto module_it = d->specifier_to_module_map.find(absolute_path);
824 405 : if (module_it != d->specifier_to_module_map.end()) {
825 : root_module = module_it->second.Get(isolate);
826 666 : } else if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
827 126 : CHECK(try_catch.HasCaught());
828 252 : resolver->Reject(realm, try_catch.Exception()).ToChecked();
829 315 : return;
830 : }
831 :
832 : MaybeLocal<Value> maybe_result;
833 279 : if (root_module->InstantiateModule(realm, ResolveModuleCallback)
834 558 : .FromMaybe(false)) {
835 252 : maybe_result = root_module->Evaluate(realm);
836 252 : EmptyMessageQueues(isolate);
837 : }
838 :
839 : Local<Value> module;
840 279 : if (!maybe_result.ToLocal(&module)) {
841 : DCHECK(try_catch.HasCaught());
842 126 : resolver->Reject(realm, try_catch.Exception()).ToChecked();
843 63 : return;
844 : }
845 :
846 : DCHECK(!try_catch.HasCaught());
847 216 : Local<Value> module_namespace = root_module->GetModuleNamespace();
848 432 : resolver->Resolve(realm, module_namespace).ToChecked();
849 : }
850 :
851 491 : bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
852 491 : HandleScope handle_scope(isolate);
853 :
854 : PerIsolateData* data = PerIsolateData::Get(isolate);
855 491 : Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
856 : Context::Scope context_scope(realm);
857 :
858 1473 : std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
859 :
860 982 : TryCatch try_catch(isolate);
861 491 : try_catch.SetVerbose(true);
862 :
863 : Local<Module> root_module;
864 : MaybeLocal<Value> maybe_exception;
865 :
866 982 : if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
867 0 : CHECK(try_catch.HasCaught());
868 0 : ReportException(isolate, &try_catch);
869 0 : return false;
870 : }
871 :
872 : MaybeLocal<Value> maybe_result;
873 491 : if (root_module->InstantiateModule(realm, ResolveModuleCallback)
874 982 : .FromMaybe(false)) {
875 491 : maybe_result = root_module->Evaluate(realm);
876 491 : EmptyMessageQueues(isolate);
877 : }
878 : Local<Value> result;
879 491 : if (!maybe_result.ToLocal(&result)) {
880 : DCHECK(try_catch.HasCaught());
881 : // Print errors that happened during execution.
882 0 : ReportException(isolate, &try_catch);
883 0 : return false;
884 : }
885 : DCHECK(!try_catch.HasCaught());
886 491 : return true;
887 : }
888 :
889 29442 : PerIsolateData::PerIsolateData(Isolate* isolate)
890 88327 : : isolate_(isolate), realms_(nullptr) {
891 : isolate->SetData(0, this);
892 29443 : if (i::FLAG_expose_async_hooks) {
893 50 : async_hooks_wrapper_ = new AsyncHooks(isolate);
894 : }
895 29443 : }
896 :
897 29443 : PerIsolateData::~PerIsolateData() {
898 29443 : isolate_->SetData(0, nullptr); // Not really needed, just to be sure...
899 29443 : if (i::FLAG_expose_async_hooks) {
900 50 : delete async_hooks_wrapper_; // This uses the isolate
901 : }
902 29443 : }
903 :
904 1296 : void PerIsolateData::SetTimeout(Local<Function> callback,
905 : Local<Context> context) {
906 1296 : set_timeout_callbacks_.emplace(isolate_, callback);
907 : set_timeout_contexts_.emplace(isolate_, context);
908 1296 : }
909 :
910 158175 : MaybeLocal<Function> PerIsolateData::GetTimeoutCallback() {
911 158175 : if (set_timeout_callbacks_.empty()) return MaybeLocal<Function>();
912 1296 : Local<Function> result = set_timeout_callbacks_.front().Get(isolate_);
913 : set_timeout_callbacks_.pop();
914 1296 : return result;
915 : }
916 :
917 1296 : MaybeLocal<Context> PerIsolateData::GetTimeoutContext() {
918 1296 : if (set_timeout_contexts_.empty()) return MaybeLocal<Context>();
919 1296 : Local<Context> result = set_timeout_contexts_.front().Get(isolate_);
920 : set_timeout_contexts_.pop();
921 1296 : return result;
922 : }
923 :
924 50579 : PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
925 50579 : data_->realm_count_ = 1;
926 50579 : data_->realm_current_ = 0;
927 50579 : data_->realm_switch_ = 0;
928 101158 : data_->realms_ = new Global<Context>[1];
929 50579 : data_->realms_[0].Reset(data_->isolate_,
930 101158 : data_->isolate_->GetEnteredOrMicrotaskContext());
931 50579 : }
932 :
933 :
934 50579 : PerIsolateData::RealmScope::~RealmScope() {
935 : // Drop realms to avoid keeping them alive. We don't dispose the
936 : // module embedder data for the first realm here, but instead do
937 : // it in RunShell or in RunMain, if not running in interactive mode
938 51264 : for (int i = 1; i < data_->realm_count_; ++i) {
939 685 : Global<Context>& realm = data_->realms_[i];
940 685 : if (realm.IsEmpty()) continue;
941 794 : DisposeModuleEmbedderData(realm.Get(data_->isolate_));
942 : }
943 50579 : data_->realm_count_ = 0;
944 101158 : delete[] data_->realms_;
945 50579 : }
946 :
947 :
948 311 : int PerIsolateData::RealmFind(Local<Context> context) {
949 2228 : for (int i = 0; i < realm_count_; ++i) {
950 4456 : if (realms_[i] == context) return i;
951 : }
952 : return -1;
953 : }
954 :
955 :
956 9881 : int PerIsolateData::RealmIndexOrThrow(
957 19771 : const v8::FunctionCallbackInfo<v8::Value>& args,
958 : int arg_offset) {
959 19762 : if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
960 0 : Throw(args.GetIsolate(), "Invalid argument");
961 0 : return -1;
962 : }
963 : int index = args[arg_offset]
964 9881 : ->Int32Value(args.GetIsolate()->GetCurrentContext())
965 19762 : .FromMaybe(-1);
966 19753 : if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
967 9 : Throw(args.GetIsolate(), "Invalid realm index");
968 9 : return -1;
969 : }
970 : return index;
971 : }
972 :
973 :
974 : // performance.now() returns a time stamp as double, measured in milliseconds.
975 : // When FLAG_verify_predictable mode is enabled it returns result of
976 : // v8::Platform::MonotonicallyIncreasingTime().
977 6 : void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
978 : if (i::FLAG_verify_predictable) {
979 : args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
980 : } else {
981 : base::TimeDelta delta =
982 6 : base::TimeTicks::HighResolutionNow() - kInitialTicks;
983 3 : args.GetReturnValue().Set(delta.InMillisecondsF());
984 : }
985 3 : }
986 :
987 :
988 : // Realm.current() returns the index of the currently active realm.
989 586 : void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
990 : Isolate* isolate = args.GetIsolate();
991 : PerIsolateData* data = PerIsolateData::Get(isolate);
992 293 : int index = data->RealmFind(isolate->GetEnteredOrMicrotaskContext());
993 586 : if (index == -1) return;
994 : args.GetReturnValue().Set(index);
995 : }
996 :
997 :
998 : // Realm.owner(o) returns the index of the realm that created o.
999 54 : void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
1000 : Isolate* isolate = args.GetIsolate();
1001 : PerIsolateData* data = PerIsolateData::Get(isolate);
1002 36 : if (args.Length() < 1 || !args[0]->IsObject()) {
1003 0 : Throw(args.GetIsolate(), "Invalid argument");
1004 0 : return;
1005 : }
1006 : int index = data->RealmFind(args[0]
1007 18 : ->ToObject(isolate->GetCurrentContext())
1008 18 : .ToLocalChecked()
1009 18 : ->CreationContext());
1010 18 : if (index == -1) return;
1011 : args.GetReturnValue().Set(index);
1012 : }
1013 :
1014 :
1015 : // Realm.global(i) returns the global object of realm i.
1016 : // (Note that properties of global objects cannot be read/written cross-realm.)
1017 1377 : void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1018 : PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
1019 459 : int index = data->RealmIndexOrThrow(args, 0);
1020 918 : if (index == -1) return;
1021 : args.GetReturnValue().Set(
1022 918 : Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1023 : }
1024 :
1025 694 : MaybeLocal<Context> Shell::CreateRealm(
1026 1388 : const v8::FunctionCallbackInfo<v8::Value>& args, int index,
1027 : v8::MaybeLocal<Value> global_object) {
1028 : Isolate* isolate = args.GetIsolate();
1029 694 : TryCatch try_catch(isolate);
1030 : PerIsolateData* data = PerIsolateData::Get(isolate);
1031 694 : if (index < 0) {
1032 685 : Global<Context>* old_realms = data->realms_;
1033 685 : index = data->realm_count_;
1034 4503 : data->realms_ = new Global<Context>[++data->realm_count_];
1035 3818 : for (int i = 0; i < index; ++i) {
1036 3133 : data->realms_[i].Reset(isolate, old_realms[i]);
1037 : old_realms[i].Reset();
1038 : }
1039 1370 : delete[] old_realms;
1040 : }
1041 694 : Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1042 : Local<Context> context =
1043 694 : Context::New(isolate, nullptr, global_template, global_object);
1044 : DCHECK(!try_catch.HasCaught());
1045 694 : if (context.IsEmpty()) return MaybeLocal<Context>();
1046 694 : InitializeModuleEmbedderData(context);
1047 694 : data->realms_[index].Reset(isolate, context);
1048 : args.GetReturnValue().Set(index);
1049 694 : return context;
1050 : }
1051 :
1052 297 : void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
1053 : int index) {
1054 : Isolate* isolate = args.GetIsolate();
1055 : PerIsolateData* data = PerIsolateData::Get(isolate);
1056 594 : DisposeModuleEmbedderData(data->realms_[index].Get(isolate));
1057 297 : data->realms_[index].Reset();
1058 297 : isolate->ContextDisposedNotification();
1059 297 : isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
1060 297 : }
1061 :
1062 : // Realm.create() creates a new realm with a distinct security token
1063 : // and returns its index.
1064 667 : void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1065 667 : CreateRealm(args, -1, v8::MaybeLocal<Value>());
1066 667 : }
1067 :
1068 : // Realm.createAllowCrossRealmAccess() creates a new realm with the same
1069 : // security token as the current realm.
1070 18 : void Shell::RealmCreateAllowCrossRealmAccess(
1071 18 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1072 : Local<Context> context;
1073 36 : if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1074 : context->SetSecurityToken(
1075 36 : args.GetIsolate()->GetEnteredOrMicrotaskContext()->GetSecurityToken());
1076 : }
1077 18 : }
1078 :
1079 : // Realm.navigate(i) creates a new realm with a distinct security token
1080 : // in place of realm i.
1081 27 : void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1082 : Isolate* isolate = args.GetIsolate();
1083 : PerIsolateData* data = PerIsolateData::Get(isolate);
1084 18 : int index = data->RealmIndexOrThrow(args, 0);
1085 27 : if (index == -1) return;
1086 27 : if (index == 0 || index == data->realm_current_ ||
1087 9 : index == data->realm_switch_) {
1088 9 : Throw(args.GetIsolate(), "Invalid realm index");
1089 9 : return;
1090 : }
1091 :
1092 9 : Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
1093 9 : v8::MaybeLocal<Value> global_object = context->Global();
1094 9 : DisposeRealm(args, index);
1095 9 : CreateRealm(args, index, global_object);
1096 : }
1097 :
1098 : // Realm.dispose(i) disposes the reference to the realm i.
1099 288 : void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1100 : Isolate* isolate = args.GetIsolate();
1101 : PerIsolateData* data = PerIsolateData::Get(isolate);
1102 288 : int index = data->RealmIndexOrThrow(args, 0);
1103 288 : if (index == -1) return;
1104 576 : if (index == 0 ||
1105 576 : index == data->realm_current_ || index == data->realm_switch_) {
1106 0 : Throw(args.GetIsolate(), "Invalid realm index");
1107 0 : return;
1108 : }
1109 288 : DisposeRealm(args, index);
1110 : }
1111 :
1112 :
1113 : // Realm.switch(i) switches to the realm i for consecutive interactive inputs.
1114 0 : void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1115 : Isolate* isolate = args.GetIsolate();
1116 : PerIsolateData* data = PerIsolateData::Get(isolate);
1117 0 : int index = data->RealmIndexOrThrow(args, 0);
1118 0 : if (index == -1) return;
1119 0 : data->realm_switch_ = index;
1120 : }
1121 :
1122 :
1123 : // Realm.eval(i, s) evaluates s in realm i and returns the result.
1124 26943 : void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1125 : Isolate* isolate = args.GetIsolate();
1126 : PerIsolateData* data = PerIsolateData::Get(isolate);
1127 9116 : int index = data->RealmIndexOrThrow(args, 0);
1128 9512 : if (index == -1) return;
1129 18214 : if (args.Length() < 2 || !args[1]->IsString()) {
1130 0 : Throw(args.GetIsolate(), "Invalid argument");
1131 0 : return;
1132 : }
1133 : ScriptCompiler::Source script_source(
1134 18214 : args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
1135 : Local<UnboundScript> script;
1136 9107 : if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
1137 9107 : .ToLocal(&script)) {
1138 : return;
1139 : }
1140 9098 : Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1141 9098 : realm->Enter();
1142 9098 : int previous_index = data->realm_current_;
1143 9098 : data->realm_current_ = data->realm_switch_ = index;
1144 : Local<Value> result;
1145 27294 : if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
1146 378 : realm->Exit();
1147 378 : data->realm_current_ = data->realm_switch_ = previous_index;
1148 378 : return;
1149 : }
1150 8720 : realm->Exit();
1151 8720 : data->realm_current_ = data->realm_switch_ = previous_index;
1152 : args.GetReturnValue().Set(result);
1153 : }
1154 :
1155 :
1156 : // Realm.shared is an accessor for a single shared value across realms.
1157 5166 : void Shell::RealmSharedGet(Local<String> property,
1158 : const PropertyCallbackInfo<Value>& info) {
1159 : Isolate* isolate = info.GetIsolate();
1160 : PerIsolateData* data = PerIsolateData::Get(isolate);
1161 10332 : if (data->realm_shared_.IsEmpty()) return;
1162 : info.GetReturnValue().Set(data->realm_shared_);
1163 : }
1164 :
1165 261 : void Shell::RealmSharedSet(Local<String> property,
1166 : Local<Value> value,
1167 : const PropertyCallbackInfo<void>& info) {
1168 : Isolate* isolate = info.GetIsolate();
1169 : PerIsolateData* data = PerIsolateData::Get(isolate);
1170 : data->realm_shared_.Reset(isolate, value);
1171 261 : }
1172 :
1173 : // async_hooks.createHook() registers functions to be called for different
1174 : // lifetime events of each async operation.
1175 117 : void Shell::AsyncHooksCreateHook(
1176 234 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1177 : Local<Object> wrap =
1178 117 : PerIsolateData::Get(args.GetIsolate())->GetAsyncHooks()->CreateHook(args);
1179 : args.GetReturnValue().Set(wrap);
1180 117 : }
1181 :
1182 : // async_hooks.executionAsyncId() returns the asyncId of the current execution
1183 : // context.
1184 45 : void Shell::AsyncHooksExecutionAsyncId(
1185 90 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1186 : Isolate* isolate = args.GetIsolate();
1187 45 : HandleScope handle_scope(isolate);
1188 : args.GetReturnValue().Set(v8::Number::New(
1189 : isolate,
1190 90 : PerIsolateData::Get(isolate)->GetAsyncHooks()->GetExecutionAsyncId()));
1191 45 : }
1192 :
1193 45 : void Shell::AsyncHooksTriggerAsyncId(
1194 90 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1195 : Isolate* isolate = args.GetIsolate();
1196 45 : HandleScope handle_scope(isolate);
1197 : args.GetReturnValue().Set(v8::Number::New(
1198 : isolate,
1199 90 : PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
1200 45 : }
1201 :
1202 13605576 : void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1203 6897786 : for (int i = 0; i < args.Length(); i++) {
1204 2235930 : HandleScope handle_scope(args.GetIsolate());
1205 2235930 : if (i != 0) {
1206 : fprintf(file, " ");
1207 : }
1208 :
1209 : // Explicitly catch potential exceptions in toString().
1210 4471860 : v8::TryCatch try_catch(args.GetIsolate());
1211 : Local<Value> arg = args[i];
1212 : Local<String> str_obj;
1213 :
1214 2235930 : if (arg->IsSymbol()) {
1215 0 : arg = Local<Symbol>::Cast(arg)->Name();
1216 : }
1217 4471860 : if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1218 2235930 : .ToLocal(&str_obj)) {
1219 0 : try_catch.ReThrow();
1220 1212963 : return;
1221 : }
1222 :
1223 4471860 : v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1224 2235930 : int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1225 2235930 : if (n != str.length()) {
1226 : printf("Error in fwrite\n");
1227 0 : base::OS::ExitProcess(1);
1228 : }
1229 2235930 : }
1230 : }
1231 :
1232 1212963 : void WriteAndFlush(FILE* file,
1233 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1234 1212963 : WriteToFile(file, args);
1235 : fprintf(file, "\n");
1236 1212963 : fflush(file);
1237 1212963 : }
1238 :
1239 1212963 : void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
1240 1212963 : WriteAndFlush(stdout, args);
1241 1212963 : }
1242 :
1243 0 : void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) {
1244 0 : WriteAndFlush(stderr, args);
1245 0 : }
1246 :
1247 0 : void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
1248 0 : WriteToFile(stdout, args);
1249 0 : }
1250 :
1251 150 : void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1252 30 : String::Utf8Value file(args.GetIsolate(), args[0]);
1253 30 : if (*file == nullptr) {
1254 0 : Throw(args.GetIsolate(), "Error loading file");
1255 0 : return;
1256 : }
1257 30 : if (args.Length() == 2) {
1258 0 : String::Utf8Value format(args.GetIsolate(), args[1]);
1259 0 : if (*format && std::strcmp(*format, "binary") == 0) {
1260 0 : ReadBuffer(args);
1261 0 : return;
1262 0 : }
1263 : }
1264 60 : Local<String> source = ReadFile(args.GetIsolate(), *file);
1265 30 : if (source.IsEmpty()) {
1266 0 : Throw(args.GetIsolate(), "Error loading file");
1267 0 : return;
1268 : }
1269 30 : args.GetReturnValue().Set(source);
1270 : }
1271 :
1272 :
1273 0 : Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1274 : static const int kBufferSize = 256;
1275 : char buffer[kBufferSize];
1276 : Local<String> accumulator =
1277 0 : String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked();
1278 : int length;
1279 : while (true) {
1280 : // Continue reading if the line ends with an escape '\\' or the line has
1281 : // not been fully read into the buffer yet (does not end with '\n').
1282 : // If fgets gets an error, just give up.
1283 : char* input = nullptr;
1284 0 : input = fgets(buffer, kBufferSize, stdin);
1285 0 : if (input == nullptr) return Local<String>();
1286 0 : length = static_cast<int>(strlen(buffer));
1287 0 : if (length == 0) {
1288 0 : return accumulator;
1289 0 : } else if (buffer[length-1] != '\n') {
1290 : accumulator = String::Concat(
1291 : isolate, accumulator,
1292 : String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
1293 0 : .ToLocalChecked());
1294 0 : } else if (length > 1 && buffer[length-2] == '\\') {
1295 0 : buffer[length-2] = '\n';
1296 : accumulator =
1297 : String::Concat(isolate, accumulator,
1298 : String::NewFromUtf8(isolate, buffer,
1299 : NewStringType::kNormal, length - 1)
1300 0 : .ToLocalChecked());
1301 : } else {
1302 : return String::Concat(
1303 : isolate, accumulator,
1304 : String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1305 : length - 1)
1306 0 : .ToLocalChecked());
1307 : }
1308 : }
1309 : }
1310 :
1311 :
1312 68606 : void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1313 26342 : for (int i = 0; i < args.Length(); i++) {
1314 8577 : HandleScope handle_scope(args.GetIsolate());
1315 13171 : String::Utf8Value file(args.GetIsolate(), args[i]);
1316 8577 : if (*file == nullptr) {
1317 0 : Throw(args.GetIsolate(), "Error loading file");
1318 0 : return;
1319 : }
1320 8577 : Local<String> source = ReadFile(args.GetIsolate(), *file);
1321 8577 : if (source.IsEmpty()) {
1322 5 : Throw(args.GetIsolate(), "Error loading file");
1323 5 : return;
1324 : }
1325 8572 : if (!ExecuteString(
1326 : args.GetIsolate(), source,
1327 8572 : String::NewFromUtf8(args.GetIsolate(), *file,
1328 : NewStringType::kNormal)
1329 : .ToLocalChecked(),
1330 : kNoPrintResult,
1331 : options.quiet_load ? kNoReportExceptions : kReportExceptions,
1332 25716 : kNoProcessMessageQueue)) {
1333 3978 : Throw(args.GetIsolate(), "Error executing file");
1334 3978 : return;
1335 : }
1336 4594 : }
1337 : }
1338 :
1339 3888 : void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
1340 : Isolate* isolate = args.GetIsolate();
1341 1296 : args.GetReturnValue().Set(v8::Number::New(isolate, 0));
1342 2592 : if (args.Length() == 0 || !args[0]->IsFunction()) return;
1343 1296 : Local<Function> callback = Local<Function>::Cast(args[0]);
1344 1296 : Local<Context> context = isolate->GetCurrentContext();
1345 1296 : PerIsolateData::Get(isolate)->SetTimeout(callback, context);
1346 : }
1347 :
1348 3975 : void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
1349 : Isolate* isolate = args.GetIsolate();
1350 676 : HandleScope handle_scope(isolate);
1351 1343 : if (args.Length() < 1 || !args[0]->IsString()) {
1352 9 : Throw(args.GetIsolate(), "1st argument must be string");
1353 63 : return;
1354 : }
1355 :
1356 : // d8 honors `options={type: string}`, which means the first argument is
1357 : // not a filename but string of script to be run.
1358 : bool load_from_file = true;
1359 1307 : if (args.Length() > 1 && args[1]->IsObject()) {
1360 631 : Local<Object> object = args[1].As<Object>();
1361 631 : Local<Context> context = isolate->GetCurrentContext();
1362 631 : Local<Value> value = GetValue(args.GetIsolate(), context, object, "type");
1363 631 : if (value->IsString()) {
1364 631 : Local<String> worker_type = value->ToString(context).ToLocalChecked();
1365 631 : String::Utf8Value str(isolate, worker_type);
1366 631 : if (strcmp("string", *str) == 0) {
1367 : load_from_file = false;
1368 9 : } else if (strcmp("classic", *str) == 0) {
1369 : load_from_file = true;
1370 : } else {
1371 9 : Throw(args.GetIsolate(), "Unsupported worker type");
1372 9 : return;
1373 622 : }
1374 : }
1375 : }
1376 :
1377 : Local<Value> source;
1378 658 : if (load_from_file) {
1379 36 : String::Utf8Value filename(args.GetIsolate(), args[0]);
1380 72 : source = ReadFile(args.GetIsolate(), *filename);
1381 36 : if (source.IsEmpty()) {
1382 27 : Throw(args.GetIsolate(), "Error loading worker script");
1383 27 : return;
1384 9 : }
1385 : } else {
1386 : source = args[0];
1387 : }
1388 :
1389 631 : if (!args.IsConstructCall()) {
1390 0 : Throw(args.GetIsolate(), "Worker must be constructed with new");
1391 0 : return;
1392 : }
1393 :
1394 : {
1395 : base::MutexGuard lock_guard(workers_mutex_.Pointer());
1396 1262 : if (workers_.size() >= kMaxWorkers) {
1397 0 : Throw(args.GetIsolate(), "Too many workers, I won't let you create more");
1398 0 : return;
1399 : }
1400 :
1401 : // Initialize the embedder field to nullptr; if we return early without
1402 : // creating a new Worker (because the main thread is terminating) we can
1403 : // early-out from the instance calls.
1404 631 : args.Holder()->SetAlignedPointerInInternalField(0, nullptr);
1405 :
1406 631 : if (!allow_new_workers_) return;
1407 :
1408 622 : Worker* worker = new Worker;
1409 622 : args.Holder()->SetAlignedPointerInInternalField(0, worker);
1410 622 : workers_.push_back(worker);
1411 :
1412 1244 : String::Utf8Value script(args.GetIsolate(), source);
1413 622 : if (!*script) {
1414 0 : Throw(args.GetIsolate(), "Can't get worker script");
1415 0 : return;
1416 : }
1417 622 : worker->StartExecuteInThread(*script);
1418 622 : }
1419 : }
1420 :
1421 :
1422 2592 : void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1423 : Isolate* isolate = args.GetIsolate();
1424 864 : HandleScope handle_scope(isolate);
1425 :
1426 864 : if (args.Length() < 1) {
1427 0 : Throw(isolate, "Invalid argument");
1428 0 : return;
1429 : }
1430 :
1431 864 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1432 864 : if (!worker) {
1433 : return;
1434 : }
1435 :
1436 855 : Local<Value> message = args[0];
1437 : Local<Value> transfer =
1438 1710 : args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
1439 : std::unique_ptr<SerializationData> data =
1440 855 : Shell::SerializeValue(isolate, message, transfer);
1441 855 : if (data) {
1442 1330 : worker->PostMessage(std::move(data));
1443 855 : }
1444 : }
1445 :
1446 :
1447 3210 : void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1448 : Isolate* isolate = args.GetIsolate();
1449 1079 : HandleScope handle_scope(isolate);
1450 1079 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1451 1079 : if (!worker) {
1452 0 : return;
1453 : }
1454 :
1455 1079 : std::unique_ptr<SerializationData> data = worker->GetMessage();
1456 1079 : if (data) {
1457 : Local<Value> value;
1458 2104 : if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
1459 : args.GetReturnValue().Set(value);
1460 : }
1461 1079 : }
1462 : }
1463 :
1464 :
1465 846 : void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1466 : Isolate* isolate = args.GetIsolate();
1467 423 : HandleScope handle_scope(isolate);
1468 423 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1469 423 : if (!worker) {
1470 0 : return;
1471 : }
1472 :
1473 423 : worker->Terminate();
1474 : }
1475 :
1476 :
1477 0 : void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
1478 : int exit_code = (*args)[0]
1479 0 : ->Int32Value(args->GetIsolate()->GetCurrentContext())
1480 0 : .FromMaybe(0);
1481 0 : CleanupWorkers();
1482 0 : args->GetIsolate()->Exit();
1483 0 : OnExit(args->GetIsolate());
1484 0 : base::OS::ExitProcess(exit_code);
1485 0 : }
1486 :
1487 :
1488 0 : void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
1489 : base::CallOnce(&quit_once_, &QuitOnce,
1490 0 : const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
1491 0 : }
1492 :
1493 1675 : void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1494 1675 : SetWaitUntilDone(args.GetIsolate(), true);
1495 1675 : }
1496 :
1497 523 : void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1498 523 : SetWaitUntilDone(args.GetIsolate(), false);
1499 523 : }
1500 :
1501 81 : void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1502 : args.GetReturnValue().Set(
1503 : String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
1504 54 : NewStringType::kNormal).ToLocalChecked());
1505 27 : }
1506 :
1507 :
1508 5403 : void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
1509 5403 : HandleScope handle_scope(isolate);
1510 5403 : Local<Context> context = isolate->GetCurrentContext();
1511 : bool enter_context = context.IsEmpty();
1512 5403 : if (enter_context) {
1513 : context = Local<Context>::New(isolate, evaluation_context_);
1514 0 : context->Enter();
1515 : }
1516 : // Converts a V8 value to a C string.
1517 17616 : auto ToCString = [](const v8::String::Utf8Value& value) {
1518 : return *value ? *value : "<string conversion failed>";
1519 17616 : };
1520 :
1521 10806 : v8::String::Utf8Value exception(isolate, try_catch->Exception());
1522 : const char* exception_string = ToCString(exception);
1523 5403 : Local<Message> message = try_catch->Message();
1524 5403 : if (message.IsEmpty()) {
1525 : // V8 didn't provide any extra information about this error; just
1526 : // print the exception.
1527 : printf("%s\n", exception_string);
1528 10806 : } else if (message->GetScriptOrigin().Options().IsWasm()) {
1529 : // Print wasm-function[(function index)]:(offset): (message).
1530 18 : int function_index = message->GetLineNumber(context).FromJust() - 1;
1531 18 : int offset = message->GetStartColumn(context).FromJust();
1532 : printf("wasm-function[%d]:%d: %s\n", function_index, offset,
1533 : exception_string);
1534 : } else {
1535 : // Print (filename):(line number): (message).
1536 : v8::String::Utf8Value filename(isolate,
1537 5394 : message->GetScriptOrigin().ResourceName());
1538 : const char* filename_string = ToCString(filename);
1539 10788 : int linenum = message->GetLineNumber(context).FromMaybe(-1);
1540 : printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1541 : Local<String> sourceline;
1542 10788 : if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1543 : // Print line of source code.
1544 5394 : v8::String::Utf8Value sourcelinevalue(isolate, sourceline);
1545 : const char* sourceline_string = ToCString(sourcelinevalue);
1546 : printf("%s\n", sourceline_string);
1547 : // Print wavy underline (GetUnderline is deprecated).
1548 10788 : int start = message->GetStartColumn(context).FromJust();
1549 64127 : for (int i = 0; i < start; i++) {
1550 : printf(" ");
1551 : }
1552 10788 : int end = message->GetEndColumn(context).FromJust();
1553 17456 : for (int i = start; i < end; i++) {
1554 : printf("^");
1555 : }
1556 5394 : printf("\n");
1557 5394 : }
1558 : }
1559 : Local<Value> stack_trace_string;
1560 12240 : if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
1561 : stack_trace_string->IsString()) {
1562 : v8::String::Utf8Value stack_trace(isolate,
1563 1425 : Local<String>::Cast(stack_trace_string));
1564 1425 : printf("%s\n", ToCString(stack_trace));
1565 : }
1566 : printf("\n");
1567 10806 : if (enter_context) context->Exit();
1568 5403 : }
1569 :
1570 :
1571 0 : int32_t* Counter::Bind(const char* name, bool is_histogram) {
1572 : int i;
1573 0 : for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
1574 0 : name_[i] = static_cast<char>(name[i]);
1575 0 : name_[i] = '\0';
1576 0 : is_histogram_ = is_histogram;
1577 0 : return ptr();
1578 : }
1579 :
1580 :
1581 0 : void Counter::AddSample(int32_t sample) {
1582 0 : count_++;
1583 0 : sample_total_ += sample;
1584 0 : }
1585 :
1586 :
1587 0 : CounterCollection::CounterCollection() {
1588 28773 : magic_number_ = 0xDEADFACE;
1589 28773 : max_counters_ = kMaxCounters;
1590 28773 : max_name_size_ = Counter::kMaxNameSize;
1591 28773 : counters_in_use_ = 0;
1592 0 : }
1593 :
1594 :
1595 0 : Counter* CounterCollection::GetNextCounter() {
1596 0 : if (counters_in_use_ == kMaxCounters) return nullptr;
1597 0 : return &counters_[counters_in_use_++];
1598 : }
1599 :
1600 :
1601 0 : void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
1602 : counters_file_ = base::OS::MemoryMappedFile::create(
1603 0 : name, sizeof(CounterCollection), &local_counters_);
1604 : void* memory =
1605 0 : (counters_file_ == nullptr) ? nullptr : counters_file_->memory();
1606 0 : if (memory == nullptr) {
1607 : printf("Could not map counters file %s\n", name);
1608 0 : base::OS::ExitProcess(1);
1609 : }
1610 0 : counters_ = static_cast<CounterCollection*>(memory);
1611 0 : isolate->SetCounterFunction(LookupCounter);
1612 0 : isolate->SetCreateHistogramFunction(CreateHistogram);
1613 0 : isolate->SetAddHistogramSampleFunction(AddHistogramSample);
1614 0 : }
1615 :
1616 0 : Counter* Shell::GetCounter(const char* name, bool is_histogram) {
1617 0 : auto map_entry = counter_map_->find(name);
1618 : Counter* counter =
1619 0 : map_entry != counter_map_->end() ? map_entry->second : nullptr;
1620 :
1621 0 : if (counter == nullptr) {
1622 0 : counter = counters_->GetNextCounter();
1623 0 : if (counter != nullptr) {
1624 0 : (*counter_map_)[name] = counter;
1625 0 : counter->Bind(name, is_histogram);
1626 : }
1627 : } else {
1628 : DCHECK(counter->is_histogram() == is_histogram);
1629 : }
1630 0 : return counter;
1631 : }
1632 :
1633 :
1634 0 : int* Shell::LookupCounter(const char* name) {
1635 0 : Counter* counter = GetCounter(name, false);
1636 :
1637 0 : if (counter != nullptr) {
1638 0 : return counter->ptr();
1639 : } else {
1640 : return nullptr;
1641 : }
1642 : }
1643 :
1644 :
1645 0 : void* Shell::CreateHistogram(const char* name,
1646 : int min,
1647 : int max,
1648 : size_t buckets) {
1649 0 : return GetCounter(name, true);
1650 : }
1651 :
1652 :
1653 0 : void Shell::AddHistogramSample(void* histogram, int sample) {
1654 : Counter* counter = reinterpret_cast<Counter*>(histogram);
1655 : counter->AddSample(sample);
1656 0 : }
1657 :
1658 : // Turn a value into a human-readable string.
1659 0 : Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
1660 : v8::Local<v8::Context> context =
1661 0 : v8::Local<v8::Context>::New(isolate, evaluation_context_);
1662 0 : if (stringify_function_.IsEmpty()) {
1663 : Local<String> source =
1664 0 : String::NewFromUtf8(isolate, stringify_source_, NewStringType::kNormal)
1665 0 : .ToLocalChecked();
1666 : Local<String> name =
1667 : String::NewFromUtf8(isolate, "d8-stringify", NewStringType::kNormal)
1668 0 : .ToLocalChecked();
1669 : ScriptOrigin origin(name);
1670 : Local<Script> script =
1671 0 : Script::Compile(context, source, &origin).ToLocalChecked();
1672 : stringify_function_.Reset(
1673 0 : isolate, script->Run(context).ToLocalChecked().As<Function>());
1674 : }
1675 : Local<Function> fun = Local<Function>::New(isolate, stringify_function_);
1676 0 : Local<Value> argv[1] = {value};
1677 0 : v8::TryCatch try_catch(isolate);
1678 0 : MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
1679 0 : if (result.IsEmpty()) return String::Empty(isolate);
1680 0 : return result.ToLocalChecked().As<String>();
1681 : }
1682 :
1683 :
1684 51273 : Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
1685 51273 : Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
1686 : global_template->Set(
1687 : String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
1688 : .ToLocalChecked(),
1689 153819 : FunctionTemplate::New(isolate, Print));
1690 : global_template->Set(
1691 : String::NewFromUtf8(isolate, "printErr", NewStringType::kNormal)
1692 : .ToLocalChecked(),
1693 153819 : FunctionTemplate::New(isolate, PrintErr));
1694 : global_template->Set(
1695 : String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
1696 : .ToLocalChecked(),
1697 153819 : FunctionTemplate::New(isolate, Write));
1698 : global_template->Set(
1699 : String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
1700 : .ToLocalChecked(),
1701 153819 : FunctionTemplate::New(isolate, Read));
1702 : global_template->Set(
1703 : String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
1704 : .ToLocalChecked(),
1705 153819 : FunctionTemplate::New(isolate, ReadBuffer));
1706 : global_template->Set(
1707 : String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
1708 : .ToLocalChecked(),
1709 153819 : FunctionTemplate::New(isolate, ReadLine));
1710 : global_template->Set(
1711 : String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
1712 : .ToLocalChecked(),
1713 153819 : FunctionTemplate::New(isolate, Load));
1714 : global_template->Set(
1715 : String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal)
1716 : .ToLocalChecked(),
1717 153819 : FunctionTemplate::New(isolate, SetTimeout));
1718 : // Some Emscripten-generated code tries to call 'quit', which in turn would
1719 : // call C's exit(). This would lead to memory leaks, because there is no way
1720 : // we can terminate cleanly then, so we need a way to hide 'quit'.
1721 51273 : if (!options.omit_quit) {
1722 : global_template->Set(
1723 : String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
1724 : .ToLocalChecked(),
1725 153711 : FunctionTemplate::New(isolate, Quit));
1726 : }
1727 51273 : Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
1728 : global_template->Set(
1729 : String::NewFromUtf8(isolate, "testRunner", NewStringType::kNormal)
1730 : .ToLocalChecked(),
1731 102546 : test_template);
1732 : test_template->Set(
1733 : String::NewFromUtf8(isolate, "notifyDone", NewStringType::kNormal)
1734 : .ToLocalChecked(),
1735 153819 : FunctionTemplate::New(isolate, NotifyDone));
1736 : test_template->Set(
1737 : String::NewFromUtf8(isolate, "waitUntilDone", NewStringType::kNormal)
1738 : .ToLocalChecked(),
1739 153819 : FunctionTemplate::New(isolate, WaitUntilDone));
1740 : global_template->Set(
1741 : String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
1742 : .ToLocalChecked(),
1743 153819 : FunctionTemplate::New(isolate, Version));
1744 : global_template->Set(
1745 : Symbol::GetToStringTag(isolate),
1746 : String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1747 153819 : .ToLocalChecked());
1748 :
1749 : // Bind the Realm object.
1750 51273 : Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
1751 : realm_template->Set(
1752 : String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
1753 : .ToLocalChecked(),
1754 153819 : FunctionTemplate::New(isolate, RealmCurrent));
1755 : realm_template->Set(
1756 : String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
1757 : .ToLocalChecked(),
1758 153819 : FunctionTemplate::New(isolate, RealmOwner));
1759 : realm_template->Set(
1760 : String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1761 : .ToLocalChecked(),
1762 153819 : FunctionTemplate::New(isolate, RealmGlobal));
1763 : realm_template->Set(
1764 : String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
1765 : .ToLocalChecked(),
1766 153819 : FunctionTemplate::New(isolate, RealmCreate));
1767 : realm_template->Set(
1768 : String::NewFromUtf8(isolate, "createAllowCrossRealmAccess",
1769 : NewStringType::kNormal)
1770 : .ToLocalChecked(),
1771 153819 : FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
1772 : realm_template->Set(
1773 : String::NewFromUtf8(isolate, "navigate", NewStringType::kNormal)
1774 : .ToLocalChecked(),
1775 153819 : FunctionTemplate::New(isolate, RealmNavigate));
1776 : realm_template->Set(
1777 : String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
1778 : .ToLocalChecked(),
1779 153819 : FunctionTemplate::New(isolate, RealmDispose));
1780 : realm_template->Set(
1781 : String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
1782 : .ToLocalChecked(),
1783 153819 : FunctionTemplate::New(isolate, RealmSwitch));
1784 : realm_template->Set(
1785 : String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
1786 : .ToLocalChecked(),
1787 153819 : FunctionTemplate::New(isolate, RealmEval));
1788 : realm_template->SetAccessor(
1789 : String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
1790 : .ToLocalChecked(),
1791 102546 : RealmSharedGet, RealmSharedSet);
1792 : global_template->Set(
1793 : String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
1794 : .ToLocalChecked(),
1795 102546 : realm_template);
1796 :
1797 51273 : Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
1798 : performance_template->Set(
1799 : String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
1800 : .ToLocalChecked(),
1801 153819 : FunctionTemplate::New(isolate, PerformanceNow));
1802 : global_template->Set(
1803 : String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
1804 : .ToLocalChecked(),
1805 102546 : performance_template);
1806 :
1807 : Local<FunctionTemplate> worker_fun_template =
1808 51273 : FunctionTemplate::New(isolate, WorkerNew);
1809 : Local<Signature> worker_signature =
1810 51273 : Signature::New(isolate, worker_fun_template);
1811 : worker_fun_template->SetClassName(
1812 : String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1813 102546 : .ToLocalChecked());
1814 51273 : worker_fun_template->ReadOnlyPrototype();
1815 102546 : worker_fun_template->PrototypeTemplate()->Set(
1816 : String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
1817 : .ToLocalChecked(),
1818 : FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
1819 205092 : worker_signature));
1820 102546 : worker_fun_template->PrototypeTemplate()->Set(
1821 : String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
1822 : .ToLocalChecked(),
1823 : FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
1824 205092 : worker_signature));
1825 102546 : worker_fun_template->PrototypeTemplate()->Set(
1826 : String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
1827 : .ToLocalChecked(),
1828 : FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
1829 205092 : worker_signature));
1830 102546 : worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
1831 : global_template->Set(
1832 : String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1833 : .ToLocalChecked(),
1834 102546 : worker_fun_template);
1835 :
1836 51273 : Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
1837 51273 : AddOSMethods(isolate, os_templ);
1838 : global_template->Set(
1839 : String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
1840 : .ToLocalChecked(),
1841 102546 : os_templ);
1842 :
1843 51273 : if (i::FLAG_expose_async_hooks) {
1844 90 : Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
1845 : async_hooks_templ->Set(
1846 : String::NewFromUtf8(isolate, "createHook", NewStringType::kNormal)
1847 : .ToLocalChecked(),
1848 270 : FunctionTemplate::New(isolate, AsyncHooksCreateHook));
1849 : async_hooks_templ->Set(
1850 : String::NewFromUtf8(isolate, "executionAsyncId", NewStringType::kNormal)
1851 : .ToLocalChecked(),
1852 270 : FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
1853 : async_hooks_templ->Set(
1854 : String::NewFromUtf8(isolate, "triggerAsyncId", NewStringType::kNormal)
1855 : .ToLocalChecked(),
1856 270 : FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
1857 : global_template->Set(
1858 : String::NewFromUtf8(isolate, "async_hooks", NewStringType::kNormal)
1859 : .ToLocalChecked(),
1860 180 : async_hooks_templ);
1861 : }
1862 :
1863 51273 : return global_template;
1864 : }
1865 :
1866 6248 : static void PrintNonErrorsMessageCallback(Local<Message> message,
1867 : Local<Value> error) {
1868 : // Nothing to do here for errors, exceptions thrown up to the shell will be
1869 : // reported
1870 : // separately by {Shell::ReportException} after they are caught.
1871 : // Do print other kinds of messages.
1872 6248 : switch (message->ErrorLevel()) {
1873 : case v8::Isolate::kMessageWarning:
1874 : case v8::Isolate::kMessageLog:
1875 : case v8::Isolate::kMessageInfo:
1876 : case v8::Isolate::kMessageDebug: {
1877 : break;
1878 : }
1879 :
1880 : case v8::Isolate::kMessageError: {
1881 : // Ignore errors, printed elsewhere.
1882 5493 : return;
1883 : }
1884 :
1885 : default: {
1886 0 : UNREACHABLE();
1887 : break;
1888 : }
1889 : }
1890 : // Converts a V8 value to a C string.
1891 1510 : auto ToCString = [](const v8::String::Utf8Value& value) {
1892 : return *value ? *value : "<string conversion failed>";
1893 1510 : };
1894 755 : Isolate* isolate = Isolate::GetCurrent();
1895 1510 : v8::String::Utf8Value msg(isolate, message->Get());
1896 : const char* msg_string = ToCString(msg);
1897 : // Print (filename):(line number): (message).
1898 : v8::String::Utf8Value filename(isolate,
1899 1510 : message->GetScriptOrigin().ResourceName());
1900 : const char* filename_string = ToCString(filename);
1901 755 : Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
1902 755 : int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
1903 755 : printf("%s:%i: %s\n", filename_string, linenum, msg_string);
1904 : }
1905 :
1906 28821 : void Shell::Initialize(Isolate* isolate) {
1907 : // Set up counters
1908 57642 : if (i::StrLength(i::FLAG_map_counters) != 0)
1909 0 : MapCounters(isolate, i::FLAG_map_counters);
1910 : // Disable default message reporting.
1911 : isolate->AddMessageListenerWithErrorLevel(
1912 : PrintNonErrorsMessageCallback,
1913 : v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
1914 : v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
1915 28821 : v8::Isolate::kMessageLog);
1916 28821 : }
1917 :
1918 :
1919 50578 : Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
1920 : // This needs to be a critical section since this is not thread-safe
1921 : base::MutexGuard lock_guard(context_mutex_.Pointer());
1922 : // Initialize the global objects
1923 50579 : Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1924 50579 : EscapableHandleScope handle_scope(isolate);
1925 50579 : Local<Context> context = Context::New(isolate, nullptr, global_template);
1926 : DCHECK(!context.IsEmpty());
1927 50579 : InitializeModuleEmbedderData(context);
1928 50579 : if (options.include_arguments) {
1929 : Context::Scope scope(context);
1930 50606 : const std::vector<const char*>& args = options.arguments;
1931 50579 : int size = static_cast<int>(args.size());
1932 50579 : Local<Array> array = Array::New(isolate, size);
1933 50606 : for (int i = 0; i < size; i++) {
1934 : Local<String> arg =
1935 54 : v8::String::NewFromUtf8(isolate, args[i], v8::NewStringType::kNormal)
1936 27 : .ToLocalChecked();
1937 27 : Local<Number> index = v8::Number::New(isolate, i);
1938 54 : array->Set(context, index, arg).FromJust();
1939 : }
1940 : Local<String> name =
1941 : String::NewFromUtf8(isolate, "arguments", NewStringType::kInternalized)
1942 50579 : .ToLocalChecked();
1943 151737 : context->Global()->Set(context, name, array).FromJust();
1944 : }
1945 50579 : return handle_scope.Escape(context);
1946 : }
1947 :
1948 : struct CounterAndKey {
1949 : Counter* counter;
1950 : const char* key;
1951 : };
1952 :
1953 :
1954 : inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
1955 0 : return strcmp(lhs.key, rhs.key) < 0;
1956 : }
1957 :
1958 0 : void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
1959 0 : HandleScope handle_scope(isolate);
1960 0 : Local<Context> context = Context::New(isolate);
1961 : Context::Scope context_scope(context);
1962 :
1963 : Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
1964 : ->interpreter()
1965 0 : ->GetDispatchCountersObject();
1966 : std::ofstream dispatch_counters_stream(
1967 0 : i::FLAG_trace_ignition_dispatches_output_file);
1968 : dispatch_counters_stream << *String::Utf8Value(
1969 0 : isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
1970 0 : }
1971 :
1972 : namespace {
1973 0 : int LineFromOffset(Local<debug::Script> script, int offset) {
1974 0 : debug::Location location = script->GetSourceLocation(offset);
1975 0 : return location.GetLineNumber();
1976 : }
1977 :
1978 0 : void WriteLcovDataForRange(std::vector<uint32_t>& lines, int start_line,
1979 : int end_line, uint32_t count) {
1980 : // Ensure space in the array.
1981 0 : lines.resize(std::max(static_cast<size_t>(end_line + 1), lines.size()), 0);
1982 : // Boundary lines could be shared between two functions with different
1983 : // invocation counts. Take the maximum.
1984 0 : lines[start_line] = std::max(lines[start_line], count);
1985 0 : lines[end_line] = std::max(lines[end_line], count);
1986 : // Invocation counts for non-boundary lines are overwritten.
1987 0 : for (int k = start_line + 1; k < end_line; k++) lines[k] = count;
1988 0 : }
1989 :
1990 0 : void WriteLcovDataForNamedRange(std::ostream& sink,
1991 : std::vector<uint32_t>& lines,
1992 : const std::string& name, int start_line,
1993 : int end_line, uint32_t count) {
1994 0 : WriteLcovDataForRange(lines, start_line, end_line, count);
1995 0 : sink << "FN:" << start_line + 1 << "," << name << std::endl;
1996 0 : sink << "FNDA:" << count << "," << name << std::endl;
1997 0 : }
1998 : } // namespace
1999 :
2000 : // Write coverage data in LCOV format. See man page for geninfo(1).
2001 49957 : void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
2002 99914 : if (!file) return;
2003 0 : HandleScope handle_scope(isolate);
2004 0 : debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2005 0 : std::ofstream sink(file, std::ofstream::app);
2006 0 : for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2007 0 : debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
2008 0 : Local<debug::Script> script = script_data.GetScript();
2009 : // Skip unnamed scripts.
2010 : Local<String> name;
2011 0 : if (!script->Name().ToLocal(&name)) continue;
2012 0 : std::string file_name = ToSTLString(isolate, name);
2013 : // Skip scripts not backed by a file.
2014 0 : if (!std::ifstream(file_name).good()) continue;
2015 0 : sink << "SF:";
2016 0 : sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
2017 : std::vector<uint32_t> lines;
2018 0 : for (size_t j = 0; j < script_data.FunctionCount(); j++) {
2019 : debug::Coverage::FunctionData function_data =
2020 0 : script_data.GetFunctionData(j);
2021 :
2022 : // Write function stats.
2023 : {
2024 : debug::Location start =
2025 0 : script->GetSourceLocation(function_data.StartOffset());
2026 : debug::Location end =
2027 0 : script->GetSourceLocation(function_data.EndOffset());
2028 0 : int start_line = start.GetLineNumber();
2029 0 : int end_line = end.GetLineNumber();
2030 0 : uint32_t count = function_data.Count();
2031 :
2032 : Local<String> name;
2033 0 : std::stringstream name_stream;
2034 0 : if (function_data.Name().ToLocal(&name)) {
2035 0 : name_stream << ToSTLString(isolate, name);
2036 : } else {
2037 0 : name_stream << "<" << start_line + 1 << "-";
2038 0 : name_stream << start.GetColumnNumber() << ">";
2039 : }
2040 :
2041 : WriteLcovDataForNamedRange(sink, lines, name_stream.str(), start_line,
2042 0 : end_line, count);
2043 : }
2044 :
2045 : // Process inner blocks.
2046 0 : for (size_t k = 0; k < function_data.BlockCount(); k++) {
2047 0 : debug::Coverage::BlockData block_data = function_data.GetBlockData(k);
2048 0 : int start_line = LineFromOffset(script, block_data.StartOffset());
2049 0 : int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2050 0 : uint32_t count = block_data.Count();
2051 0 : WriteLcovDataForRange(lines, start_line, end_line, count);
2052 : }
2053 : }
2054 : // Write per-line coverage. LCOV uses 1-based line numbers.
2055 0 : for (size_t i = 0; i < lines.size(); i++) {
2056 0 : sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
2057 : }
2058 0 : sink << "end_of_record" << std::endl;
2059 0 : }
2060 : }
2061 :
2062 28773 : void Shell::OnExit(v8::Isolate* isolate) {
2063 : // Dump basic block profiling data.
2064 28773 : if (i::FLAG_turbo_profiling) {
2065 0 : i::BasicBlockProfiler* profiler = i::BasicBlockProfiler::Get();
2066 0 : i::StdoutStream{} << *profiler;
2067 : }
2068 28773 : isolate->Dispose();
2069 :
2070 28773 : if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2071 0 : const int number_of_counters = static_cast<int>(counter_map_->size());
2072 0 : CounterAndKey* counters = new CounterAndKey[number_of_counters];
2073 : int j = 0;
2074 0 : for (auto map_entry : *counter_map_) {
2075 0 : counters[j].counter = map_entry.second;
2076 0 : counters[j].key = map_entry.first;
2077 0 : j++;
2078 : }
2079 0 : std::sort(counters, counters + number_of_counters);
2080 :
2081 0 : if (i::FLAG_dump_counters_nvp) {
2082 : // Dump counters as name-value pairs.
2083 0 : for (j = 0; j < number_of_counters; j++) {
2084 0 : Counter* counter = counters[j].counter;
2085 0 : const char* key = counters[j].key;
2086 0 : if (counter->is_histogram()) {
2087 : printf("\"c:%s\"=%i\n", key, counter->count());
2088 : printf("\"t:%s\"=%i\n", key, counter->sample_total());
2089 : } else {
2090 : printf("\"%s\"=%i\n", key, counter->count());
2091 : }
2092 : }
2093 : } else {
2094 : // Dump counters in formatted boxes.
2095 : printf(
2096 : "+----------------------------------------------------------------+"
2097 : "-------------+\n");
2098 : printf(
2099 : "| Name |"
2100 : " Value |\n");
2101 : printf(
2102 : "+----------------------------------------------------------------+"
2103 : "-------------+\n");
2104 0 : for (j = 0; j < number_of_counters; j++) {
2105 0 : Counter* counter = counters[j].counter;
2106 0 : const char* key = counters[j].key;
2107 0 : if (counter->is_histogram()) {
2108 : printf("| c:%-60s | %11i |\n", key, counter->count());
2109 : printf("| t:%-60s | %11i |\n", key, counter->sample_total());
2110 : } else {
2111 : printf("| %-62s | %11i |\n", key, counter->count());
2112 : }
2113 : }
2114 : printf(
2115 : "+----------------------------------------------------------------+"
2116 : "-------------+\n");
2117 : }
2118 0 : delete [] counters;
2119 : }
2120 :
2121 28773 : delete counters_file_;
2122 57546 : delete counter_map_;
2123 28773 : }
2124 :
2125 :
2126 18 : static FILE* FOpen(const char* path, const char* mode) {
2127 : #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2128 : FILE* result;
2129 : if (fopen_s(&result, path, mode) == 0) {
2130 : return result;
2131 : } else {
2132 : return nullptr;
2133 : }
2134 : #else
2135 18 : FILE* file = fopen(path, mode);
2136 18 : if (file == nullptr) return nullptr;
2137 : struct stat file_stat;
2138 36 : if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2139 18 : bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
2140 18 : if (is_regular_file) return file;
2141 0 : fclose(file);
2142 0 : return nullptr;
2143 : #endif
2144 : }
2145 :
2146 18 : static char* ReadChars(const char* name, int* size_out) {
2147 18 : if (Shell::options.read_from_tcp_port >= 0) {
2148 0 : return Shell::ReadCharsFromTcpPort(name, size_out);
2149 : }
2150 :
2151 18 : FILE* file = FOpen(name, "rb");
2152 18 : if (file == nullptr) return nullptr;
2153 :
2154 18 : fseek(file, 0, SEEK_END);
2155 18 : size_t size = ftell(file);
2156 18 : rewind(file);
2157 :
2158 18 : char* chars = new char[size + 1];
2159 18 : chars[size] = '\0';
2160 54 : for (size_t i = 0; i < size;) {
2161 36 : i += fread(&chars[i], 1, size - i, file);
2162 18 : if (ferror(file)) {
2163 0 : fclose(file);
2164 0 : delete[] chars;
2165 : return nullptr;
2166 : }
2167 : }
2168 18 : fclose(file);
2169 18 : *size_out = static_cast<int>(size);
2170 18 : return chars;
2171 : }
2172 :
2173 :
2174 : struct DataAndPersistent {
2175 : uint8_t* data;
2176 : int byte_length;
2177 : Global<ArrayBuffer> handle;
2178 : };
2179 :
2180 :
2181 1 : static void ReadBufferWeakCallback(
2182 5 : const v8::WeakCallbackInfo<DataAndPersistent>& data) {
2183 1 : int byte_length = data.GetParameter()->byte_length;
2184 : data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
2185 1 : -static_cast<intptr_t>(byte_length));
2186 :
2187 1 : delete[] data.GetParameter()->data;
2188 : data.GetParameter()->handle.Reset();
2189 2 : delete data.GetParameter();
2190 1 : }
2191 :
2192 :
2193 36 : void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
2194 : static_assert(sizeof(char) == sizeof(uint8_t),
2195 : "char and uint8_t should both have 1 byte");
2196 : Isolate* isolate = args.GetIsolate();
2197 18 : String::Utf8Value filename(isolate, args[0]);
2198 : int length;
2199 18 : if (*filename == nullptr) {
2200 0 : Throw(isolate, "Error loading file");
2201 0 : return;
2202 : }
2203 :
2204 18 : DataAndPersistent* data = new DataAndPersistent;
2205 18 : data->data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
2206 18 : if (data->data == nullptr) {
2207 0 : delete data;
2208 0 : Throw(isolate, "Error reading file");
2209 0 : return;
2210 : }
2211 18 : data->byte_length = length;
2212 18 : Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
2213 : data->handle.Reset(isolate, buffer);
2214 : data->handle.SetWeak(data, ReadBufferWeakCallback,
2215 : v8::WeakCallbackType::kParameter);
2216 18 : isolate->AdjustAmountOfExternalAllocatedMemory(length);
2217 :
2218 18 : args.GetReturnValue().Set(buffer);
2219 : }
2220 :
2221 : // Reads a file into a v8 string.
2222 115537 : Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2223 : std::unique_ptr<base::OS::MemoryMappedFile> file(
2224 115537 : base::OS::MemoryMappedFile::open(name));
2225 115537 : if (!file) return Local<String>();
2226 :
2227 115442 : int size = static_cast<int>(file->size());
2228 115442 : char* chars = static_cast<char*>(file->memory());
2229 : Local<String> result;
2230 115460 : if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
2231 : String::ExternalOneByteStringResource* resource =
2232 18 : new ExternalOwningOneByteStringResource(std::move(file));
2233 18 : result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
2234 : } else {
2235 : result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
2236 115424 : .ToLocalChecked();
2237 : }
2238 115442 : return result;
2239 : }
2240 :
2241 :
2242 0 : void Shell::RunShell(Isolate* isolate) {
2243 0 : HandleScope outer_scope(isolate);
2244 : v8::Local<v8::Context> context =
2245 : v8::Local<v8::Context>::New(isolate, evaluation_context_);
2246 : v8::Context::Scope context_scope(context);
2247 0 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2248 : Local<String> name =
2249 : String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
2250 0 : .ToLocalChecked();
2251 0 : printf("V8 version %s\n", V8::GetVersion());
2252 : while (true) {
2253 0 : HandleScope inner_scope(isolate);
2254 : printf("d8> ");
2255 0 : Local<String> input = Shell::ReadFromStdin(isolate);
2256 0 : if (input.IsEmpty()) break;
2257 : ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
2258 0 : kProcessMessageQueue);
2259 0 : }
2260 : printf("\n");
2261 : // We need to explicitly clean up the module embedder data for
2262 : // the interative shell context.
2263 0 : DisposeModuleEmbedderData(context);
2264 0 : }
2265 :
2266 : class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
2267 : public:
2268 5038 : explicit InspectorFrontend(Local<Context> context) {
2269 2519 : isolate_ = context->GetIsolate();
2270 : context_.Reset(isolate_, context);
2271 2519 : }
2272 5038 : ~InspectorFrontend() override = default;
2273 :
2274 : private:
2275 127894 : void sendResponse(
2276 : int callId,
2277 : std::unique_ptr<v8_inspector::StringBuffer> message) override {
2278 127894 : Send(message->string());
2279 127894 : }
2280 143381 : void sendNotification(
2281 : std::unique_ptr<v8_inspector::StringBuffer> message) override {
2282 143381 : Send(message->string());
2283 143381 : }
2284 0 : void flushProtocolNotifications() override {}
2285 :
2286 1085100 : void Send(const v8_inspector::StringView& string) {
2287 271275 : v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2288 271275 : int length = static_cast<int>(string.length());
2289 : DCHECK_LT(length, v8::String::kMaxLength);
2290 : Local<String> message =
2291 : (string.is8Bit()
2292 : ? v8::String::NewFromOneByte(
2293 : isolate_,
2294 : reinterpret_cast<const uint8_t*>(string.characters8()),
2295 0 : v8::NewStringType::kNormal, length)
2296 : : v8::String::NewFromTwoByte(
2297 : isolate_,
2298 : reinterpret_cast<const uint16_t*>(string.characters16()),
2299 271275 : v8::NewStringType::kNormal, length))
2300 542550 : .ToLocalChecked();
2301 : Local<String> callback_name =
2302 271275 : v8::String::NewFromUtf8(isolate_, "receive", v8::NewStringType::kNormal)
2303 271275 : .ToLocalChecked();
2304 271275 : Local<Context> context = context_.Get(isolate_);
2305 : Local<Value> callback =
2306 813825 : context->Global()->Get(context, callback_name).ToLocalChecked();
2307 271275 : if (callback->IsFunction()) {
2308 271275 : v8::TryCatch try_catch(isolate_);
2309 : Local<Value> args[] = {message};
2310 542550 : USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 1,
2311 271275 : args));
2312 : #ifdef DEBUG
2313 : if (try_catch.HasCaught()) {
2314 : Local<Object> exception = Local<Object>::Cast(try_catch.Exception());
2315 : Local<String> key = v8::String::NewFromUtf8(isolate_, "message",
2316 : v8::NewStringType::kNormal)
2317 : .ToLocalChecked();
2318 : Local<String> expected =
2319 : v8::String::NewFromUtf8(isolate_,
2320 : "Maximum call stack size exceeded",
2321 : v8::NewStringType::kNormal)
2322 : .ToLocalChecked();
2323 : Local<Value> value = exception->Get(context, key).ToLocalChecked();
2324 : DCHECK(value->StrictEquals(expected));
2325 : }
2326 : #endif
2327 271275 : }
2328 271275 : }
2329 :
2330 : Isolate* isolate_;
2331 : Global<Context> context_;
2332 : };
2333 :
2334 99914 : class InspectorClient : public v8_inspector::V8InspectorClient {
2335 : public:
2336 99914 : InspectorClient(Local<Context> context, bool connect) {
2337 97395 : if (!connect) return;
2338 2519 : isolate_ = context->GetIsolate();
2339 5038 : channel_.reset(new InspectorFrontend(context));
2340 5038 : inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
2341 5038 : session_ =
2342 2519 : inspector_->connect(1, channel_.get(), v8_inspector::StringView());
2343 2519 : context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
2344 : inspector_->contextCreated(v8_inspector::V8ContextInfo(
2345 5038 : context, kContextGroupId, v8_inspector::StringView()));
2346 :
2347 : Local<Value> function =
2348 5038 : FunctionTemplate::New(isolate_, SendInspectorMessage)
2349 5038 : ->GetFunction(context)
2350 2519 : .ToLocalChecked();
2351 : Local<String> function_name =
2352 2519 : String::NewFromUtf8(isolate_, "send", NewStringType::kNormal)
2353 2519 : .ToLocalChecked();
2354 7557 : CHECK(context->Global()->Set(context, function_name, function).FromJust());
2355 :
2356 2519 : context_.Reset(isolate_, context);
2357 : }
2358 :
2359 : private:
2360 : static v8_inspector::V8InspectorSession* GetSession(Local<Context> context) {
2361 : InspectorClient* inspector_client = static_cast<InspectorClient*>(
2362 : context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
2363 : return inspector_client->session_.get();
2364 : }
2365 :
2366 513 : Local<Context> ensureDefaultContextInGroup(int group_id) override {
2367 : DCHECK(isolate_);
2368 : DCHECK_EQ(kContextGroupId, group_id);
2369 1026 : return context_.Get(isolate_);
2370 : }
2371 :
2372 127894 : static void SendInspectorMessage(
2373 383682 : const v8::FunctionCallbackInfo<v8::Value>& args) {
2374 : Isolate* isolate = args.GetIsolate();
2375 127894 : v8::HandleScope handle_scope(isolate);
2376 127894 : Local<Context> context = isolate->GetCurrentContext();
2377 : args.GetReturnValue().Set(Undefined(isolate));
2378 127894 : Local<String> message = args[0]->ToString(context).ToLocalChecked();
2379 : v8_inspector::V8InspectorSession* session =
2380 : InspectorClient::GetSession(context);
2381 127894 : int length = message->Length();
2382 127894 : std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
2383 127894 : message->Write(isolate, buffer.get(), 0, length);
2384 : v8_inspector::StringView message_view(buffer.get(), length);
2385 127894 : session->dispatchProtocolMessage(message_view);
2386 127894 : args.GetReturnValue().Set(True(isolate));
2387 127894 : }
2388 :
2389 : static const int kContextGroupId = 1;
2390 :
2391 : std::unique_ptr<v8_inspector::V8Inspector> inspector_;
2392 : std::unique_ptr<v8_inspector::V8InspectorSession> session_;
2393 : std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
2394 : Global<Context> context_;
2395 : Isolate* isolate_;
2396 : };
2397 :
2398 57546 : SourceGroup::~SourceGroup() {
2399 28773 : delete thread_;
2400 28773 : thread_ = nullptr;
2401 28773 : }
2402 :
2403 106977 : bool ends_with(const char* input, const char* suffix) {
2404 106977 : size_t input_length = strlen(input);
2405 106977 : size_t suffix_length = strlen(suffix);
2406 106977 : if (suffix_length <= input_length) {
2407 106977 : return strcmp(input + input_length - suffix_length, suffix) == 0;
2408 : }
2409 : return false;
2410 : }
2411 :
2412 49957 : void SourceGroup::Execute(Isolate* isolate) {
2413 : bool exception_was_thrown = false;
2414 157074 : for (int i = begin_offset_; i < end_offset_; ++i) {
2415 108476 : const char* arg = argv_[i];
2416 108476 : if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
2417 : // Execute argument given to -e option directly.
2418 1499 : HandleScope handle_scope(isolate);
2419 : Local<String> file_name =
2420 : String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2421 1499 : .ToLocalChecked();
2422 : Local<String> source =
2423 1499 : String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
2424 2998 : .ToLocalChecked();
2425 : Shell::set_script_executed();
2426 1499 : if (!Shell::ExecuteString(isolate, source, file_name,
2427 : Shell::kNoPrintResult, Shell::kReportExceptions,
2428 1499 : Shell::kNoProcessMessageQueue)) {
2429 : exception_was_thrown = true;
2430 1341 : break;
2431 : }
2432 : ++i;
2433 158 : continue;
2434 106977 : } else if (ends_with(arg, ".mjs")) {
2435 : Shell::set_script_executed();
2436 0 : if (!Shell::ExecuteModule(isolate, arg)) {
2437 : exception_was_thrown = true;
2438 : break;
2439 : }
2440 : continue;
2441 106977 : } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
2442 : // Treat the next file as a module.
2443 491 : arg = argv_[++i];
2444 : Shell::set_script_executed();
2445 491 : if (!Shell::ExecuteModule(isolate, arg)) {
2446 : exception_was_thrown = true;
2447 : break;
2448 : }
2449 : continue;
2450 106486 : } else if (arg[0] == '-') {
2451 : // Ignore other options. They have been parsed already.
2452 : continue;
2453 : }
2454 :
2455 : // Use all other arguments as names of files to load and run.
2456 105557 : HandleScope handle_scope(isolate);
2457 : Local<String> file_name =
2458 : String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
2459 105557 : .ToLocalChecked();
2460 : Local<String> source = ReadFile(isolate, arg);
2461 105557 : if (source.IsEmpty()) {
2462 : printf("Error reading '%s'\n", arg);
2463 0 : base::OS::ExitProcess(1);
2464 : }
2465 : Shell::set_script_executed();
2466 105557 : if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
2467 : Shell::kReportExceptions,
2468 105557 : Shell::kProcessMessageQueue)) {
2469 : exception_was_thrown = true;
2470 18 : break;
2471 : }
2472 105539 : }
2473 49957 : if (exception_was_thrown != Shell::options.expected_to_throw) {
2474 0 : base::OS::ExitProcess(1);
2475 : }
2476 49957 : }
2477 :
2478 0 : Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
2479 105557 : return Shell::ReadFile(isolate, name);
2480 : }
2481 :
2482 0 : SourceGroup::IsolateThread::IsolateThread(SourceGroup* group)
2483 0 : : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {}
2484 :
2485 0 : void SourceGroup::ExecuteInThread() {
2486 : Isolate::CreateParams create_params;
2487 0 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2488 0 : Isolate* isolate = Isolate::New(create_params);
2489 : isolate->SetHostImportModuleDynamicallyCallback(
2490 0 : Shell::HostImportModuleDynamically);
2491 : isolate->SetHostInitializeImportMetaObjectCallback(
2492 0 : Shell::HostInitializeImportMetaObject);
2493 0 : Shell::SetWaitUntilDone(isolate, false);
2494 0 : D8Console console(isolate);
2495 0 : debug::SetConsoleDelegate(isolate, &console);
2496 0 : for (int i = 0; i < Shell::options.stress_runs; ++i) {
2497 0 : next_semaphore_.Wait();
2498 : {
2499 : Isolate::Scope iscope(isolate);
2500 0 : PerIsolateData data(isolate);
2501 : {
2502 0 : HandleScope scope(isolate);
2503 0 : Local<Context> context = Shell::CreateEvaluationContext(isolate);
2504 : {
2505 : Context::Scope cscope(context);
2506 : InspectorClient inspector_client(context,
2507 0 : Shell::options.enable_inspector);
2508 0 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2509 0 : Execute(isolate);
2510 0 : Shell::CompleteMessageLoop(isolate);
2511 : }
2512 0 : DisposeModuleEmbedderData(context);
2513 : }
2514 0 : Shell::CollectGarbage(isolate);
2515 : }
2516 0 : done_semaphore_.Signal();
2517 : }
2518 :
2519 0 : isolate->Dispose();
2520 0 : }
2521 :
2522 :
2523 0 : void SourceGroup::StartExecuteInThread() {
2524 0 : if (thread_ == nullptr) {
2525 0 : thread_ = new IsolateThread(this);
2526 0 : thread_->Start();
2527 : }
2528 0 : next_semaphore_.Signal();
2529 0 : }
2530 :
2531 :
2532 0 : void SourceGroup::WaitForThread() {
2533 0 : if (thread_ == nullptr) return;
2534 0 : done_semaphore_.Wait();
2535 : }
2536 :
2537 :
2538 0 : void SourceGroup::JoinThread() {
2539 0 : if (thread_ == nullptr) return;
2540 0 : thread_->Join();
2541 : }
2542 :
2543 0 : ExternalizedContents::~ExternalizedContents() {
2544 403 : if (data_ != nullptr) {
2545 153 : deleter_(data_, length_, deleter_data_);
2546 : }
2547 0 : }
2548 :
2549 3369 : void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
2550 3369 : base::MutexGuard lock_guard(&mutex_);
2551 3383 : data_.push_back(std::move(data));
2552 3384 : }
2553 :
2554 2902 : bool SerializationDataQueue::Dequeue(
2555 : std::unique_ptr<SerializationData>* out_data) {
2556 : out_data->reset();
2557 2902 : base::MutexGuard lock_guard(&mutex_);
2558 2902 : if (data_.empty()) return false;
2559 : *out_data = std::move(data_[0]);
2560 2222 : data_.erase(data_.begin());
2561 2222 : return true;
2562 : }
2563 :
2564 :
2565 0 : bool SerializationDataQueue::IsEmpty() {
2566 0 : base::MutexGuard lock_guard(&mutex_);
2567 0 : return data_.empty();
2568 : }
2569 :
2570 :
2571 1244 : void SerializationDataQueue::Clear() {
2572 1244 : base::MutexGuard lock_guard(&mutex_);
2573 : data_.clear();
2574 1244 : }
2575 :
2576 622 : Worker::Worker()
2577 : : in_semaphore_(0),
2578 : out_semaphore_(0),
2579 : thread_(nullptr),
2580 : script_(nullptr),
2581 1244 : running_(false) {}
2582 :
2583 1244 : Worker::~Worker() {
2584 622 : delete thread_;
2585 622 : thread_ = nullptr;
2586 622 : delete[] script_;
2587 622 : script_ = nullptr;
2588 622 : in_queue_.Clear();
2589 622 : out_queue_.Clear();
2590 622 : }
2591 :
2592 :
2593 622 : void Worker::StartExecuteInThread(const char* script) {
2594 622 : running_ = true;
2595 622 : script_ = i::StrDup(script);
2596 1244 : thread_ = new WorkerThread(this);
2597 622 : thread_->Start();
2598 622 : }
2599 :
2600 1710 : void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
2601 3420 : in_queue_.Enqueue(std::move(data));
2602 1710 : in_semaphore_.Signal();
2603 1710 : }
2604 :
2605 1079 : std::unique_ptr<SerializationData> Worker::GetMessage() {
2606 1079 : std::unique_ptr<SerializationData> result;
2607 2829 : while (!out_queue_.Dequeue(&result)) {
2608 : // If the worker is no longer running, and there are no messages in the
2609 : // queue, don't expect any more messages from it.
2610 1360 : if (!base::Relaxed_Load(&running_)) break;
2611 671 : out_semaphore_.Wait();
2612 : }
2613 1079 : return result;
2614 : }
2615 :
2616 :
2617 1045 : void Worker::Terminate() {
2618 1045 : base::Relaxed_Store(&running_, false);
2619 : // Post nullptr to wake the Worker thread message loop, and tell it to stop
2620 : // running.
2621 2090 : PostMessage(nullptr);
2622 1045 : }
2623 :
2624 :
2625 0 : void Worker::WaitForThread() {
2626 622 : Terminate();
2627 622 : thread_->Join();
2628 0 : }
2629 :
2630 :
2631 622 : void Worker::ExecuteInThread() {
2632 : Isolate::CreateParams create_params;
2633 622 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2634 622 : Isolate* isolate = Isolate::New(create_params);
2635 : isolate->SetHostImportModuleDynamicallyCallback(
2636 621 : Shell::HostImportModuleDynamically);
2637 : isolate->SetHostInitializeImportMetaObjectCallback(
2638 621 : Shell::HostInitializeImportMetaObject);
2639 621 : D8Console console(isolate);
2640 622 : debug::SetConsoleDelegate(isolate, &console);
2641 : {
2642 : Isolate::Scope iscope(isolate);
2643 : {
2644 622 : HandleScope scope(isolate);
2645 1244 : PerIsolateData data(isolate);
2646 621 : Local<Context> context = Shell::CreateEvaluationContext(isolate);
2647 : {
2648 : Context::Scope cscope(context);
2649 1244 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2650 :
2651 622 : Local<Object> global = context->Global();
2652 622 : Local<Value> this_value = External::New(isolate, this);
2653 : Local<FunctionTemplate> postmessage_fun_template =
2654 622 : FunctionTemplate::New(isolate, PostMessageOut, this_value);
2655 :
2656 : Local<Function> postmessage_fun;
2657 622 : if (postmessage_fun_template->GetFunction(context)
2658 622 : .ToLocal(&postmessage_fun)) {
2659 : global->Set(context, String::NewFromUtf8(isolate, "postMessage",
2660 : NewStringType::kNormal)
2661 : .ToLocalChecked(),
2662 1866 : postmessage_fun).FromJust();
2663 : }
2664 :
2665 : // First run the script
2666 : Local<String> file_name =
2667 : String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2668 622 : .ToLocalChecked();
2669 : Local<String> source =
2670 622 : String::NewFromUtf8(isolate, script_, NewStringType::kNormal)
2671 1244 : .ToLocalChecked();
2672 1244 : if (Shell::ExecuteString(
2673 : isolate, source, file_name, Shell::kNoPrintResult,
2674 622 : Shell::kReportExceptions, Shell::kProcessMessageQueue)) {
2675 : // Get the message handler
2676 : Local<Value> onmessage =
2677 : global->Get(context, String::NewFromUtf8(isolate, "onmessage",
2678 : NewStringType::kNormal)
2679 1866 : .ToLocalChecked()).ToLocalChecked();
2680 622 : if (onmessage->IsFunction()) {
2681 : Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
2682 : // Now wait for messages
2683 : while (true) {
2684 1146 : in_semaphore_.Wait();
2685 1152 : std::unique_ptr<SerializationData> data;
2686 1152 : if (!in_queue_.Dequeue(&data)) continue;
2687 1152 : if (!data) {
2688 : break;
2689 : }
2690 1237 : v8::TryCatch try_catch(isolate);
2691 : Local<Value> value;
2692 620 : if (Shell::DeserializeValue(isolate, std::move(data))
2693 620 : .ToLocal(&value)) {
2694 620 : Local<Value> argv[] = {value};
2695 : MaybeLocal<Value> result =
2696 620 : onmessage_fun->Call(context, global, 1, argv);
2697 : USE(result);
2698 : }
2699 615 : if (try_catch.HasCaught()) {
2700 9 : Shell::ReportException(isolate, &try_catch);
2701 : }
2702 : }
2703 : }
2704 : }
2705 : }
2706 1244 : DisposeModuleEmbedderData(context);
2707 : }
2708 622 : Shell::CollectGarbage(isolate);
2709 : }
2710 622 : isolate->Dispose();
2711 :
2712 : // Post nullptr to wake the thread waiting on GetMessage() if there is one.
2713 1244 : out_queue_.Enqueue(nullptr);
2714 622 : out_semaphore_.Signal();
2715 621 : }
2716 :
2717 :
2718 3132 : void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
2719 : Isolate* isolate = args.GetIsolate();
2720 1041 : HandleScope handle_scope(isolate);
2721 :
2722 1042 : if (args.Length() < 1) {
2723 0 : Throw(isolate, "Invalid argument");
2724 1045 : return;
2725 : }
2726 :
2727 1042 : Local<Value> message = args[0];
2728 : Local<Value> transfer = Undefined(isolate);
2729 : std::unique_ptr<SerializationData> data =
2730 1042 : Shell::SerializeValue(isolate, message, transfer);
2731 1048 : if (data) {
2732 : DCHECK(args.Data()->IsExternal());
2733 : Local<External> this_value = Local<External>::Cast(args.Data());
2734 1049 : Worker* worker = static_cast<Worker*>(this_value->Value());
2735 2101 : worker->out_queue_.Enqueue(std::move(data));
2736 1052 : worker->out_semaphore_.Signal();
2737 1044 : }
2738 : }
2739 :
2740 :
2741 86318 : void SetFlagsFromString(const char* flags) {
2742 86318 : v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags)));
2743 86318 : }
2744 :
2745 :
2746 28773 : bool Shell::SetOptions(int argc, char* argv[]) {
2747 : bool logfile_per_isolate = false;
2748 255083 : for (int i = 0; i < argc; i++) {
2749 226315 : if (strcmp(argv[i], "--") == 0) {
2750 5 : argv[i] = nullptr;
2751 20 : for (int j = i + 1; j < argc; j++) {
2752 30 : options.arguments.push_back(argv[j]);
2753 15 : argv[j] = nullptr;
2754 : }
2755 : break;
2756 226310 : } else if (strcmp(argv[i], "--no-arguments") == 0) {
2757 0 : options.include_arguments = false;
2758 0 : argv[i] = nullptr;
2759 226310 : } else if (strcmp(argv[i], "--stress-opt") == 0) {
2760 5456 : options.stress_opt = true;
2761 5456 : argv[i] = nullptr;
2762 441643 : } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
2763 220789 : strcmp(argv[i], "--no-stress-opt") == 0) {
2764 385 : options.stress_opt = false;
2765 385 : argv[i] = nullptr;
2766 220469 : } else if (strcmp(argv[i], "--stress-deopt") == 0) {
2767 0 : options.stress_deopt = true;
2768 0 : argv[i] = nullptr;
2769 220469 : } else if (strcmp(argv[i], "--stress-background-compile") == 0) {
2770 5453 : options.stress_background_compile = true;
2771 5453 : argv[i] = nullptr;
2772 430032 : } else if (strcmp(argv[i], "--nostress-background-compile") == 0 ||
2773 215016 : strcmp(argv[i], "--no-stress-background-compile") == 0) {
2774 10 : options.stress_background_compile = false;
2775 10 : argv[i] = nullptr;
2776 429857 : } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
2777 214851 : strcmp(argv[i], "--no-always-opt") == 0) {
2778 : // No support for stressing if we can't use --always-opt.
2779 619 : options.stress_opt = false;
2780 619 : options.stress_deopt = false;
2781 214387 : } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
2782 : logfile_per_isolate = true;
2783 0 : argv[i] = nullptr;
2784 214387 : } else if (strcmp(argv[i], "--shell") == 0) {
2785 0 : options.interactive_shell = true;
2786 0 : argv[i] = nullptr;
2787 214387 : } else if (strcmp(argv[i], "--test") == 0) {
2788 28773 : options.test_shell = true;
2789 28773 : argv[i] = nullptr;
2790 371228 : } else if (strcmp(argv[i], "--notest") == 0 ||
2791 185614 : strcmp(argv[i], "--no-test") == 0) {
2792 10 : options.test_shell = false;
2793 10 : argv[i] = nullptr;
2794 185604 : } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
2795 5 : options.send_idle_notification = true;
2796 5 : argv[i] = nullptr;
2797 185599 : } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
2798 35 : options.invoke_weak_callbacks = true;
2799 : // TODO(jochen) See issue 3351
2800 35 : options.send_idle_notification = true;
2801 35 : argv[i] = nullptr;
2802 185564 : } else if (strcmp(argv[i], "--omit-quit") == 0) {
2803 20 : options.omit_quit = true;
2804 20 : argv[i] = nullptr;
2805 185544 : } else if (strcmp(argv[i], "--no-wait-for-wasm") == 0) {
2806 : // TODO(herhut) Remove this flag once wasm compilation is fully
2807 : // isolate-independent.
2808 10 : options.wait_for_wasm = false;
2809 10 : argv[i] = nullptr;
2810 185534 : } else if (strcmp(argv[i], "-f") == 0) {
2811 : // Ignore any -f flags for compatibility with other stand-alone
2812 : // JavaScript engines.
2813 : continue;
2814 185534 : } else if (strcmp(argv[i], "--isolate") == 0) {
2815 0 : options.num_isolates++;
2816 185534 : } else if (strcmp(argv[i], "--throws") == 0) {
2817 1351 : options.expected_to_throw = true;
2818 1351 : argv[i] = nullptr;
2819 184183 : } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
2820 0 : options.icu_data_file = argv[i] + 16;
2821 0 : argv[i] = nullptr;
2822 : #ifdef V8_USE_EXTERNAL_STARTUP_DATA
2823 184183 : } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) {
2824 0 : options.natives_blob = argv[i] + 15;
2825 0 : argv[i] = nullptr;
2826 184183 : } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
2827 0 : options.snapshot_blob = argv[i] + 16;
2828 0 : argv[i] = nullptr;
2829 : #endif // V8_USE_EXTERNAL_STARTUP_DATA
2830 368366 : } else if (strcmp(argv[i], "--cache") == 0 ||
2831 184183 : strncmp(argv[i], "--cache=", 8) == 0) {
2832 60 : const char* value = argv[i] + 7;
2833 60 : if (!*value || strncmp(value, "=code", 6) == 0) {
2834 55 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2835 : options.code_cache_options =
2836 55 : ShellOptions::CodeCacheOptions::kProduceCache;
2837 5 : } else if (strncmp(value, "=none", 6) == 0) {
2838 0 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2839 : options.code_cache_options =
2840 0 : ShellOptions::CodeCacheOptions::kNoProduceCache;
2841 5 : } else if (strncmp(value, "=after-execute", 15) == 0) {
2842 5 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2843 : options.code_cache_options =
2844 5 : ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute;
2845 0 : } else if (strncmp(value, "=full-code-cache", 17) == 0) {
2846 0 : options.compile_options = v8::ScriptCompiler::kEagerCompile;
2847 : options.code_cache_options =
2848 0 : ShellOptions::CodeCacheOptions::kProduceCache;
2849 : } else {
2850 : printf("Unknown option to --cache.\n");
2851 0 : return false;
2852 : }
2853 60 : argv[i] = nullptr;
2854 184123 : } else if (strcmp(argv[i], "--enable-tracing") == 0) {
2855 1 : options.trace_enabled = true;
2856 1 : argv[i] = nullptr;
2857 184122 : } else if (strncmp(argv[i], "--trace-path=", 13) == 0) {
2858 1 : options.trace_path = argv[i] + 13;
2859 1 : argv[i] = nullptr;
2860 184121 : } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
2861 0 : options.trace_config = argv[i] + 15;
2862 0 : argv[i] = nullptr;
2863 184121 : } else if (strcmp(argv[i], "--enable-inspector") == 0) {
2864 1453 : options.enable_inspector = true;
2865 1453 : argv[i] = nullptr;
2866 182668 : } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
2867 0 : options.lcov_file = argv[i] + 7;
2868 0 : argv[i] = nullptr;
2869 182668 : } else if (strcmp(argv[i], "--disable-in-process-stack-traces") == 0) {
2870 10 : options.disable_in_process_stack_traces = true;
2871 10 : argv[i] = nullptr;
2872 : #ifdef V8_OS_POSIX
2873 182658 : } else if (strncmp(argv[i], "--read-from-tcp-port=", 21) == 0) {
2874 0 : options.read_from_tcp_port = atoi(argv[i] + 21);
2875 0 : argv[i] = nullptr;
2876 : #endif // V8_OS_POSIX
2877 182658 : } else if (strcmp(argv[i], "--enable-os-system") == 0) {
2878 0 : options.enable_os_system = true;
2879 0 : argv[i] = nullptr;
2880 182658 : } else if (strcmp(argv[i], "--quiet-load") == 0) {
2881 0 : options.quiet_load = true;
2882 0 : argv[i] = nullptr;
2883 182658 : } else if (strncmp(argv[i], "--thread-pool-size=", 19) == 0) {
2884 0 : options.thread_pool_size = atoi(argv[i] + 19);
2885 0 : argv[i] = nullptr;
2886 182658 : } else if (strcmp(argv[i], "--stress-delay-tasks") == 0) {
2887 : // Delay execution of tasks by 0-100ms randomly (based on --random-seed).
2888 0 : options.stress_delay_tasks = true;
2889 0 : argv[i] = nullptr;
2890 : }
2891 : }
2892 :
2893 28773 : v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
2894 28773 : options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator;
2895 : options.mock_arraybuffer_allocator_limit =
2896 28773 : i::FLAG_mock_arraybuffer_allocator_limit;
2897 :
2898 : // Set up isolated source groups.
2899 28773 : options.isolate_sources = new SourceGroup[options.num_isolates];
2900 : SourceGroup* current = options.isolate_sources;
2901 : current->Begin(argv, 1);
2902 92187 : for (int i = 1; i < argc; i++) {
2903 63414 : const char* str = argv[i];
2904 63414 : if (strcmp(str, "--isolate") == 0) {
2905 : current->End(i);
2906 0 : current++;
2907 0 : current->Begin(argv, i + 1);
2908 63414 : } else if (strcmp(str, "--module") == 0) {
2909 : // Pass on to SourceGroup, which understands this option.
2910 63139 : } else if (strncmp(str, "--", 2) == 0) {
2911 : printf("Warning: unknown flag %s.\nTry --help for options\n", str);
2912 62614 : } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
2913 : set_script_executed();
2914 61123 : } else if (strncmp(str, "-", 1) != 0) {
2915 : // Not a flag, so it must be a script to execute.
2916 : set_script_executed();
2917 : }
2918 : }
2919 : current->End(argc);
2920 :
2921 28773 : if (!logfile_per_isolate && options.num_isolates) {
2922 28773 : SetFlagsFromString("--nologfile_per_isolate");
2923 : }
2924 :
2925 : return true;
2926 : }
2927 :
2928 49957 : int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
2929 49957 : for (int i = 1; i < options.num_isolates; ++i) {
2930 0 : options.isolate_sources[i].StartExecuteInThread();
2931 : }
2932 : {
2933 49957 : SetWaitUntilDone(isolate, false);
2934 49957 : if (options.lcov_file) {
2935 0 : debug::Coverage::SelectMode(isolate, debug::Coverage::kBlockCount);
2936 : }
2937 49957 : HandleScope scope(isolate);
2938 49957 : Local<Context> context = CreateEvaluationContext(isolate);
2939 49957 : bool use_existing_context = last_run && use_interactive_shell();
2940 49957 : if (use_existing_context) {
2941 : // Keep using the same context in the interactive shell.
2942 : evaluation_context_.Reset(isolate, context);
2943 : }
2944 : {
2945 : Context::Scope cscope(context);
2946 99914 : InspectorClient inspector_client(context, options.enable_inspector);
2947 99914 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2948 49957 : options.isolate_sources[0].Execute(isolate);
2949 49957 : CompleteMessageLoop(isolate);
2950 : }
2951 49957 : if (!use_existing_context) {
2952 49957 : DisposeModuleEmbedderData(context);
2953 : }
2954 49957 : WriteLcovData(isolate, options.lcov_file);
2955 : }
2956 49957 : CollectGarbage(isolate);
2957 49957 : for (int i = 1; i < options.num_isolates; ++i) {
2958 0 : if (last_run) {
2959 0 : options.isolate_sources[i].JoinThread();
2960 : } else {
2961 0 : options.isolate_sources[i].WaitForThread();
2962 : }
2963 : }
2964 49957 : CleanupWorkers();
2965 49957 : return 0;
2966 : }
2967 :
2968 :
2969 79352 : void Shell::CollectGarbage(Isolate* isolate) {
2970 79352 : if (options.send_idle_notification) {
2971 : const double kLongIdlePauseInSeconds = 1.0;
2972 148 : isolate->ContextDisposedNotification();
2973 : isolate->IdleNotificationDeadline(
2974 148 : g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
2975 : }
2976 79352 : if (options.invoke_weak_callbacks) {
2977 : // By sending a low memory notifications, we will try hard to collect all
2978 : // garbage and will therefore also invoke all weak callbacks of actually
2979 : // unreachable persistent handles.
2980 103 : isolate->LowMemoryNotification();
2981 : }
2982 79352 : }
2983 :
2984 52155 : void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
2985 : base::MutexGuard guard(isolate_status_lock_.Pointer());
2986 52155 : if (isolate_status_.count(isolate) == 0) {
2987 57642 : isolate_status_.insert(std::make_pair(isolate, value));
2988 : } else {
2989 23334 : isolate_status_[isolate] = value;
2990 : }
2991 52155 : }
2992 :
2993 : namespace {
2994 156879 : bool ProcessMessages(
2995 : Isolate* isolate,
2996 : const std::function<platform::MessageLoopBehavior()>& behavior) {
2997 : while (true) {
2998 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2999 158175 : i::SaveContext saved_context(i_isolate);
3000 : i_isolate->set_context(i::Context());
3001 159471 : SealHandleScope shs(isolate);
3002 384252 : while (v8::platform::PumpMessageLoop(g_default_platform, isolate,
3003 226077 : behavior())) {
3004 67902 : MicrotasksScope::PerformCheckpoint(isolate);
3005 : }
3006 158175 : if (g_default_platform->IdleTasksEnabled(isolate)) {
3007 : v8::platform::RunIdleTasks(g_default_platform, isolate,
3008 158175 : 50.0 / base::Time::kMillisecondsPerSecond);
3009 : }
3010 159471 : HandleScope handle_scope(isolate);
3011 : PerIsolateData* data = PerIsolateData::Get(isolate);
3012 : Local<Function> callback;
3013 316350 : if (!data->GetTimeoutCallback().ToLocal(&callback)) break;
3014 : Local<Context> context;
3015 2592 : if (!data->GetTimeoutContext().ToLocal(&context)) break;
3016 2592 : TryCatch try_catch(isolate);
3017 1296 : try_catch.SetVerbose(true);
3018 : Context::Scope context_scope(context);
3019 2592 : if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
3020 0 : Shell::ReportException(isolate, &try_catch);
3021 : return false;
3022 : }
3023 1296 : }
3024 156879 : return true;
3025 : }
3026 : } // anonymous namespace
3027 :
3028 49957 : void Shell::CompleteMessageLoop(Isolate* isolate) {
3029 53819 : auto get_waiting_behaviour = [isolate]() {
3030 : base::MutexGuard guard(isolate_status_lock_.Pointer());
3031 : DCHECK_GT(isolate_status_.count(isolate), 0);
3032 53819 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3033 : i::wasm::WasmEngine* wasm_engine = i_isolate->wasm_engine();
3034 53798 : bool should_wait = (options.wait_for_wasm &&
3035 104240 : wasm_engine->HasRunningCompileJob(i_isolate)) ||
3036 50421 : isolate_status_[isolate];
3037 : return should_wait ? platform::MessageLoopBehavior::kWaitForWork
3038 107638 : : platform::MessageLoopBehavior::kDoNotWait;
3039 : };
3040 99914 : ProcessMessages(isolate, get_waiting_behaviour);
3041 49957 : }
3042 :
3043 106922 : bool Shell::EmptyMessageQueues(Isolate* isolate) {
3044 : return ProcessMessages(
3045 213844 : isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
3046 : }
3047 :
3048 5712 : class Serializer : public ValueSerializer::Delegate {
3049 : public:
3050 1897 : explicit Serializer(Isolate* isolate)
3051 : : isolate_(isolate),
3052 : serializer_(isolate, this),
3053 3795 : current_memory_usage_(0) {}
3054 :
3055 1898 : Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
3056 : Local<Value> transfer) {
3057 : bool ok;
3058 : DCHECK(!data_);
3059 1898 : data_.reset(new SerializationData);
3060 3803 : if (!PrepareTransfer(context, transfer).To(&ok)) {
3061 : return Nothing<bool>();
3062 : }
3063 1735 : serializer_.WriteHeader();
3064 :
3065 3468 : if (!serializer_.WriteValue(context, value).To(&ok)) {
3066 : data_.reset();
3067 : return Nothing<bool>();
3068 : }
3069 :
3070 3411 : if (!FinalizeTransfer().To(&ok)) {
3071 : return Nothing<bool>();
3072 : }
3073 :
3074 1691 : std::pair<uint8_t*, size_t> pair = serializer_.Release();
3075 1691 : data_->data_.reset(pair.first);
3076 1691 : data_->size_ = pair.second;
3077 : return Just(true);
3078 : }
3079 :
3080 : std::unique_ptr<SerializationData> Release() { return std::move(data_); }
3081 :
3082 1907 : void AppendExternalizedContentsTo(std::vector<ExternalizedContents>* to) {
3083 : to->insert(to->end(),
3084 : std::make_move_iterator(externalized_contents_.begin()),
3085 1907 : std::make_move_iterator(externalized_contents_.end()));
3086 : externalized_contents_.clear();
3087 1907 : }
3088 :
3089 : protected:
3090 : // Implements ValueSerializer::Delegate.
3091 10 : void ThrowDataCloneError(Local<String> message) override {
3092 10 : isolate_->ThrowException(Exception::Error(message));
3093 10 : }
3094 :
3095 378 : Maybe<uint32_t> GetSharedArrayBufferId(
3096 : Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override {
3097 : DCHECK_NOT_NULL(data_);
3098 756 : for (size_t index = 0; index < shared_array_buffers_.size(); ++index) {
3099 378 : if (shared_array_buffers_[index] == shared_array_buffer) {
3100 0 : return Just<uint32_t>(static_cast<uint32_t>(index));
3101 : }
3102 : }
3103 :
3104 : size_t index = shared_array_buffers_.size();
3105 378 : shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
3106 : data_->shared_array_buffer_contents_.push_back(
3107 1134 : MaybeExternalize(shared_array_buffer));
3108 378 : return Just<uint32_t>(static_cast<uint32_t>(index));
3109 : }
3110 :
3111 99 : Maybe<uint32_t> GetWasmModuleTransferId(
3112 : Isolate* isolate, Local<WasmModuleObject> module) override {
3113 : DCHECK_NOT_NULL(data_);
3114 198 : for (size_t index = 0; index < wasm_modules_.size(); ++index) {
3115 99 : if (wasm_modules_[index] == module) {
3116 0 : return Just<uint32_t>(static_cast<uint32_t>(index));
3117 : }
3118 : }
3119 :
3120 : size_t index = wasm_modules_.size();
3121 99 : wasm_modules_.emplace_back(isolate_, module);
3122 297 : data_->transferrable_modules_.push_back(module->GetTransferrableModule());
3123 99 : return Just<uint32_t>(static_cast<uint32_t>(index));
3124 : }
3125 :
3126 1771 : void* ReallocateBufferMemory(void* old_buffer, size_t size,
3127 : size_t* actual_size) override {
3128 : // Not accurate, because we don't take into account reallocated buffers,
3129 : // but this is fine for testing.
3130 1771 : current_memory_usage_ += size;
3131 1771 : if (current_memory_usage_ > kMaxSerializerMemoryUsage) return nullptr;
3132 :
3133 1791 : void* result = realloc(old_buffer, size);
3134 1791 : *actual_size = result ? size : 0;
3135 1791 : return result;
3136 : }
3137 :
3138 28 : void FreeBufferMemory(void* buffer) override { free(buffer); }
3139 :
3140 : private:
3141 1903 : Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
3142 1903 : if (transfer->IsArray()) {
3143 : Local<Array> transfer_array = Local<Array>::Cast(transfer);
3144 198 : uint32_t length = transfer_array->Length();
3145 243 : for (uint32_t i = 0; i < length; ++i) {
3146 : Local<Value> element;
3147 414 : if (transfer_array->Get(context, i).ToLocal(&element)) {
3148 198 : if (!element->IsArrayBuffer()) {
3149 144 : Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
3150 153 : return Nothing<bool>();
3151 : }
3152 :
3153 54 : Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
3154 :
3155 54 : if (std::find(array_buffers_.begin(), array_buffers_.end(),
3156 45 : array_buffer) != array_buffers_.end()) {
3157 : Throw(isolate_,
3158 9 : "ArrayBuffer occurs in the transfer array more than once");
3159 : return Nothing<bool>();
3160 : }
3161 :
3162 : serializer_.TransferArrayBuffer(
3163 45 : static_cast<uint32_t>(array_buffers_.size()), array_buffer);
3164 45 : array_buffers_.emplace_back(isolate_, array_buffer);
3165 : } else {
3166 : return Nothing<bool>();
3167 : }
3168 : }
3169 : return Just(true);
3170 1699 : } else if (transfer->IsUndefined()) {
3171 : return Just(true);
3172 : } else {
3173 0 : Throw(isolate_, "Transfer list must be an Array or undefined");
3174 : return Nothing<bool>();
3175 : }
3176 : }
3177 :
3178 : template <typename T>
3179 405 : typename T::Contents MaybeExternalize(Local<T> array_buffer) {
3180 405 : if (array_buffer->IsExternal()) {
3181 234 : return array_buffer->GetContents();
3182 : } else {
3183 171 : typename T::Contents contents = array_buffer->Externalize();
3184 171 : externalized_contents_.emplace_back(contents);
3185 171 : return contents;
3186 : }
3187 : }
3188 :
3189 1698 : Maybe<bool> FinalizeTransfer() {
3190 3423 : for (const auto& global_array_buffer : array_buffers_) {
3191 : Local<ArrayBuffer> array_buffer =
3192 36 : Local<ArrayBuffer>::New(isolate_, global_array_buffer);
3193 36 : if (!array_buffer->IsDetachable()) {
3194 9 : Throw(isolate_, "ArrayBuffer could not be transferred");
3195 9 : return Nothing<bool>();
3196 : }
3197 :
3198 27 : ArrayBuffer::Contents contents = MaybeExternalize(array_buffer);
3199 27 : array_buffer->Detach();
3200 27 : data_->array_buffer_contents_.push_back(contents);
3201 : }
3202 :
3203 : return Just(true);
3204 : }
3205 :
3206 : Isolate* isolate_;
3207 : ValueSerializer serializer_;
3208 : std::unique_ptr<SerializationData> data_;
3209 : std::vector<Global<ArrayBuffer>> array_buffers_;
3210 : std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
3211 : std::vector<Global<WasmModuleObject>> wasm_modules_;
3212 : std::vector<ExternalizedContents> externalized_contents_;
3213 : size_t current_memory_usage_;
3214 :
3215 : DISALLOW_COPY_AND_ASSIGN(Serializer);
3216 : };
3217 :
3218 5016 : class Deserializer : public ValueDeserializer::Delegate {
3219 : public:
3220 1672 : Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
3221 : : isolate_(isolate),
3222 : deserializer_(isolate, data->data(), data->size(), this),
3223 5016 : data_(std::move(data)) {
3224 1672 : deserializer_.SetSupportsLegacyWireFormat(true);
3225 1672 : }
3226 :
3227 1672 : MaybeLocal<Value> ReadValue(Local<Context> context) {
3228 : bool read_header;
3229 3343 : if (!deserializer_.ReadHeader(context).To(&read_header)) {
3230 0 : return MaybeLocal<Value>();
3231 : }
3232 :
3233 : uint32_t index = 0;
3234 3369 : for (const auto& contents : data_->array_buffer_contents()) {
3235 : Local<ArrayBuffer> array_buffer =
3236 27 : ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
3237 27 : deserializer_.TransferArrayBuffer(index++, array_buffer);
3238 : }
3239 :
3240 1671 : return deserializer_.ReadValue(context);
3241 : }
3242 :
3243 378 : MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
3244 : Isolate* isolate, uint32_t clone_id) override {
3245 : DCHECK_NOT_NULL(data_);
3246 1134 : if (clone_id < data_->shared_array_buffer_contents().size()) {
3247 : SharedArrayBuffer::Contents contents =
3248 378 : data_->shared_array_buffer_contents().at(clone_id);
3249 : return SharedArrayBuffer::New(isolate_, contents.Data(),
3250 378 : contents.ByteLength());
3251 : }
3252 0 : return MaybeLocal<SharedArrayBuffer>();
3253 : }
3254 :
3255 99 : MaybeLocal<WasmModuleObject> GetWasmModuleFromId(
3256 : Isolate* isolate, uint32_t transfer_id) override {
3257 : DCHECK_NOT_NULL(data_);
3258 297 : if (transfer_id < data_->transferrable_modules().size()) {
3259 : return WasmModuleObject::FromTransferrableModule(
3260 99 : isolate_, data_->transferrable_modules().at(transfer_id));
3261 : }
3262 0 : return MaybeLocal<WasmModuleObject>();
3263 : }
3264 :
3265 : private:
3266 : Isolate* isolate_;
3267 : ValueDeserializer deserializer_;
3268 : std::unique_ptr<SerializationData> data_;
3269 :
3270 : DISALLOW_COPY_AND_ASSIGN(Deserializer);
3271 : };
3272 :
3273 1894 : std::unique_ptr<SerializationData> Shell::SerializeValue(
3274 : Isolate* isolate, Local<Value> value, Local<Value> transfer) {
3275 : bool ok;
3276 1894 : Local<Context> context = isolate->GetCurrentContext();
3277 1898 : Serializer serializer(isolate);
3278 1898 : std::unique_ptr<SerializationData> data;
3279 3782 : if (serializer.WriteValue(context, value, transfer).To(&ok)) {
3280 : data = serializer.Release();
3281 : }
3282 : // Append externalized contents even when WriteValue fails.
3283 : base::MutexGuard lock_guard(workers_mutex_.Pointer());
3284 1907 : serializer.AppendExternalizedContentsTo(&externalized_contents_);
3285 1907 : return data;
3286 : }
3287 :
3288 1672 : MaybeLocal<Value> Shell::DeserializeValue(
3289 : Isolate* isolate, std::unique_ptr<SerializationData> data) {
3290 : Local<Value> value;
3291 1672 : Local<Context> context = isolate->GetCurrentContext();
3292 3344 : Deserializer deserializer(isolate, std::move(data));
3293 1672 : return deserializer.ReadValue(context);
3294 : }
3295 :
3296 :
3297 49957 : void Shell::CleanupWorkers() {
3298 : // Make a copy of workers_, because we don't want to call Worker::Terminate
3299 : // while holding the workers_mutex_ lock. Otherwise, if a worker is about to
3300 : // create a new Worker, it would deadlock.
3301 : std::vector<Worker*> workers_copy;
3302 : {
3303 : base::MutexGuard lock_guard(workers_mutex_.Pointer());
3304 49957 : allow_new_workers_ = false;
3305 : workers_copy.swap(workers_);
3306 : }
3307 :
3308 100536 : for (Worker* worker : workers_copy) {
3309 : worker->WaitForThread();
3310 622 : delete worker;
3311 : }
3312 :
3313 : // Now that all workers are terminated, we can re-enable Worker creation.
3314 : base::MutexGuard lock_guard(workers_mutex_.Pointer());
3315 49957 : allow_new_workers_ = true;
3316 : externalized_contents_.clear();
3317 49957 : }
3318 :
3319 28773 : int Shell::Main(int argc, char* argv[]) {
3320 28773 : std::ofstream trace_file;
3321 : v8::base::EnsureConsoleOutput();
3322 28773 : if (!SetOptions(argc, argv)) return 1;
3323 28773 : v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
3324 :
3325 : v8::platform::InProcessStackDumping in_process_stack_dumping =
3326 : options.disable_in_process_stack_traces
3327 : ? v8::platform::InProcessStackDumping::kDisabled
3328 28773 : : v8::platform::InProcessStackDumping::kEnabled;
3329 :
3330 : std::unique_ptr<platform::tracing::TracingController> tracing;
3331 28773 : if (options.trace_enabled && !i::FLAG_verify_predictable) {
3332 2 : tracing = base::make_unique<platform::tracing::TracingController>();
3333 :
3334 1 : trace_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
3335 : platform::tracing::TraceBuffer* trace_buffer =
3336 : platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
3337 : platform::tracing::TraceBuffer::kRingBufferChunks,
3338 1 : platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
3339 1 : tracing->Initialize(trace_buffer);
3340 : }
3341 :
3342 : platform::tracing::TracingController* tracing_controller = tracing.get();
3343 86319 : g_platform = v8::platform::NewDefaultPlatform(
3344 : options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
3345 : in_process_stack_dumping, std::move(tracing));
3346 28773 : g_default_platform = g_platform.get();
3347 : if (i::FLAG_verify_predictable) {
3348 : g_platform = MakePredictablePlatform(std::move(g_platform));
3349 : }
3350 28773 : if (options.stress_delay_tasks) {
3351 0 : int64_t random_seed = i::FLAG_fuzzer_random_seed;
3352 0 : if (!random_seed) random_seed = i::FLAG_random_seed;
3353 : // If random_seed is still 0 here, the {DelayedTasksPlatform} will choose a
3354 : // random seed.
3355 0 : g_platform = MakeDelayedTasksPlatform(std::move(g_platform), random_seed);
3356 : }
3357 :
3358 28773 : if (i::FLAG_trace_turbo_cfg_file == nullptr) {
3359 28772 : SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
3360 : }
3361 28773 : if (i::FLAG_redirect_code_traces_to == nullptr) {
3362 28773 : SetFlagsFromString("--redirect-code-traces-to=code.asm");
3363 : }
3364 28773 : v8::V8::InitializePlatform(g_platform.get());
3365 28773 : v8::V8::Initialize();
3366 28773 : if (options.natives_blob || options.snapshot_blob) {
3367 : v8::V8::InitializeExternalStartupData(options.natives_blob,
3368 0 : options.snapshot_blob);
3369 : } else {
3370 28773 : v8::V8::InitializeExternalStartupData(argv[0]);
3371 : }
3372 : int result = 0;
3373 : Isolate::CreateParams create_params;
3374 : ShellArrayBufferAllocator shell_array_buffer_allocator;
3375 : MockArrayBufferAllocator mock_arraybuffer_allocator;
3376 : const size_t memory_limit =
3377 28773 : options.mock_arraybuffer_allocator_limit * options.num_isolates;
3378 : MockArrayBufferAllocatiorWithLimit mock_arraybuffer_allocator_with_limit(
3379 : memory_limit >= options.mock_arraybuffer_allocator_limit
3380 : ? memory_limit
3381 28773 : : std::numeric_limits<size_t>::max());
3382 28773 : if (options.mock_arraybuffer_allocator) {
3383 21 : if (memory_limit) {
3384 0 : Shell::array_buffer_allocator = &mock_arraybuffer_allocator_with_limit;
3385 : } else {
3386 21 : Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
3387 : }
3388 : } else {
3389 28752 : Shell::array_buffer_allocator = &shell_array_buffer_allocator;
3390 : }
3391 28773 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3392 : #ifdef ENABLE_VTUNE_JIT_INTERFACE
3393 : create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
3394 : #endif
3395 : create_params.constraints.ConfigureDefaults(
3396 28773 : base::SysInfo::AmountOfPhysicalMemory(),
3397 57546 : base::SysInfo::AmountOfVirtualMemory());
3398 :
3399 57546 : Shell::counter_map_ = new CounterMap();
3400 28773 : if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp || i::FLAG_gc_stats) {
3401 0 : create_params.counter_lookup_callback = LookupCounter;
3402 0 : create_params.create_histogram_callback = CreateHistogram;
3403 0 : create_params.add_histogram_sample_callback = AddHistogramSample;
3404 : }
3405 :
3406 28773 : if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) {
3407 : constexpr bool use_default_trap_handler = true;
3408 28763 : if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_trap_handler)) {
3409 0 : FATAL("Could not register trap handler");
3410 : }
3411 : }
3412 :
3413 28773 : Isolate* isolate = Isolate::New(create_params);
3414 : isolate->SetHostImportModuleDynamicallyCallback(
3415 28773 : Shell::HostImportModuleDynamically);
3416 : isolate->SetHostInitializeImportMetaObjectCallback(
3417 28773 : Shell::HostInitializeImportMetaObject);
3418 :
3419 28773 : D8Console console(isolate);
3420 : {
3421 : Isolate::Scope scope(isolate);
3422 28773 : Initialize(isolate);
3423 57546 : PerIsolateData data(isolate);
3424 28773 : debug::SetConsoleDelegate(isolate, &console);
3425 :
3426 28773 : if (options.trace_enabled) {
3427 : platform::tracing::TraceConfig* trace_config;
3428 1 : if (options.trace_config) {
3429 0 : int size = 0;
3430 0 : char* trace_config_json_str = ReadChars(options.trace_config, &size);
3431 : trace_config =
3432 0 : tracing::CreateTraceConfigFromJSON(isolate, trace_config_json_str);
3433 0 : delete[] trace_config_json_str;
3434 : } else {
3435 : trace_config =
3436 1 : platform::tracing::TraceConfig::CreateDefaultTraceConfig();
3437 : }
3438 1 : tracing_controller->StartTracing(trace_config);
3439 : }
3440 :
3441 28773 : if (options.stress_opt || options.stress_deopt) {
3442 : Testing::SetStressRunType(options.stress_opt
3443 : ? Testing::kStressTypeOpt
3444 5265 : : Testing::kStressTypeDeopt);
3445 5265 : options.stress_runs = Testing::GetStressRuns();
3446 36851 : for (int i = 0; i < options.stress_runs && result == 0; i++) {
3447 : printf("============ Stress %d/%d ============\n", i + 1,
3448 26321 : options.stress_runs);
3449 26321 : Testing::PrepareStressRun(i);
3450 26321 : bool last_run = i == options.stress_runs - 1;
3451 26321 : result = RunMain(isolate, argc, argv, last_run);
3452 : }
3453 : printf("======== Full Deoptimization =======\n");
3454 5265 : Testing::DeoptimizeAll(isolate);
3455 23508 : } else if (i::FLAG_stress_runs > 0) {
3456 24 : options.stress_runs = i::FLAG_stress_runs;
3457 152 : for (int i = 0; i < options.stress_runs && result == 0; i++) {
3458 : printf("============ Run %d/%d ============\n", i + 1,
3459 104 : options.stress_runs);
3460 104 : bool last_run = i == options.stress_runs - 1;
3461 104 : result = RunMain(isolate, argc, argv, last_run);
3462 : }
3463 23484 : } else if (options.code_cache_options !=
3464 : ShellOptions::CodeCacheOptions::kNoProduceCache) {
3465 : printf("============ Run: Produce code cache ============\n");
3466 : // First run to produce the cache
3467 : Isolate::CreateParams create_params;
3468 48 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3469 48 : i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
3470 48 : Isolate* isolate2 = Isolate::New(create_params);
3471 48 : i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
3472 : isolate2->SetHostImportModuleDynamicallyCallback(
3473 48 : Shell::HostImportModuleDynamically);
3474 : isolate2->SetHostInitializeImportMetaObjectCallback(
3475 48 : Shell::HostInitializeImportMetaObject);
3476 : {
3477 48 : D8Console console(isolate2);
3478 48 : Initialize(isolate2);
3479 48 : debug::SetConsoleDelegate(isolate2, &console);
3480 96 : PerIsolateData data(isolate2);
3481 : Isolate::Scope isolate_scope(isolate2);
3482 :
3483 48 : result = RunMain(isolate2, argc, argv, false);
3484 : }
3485 48 : isolate2->Dispose();
3486 :
3487 : // Change the options to consume cache
3488 : DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
3489 : options.compile_options == v8::ScriptCompiler::kNoCompileOptions);
3490 48 : options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
3491 : options.code_cache_options =
3492 48 : ShellOptions::CodeCacheOptions::kNoProduceCache;
3493 :
3494 : printf("============ Run: Consume code cache ============\n");
3495 : // Second run to consume the cache in current isolate
3496 48 : result = RunMain(isolate, argc, argv, true);
3497 48 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3498 : } else {
3499 : bool last_run = true;
3500 23436 : result = RunMain(isolate, argc, argv, last_run);
3501 : }
3502 :
3503 : // Run interactive shell if explicitly requested or if no script has been
3504 : // executed, but never on --test
3505 28773 : if (use_interactive_shell()) {
3506 0 : RunShell(isolate);
3507 : }
3508 :
3509 28778 : if (i::FLAG_trace_ignition_dispatches &&
3510 5 : i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
3511 0 : WriteIgnitionDispatchCountersFile(isolate);
3512 : }
3513 :
3514 : // Shut down contexts and collect garbage.
3515 : cached_code_map_.clear();
3516 : evaluation_context_.Reset();
3517 : stringify_function_.Reset();
3518 28773 : CollectGarbage(isolate);
3519 : }
3520 28773 : OnExit(isolate);
3521 28773 : V8::Dispose();
3522 28773 : V8::ShutdownPlatform();
3523 :
3524 : // Delete the platform explicitly here to write the tracing output to the
3525 : // tracing file.
3526 : g_platform.reset();
3527 28773 : return result;
3528 : }
3529 :
3530 : } // namespace v8
3531 :
3532 :
3533 : #ifndef GOOGLE3
3534 28773 : int main(int argc, char* argv[]) {
3535 28773 : return v8::Shell::Main(argc, argv);
3536 86319 : }
3537 : #endif
3538 :
3539 : #undef CHECK
3540 : #undef DCHECK
|