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